chromium/third_party/blink/renderer/core/html/forms/resources/color_suggestion_picker.js

'use strict';
/*
 * Copyright (C) 2012 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

function initializeColorSuggestionPicker() {
  new ColorSuggestionPicker(main, global.params);
}

/**
 * @param {!Object} args
 * @return {?string} An error message, or null if the argument has no errors.
 */
function validateColorSuggestionPickerArguments(args) {
  if (!args.shouldShowColorSuggestionPicker)
    return 'Should not be showing the color suggestion picker.';
  if (!args.values)
    return 'No values.';
  if (!args.otherColorLabel)
    return 'No otherColorLabel.';
  return null;
}

function handleArgumentsTimeout() {
  if (global.argumentsReceived)
    return;
  const args = {values: DefaultColorPalette, otherColorLabel: 'Other...'};
  initialize(args);
}

class ColorSuggestionPicker extends Picker {
  constructor(element, config) {
    super(element, config);
    if (this.config_.values.length === 0)
      this.config_.values = DefaultColorPalette;
    this.container_ = null;
    this.layout_();
    document.body.addEventListener('keydown', this.handleKeyDown_.bind(this));
    this.element_.addEventListener(
        'mousemove', this.handleMouseMove_.bind(this));
    this.element_.addEventListener(
        'mousedown', this.handleMouseDown_.bind(this));
  }

  get getColorSwatchWidth_() {
    return Number(window.getComputedStyle(document.body)
                      .getPropertyValue('--color-swatch-width')
                      .replace('px', ''));
  }

  get getColorSwatchHeight_() {
    return Number(window.getComputedStyle(document.body)
                      .getPropertyValue('--color-swatch-height')
                      .replace('px', ''));
  }

  get getColorSwatchPadding_() {
    return Number(window.getComputedStyle(document.body)
                      .getPropertyValue('--color-swatch-padding')
                      .replace('px', ''));
  }

  get getColorSwatchBorderWidth_() {
    return Number(window.getComputedStyle(document.body)
                      .getPropertyValue('--color-swatch-border-width')
                      .replace('px', ''));
  }

  get getColorSwatchMargin_() {
    return Number(window.getComputedStyle(document.body)
                      .getPropertyValue('--color-swatch-margin')
                      .replace('px', ''));
  }

  get getSwatchBorderBoxWidth() {
    return (
        this.getColorSwatchWidth_ + this.getColorSwatchPadding_ * 2 +
        this.getColorSwatchBorderWidth_ * 2 + this.getColorSwatchMargin_ * 2);
  }

  get getSwatchBorderBoxHeight() {
    return (
        this.getColorSwatchHeight_ + this.getColorSwatchPadding_ * 2 +
        this.getColorSwatchBorderWidth_ * 2 + this.getColorSwatchMargin_ * 2);
  }

  get getSwatchesPerRow() {
    return 5;
  }

  get getSwatchesMaxRow() {
    return 3;
  }

  get getScrollbarWidth() {
    return Number(window.getComputedStyle(document.body)
                      .getPropertyValue('--scrollbar-width')
                      .replace('px', ''));
  }

  layout_() {
    const container = createElement('div', 'color-swatch-container');
    container.addEventListener(
        'click', this.handleSwatchClick_.bind(this), false);
    for (let i = 0; i < this.config_.values.length; ++i) {
      const swatch = createElement('button', 'color-swatch');
      swatch.dataset.index = i;
      swatch.dataset.value = this.config_.values[i];
      swatch.title = this.config_.values[i];
      swatch.style.backgroundColor = this.config_.values[i];
      container.appendChild(swatch);
    }
    let containerWidth = this.getSwatchBorderBoxWidth * this.getSwatchesPerRow;
    if (this.config_.values.length >
        this.getSwatchesPerRow * this.getSwatchesMaxRow)
      containerWidth += this.getScrollbarWidth;
    container.style.width = containerWidth + 'px';
    container.style.maxHeight =
        this.getSwatchBorderBoxHeight* this.getSwatchesMaxRow + 'px';
    this.element_.appendChild(container);
    const otherButton =
        createElement('button', 'other-color', this.config_.otherColorLabel);
    otherButton.addEventListener(
        'click', this.onOtherButtonClick_.bind(this), false);
    this.element_.appendChild(otherButton);
    this.container_ = container;
    this.otherButton_ = otherButton;
    const elementWidth = this.element_.offsetWidth;
    const elementHeight = this.element_.offsetHeight;
    resizeWindow(elementWidth, elementHeight);
  }

  onOtherButtonClick_() {
    const main = $('main');
    main.innerHTML = '';
    main.classList.remove('color-suggestion-picker-main');
    main.classList.add('color-picker-main');
    // Replace document.body with a deep clone to drop all event listeners.
    const oldBody = document.body;
    const newBody = oldBody.cloneNode(true);
    oldBody.parentElement.replaceChild(newBody, oldBody);
    initializeColorPicker();
  }

  selectColorAtIndex_(index) {
    index = Math.max(Math.min(this.container_.childNodes.length - 1, index), 0);
    this.container_.childNodes[index].focus();
  }

  handleMouseMove_(event) {
    if (event.target.classList.contains('color-swatch'))
      event.target.focus();
  }

  handleMouseDown_(event) {
    // Prevent blur.
    if (event.target.classList.contains('color-swatch'))
      event.preventDefault();
  }

  handleKeyDown_(event) {
    const key = event.key;
    if (key === 'Escape')
      this.handleCancel();
    else if (
        key == 'ArrowLeft' || key == 'ArrowUp' || key == 'ArrowRight' ||
        key == 'ArrowDown') {
      const selectedElement = document.activeElement;
      let index = 0;
      if (selectedElement.classList.contains('other-color')) {
        if (key != 'ArrowRight' && key != 'ArrowUp')
          return;
        index = this.container_.childNodes.length - 1;
      } else if (selectedElement.classList.contains('color-swatch')) {
        index = parseInt(selectedElement.dataset.index, 10);
        switch (key) {
          case 'ArrowLeft':
            index--;
            break;
          case 'ArrowRight':
            index++;
            break;
          case 'ArrowUp':
            index -= this.getSwatchesPerRow;
            break;
          case 'ArrowDown':
            index += this.getSwatchesPerRow;
            break;
        }
        if (index > this.container_.childNodes.length - 1) {
          this.otherButton_.focus();
          return;
        }
      }
      this.selectColorAtIndex_(index);
    }
    event.preventDefault();
  }

  handleSwatchClick_(event) {
    if (event.target.classList.contains('color-swatch'))
      this.submitValue(event.target.dataset.value);
  }
}