PageRenderTime 104ms CodeModel.GetById 14ms app.highlight 79ms RepoModel.GetById 1ms app.codeStats 0ms

/hippo/src/main/webapp/yui/colorpicker/colorpicker-debug.js

http://hdbc.googlecode.com/
JavaScript | 1783 lines | 850 code | 263 blank | 670 comment | 79 complexity | 8d600231b5b86bfb77a02a1d4901fd7a MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*
   2Copyright (c) 2009, Yahoo! Inc. All rights reserved.
   3Code licensed under the BSD License:
   4http://developer.yahoo.net/yui/license.txt
   5version: 2.7.0
   6*/
   7/**
   8 * Provides color conversion and validation utils
   9 * @class YAHOO.util.Color
  10 * @namespace YAHOO.util
  11 */
  12YAHOO.util.Color = function() {
  13
  14    var ZERO     = "0",
  15        isArray  = YAHOO.lang.isArray,
  16        isNumber = YAHOO.lang.isNumber;
  17
  18    return {
  19
  20        /**
  21         * Converts 0-1 to 0-255
  22         * @method real2dec
  23         * @param n {float} the number to convert
  24         * @return {int} a number 0-255
  25         */
  26        real2dec: function(n) {
  27            return Math.min(255, Math.round(n*256));
  28        },
  29
  30        /**
  31         * Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
  32         * @method hsv2rgb
  33         * @param h {int|[int, float, float]} the hue, or an
  34         *        array containing all three parameters
  35         * @param s {float} the saturation
  36         * @param v {float} the value/brightness
  37         * @return {[int, int, int]} the red, green, blue values in
  38         *          decimal.
  39         */
  40        hsv2rgb: function(h, s, v) { 
  41
  42            if (isArray(h)) {
  43                return this.hsv2rgb.call(this, h[0], h[1], h[2]);
  44            }
  45
  46            var r, g, b,
  47                i = Math.floor((h/60)%6),
  48                f = (h/60)-i,
  49                p = v*(1-s),
  50                q = v*(1-f*s),
  51                t = v*(1-(1-f)*s),
  52                fn;
  53
  54            switch (i) {
  55                case 0: r=v; g=t; b=p; break;
  56                case 1: r=q; g=v; b=p; break;
  57                case 2: r=p; g=v; b=t; break;
  58                case 3: r=p; g=q; b=v; break;
  59                case 4: r=t; g=p; b=v; break;
  60                case 5: r=v; g=p; b=q; break;
  61            }
  62
  63            fn=this.real2dec;
  64
  65            return [fn(r), fn(g), fn(b)];
  66        },
  67
  68        /**
  69         * Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
  70         * @method rgb2hsv
  71         * @param r {int|[int, int, int]} the red value, or an
  72         *        array containing all three parameters
  73         * @param g {int} the green value
  74         * @param b {int} the blue value
  75         * @return {[int, float, float]} the value converted to hsv
  76         */
  77        rgb2hsv: function(r, g, b) {
  78
  79            if (isArray(r)) {
  80                return this.rgb2hsv.apply(this, r);
  81            }
  82
  83            r /= 255;
  84            g /= 255;
  85            b /= 255;
  86
  87            var h,s,
  88                min = Math.min(Math.min(r,g),b),
  89                max = Math.max(Math.max(r,g),b),
  90                delta = max-min,
  91                hsv;
  92
  93            switch (max) {
  94                case min: h=0; break;
  95                case r:   h=60*(g-b)/delta; 
  96                          if (g<b) {
  97                              h+=360;
  98                          }
  99                          break;
 100                case g:   h=(60*(b-r)/delta)+120; break;
 101                case b:   h=(60*(r-g)/delta)+240; break;
 102            }
 103            
 104            s = (max === 0) ? 0 : 1-(min/max);
 105
 106            hsv = [Math.round(h), s, max];
 107
 108            return hsv;
 109        },
 110
 111        /**
 112         * Converts decimal rgb values into a hex string
 113         * 255,255,255 -> FFFFFF
 114         * @method rgb2hex
 115         * @param r {int|[int, int, int]} the red value, or an
 116         *        array containing all three parameters
 117         * @param g {int} the green value
 118         * @param b {int} the blue value
 119         * @return {string} the hex string
 120         */
 121        rgb2hex: function(r, g, b) {
 122            if (isArray(r)) {
 123                return this.rgb2hex.apply(this, r);
 124            }
 125
 126            var f=this.dec2hex;
 127            return f(r) + f(g) + f(b);
 128        },
 129     
 130        /**
 131         * Converts an int 0...255 to hex pair 00...FF
 132         * @method dec2hex
 133         * @param n {int} the number to convert
 134         * @return {string} the hex equivalent
 135         */
 136        dec2hex: function(n) {
 137            n = parseInt(n,10)|0;
 138            n = (n > 255 || n < 0) ? 0 : n;
 139
 140            return (ZERO+n.toString(16)).slice(-2).toUpperCase();
 141        },
 142
 143        /**
 144         * Converts a hex pair 00...FF to an int 0...255 
 145         * @method hex2dec
 146         * @param str {string} the hex pair to convert
 147         * @return {int} the decimal
 148         */
 149        hex2dec: function(str) {
 150            return parseInt(str,16);
 151        },
 152
 153        /**
 154         * Converts a hex string to rgb
 155         * @method hex2rgb
 156         * @param str {string} the hex string
 157         * @return {[int, int, int]} an array containing the rgb values
 158         */
 159        hex2rgb: function(s) { 
 160            var f = this.hex2dec;
 161            return [f(s.slice(0, 2)), f(s.slice(2, 4)), f(s.slice(4, 6))];
 162        },
 163
 164        /**
 165         * Returns the closest websafe color to the supplied rgb value.
 166         * @method websafe
 167         * @param r {int|[int, int, int]} the red value, or an
 168         *        array containing all three parameters
 169         * @param g {int} the green value
 170         * @param b {int} the blue value
 171         * @return {[int, int, int]} an array containing the closes
 172         *                           websafe rgb colors.
 173         */
 174        websafe: function(r, g, b) {
 175
 176            if (isArray(r)) {
 177                return this.websafe.apply(this, r);
 178            }
 179
 180            // returns the closest match [0, 51, 102, 153, 204, 255]
 181            var f = function(v) {
 182                if (isNumber(v)) {
 183                    v = Math.min(Math.max(0, v), 255);
 184                    var i, next;
 185                    for (i=0; i<256; i=i+51) {
 186                        next = i+51;
 187                        if (v >= i && v <= next) {
 188                            return (v-i > 25) ? next : i;
 189                        }
 190                    }
 191 YAHOO.log("Error calculating the websafe value for " + v, "warn");
 192                }
 193
 194                return v;
 195            };
 196
 197            return [f(r), f(g), f(b)];
 198        }
 199    };
 200}();
 201
 202
 203/**
 204 * The colorpicker module provides a widget for selecting colors
 205 * @module colorpicker
 206 * @requires yahoo, dom, event, element, slider
 207 */
 208(function() {
 209
 210    var _pickercount = 0,
 211        util   = YAHOO.util,
 212        lang   = YAHOO.lang,
 213        Slider = YAHOO.widget.Slider,
 214        Color  = util.Color,
 215        Dom    = util.Dom,
 216        Event  = util.Event,
 217        sub    = lang.substitute,
 218        
 219        b = "yui-picker";
 220    
 221
 222    /**
 223     * A widget to select colors
 224     * @namespace YAHOO.widget
 225     * @class YAHOO.widget.ColorPicker
 226     * @extends YAHOO.util.Element
 227     * @constructor
 228     * @param {HTMLElement | String | Object} el(optional) The html 
 229     * element that represents the colorpicker, or the attribute object to use. 
 230     * An element will be created if none provided.
 231     * @param {Object} attr (optional) A key map of the colorpicker's 
 232     * initial attributes.  Ignored if first arg is attributes object.
 233     */
 234    function ColorPicker(el, attr) {
 235        _pickercount = _pickercount + 1;
 236        this.logger = new YAHOO.widget.LogWriter("ColorPicker");
 237        attr = attr || {};
 238        if (arguments.length === 1 && !YAHOO.lang.isString(el) && !el.nodeName) {
 239            attr = el; // treat first arg as attr object
 240            el = attr.element || null;
 241        }
 242        
 243        if (!el && !attr.element) { // create if we dont have one
 244            this.logger.log("creating host element");
 245            el = this._createHostElement(attr);
 246        }
 247
 248    	ColorPicker.superclass.constructor.call(this, el, attr); 
 249
 250        this.initPicker();
 251    }
 252
 253    YAHOO.extend(ColorPicker, YAHOO.util.Element, {
 254    
 255        /**
 256         * The element ids used by this control
 257         * @property ID
 258         * @final
 259         */
 260        ID : {
 261
 262            /**
 263             * The id for the "red" form field
 264             * @property ID.R
 265             * @type String
 266             * @final
 267             * @default yui-picker-r
 268             */
 269            R: b + "-r",
 270
 271            /**
 272             * The id for the "red" hex pair output
 273             * @property ID.R_HEX
 274             * @type String
 275             * @final
 276             * @default yui-picker-rhex
 277             */
 278            R_HEX: b + "-rhex",
 279
 280            /**
 281             * The id for the "green" form field
 282             * @property ID.G
 283             * @type String
 284             * @final
 285             * @default yui-picker-g
 286             */
 287            G: b + "-g",
 288
 289            /**
 290             * The id for the "green" hex pair output
 291             * @property ID.G_HEX
 292             * @type String
 293             * @final
 294             * @default yui-picker-ghex
 295             */
 296            G_HEX: b + "-ghex",
 297
 298
 299            /**
 300             * The id for the "blue" form field
 301             * @property ID.B
 302             * @type String
 303             * @final
 304             * @default yui-picker-b
 305             */
 306            B: b + "-b",
 307
 308            /**
 309             * The id for the "blue" hex pair output
 310             * @property ID.B_HEX
 311             * @type String
 312             * @final
 313             * @default yui-picker-bhex
 314             */
 315            B_HEX: b + "-bhex",
 316
 317            /**
 318             * The id for the "hue" form field
 319             * @property ID.H
 320             * @type String
 321             * @final
 322             * @default yui-picker-h
 323             */
 324            H: b + "-h",
 325
 326            /**
 327             * The id for the "saturation" form field
 328             * @property ID.S
 329             * @type String
 330             * @final
 331             * @default yui-picker-s
 332             */
 333            S: b + "-s",
 334
 335            /**
 336             * The id for the "value" form field
 337             * @property ID.V
 338             * @type String
 339             * @final
 340             * @default yui-picker-v
 341             */
 342            V: b + "-v",
 343
 344            /**
 345             * The id for the picker region slider
 346             * @property ID.PICKER_BG
 347             * @type String
 348             * @final
 349             * @default yui-picker-bg
 350             */
 351            PICKER_BG:      b + "-bg",
 352
 353            /**
 354             * The id for the picker region thumb
 355             * @property ID.PICKER_THUMB
 356             * @type String
 357             * @final
 358             * @default yui-picker-thumb
 359             */
 360            PICKER_THUMB:   b + "-thumb",
 361
 362            /**
 363             * The id for the hue slider
 364             * @property ID.HUE_BG
 365             * @type String
 366             * @final
 367             * @default yui-picker-hue-bg
 368             */
 369            HUE_BG:         b + "-hue-bg",
 370
 371            /**
 372             * The id for the hue thumb
 373             * @property ID.HUE_THUMB
 374             * @type String
 375             * @final
 376             * @default yui-picker-hue-thumb
 377             */
 378            HUE_THUMB:      b + "-hue-thumb",
 379
 380            /**
 381             * The id for the hex value form field
 382             * @property ID.HEX
 383             * @type String
 384             * @final
 385             * @default yui-picker-hex
 386             */
 387            HEX:            b + "-hex",
 388
 389            /**
 390             * The id for the color swatch
 391             * @property ID.SWATCH
 392             * @type String
 393             * @final
 394             * @default yui-picker-swatch
 395             */
 396            SWATCH:         b + "-swatch",
 397
 398            /**
 399             * The id for the websafe color swatch
 400             * @property ID.WEBSAFE_SWATCH
 401             * @type String
 402             * @final
 403             * @default yui-picker-websafe-swatch
 404             */
 405            WEBSAFE_SWATCH: b + "-websafe-swatch",
 406
 407            /**
 408             * The id for the control details
 409             * @property ID.CONTROLS
 410             * @final
 411             * @default yui-picker-controls
 412             */
 413            CONTROLS: b + "-controls",
 414
 415            /**
 416             * The id for the rgb controls
 417             * @property ID.RGB_CONTROLS
 418             * @final
 419             * @default yui-picker-rgb-controls
 420             */
 421            RGB_CONTROLS: b + "-rgb-controls",
 422
 423            /**
 424             * The id for the hsv controls
 425             * @property ID.HSV_CONTROLS
 426             * @final
 427             * @default yui-picker-hsv-controls
 428             */
 429            HSV_CONTROLS: b + "-hsv-controls",
 430            
 431            /**
 432             * The id for the hsv controls
 433             * @property ID.HEX_CONTROLS
 434             * @final
 435             * @default yui-picker-hex-controls
 436             */
 437            HEX_CONTROLS: b + "-hex-controls",
 438
 439            /**
 440             * The id for the hex summary
 441             * @property ID.HEX_SUMMARY
 442             * @final
 443             * @default yui-picker-hex-summary
 444             */
 445            HEX_SUMMARY: b + "-hex-summary",
 446
 447            /**
 448             * The id for the controls section header
 449             * @property ID.CONTROLS_LABEL
 450             * @final
 451             * @default yui-picker-controls-label
 452             */
 453            CONTROLS_LABEL: b + "-controls-label"
 454        },
 455
 456        /**
 457         * Constants for any script-generated messages.  The values here
 458         * are the default messages.  They can be updated by providing
 459         * the complete list to the constructor for the "txt" attribute.
 460         * @property TXT
 461         * @final
 462         */
 463        TXT : {
 464            ILLEGAL_HEX: "Illegal hex value entered",
 465            SHOW_CONTROLS: "Show color details",
 466            HIDE_CONTROLS: "Hide color details",
 467            CURRENT_COLOR: "Currently selected color: {rgb}",
 468            CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.",
 469            R: "R",
 470            G: "G",
 471            B: "B",
 472            H: "H",
 473            S: "S",
 474            V: "V",
 475            HEX: "#",
 476            DEG: "\u00B0",
 477            PERCENT: "%"
 478        },
 479
 480        /**
 481         * Constants for the default image locations for img tags that are
 482         * generated by the control.  They can be modified by passing the
 483         * complete list to the contructor for the "images" attribute
 484         * @property IMAGE
 485         * @final
 486         */
 487        IMAGE : {
 488            PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png",
 489            HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png"
 490        },
 491
 492        /**
 493         * Constants for the control's default default values
 494         * @property DEFAULT
 495         * @final
 496         */
 497        DEFAULT : {
 498            PICKER_SIZE: 180
 499        },
 500
 501        /**
 502         * Constants for the control's configuration attributes
 503         * @property OPT
 504         * @final
 505         */
 506        OPT : {
 507            HUE         : "hue",
 508            SATURATION  : "saturation",
 509            VALUE       : "value",
 510            RED     : "red",
 511            GREEN   : "green",
 512            BLUE    : "blue",
 513            HSV     : "hsv",
 514            RGB     : "rgb",
 515            WEBSAFE : "websafe",
 516            HEX     : "hex",
 517            PICKER_SIZE       : "pickersize",
 518            SHOW_CONTROLS     : "showcontrols",
 519            SHOW_RGB_CONTROLS : "showrgbcontrols",
 520            SHOW_HSV_CONTROLS : "showhsvcontrols",
 521            SHOW_HEX_CONTROLS : "showhexcontrols",
 522            SHOW_HEX_SUMMARY  : "showhexsummary",
 523            SHOW_WEBSAFE      : "showwebsafe",
 524            CONTAINER         : "container",
 525            IDS      : "ids",
 526            ELEMENTS : "elements",
 527            TXT      : "txt",
 528            IMAGES   : "images",
 529            ANIMATE  : "animate"
 530        },
 531
 532        /**
 533         * Flag to allow individual UI updates to forego animation if available.
 534         * True during construction for initial thumb placement.  Set to false
 535         * after that.
 536         *
 537         * @property skipAnim
 538         * @type Boolean
 539         * @default true
 540         */
 541        skipAnim : true,
 542
 543        /**
 544         * Creates the host element if it doesn't exist
 545         * @method _createHostElement
 546         * @protected
 547         */
 548        _createHostElement : function () {
 549            var el = document.createElement('div');
 550
 551            if (this.CSS.BASE) {
 552                el.className = this.CSS.BASE;
 553            }
 554            
 555            return el;
 556        },
 557
 558        /**
 559         * Moves the hue slider into the position dictated by the current state
 560         * of the control
 561         * @method _updateHueSlider
 562         * @protected
 563         */
 564        _updateHueSlider : function() {
 565            var size = this.get(this.OPT.PICKER_SIZE),
 566                h = this.get(this.OPT.HUE);
 567
 568            h = size - Math.round(h / 360 * size);
 569            
 570            // 0 is at the top and bottom of the hue slider.  Always go to
 571            // the top so we don't end up sending the thumb to the bottom
 572            // when the value didn't actually change (e.g., a conversion
 573            // produced 360 instead of 0 and the value was already 0).
 574            if (h === size) {
 575                h = 0;
 576            }
 577            this.logger.log("Hue slider is being set to " + h);
 578
 579            this.hueSlider.setValue(h, this.skipAnim);
 580        },
 581
 582        /**
 583         * Moves the picker slider into the position dictated by the current state
 584         * of the control
 585         * @method _updatePickerSlider
 586         * @protected
 587         */
 588        _updatePickerSlider : function() {
 589            var size = this.get(this.OPT.PICKER_SIZE),
 590                s = this.get(this.OPT.SATURATION),
 591                v = this.get(this.OPT.VALUE);
 592
 593            s = Math.round(s * size / 100);
 594            v = Math.round(size - (v * size / 100));
 595
 596            this.logger.log("Setting picker slider to " + [s, v]);
 597
 598            this.pickerSlider.setRegionValue(s, v, this.skipAnim);
 599        },
 600
 601        /**
 602         * Moves the sliders into the position dictated by the current state
 603         * of the control
 604         * @method _updateSliders
 605         * @protected
 606         */
 607        _updateSliders : function() {
 608            this._updateHueSlider();
 609            this._updatePickerSlider();
 610        },
 611
 612        /**
 613         * Sets the control to the specified rgb value and
 614         * moves the sliders to the proper positions
 615         * @method setValue
 616         * @param rgb {[int, int, int]} the rgb value
 617         * @param silent {boolean} whether or not to fire the change event
 618         */
 619        setValue : function(rgb, silent) {
 620            silent = (silent) || false;
 621            this.set(this.OPT.RGB, rgb, silent);
 622            this._updateSliders();
 623        },
 624
 625        /**
 626         * The hue slider
 627         * @property hueSlider
 628         * @type YAHOO.widget.Slider
 629         */
 630        hueSlider : null,
 631        
 632        /**
 633         * The picker region
 634         * @property pickerSlider
 635         * @type YAHOO.widget.Slider
 636         */
 637        pickerSlider : null,
 638
 639        /**
 640         * Translates the slider value into hue, int[0,359]
 641         * @method _getH
 642         * @protected
 643         * @return {int} the hue from 0 to 359
 644         */
 645        _getH : function() {
 646            var size = this.get(this.OPT.PICKER_SIZE),
 647                h = (size - this.hueSlider.getValue()) / size;
 648            h = Math.round(h*360);
 649            return (h === 360) ? 0 : h;
 650        },
 651
 652        /**
 653         * Translates the slider value into saturation, int[0,1], left to right
 654         * @method _getS
 655         * @protected
 656         * @return {int} the saturation from 0 to 1
 657         */
 658        _getS : function() {
 659            return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE);
 660        },
 661
 662        /**
 663         * Translates the slider value into value/brightness, int[0,1], top
 664         * to bottom
 665         * @method _getV
 666         * @protected
 667         * @return {int} the value from 0 to 1
 668         */
 669        _getV : function() {
 670            var size = this.get(this.OPT.PICKER_SIZE);
 671            return (size - this.pickerSlider.getYValue()) / size;
 672        },
 673
 674        /**
 675         * Updates the background of the swatch with the current rbg value.
 676         * Also updates the websafe swatch to the closest websafe color
 677         * @method _updateSwatch
 678         * @protected
 679         */
 680        _updateSwatch : function() {
 681            var rgb = this.get(this.OPT.RGB),
 682                websafe = this.get(this.OPT.WEBSAFE),
 683                el = this.getElement(this.ID.SWATCH),
 684                color = rgb.join(","),
 685                txt = this.get(this.OPT.TXT);
 686
 687            Dom.setStyle(el, "background-color", "rgb(" + color  + ")");
 688            el.title = sub(txt.CURRENT_COLOR, {
 689                    "rgb": "#" + this.get(this.OPT.HEX)
 690                });
 691
 692
 693            el = this.getElement(this.ID.WEBSAFE_SWATCH);
 694            color = websafe.join(",");
 695
 696            Dom.setStyle(el, "background-color", "rgb(" + color + ")");
 697            el.title = sub(txt.CLOSEST_WEBSAFE, {
 698                    "rgb": "#" + Color.rgb2hex(websafe)
 699                });
 700
 701        },
 702
 703        /**
 704         * Reads the sliders and converts the values to RGB, updating the
 705         * internal state for all the individual form fields
 706         * @method _getValuesFromSliders
 707         * @protected
 708         */
 709        _getValuesFromSliders : function() {
 710            this.logger.log("hsv " + [this._getH(),this._getS(),this._getV()]);
 711            this.set(this.OPT.RGB, Color.hsv2rgb(this._getH(), this._getS(), this._getV()));
 712        },
 713
 714        /**
 715         * Updates the form field controls with the state data contained
 716         * in the control.
 717         * @method _updateFormFields
 718         * @protected
 719         */
 720        _updateFormFields : function() {
 721            this.getElement(this.ID.H).value = this.get(this.OPT.HUE);
 722            this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION);
 723            this.getElement(this.ID.V).value = this.get(this.OPT.VALUE);
 724            this.getElement(this.ID.R).value = this.get(this.OPT.RED);
 725            this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED));
 726            this.getElement(this.ID.G).value = this.get(this.OPT.GREEN);
 727            this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN));
 728            this.getElement(this.ID.B).value = this.get(this.OPT.BLUE);
 729            this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE));
 730            this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX);
 731        },
 732
 733        /**
 734         * Event handler for the hue slider.
 735         * @method _onHueSliderChange
 736         * @param newOffset {int} pixels from the start position
 737         * @protected
 738         */
 739        _onHueSliderChange : function(newOffset) {
 740            this.logger.log("hue update: " + newOffset , "warn");
 741
 742            var h        = this._getH(),
 743                rgb      = Color.hsv2rgb(h, 1, 1),
 744                styleDef = "rgb(" + rgb.join(",") + ")";
 745
 746            this.set(this.OPT.HUE, h, true);
 747
 748            // set picker background to the hue
 749            Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef);
 750
 751            if (this.hueSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
 752                this._getValuesFromSliders();
 753            }
 754
 755            this._updateFormFields();
 756            this._updateSwatch();
 757        },
 758
 759        /**
 760         * Event handler for the picker slider, which controls the
 761         * saturation and value/brightness.
 762         * @method _onPickerSliderChange
 763         * @param newOffset {{x: int, y: int}} x/y pixels from the start position
 764         * @protected
 765         */
 766        _onPickerSliderChange : function(newOffset) {
 767            this.logger.log(sub("picker update [{x}, {y}]", newOffset));
 768
 769            var s=this._getS(), v=this._getV();
 770            this.set(this.OPT.SATURATION, Math.round(s*100), true);
 771            this.set(this.OPT.VALUE, Math.round(v*100), true);
 772
 773            if (this.pickerSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
 774                this._getValuesFromSliders();
 775            }
 776
 777            this._updateFormFields();
 778            this._updateSwatch();
 779        },
 780
 781
 782        /**
 783         * Key map to well-known commands for txt field input
 784         * @method _getCommand
 785         * @param e {Event} the keypress or keydown event
 786         * @return {int} a command code
 787         * <ul>
 788         * <li>0 = not a number, letter in range, or special key</li>
 789         * <li>1 = number</li>
 790         * <li>2 = a-fA-F</li>
 791         * <li>3 = increment (up arrow)</li>
 792         * <li>4 = decrement (down arrow)</li>
 793         * <li>5 = special key (tab, delete, return, escape, left, right)</li> 
 794         * <li>6 = return</li>
 795         * </ul>
 796         * @protected
 797         */
 798        _getCommand : function(e) {
 799            var c = Event.getCharCode(e);
 800
 801            //alert(Event.getCharCode(e) + ", " + e.keyCode + ", " + e.charCode);
 802
 803            // special keys
 804            if (c === 38) { // up arrow
 805                return 3;
 806            } else if (c === 13) { // return
 807                return 6;
 808            } else if (c === 40) { // down array
 809                return 4;
 810            } else if (c >= 48 && c<=57) { // 0-9
 811                return 1;
 812            } else if (c >= 97 && c<=102) { // a-f
 813                return 2;
 814            } else if (c >= 65 && c<=70) { // A-F
 815                return 2;
 816            //} else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 || 
 817            //              (c >= 112 && c <=123)) { // including F-keys
 818            // tab, delete, return, escape, left, right or ctrl/meta sequences
 819            } else if ("8, 9, 13, 27, 37, 39".indexOf(c) > -1 ||
 820                       e.ctrlKey || e.metaKey) { // special chars
 821                return 5;
 822            } else { // something we probably don't want
 823                return 0;
 824            }
 825        },
 826
 827        /**
 828         * Use the value of the text field to update the control
 829         * @method _useFieldValue
 830         * @param e {Event} an event
 831         * @param el {HTMLElement} the field
 832         * @param prop {string} the key to the linked property
 833         * @protected
 834         */
 835        _useFieldValue : function(e, el, prop) {
 836            var val = el.value;
 837
 838            if (prop !== this.OPT.HEX) {
 839                val = parseInt(val, 10);
 840            }
 841
 842            if (val !== this.get(prop)) {
 843                this.set(prop, val);
 844            }
 845        },
 846
 847        /**
 848         * Handle keypress on one of the rgb or hsv fields.
 849         * @method _rgbFieldKeypress
 850         * @param e {Event} the keypress event
 851         * @param el {HTMLElement} the field
 852         * @param prop {string} the key to the linked property
 853         * @protected
 854         */
 855        _rgbFieldKeypress : function(e, el, prop) {
 856            var command = this._getCommand(e),
 857                inc = (e.shiftKey) ? 10 : 1;
 858            switch (command) {
 859                case 6: // return, update the value
 860                    this._useFieldValue.apply(this, arguments);
 861                    break;
 862                            
 863                case 3: // up arrow, increment
 864                    this.set(prop, Math.min(this.get(prop)+inc, 255));
 865                    this._updateFormFields();
 866                    //Event.stopEvent(e);
 867                    break;
 868                case 4: // down arrow, decrement
 869                    this.set(prop, Math.max(this.get(prop)-inc, 0));
 870                    this._updateFormFields();
 871                    //Event.stopEvent(e);
 872                    break;
 873
 874                default:
 875            }
 876
 877        },
 878
 879        /**
 880         * Handle keydown on the hex field
 881         * @method _hexFieldKeypress
 882         * @param e {Event} the keypress event
 883         * @param el {HTMLElement} the field
 884         * @param prop {string} the key to the linked property
 885         * @protected
 886         */
 887        _hexFieldKeypress : function(e, el, prop) {
 888            var command = this._getCommand(e);
 889            if (command === 6) { // return, update the value
 890                this._useFieldValue.apply(this, arguments);
 891            }
 892        },
 893
 894        /** 
 895         * Allows numbers and special chars, and by default allows a-f.  
 896         * Used for the hex field keypress handler.
 897         * @method _hexOnly
 898         * @param e {Event} the event
 899         * @param numbersOnly omits a-f if set to true
 900         * @protected
 901         * @return {boolean} false if we are canceling the event
 902         */
 903        _hexOnly : function(e, numbersOnly) {
 904            var command = this._getCommand(e);
 905            switch (command) {
 906                case 6: // return
 907                case 5: // special char
 908                case 1: // number
 909                    break;
 910                case 2: // hex char (a-f)
 911                    if (numbersOnly !== true) {
 912                        break;
 913                    }
 914
 915                    // fallthrough is intentional
 916
 917                default: // prevent alpha and punctuation
 918                    Event.stopEvent(e);
 919                    return false;
 920            }
 921        },
 922
 923        /** 
 924         * Allows numbers and special chars only.  Used for the
 925         * rgb and hsv fields keypress handler.
 926         * @method _numbersOnly
 927         * @param e {Event} the event
 928         * @protected
 929         * @return {boolean} false if we are canceling the event
 930         */
 931        _numbersOnly : function(e) {
 932            return this._hexOnly(e, true);
 933        },
 934
 935        /**
 936         * Returns the element reference that is saved.  The id can be either
 937         * the element id, or the key for this id in the "id" config attribute.
 938         * For instance, the host element id can be obtained by passing its
 939         * id (default: "yui_picker") or by its key "YUI_PICKER".
 940         * @param id {string} the element id, or key 
 941         * @return {HTMLElement} a reference to the element
 942         */
 943        getElement : function(id) { 
 944            return this.get(this.OPT.ELEMENTS)[this.get(this.OPT.IDS)[id]]; 
 945        },
 946
 947        _createElements : function() {
 948            this.logger.log("Building markup");
 949            var el, child, img, fld, p,
 950                ids = this.get(this.OPT.IDS),
 951                txt = this.get(this.OPT.TXT),
 952                images = this.get(this.OPT.IMAGES),
 953                Elem = function(type, o) {
 954                    var n = document.createElement(type);
 955                    if (o) {
 956                        lang.augmentObject(n, o, true);
 957                    }
 958                    return n;
 959                },
 960                RGBElem = function(type, obj) {
 961                    var o = lang.merge({
 962                            //type: "txt",
 963                            autocomplete: "off",
 964                            value: "0",
 965                            size: 3,
 966                            maxlength: 3
 967                        }, obj);
 968
 969                    o.name = o.id;
 970                    return new Elem(type, o);
 971                };
 972
 973            p = this.get("element");
 974
 975            // Picker slider (S and V) ---------------------------------------------
 976
 977            el = new Elem("div", {
 978                id: ids[this.ID.PICKER_BG],
 979                className: "yui-picker-bg",
 980                tabIndex: -1,
 981                hideFocus: true
 982            });
 983
 984            child = new Elem("div", {
 985                id: ids[this.ID.PICKER_THUMB],
 986                className: "yui-picker-thumb"
 987            });
 988
 989            img = new Elem("img", {
 990                src: images.PICKER_THUMB
 991            });
 992
 993            child.appendChild(img);
 994            el.appendChild(child);
 995            p.appendChild(el);
 996            
 997            // Hue slider ---------------------------------------------
 998            el = new Elem("div", {
 999                id: ids[this.ID.HUE_BG],
1000                className: "yui-picker-hue-bg",
1001                tabIndex: -1,
1002                hideFocus: true
1003            });
1004
1005            child = new Elem("div", {
1006                id: ids[this.ID.HUE_THUMB],
1007                className: "yui-picker-hue-thumb"
1008            });
1009
1010            img = new Elem("img", {
1011                src: images.HUE_THUMB
1012            });
1013
1014            child.appendChild(img);
1015            el.appendChild(child);
1016            p.appendChild(el);
1017
1018
1019            // controls ---------------------------------------------
1020
1021            el = new Elem("div", {
1022                id: ids[this.ID.CONTROLS],
1023                className: "yui-picker-controls"
1024            });
1025
1026            p.appendChild(el);
1027            p = el;
1028
1029                // controls header
1030                el = new Elem("div", {
1031                    className: "hd"
1032                });
1033
1034                child = new Elem("a", {
1035                    id: ids[this.ID.CONTROLS_LABEL],
1036                    //className: "yui-picker-controls-label",
1037                    href: "#"
1038                });
1039                el.appendChild(child);
1040                p.appendChild(el);
1041
1042                // bd
1043                el = new Elem("div", {
1044                    className: "bd"
1045                });
1046
1047                p.appendChild(el);
1048                p = el;
1049
1050                    // rgb
1051                    el = new Elem("ul", {
1052                        id: ids[this.ID.RGB_CONTROLS],
1053                        className: "yui-picker-rgb-controls"
1054                    });
1055
1056                    child = new Elem("li");
1057                    child.appendChild(document.createTextNode(txt.R + " "));
1058
1059                    fld = new RGBElem("input", {
1060                        id: ids[this.ID.R],
1061                        className: "yui-picker-r"
1062                    });
1063
1064                    child.appendChild(fld);
1065                    el.appendChild(child);
1066
1067                    child = new Elem("li");
1068                    child.appendChild(document.createTextNode(txt.G + " "));
1069
1070                    fld = new RGBElem("input", {
1071                        id: ids[this.ID.G],
1072                        className: "yui-picker-g"
1073                    });
1074
1075                    child.appendChild(fld);
1076                    el.appendChild(child);
1077
1078                    child = new Elem("li");
1079                    child.appendChild(document.createTextNode(txt.B + " "));
1080
1081                    fld = new RGBElem("input", {
1082                        id: ids[this.ID.B],
1083                        className: "yui-picker-b"
1084                    });
1085
1086                    child.appendChild(fld);
1087                    el.appendChild(child);
1088
1089                    p.appendChild(el);
1090
1091                    // hsv
1092                    el = new Elem("ul", {
1093                        id: ids[this.ID.HSV_CONTROLS],
1094                        className: "yui-picker-hsv-controls"
1095                    });
1096
1097                    child = new Elem("li");
1098                    child.appendChild(document.createTextNode(txt.H + " "));
1099
1100                    fld = new RGBElem("input", {
1101                        id: ids[this.ID.H],
1102                        className: "yui-picker-h"
1103                    });
1104
1105                    child.appendChild(fld);
1106                    child.appendChild(document.createTextNode(" " + txt.DEG));
1107
1108                    el.appendChild(child);
1109
1110                    child = new Elem("li");
1111                    child.appendChild(document.createTextNode(txt.S + " "));
1112
1113                    fld = new RGBElem("input", {
1114                        id: ids[this.ID.S],
1115                        className: "yui-picker-s"
1116                    });
1117
1118                    child.appendChild(fld);
1119                    child.appendChild(document.createTextNode(" " + txt.PERCENT));
1120
1121                    el.appendChild(child);
1122
1123                    child = new Elem("li");
1124                    child.appendChild(document.createTextNode(txt.V + " "));
1125
1126                    fld = new RGBElem("input", {
1127                        id: ids[this.ID.V],
1128                        className: "yui-picker-v"
1129                    });
1130
1131                    child.appendChild(fld);
1132                    child.appendChild(document.createTextNode(" " + txt.PERCENT));
1133
1134                    el.appendChild(child);
1135                    p.appendChild(el);
1136
1137
1138                    // hex summary
1139
1140                    el = new Elem("ul", {
1141                        id: ids[this.ID.HEX_SUMMARY],
1142                        className: "yui-picker-hex_summary"
1143                    });
1144
1145                    child = new Elem("li", {
1146                        id: ids[this.ID.R_HEX]
1147                    });
1148                    el.appendChild(child);
1149
1150                    child = new Elem("li", {
1151                        id: ids[this.ID.G_HEX]
1152                    });
1153                    el.appendChild(child);
1154
1155                    child = new Elem("li", {
1156                        id: ids[this.ID.B_HEX]
1157                    });
1158                    el.appendChild(child);
1159                    p.appendChild(el);
1160
1161                    // hex field
1162                    el = new Elem("div", {
1163                        id: ids[this.ID.HEX_CONTROLS],
1164                        className: "yui-picker-hex-controls"
1165                    });
1166                    el.appendChild(document.createTextNode(txt.HEX + " "));
1167
1168                    child = new RGBElem("input", {
1169                        id: ids[this.ID.HEX],
1170                        className: "yui-picker-hex",
1171                        size: 6,
1172                        maxlength: 6
1173                    });
1174
1175                    el.appendChild(child);
1176                    p.appendChild(el);
1177
1178                    p = this.get("element");
1179
1180                    // swatch
1181                    el = new Elem("div", {
1182                        id: ids[this.ID.SWATCH],
1183                        className: "yui-picker-swatch"
1184                    });
1185
1186                    p.appendChild(el);
1187
1188                    // websafe swatch
1189                    el = new Elem("div", {
1190                        id: ids[this.ID.WEBSAFE_SWATCH],
1191                        className: "yui-picker-websafe-swatch"
1192                    });
1193
1194                    p.appendChild(el);
1195
1196        },
1197
1198        _attachRGBHSV : function(id, config) {
1199            Event.on(this.getElement(id), "keydown", function(e, me) {
1200                    me._rgbFieldKeypress(e, this, config);
1201                }, this);
1202            Event.on(this.getElement(id), "keypress", this._numbersOnly, this, true);
1203            Event.on(this.getElement(id), "blur", function(e, me) {
1204                    me._useFieldValue(e, this, config);
1205                }, this);
1206        },
1207
1208
1209        /**
1210         * Updates the rgb attribute with the current state of the r,g,b
1211         * fields.  This is invoked from change listeners on these
1212         * attributes to facilitate updating these values from the
1213         * individual form fields
1214         * @method _updateRGB
1215         * @protected
1216         */
1217        _updateRGB : function() {
1218            var rgb = [this.get(this.OPT.RED), 
1219                       this.get(this.OPT.GREEN),
1220                       this.get(this.OPT.BLUE)];
1221
1222            this.logger.log("RGB value set to " + rgb);
1223            this.set(this.OPT.RGB, rgb);
1224
1225            this._updateSliders();
1226        },
1227
1228        /**
1229         * Creates any missing DOM structure.
1230         *
1231         * @method _initElements
1232         * @protected
1233         */
1234        _initElements : function () {
1235            // bind all of our elements
1236            var o=this.OPT, 
1237                ids = this.get(o.IDS), 
1238                els = this.get(o.ELEMENTS), 
1239                      i, el, id;
1240
1241            // Add the default value as a key for each element for easier lookup
1242            for (i in this.ID) {
1243                if (lang.hasOwnProperty(this.ID, i)) {
1244                    ids[this.ID[i]] = ids[i];
1245                }
1246            }
1247
1248            // Check for picker element, if not there, create all of them
1249            el = Dom.get(ids[this.ID.PICKER_BG]);
1250            if (!el) {
1251                this._createElements();
1252            } else {
1253                this.logger.log("Using pre-existing markup");
1254            }
1255
1256            for (i in ids) {
1257                if (lang.hasOwnProperty(ids, i)) {
1258                    // look for element
1259                    el = Dom.get(ids[i]);
1260
1261                    // generate an id if the implementer passed in an element reference,
1262                    // and the element did not have an id already
1263                    id = Dom.generateId(el);
1264
1265                    // update the id in case we generated the id
1266                    ids[i] = id; // key is WEBSAFE_SWATCH
1267                    ids[ids[i]] = id; // key is websafe_swatch
1268
1269                    // store the dom ref
1270                    els[id] = el;
1271                }
1272            }
1273
1274        },
1275
1276        /**
1277         * Sets the initial state of the sliders
1278         * @method initPicker
1279         */
1280        initPicker : function () {
1281            this._initSliders();
1282            this._bindUI();
1283            this.syncUI(true);
1284        },
1285
1286        /**
1287         * Creates the Hue and Value/Saturation Sliders.
1288         *
1289         * @method _initSliders
1290         * @protected
1291         */
1292        _initSliders : function () {
1293            var ID = this.ID,
1294                size = this.get(this.OPT.PICKER_SIZE);
1295
1296            this.logger.log("picker size" + size);
1297
1298            this.hueSlider = Slider.getVertSlider(
1299                this.getElement(ID.HUE_BG), 
1300                this.getElement(ID.HUE_THUMB), 0, size);
1301
1302            this.pickerSlider = Slider.getSliderRegion(
1303                this.getElement(ID.PICKER_BG), 
1304                this.getElement(ID.PICKER_THUMB), 0, size, 0, size);
1305
1306            // Apply animate attribute configuration
1307            this.set(this.OPT.ANIMATE, this.get(this.OPT.ANIMATE));
1308        },
1309
1310        /**
1311         * Adds event listeners to Sliders and UI elements.  Wires everything
1312         * up.
1313         *
1314         * @method _bindUI
1315         * @protected
1316         */
1317        _bindUI : function () {
1318            var ID = this.ID,
1319                O  = this.OPT;
1320
1321            this.hueSlider.subscribe("change",
1322                this._onHueSliderChange, this, true);
1323            this.pickerSlider.subscribe("change",
1324                this._onPickerSliderChange, this, true);
1325
1326            Event.on(this.getElement(ID.WEBSAFE_SWATCH), "click", function(e) {
1327                   this.setValue(this.get(O.WEBSAFE));
1328               }, this, true);
1329
1330            Event.on(this.getElement(ID.CONTROLS_LABEL), "click", function(e) {
1331                   this.set(O.SHOW_CONTROLS, !this.get(O.SHOW_CONTROLS));
1332                   Event.preventDefault(e);
1333               }, this, true);
1334
1335            this._attachRGBHSV(ID.R, O.RED); 
1336            this._attachRGBHSV(ID.G, O.GREEN); 
1337            this._attachRGBHSV(ID.B, O.BLUE); 
1338            this._attachRGBHSV(ID.H, O.HUE); 
1339            this._attachRGBHSV(ID.S, O.SATURATION); 
1340            this._attachRGBHSV(ID.V, O.VALUE); 
1341
1342            Event.on(this.getElement(ID.HEX), "keydown", function(e, me) {
1343                    me._hexFieldKeypress(e, this, O.HEX);
1344                }, this);
1345
1346            Event.on(this.getElement(this.ID.HEX), "keypress",
1347                this._hexOnly, this,true);
1348            Event.on(this.getElement(this.ID.HEX), "blur", function(e, me) {
1349                    me._useFieldValue(e, this, O.HEX);
1350                }, this);
1351        },
1352
1353        /**
1354         * Wrapper for _updateRGB, but allows setting 
1355         *
1356         * @method syncUI
1357         * @param skipAnim {Boolean} Omit Slider animation for this action
1358         */
1359        syncUI : function (skipAnim) {
1360            this.skipAnim = skipAnim;
1361            this._updateRGB();
1362            this.skipAnim = false;
1363        },
1364
1365
1366        /**
1367         * Updates the RGB values from the current state of the HSV
1368         * values.  Executed when the one of the HSV form fields are
1369         * updated
1370         * _updateRGBFromHSV
1371         * @protected
1372         */
1373        _updateRGBFromHSV : function() {
1374            var hsv = [this.get(this.OPT.HUE), 
1375                       this.get(this.OPT.SATURATION)/100,
1376                       this.get(this.OPT.VALUE)/100],
1377                rgb = Color.hsv2rgb(hsv);
1378
1379            this.logger.log("HSV converted to RGB " + hsv + " : " + rgb);
1380            this.set(this.OPT.RGB, rgb);
1381
1382            this._updateSliders();
1383        },
1384
1385        /**
1386         * Parses the hex string to normalize shorthand values, converts
1387         * the hex value to rgb and updates the rgb attribute (which
1388         * updates the state for all of the other values)
1389         * method _updateHex
1390         * @protected
1391         */
1392        _updateHex : function() {
1393           
1394            var hex = this.get(this.OPT.HEX),
1395                l   = hex.length,
1396                c,i,rgb;
1397
1398            // support #369 -> #336699 shorthand
1399            if (l === 3) {
1400                c = hex.split("");
1401                for (i=0; i<l; i=i+1) {
1402                    c[i] = c[i] + c[i];
1403                }
1404
1405                hex = c.join("");
1406            }
1407
1408            if (hex.length !== 6) {
1409                this.logger.log(this.get(this.TXT.ILLEGAL_HEX), "error");
1410                return false;
1411            }
1412
1413            rgb = Color.hex2rgb(hex);
1414
1415            this.logger.log(sub("Hex value set to {hex} ({rgb})", {
1416                    hex: hex, rgb: rgb
1417                }));
1418
1419            this.setValue(rgb);
1420        },
1421
1422
1423        /**
1424         * Returns the cached element reference.  If the id is not a string, it
1425         * is assumed that it is an element and this is returned.
1426         * @param id {string|HTMLElement} the element key, id, or ref
1427         * @param on {boolean} hide or show.  If true, show
1428         * @protected
1429         */
1430        _hideShowEl : function(id, on) {
1431            var el = (lang.isString(id) ? this.getElement(id) : id);
1432            Dom.setStyle(el, "display", (on) ? "" : "none");
1433        },
1434
1435
1436        /**
1437         * Sets up the config attributes and the change listeners for this
1438         * properties
1439         * @method initAttributes
1440         * @param attr An object containing default attribute values
1441         */
1442        initAttributes : function(attr) {
1443
1444            attr = attr || {};
1445            ColorPicker.superclass.initAttributes.call(this, attr);
1446            
1447            /**
1448             * The size of the picker. Trying to change this is not recommended.
1449             * @attribute pickersize
1450             * @default 180
1451             * @type int
1452             */
1453            this.setAttributeConfig(this.OPT.PICKER_SIZE, {
1454                    value: attr.size || this.DEFAULT.PICKER_SIZE
1455                });
1456
1457            /**
1458             * The current hue value 0-360
1459             * @attribute hue
1460             * @type int
1461             */
1462            this.setAttributeConfig(this.OPT.HUE, {
1463                    value: attr.hue || 0,
1464                    validator: lang.isNumber
1465                });
1466
1467            /**
1468             * The current saturation value 0-100
1469             * @attribute saturation
1470             * @type int
1471             */
1472            this.setAttributeConfig(this.OPT.SATURATION, {
1473                    value: attr.saturation || 0,
1474                    validator: lang.isNumber
1475                });
1476
1477            /**
1478             * The current value/brightness value 0-100
1479             * @attribute value
1480             * @type int
1481             */
1482            this.setAttributeConfig(this.OPT.VALUE, {
1483                    value: lang.isNumber(attr.value) ? attr.value : 100,
1484                    validator: lang.isNumber
1485                });
1486
1487            /**
1488             * The current red value 0-255
1489             * @attribute red
1490             * @type int
1491             */
1492            this.setAttributeConfig(this.OPT.RED, {
1493                    value: lang.isNumber(attr.red) ? attr.red : 255,
1494                    validator: lang.isNumber
1495                });
1496
1497            /**
1498             * The current green value 0-255
1499             * @attribute green 
1500             * @type int
1501             */
1502            this.setAttributeConfig(this.OPT.GREEN, {
1503                    value: lang.isNumber(attr.green) ? attr.green : 255,
1504                    validator: lang.isNumber
1505                });
1506
1507            /**
1508             * The current blue value 0-255
1509             * @attribute blue
1510             * @type int
1511             */
1512            this.setAttributeConfig(this.OPT.BLUE, {
1513                    value: lang.isNumber(attr.blue) ? attr.blue : 255,
1514                    validator: lang.isNumber
1515                });
1516
1517            /**
1518             * The current hex value #000000-#FFFFFF, without the #
1519             * @attribute hex
1520             * @type string
1521             */
1522            this.setAttributeConfig(this.OPT.HEX, {
1523                    value: attr.hex || "FFFFFF",
1524                    validator: lang.isString
1525                });
1526
1527            /**
1528             * The current rgb value.  Updates the state of all of the
1529             * other value fields.  Read-only: use setValue to set the
1530             * controls rgb value.
1531             * @attribute hex
1532             * @type [int, int, int]
1533             * @readonly
1534             */
1535            this.setAttributeConfig(this.OPT.RGB, {
1536                    value: attr.rgb || [255,255,255],
1537                    method: function(rgb) {
1538
1539                        this.set(this.OPT.RED, rgb[0], true);
1540                        this.set(this.OPT.GREEN, rgb[1], true);
1541                        this.set(this.OPT.BLUE, rgb[2], true);
1542
1543                        var websafe = Color.websafe(rgb),
1544                            hex = Color.rgb2hex(rgb),
1545                            hsv = Color.rgb2hsv(rgb);
1546
1547                        this.set(this.OPT.WEBSAFE, websafe, true);
1548                        this.set(this.OPT.HEX, hex, true);
1549
1550
1551                        this.logger.log(sub("RGB value set to {rgb} (hsv: {hsv})", {
1552    

Large files files are truncated, but you can click here to view the full file