"use strict";

exports.__esModule = true;
exports.checkboxRenderer = checkboxRenderer;
require("core-js/modules/es.array.push.js");
var _baseRenderer = require("../baseRenderer");
var _eventManager = _interopRequireDefault(require("../../eventManager"));
var _element = require("../../helpers/dom/element");
var _mixed = require("../../helpers/mixed");
var _shortcutContexts = require("../../shortcutContexts");
var _pluginHooks = _interopRequireDefault(require("../../pluginHooks"));
var _a11y = require("../../helpers/a11y");
var _constants = require("../../i18n/constants");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const isListeningKeyDownEvent = new WeakMap();
const isCheckboxListenerAdded = new WeakMap();
const BAD_VALUE_CLASS = 'htBadValue';
const ATTR_ROW = 'data-row';
const ATTR_COLUMN = 'data-col';
const SHORTCUTS_GROUP = 'checkboxRenderer';
const RENDERER_TYPE = exports.RENDERER_TYPE = 'checkbox';
_pluginHooks.default.getSingleton().add('modifyAutoColumnSizeSeed', function (bundleSeed, cellMeta, cellValue) {
  const {
    label,
    type,
    row,
    column,
    prop
  } = cellMeta;
  if (type !== RENDERER_TYPE) {
    return;
  }
  if (label) {
    const {
      value: labelValue,
      property: labelProperty
    } = label;
    let labelText = cellValue;
    if (labelValue) {
      labelText = typeof labelValue === 'function' ? labelValue(row, column, prop, cellValue) : labelValue;
    } else if (labelProperty) {
      const labelData = this.getDataAtRowProp(row, labelProperty);
      labelText = labelData !== null ? labelData : cellValue;
    }
    bundleSeed = labelText;
  }
  return bundleSeed;
});
/**
 * Checkbox renderer.
 *
 * @private
 * @param {Core} instance The Handsontable instance.
 * @param {HTMLTableCellElement} TD The rendered cell element.
 * @param {number} row The visual row index.
 * @param {number} col The visual column index.
 * @param {number|string} prop The column property (passed when datasource is an array of objects).
 * @param {*} value The rendered value.
 * @param {object} cellProperties The cell meta object ({@see Core#getCellMeta}).
 */
function checkboxRenderer(instance, TD, row, col, prop, value, cellProperties) {
  const {
    rootDocument
  } = instance;
  const ariaEnabled = instance.getSettings().ariaTags;
  _baseRenderer.baseRenderer.apply(this, [instance, TD, row, col, prop, value, cellProperties]);
  registerEvents(instance);
  let input = createInput(rootDocument);
  const labelOptions = cellProperties.label;
  let badValue = false;
  if (typeof cellProperties.checkedTemplate === 'undefined') {
    cellProperties.checkedTemplate = true;
  }
  if (typeof cellProperties.uncheckedTemplate === 'undefined') {
    cellProperties.uncheckedTemplate = false;
  }
  (0, _element.empty)(TD); // TODO identify under what circumstances this line can be removed

  if (value === cellProperties.checkedTemplate || (0, _mixed.stringify)(value).toLocaleLowerCase(cellProperties.locale) === (0, _mixed.stringify)(cellProperties.checkedTemplate).toLocaleLowerCase(cellProperties.locale)) {
    input.checked = true;
  } else if (value === cellProperties.uncheckedTemplate || (0, _mixed.stringify)(value).toLocaleLowerCase(cellProperties.locale) === (0, _mixed.stringify)(cellProperties.uncheckedTemplate).toLocaleLowerCase(cellProperties.locale)) {
    input.checked = false;
  } else if ((0, _mixed.isEmpty)(value)) {
    // default value
    (0, _element.addClass)(input, 'noValue');
  } else {
    input.style.display = 'none';
    (0, _element.addClass)(input, BAD_VALUE_CLASS);
    badValue = true;
  }
  (0, _element.setAttribute)(input, [[ATTR_ROW, row], [ATTR_COLUMN, col]]);
  if (ariaEnabled) {
    (0, _element.setAttribute)(input, [(0, _a11y.A11Y_LABEL)(input.checked ? instance.getTranslatedPhrase(_constants.CHECKBOX_CHECKED) : instance.getTranslatedPhrase(_constants.CHECKBOX_UNCHECKED)), (0, _a11y.A11Y_CHECKED)(input.checked), (0, _a11y.A11Y_CHECKBOX)()]);
  }
  if (!badValue && labelOptions) {
    let labelText = '';
    if (labelOptions.value) {
      labelText = typeof labelOptions.value === 'function' ? labelOptions.value.call(this, row, col, prop, value) : labelOptions.value;
    } else if (labelOptions.property) {
      const labelValue = instance.getDataAtRowProp(row, labelOptions.property);
      labelText = labelValue !== null ? labelValue : '';
    }
    const label = createLabel(rootDocument, labelText, labelOptions.separated !== true);
    if (labelOptions.position === 'before') {
      if (labelOptions.separated) {
        TD.appendChild(label);
        TD.appendChild(input);
      } else {
        label.appendChild(input);
        input = label;
      }
    } else if (!labelOptions.position || labelOptions.position === 'after') {
      if (labelOptions.separated) {
        TD.appendChild(input);
        TD.appendChild(label);
      } else {
        label.insertBefore(input, label.firstChild);
        input = label;
      }
    }
  }
  if (!labelOptions || labelOptions && !labelOptions.separated) {
    TD.appendChild(input);
  }
  if (badValue) {
    TD.appendChild(rootDocument.createTextNode('#bad-value#'));
  }
  if (!isListeningKeyDownEvent.has(instance)) {
    isListeningKeyDownEvent.set(instance, true);
    registerShortcuts();
  }

  /**
   * Register shortcuts responsible for toggling checkbox state.
   *
   * @private
   */
  function registerShortcuts() {
    const shortcutManager = instance.getShortcutManager();
    const gridContext = shortcutManager.getContext('grid');
    const config = {
      group: SHORTCUTS_GROUP
    };
    gridContext.addShortcuts([{
      keys: [['space']],
      callback: () => {
        changeSelectedCheckboxesState();
        return !areSelectedCheckboxCells(); // False blocks next action associated with the keyboard shortcut.
      }
    }, {
      keys: [['enter']],
      callback: () => {
        changeSelectedCheckboxesState();
        return !areSelectedCheckboxCells(); // False blocks next action associated with the keyboard shortcut.
      },

      runOnlyIf: () => instance.getSettings().enterBeginsEditing
    }, {
      keys: [['delete'], ['backspace']],
      callback: () => {
        changeSelectedCheckboxesState(true);
        return !areSelectedCheckboxCells(); // False blocks next action associated with the keyboard shortcut.
      },

      relativeToGroup: _shortcutContexts.EDITOR_EDIT_GROUP,
      position: 'before'
    }], config);
  }

  /**
   * Change checkbox checked property.
   *
   * @private
   * @param {boolean} [uncheckCheckbox=false] The new "checked" state for the checkbox elements.
   */
  function changeSelectedCheckboxesState() {
    let uncheckCheckbox = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
    const selRange = instance.getSelectedRange();
    if (!selRange) {
      return;
    }
    for (let key = 0; key < selRange.length; key++) {
      const {
        row: startRow,
        col: startColumn
      } = selRange[key].getTopStartCorner();
      const {
        row: endRow,
        col: endColumn
      } = selRange[key].getBottomEndCorner();
      const changes = [];
      for (let visualRow = startRow; visualRow <= endRow; visualRow += 1) {
        for (let visualColumn = startColumn; visualColumn <= endColumn; visualColumn += 1) {
          const cachedCellProperties = instance.getCellMeta(visualRow, visualColumn);
          if (cachedCellProperties.type !== 'checkbox') {
            return;
          }

          /* eslint-disable no-continue */
          if (cachedCellProperties.readOnly === true) {
            continue;
          }
          if (typeof cachedCellProperties.checkedTemplate === 'undefined') {
            cachedCellProperties.checkedTemplate = true;
          }
          if (typeof cachedCellProperties.uncheckedTemplate === 'undefined') {
            cachedCellProperties.uncheckedTemplate = false;
          }
          const dataAtCell = instance.getDataAtCell(visualRow, visualColumn);
          if (uncheckCheckbox === false) {
            if ([cachedCellProperties.checkedTemplate, cachedCellProperties.checkedTemplate.toString()].includes(dataAtCell)) {
              // eslint-disable-line max-len
              changes.push([visualRow, visualColumn, cachedCellProperties.uncheckedTemplate]);
            } else if ([cachedCellProperties.uncheckedTemplate, cachedCellProperties.uncheckedTemplate.toString(), null, void 0].includes(dataAtCell)) {
              // eslint-disable-line max-len
              changes.push([visualRow, visualColumn, cachedCellProperties.checkedTemplate]);
            }
          } else {
            changes.push([visualRow, visualColumn, cachedCellProperties.uncheckedTemplate]);
          }
        }
      }
      if (changes.length > 0) {
        instance.setDataAtCell(changes);
      }
    }
  }

  /**
   * Check whether all selected cells are with checkbox type.
   *
   * @returns {boolean}
   * @private
   */
  function areSelectedCheckboxCells() {
    const selRange = instance.getSelectedRange();
    if (!selRange) {
      return;
    }
    for (let key = 0; key < selRange.length; key++) {
      const topLeft = selRange[key].getTopStartCorner();
      const bottomRight = selRange[key].getBottomEndCorner();
      for (let visualRow = topLeft.row; visualRow <= bottomRight.row; visualRow++) {
        for (let visualColumn = topLeft.col; visualColumn <= bottomRight.col; visualColumn++) {
          const cachedCellProperties = instance.getCellMeta(visualRow, visualColumn);
          if (cachedCellProperties.type !== 'checkbox') {
            return false;
          }
          const cell = instance.getCell(visualRow, visualColumn);
          if (cell === null || cell === void 0) {
            return true;
          } else {
            const checkboxes = cell.querySelectorAll('input[type=checkbox]');
            if (checkboxes.length > 0 && !cachedCellProperties.readOnly) {
              return true;
            }
          }
        }
      }
    }
    return false;
  }
}
checkboxRenderer.RENDERER_TYPE = RENDERER_TYPE;

/**
 * Register checkbox listeners.
 *
 * @param {Core} instance The Handsontable instance.
 * @returns {EventManager}
 */
function registerEvents(instance) {
  let eventManager = isCheckboxListenerAdded.get(instance);
  if (!eventManager) {
    const {
      rootElement
    } = instance;
    eventManager = new _eventManager.default(instance);
    eventManager.addEventListener(rootElement, 'click', event => onClick(event, instance));
    eventManager.addEventListener(rootElement, 'mouseup', event => onMouseUp(event, instance));
    eventManager.addEventListener(rootElement, 'change', event => onChange(event, instance));
    isCheckboxListenerAdded.set(instance, eventManager);
  }
  return eventManager;
}

/**
 * Create input element.
 *
 * @param {Document} rootDocument The document owner.
 * @returns {Node}
 */
function createInput(rootDocument) {
  const input = rootDocument.createElement('input');
  input.className = 'htCheckboxRendererInput';
  input.type = 'checkbox';
  input.setAttribute('autocomplete', 'off');
  input.setAttribute('tabindex', '-1');
  return input.cloneNode(false);
}

/**
 * Create label element.
 *
 * @param {Document} rootDocument The document owner.
 * @param {string} text The label text.
 * @param {boolean} fullWidth Determines whether label should have full width.
 * @returns {Node}
 */
function createLabel(rootDocument, text, fullWidth) {
  const label = rootDocument.createElement('label');
  label.className = `htCheckboxRendererLabel ${fullWidth ? 'fullWidth' : ''}`;
  label.appendChild(rootDocument.createTextNode(text));
  return label.cloneNode(true);
}

/**
 * `mouseup` callback.
 *
 * @private
 * @param {Event} event `mouseup` event.
 * @param {Core} instance The Handsontable instance.
 */
function onMouseUp(event, instance) {
  const {
    target
  } = event;
  if (!isCheckboxInput(target)) {
    return;
  }
  if (!target.hasAttribute(ATTR_ROW) || !target.hasAttribute(ATTR_COLUMN)) {
    return;
  }
  setTimeout(instance.listen, 10);
}

/**
 * `click` callback.
 *
 * @private
 * @param {MouseEvent} event `click` event.
 * @param {Core} instance The Handsontable instance.
 */
function onClick(event, instance) {
  const {
    target
  } = event;
  if (!isCheckboxInput(target)) {
    return;
  }
  if (!target.hasAttribute(ATTR_ROW) || !target.hasAttribute(ATTR_COLUMN)) {
    return;
  }
  const row = parseInt(target.getAttribute(ATTR_ROW), 10);
  const col = parseInt(target.getAttribute(ATTR_COLUMN), 10);
  const cellProperties = instance.getCellMeta(row, col);
  if (cellProperties.readOnly) {
    event.preventDefault();
  }
}

/**
 * `change` callback.
 *
 * @param {Event} event `change` event.
 * @param {Core} instance The Handsontable instance.
 */
function onChange(event, instance) {
  const {
    target
  } = event;
  if (!isCheckboxInput(target)) {
    return;
  }
  if (!target.hasAttribute(ATTR_ROW) || !target.hasAttribute(ATTR_COLUMN)) {
    return;
  }
  const row = parseInt(target.getAttribute(ATTR_ROW), 10);
  const col = parseInt(target.getAttribute(ATTR_COLUMN), 10);
  const cellProperties = instance.getCellMeta(row, col);
  if (!cellProperties.readOnly) {
    let newCheckboxValue = null;
    if (event.target.checked) {
      newCheckboxValue = cellProperties.uncheckedTemplate === void 0 ? true : cellProperties.checkedTemplate;
    } else {
      newCheckboxValue = cellProperties.uncheckedTemplate === void 0 ? false : cellProperties.uncheckedTemplate;
    }
    instance.setDataAtCell(row, col, newCheckboxValue);
  }
}

/**
 * Check if the provided element is the checkbox input.
 *
 * @private
 * @param {HTMLElement} element The element in question.
 * @returns {boolean}
 */
function isCheckboxInput(element) {
  return element.tagName === 'INPUT' && element.getAttribute('type') === 'checkbox';
}