function ContextMenu (items, options) {
  this.dom = {};

  var me = this;
  var dom = this.dom;
  this.anchor = undefined;
  this.items = items;
  this.eventListeners = {};
  this.selection = undefined; // holds the selection before the menu was opened
  this.onClose = options ? options.close : undefined;

  // create root element
  var root = document.createElement('div');
  root.className = 'jsoneditor-contextmenu-root';
  dom.root = root;

  // create a container element
  var menu = document.createElement('div');
  menu.className = 'jsoneditor-contextmenu';
  dom.menu = menu;
  root.appendChild(menu);

  // create a list to hold the menu items
  var list = document.createElement('ul');
  list.className = 'jsoneditor-menu';
  menu.appendChild(list);
  dom.list = list;
  dom.items = []; // list with all buttons

  // create a (non-visible) button to set the focus to the menu
  var focusButton = document.createElement('button');
  focusButton.type = 'button';
  dom.focusButton = focusButton;
  var li = document.createElement('li');
  li.style.overflow = 'hidden';
  li.style.height = '0';
  li.appendChild(focusButton);
  list.appendChild(li);

  function createMenuItems (list, domItems, items) {
    items.forEach(function (item) {
      if (item.type == 'separator') {
        // create a separator
        var separator = document.createElement('div');
        separator.className = 'jsoneditor-separator';
        li = document.createElement('li');
        li.appendChild(separator);
        list.appendChild(li);
      }
      else {
        var domItem = {};

        // create a menu item
        var li = document.createElement('li');
        list.appendChild(li);

        // create a button in the menu item
        var button = document.createElement('button');
        button.type = 'button';
        button.className = item.className;
        domItem.button = button;
        if (item.title) {
          button.title = item.title;
        }
        if (item.click) {
          button.onclick = function (event) {
            event.preventDefault();
            me.hide();
            item.click();
          };
        }
        li.appendChild(button);

        // create the contents of the button
        if (item.submenu) {
          // add the icon to the button
          var divIcon = document.createElement('div');
          divIcon.className = 'jsoneditor-icon';
          button.appendChild(divIcon);
          var divText = document.createElement('div');
          divText.className = 'jsoneditor-text' +
              (item.click ? '' : ' jsoneditor-right-margin');
          divText.appendChild(document.createTextNode(item.text));
          button.appendChild(divText);

          var buttonSubmenu;
          if (item.click) {
            // submenu and a button with a click handler
            button.className += ' jsoneditor-default';

            var buttonExpand = document.createElement('button');
            buttonExpand.type = 'button';
            domItem.buttonExpand = buttonExpand;
            buttonExpand.className = 'jsoneditor-expand';
            buttonExpand.innerHTML = '<div class="jsoneditor-expand"></div>';
            li.appendChild(buttonExpand);
            if (item.submenuTitle) {
              buttonExpand.title = item.submenuTitle;
            }

            buttonSubmenu = buttonExpand;
          }
          else {
            // submenu and a button without a click handler
            var divExpand = document.createElement('div');
            divExpand.className = 'jsoneditor-expand';
            button.appendChild(divExpand);

            buttonSubmenu = button;
          }

          // attach a handler to expand/collapse the submenu
          buttonSubmenu.onclick = function (event) {
            event.preventDefault();
            me._onExpandItem(domItem);
            buttonSubmenu.focus();
          };

          // create the submenu
          var domSubItems = [];
          domItem.subItems = domSubItems;
          var ul = document.createElement('ul');
          domItem.ul = ul;
          ul.className = 'jsoneditor-menu';
          ul.style.height = '0';
          li.appendChild(ul);
          createMenuItems(ul, domSubItems, item.submenu);
        }
        else {
          // no submenu, just a button with clickhandler
          button.innerHTML = '<div class="jsoneditor-icon"></div>' +
              '<div class="jsoneditor-text">' + translate(item.text) + '</div>';
        }

        domItems.push(domItem);
      }
    });
  }
  createMenuItems(list, this.dom.items, items);

  // TODO: when the editor is small, show the submenu on the right instead of inline?

  // calculate the max height of the menu with one submenu expanded
  this.maxHeight = 0; // height in pixels
  items.forEach(function (item) {
    var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24;
    me.maxHeight = Math.max(me.maxHeight, height);
  });
}
function ContextMenu (items, options) {
  this.dom = {};

  var me = this;
  var dom = this.dom;
  this.anchor = undefined;
  this.items = items;
  this.eventListeners = {};
  this.selection = undefined; 
  this.onClose = options ? options.close : undefined;

  
  var root = document.createElement('div');
  root.className = 'jsoneditor-contextmenu-root';
  dom.root = root;

  
  var menu = document.createElement('div');
  menu.className = 'jsoneditor-contextmenu';
  dom.menu = menu;
  root.appendChild(menu);

  
  var list = document.createElement('ul');
  list.className = 'jsoneditor-menu';
  menu.appendChild(list);
  dom.list = list;
  dom.items = []; 

  
  var focusButton = document.createElement('button');
  focusButton.type = 'button';
  dom.focusButton = focusButton;
  var li = document.createElement('li');
  li.style.overflow = 'hidden';
  li.style.height = '0';
  li.appendChild(focusButton);
  list.appendChild(li);

  function createMenuItems (list, domItems, items) {
    items.forEach(function (item) {
      if (item.type == 'separator') {
        
        var separator = document.createElement('div');
        separator.className = 'jsoneditor-separator';
        li = document.createElement('li');
        li.appendChild(separator);
        list.appendChild(li);
      }
      else {
        var domItem = {};

        
        var li = document.createElement('li');
        list.appendChild(li);

        
        var button = document.createElement('button');
        button.type = 'button';
        button.className = item.className;
        domItem.button = button;
        if (item.title) {
          button.title = item.title;
        }
        if (item.click) {
          button.onclick = function (event) {
            event.preventDefault();
            me.hide();
            item.click();
          };
        }
        li.appendChild(button);

        
        if (item.submenu) {
          
          var divIcon = document.createElement('div');
          divIcon.className = 'jsoneditor-icon';
          button.appendChild(divIcon);
          var divText = document.createElement('div');
          divText.className = 'jsoneditor-text' +
              (item.click ? '' : ' jsoneditor-right-margin');
          divText.appendChild(document.createTextNode(item.text));
          button.appendChild(divText);

          var buttonSubmenu;
          if (item.click) {
            
            button.className += ' jsoneditor-default';

            var buttonExpand = document.createElement('button');
            buttonExpand.type = 'button';
            domItem.buttonExpand = buttonExpand;
            buttonExpand.className = 'jsoneditor-expand';
            buttonExpand.innerHTML = '<div class="jsoneditor-expand"></div>';
            li.appendChild(buttonExpand);
            if (item.submenuTitle) {
              buttonExpand.title = item.submenuTitle;
            }

            buttonSubmenu = buttonExpand;
          }
          else {
            
            var divExpand = document.createElement('div');
            divExpand.className = 'jsoneditor-expand';
            button.appendChild(divExpand);

            buttonSubmenu = button;
          }

          
          buttonSubmenu.onclick = function (event) {
            event.preventDefault();
            me._onExpandItem(domItem);
            buttonSubmenu.focus();
          };

          
          var domSubItems = [];
          domItem.subItems = domSubItems;
          var ul = document.createElement('ul');
          domItem.ul = ul;
          ul.className = 'jsoneditor-menu';
          ul.style.height = '0';
          li.appendChild(ul);
          createMenuItems(ul, domSubItems, item.submenu);
        }
        else {
          
          button.innerHTML = '<div class="jsoneditor-icon"></div>' +
              '<div class="jsoneditor-text">' + translate(item.text) + '</div>';
        }

        domItems.push(domItem);
      }
    });
  }
  createMenuItems(list, this.dom.items, items);

  

  
  this.maxHeight = 0; 
  items.forEach(function (item) {
    var height = (items.length + (item.submenu ? item.submenu.length : 0)) * 24;
    me.maxHeight = Math.max(me.maxHeight, height);
  });
}
function ContextMenu ( items , options ) { this . dom = { } ; var me = this ; var dom = this . dom ; this . anchor = undefined ; this . items = items ; this . eventListeners = { } ; this . selection = undefined ; // holds the selection before the menu was opened this . onClose = options ? options . close : undefined ; // create root element var root = document . createElement ( 'div' ) ; root . className = 'jsoneditor-contextmenu-root' ; dom . root = root ; // create a container element var menu = document . createElement ( 'div' ) ; menu . className = 'jsoneditor-contextmenu' ; dom . menu = menu ; root . appendChild ( menu ) ; // create a list to hold the menu items var list = document . createElement ( 'ul' ) ; list . className = 'jsoneditor-menu' ; menu . appendChild ( list ) ; dom . list = list ; dom . items = [ ] ; // list with all buttons // create a (non-visible) button to set the focus to the menu var focusButton = document . createElement ( 'button' ) ; focusButton . type = 'button' ; dom . focusButton = focusButton ; var li = document . createElement ( 'li' ) ; li . style . overflow = 'hidden' ; li . style . height = '0' ; li . appendChild ( focusButton ) ; list . appendChild ( li ) ; function createMenuItems ( list , domItems , items ) { items . forEach ( function ( item ) { if ( item . type == 'separator' ) { // create a separator var separator = document . createElement ( 'div' ) ; separator . className = 'jsoneditor-separator' ; li = document . createElement ( 'li' ) ; li . appendChild ( separator ) ; list . appendChild ( li ) ; } else { var domItem = { } ; // create a menu item var li = document . createElement ( 'li' ) ; list . appendChild ( li ) ; // create a button in the menu item var button = document . createElement ( 'button' ) ; button . type = 'button' ; button . className = item . className ; domItem . button = button ; if ( item . title ) { button . title = item . title ; } if ( item . click ) { button . onclick = function ( event ) { event . preventDefault ( ) ; me . hide ( ) ; item . click ( ) ; } ; } li . appendChild ( button ) ; // create the contents of the button if ( item . submenu ) { // add the icon to the button var divIcon = document . createElement ( 'div' ) ; divIcon . className = 'jsoneditor-icon' ; button . appendChild ( divIcon ) ; var divText = document . createElement ( 'div' ) ; divText . className = 'jsoneditor-text' + ( item . click ? '' : ' jsoneditor-right-margin' ) ; divText . appendChild ( document . createTextNode ( item . text ) ) ; button . appendChild ( divText ) ; var buttonSubmenu ; if ( item . click ) { // submenu and a button with a click handler button . className += ' jsoneditor-default' ; var buttonExpand = document . createElement ( 'button' ) ; buttonExpand . type = 'button' ; domItem . buttonExpand = buttonExpand ; buttonExpand . className = 'jsoneditor-expand' ; buttonExpand . innerHTML = '<div class="jsoneditor-expand"></div>' ; li . appendChild ( buttonExpand ) ; if ( item . submenuTitle ) { buttonExpand . title = item . submenuTitle ; } buttonSubmenu = buttonExpand ; } else { // submenu and a button without a click handler var divExpand = document . createElement ( 'div' ) ; divExpand . className = 'jsoneditor-expand' ; button . appendChild ( divExpand ) ; buttonSubmenu = button ; } // attach a handler to expand/collapse the submenu buttonSubmenu . onclick = function ( event ) { event . preventDefault ( ) ; me . _onExpandItem ( domItem ) ; buttonSubmenu . focus ( ) ; } ; // create the submenu var domSubItems = [ ] ; domItem . subItems = domSubItems ; var ul = document . createElement ( 'ul' ) ; domItem . ul = ul ; ul . className = 'jsoneditor-menu' ; ul . style . height = '0' ; li . appendChild ( ul ) ; createMenuItems ( ul , domSubItems , item . submenu ) ; } else { // no submenu, just a button with clickhandler button . innerHTML = '<div class="jsoneditor-icon"></div>' + '<div class="jsoneditor-text">' + translate ( item . text ) + '</div>' ; } domItems . push ( domItem ) ; } } ) ; } createMenuItems ( list , this . dom . items , items ) ; // TODO: when the editor is small, show the submenu on the right instead of inline? // calculate the max height of the menu with one submenu expanded this . maxHeight = 0 ; // height in pixels items . forEach ( function ( item ) { var height = ( items . length + ( item . submenu ? item . submenu . length : 0 ) ) * 24 ; me . maxHeight = Math . max ( me . maxHeight , height ) ; } ) ; }
A context menu
@param {Object[]} items    Array containing the menu structure
TODO: describe structure
@param {Object} [options]  Object with options. Available options:
{function} close    Callback called when the
context menu is being closed.
@constructor
A context menu