<commit_before>/* eslint-disable no-underscore-dangle */

/* global $ */

import { escapeHtml as h } from '../../../utils/text';

class ActionButton {
  constructor(container, iconName, label, opts) {
    this.dom = $(`
      <button type="button" class="c-sf-block__actions__single" title="${h(label)}">
        <i class="icon icon-${h(iconName)}" aria-hidden="true"></i>
      </button>
    `);
    $(container).append(this.dom);

    if (opts?.disabled) {
      this.disable();
    }

    if (opts?.onClick) {
      this.dom.click(() => {
        opts.onClick();
        return false;  // don't propagate to header's onclick event (which collapses the block)
      });
    }
  }

  enable() {
    this.dom.removeAttr('disabled');
  }
  disable() {
    this.dom.attr('disabled', 'true');
  }
}

export class BaseSequenceChild {
  constructor(blockDef, placeholder, prefix, index, id, initialState, sequence, opts) {
    this.blockDef = blockDef;
    this.type = blockDef.name;
    this.prefix = prefix;
    this.index = index;
    this.id = id;
    this.sequence = sequence;

    const animate = opts && opts.animate;
    this.collapsed = opts && opts.collapsed;
    const strings = (opts && opts.strings) || {};

    const dom = $(`
      <div aria-hidden="false" ${this.id ? `data-contentpath="${h(this.id)}"` : 'data-contentpath-disabled'}>
        <input type="hidden"  name="${this.prefix}-deleted" value="">
        <input type="hidden" name="${this.prefix}-order" value="${index}">
        <input type="hidden" name="${this.prefix}-type" value="${h(this.type || '')}">
        <input type="hidden" name="${this.prefix}-id" value="${h(this.id || '')}">

        <div>
          <div class="c-sf-container__block-container">
            <div class="c-sf-block">
              <div data-block-header class="c-sf-block__header c-sf-block__header--collapsible">
                <span class="c-sf-block__header__icon">
                  <i class="icon icon-${h(this.blockDef.meta.icon)}"></i>
                </span>
                <h3 data-block-title class="c-sf-block__header__title"></h3>
                <div class="c-sf-block__actions" data-block-actions>
                  <span class="c-sf-block__type">${h(this.blockDef.meta.label)}</span>
                </div>
              </div>
              <div data-block-content class="c-sf-block__content" aria-hidden="false">
                <div class="c-sf-block__content-inner">
                  <div data-streamfield-block></div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    `);

    $(placeholder).replaceWith(dom);
    this.element = dom.get(0);
    const blockElement = dom.find('[data-streamfield-block]').get(0);
    const actionsContainer = dom.find('[data-block-actions]').get(0);
    this.titleElement = dom.find('[data-block-title]');
    this.contentElement = dom.find('[data-block-content]');
    this.deletedInput = dom.find(`input[name="${this.prefix}-deleted"]`);
    this.indexInput = dom.find(`input[name="${this.prefix}-order"]`);

    dom.find('[data-block-header]').click(() => {
      this.toggleCollapsedState();
    });

    this.moveUpButton = new ActionButton(actionsContainer, 'arrow-up', strings.MOVE_UP, {
      disabled: true,
      onClick: () => {
        this.sequence.moveBlockUp(this.index);
      }
    });

    this.moveDownButton = new ActionButton(actionsContainer, 'arrow-down', strings.MOVE_DOWN, {
      disabled: true,
      onClick: () => {
        this.sequence.moveBlockDown(this.index);
      }
    });

    this.duplicateButton = new ActionButton(actionsContainer, 'duplicate', strings.DUPLICATE, {
      onClick: () => {
        this.sequence.duplicateBlock(this.index, { animate: true });
      }
    });

    this.deleteButton = new ActionButton(actionsContainer, 'bin', strings.DELETE, {
      onClick: () => {
        this.sequence.deleteBlock(this.index, { animate: true });
      }
    });

    this.block = this.blockDef.render(blockElement, this.prefix + '-value', initialState);

    if (this.collapsed) {
      this.collapse();
    }

    if (animate) {
      dom.hide();
      setTimeout(() => {
        dom.slideDown();
      }, 10);
    }
  }

  markDeleted({ animate = false }) {
    this.deletedInput.val('1');
    if (animate) {
      $(this.element).slideUp().dequeue()
        .fadeOut()
        .attr('aria-hidden', 'true');
    } else {
      $(this.element).hide().attr('aria-hidden', 'true');
    }

    // Inform the comment app that the content path of this block is no longer valid
    // This will hide any comments that were previously on the block
    const contentPath = window.comments?.getContentPath(this.element);
    if (contentPath && window.comments.commentApp) {
      window.comments.commentApp.invalidateContentPath(contentPath);
    }
  }

  enableDuplication() {
    this.duplicateButton.enable();
  }
  disableDuplication() {
    this.duplicateButton.disable();
  }
  enableMoveUp() {
    this.moveUpButton.enable();
  }
  disableMoveUp() {
    this.moveUpButton.disable();
  }
  enableMoveDown() {
    this.moveDownButton.enable();
  }
  disableMoveDown() {
    this.moveDownButton.disable();
  }

  setIndex(newIndex) {
    this.index = newIndex;
    this.indexInput.val(newIndex);
  }

  setError(error) {
    this.block.setError(error);
  }

  focus(opts) {
    this.block.focus(opts);
  }

  getTextLabel(opts) {
    if (this.block.getTextLabel) {
      return this.block.getTextLabel(opts);
    }
    return null;
  }

  collapse() {
    this.contentElement.hide().attr('aria-hidden', 'true');
    const label = this.getTextLabel({ maxLength: 50 });
    this.titleElement.text(label || '');<commit_msg>Add event listeners for enable/disable logic<commit_after>/* eslint-disable no-underscore-dangle */

/* global $ */

import EventEmitter from 'events';
import { escapeHtml as h } from '../../../utils/text';

class ActionButton {
  constructor(container, iconName, label, opts) {
    this.dom = $(`
      <button type="button" class="c-sf-block__actions__single" title="${h(label)}">
        <i class="icon icon-${h(iconName)}" aria-hidden="true"></i>
      </button>
    `);
    $(container).append(this.dom);

    if (opts?.disabled) {
      this.disable();
    }

    if (opts?.onClick) {
      this.dom.click(() => {
        opts.onClick();
        return false;  // don't propagate to header's onclick event (which collapses the block)
      });
    }
  }

  enable() {
    this.dom.removeAttr('disabled');
  }
  disable() {
    this.dom.attr('disabled', 'true');
  }
}

export class BaseSequenceChild extends EventEmitter {
  constructor(blockDef, placeholder, prefix, index, id, initialState, sequence, opts) {
    this.blockDef = blockDef;
    this.type = blockDef.name;
    this.prefix = prefix;
    this.index = index;
    this.id = id;
    this.sequence = sequence;

    const animate = opts && opts.animate;
    this.collapsed = opts && opts.collapsed;
    const strings = (opts && opts.strings) || {};

    const dom = $(`
      <div aria-hidden="false" ${this.id ? `data-contentpath="${h(this.id)}"` : 'data-contentpath-disabled'}>
        <input type="hidden"  name="${this.prefix}-deleted" value="">
        <input type="hidden" name="${this.prefix}-order" value="${index}">
        <input type="hidden" name="${this.prefix}-type" value="${h(this.type || '')}">
        <input type="hidden" name="${this.prefix}-id" value="${h(this.id || '')}">

        <div>
          <div class="c-sf-container__block-container">
            <div class="c-sf-block">
              <div data-block-header class="c-sf-block__header c-sf-block__header--collapsible">
                <span class="c-sf-block__header__icon">
                  <i class="icon icon-${h(this.blockDef.meta.icon)}"></i>
                </span>
                <h3 data-block-title class="c-sf-block__header__title"></h3>
                <div class="c-sf-block__actions" data-block-actions>
                  <span class="c-sf-block__type">${h(this.blockDef.meta.label)}</span>
                </div>
              </div>
              <div data-block-content class="c-sf-block__content" aria-hidden="false">
                <div class="c-sf-block__content-inner">
                  <div data-streamfield-block></div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    `);

    $(placeholder).replaceWith(dom);
    this.element = dom.get(0);
    const blockElement = dom.find('[data-streamfield-block]').get(0);
    const actionsContainer = dom.find('[data-block-actions]').get(0);
    this.titleElement = dom.find('[data-block-title]');
    this.contentElement = dom.find('[data-block-content]');
    this.deletedInput = dom.find(`input[name="${this.prefix}-deleted"]`);
    this.indexInput = dom.find(`input[name="${this.prefix}-order"]`);

    dom.find('[data-block-header]').click(() => {
      this.toggleCollapsedState();
    });

    const moveUpButton = new ActionButton(actionsContainer, 'arrow-up', strings.MOVE_UP, {
      disabled: true,
      onClick: () => {
        this.sequence.moveBlockUp(this.index);
      }
    });
    this.on('enableMoveUp', () => {
      moveUpButton.enable();
    });
    this.on('disableMoveUp', () => {
      moveUpButton.disable();
    });

    const moveDownButton = new ActionButton(actionsContainer, 'arrow-down', strings.MOVE_DOWN, {
      disabled: true,
      onClick: () => {
        this.sequence.moveBlockDown(this.index);
      }
    });
    this.on('enableMoveDown', () => {
      moveDownButton.enable();
    });
    this.on('disableMoveDown', () => {
      moveDownButton.disable();
    });

    const duplicateButton = new ActionButton(actionsContainer, 'duplicate', strings.DUPLICATE, {
      onClick: () => {
        this.sequence.duplicateBlock(this.index, { animate: true });
      }
    });
    this.on('enableDuplication', () => {
      duplicateButton.enable();
    });
    this.on('disableDuplication', () => {
      duplicateButton.disable();
    });

    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const deleteButton = new ActionButton(actionsContainer, 'bin', strings.DELETE, {
      onClick: () => {
        this.sequence.deleteBlock(this.index, { animate: true });
      }
    });

    this.block = this.blockDef.render(blockElement, this.prefix + '-value', initialState);

    if (this.collapsed) {
      this.collapse();
    }

    if (animate) {
      dom.hide();
      setTimeout(() => {
        dom.slideDown();
      }, 10);
    }
  }

  markDeleted({ animate = false }) {
    this.deletedInput.val('1');
    if (animate) {
      $(this.element).slideUp().dequeue()
        .fadeOut()
        .attr('aria-hidden', 'true');
    } else {
      $(this.element).hide().attr('aria-hidden', 'true');
    }

    // Inform the comment app that the content path of this block is no longer valid
    // This will hide any comments that were previously on the block
    const contentPath = window.comments?.getContentPath(this.element);
    if (contentPath && window.comments.commentApp) {
      window.comments.commentApp.invalidateContentPath(contentPath);
    }
  }

  enableDuplication() {
    this.emit('enableDuplication');
  }
  disableDuplication() {
    this.emit('disableDuplication');
  }
  enableMoveUp() {
    this.emit('enableMoveUp');
  }
  disableMoveUp() {
    this.emit('disableMoveUp');
  }
  enableMoveDown() {
    this.emit('enableMoveDown');
  }
  disableMoveDown() {
    this.emit('disableMoveDown');
  }

  setIndex(newIndex) {
    this.index = newIndex;
    this.indexInput.val(newIndex);
  }

  setError(error) {
    this.block.setError(error);
  }

  focus(opts) {
    this.block.focus(opts);
  }

  getTextLabel(opts) {
    if (this.block.getTextLabel) {
      return this.block.getTextLabel(opts);
    }
    return null;
  }

  collapse() {
    this.contentElement.hide().attr('aria-hidden', 'true');
    const label = this.getTextLabel({ maxLength: 50 });
    this.titleElement.text(label || '');
{'fraction_non_alphanumeric': 0.17322182661256935, 'fraction_numerical': 0.0016560404073859402, 'mean_word_length': 2.619418639496554, 'pattern_counts': {'":': 0, '<': 71, '<?xml version=': 0, '>': 91, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5323874', 'n_tokens_mistral': 3799, 'n_tokens_neox': 3729, 'n_words': 625}