text: /**
   * Copyright (c) 2006-2015, JGraph Ltd
   * Copyright (c) 2006-2015, Gaudenz Alder
   */
  /**
   * Class: mxTooltipHandler
   * 
   * Graph event handler that displays tooltips. <mxGraph.getTooltip> is used to
   * get the tooltip for a cell or handle. This handler is built-into
   * <mxGraph.tooltipHandler> and enabled using <mxGraph.setTooltips>.
   *
   * Example:
   * 
   * (code>
   * new mxTooltipHandler(graph);
   * (end)
   * 
   * Constructor: mxTooltipHandler
   * 
   * Constructs an event handler that displays tooltips with the specified
   * delay (in milliseconds). If no delay is specified then a default delay
   * of 500 ms (0.5 sec) is used.
   * 
   * Parameters:
   * 
   * graph - Reference to the enclosing <mxGraph>.
   * delay - Optional delay in milliseconds.
   */
  function mxTooltipHandler(graph, delay)
  {
  	if (graph != null)
  	{
  		this.graph = graph;
  		this.delay = delay || 500;
  		this.graph.addMouseListener(this);
  	}
  };
  
  /**
   * Variable: zIndex
   * 
   * Specifies the zIndex for the tooltip and its shadow. Default is 10005.
   */
  mxTooltipHandler.prototype.zIndex = 10005;
  
  /**
   * Variable: graph
   * 
   * Reference to the enclosing <mxGraph>.
   */
  mxTooltipHandler.prototype.graph = null;
  
  /**
   * Variable: delay
   * 
   * Delay to show the tooltip in milliseconds. Default is 500.
   */
  mxTooltipHandler.prototype.delay = null;
  
  /**
   * Variable: ignoreTouchEvents
   * 
   * Specifies if touch and pen events should be ignored. Default is true.
   */
  mxTooltipHandler.prototype.ignoreTouchEvents = true;
  
  /**
   * Variable: hideOnHover
   * 
   * Specifies if the tooltip should be hidden if the mouse is moved over the
   * current cell. Default is false.
   */
  mxTooltipHandler.prototype.hideOnHover = false;
  
  /**
   * Variable: destroyed
   * 
   * True if this handler was destroyed using <destroy>.
   */
  mxTooltipHandler.prototype.destroyed = false;
  
  /**
   * Variable: enabled
   * 
   * Specifies if events are handled. Default is true.
   */
  mxTooltipHandler.prototype.enabled = true;
  
  /**
   * Function: isEnabled
   * 
   * Returns true if events are handled. This implementation
   * returns <enabled>.
   */
  mxTooltipHandler.prototype.isEnabled = function()
  {
  	return this.enabled;
  };
  
  /**
   * Function: setEnabled
   * 
   * Enables or disables event handling. This implementation
   * updates <enabled>.
   */
  mxTooltipHandler.prototype.setEnabled = function(enabled)
  {
  	this.enabled = enabled;
  };
  
  /**
   * Function: isHideOnHover
   * 
   * Returns <hideOnHover>.
   */
  mxTooltipHandler.prototype.isHideOnHover = function()
  {
  	return this.hideOnHover;
  };
  
  /**
   * Function: setHideOnHover
   * 
   * Sets <hideOnHover>.
   */
  mxTooltipHandler.prototype.setHideOnHover = function(value)
  {
  	this.hideOnHover = value;
  };
  
  /**
   * Function: init
   * 
   * Initializes the DOM nodes required for this tooltip handler.
   */
  mxTooltipHandler.prototype.init = function()
  {
  	if (document.body != null)
  	{
  		this.div = document.createElement('div');
  		this.div.className = 'mxTooltip';
  		this.div.style.visibility = 'hidden';
  
  		document.body.appendChild(this.div);
  
  		mxEvent.addGestureListeners(this.div, mxUtils.bind(this, function(evt)
  		{
  			this.hideTooltip();
  		}));
  	}
  };
  
  /**
   * Function: getStateForEvent
   * 
   * Returns the <mxCellState> to be used for showing a tooltip for this event.
   */
  mxTooltipHandler.prototype.getStateForEvent = function(me)
  {
  	return me.getState();
  };
  
  /**
   * Function: mouseDown
   * 
   * Handles the event by initiating a rubberband selection. By consuming the
   * event all subsequent events of the gesture are redirected to this
   * handler.
   */
  mxTooltipHandler.prototype.mouseDown = function(sender, me)
  {
  	this.reset(me, false);
  	this.hideTooltip();
  };
  
  /**
   * Function: mouseMove
   * 
   * Handles the event by updating the rubberband selection.
   */
  mxTooltipHandler.prototype.mouseMove = function(sender, me)
  {
  	if (me.getX() != this.lastX || me.getY() != this.lastY)
  	{
  		this.reset(me, true);
  		var state = this.getStateForEvent(me);
  		
  		if (this.isHideOnHover() || state != this.state || (me.getSource() != this.node &&
  			(!this.stateSource || (state != null && this.stateSource ==
  			(me.isSource(state.shape) || !me.isSource(state.text))))))
  		{
  			this.hideTooltip();
  		}
  	}
  	
  	this.lastX = me.getX();
  	this.lastY = me.getY();
  };
  
  /**
   * Function: mouseUp
   * 
   * Handles the event by resetting the tooltip timer or hiding the existing
   * tooltip.
   */
  mxTooltipHandler.prototype.mouseUp = function(sender, me)
  {
  	this.reset(me, true);
  	this.hideTooltip();
  };
  
  
  /**
   * Function: resetTimer
   * 
   * Resets the timer.
   */
  mxTooltipHandler.prototype.resetTimer = function()
  {
  	if (this.thread != null)
  	{
  		window.clearTimeout(this.thread);
  		this.thread = null;
  	}
  };
  
  /**
   * Function: reset
   * 
   * Resets and/or restarts the timer to trigger the display of the tooltip.
   */
  mxTooltipHandler.prototype.reset = function(me, restart, state)
  {
  	if (!this.ignoreTouchEvents || mxEvent.isMouseEvent(me.getEvent()))
  	{
  		this.resetTimer();
  		state = (state != null) ? state : this.getStateForEvent(me);
  		
  		if (restart && this.isEnabled() && state != null && (this.div == null ||
  			this.div.style.visibility == 'hidden'))
  		{
  			var node = me.getSource();
  			var x = me.getX();
  			var y = me.getY();
  			var stateSource = me.isSource(state.shape) || me.isSource(state.text);
  	
  			this.thread = window.setTimeout(mxUtils.bind(this, function()
  			{
  				if (!this.graph.isEditing() && !this.graph.popupMenuHandler.isMenuShowing() && !this.graph.isMouseDown)
  				{
  					// Uses information from inside event cause using the event at
  					// this (delayed) point in time is not possible in IE as it no
  					// longer contains the required information (member not found)
  					var tip = this.graph.getTooltip(state, node, x, y);
  					this.show(tip, x, y);
  					this.state = state;
  					this.node = node;
  					this.stateSource = stateSource;
  				}
  			}), this.delay);
  		}
  	}
  };
  
  /**
   * Function: hide
   * 
   * Hides the tooltip and resets the timer.
   */
  mxTooltipHandler.prototype.hide = function()
  {
  	this.resetTimer();
  	this.hideTooltip();
  };
  
  /**
   * Function: hideTooltip
   * 
   * Hides the tooltip.
   */
  mxTooltipHandler.prototype.hideTooltip = function()
  {
  	if (this.div != null)
  	{
  		this.div.style.visibility = 'hidden';
  		this.div.innerHTML = '';
  	}
  };
  
  /**
   * Function: show
   * 
   * Shows the tooltip for the specified cell and optional index at the
   * specified location (with a vertical offset of 10 pixels).
   */
  mxTooltipHandler.prototype.show = function(tip, x, y)
  {
  	if (!this.destroyed && tip != null && tip.length > 0)
  	{
  		// Initializes the DOM nodes if required
  		if (this.div == null)
  		{
  			this.init();
  		}
  		
  		var origin = mxUtils.getScrollOrigin();
  
  		this.div.style.zIndex = this.zIndex;
  		this.div.style.left = (x + origin.x) + 'px';
  		this.div.style.top = (y + mxConstants.TOOLTIP_VERTICAL_OFFSET +
  			origin.y) + 'px';
  
  		if (!mxUtils.isNode(tip))
  		{	
  			this.div.innerHTML = tip.replace(/\n/g, '<br>');
  		}
  		else
  		{
  			this.div.innerHTML = '';
  			this.div.appendChild(tip);
  		}
  		
  		this.div.style.visibility = '';
  		mxUtils.fit(this.div);
  	}
  };
  
  /**
   * Function: destroy
   * 
   * Destroys the handler and all its resources and DOM nodes.
   */
  mxTooltipHandler.prototype.destroy = function()
  {
  	if (!this.destroyed)
  	{
  		this.graph.removeMouseListener(this);
  		mxEvent.release(this.div);
  		
  		if (this.div != null && this.div.parentNode != null)
  		{
  			this.div.parentNode.removeChild(this.div);
  		}
  		
  		this.destroyed = true;
  		this.div = null;
  	}
  };
  
  window.mxTooltipHandler = mxTooltipHandler;
  