123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- /**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- /**
- * Class: mxPopupMenu
- *
- * Basic popup menu. To add a vertical scrollbar to a given submenu, the
- * following code can be used.
- *
- * (code)
- * var mxPopupMenuShowMenu = mxPopupMenu.prototype.showMenu;
- * mxPopupMenu.prototype.showMenu = function()
- * {
- * mxPopupMenuShowMenu.apply(this, arguments);
- *
- * this.div.style.overflowY = 'auto';
- * this.div.style.overflowX = 'hidden';
- * this.div.style.maxHeight = '160px';
- * };
- * (end)
- *
- * Constructor: mxPopupMenu
- *
- * Constructs a popupmenu.
- *
- * Event: mxEvent.SHOW
- *
- * Fires after the menu has been shown in <popup>.
- */
- function mxPopupMenu(factoryMethod)
- {
- this.factoryMethod = factoryMethod;
-
- if (factoryMethod != null)
- {
- this.init();
- }
- };
- /**
- * Extends mxEventSource.
- */
- mxPopupMenu.prototype = new mxEventSource();
- mxPopupMenu.prototype.constructor = mxPopupMenu;
- /**
- * Variable: submenuImage
- *
- * URL of the image to be used for the submenu icon.
- */
- mxPopupMenu.prototype.submenuImage = mxClient.imageBasePath + '/submenu.gif';
- /**
- * Variable: zIndex
- *
- * Specifies the zIndex for the popupmenu and its shadow. Default is 10006.
- */
- mxPopupMenu.prototype.zIndex = 10006;
- /**
- * Variable: factoryMethod
- *
- * Function that is used to create the popup menu. The function takes the
- * current panning handler, the <mxCell> under the mouse and the mouse
- * event that triggered the call as arguments.
- */
- mxPopupMenu.prototype.factoryMethod = null;
- /**
- * Variable: useLeftButtonForPopup
- *
- * Specifies if popupmenus should be activated by clicking the left mouse
- * button. Default is false.
- */
- mxPopupMenu.prototype.useLeftButtonForPopup = false;
- /**
- * Variable: enabled
- *
- * Specifies if events are handled. Default is true.
- */
- mxPopupMenu.prototype.enabled = true;
- /**
- * Variable: itemCount
- *
- * Contains the number of times <addItem> has been called for a new menu.
- */
- mxPopupMenu.prototype.itemCount = 0;
- /**
- * Variable: autoExpand
- *
- * Specifies if submenus should be expanded on mouseover. Default is false.
- */
- mxPopupMenu.prototype.autoExpand = false;
- /**
- * Variable: smartSeparators
- *
- * Specifies if separators should only be added if a menu item follows them.
- * Default is false.
- */
- mxPopupMenu.prototype.smartSeparators = false;
- /**
- * Variable: labels
- *
- * Specifies if any labels should be visible. Default is true.
- */
- mxPopupMenu.prototype.labels = true;
- /**
- * Function: init
- *
- * Initializes the shapes required for this vertex handler.
- */
- mxPopupMenu.prototype.init = function()
- {
- // Adds the inner table
- this.table = document.createElement('table');
- this.table.className = 'mxPopupMenu';
-
- this.tbody = document.createElement('tbody');
- this.table.appendChild(this.tbody);
- // Adds the outer div
- this.div = document.createElement('div');
- this.div.className = 'mxPopupMenu';
- this.div.style.display = 'inline';
- this.div.style.zIndex = this.zIndex;
- this.div.appendChild(this.table);
- // Disables the context menu on the outer div
- mxEvent.disableContextMenu(this.div);
- };
- /**
- * Function: isEnabled
- *
- * Returns true if events are handled. This implementation
- * returns <enabled>.
- */
- mxPopupMenu.prototype.isEnabled = function()
- {
- return this.enabled;
- };
-
- /**
- * Function: setEnabled
- *
- * Enables or disables event handling. This implementation
- * updates <enabled>.
- */
- mxPopupMenu.prototype.setEnabled = function(enabled)
- {
- this.enabled = enabled;
- };
- /**
- * Function: isPopupTrigger
- *
- * Returns true if the given event is a popupmenu trigger for the optional
- * given cell.
- *
- * Parameters:
- *
- * me - <mxMouseEvent> that represents the mouse event.
- */
- mxPopupMenu.prototype.isPopupTrigger = function(me)
- {
- return me.isPopupTrigger() || (this.useLeftButtonForPopup && mxEvent.isLeftMouseButton(me.getEvent()));
- };
- /**
- * Function: addItem
- *
- * Adds the given item to the given parent item. If no parent item is specified
- * then the item is added to the top-level menu. The return value may be used
- * as the parent argument, ie. as a submenu item. The return value is the table
- * row that represents the item.
- *
- * Paramters:
- *
- * title - String that represents the title of the menu item.
- * image - Optional URL for the image icon.
- * funct - Function associated that takes a mouseup or touchend event.
- * parent - Optional item returned by <addItem>.
- * iconCls - Optional string that represents the CSS class for the image icon.
- * IconsCls is ignored if image is given.
- * enabled - Optional boolean indicating if the item is enabled. Default is true.
- * active - Optional boolean indicating if the menu should implement any event handling.
- * Default is true.
- * noHover - Optional boolean to disable hover state.
- */
- mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled, active, noHover)
- {
- parent = parent || this;
- this.itemCount++;
-
- // Smart separators only added if element contains items
- if (parent.willAddSeparator)
- {
- if (parent.containsItems)
- {
- this.addSeparator(parent, true);
- }
- parent.willAddSeparator = false;
- }
- parent.containsItems = true;
- var tr = document.createElement('tr');
- tr.className = 'mxPopupMenuItem';
- var col1 = document.createElement('td');
- col1.className = 'mxPopupMenuIcon';
- // Adds the given image into the first column
- if (image != null)
- {
- var img = document.createElement('img');
- img.src = image;
- col1.appendChild(img);
- }
- else if (iconCls != null)
- {
- var div = document.createElement('div');
- div.className = iconCls;
- col1.appendChild(div);
- }
-
- tr.appendChild(col1);
-
- if (this.labels)
- {
- var col2 = document.createElement('td');
- col2.className = 'mxPopupMenuItem' +
- ((enabled != null && !enabled) ? ' mxDisabled' : '');
-
- mxUtils.write(col2, title);
- col2.align = 'left';
- tr.appendChild(col2);
-
- var col3 = document.createElement('td');
- col3.className = 'mxPopupMenuItem' +
- ((enabled != null && !enabled) ? ' mxDisabled' : '');
- col3.style.paddingRight = '6px';
- col3.style.textAlign = 'right';
-
- tr.appendChild(col3);
-
- if (parent.div == null)
- {
- this.createSubmenu(parent);
- }
- }
-
- parent.tbody.appendChild(tr);
- if (active != false && enabled != false)
- {
- var currentSelection = null;
-
- mxEvent.addGestureListeners(tr,
- mxUtils.bind(this, function(evt)
- {
- this.eventReceiver = tr;
-
- if (parent.activeRow != tr && parent.activeRow != parent)
- {
- if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
- {
- this.hideSubmenu(parent);
- }
-
- if (tr.div != null)
- {
- this.showSubmenu(parent, tr);
- parent.activeRow = tr;
- }
- }
-
- // Workaround for lost current selection in page because of focus in IE
- if (document.selection != null && (mxClient.IS_QUIRKS || document.documentMode == 8))
- {
- currentSelection = document.selection.createRange();
- }
-
- mxEvent.consume(evt);
- }),
- mxUtils.bind(this, function(evt)
- {
- if (parent.activeRow != tr && parent.activeRow != parent)
- {
- if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
- {
- this.hideSubmenu(parent);
- }
-
- if (this.autoExpand && tr.div != null)
- {
- this.showSubmenu(parent, tr);
- parent.activeRow = tr;
- }
- }
-
- // Sets hover style because TR in IE doesn't have hover
- if (!noHover)
- {
- tr.className = 'mxPopupMenuItemHover';
- }
- }),
- mxUtils.bind(this, function(evt)
- {
- // EventReceiver avoids clicks on a submenu item
- // which has just been shown in the mousedown
- if (this.eventReceiver == tr)
- {
- if (parent.activeRow != tr)
- {
- this.hideMenu();
- }
-
- // Workaround for lost current selection in page because of focus in IE
- if (currentSelection != null)
- {
- // Workaround for "unspecified error" in IE8 standards
- try
- {
- currentSelection.select();
- }
- catch (e)
- {
- // ignore
- }
- currentSelection = null;
- }
-
- if (funct != null)
- {
- funct(evt);
- }
- }
-
- this.eventReceiver = null;
- mxEvent.consume(evt);
- })
- );
-
- // Resets hover style because TR in IE doesn't have hover
- if (!noHover)
- {
- mxEvent.addListener(tr, 'mouseout',
- mxUtils.bind(this, function(evt)
- {
- tr.className = 'mxPopupMenuItem';
- })
- );
- }
- }
-
- return tr;
- };
- /**
- * Adds a checkmark to the given menuitem.
- */
- mxPopupMenu.prototype.addCheckmark = function(item, img)
- {
- var td = item.firstChild.nextSibling;
- td.style.backgroundImage = 'url(\'' + img + '\')';
- td.style.backgroundRepeat = 'no-repeat';
- td.style.backgroundPosition = '2px 50%';
- };
- /**
- * Function: createSubmenu
- *
- * Creates the nodes required to add submenu items inside the given parent
- * item. This is called in <addItem> if a parent item is used for the first
- * time. This adds various DOM nodes and a <submenuImage> to the parent.
- *
- * Parameters:
- *
- * parent - An item returned by <addItem>.
- */
- mxPopupMenu.prototype.createSubmenu = function(parent)
- {
- parent.table = document.createElement('table');
- parent.table.className = 'mxPopupMenu';
- parent.tbody = document.createElement('tbody');
- parent.table.appendChild(parent.tbody);
- parent.div = document.createElement('div');
- parent.div.className = 'mxPopupMenu';
- parent.div.style.position = 'absolute';
- parent.div.style.display = 'inline';
- parent.div.style.zIndex = this.zIndex;
-
- parent.div.appendChild(parent.table);
-
- var img = document.createElement('img');
- img.setAttribute('src', this.submenuImage);
-
- // Last column of the submenu item in the parent menu
- td = parent.firstChild.nextSibling.nextSibling;
- td.appendChild(img);
- };
- /**
- * Function: showSubmenu
- *
- * Shows the submenu inside the given parent row.
- */
- mxPopupMenu.prototype.showSubmenu = function(parent, row)
- {
- if (row.div != null)
- {
- row.div.style.left = (parent.div.offsetLeft +
- row.offsetLeft+row.offsetWidth - 1) + 'px';
- row.div.style.top = (parent.div.offsetTop+row.offsetTop) + 'px';
- document.body.appendChild(row.div);
-
- // Moves the submenu to the left side if there is no space
- var left = parseInt(row.div.offsetLeft);
- var width = parseInt(row.div.offsetWidth);
- var offset = mxUtils.getDocumentScrollOrigin(document);
-
- var b = document.body;
- var d = document.documentElement;
-
- var right = offset.x + (b.clientWidth || d.clientWidth);
-
- if (left + width > right)
- {
- row.div.style.left = Math.max(0, (parent.div.offsetLeft - width + ((mxClient.IS_IE) ? 6 : -6))) + 'px';
- }
-
- mxUtils.fit(row.div);
- }
- };
- /**
- * Function: addSeparator
- *
- * Adds a horizontal separator in the given parent item or the top-level menu
- * if no parent is specified.
- *
- * Parameters:
- *
- * parent - Optional item returned by <addItem>.
- * force - Optional boolean to ignore <smartSeparators>. Default is false.
- */
- mxPopupMenu.prototype.addSeparator = function(parent, force)
- {
- parent = parent || this;
-
- if (this.smartSeparators && !force)
- {
- parent.willAddSeparator = true;
- }
- else if (parent.tbody != null)
- {
- parent.willAddSeparator = false;
- var tr = document.createElement('tr');
-
- var col1 = document.createElement('td');
- col1.className = 'mxPopupMenuIcon';
- col1.style.padding = '0 0 0 0px';
-
- tr.appendChild(col1);
-
- var col2 = document.createElement('td');
- col2.style.padding = '0 0 0 0px';
- col2.setAttribute('colSpan', '2');
-
- var hr = document.createElement('hr');
- hr.setAttribute('size', '1');
- col2.appendChild(hr);
-
- tr.appendChild(col2);
-
- parent.tbody.appendChild(tr);
- }
- };
- /**
- * Function: popup
- *
- * Shows the popup menu for the given event and cell.
- *
- * Example:
- *
- * (code)
- * graph.panningHandler.popup = function(x, y, cell, evt)
- * {
- * mxUtils.alert('Hello, World!');
- * }
- * (end)
- */
- mxPopupMenu.prototype.popup = function(x, y, cell, evt)
- {
- if (this.div != null && this.tbody != null && this.factoryMethod != null)
- {
- this.div.style.left = x + 'px';
- this.div.style.top = y + 'px';
-
- // Removes all child nodes from the existing menu
- while (this.tbody.firstChild != null)
- {
- mxEvent.release(this.tbody.firstChild);
- this.tbody.removeChild(this.tbody.firstChild);
- }
-
- this.itemCount = 0;
- this.factoryMethod(this, cell, evt);
-
- if (this.itemCount > 0)
- {
- this.showMenu();
- this.fireEvent(new mxEventObject(mxEvent.SHOW));
- }
- }
- };
- /**
- * Function: isMenuShowing
- *
- * Returns true if the menu is showing.
- */
- mxPopupMenu.prototype.isMenuShowing = function()
- {
- return this.div != null && this.div.parentNode == document.body;
- };
- /**
- * Function: showMenu
- *
- * Shows the menu.
- */
- mxPopupMenu.prototype.showMenu = function()
- {
- // Disables filter-based shadow in IE9 standards mode
- if (document.documentMode >= 9)
- {
- this.div.style.filter = 'none';
- }
-
- // Fits the div inside the viewport
- document.body.appendChild(this.div);
- mxUtils.fit(this.div);
- };
- /**
- * Function: hideMenu
- *
- * Removes the menu and all submenus.
- */
- mxPopupMenu.prototype.hideMenu = function()
- {
- if (this.div != null)
- {
- if (this.div.parentNode != null)
- {
- this.div.parentNode.removeChild(this.div);
- }
-
- this.hideSubmenu(this);
- this.containsItems = false;
- this.fireEvent(new mxEventObject(mxEvent.HIDE));
- }
- };
- /**
- * Function: hideSubmenu
- *
- * Removes all submenus inside the given parent.
- *
- * Parameters:
- *
- * parent - An item returned by <addItem>.
- */
- mxPopupMenu.prototype.hideSubmenu = function(parent)
- {
- if (parent.activeRow != null)
- {
- this.hideSubmenu(parent.activeRow);
-
- if (parent.activeRow.div.parentNode != null)
- {
- parent.activeRow.div.parentNode.removeChild(parent.activeRow.div);
- }
-
- parent.activeRow = null;
- }
- };
- /**
- * Function: destroy
- *
- * Destroys the handler and all its resources and DOM nodes.
- */
- mxPopupMenu.prototype.destroy = function()
- {
- if (this.div != null)
- {
- mxEvent.release(this.div);
-
- if (this.div.parentNode != null)
- {
- this.div.parentNode.removeChild(this.div);
- }
-
- this.div = null;
- }
- };
|