Home | History | Annotate | Download | only in resources
      1 "use strict";
      2 /*
      3  * Copyright (C) 2012 Google Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      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  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     17  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     21  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     23  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 var global = {
     27     argumentsReceived: false,
     28     params: null
     29 };
     30 
     31 /**
     32  * @param {Event} event
     33  */
     34 function handleMessage(event) {
     35     initialize(JSON.parse(event.data));
     36     global.argumentsReceived = true;
     37 }
     38 
     39 /**
     40  * @param {!Object} args
     41  */
     42 function initialize(args) { 
     43     global.params = args;
     44     var main = $("main");
     45     main.innerHTML = "";
     46     var errorString = validateArguments(args);
     47     if (errorString) {
     48         main.textContent = "Internal error: " + errorString;
     49         resizeWindow(main.offsetWidth, main.offsetHeight);
     50     } else
     51         new ColorPicker(main, args);
     52 }
     53 
     54 // The DefaultColorPalette is used when the list of values are empty. 
     55 var DefaultColorPalette = ["#000000", "#404040", "#808080", "#c0c0c0",
     56     "#ffffff", "#980000", "#ff0000", "#ff9900", "#ffff00", "#00ff00", "#00ffff",
     57     "#4a86e8", "#0000ff", "#9900ff", "#ff00ff"];
     58 
     59 function handleArgumentsTimeout() {
     60     if (global.argumentsReceived)
     61         return;
     62     var args = {
     63         values : DefaultColorPalette,
     64         otherColorLabel: "Other..."
     65     };
     66     initialize(args);
     67 }
     68 
     69 /**
     70  * @param {!Object} args
     71  * @return {?string} An error message, or null if the argument has no errors.
     72  */
     73 function validateArguments(args) {
     74     if (!args.values)
     75         return "No values.";
     76     if (!args.otherColorLabel)
     77         return "No otherColorLabel.";
     78     return null;
     79 }
     80 
     81 function ColorPicker(element, config) {
     82     Picker.call(this, element, config);
     83     this._config = config;
     84     if (this._config.values.length === 0)
     85         this._config.values = DefaultColorPalette;
     86     this._container = null;
     87     this._layout();
     88     document.body.addEventListener("keydown", this._handleKeyDown.bind(this));
     89     this._element.addEventListener("mousemove", this._handleMouseMove.bind(this));
     90     this._element.addEventListener("mousedown", this._handleMouseDown.bind(this));
     91 }
     92 ColorPicker.prototype = Object.create(Picker.prototype);
     93 
     94 var SwatchBorderBoxWidth = 24; // keep in sync with CSS
     95 var SwatchBorderBoxHeight = 24; // keep in sync with CSS
     96 var SwatchesPerRow = 5;
     97 var SwatchesMaxRow = 4;
     98 
     99 ColorPicker.prototype._layout = function() {
    100     var container = createElement("div", "color-swatch-container");
    101     container.addEventListener("click", this._handleSwatchClick.bind(this), false);
    102     for (var i = 0; i < this._config.values.length; ++i) {
    103         var swatch = createElement("button", "color-swatch");
    104         swatch.dataset.index = i;
    105         swatch.dataset.value = this._config.values[i];
    106         swatch.title = this._config.values[i];
    107         swatch.style.backgroundColor = this._config.values[i];
    108         container.appendChild(swatch);
    109     }
    110     var containerWidth = SwatchBorderBoxWidth * SwatchesPerRow;
    111     if (this._config.values.length > SwatchesPerRow * SwatchesMaxRow)
    112         containerWidth += getScrollbarWidth();
    113     container.style.width = containerWidth + "px";
    114     container.style.maxHeight = (SwatchBorderBoxHeight * SwatchesMaxRow) + "px";
    115     this._element.appendChild(container);
    116     var otherButton = createElement("button", "other-color", this._config.otherColorLabel);
    117     otherButton.addEventListener("click", this.chooseOtherColor.bind(this), false);
    118     this._element.appendChild(otherButton);
    119     this._container = container;
    120     this._otherButton = otherButton;
    121     var elementWidth = this._element.offsetWidth;
    122     var elementHeight = this._element.offsetHeight;
    123     resizeWindow(elementWidth, elementHeight);
    124 };
    125 
    126 ColorPicker.prototype.selectColorAtIndex = function(index) {
    127     index = Math.max(Math.min(this._container.childNodes.length - 1, index), 0);
    128     this._container.childNodes[index].focus();
    129 };
    130 
    131 ColorPicker.prototype._handleMouseMove = function(event) {
    132     if (event.target.classList.contains("color-swatch"))
    133         event.target.focus();
    134 };
    135 
    136 ColorPicker.prototype._handleMouseDown = function(event) {
    137     // Prevent blur.
    138     if (event.target.classList.contains("color-swatch"))
    139         event.preventDefault();
    140 };
    141 
    142 ColorPicker.prototype._handleKeyDown = function(event) {
    143     var key = event.keyIdentifier;
    144     if (key === "U+001B") // ESC
    145         this.handleCancel();
    146     else if (key == "Left" || key == "Up" || key == "Right" || key == "Down") {
    147         var selectedElement = document.activeElement;
    148         var index = 0;
    149         if (selectedElement.classList.contains("other-color")) {
    150             if (key != "Right" && key != "Up")
    151                 return;
    152             index = this._container.childNodes.length - 1;
    153         } else if (selectedElement.classList.contains("color-swatch")) {
    154             index = parseInt(selectedElement.dataset.index, 10);
    155             switch (key) {
    156             case "Left":
    157                 index--;
    158                 break;
    159             case "Right":
    160                 index++;
    161                 break;
    162             case "Up":
    163                 index -= SwatchesPerRow;
    164                 break;
    165             case "Down":
    166                 index += SwatchesPerRow;
    167                 break;
    168             }
    169             if (index > this._container.childNodes.length - 1) {
    170                 this._otherButton.focus();
    171                 return;
    172             }
    173         }
    174         this.selectColorAtIndex(index);
    175     }
    176     event.preventDefault();
    177 };
    178 
    179 ColorPicker.prototype._handleSwatchClick = function(event) {
    180     if (event.target.classList.contains("color-swatch"))
    181         this.submitValue(event.target.dataset.value);
    182 };
    183 
    184 if (window.dialogArguments) {
    185     initialize(dialogArguments);
    186 } else {
    187     window.addEventListener("message", handleMessage, false);
    188     window.setTimeout(handleArgumentsTimeout, 1000);
    189 }
    190