Your IP : 216.73.216.79


Current Path : /var/www/html/reddsis/node_modules/grapick/src/
Upload File :
Current File : /var/www/html/reddsis/node_modules/grapick/src/Grapick.js

import EventEmitter from './emitter'
import Handler from './Handler'
import { on } from './utils'

const comparator = (l, r) => {
  return l.position - r.position;
}

const typeName = name => `${name}-gradient(`;

/**
 * Main Grapick class
 * @extends EventEmitter
 */
export default class Grapick extends EventEmitter {

  constructor(options = {}) {
    super();
    const pfx = 'grp';
    options = Object.assign({}, options);
    const defaults = {
      // Class prefix
      pfx,

      // HTMLElement/query string on which the gradient input will be attached
      el: `.${pfx}`,

      // Element to use for the custom color picker, eg. '<div class="my-custom-el"></div>'
      // Will be added inside the color picker container
      colorEl: '',

      // Minimum handler position, default: 0
      min: 0,

      // Maximum handler position, default: 100
      max: 100,

      // Any supported gradient direction: '90deg' (default), 'top', 'bottom', 'right', '135deg', etc.
      direction: '90deg',

      // Gradient type, available options: 'linear' (default) | 'radial' | 'repeating-linear' | 'repeating-radial'
      type: 'linear',

      // Gradient input height, default: '30px'
      height: '30px',

      // Gradient input width, default: '100%'
      width: '100%',

      // Default empty color (when you click on an empty color picker)
      emptyColor: '#000',

      // Format handler position value, default (to avoid floats): val => parseInt(val)
      onValuePos: val => parseInt(val),
    };

    for (let name in defaults) {
      if (!(name in options))
        options[name] = defaults[name];
    }

    let el = options.el;
    el = typeof el == 'string' ? document.querySelector(el) : el;

    if (!(el instanceof HTMLElement)) {
      throw `Element not found, given ${el}`;
    }

    this.el = el;
    this.handlers = [];
    this.options = options;
    this.on('handler:color:change', (h, c) => this.change(c));
    this.on('handler:position:change', (h, c) => this.change(c));
    this.on('handler:remove', h => this.change(1));
    this.on('handler:add', h => this.change(1));
    this.render();
  }

  /**
   * Destroy Grapick
   */
  destroy() {
    this.clear();
    this.e = {}; // Clear all events;
    [ // Clear the state
      'el', 'handlers', 'options', 'colorPicker',
    ].forEach(i => this[i] = 0);
    [ // Remove DOM elements
      'previewEl', 'wrapperEl', 'sandEl',
    ].forEach(key => {
      const el = this[key];
      el && el.parentNode && el.parentNode.removeChild(el);
      delete this[key];
    });
  }


  /**
   * Set custom color picker
   * @param {Object} cp Color picker interface
   * @example
   * const gp = new Grapick({
   *  el: '#gp',
   *  colorEl: '<input id="colorpicker"/>'
   * });
   * gp.setColorPicker(handler => {
   *    const colorEl = handler.getEl().querySelector('#colorpicker');
   *
   *    // Or you might face something like this
   *    colorPicker({
   *      el: colorEl,
   *      startColoer: handler.getColor(),
   *      change(color) {
   *        handler.setColor(color);
   *      }
   *    });
   *
   *    // jQuery style color picker
   *    $(colorEl).colorPicker2({...}).on('change', () => {
   *      handler.setColor(this.value);
   *    })
   *
   *    // In order to avoid memory leaks, return a function and call there
   *    // the destroy method of you color picker instance
   *    return () => {
   *      // destroy your color picker instance
   *    }
   * })
   */
  setColorPicker(cp) {
    this.colorPicker = cp;
  }


  /**
   * Get the complete style value
   * @return {string}
   * @example
   * const ga = new Grapick({...});
   * ga.addHandler(0, '#000');
   * ga.addHandler(55, 'white');
   * console.log(ga.getValue());
   * // -> `linear-gradient(left, #000 0%, white 55%)`
   */
  getValue(type, angle) {
    const color = this.getColorValue();
    const tp = type || this.getType();
    const defDir = ['top', 'left', 'bottom', 'right', 'center'];
    let ang = angle || this.getDirection();

    if (
      ['linear', 'repeating-linear'].indexOf(tp) >= 0
      && defDir.indexOf(ang) >= 0
    ) {
      ang = ang === 'center' ? 'to right' : `to ${ang}`;
    }

    if (
      ['radial', 'repeating-radial'].indexOf(tp) >= 0
      && defDir.indexOf(ang) >= 0
    ) {
      ang = `circle at ${ang}`;
    }

    return color ? `${tp}-gradient(${ang}, ${color})` : '';
  }


  /**
   * Get the gradient value with the browser prefix if necessary.
   * The usage of this method is deprecated (noticed weird behaviors in modern browsers).
   * @return {string}
   * @deprecated
   */
  getSafeValue(type, angle) {
    const previewEl = this.previewEl;
    const value = this.getValue(type, angle);
    !this.sandEl && (this.sandEl = document.createElement('div'))

    if (!previewEl || !value) {
      return '';
    }

    const style = this.sandEl.style;
    const values = [value, ...this.getPrefixedValues(type, angle)];
    let val;

    for (let i = 0; i < values.length; i++) {
      val = values[i];
      style.backgroundImage = val;

      if (style.backgroundImage == val) {
          break;
      }
    }

    return style.backgroundImage;
  }


   /**
    * Parse and apply the value to the picker
    * @param {string} value Any valid gradient string
    * @param {Object} [options={}] Options
    * @param {Boolean} [options.silent] Don't trigger events
    * @example
    * ga.setValue('linear-gradient(90deg, rgba(18, 215, 151, 0.75) 31.25%, white 85.1562%)');
    * ga.setValue('-webkit-radial-gradient(left, red 10%, blue 85%)');
    */
  setValue(value = '', options = {}) {
    let type = this.type;
    let direction = this.direction;
    let start = value.indexOf('(') + 1;
    let end = value.lastIndexOf(')');
    let gradients = value.substring(start, end);
    const values = gradients.split(/,(?![^(]*\)) /);
    this.clear(options);

    if (!gradients) {
      this.updatePreview();
      return;
    }

    if (values.length > 2) {
      direction = values.shift();
    }

    let typeFound;
    const types = ['repeating-linear', 'repeating-radial', 'linear', 'radial'];
    types.forEach(name => {
      if (value.indexOf(typeName(name)) > -1 && !typeFound) {
        typeFound = 1;
        type = name;
      }
    })

    this.setDirection(direction, options);
    this.setType(type, options);
    values.forEach(value => {
      const hdlValues = value.split(' ');
      const position = parseFloat(hdlValues.pop());
      const color = hdlValues.join('');
      this.addHandler(position, color, 0, options);
    })
    this.updatePreview();
  }


  /**
   * Get only colors value
   * @return {string}
   * @example
   * const ga = new Grapick({...});
   * ga.addHandler(0, '#000');
   * ga.addHandler(55, 'white');
   * console.log(ga.getColorValue());
   * // -> `#000 0%, white 55%`
   */
  getColorValue() {
    let handlers = this.handlers;
    handlers.sort(comparator);
    handlers = handlers.length == 1 ? [handlers[0], handlers[0]] : handlers;
    return handlers.map(handler => handler.getValue()).join(', ');
  }


  /**
   * Get an array with browser specific values
   * @return {Array}
   * @example
   * const ga = new Grapick({...});
   * ga.addHandler(0, '#000');
   * ga.addHandler(55, 'white');
   * console.log(ga.getPrefixedValues());
   * // -> [
   *  "-moz-linear-gradient(left, #000 0%, white 55%)",
   *  "-webkit-linear-gradient(left, #000 0%, white 55%)"
   *  "-o-linear-gradient(left, #000 0%, white 55%)"
   * ]
   */
  getPrefixedValues(type, angle) {
    const value = this.getValue(type, angle);
    return ['-moz-', '-webkit-', '-o-', '-ms-'].map(prefix =>
      `${prefix}${value}`);
  }


  /**
   * Trigger change
   * @param {Boolean} complete Indicates if the change is complete (eg. while dragging is not complete)
   * @param {Object} [options={}] Options
   * @param {Boolean} [options.silent] Don't trigger events
   */
  change(complete = 1, options = {}) {
    this.updatePreview();
    !options.silent && this.emit('change', complete);
    // TODO can't make it work with jsdom
    //timerChange && clearTimeout(timerChange);
    //timerChange = setTimeout(() => this.emit('change', complete), 0);
  }


  /**
   * Set gradient direction, eg. 'top', 'left', 'bottom', 'right', '90deg', etc.
   * @param {string} direction Any supported direction
   * @param {Object} [options={}] Options
   * @param {Boolean} [options.silent] Don't trigger events
   */
  setDirection(direction, options = {}) {
    this.options.direction = direction;
    const { complete = 1 } = options;
    this.change(complete, options);
  }


  /**
   * Set gradient direction, eg. 'top', 'left', 'bottom', 'right', '90deg', etc.
   * @param {string} direction Any supported direction
   */
  getDirection() {
    return this.options.direction;
  }


  /**
   * Set gradient type, available options: 'linear' or 'radial'
   * @param {string} direction Any supported direction
   * @param {Object} [options={}] Options
   * @param {Boolean} [options.silent] Don't trigger events
   */
  setType(type, options = {}) {
    this.options.type = type;
    const { complete = 1 } = options;
    this.change(complete, options);
  }


  /**
   * Get gradient type
   * @return {string}
   */
  getType() {
    return this.options.type;
  }


  /**
   * Add gradient handler
   * @param {integer} position Position integer in percentage
   * @param {string} color Color string, eg. red, #123, 'rgba(30,87,153,1)', etc..
   * @param {Boolean} select Select the handler once it's added
   * @param {Object} [options={}] Handler options
   * @param {Boolean} [options.silent] Don't trigger events
   * @return {Object} Handler object
   */
  addHandler(position, color, select = 1, options = {}) {
    const handler = new Handler(this, position, color, select, options);
    !options.silent && this.emit('handler:add', handler);
    return handler;
  }


  /**
   * Get handler by index
   * @param  {integer} index
   * @return {Object}
   */
  getHandler(index) {
    return this.handlers[index];
  }


  /**
   * Get all handlers
   * @return {Array}
   */
  getHandlers() {
    return this.handlers;
  }


  /**
   * Remove all handlers
   * @param {Object} [options={}] Options
   * @param {Boolean} [options.silent] Don't trigger events
   * @example
   * ga.clear();
   * // Don't trigger events
   * ga.clear({silent: 1});
   */
  clear(options = {}) {
    const handlers = this.handlers;

    for (var i = handlers.length - 1; i >= 0; i--) {
       handlers[i].remove(options);
    }
  }


  /**
   * Return selected handler if one exists
   * @return {Handler|null}
   */
  getSelected() {
    const handlers = this.getHandlers();

    for (let i = 0; i < handlers.length; i++) {
      let handler = handlers[i];

      if (handler.isSelected()) {
        return handler;
      }
    }

    return null;
  }


  /**
   * Update preview element
   */
  updatePreview() {
    const previewEl = this.previewEl;
    previewEl && (previewEl.style.backgroundImage = this.getValue('linear', 'to right'));
  }


  initEvents() {
    const pEl = this.previewEl;
    pEl && on(pEl, 'click', e => {
      // First of all, find a position of the click in percentage
      const opt = this.options;
      const { min, max } = opt;
      const elDim = {
        w: pEl.clientWidth,
        h: pEl.clientHeight,
      };
      const x = e.offsetX - pEl.clientLeft;
      const y = e.offsetY - pEl.clientTop;
      const percentage = x / elDim.w * 100;

      if (
        percentage > max || percentage < min ||
        y > elDim.h || y < 0
      ) {
        return;
      }

      // Now let's find the pixel color by using the canvas
      let canvas = document.createElement('canvas');
      let context = canvas.getContext('2d');
      canvas.width = elDim.w;
      canvas.height = elDim.h;
      let grad = context.createLinearGradient(0, 0, elDim.w, elDim.h);
      this.getHandlers().forEach(h => grad.addColorStop(h.position/100, h.color));
      context.fillStyle = grad;
      context.fillRect(0, 0, canvas.width, canvas.height);
      canvas.style.background = 'black';
      const rgba = canvas.getContext('2d').getImageData(x, y, 1, 1).data;
      const color = `rgba(${rgba[0]}, ${rgba[1]}, ${rgba[2]}, ${rgba[3]})`;
      const fc = color == 'rgba(0, 0, 0, 0)' ? opt.emptyColor : color;
      this.addHandler(percentage, fc);
    });
  }


  /**
   * Render the gradient picker
   */
  render() {
    const opt = this.options;
    const el = this.el;
    const pfx = opt.pfx;
    const height = opt.height;
    const width = opt.width;

    if (!el) {
      return;
    }

    const wrapperCls = `${pfx}-wrapper`;
    const previewCls = `${pfx}-preview`;
    el.innerHTML = `
      <div class="${wrapperCls}">
        <div class="${previewCls}"></div>
      </div>
    `;
    const wrapperEl = el.querySelector(`.${wrapperCls}`);
    const previewEl = el.querySelector(`.${previewCls}`);
    const styleWrap = wrapperEl.style;
    styleWrap.position = 'relative';
    this.wrapperEl = wrapperEl;
    this.previewEl = previewEl;

    if (height) {
      styleWrap.height = height;
    }

    if (width) {
      styleWrap.width = width;
    }

    this.initEvents();
    this.updatePreview();
  }
}