src/cm/media/js/lib/yui/yui_3.10.3/docs/widget/widget-tooltip.html
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 <!DOCTYPE html>
       
     2 <html lang="en">
       
     3 <head>
       
     4     <meta charset="utf-8">
       
     5     <title>Example: Creating a Simple Tooltip Widget With Extensions</title>
       
     6     <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic">
       
     7     <link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css">
       
     8     <link rel="stylesheet" href="../assets/css/main.css">
       
     9     <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
       
    10     <link rel="shortcut icon" type="image/png" href="../assets/favicon.png">
       
    11     <script src="../../build/yui/yui-min.js"></script>
       
    12     
       
    13 </head>
       
    14 <body>
       
    15 <!--
       
    16 <a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
       
    17 -->
       
    18 <div id="doc">
       
    19     <div id="hd">
       
    20         <h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1>
       
    21     </div>
       
    22     
       
    23 
       
    24             <h1>Example: Creating a Simple Tooltip Widget With Extensions</h1>
       
    25     <div class="yui3-g">
       
    26         <div class="yui3-u-3-4">
       
    27             <div id="main">
       
    28                 <div class="content"><style type="text/css" scoped>
       
    29     .yui3-tooltip {
       
    30         position:absolute;
       
    31     }
       
    32 
       
    33     .yui3-tooltip-content {
       
    34         color: #000;
       
    35         padding: 2px 5px;
       
    36         border-color: #D4C237 #A6982B #A6982B #A6982B;
       
    37         border-width: 1px;
       
    38         border-style: solid;
       
    39         background-color: #FFEE69;
       
    40     }
       
    41 
       
    42     .yui3-tooltip-hidden {
       
    43         visibility:hidden;
       
    44     }
       
    45 
       
    46     div.yui3-hastooltip {
       
    47         border:1px solid #243356;
       
    48         background-color:#406ED9;
       
    49         color:#ffffff;
       
    50         width:25em;
       
    51         margin:20px 0px;
       
    52         padding:5px;
       
    53         cursor:default;
       
    54     }
       
    55 
       
    56     div.yui3-hastooltip span {
       
    57         font-style:italic;
       
    58         font-weight:bold;
       
    59         color:#ABCEFF;
       
    60     }
       
    61 
       
    62     .yui3-tooltip-content strong {
       
    63         font-weight:bold;
       
    64     }
       
    65 </style>
       
    66 
       
    67 <div class="intro">
       
    68     <p>This is an advanced example, in which we create a Tooltip widget, by extending the base <code>Widget</code> class, and adding <code>WidgetStack</code> and <code>WidgetPosition</code> extensions, through <code>Base.build</code>.</p>
       
    69 </div>
       
    70 
       
    71 <div class="example">
       
    72     <div id="delegate">
       
    73     <div class="yui3-hastooltip" title="Tooltip 1" id="tt1">Tooltip One <span>(content from title)</span></div>
       
    74     <div class="yui3-hastooltip" title="Tooltip 2" id="tt2">Tooltip Two <span>(content set in event listener)</span></div>
       
    75     <div class="yui3-hastooltip" title="Tooltip 3" id="tt3">Tooltip Three <span>(content from lookup)</span></div>
       
    76     <div class="yui3-hastooltip" title="Tooltip 4" id="tt4">Tooltip Four <span>(content from title)</span></div>
       
    77     <label><input type="checkbox" id="prevent" /> Prevent Tooltip Four</label>
       
    78 </div>
       
    79 
       
    80 <script type="text/javascript">
       
    81 YUI().use("event-mouseenter", "widget", "widget-position", "widget-stack", function(Y) {
       
    82     var Lang = Y.Lang,
       
    83         Node = Y.Node,
       
    84         OX = -10000,
       
    85         OY = -10000;
       
    86 
       
    87     var Tooltip = Y.Base.create("tooltip", Y.Widget, [Y.WidgetPosition, Y.WidgetStack], {
       
    88 
       
    89         // PROTOTYPE METHODS/PROPERTIES
       
    90 
       
    91         /*
       
    92          * Initialization Code: Sets up privately used state
       
    93          * properties, and publishes the events Tooltip introduces
       
    94          */
       
    95         initializer : function(config) {
       
    96 
       
    97             this._triggerClassName = this.getClassName("trigger");
       
    98 
       
    99             // Currently bound trigger node information
       
   100             this._currTrigger = {
       
   101                 node: null,
       
   102                 title: null,
       
   103                 mouseX: Tooltip.OFFSCREEN_X,
       
   104                 mouseY: Tooltip.OFFSCREEN_Y
       
   105             };
       
   106 
       
   107             // Event handles - mouse over is set on the delegate
       
   108             // element, mousemove and mouseleave are set on the trigger node
       
   109             this._eventHandles = {
       
   110                 delegate: null,
       
   111                 trigger: {
       
   112                     mouseMove : null,
       
   113                     mouseOut: null
       
   114                 }
       
   115             };
       
   116 
       
   117             // Show/hide timers
       
   118             this._timers = {
       
   119                 show: null,
       
   120                 hide: null
       
   121             };
       
   122 
       
   123             // Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
       
   124             // with the default behavior defined in the _defTriggerEnterFn method 
       
   125             this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true});
       
   126             this.publish("triggerLeave", {preventable:false});
       
   127         },
       
   128 
       
   129         /*
       
   130          * Destruction Code: Clears event handles, timers,
       
   131          * and current trigger information
       
   132          */
       
   133         destructor : function() {
       
   134             this._clearCurrentTrigger();
       
   135             this._clearTimers();
       
   136             this._clearHandles();
       
   137         },
       
   138 
       
   139         /*
       
   140          * bindUI is used to bind attribute change and dom event
       
   141          * listeners
       
   142          */
       
   143         bindUI : function() {
       
   144             this.after("delegateChange", this._afterSetDelegate);
       
   145             this.after("nodesChange", this._afterSetNodes);
       
   146 
       
   147             this._bindDelegate();
       
   148         },
       
   149 
       
   150         /*
       
   151          * syncUI is used to update the rendered DOM, based on the current
       
   152          * Tooltip state
       
   153          */
       
   154         syncUI : function() {
       
   155             this._uiSetNodes(this.get("triggerNodes"));
       
   156         },
       
   157 
       
   158         /*
       
   159          * Public method, which can be used by triggerEvent event listeners
       
   160          * to set the content of the tooltip for the current trigger node
       
   161          */
       
   162         setTriggerContent : function(content) {
       
   163             var contentBox = this.get("contentBox");
       
   164             contentBox.set("innerHTML", "");
       
   165 
       
   166             if (content) {
       
   167                 if (content instanceof Node) {
       
   168                     for (var i = 0, l = content.size(); i < l; ++i) {
       
   169                         contentBox.appendChild(content.item(i));
       
   170                     }
       
   171                 } else if (Lang.isString(content)) {
       
   172                     contentBox.set("innerHTML", content);
       
   173                 }
       
   174             }
       
   175         },
       
   176 
       
   177         /*
       
   178          * Default attribute change listener for 
       
   179          * the triggerNodes attribute
       
   180          */
       
   181         _afterSetNodes : function(e) {
       
   182             this._uiSetNodes(e.newVal);
       
   183         },
       
   184 
       
   185         /*
       
   186          * Default attribute change listener for 
       
   187          * the delegate attribute
       
   188          */
       
   189         _afterSetDelegate : function(e) {
       
   190             this._bindDelegate(e.newVal);
       
   191         },
       
   192 
       
   193         /*
       
   194          * Updates the rendered DOM to reflect the
       
   195          * set of trigger nodes passed in
       
   196          */
       
   197         _uiSetNodes : function(nodes) {
       
   198             if (this._triggerNodes) {
       
   199                 this._triggerNodes.removeClass(this._triggerClassName);
       
   200             }
       
   201 
       
   202             if (nodes) {
       
   203                 this._triggerNodes = nodes;
       
   204                 this._triggerNodes.addClass(this._triggerClassName);
       
   205             }
       
   206         },
       
   207 
       
   208         /*
       
   209          * Attaches the default mouseover DOM listener to the 
       
   210          * current delegate node
       
   211          */
       
   212         _bindDelegate : function() {
       
   213             var eventHandles = this._eventHandles;
       
   214 
       
   215             if (eventHandles.delegate) {
       
   216                 eventHandles.delegate.detach();
       
   217                 eventHandles.delegate = null;
       
   218             }
       
   219             eventHandles.delegate = Y.delegate("mouseenter", Y.bind(this._onNodeMouseEnter, this), this.get("delegate"), "." + this._triggerClassName);
       
   220         },
       
   221 
       
   222         /*
       
   223          * Default mouse enter DOM event listener.
       
   224          * 
       
   225          * Delegates to the _enterTrigger method,
       
   226          * if the mouseover enters a trigger node.
       
   227          */
       
   228         _onNodeMouseEnter : function(e) {
       
   229             var node = e.currentTarget;
       
   230             if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
       
   231                 this._enterTrigger(node, e.pageX, e.pageY);
       
   232             }
       
   233         },
       
   234 
       
   235         /*
       
   236          * Default mouse leave DOM event listener
       
   237          * 
       
   238          * Delegates to _leaveTrigger if the mouse
       
   239          * leaves the current trigger node
       
   240          */
       
   241         _onNodeMouseLeave : function(e) {
       
   242             this._leaveTrigger(e.currentTarget);
       
   243         },
       
   244 
       
   245         /*
       
   246          * Default mouse move DOM event listener
       
   247          */
       
   248         _onNodeMouseMove : function(e) {
       
   249             this._overTrigger(e.pageX, e.pageY);
       
   250         },
       
   251 
       
   252         /*
       
   253          * Default handler invoked when the mouse enters
       
   254          * a trigger node. Fires the triggerEnter
       
   255          * event which can be prevented by listeners to 
       
   256          * show the tooltip from being displayed.
       
   257          */
       
   258         _enterTrigger : function(node, x, y) {
       
   259             this._setCurrentTrigger(node, x, y);
       
   260             this.fire("triggerEnter", {node:node, pageX:x, pageY:y});
       
   261         },
       
   262 
       
   263         /*
       
   264          * Default handler for the triggerEvent event,
       
   265          * which will setup the timer to display the tooltip,
       
   266          * if the default handler has not been prevented.
       
   267          */
       
   268         _defTriggerEnterFn : function(e) {
       
   269             var node = e.node;
       
   270             if (!this.get("disabled")) {
       
   271                 this._clearTimers();
       
   272                 var delay = (this.get("visible")) ? 0 : this.get("showDelay");
       
   273                 this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
       
   274             }
       
   275         },
       
   276 
       
   277         /*
       
   278          * Default handler invoked when the mouse leaves
       
   279          * the current trigger node. Fires the triggerLeave
       
   280          * event and sets up the hide timer
       
   281          */
       
   282         _leaveTrigger : function(node) {
       
   283             this.fire("triggerLeave");
       
   284 
       
   285             this._clearCurrentTrigger();
       
   286             this._clearTimers();
       
   287 
       
   288             this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip);
       
   289         },
       
   290 
       
   291         /*
       
   292          * Default handler invoked for mousemove events
       
   293          * on the trigger node. Stores the current mouse 
       
   294          * x, y positions
       
   295          */
       
   296         _overTrigger : function(x, y) {
       
   297             this._currTrigger.mouseX = x;
       
   298             this._currTrigger.mouseY = y;
       
   299         },
       
   300 
       
   301         /*
       
   302          * Shows the tooltip, after moving it to the current mouse
       
   303          * position.
       
   304          */
       
   305         _showTooltip : function(node) {
       
   306             var x = this._currTrigger.mouseX;
       
   307             var y = this._currTrigger.mouseY;
       
   308 
       
   309             this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y);
       
   310 
       
   311             this.show();
       
   312             this._clearTimers();
       
   313 
       
   314             this._timers.hide = Y.later(this.get("autoHideDelay"), this, this._hideTooltip);
       
   315         },
       
   316 
       
   317         /*
       
   318          * Hides the tooltip, after clearing existing timers.
       
   319          */
       
   320         _hideTooltip : function() {
       
   321             this._clearTimers();
       
   322             this.hide();
       
   323         },
       
   324 
       
   325         /*
       
   326          * Set the rendered content of the tooltip for the current
       
   327          * trigger, based on (in order of precedence):
       
   328          * 
       
   329          * a). The string/node content attribute value
       
   330          * b). From the content lookup map if it is set, or 
       
   331          * c). From the title attribute if set.
       
   332          */
       
   333         _setTriggerContent : function(node) {
       
   334             var content = this.get("content");
       
   335             if (content && !(content instanceof Node || Lang.isString(content))) {
       
   336                 content = content[node.get("id")] || node.getAttribute("title");
       
   337             }
       
   338             this.setTriggerContent(content);
       
   339         },
       
   340 
       
   341         /*
       
   342          * Set the currently bound trigger node information, clearing 
       
   343          * out the title attribute if set and setting up mousemove/out 
       
   344          * listeners.
       
   345          */
       
   346         _setCurrentTrigger : function(node, x, y) {
       
   347 
       
   348             var currTrigger = this._currTrigger,
       
   349                 triggerHandles = this._eventHandles.trigger;
       
   350 
       
   351             this._setTriggerContent(node);
       
   352 
       
   353             triggerHandles.mouseMove = Y.on("mousemove", Y.bind(this._onNodeMouseMove, this), node);
       
   354             triggerHandles.mouseOut = Y.on("mouseleave", Y.bind(this._onNodeMouseLeave, this), node);
       
   355 
       
   356             var title = node.getAttribute("title");
       
   357             node.setAttribute("title", "");
       
   358 
       
   359             currTrigger.mouseX = x;
       
   360             currTrigger.mouseY = y;
       
   361             currTrigger.node = node;
       
   362             currTrigger.title = title;
       
   363         },
       
   364 
       
   365         /*
       
   366          * Clear out the current trigger state, restoring
       
   367          * the title attribute on the trigger node, 
       
   368          * if it was originally set.
       
   369          */
       
   370         _clearCurrentTrigger : function() {
       
   371 
       
   372             var currTrigger = this._currTrigger,
       
   373                 triggerHandles = this._eventHandles.trigger;
       
   374 
       
   375             if (currTrigger.node) {
       
   376                 var node = currTrigger.node;
       
   377                 var title = currTrigger.title || "";
       
   378 
       
   379                 currTrigger.node = null;
       
   380                 currTrigger.title = "";
       
   381 
       
   382                 triggerHandles.mouseMove.detach();
       
   383                 triggerHandles.mouseOut.detach();
       
   384                 triggerHandles.mouseMove = null;
       
   385                 triggerHandles.mouseOut = null;
       
   386 
       
   387                 node.setAttribute("title", title);
       
   388             }
       
   389         },
       
   390 
       
   391         /*
       
   392          * Cancel any existing show/hide timers
       
   393          */
       
   394         _clearTimers : function() {
       
   395             var timers = this._timers;
       
   396             if (timers.hide) {
       
   397                 timers.hide.cancel();
       
   398                 timers.hide = null;
       
   399             }
       
   400             if (timers.show) {
       
   401               timers.show.cancel();
       
   402               timers.show = null;
       
   403             }
       
   404         },
       
   405 
       
   406         /*
       
   407          * Detach any stored event handles
       
   408          */
       
   409         _clearHandles : function() {
       
   410             var eventHandles = this._eventHandles;
       
   411 
       
   412             if (eventHandles.delegate) {
       
   413                 this._eventHandles.delegate.detach();
       
   414             }
       
   415             if (eventHandles.trigger.mouseOut) {
       
   416                 eventHandles.trigger.mouseOut.detach();
       
   417             }
       
   418             if (eventHandles.trigger.mouseMove) {
       
   419                 eventHandles.trigger.mouseMove.detach();
       
   420             }
       
   421         }
       
   422     }, {
       
   423     
       
   424         // STATIC METHODS/PROPERTIES
       
   425        
       
   426         OFFSET_X : 15,
       
   427         OFFSET_Y : 15,
       
   428         OFFSCREEN_X : OX,
       
   429         OFFSCREEN_Y : OY,
       
   430 
       
   431         ATTRS : {
       
   432     
       
   433             /* 
       
   434              * The tooltip content. This can either be a fixed content value, 
       
   435              * or a map of id-to-values, designed to be used when a single
       
   436              * tooltip is mapped to multiple trigger elements.
       
   437              */
       
   438             content : {
       
   439                 value: null
       
   440             },
       
   441     
       
   442             /* 
       
   443              * The set of nodes to bind to the tooltip instance. Can be a string, 
       
   444              * or a node instance.
       
   445              */
       
   446             triggerNodes : {
       
   447                 value: null,
       
   448                 setter: function(val) {
       
   449                     if (val && Lang.isString(val)) {
       
   450                         val = Node.all(val);
       
   451                     }
       
   452                     return val;
       
   453                 }
       
   454             },
       
   455     
       
   456             /*
       
   457              * The delegate node to which event listeners should be attached.
       
   458              * This node should be an ancestor of all trigger nodes bound
       
   459              * to the instance. By default the document is used.
       
   460              */
       
   461             delegate : {
       
   462                 value: null,
       
   463                 setter: function(val) {
       
   464                     return Y.one(val) || Y.one("document");
       
   465                 }
       
   466             },
       
   467     
       
   468             /*
       
   469              * The time to wait, after the mouse enters the trigger node,
       
   470              * to display the tooltip
       
   471              */
       
   472             showDelay : {
       
   473                 value:250
       
   474             },
       
   475     
       
   476             /*
       
   477              * The time to wait, after the mouse leaves the trigger node,
       
   478              * to hide the tooltip
       
   479              */
       
   480             hideDelay : {
       
   481                 value:10
       
   482             },
       
   483     
       
   484             /*
       
   485              * The time to wait, after the tooltip is first displayed for 
       
   486              * a trigger node, to hide it, if the mouse has not left the 
       
   487              * trigger node
       
   488              */
       
   489             autoHideDelay : {
       
   490                 value:2000
       
   491             },
       
   492     
       
   493             /*
       
   494              * Override the default visibility set by the widget base class
       
   495              */
       
   496             visible : {
       
   497                 value:false
       
   498             },
       
   499     
       
   500             /*
       
   501              * Override the default XY value set by the widget base class,
       
   502              * to position the tooltip offscreen
       
   503              */
       
   504             xy: {
       
   505                 value:[OX, OY]
       
   506             }
       
   507         }
       
   508     });
       
   509 
       
   510     var tt = new Tooltip({
       
   511         triggerNodes:".yui3-hastooltip",
       
   512         delegate: "#delegate",
       
   513         content: {
       
   514             tt3: "Tooltip 3 (from lookup)"
       
   515         },
       
   516         shim:false,
       
   517         zIndex:2
       
   518     });
       
   519     tt.render();
       
   520 
       
   521     tt.on("triggerEnter", function(e) {
       
   522         var node = e.node;
       
   523         if (node && node.get("id") == "tt2") {
       
   524             this.setTriggerContent("Tooltip 2 (from triggerEvent)");
       
   525         }
       
   526     });
       
   527 
       
   528     var prevent = Y.one("#prevent");
       
   529     tt.on("triggerEnter", function(e) {
       
   530         var node = e.node;
       
   531         if (prevent.get("checked")) {
       
   532             if (node && node.get("id") == "tt4") {
       
   533                 e.preventDefault();
       
   534             }
       
   535         }
       
   536     });
       
   537 });
       
   538 </script>
       
   539 
       
   540 </div>
       
   541 
       
   542 <h2>Creating A Tooltip Widget Class</h2>
       
   543 
       
   544 <h3>Basic Class Structure</h3>
       
   545 
       
   546 <p>As with the basic <a href="widget-extend.html">"Extending Widget"</a> example, the <code>Tooltip</code> class will extend the <code>Widget</code> base class and follows the same pattern we use for other classes which extend Base.</p>
       
   547 
       
   548 <p>Namely:</p>
       
   549 
       
   550 <ul>
       
   551     <li>Set up the constructor to invoke the superclass constructor</li>
       
   552     <li>Define a <code>NAME</code> property, to identify the class</li>
       
   553     <li>Define the default attribute configuration, using the <code>ATTRS</code> property</li>
       
   554     <li>Implement prototype methods</li>
       
   555 </ul>
       
   556 
       
   557 <p>This basic structure is shown below:</p>
       
   558 
       
   559 <pre class="code prettyprint">&#x2F;* 
       
   560  *  Required NAME static field, used to identify the Widget class and 
       
   561  *  used as an event prefix, to generate class names etc. (set to the 
       
   562  *  class name in camel case). 
       
   563  *&#x2F;
       
   564 Tooltip.NAME = &quot;tooltip&quot;;
       
   565 
       
   566 &#x2F;* Default Tooltip Attributes *&#x2F;
       
   567 Tooltip.ATTRS = {
       
   568 
       
   569     &#x2F;* 
       
   570      * The tooltip content. This can either be a fixed content value, 
       
   571      * or a map of id-to-values, designed to be used when a single
       
   572      * tooltip is mapped to multiple trigger elements.
       
   573      *&#x2F;
       
   574     content : {
       
   575         value: null
       
   576     },
       
   577 
       
   578     &#x2F;* 
       
   579      * The set of nodes to bind to the tooltip instance. Can be a string, 
       
   580      * or a node instance.
       
   581      *&#x2F;
       
   582     triggerNodes : {
       
   583         value: null,
       
   584         setter: function(val) {
       
   585             if (val &amp;&amp; Lang.isString(val)) {
       
   586                 val = Node.all(val);
       
   587             }
       
   588             return val;
       
   589         }
       
   590     },
       
   591 
       
   592     &#x2F;*
       
   593      * The delegate node to which event listeners should be attached.
       
   594      * This node should be an ancestor of all trigger nodes bound
       
   595      * to the instance. By default the document is used.
       
   596      *&#x2F;
       
   597     delegate : {
       
   598         value: null,
       
   599         setter: function(val) {
       
   600             return Y.one(val) || Y.one(&quot;document&quot;);
       
   601         }
       
   602     },
       
   603 
       
   604     &#x2F;*
       
   605      * The time to wait, after the mouse enters the trigger node,
       
   606      * to display the tooltip
       
   607      *&#x2F;
       
   608     showDelay : {
       
   609         value:250
       
   610     },
       
   611 
       
   612     &#x2F;*
       
   613      * The time to wait, after the mouse leaves the trigger node,
       
   614      * to hide the tooltip
       
   615      *&#x2F;
       
   616     hideDelay : {
       
   617         value:10
       
   618     },
       
   619 
       
   620     &#x2F;*
       
   621      * The time to wait, after the tooltip is first displayed for 
       
   622      * a trigger node, to hide it, if the mouse has not left the 
       
   623      * trigger node
       
   624      *&#x2F;
       
   625     autoHideDelay : {
       
   626         value:2000
       
   627     },
       
   628 
       
   629     &#x2F;*
       
   630      * Override the default visibility set by the widget base class
       
   631      *&#x2F;
       
   632     visible : {
       
   633         value:false
       
   634     },
       
   635 
       
   636     &#x2F;*
       
   637      * Override the default XY value set by the widget base class,
       
   638      * to position the tooltip offscreen
       
   639      *&#x2F;
       
   640     xy: {
       
   641         value:[Tooltip.OFFSCREEN_X, Tooltip.OFFSCREEN_Y]
       
   642     }
       
   643 };
       
   644 
       
   645 Y.extend(Tooltip, Y.Widget, { 
       
   646     &#x2F;&#x2F; Prototype methods&#x2F;properties
       
   647 });</pre>
       
   648 
       
   649 
       
   650 <h3>Adding WidgetPosition and WidgetStack Extension Support</h3>
       
   651 
       
   652 <p>The Tooltip class also needs basic positioning and stacking (z-index, shimming) support. As with the <a href="widget-build.html">Custom Widget Classes</a> example, we use
       
   653 <code>Base.create</code> to create a new <code>Tooltip</code> class with this support:</p>
       
   654 
       
   655 <pre class="code prettyprint">var Tooltip = Y.Base.create(&quot;tooltip&quot;, Y.Widget, [Y.WidgetPosition, Y.WidgetStack], 
       
   656                                   { ... prototype properties ... },
       
   657                                   { ... static properties ... },</pre>
       
   658 
       
   659 
       
   660 <h3>Lifecycle Methods: initializer, destructor</h3>
       
   661 
       
   662 <p>The <code>initializer</code> method is invoked during the <code>init</code> lifecycle phase, after the attributes are configured for each class. <code>Tooltip</code> uses it 
       
   663 to setup the private state variables it will use to store the trigger node currently being serviced by the tooltip instance, event handles and show/hide timers.</p>
       
   664 
       
   665 <pre class="code prettyprint">initializer : function(config) {
       
   666 
       
   667     this._triggerClassName = this.getClassName(&quot;trigger&quot;);
       
   668 
       
   669     &#x2F;&#x2F; Currently bound trigger node information
       
   670     this._currTrigger = {
       
   671         node: null,
       
   672         title: null,
       
   673         mouseX: Tooltip.OFFSCREEN_X,
       
   674         mouseY: Tooltip.OFFSCREEN_Y
       
   675     };
       
   676 
       
   677     &#x2F;&#x2F; Event handles - mouse over is set on the delegate
       
   678     &#x2F;&#x2F; element, mousemove and mouseleave are set on the trigger node
       
   679     this._eventHandles = {
       
   680         delegate: null,
       
   681         trigger: {
       
   682             mouseMove : null,
       
   683             mouseOut: null
       
   684         }
       
   685     };
       
   686 
       
   687     &#x2F;&#x2F; Show&#x2F;hide timers
       
   688     this._timers = {
       
   689         show: null,
       
   690         hide: null
       
   691     };
       
   692 
       
   693     &#x2F;&#x2F; Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
       
   694     &#x2F;&#x2F; with the default behavior defined in the _defTriggerEnterFn method 
       
   695     this.publish(&quot;triggerEnter&quot;, {defaultFn: this._defTriggerEnterFn, preventable:true});
       
   696     this.publish(&quot;triggerLeave&quot;, {preventable:false});
       
   697 }</pre>
       
   698 
       
   699 
       
   700 <p>The <code>destructor</code> is used to clear out stored state, detach any event handles and clear out the show/hide timers:</p>
       
   701 
       
   702 <pre class="code prettyprint">destructor : function() {
       
   703     this._clearCurrentTrigger();
       
   704     this._clearTimers();
       
   705     this._clearHandles();
       
   706 }</pre>
       
   707 
       
   708 
       
   709 <h3>Lifecycle Methods: bindUI, syncUI</h3>
       
   710 
       
   711 <p>The <code>bindUI</code> and <code>syncUI</code> are invoked by the base Widget class' <code>renderer</code> method.</p>
       
   712 
       
   713 <p><code>bindUI</code> is used to bind the attribute change listeners used to update the rendered UI from the current state of the widget and also to bind
       
   714 the DOM listeners required to enable the UI for interaction.</p>
       
   715 
       
   716 <p><code>syncUI</code> is used to sync the UI state from the current widget state, when initially rendered.</p>
       
   717 
       
   718 <p><em>NOTE:</em> Widget's <code>renderer</code> method also invokes the <code>renderUI</code> method, which is responsible for laying down any additional content elements a widget requires. However
       
   719 tooltip does not have any additional elements in needs to add to the DOM, outside of the default Widget boundingBox and contentBox.</p>
       
   720 
       
   721 <pre class="code prettyprint">bindUI : function() {
       
   722     this.after(&quot;delegateChange&quot;, this._afterSetDelegate);
       
   723     this.after(&quot;nodesChange&quot;, this._afterSetNodes);
       
   724 
       
   725     this._bindDelegate();
       
   726 },
       
   727 
       
   728 syncUI : function() {
       
   729     this._uiSetNodes(this.get(&quot;triggerNodes&quot;));
       
   730 }</pre>
       
   731 
       
   732 
       
   733 <h3>Attribute Supporting Methods</h3>
       
   734 
       
   735 <p>Tooltip's <code>triggerNodes</code>, which defines the set of nodes which should trigger this tooltip instance,
       
   736 has a couple of supporting methods associated with it.</p>
       
   737 
       
   738 <p>The <code>_afterSetNodes</code> method is the default attribute change event handler for the <code>triggerNodes</code>
       
   739 attribute. It invokes the <code>_uiSetNodes</code> method, which marks all trigger nodes with a trigger class name (<code>yui-tooltip-trigger</code>) when set.</p>
       
   740 
       
   741 <pre class="code prettyprint">_afterSetNodes : function(e) {
       
   742     this._uiSetNodes(e.newVal);
       
   743 },
       
   744 
       
   745 _uiSetNodes : function(nodes) {
       
   746     if (this._triggerNodes) {
       
   747         this._triggerNodes.removeClass(this._triggerClassName);
       
   748     }
       
   749 
       
   750     if (nodes) {
       
   751         this._triggerNodes = nodes;
       
   752         this._triggerNodes.addClass(this._triggerClassName);
       
   753     }
       
   754 },</pre>
       
   755 
       
   756 
       
   757 <p>Similarly the <code>_afterSetDelegate</code> method is the default attribute change listener for the <code>delegate</code> attribute,
       
   758 and invokes <code>_bindDelegate</code> to set up the listeners when a new delegate node is set. We use <code>Y.delegate</code> support, along with Event's <code>mouseenter</code> support, 
       
   759 which means the only thing we need to do is tell delegate which node we want to act as the delegate, and which elements we want to target using the <code>&quot;.&quot; + this._triggerClassName</code> selector.</p>
       
   760 
       
   761 <pre class="code prettyprint">_afterSetDelegate : function(e) {
       
   762     this._bindDelegate(e.newVal);
       
   763 },
       
   764 
       
   765 _bindDelegate : function() {
       
   766     var eventHandles = this._eventHandles;
       
   767 
       
   768     if (eventHandles.delegate) {
       
   769         eventHandles.delegate.detach();
       
   770         eventHandles.delegate = null;
       
   771     }
       
   772     eventHandles.delegate = Y.delegate(&quot;mouseenter&quot;, Y.bind(this._onNodeMouseEnter, this), this.get(&quot;delegate&quot;), &quot;.&quot; + this._triggerClassName);
       
   773 },</pre>
       
   774 
       
   775 
       
   776 <h3>DOM Event Handlers</h3>
       
   777 
       
   778 <p>Tooltips interaction revolves around the <code>mouseenter</code>, <code>mousemove</code> and <code>mouseleave</code> DOM events. The mousenter listener is the only listener set up initially, on the <code>delegate</code> node:</p>
       
   779 
       
   780 <pre class="code prettyprint">_onNodeMouseEnter : function(e) {
       
   781     var node = e.currentTarget;
       
   782     if (node &amp;&amp; (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
       
   783         this._enterTrigger(node, e.pageX, e.pageY);
       
   784     }
       
   785 }</pre>
       
   786 
       
   787 
       
   788 <p>Since the <code>mouseenter</code> implementation doesn't invoke it's listeners for <code>mouseover</code> events generated from elements nested 
       
   789 inside the targeted node (for example when mousing out of a child element of a trigger node), there are no additional checks we need to perform other than to see if the node is the current trigger, before handing off to 
       
   790 the <code>_enterTrigger</code> method to setup the current trigger state and attach mousemove and mouseleave listeners on the current trigger node.</p>
       
   791 
       
   792 <p>The mouseleave listener delegates to the <code>_leaveTrigger</code> method, and again, since the <code>mouseleave</code> implementation deals with nested elements, we don't need to perform any additional target checks:</p>
       
   793 
       
   794 <pre class="code prettyprint">_onNodeMouseLeave : function(e) {
       
   795     this._leaveTrigger(e.currentTarget);
       
   796 }</pre>
       
   797 
       
   798 
       
   799 <p>The mouse move listener delegates to the <code>_overTrigger</code> method to store the current mouse XY co-ordinates (used to position the Tooltip when it is displayed after the <code>showDelay</code>):</p>
       
   800 
       
   801 <pre class="code prettyprint">_onNodeMouseMove : function(e) {
       
   802     this._overTrigger(e.pageX, e.pageY);
       
   803 }</pre>
       
   804 
       
   805 
       
   806 <h3>Trigger Event Delegates: _enterTrigger, _leaveTrigger, _overTrigger</h3>
       
   807 
       
   808 <p>As seen above, the DOM event handlers delegate to the <code>_enterTrigger, _leaveTrigger and _overTrigger</code> methods to update the 
       
   809 Tooltip state based on the currently active trigger node.</p>
       
   810 
       
   811 <p>The <code>_enterTrigger</code> method sets the current trigger state (which node is the current tooltip trigger, 
       
   812 what the current mouse XY position is, etc.). The method also fires the <code>triggerEnter</code> event, whose default function actually handles 
       
   813 showing the tooltip after the configured <code>showDelay</code> period. The <code>triggerEnter</code> event can be prevented by listeners, allowing 
       
   814 users to prevent the tooltip from being shown if required. (<code>triggerEnter</code> listeners are passed the current trigger node and pageX, pageY mouse co-ordinates as event facade properties):</p>
       
   815 
       
   816 <pre class="code prettyprint">_enterTrigger : function(node, x, y) {
       
   817     this._setCurrentTrigger(node, x, y);
       
   818     this.fire(&quot;triggerEnter&quot;, null, node, x, y);
       
   819 },
       
   820 
       
   821 _defTriggerEnterFn : function(e) {
       
   822     var node = e.node;
       
   823     if (!this.get(&quot;disabled&quot;)) {
       
   824         this._clearTimers();
       
   825         var delay = (this.get(&quot;visible&quot;)) ? 0 : this.get(&quot;showDelay&quot;);
       
   826         this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
       
   827     }
       
   828 },</pre>
       
   829 
       
   830 
       
   831 <p>Similarly the <code>_leaveTrigger</code> method is invoked when the mouse leaves a trigger node, and clears any stored state, timers and listeners before setting up
       
   832 the <code>hideDelay</code> timer. It fires a <code>triggerLeave</code> event, but cannot be prevented, and has no default behavior to prevent:</p>
       
   833 
       
   834 <pre class="code prettyprint">_leaveTrigger : function(node) {
       
   835     this.fire(&quot;triggerLeave&quot;);
       
   836 
       
   837     this._clearCurrentTrigger();
       
   838     this._clearTimers();
       
   839 
       
   840     this._timers.hide = Y.later(this.get(&quot;hideDelay&quot;), this, this._hideTooltip);
       
   841 },</pre>
       
   842 
       
   843 
       
   844 <p>As mentioned previously, the <code>_overTrigger</code> method simply stores the current mouse XY co-ordinates for use when the tooltip is shown:</p>
       
   845 
       
   846 <pre class="code prettyprint">_overTrigger : function(x, y) {
       
   847     this._currTrigger.mouseX = x;
       
   848     this._currTrigger.mouseY = y;
       
   849 }</pre>
       
   850 
       
   851 
       
   852 <h3>Setting Tooltip Content</h3>
       
   853 
       
   854 <p>Since the content for a tooltip is usually a function of the trigger node and not constant, <code>Tooltip</code> provides a number of ways to set the content.</p>
       
   855 
       
   856 <ol>
       
   857     <li>Setting the <code>content</code> attribute to a string or node. In this case, the value of the <code>content</code> attribute is used
       
   858     for all triggerNodes</li>
       
   859     <li>Setting the <code>content</code> attribute to an object literal, containing a map of triggerNode id to content. The content for a trigger node
       
   860     will be set using the map, when the tooltip is triggered for the node.</li>
       
   861     <li>Setting the title attribute on the trigger node. The value of the title attribute is used to set the tooltip content,
       
   862     when triggered for the node.</li>
       
   863     <li>By calling the <code>setTriggerContent</code> method to set content for a specific trigger node, in a <code>triggerEnter</code> event listener.</li>
       
   864 </ol>
       
   865 
       
   866 <p>The precedence of these methods is handled in the <code>_setTriggerContent</code> method, invoked when the mouse enters a trigger:</p>
       
   867 
       
   868 <pre class="code prettyprint">_setTriggerContent : function(node) {
       
   869     var content = this.get(&quot;content&quot;);
       
   870     if (content &amp;&amp; !(content instanceof Node || Lang.isString(content))) {
       
   871         content = content[node.get(&quot;id&quot;)] || node.getAttribute(&quot;title&quot;);
       
   872     }
       
   873     this.setTriggerContent(content);
       
   874 },
       
   875 
       
   876 setTriggerContent : function(content) {
       
   877     var contentBox = this.get(&quot;contentBox&quot;);
       
   878     contentBox.set(&quot;innerHTML&quot;, &quot;&quot;);
       
   879 
       
   880     if (content) {
       
   881         if (content instanceof Node) {
       
   882             for (var i = 0, l = content.size(); i &lt; l; ++i) {
       
   883                 contentBox.appendChild(content.item(i));
       
   884             }
       
   885         } else if (Lang.isString(content)) {
       
   886             contentBox.set(&quot;innerHTML&quot;, content);
       
   887         }
       
   888     }
       
   889 }</pre>
       
   890 
       
   891 
       
   892 <p>Calling the public <code>setTriggerContent</code> in a <code>triggerEvent</code> listener will over-ride content set using the <code>content</code> attribute or the trigger node's title value.</p>
       
   893 
       
   894 <h3>Using Tooltip</h3>
       
   895 
       
   896 <p>For this example, we set up 4 DIV elements which will act as tooltip triggers. They are all marked using a <code>yui-hastooltip</code> class, so that they can be queried using a simple selector, passed as the value for the <code>triggerNodes</code> attribute in the tooltip's constructor Also all 4 trigger nodes are contained in a wrapper DIV with <code>id=&quot;delegate&quot;</code> which will act as the <code>delegate</code> node.</p>
       
   897 
       
   898 <pre class="code prettyprint">var tt = new Tooltip({
       
   899     triggerNodes:&quot;.yui3-hastooltip&quot;,
       
   900     delegate: &quot;#delegate&quot;,
       
   901     content: {
       
   902         tt3: &quot;Tooltip 3 (from lookup)&quot;
       
   903     },
       
   904     shim:false,
       
   905     zIndex:2
       
   906 });
       
   907 tt.render();</pre>
       
   908 
       
   909 
       
   910 <p>The tooltip content for each of the trigger nodes is setup differently. The first trigger node uses the title attribute to set it's content. The third trigger node's content is set using the content map set in the constructor above. The second trigger node's content is set using a <code>triggerEnter</code> event listener and the <code>setTriggerContent</code> method as shown below:</p>
       
   911 
       
   912 <pre class="code prettyprint">tt.on(&quot;triggerEnter&quot;, function(e) {
       
   913     var node = e.node;
       
   914     if (node &amp;&amp; node.get(&quot;id&quot;) == &quot;tt2&quot;) {
       
   915         this.setTriggerContent(&quot;Tooltip 2 (from triggerEvent)&quot;);
       
   916     }
       
   917 });</pre>
       
   918 
       
   919 
       
   920 <p>The fourth trigger node's content is set using it's title attribute, however it also has a <code>triggerEvent</code> listener which prevents the tooltip from being displayed for it, if the checkbox is checked.</p>
       
   921 
       
   922 <pre class="code prettyprint">var prevent = Y.one(&quot;#prevent&quot;);
       
   923 tt.on(&quot;triggerEnter&quot;, function(e) {
       
   924     var node = e.node;
       
   925     if (prevent.get(&quot;checked&quot;)) {
       
   926         if (node &amp;&amp; node.get(&quot;id&quot;) == &quot;tt4&quot;) {
       
   927             e.preventDefault();
       
   928         }
       
   929     }
       
   930 });</pre>
       
   931 
       
   932 
       
   933 <h2>Complete Example Source</h2>
       
   934 <pre class="code prettyprint">&lt;div id=&quot;delegate&quot;&gt;
       
   935     &lt;div class=&quot;yui3-hastooltip&quot; title=&quot;Tooltip 1&quot; id=&quot;tt1&quot;&gt;Tooltip One &lt;span&gt;(content from title)&lt;&#x2F;span&gt;&lt;&#x2F;div&gt;
       
   936     &lt;div class=&quot;yui3-hastooltip&quot; title=&quot;Tooltip 2&quot; id=&quot;tt2&quot;&gt;Tooltip Two &lt;span&gt;(content set in event listener)&lt;&#x2F;span&gt;&lt;&#x2F;div&gt;
       
   937     &lt;div class=&quot;yui3-hastooltip&quot; title=&quot;Tooltip 3&quot; id=&quot;tt3&quot;&gt;Tooltip Three &lt;span&gt;(content from lookup)&lt;&#x2F;span&gt;&lt;&#x2F;div&gt;
       
   938     &lt;div class=&quot;yui3-hastooltip&quot; title=&quot;Tooltip 4&quot; id=&quot;tt4&quot;&gt;Tooltip Four &lt;span&gt;(content from title)&lt;&#x2F;span&gt;&lt;&#x2F;div&gt;
       
   939     &lt;label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;prevent&quot; &#x2F;&gt; Prevent Tooltip Four&lt;&#x2F;label&gt;
       
   940 &lt;&#x2F;div&gt;
       
   941 
       
   942 &lt;script type=&quot;text&#x2F;javascript&quot;&gt;
       
   943 YUI().use(&quot;event-mouseenter&quot;, &quot;widget&quot;, &quot;widget-position&quot;, &quot;widget-stack&quot;, function(Y) {
       
   944     var Lang = Y.Lang,
       
   945         Node = Y.Node,
       
   946         OX = -10000,
       
   947         OY = -10000;
       
   948 
       
   949     var Tooltip = Y.Base.create(&quot;tooltip&quot;, Y.Widget, [Y.WidgetPosition, Y.WidgetStack], {
       
   950 
       
   951         &#x2F;&#x2F; PROTOTYPE METHODS&#x2F;PROPERTIES
       
   952 
       
   953         &#x2F;*
       
   954          * Initialization Code: Sets up privately used state
       
   955          * properties, and publishes the events Tooltip introduces
       
   956          *&#x2F;
       
   957         initializer : function(config) {
       
   958 
       
   959             this._triggerClassName = this.getClassName(&quot;trigger&quot;);
       
   960 
       
   961             &#x2F;&#x2F; Currently bound trigger node information
       
   962             this._currTrigger = {
       
   963                 node: null,
       
   964                 title: null,
       
   965                 mouseX: Tooltip.OFFSCREEN_X,
       
   966                 mouseY: Tooltip.OFFSCREEN_Y
       
   967             };
       
   968 
       
   969             &#x2F;&#x2F; Event handles - mouse over is set on the delegate
       
   970             &#x2F;&#x2F; element, mousemove and mouseleave are set on the trigger node
       
   971             this._eventHandles = {
       
   972                 delegate: null,
       
   973                 trigger: {
       
   974                     mouseMove : null,
       
   975                     mouseOut: null
       
   976                 }
       
   977             };
       
   978 
       
   979             &#x2F;&#x2F; Show&#x2F;hide timers
       
   980             this._timers = {
       
   981                 show: null,
       
   982                 hide: null
       
   983             };
       
   984 
       
   985             &#x2F;&#x2F; Publish events introduced by Tooltip. Note the triggerEnter event is preventable,
       
   986             &#x2F;&#x2F; with the default behavior defined in the _defTriggerEnterFn method 
       
   987             this.publish(&quot;triggerEnter&quot;, {defaultFn: this._defTriggerEnterFn, preventable:true});
       
   988             this.publish(&quot;triggerLeave&quot;, {preventable:false});
       
   989         },
       
   990 
       
   991         &#x2F;*
       
   992          * Destruction Code: Clears event handles, timers,
       
   993          * and current trigger information
       
   994          *&#x2F;
       
   995         destructor : function() {
       
   996             this._clearCurrentTrigger();
       
   997             this._clearTimers();
       
   998             this._clearHandles();
       
   999         },
       
  1000 
       
  1001         &#x2F;*
       
  1002          * bindUI is used to bind attribute change and dom event
       
  1003          * listeners
       
  1004          *&#x2F;
       
  1005         bindUI : function() {
       
  1006             this.after(&quot;delegateChange&quot;, this._afterSetDelegate);
       
  1007             this.after(&quot;nodesChange&quot;, this._afterSetNodes);
       
  1008 
       
  1009             this._bindDelegate();
       
  1010         },
       
  1011 
       
  1012         &#x2F;*
       
  1013          * syncUI is used to update the rendered DOM, based on the current
       
  1014          * Tooltip state
       
  1015          *&#x2F;
       
  1016         syncUI : function() {
       
  1017             this._uiSetNodes(this.get(&quot;triggerNodes&quot;));
       
  1018         },
       
  1019 
       
  1020         &#x2F;*
       
  1021          * Public method, which can be used by triggerEvent event listeners
       
  1022          * to set the content of the tooltip for the current trigger node
       
  1023          *&#x2F;
       
  1024         setTriggerContent : function(content) {
       
  1025             var contentBox = this.get(&quot;contentBox&quot;);
       
  1026             contentBox.set(&quot;innerHTML&quot;, &quot;&quot;);
       
  1027 
       
  1028             if (content) {
       
  1029                 if (content instanceof Node) {
       
  1030                     for (var i = 0, l = content.size(); i &lt; l; ++i) {
       
  1031                         contentBox.appendChild(content.item(i));
       
  1032                     }
       
  1033                 } else if (Lang.isString(content)) {
       
  1034                     contentBox.set(&quot;innerHTML&quot;, content);
       
  1035                 }
       
  1036             }
       
  1037         },
       
  1038 
       
  1039         &#x2F;*
       
  1040          * Default attribute change listener for 
       
  1041          * the triggerNodes attribute
       
  1042          *&#x2F;
       
  1043         _afterSetNodes : function(e) {
       
  1044             this._uiSetNodes(e.newVal);
       
  1045         },
       
  1046 
       
  1047         &#x2F;*
       
  1048          * Default attribute change listener for 
       
  1049          * the delegate attribute
       
  1050          *&#x2F;
       
  1051         _afterSetDelegate : function(e) {
       
  1052             this._bindDelegate(e.newVal);
       
  1053         },
       
  1054 
       
  1055         &#x2F;*
       
  1056          * Updates the rendered DOM to reflect the
       
  1057          * set of trigger nodes passed in
       
  1058          *&#x2F;
       
  1059         _uiSetNodes : function(nodes) {
       
  1060             if (this._triggerNodes) {
       
  1061                 this._triggerNodes.removeClass(this._triggerClassName);
       
  1062             }
       
  1063 
       
  1064             if (nodes) {
       
  1065                 this._triggerNodes = nodes;
       
  1066                 this._triggerNodes.addClass(this._triggerClassName);
       
  1067             }
       
  1068         },
       
  1069 
       
  1070         &#x2F;*
       
  1071          * Attaches the default mouseover DOM listener to the 
       
  1072          * current delegate node
       
  1073          *&#x2F;
       
  1074         _bindDelegate : function() {
       
  1075             var eventHandles = this._eventHandles;
       
  1076 
       
  1077             if (eventHandles.delegate) {
       
  1078                 eventHandles.delegate.detach();
       
  1079                 eventHandles.delegate = null;
       
  1080             }
       
  1081             eventHandles.delegate = Y.delegate(&quot;mouseenter&quot;, Y.bind(this._onNodeMouseEnter, this), this.get(&quot;delegate&quot;), &quot;.&quot; + this._triggerClassName);
       
  1082         },
       
  1083 
       
  1084         &#x2F;*
       
  1085          * Default mouse enter DOM event listener.
       
  1086          * 
       
  1087          * Delegates to the _enterTrigger method,
       
  1088          * if the mouseover enters a trigger node.
       
  1089          *&#x2F;
       
  1090         _onNodeMouseEnter : function(e) {
       
  1091             var node = e.currentTarget;
       
  1092             if (node &amp;&amp; (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) {
       
  1093                 this._enterTrigger(node, e.pageX, e.pageY);
       
  1094             }
       
  1095         },
       
  1096 
       
  1097         &#x2F;*
       
  1098          * Default mouse leave DOM event listener
       
  1099          * 
       
  1100          * Delegates to _leaveTrigger if the mouse
       
  1101          * leaves the current trigger node
       
  1102          *&#x2F;
       
  1103         _onNodeMouseLeave : function(e) {
       
  1104             this._leaveTrigger(e.currentTarget);
       
  1105         },
       
  1106 
       
  1107         &#x2F;*
       
  1108          * Default mouse move DOM event listener
       
  1109          *&#x2F;
       
  1110         _onNodeMouseMove : function(e) {
       
  1111             this._overTrigger(e.pageX, e.pageY);
       
  1112         },
       
  1113 
       
  1114         &#x2F;*
       
  1115          * Default handler invoked when the mouse enters
       
  1116          * a trigger node. Fires the triggerEnter
       
  1117          * event which can be prevented by listeners to 
       
  1118          * show the tooltip from being displayed.
       
  1119          *&#x2F;
       
  1120         _enterTrigger : function(node, x, y) {
       
  1121             this._setCurrentTrigger(node, x, y);
       
  1122             this.fire(&quot;triggerEnter&quot;, {node:node, pageX:x, pageY:y});
       
  1123         },
       
  1124 
       
  1125         &#x2F;*
       
  1126          * Default handler for the triggerEvent event,
       
  1127          * which will setup the timer to display the tooltip,
       
  1128          * if the default handler has not been prevented.
       
  1129          *&#x2F;
       
  1130         _defTriggerEnterFn : function(e) {
       
  1131             var node = e.node;
       
  1132             if (!this.get(&quot;disabled&quot;)) {
       
  1133                 this._clearTimers();
       
  1134                 var delay = (this.get(&quot;visible&quot;)) ? 0 : this.get(&quot;showDelay&quot;);
       
  1135                 this._timers.show = Y.later(delay, this, this._showTooltip, [node]);
       
  1136             }
       
  1137         },
       
  1138 
       
  1139         &#x2F;*
       
  1140          * Default handler invoked when the mouse leaves
       
  1141          * the current trigger node. Fires the triggerLeave
       
  1142          * event and sets up the hide timer
       
  1143          *&#x2F;
       
  1144         _leaveTrigger : function(node) {
       
  1145             this.fire(&quot;triggerLeave&quot;);
       
  1146 
       
  1147             this._clearCurrentTrigger();
       
  1148             this._clearTimers();
       
  1149 
       
  1150             this._timers.hide = Y.later(this.get(&quot;hideDelay&quot;), this, this._hideTooltip);
       
  1151         },
       
  1152 
       
  1153         &#x2F;*
       
  1154          * Default handler invoked for mousemove events
       
  1155          * on the trigger node. Stores the current mouse 
       
  1156          * x, y positions
       
  1157          *&#x2F;
       
  1158         _overTrigger : function(x, y) {
       
  1159             this._currTrigger.mouseX = x;
       
  1160             this._currTrigger.mouseY = y;
       
  1161         },
       
  1162 
       
  1163         &#x2F;*
       
  1164          * Shows the tooltip, after moving it to the current mouse
       
  1165          * position.
       
  1166          *&#x2F;
       
  1167         _showTooltip : function(node) {
       
  1168             var x = this._currTrigger.mouseX;
       
  1169             var y = this._currTrigger.mouseY;
       
  1170 
       
  1171             this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y);
       
  1172 
       
  1173             this.show();
       
  1174             this._clearTimers();
       
  1175 
       
  1176             this._timers.hide = Y.later(this.get(&quot;autoHideDelay&quot;), this, this._hideTooltip);
       
  1177         },
       
  1178 
       
  1179         &#x2F;*
       
  1180          * Hides the tooltip, after clearing existing timers.
       
  1181          *&#x2F;
       
  1182         _hideTooltip : function() {
       
  1183             this._clearTimers();
       
  1184             this.hide();
       
  1185         },
       
  1186 
       
  1187         &#x2F;*
       
  1188          * Set the rendered content of the tooltip for the current
       
  1189          * trigger, based on (in order of precedence):
       
  1190          * 
       
  1191          * a). The string&#x2F;node content attribute value
       
  1192          * b). From the content lookup map if it is set, or 
       
  1193          * c). From the title attribute if set.
       
  1194          *&#x2F;
       
  1195         _setTriggerContent : function(node) {
       
  1196             var content = this.get(&quot;content&quot;);
       
  1197             if (content &amp;&amp; !(content instanceof Node || Lang.isString(content))) {
       
  1198                 content = content[node.get(&quot;id&quot;)] || node.getAttribute(&quot;title&quot;);
       
  1199             }
       
  1200             this.setTriggerContent(content);
       
  1201         },
       
  1202 
       
  1203         &#x2F;*
       
  1204          * Set the currently bound trigger node information, clearing 
       
  1205          * out the title attribute if set and setting up mousemove&#x2F;out 
       
  1206          * listeners.
       
  1207          *&#x2F;
       
  1208         _setCurrentTrigger : function(node, x, y) {
       
  1209 
       
  1210             var currTrigger = this._currTrigger,
       
  1211                 triggerHandles = this._eventHandles.trigger;
       
  1212 
       
  1213             this._setTriggerContent(node);
       
  1214 
       
  1215             triggerHandles.mouseMove = Y.on(&quot;mousemove&quot;, Y.bind(this._onNodeMouseMove, this), node);
       
  1216             triggerHandles.mouseOut = Y.on(&quot;mouseleave&quot;, Y.bind(this._onNodeMouseLeave, this), node);
       
  1217 
       
  1218             var title = node.getAttribute(&quot;title&quot;);
       
  1219             node.setAttribute(&quot;title&quot;, &quot;&quot;);
       
  1220 
       
  1221             currTrigger.mouseX = x;
       
  1222             currTrigger.mouseY = y;
       
  1223             currTrigger.node = node;
       
  1224             currTrigger.title = title;
       
  1225         },
       
  1226 
       
  1227         &#x2F;*
       
  1228          * Clear out the current trigger state, restoring
       
  1229          * the title attribute on the trigger node, 
       
  1230          * if it was originally set.
       
  1231          *&#x2F;
       
  1232         _clearCurrentTrigger : function() {
       
  1233 
       
  1234             var currTrigger = this._currTrigger,
       
  1235                 triggerHandles = this._eventHandles.trigger;
       
  1236 
       
  1237             if (currTrigger.node) {
       
  1238                 var node = currTrigger.node;
       
  1239                 var title = currTrigger.title || &quot;&quot;;
       
  1240 
       
  1241                 currTrigger.node = null;
       
  1242                 currTrigger.title = &quot;&quot;;
       
  1243 
       
  1244                 triggerHandles.mouseMove.detach();
       
  1245                 triggerHandles.mouseOut.detach();
       
  1246                 triggerHandles.mouseMove = null;
       
  1247                 triggerHandles.mouseOut = null;
       
  1248 
       
  1249                 node.setAttribute(&quot;title&quot;, title);
       
  1250             }
       
  1251         },
       
  1252 
       
  1253         &#x2F;*
       
  1254          * Cancel any existing show&#x2F;hide timers
       
  1255          *&#x2F;
       
  1256         _clearTimers : function() {
       
  1257             var timers = this._timers;
       
  1258             if (timers.hide) {
       
  1259                 timers.hide.cancel();
       
  1260                 timers.hide = null;
       
  1261             }
       
  1262             if (timers.show) {
       
  1263               timers.show.cancel();
       
  1264               timers.show = null;
       
  1265             }
       
  1266         },
       
  1267 
       
  1268         &#x2F;*
       
  1269          * Detach any stored event handles
       
  1270          *&#x2F;
       
  1271         _clearHandles : function() {
       
  1272             var eventHandles = this._eventHandles;
       
  1273 
       
  1274             if (eventHandles.delegate) {
       
  1275                 this._eventHandles.delegate.detach();
       
  1276             }
       
  1277             if (eventHandles.trigger.mouseOut) {
       
  1278                 eventHandles.trigger.mouseOut.detach();
       
  1279             }
       
  1280             if (eventHandles.trigger.mouseMove) {
       
  1281                 eventHandles.trigger.mouseMove.detach();
       
  1282             }
       
  1283         }
       
  1284     }, {
       
  1285     
       
  1286         &#x2F;&#x2F; STATIC METHODS&#x2F;PROPERTIES
       
  1287        
       
  1288         OFFSET_X : 15,
       
  1289         OFFSET_Y : 15,
       
  1290         OFFSCREEN_X : OX,
       
  1291         OFFSCREEN_Y : OY,
       
  1292 
       
  1293         ATTRS : {
       
  1294     
       
  1295             &#x2F;* 
       
  1296              * The tooltip content. This can either be a fixed content value, 
       
  1297              * or a map of id-to-values, designed to be used when a single
       
  1298              * tooltip is mapped to multiple trigger elements.
       
  1299              *&#x2F;
       
  1300             content : {
       
  1301                 value: null
       
  1302             },
       
  1303     
       
  1304             &#x2F;* 
       
  1305              * The set of nodes to bind to the tooltip instance. Can be a string, 
       
  1306              * or a node instance.
       
  1307              *&#x2F;
       
  1308             triggerNodes : {
       
  1309                 value: null,
       
  1310                 setter: function(val) {
       
  1311                     if (val &amp;&amp; Lang.isString(val)) {
       
  1312                         val = Node.all(val);
       
  1313                     }
       
  1314                     return val;
       
  1315                 }
       
  1316             },
       
  1317     
       
  1318             &#x2F;*
       
  1319              * The delegate node to which event listeners should be attached.
       
  1320              * This node should be an ancestor of all trigger nodes bound
       
  1321              * to the instance. By default the document is used.
       
  1322              *&#x2F;
       
  1323             delegate : {
       
  1324                 value: null,
       
  1325                 setter: function(val) {
       
  1326                     return Y.one(val) || Y.one(&quot;document&quot;);
       
  1327                 }
       
  1328             },
       
  1329     
       
  1330             &#x2F;*
       
  1331              * The time to wait, after the mouse enters the trigger node,
       
  1332              * to display the tooltip
       
  1333              *&#x2F;
       
  1334             showDelay : {
       
  1335                 value:250
       
  1336             },
       
  1337     
       
  1338             &#x2F;*
       
  1339              * The time to wait, after the mouse leaves the trigger node,
       
  1340              * to hide the tooltip
       
  1341              *&#x2F;
       
  1342             hideDelay : {
       
  1343                 value:10
       
  1344             },
       
  1345     
       
  1346             &#x2F;*
       
  1347              * The time to wait, after the tooltip is first displayed for 
       
  1348              * a trigger node, to hide it, if the mouse has not left the 
       
  1349              * trigger node
       
  1350              *&#x2F;
       
  1351             autoHideDelay : {
       
  1352                 value:2000
       
  1353             },
       
  1354     
       
  1355             &#x2F;*
       
  1356              * Override the default visibility set by the widget base class
       
  1357              *&#x2F;
       
  1358             visible : {
       
  1359                 value:false
       
  1360             },
       
  1361     
       
  1362             &#x2F;*
       
  1363              * Override the default XY value set by the widget base class,
       
  1364              * to position the tooltip offscreen
       
  1365              *&#x2F;
       
  1366             xy: {
       
  1367                 value:[OX, OY]
       
  1368             }
       
  1369         }
       
  1370     });
       
  1371 
       
  1372     var tt = new Tooltip({
       
  1373         triggerNodes:&quot;.yui3-hastooltip&quot;,
       
  1374         delegate: &quot;#delegate&quot;,
       
  1375         content: {
       
  1376             tt3: &quot;Tooltip 3 (from lookup)&quot;
       
  1377         },
       
  1378         shim:false,
       
  1379         zIndex:2
       
  1380     });
       
  1381     tt.render();
       
  1382 
       
  1383     tt.on(&quot;triggerEnter&quot;, function(e) {
       
  1384         var node = e.node;
       
  1385         if (node &amp;&amp; node.get(&quot;id&quot;) == &quot;tt2&quot;) {
       
  1386             this.setTriggerContent(&quot;Tooltip 2 (from triggerEvent)&quot;);
       
  1387         }
       
  1388     });
       
  1389 
       
  1390     var prevent = Y.one(&quot;#prevent&quot;);
       
  1391     tt.on(&quot;triggerEnter&quot;, function(e) {
       
  1392         var node = e.node;
       
  1393         if (prevent.get(&quot;checked&quot;)) {
       
  1394             if (node &amp;&amp; node.get(&quot;id&quot;) == &quot;tt4&quot;) {
       
  1395                 e.preventDefault();
       
  1396             }
       
  1397         }
       
  1398     });
       
  1399 });
       
  1400 &lt;&#x2F;script&gt;</pre>
       
  1401 
       
  1402 </div>
       
  1403             </div>
       
  1404         </div>
       
  1405 
       
  1406         <div class="yui3-u-1-4">
       
  1407             <div class="sidebar">
       
  1408                 
       
  1409 
       
  1410                 
       
  1411                     <div class="sidebox">
       
  1412                         <div class="hd">
       
  1413                             <h2 class="no-toc">Examples</h2>
       
  1414                         </div>
       
  1415 
       
  1416                         <div class="bd">
       
  1417                             <ul class="examples">
       
  1418                                 
       
  1419                                     
       
  1420                                         <li data-description="Shows how to extend the base widget class, to create your own Widgets.">
       
  1421                                             <a href="widget-extend.html">Extending the Base Widget Class</a>
       
  1422                                         </li>
       
  1423                                     
       
  1424                                 
       
  1425                                     
       
  1426                                         <li data-description="Shows how to use Base.create and mix/match extensions to create custom Widget classes.">
       
  1427                                             <a href="widget-build.html">Creating Custom Widget Classes With Extensions</a>
       
  1428                                         </li>
       
  1429                                     
       
  1430                                 
       
  1431                                     
       
  1432                                         <li data-description="Shows how to create an IO plugin for Widget.">
       
  1433                                             <a href="widget-plugin.html">Creating a Widget Plugin</a>
       
  1434                                         </li>
       
  1435                                     
       
  1436                                 
       
  1437                                     
       
  1438                                         <li data-description="Shows how to extend the Widget class, and add WidgetPosition and WidgetStack to create a Tooltip widget class.">
       
  1439                                             <a href="widget-tooltip.html">Creating a Simple Tooltip Widget With Extensions</a>
       
  1440                                         </li>
       
  1441                                     
       
  1442                                 
       
  1443                                     
       
  1444                                         <li data-description="Shows how to extend the Widget class, and add WidgetParent and WidgetChild to create a simple ListBox widget.">
       
  1445                                             <a href="widget-parentchild-listbox.html">Creating a Hierarchical ListBox Widget</a>
       
  1446                                         </li>
       
  1447                                     
       
  1448                                 
       
  1449                             </ul>
       
  1450                         </div>
       
  1451                     </div>
       
  1452                 
       
  1453 
       
  1454                 
       
  1455             </div>
       
  1456         </div>
       
  1457     </div>
       
  1458 </div>
       
  1459 
       
  1460 <script src="../assets/vendor/prettify/prettify-min.js"></script>
       
  1461 <script>prettyPrint();</script>
       
  1462 
       
  1463 <script>
       
  1464 YUI.Env.Tests = {
       
  1465     examples: [],
       
  1466     project: '../assets',
       
  1467     assets: '../assets/widget',
       
  1468     name: 'widget-tooltip',
       
  1469     title: 'Creating a Simple Tooltip Widget With Extensions',
       
  1470     newWindow: '',
       
  1471     auto:  false 
       
  1472 };
       
  1473 YUI.Env.Tests.examples.push('widget-extend');
       
  1474 YUI.Env.Tests.examples.push('widget-build');
       
  1475 YUI.Env.Tests.examples.push('widget-plugin');
       
  1476 YUI.Env.Tests.examples.push('widget-tooltip');
       
  1477 YUI.Env.Tests.examples.push('widget-parentchild-listbox');
       
  1478 
       
  1479 </script>
       
  1480 <script src="../assets/yui/test-runner.js"></script>
       
  1481 
       
  1482 
       
  1483 
       
  1484 </body>
       
  1485 </html>