Source: menus.js

/** @module menus */

import * as consts from './consts.js';
import * as graph_ops from './graph_ops.js';

export function machine_type() {
  return document.getElementById('select_machine').value;
}

/**
 * reports the type of machine the user is working on
 * @returns {boolean} true or false 
 */
export function is_NFA() {
  return machine_type() === consts.MACHINE_TYPES.NFA;
}

/**
 * reports the type of machine the user is working on
 * @returns {boolean} true or false 
 */
export function is_PDA() {
  return machine_type() === consts.MACHINE_TYPES.PDA;
}

/**
 * reports the type of machine the user is working on
 * @returns {boolean} true or false 
 */
export function is_Turing() {
  return machine_type() === consts.MACHINE_TYPES.Turing;
}

/** dynamically change the length of textboxes inside the container */
function bind_elongate_textbox(container) {
  const minimum_width = 4;  // minimum width of 4ch
  const change_width_func = e => {
    e.target.style.width = `${Math.max(minimum_width, e.target.value.length)}ch`;
  };
  const text_boxes = container.querySelectorAll('input[type="text"]');
  for (const text_box of text_boxes) {
    text_box.style.width = `${Math.max(minimum_width, text_box.value.length)}ch`;  // set initial width
    text_box.addEventListener('input', change_width_func);
  }
}

export function is_Mealy() {
  return machine_type() === consts.MACHINE_TYPES.Mealy;
}

/**
 * reports the type of machine the user is working on
 * @returns {boolean} true or false 
 */
export function is_Moore() {
  return machine_type() === consts.MACHINE_TYPES.Moore;
}

/**
 * reports the type of machine the user is working on
 * @returns {boolean} true or false 
 */
export function is_Regex() {
  return machine_type() === consts.MACHINE_TYPES.Regex;
}

/**
 * creates the context menu to change a vertex and display it
 * @param {Object} graph - the graph containing the vertex v
 * @param {string} v - the vertex we clicked on and want to change
 * @param {float} x - x position of the top left corner of the menu
 * @param {float} y - y position of the top left corner of the menu
 */
export function display_vertex_menu(graph, v, x, y) {
  const container = document.createElement('div');
  container.className = 'context_menu';
  const rename_div = document.createElement('div');
  const buttons_div = document.createElement('div');
  const delete_div = document.createElement('div');
  delete_div.innerText = 'delete';
  delete_div.className = 'delete';
  delete_div.addEventListener('click', () => graph_ops.delete_vertex(graph, v));
  container.appendChild(rename_div);
  container.appendChild(buttons_div);
  container.appendChild(delete_div);
  const rename = document.createElement('input');
  bind_elongate_textbox(rename);
  rename.type = 'text';
  rename.value = v;  // prepopulate vertex name
  rename_div.appendChild(rename);
  let moore_output;
  if (is_Moore()) {
    moore_output = document.createElement('input');
    moore_output.type = 'text';
    moore_output.value = graph[v].moore_output;
    rename_div.appendChild(moore_output);
  }
  const start_btn = document.createElement('button');
  start_btn.innerText = 'make start';
  start_btn.className = 'start';
  start_btn.addEventListener('click', () => graph_ops.set_start(graph, v));
  const final_btn = document.createElement('button');
  final_btn.innerText = 'toggle final';
  final_btn.className = 'final';
  final_btn.addEventListener('click', () => graph_ops.toggle_final(graph, v));
  buttons_div.appendChild(start_btn);
  buttons_div.appendChild(final_btn);
  container.style = `left:${x}px; top:${y}px`;
  container.addEventListener('keyup', e => {
    if (e.key === 'Enter') {
      if (is_Moore()) {
        graph_ops.rename_vertex(graph, v, rename.value, moore_output.value);
      } else {
        graph_ops.rename_vertex(graph, v, rename.value);
      }
    }
  });
  document.querySelector('body').appendChild(container);
  rename.focus();  // focus on the first text box
  rename.select();  // select all text
  bind_elongate_textbox(container);
}

/**
 * creates the context menu to change a vertex and display it
 * @param {Object} graph - the graph containing the edge
 * @param {Object} edge - the edge we clicked on and want to change
 * @param {float} x - x position of the top left corner of the menu
 * @param {float} y - y position of the top left corner of the menu
 */
export function display_edge_menu(graph, edge, x, y) {
  const container = document.createElement('div');
  container.className = 'context_menu';
  const rename_div = document.createElement('div');
  const delete_div = document.createElement('div');
  delete_div.innerText = 'delete';
  delete_div.className = 'delete';
  delete_div.addEventListener('click', () => graph_ops.delete_edge(graph, edge));
  container.appendChild(rename_div);
  container.appendChild(delete_div);
  const transition = document.createElement('input');
  transition.type = 'text';
  transition.value = edge.transition;
  const pop = document.createElement('input');
  pop.type = 'text';
  pop.value = edge.pop_symbol;
  const push = document.createElement('input');
  push.type = 'text';
  push.value = edge.push_symbol;
  const left_right_choice = document.createElement('input');
  left_right_choice.type = 'checkbox';
  left_right_choice.className = 'L_R_toggle';
  left_right_choice.checked = edge.move === consts.LEFT;
  const m_output = document.createElement('input');
  m_output.type = 'text';
  m_output.value = edge.mealy_output;
  rename_div.appendChild(transition);
  if (is_PDA()) {
    rename_div.appendChild(pop);
    rename_div.appendChild(push);
  } else if (is_Turing()) {
    rename_div.appendChild(push);
    rename_div.appendChild(left_right_choice);
  } else if (is_Mealy()) {
    rename_div.appendChild(m_output);
  }
  container.style = `left:${x}px; top:${y}px`;
  container.addEventListener('keyup', e => {
    if (e.key === 'Enter') {
      graph_ops.rename_edge(graph, edge,
        transition.value, pop.value, push.value,
        left_right_choice.checked ? consts.LEFT : consts.RIGHT, m_output.value);
    }
  });
  document.querySelector('body').appendChild(container);
  transition.focus();  // focus on the first text box
  transition.select();  // select all text
  bind_elongate_textbox(container);
}

/** wipes the context menu; does nothing if none exists */
export function remove_context_menu() {
  const menus = document.querySelectorAll('.context_menu');
  for (const menu of menus) {
    document.querySelector('body').removeChild(menu);
  }
}

/** show/hide UI specific for a machine */
export function set_UI_visibility(machine, visible) {
  const UIs = document.getElementsByClassName(machine+'_specific');
  for (let i = 0; i < UIs.length; i++) {
    UIs[i].hidden = !visible;
  }
}

/** displays exactly those UI elements specific to the machine from {NFA, PDA, Turing, Regex} */
export function display_UI_for(machine) {
  for (const machine_type of Object.values(consts.MACHINE_TYPES)) {
    set_UI_visibility(machine_type, false);  // hide all UI elements
  }
  set_UI_visibility(machine, true);  // show only the UI elements for the specified machine
}