<commit_before>function Menu (node, menuButton) {

  this.keyCode = Object.freeze({
    "TAB"      : 9,
    "RETURN"   : 13,
    "ESC"      : 27,
    "SPACE"    : 32,
    "PAGEUP"   : 33,
    "PAGEDOWN" : 34,
    "END"      : 35,
    "HOME"     : 36,
    "LEFT"     : 37,
    "UP"       : 38,
    "RIGHT"    : 39,
    "DOWN"     : 40
  });

  // Check for DOM element node
  if (typeof node !== 'object' || !node.getElementsByClassName) return false;

  this.menuNode = node;
  node.tabIndex = -1;

  this.menuButton = menuButton;

  this.firstMenuItem = false;
  this.lastMenuItem = false;
};

Menu.prototype.initMenu = function () {
  var menu = this,
      ce = this.menuNode.firstElementChild;

  while (ce) {
    if (ce.getAttribute('role')  === 'menuitem') {
      ce.tabIndex = -1;
      if (!this.firstMenuItem) this.firstMenuItem = ce;
      this.lastMenuItem = ce;

      ce.addEventListener('keydown', function (event) {
        menu.handleKeyDown(event, menu);
      });

      ce.addEventListener('click', function (event) {
        menu.handleMouseClick(event, menu);
      });

      ce.addEventListener('blur', function (event) {
        menu.handleBlur(event, menu);
      });

      ce.addEventListener('focus', function (event) {
        menu.handleFocus(event, menu);
      });
    }

    ce = ce.nextElementSibling;
  }
};

Menu.prototype.nextMenuItem = function (currentMenuItem) {
  var mi = currentMenuItem.nextElementSibling;

  while (mi) {
    if (mi.getAttribute('role')  === 'menuitem') {
      mi.focus();
      break;
    }
    mi = mi.nextElementSibling;
  }

  if (!mi && this.firstMenuItem) {
    this.firstMenuItem.focus();
  }
};

Menu.prototype.previousMenuItem = function (currentMenuItem) {
  var mi = currentMenuItem.previousElementSibling;

  while (mi) {
    if (mi.getAttribute('role')  === 'menuitem') {
      mi.focus();
      break;
    }
    mi = mi.previousElementSibling;
  }

  if (!mi && this.lastMenuItem) {
    this.lastMenuItem.focus();
  }
};

Menu.prototype.eventKeyDown = function (event, menu) {
  var tgt = event.currentTarget,
      flag = false;

  switch (event.keyCode) {
    case menu.keyCode.SPACE:
    case menu.keyCode.RETURN:
      var clickEvent = new MouseEvent('click', {
        'view': window,
        'bubbles': true,
        'cancelable': true
      });
      tgt.dispatchEvent(clickEvent);
      menu.menuButton.closeMenu(true);
      flag = true;
      break;

    case menu.keyCode.ESC:
      menu.menuButton.closeMenu(true);
      menu.menuButton.buttonNode.focus();
      flag = true;
      break;

    case menu.keyCode.UP:
    case menu.keyCode.LEFT:
      menu.previousMenuItem(tgt);
      flag = true;
      break;

    case menu.keyCode.DOWN:
    case menu.keyCode.RIGHT:
      menu.nextMenuItem(tgt);
      flag = true;
      break;

    case menu.keyCode.TAB:
      menu.menuButton.closeMenu(true, false);
      break;

    default:
      break;
  }

  if (flag) {
    event.stopPropagation();
    event.preventDefault();
  }

};<commit_msg>Port from menu-button-1.js<commit_after>/*
*   @constructor Menu: Encapsulate state and behavior of menu
*
*   @param node : The element node that serves as the menu container.
*          Each child element that serves as a menuitem must have its
*          role attribute set to 'menuitem'.
*/
var Menu = function (node) {
  // Check whether node is a DOM element
  if (!node instanceof Element)
    throw new TypeError("Constructor argument 'node' is not a DOM Element.");

  // Check whether menu has child menuitems
  if (node.childElementCount === 0)
    throw new Error("Constructor argument 'node' has no Element children!")

  this.menuNode = node;
  node.tabIndex = -1;

  this.firstItem = null;
  this.lastItem  = null;

  this.keyCode = Object.freeze({
    'TAB'      :  9,
    'RETURN'   : 13,
    'ESC'      : 27,
    'SPACE'    : 32,
    'PAGEUP'   : 33,
    'PAGEDOWN' : 34,
    'END'      : 35,
    'HOME'     : 36,
    'LEFT'     : 37,
    'UP'       : 38,
    'RIGHT'    : 39,
    'DOWN'     : 40
  });
};

/*
*   @method Menu.prototype.init
*
*   Initialize firstItem, lastItem
*   Set tabindex for each menuitem
*   Add event listeners for 'keydown', 'click', 'blur' and 'focus' events
*/
Menu.prototype.init = function () {
  var menu = this, // for reference from within event handler
      mi = this.menuNode.firstElementChild; // first menuitem

  while (mi) {
    if (mi.getAttribute('role')  === 'menuitem') {
      mi.tabIndex = -1;
      if (!this.firstItem) this.firstItem = mi;
      this.lastItem = mi;

      mi.addEventListener('keydown', function (event) {
        menu.handleKeydown(event);
      });

      mi.addEventListener('click', function (event) {
        menu.handleClick(event);
      });

      mi.addEventListener('focus', function (event) {
        menu.handleFocus(event);
      });

      mi.addEventListener('blur', function (event) {
        menu.handleBlur(event);
      });
    }

    mi = mi.nextElementSibling;
  }
};

/* Event handler methods */

Menu.prototype.handleKeydown = function (event) {
  var tgt = event.currentTarget,
      flag = false;

  switch (event.keyCode) {
    case this.keyCode.SPACE:
    case this.keyCode.RETURN:
      var clickEvent = new MouseEvent('click', {
        'view': window,
        'bubbles': true,
        'cancelable': true
      });
      tgt.dispatchEvent(clickEvent);
      this.menuButton.closeMenu(true);
      flag = true;
      break;

    case this.keyCode.ESC:
      this.menuButton.closeMenu(true);
      this.menuButton.buttonNode.focus();
      flag = true;
      break;

    case this.keyCode.UP:
    case this.keyCode.LEFT:
      this.previousItem(tgt);
      flag = true;
      break;

    case this.keyCode.DOWN:
    case this.keyCode.RIGHT:
      this.nextItem(tgt);
      flag = true;
      break;

    case this.keyCode.TAB:
      this.menuButton.closeMenu(true, false);
      break;

    default:
      break;
  }

  if (flag) {
    event.stopPropagation();
    event.preventDefault();
  }
};

Menu.prototype.handleClick = function (event) {
  this.close();
};

Menu.prototype.handleFocus = function (event) {
  this.menuHasFocus = true;
};

Menu.prototype.handleBlur = function (event) {
  this.menuHasFocus = false;
  setTimeout(function () { this.close() }, 500);
};

// not currently used
Menu.prototype.handleMouseOver = function (event) {
  this.mouseInMenu = true;
  this.open();
};

// not currently used
Menu.prototype.handleMouseOut = function (event) {
  this.mouseInMenu = false;
  setTimeout(function () { this.close() }, 500);
};

/* Additional methods */

Menu.prototype.previousItem = function (currentItem) {
  var mi = currentItem.previousElementSibling;

  while (mi) {
    if (mi.getAttribute('role')  === 'menuitem') {
      mi.focus();
      break;
    }
    mi = mi.previousElementSibling;
  }

  if (!mi && this.lastItem) {
    this.lastItem.focus();
  }
};

Menu.prototype.nextItem = function (currentItem) {
  var mi = currentItem.nextElementSibling;

  while (mi) {
    if (mi.getAttribute('role')  === 'menuitem') {
      mi.focus();
      break;
    }
    mi = mi.nextElementSibling;
  }

  if (!mi && this.firstItem) {
    this.firstItem.focus();
  }
};

Menu.prototype.open = function (relNode) {
  // get position and bounding rectangle of relNode (e.g. menubutton)
  var pos  = this.getPosition(relNode);
  var rect = relNode.getBoundingClientRect();

  this.menuNode.style.display = 'block';
  this.menuNode.style.position = 'absolute';
  this.menuNode.style.top  = (pos.y + rect.height) + "px";
  this.menuNode.style.left = pos.x + "px"; ;
};

Menu.prototype.close = function (force) {
  if (force || (!this.mouseInMenu && !this.menuHasFocus))
    this.menuNode.style.display = 'none';
};

Menu.prototype.getPosition = function (element) {
  var x = 0, y = 0;

  while (element) {
    x += (element.offsetLeft - element.scrollLeft + element.clientLeft);
    y += (element.offsetTop - element.scrollTop + element.clientTop);
    element = element.offsetParent;
  }

  return { x: x, y: y };
};
{'fraction_non_alphanumeric': 0.14151177199504336, 'fraction_numerical': 0.007434944237918215, 'mean_word_length': 2.5523767605633805, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '1603028', 'n_tokens_mistral': 2705, 'n_tokens_neox': 2632, 'n_words': 583}