|
1 |
|
2 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
|
3 <html> |
|
4 <head> |
|
5 <title>YUI Library Examples: Widget: Creating a simple Tooltip widget</title> |
|
6 <meta http-equiv="content-type" content="text/html; charset=utf-8"> |
|
7 <link rel="stylesheet" type="text/css" href="../../assets/yui.css" > |
|
8 |
|
9 <style> |
|
10 /*Supplemental CSS for the YUI distribution*/ |
|
11 #custom-doc { width: 95%; min-width: 950px; } |
|
12 #pagetitle {background-image: url(../../assets/bg_hd.gif);} |
|
13 /* #pagetitle h1 {background-image: url(../../assets/title_h_bg.gif);}*/ |
|
14 </style> |
|
15 |
|
16 <link rel="stylesheet" type="text/css" href="../../assets/dpSyntaxHighlighter.css"> |
|
17 <link type="text/css" rel="stylesheet" href="../../build/cssfonts/fonts-min.css" /> |
|
18 <script type="text/javascript" src="../../build/yui/yui-min.js"></script> |
|
19 <style type="text/css"> |
|
20 .yui-tooltip { |
|
21 position:absolute; |
|
22 } |
|
23 |
|
24 .yui-tooltip-content { |
|
25 color: #000; |
|
26 padding: 2px 5px; |
|
27 border-color: #D4C237 #A6982B #A6982B #A6982B; |
|
28 border-width: 1px; |
|
29 border-style: solid; |
|
30 background-color: #FFEE69; |
|
31 } |
|
32 |
|
33 .yui-tooltip-hidden { |
|
34 visibility:hidden; |
|
35 } |
|
36 |
|
37 div.yui-hastooltip { |
|
38 border:1px solid #243356; |
|
39 background-color:#406ED9; |
|
40 color:#ffffff; |
|
41 width:25em; |
|
42 margin:20px 0px; |
|
43 padding:5px; |
|
44 cursor:default; |
|
45 } |
|
46 |
|
47 div.yui-hastooltip span { |
|
48 font-style:italic; |
|
49 font-weight:bold; |
|
50 color:#ABCEFF; |
|
51 } |
|
52 |
|
53 .yui-tooltip-content strong { |
|
54 font-weight:bold; |
|
55 } |
|
56 </style> |
|
57 |
|
58 </head> |
|
59 <body id="yahoo-com" class=" yui-skin-sam"> |
|
60 <div id="custom-doc" class="yui-t2"> |
|
61 <div id="hd"> |
|
62 <div id="ygunav"> |
|
63 <p> |
|
64 <em> |
|
65 <a href="http://developer.yahoo.com/yui/3/">YUI 3.x Home</a> <i> - </i> |
|
66 </em> |
|
67 </p> |
|
68 <form action="http://search.yahoo.com/search" id="sitesearchform"> |
|
69 <input name="vs" type="hidden" value="developer.yahoo.com"> |
|
70 <input name="vs" type="hidden" value="yuiblog.com"> |
|
71 <div id="sitesearch"> |
|
72 <label for="searchinput">Site Search (YDN & YUIBlog): </label> |
|
73 <input type="text" id="searchinput" name="p"> |
|
74 <input type="submit" value="Search" id="searchsubmit" class="ygbt"> |
|
75 </div> |
|
76 </form> |
|
77 </div> |
|
78 <div id="ygma"><a href="../../"><img src="../../assets/logo.gif" border="0" width="200" height="93"></a></div> |
|
79 <div id="pagetitle"><h1>YUI Library Examples: Widget: Creating a simple Tooltip widget</h1></div> |
|
80 </div> |
|
81 <div id="bd"> |
|
82 |
|
83 <div id="bar-note"><p><strong>Note:</strong> This is YUI 3.x. Looking for <a href="http://developer.yahoo.com/yui/">YUI 2.x</a>?</p></div> |
|
84 |
|
85 <div id="yui-main"> |
|
86 <div class="yui-b"> |
|
87 <div class="yui-ge"> |
|
88 <div class="yui-u first example" id="main"> |
|
89 |
|
90 <h2>Widget: Creating a simple Tooltip widget</h2> |
|
91 |
|
92 <div id="example" class="promo"> |
|
93 <p> |
|
94 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> |
|
95 |
|
96 <div class="module example-container "> |
|
97 <div class="hd exampleHd"> |
|
98 <p class="newWindowButton yui-skin-sam"> |
|
99 <a href="widget-tooltip_clean.html" target="_blank">View example in new window.</a> |
|
100 </p> |
|
101 </div> <div id="example-canvas" class="bd"> |
|
102 |
|
103 |
|
104 <!--BEGIN SOURCE CODE FOR EXAMPLE =============================== --> |
|
105 |
|
106 <div id="delegate"> |
|
107 <div class="yui-hastooltip" title="Tooltip 1" id="tt1">Tooltip One <span>(content from title)</span></div> |
|
108 <div class="yui-hastooltip" title="Tooltip 2" id="tt2">Tooltip Two <span>(content set in event listener)</span></div> |
|
109 <div class="yui-hastooltip" title="Tooltip 3" id="tt3">Tooltip Three <span>(content from lookup)</span></div> |
|
110 <div class="yui-hastooltip" title="Tooltip 4" id="tt4">Tooltip Four <span>(content from title)</span><label></div> |
|
111 <input type="checkbox" id="prevent" /> Prevent Tooltip Four</label> |
|
112 </div> |
|
113 |
|
114 <script type="text/javascript"> |
|
115 YUI({base:"../../build/", timeout: 10000}).use("widget", "widget-position", "widget-stack", function(Y) { |
|
116 var Lang = Y.Lang, |
|
117 Node = Y.Node; |
|
118 |
|
119 /* Tooltip constructor */ |
|
120 function Tooltip(config) { |
|
121 Tooltip.superclass.constructor.apply(this, arguments); |
|
122 } |
|
123 |
|
124 /* |
|
125 Required NAME static field, used to identify the Widget class and |
|
126 used as an event prefix, to generate class names etc. (set to the |
|
127 class name in camel case). |
|
128 */ |
|
129 Tooltip.NAME = "tooltip"; |
|
130 |
|
131 /* Static constants */ |
|
132 Tooltip.OFFSET_X = 15; |
|
133 Tooltip.OFFSET_Y = 15; |
|
134 Tooltip.OFFSCREEN_X = -10000; |
|
135 Tooltip.OFFSCREEN_Y = -10000; |
|
136 |
|
137 /* Default Tooltip Attributes */ |
|
138 Tooltip.ATTRS = { |
|
139 |
|
140 /* |
|
141 * The tooltip content. This can either be a fixed content value, |
|
142 * or a map of id-to-values, designed to be used when a single |
|
143 * tooltip is mapped to multiple trigger elements. |
|
144 */ |
|
145 content : { |
|
146 value: null |
|
147 }, |
|
148 |
|
149 /* |
|
150 * The set of nodes to bind to the tooltip instance. Can be a string, |
|
151 * or a node instance. |
|
152 */ |
|
153 triggerNodes : { |
|
154 value: null, |
|
155 setter: function(val) { |
|
156 if (val && Lang.isString(val)) { |
|
157 val = Node.all(val); |
|
158 } |
|
159 return val; |
|
160 } |
|
161 }, |
|
162 |
|
163 /* |
|
164 * The delegate node to which event listeners should be attached. |
|
165 * This node should be an ancestor of all trigger nodes bound |
|
166 * to the instance. By default the document is used. |
|
167 */ |
|
168 delegate : { |
|
169 value: null, |
|
170 setter: function(val) { |
|
171 return Node.get(val) || Node.get("document"); |
|
172 } |
|
173 }, |
|
174 |
|
175 /* |
|
176 * The time to wait, after the mouse enters the trigger node, |
|
177 * to display the tooltip |
|
178 */ |
|
179 showDelay : { |
|
180 value:250 |
|
181 }, |
|
182 |
|
183 /* |
|
184 * The time to wait, after the mouse leaves the trigger node, |
|
185 * to hide the tooltip |
|
186 */ |
|
187 hideDelay : { |
|
188 value:10 |
|
189 }, |
|
190 |
|
191 /* |
|
192 * The time to wait, after the tooltip is first displayed for |
|
193 * a trigger node, to hide it, if the mouse has not left the |
|
194 * trigger node |
|
195 */ |
|
196 autoHideDelay : { |
|
197 value:2000 |
|
198 }, |
|
199 |
|
200 /* |
|
201 * Override the default visibility set by the widget base class |
|
202 */ |
|
203 visible : { |
|
204 value:false |
|
205 }, |
|
206 |
|
207 /* |
|
208 * Override the default XY value set by the widget base class, |
|
209 * to position the tooltip offscreen |
|
210 */ |
|
211 xy: { |
|
212 value:[Tooltip.OFFSCREEN_X, Tooltip.OFFSCREEN_Y] |
|
213 } |
|
214 }; |
|
215 |
|
216 /* Extend the base Widget class */ |
|
217 Y.extend(Tooltip, Y.Widget, { |
|
218 |
|
219 /* |
|
220 * Initialization Code: Sets up privately used state |
|
221 * properties, and publishes the events Tooltip introduces |
|
222 */ |
|
223 initializer : function(config) { |
|
224 |
|
225 this._triggerClassName = this.getClassName("trigger"); |
|
226 |
|
227 // Currently bound trigger node information |
|
228 this._currTrigger = { |
|
229 node: null, |
|
230 title: null, |
|
231 mouseX: Tooltip.OFFSCREEN_X, |
|
232 mouseY: Tooltip.OFFSCREEN_Y |
|
233 }; |
|
234 |
|
235 // Event handles - mouse over is set on the delegate |
|
236 // element, mousemove and mouseout are set on the trigger node |
|
237 this._eventHandles = { |
|
238 delegate: null, |
|
239 trigger: { |
|
240 mouseMove : null, |
|
241 mouseOut: null |
|
242 } |
|
243 }; |
|
244 |
|
245 // Show/hide timers |
|
246 this._timers = { |
|
247 show: null, |
|
248 hide: null |
|
249 }; |
|
250 |
|
251 // Publish events introduced by Tooltip. Note the triggerEnter event is preventable, |
|
252 // with the default behavior defined in the _defTriggerEnterFn method |
|
253 this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true}); |
|
254 this.publish("triggerLeave", {preventable:false}); |
|
255 }, |
|
256 |
|
257 /* |
|
258 * Destruction Code: Clears event handles, timers, |
|
259 * and current trigger information |
|
260 */ |
|
261 destructor : function() { |
|
262 this._clearCurrentTrigger(); |
|
263 this._clearTimers(); |
|
264 this._clearHandles(); |
|
265 }, |
|
266 |
|
267 /* |
|
268 * bindUI is used to bind attribute change and dom event |
|
269 * listeners |
|
270 */ |
|
271 bindUI : function() { |
|
272 this.after("delegateChange", this._afterSetDelegate); |
|
273 this.after("nodesChange", this._afterSetNodes); |
|
274 |
|
275 this._bindDelegate(); |
|
276 }, |
|
277 |
|
278 /* |
|
279 * syncUI is used to update the rendered DOM, based on the current |
|
280 * Tooltip state |
|
281 */ |
|
282 syncUI : function() { |
|
283 this._uiSetNodes(this.get("triggerNodes")); |
|
284 }, |
|
285 |
|
286 /* |
|
287 * Public method, which can be used by triggerEvent event listeners |
|
288 * to set the content of the tooltip for the current trigger node |
|
289 */ |
|
290 setTriggerContent : function(content) { |
|
291 var contentBox = this.get("contentBox"); |
|
292 contentBox.set("innerHTML", ""); |
|
293 |
|
294 if (content) { |
|
295 if (content instanceof Node) { |
|
296 for (var i = 0, l = content.size(); i < l; ++i) { |
|
297 contentBox.appendChild(content.item(i)); |
|
298 } |
|
299 } else if (Lang.isString(content)) { |
|
300 contentBox.set("innerHTML", content); |
|
301 } |
|
302 } |
|
303 }, |
|
304 |
|
305 /* |
|
306 * Gets the closest ancestor of the given node, |
|
307 * which is a tooltip trigger node |
|
308 */ |
|
309 getParentTrigger : function(node) { |
|
310 var cn = this._triggerClassName; |
|
311 return (node.hasClass(cn)) ? node : node.ancestor(function(node) {node.hasClass(cn)}); |
|
312 }, |
|
313 |
|
314 /* |
|
315 * Default attribute change listener for |
|
316 * the triggerNodes attribute |
|
317 */ |
|
318 _afterSetNodes : function(e) { |
|
319 this._uiSetNodes(e.newVal); |
|
320 }, |
|
321 |
|
322 /* |
|
323 * Default attribute change listener for |
|
324 * the delegate attribute |
|
325 */ |
|
326 _afterSetDelegate : function(e) { |
|
327 this._bindDelegate(e.newVal); |
|
328 }, |
|
329 |
|
330 /* |
|
331 * Updates the rendered DOM to reflect the |
|
332 * set of trigger nodes passed in |
|
333 */ |
|
334 _uiSetNodes : function(nodes) { |
|
335 if (this._triggerNodes) { |
|
336 this._triggerNodes.removeClass(this._triggerClassName); |
|
337 } |
|
338 |
|
339 if (nodes) { |
|
340 this._triggerNodes = nodes; |
|
341 this._triggerNodes.addClass(this._triggerClassName); |
|
342 } |
|
343 }, |
|
344 |
|
345 /* |
|
346 * Attaches the default mouseover DOM listener to the |
|
347 * current delegate node |
|
348 */ |
|
349 _bindDelegate : function() { |
|
350 var eventHandles = this._eventHandles; |
|
351 |
|
352 if (eventHandles.delegate) { |
|
353 eventHandles.delegate.detach(); |
|
354 eventHandles.delegate = null; |
|
355 } |
|
356 eventHandles.delegate = Y.on("mouseover", Y.bind(this._onDelegateMouseOver, this), this.get("delegate")); |
|
357 }, |
|
358 |
|
359 /* |
|
360 * Default mouse over DOM event listener. |
|
361 * |
|
362 * Delegates to the _enterTrigger method, |
|
363 * if the mouseover enters a trigger node. |
|
364 */ |
|
365 _onDelegateMouseOver : function(e) { |
|
366 var node = this.getParentTrigger(e.target); |
|
367 if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) { |
|
368 this._enterTrigger(node, e.pageX, e.pageY); |
|
369 } |
|
370 }, |
|
371 |
|
372 /* |
|
373 * Default mouse out DOM event listener |
|
374 * |
|
375 * Delegates to _leaveTrigger if the mouseout |
|
376 * leaves the current trigger node |
|
377 */ |
|
378 _onNodeMouseOut : function(e) { |
|
379 var to = e.relatedTarget; |
|
380 var trigger = e.currentTarget; |
|
381 |
|
382 if (!trigger.contains(to)) { |
|
383 this._leaveTrigger(trigger); |
|
384 } |
|
385 }, |
|
386 |
|
387 /* |
|
388 * Default mouse move DOM event listener |
|
389 */ |
|
390 _onNodeMouseMove : function(e) { |
|
391 this._overTrigger(e.pageX, e.pageY); |
|
392 }, |
|
393 |
|
394 /* |
|
395 * Default handler invoked when the mouse enters |
|
396 * a trigger node. Fires the triggerEnter |
|
397 * event which can be prevented by listeners to |
|
398 * show the tooltip from being displayed. |
|
399 */ |
|
400 _enterTrigger : function(node, x, y) { |
|
401 this._setCurrentTrigger(node, x, y); |
|
402 this.fire("triggerEnter", {node:node, pageX:x, pageY:y}); |
|
403 }, |
|
404 |
|
405 /* |
|
406 * Default handler for the triggerEvent event, |
|
407 * which will setup the timer to display the tooltip, |
|
408 * if the default handler has not been prevented. |
|
409 */ |
|
410 _defTriggerEnterFn : function(e) { |
|
411 var node = e.node; |
|
412 if (!this.get("disabled")) { |
|
413 this._clearTimers(); |
|
414 var delay = (this.get("visible")) ? 0 : this.get("showDelay"); |
|
415 this._timers.show = Y.later(delay, this, this._showTooltip, [node]); |
|
416 } |
|
417 }, |
|
418 |
|
419 /* |
|
420 * Default handler invoked when the mouse leaves |
|
421 * the current trigger node. Fires the triggerLeave |
|
422 * event and sets up the hide timer |
|
423 */ |
|
424 _leaveTrigger : function(node) { |
|
425 this.fire("triggerLeave"); |
|
426 |
|
427 this._clearCurrentTrigger(); |
|
428 this._clearTimers(); |
|
429 |
|
430 this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip); |
|
431 }, |
|
432 |
|
433 /* |
|
434 * Default handler invoked for mousemove events |
|
435 * on the trigger node. Stores the current mouse |
|
436 * x, y positions |
|
437 */ |
|
438 _overTrigger : function(x, y) { |
|
439 this._currTrigger.mouseX = x; |
|
440 this._currTrigger.mouseY = y; |
|
441 }, |
|
442 |
|
443 /* |
|
444 * Shows the tooltip, after moving it to the current mouse |
|
445 * position. |
|
446 */ |
|
447 _showTooltip : function(node) { |
|
448 var x = this._currTrigger.mouseX; |
|
449 var y = this._currTrigger.mouseY; |
|
450 |
|
451 this.move(x + Tooltip.OFFSET_X, y + Tooltip.OFFSET_Y); |
|
452 |
|
453 this.show(); |
|
454 this._clearTimers(); |
|
455 |
|
456 this._timers.hide = Y.later(this.get("autoHideDelay"), this, this._hideTooltip); |
|
457 }, |
|
458 |
|
459 /* |
|
460 * Hides the tooltip, after clearing existing timers. |
|
461 */ |
|
462 _hideTooltip : function() { |
|
463 this._clearTimers(); |
|
464 this.hide(); |
|
465 }, |
|
466 |
|
467 /* |
|
468 * Set the rendered content of the tooltip for the current |
|
469 * trigger, based on (in order of precedence): |
|
470 * |
|
471 * a). The string/node content attribute value |
|
472 * b). From the content lookup map if it is set, or |
|
473 * c). From the title attribute if set. |
|
474 */ |
|
475 _setTriggerContent : function(node) { |
|
476 var content = this.get("content"); |
|
477 if (content && !(content instanceof Node || Lang.isString(content))) { |
|
478 content = content[node.get("id")] || node.getAttribute("title"); |
|
479 } |
|
480 this.setTriggerContent(content); |
|
481 }, |
|
482 |
|
483 /* |
|
484 * Set the currently bound trigger node information, clearing |
|
485 * out the title attribute if set and setting up mousemove/out |
|
486 * listeners. |
|
487 */ |
|
488 _setCurrentTrigger : function(node, x, y) { |
|
489 |
|
490 var currTrigger = this._currTrigger, |
|
491 triggerHandles = this._eventHandles.trigger; |
|
492 |
|
493 this._setTriggerContent(node); |
|
494 |
|
495 triggerHandles.mouseMove = Y.on("mousemove", Y.bind(this._onNodeMouseMove, this), node); |
|
496 triggerHandles.mouseOut = Y.on("mouseout", Y.bind(this._onNodeMouseOut, this), node); |
|
497 |
|
498 var title = node.getAttribute("title"); |
|
499 node.setAttribute("title", ""); |
|
500 |
|
501 currTrigger.mouseX = x; |
|
502 currTrigger.mouseY = y; |
|
503 currTrigger.node = node; |
|
504 currTrigger.title = title; |
|
505 }, |
|
506 |
|
507 /* |
|
508 * Clear out the current trigger state, restoring |
|
509 * the title attribute on the trigger node, |
|
510 * if it was originally set. |
|
511 */ |
|
512 _clearCurrentTrigger : function() { |
|
513 |
|
514 var currTrigger = this._currTrigger, |
|
515 triggerHandles = this._eventHandles.trigger; |
|
516 |
|
517 if (currTrigger.node) { |
|
518 var node = currTrigger.node; |
|
519 var title = currTrigger.title || ""; |
|
520 |
|
521 currTrigger.node = null; |
|
522 currTrigger.title = ""; |
|
523 |
|
524 triggerHandles.mouseMove.detach(); |
|
525 triggerHandles.mouseOut.detach(); |
|
526 triggerHandles.mouseMove = null; |
|
527 triggerHandles.mouseOut = null; |
|
528 |
|
529 node.setAttribute("title", title); |
|
530 } |
|
531 }, |
|
532 |
|
533 /* |
|
534 * Cancel any existing show/hide timers |
|
535 */ |
|
536 _clearTimers : function() { |
|
537 var timers = this._timers; |
|
538 if (timers.hide) { |
|
539 timers.hide.cancel(); |
|
540 timers.hide = null; |
|
541 } |
|
542 if (timers.show) { |
|
543 timers.show.cancel(); |
|
544 timers.show = null; |
|
545 } |
|
546 }, |
|
547 |
|
548 /* |
|
549 * Detach any stored event handles |
|
550 */ |
|
551 _clearHandles : function() { |
|
552 var eventHandles = this._eventHandles; |
|
553 |
|
554 if (eventHandles.delegate) { |
|
555 this._eventHandles.delegate.detach(); |
|
556 } |
|
557 if (eventHandles.trigger.mouseOut) { |
|
558 eventHandles.trigger.mouseOut.detach(); |
|
559 } |
|
560 if (eventHandles.trigger.mouseMove) { |
|
561 eventHandles.trigger.mouseMove.detach(); |
|
562 } |
|
563 } |
|
564 }); |
|
565 |
|
566 // dynamic:false = Modify the existing Tooltip class |
|
567 Tooltip = Y.Base.build(Tooltip.NAME, Tooltip, [Y.WidgetPosition, Y.WidgetStack], {dynamic:false}); |
|
568 |
|
569 var tt = new Tooltip({ |
|
570 triggerNodes:".yui-hastooltip", |
|
571 delegate: "#delegate", |
|
572 content: { |
|
573 tt3: "Tooltip 3 (from lookup)" |
|
574 }, |
|
575 shim:false, |
|
576 zIndex:2 |
|
577 }); |
|
578 tt.render(); |
|
579 |
|
580 tt.on("triggerEnter", function(e) { |
|
581 var node = e.node; |
|
582 if (node && node.get("id") == "tt2") { |
|
583 this.setTriggerContent("Tooltip 2 (from triggerEvent)"); |
|
584 } |
|
585 }); |
|
586 |
|
587 var prevent = Y.Node.get("#prevent"); |
|
588 tt.on("triggerEnter", function(e) { |
|
589 var node = e.node; |
|
590 if (prevent.get("checked")) { |
|
591 if (node && node.get("id") == "tt4") { |
|
592 e.preventDefault(); |
|
593 } |
|
594 } |
|
595 }); |
|
596 }); |
|
597 </script> |
|
598 |
|
599 <!--END SOURCE CODE FOR EXAMPLE =============================== --> |
|
600 |
|
601 |
|
602 </div> |
|
603 </div> |
|
604 </div> |
|
605 |
|
606 <h3>Creating A Tooltip Widget Class</h3> |
|
607 |
|
608 <h4>Basic Class Structure</h4> |
|
609 |
|
610 <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> |
|
611 |
|
612 <p>Namely:</p> |
|
613 |
|
614 <ul> |
|
615 <li>Set up the constructor to invoke the superclass constructor</li> |
|
616 <li>Define a <code>NAME</code> property, to identify the class</li> |
|
617 <li>Define the default attribute configuration, using the <code>ATTRS</code> property</li> |
|
618 <li>Implement prototype methods</li> |
|
619 </ul> |
|
620 |
|
621 <p>This basic structure is shown below:</p> |
|
622 |
|
623 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
624 /* |
|
625 * Required NAME static field, used to identify the Widget class and |
|
626 * used as an event prefix, to generate class names etc. (set to the |
|
627 * class name in camel case). |
|
628 */ |
|
629 Tooltip.NAME = "tooltip"; |
|
630 |
|
631 /* Default Tooltip Attributes */ |
|
632 Tooltip.ATTRS = { |
|
633 |
|
634 /* |
|
635 * The tooltip content. This can either be a fixed content value, |
|
636 * or a map of id-to-values, designed to be used when a single |
|
637 * tooltip is mapped to multiple trigger elements. |
|
638 */ |
|
639 content : { |
|
640 value: null |
|
641 }, |
|
642 |
|
643 /* |
|
644 * The set of nodes to bind to the tooltip instance. Can be a string, |
|
645 * or a node instance. |
|
646 */ |
|
647 triggerNodes : { |
|
648 value: null, |
|
649 setter: function(val) { |
|
650 if (val && Lang.isString(val)) { |
|
651 val = Node.all(val); |
|
652 } |
|
653 return val; |
|
654 } |
|
655 }, |
|
656 |
|
657 /* |
|
658 * The delegate node to which event listeners should be attached. |
|
659 * This node should be an ancestor of all trigger nodes bound |
|
660 * to the instance. By default the document is used. |
|
661 */ |
|
662 delegate : { |
|
663 value: null, |
|
664 setter: function(val) { |
|
665 return Node.get(val) || Node.get("document"); |
|
666 } |
|
667 }, |
|
668 |
|
669 /* |
|
670 * The time to wait, after the mouse enters the trigger node, |
|
671 * to display the tooltip |
|
672 */ |
|
673 showDelay : { |
|
674 value:250 |
|
675 }, |
|
676 |
|
677 /* |
|
678 * The time to wait, after the mouse leaves the trigger node, |
|
679 * to hide the tooltip |
|
680 */ |
|
681 hideDelay : { |
|
682 value:10 |
|
683 }, |
|
684 |
|
685 /* |
|
686 * The time to wait, after the tooltip is first displayed for |
|
687 * a trigger node, to hide it, if the mouse has not left the |
|
688 * trigger node |
|
689 */ |
|
690 autoHideDelay : { |
|
691 value:2000 |
|
692 }, |
|
693 |
|
694 /* |
|
695 * Override the default visibility set by the widget base class |
|
696 */ |
|
697 visible : { |
|
698 value:false |
|
699 }, |
|
700 |
|
701 /* |
|
702 * Override the default XY value set by the widget base class, |
|
703 * to position the tooltip offscreen |
|
704 */ |
|
705 xy: { |
|
706 value:[Tooltip.OFFSCREEN_X, Tooltip.OFFSCREEN_Y] |
|
707 } |
|
708 }; |
|
709 |
|
710 Y.extend(Tooltip, Y.Widget, { |
|
711 // Prototype methods/properties |
|
712 }); |
|
713 </textarea> |
|
714 |
|
715 <h4>Adding WidgetPosition and WidgetStack Extension Support</h4> |
|
716 |
|
717 <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 |
|
718 <code>Base.build</code> to add this support to the <code>Tooltip</code> class. However in this case, we want to modify the Tooltip class, as opposed to extending it |
|
719 to create a completely new class, hence we set the <code>dynamic</code> configuration property to <code>false</code>:</p> |
|
720 |
|
721 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
722 // dynamic:false = Modify the existing Tooltip class |
|
723 Tooltip = Y.Base.build("tooltip", Tooltip, [Y.WidgetPosition, Y.WidgetStack], {dynamic:false}); |
|
724 </textarea> |
|
725 |
|
726 <h4>Lifecycle Methods: initializer, destructor</h4> |
|
727 |
|
728 <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 |
|
729 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> |
|
730 |
|
731 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
732 |
|
733 initializer : function(config) { |
|
734 |
|
735 this._triggerClassName = this.getClassName("trigger"); |
|
736 |
|
737 // Currently bound trigger node information |
|
738 this._currTrigger = { |
|
739 node: null, |
|
740 title: null, |
|
741 mouseX: Tooltip.OFFSCREEN_X, |
|
742 mouseY: Tooltip.OFFSCREEN_Y |
|
743 }; |
|
744 |
|
745 // Event handles - mouse over is set on the delegate |
|
746 // element, mousemove and mouseout are set on the trigger node |
|
747 this._eventHandles = { |
|
748 delegate: null, |
|
749 trigger: { |
|
750 mouseMove : null, |
|
751 mouseOut: null |
|
752 } |
|
753 }; |
|
754 |
|
755 // Show/hide timers |
|
756 this._timers = { |
|
757 show: null, |
|
758 hide: null |
|
759 }; |
|
760 |
|
761 // Publish events introduced by Tooltip. Note the triggerEnter event is preventable, |
|
762 // with the default behavior defined in the _defTriggerEnterFn method |
|
763 this.publish("triggerEnter", {defaultFn: this._defTriggerEnterFn, preventable:true}); |
|
764 this.publish("triggerLeave", {preventable:false}); |
|
765 } |
|
766 </textarea> |
|
767 |
|
768 <p>The <code>destructor</code> is used to clear out stored state, detach any event handles and clear out the show/hide timers:</p> |
|
769 |
|
770 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
771 destructor : function() { |
|
772 this._clearCurrentTrigger(); |
|
773 this._clearTimers(); |
|
774 this._clearHandles(); |
|
775 } |
|
776 </textarea> |
|
777 |
|
778 <h4>Lifecycle Methods: bindUI, syncUI</h4> |
|
779 |
|
780 <p>The <code>bindUI</code> and <code>syncUI</code> are invoked by the base Widget class' <code>renderer</code> method.</p> |
|
781 |
|
782 <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 |
|
783 the DOM listeners required to enable the UI for interaction.</p> |
|
784 |
|
785 <p><code>syncUI</code> is used to sync the UI state from the current widget state, when initially rendered.</p> |
|
786 |
|
787 <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 |
|
788 tooltip does not have any additional elements in needs to add to the DOM, outside of the default Widget boundingBox and contentBox.</p> |
|
789 |
|
790 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
791 bindUI : function() { |
|
792 this.after("delegateChange", this._afterSetDelegate); |
|
793 this.after("nodesChange", this._afterSetNodes); |
|
794 |
|
795 this._bindDelegate(); |
|
796 }, |
|
797 |
|
798 syncUI : function() { |
|
799 this._uiSetNodes(this.get("triggerNodes")); |
|
800 } |
|
801 </textarea> |
|
802 |
|
803 <h4>Attribute Supporting Methods</h4> |
|
804 |
|
805 <p>Tooltip's <code>triggerNodes</code>, which defines the set of nodes which should trigger this tooltip instance, |
|
806 has a couple of supporting methods associated with it.</p> |
|
807 |
|
808 <p>The <code>_afterSetNodes</code> method is the default attribute change event handler for the <code>triggerNodes</code> |
|
809 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> |
|
810 |
|
811 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
812 _afterSetNodes : function(e) { |
|
813 this._uiSetNodes(e.newVal); |
|
814 }, |
|
815 |
|
816 _uiSetNodes : function(nodes) { |
|
817 if (this._triggerNodes) { |
|
818 this._triggerNodes.removeClass(this._triggerClassName); |
|
819 } |
|
820 |
|
821 if (nodes) { |
|
822 this._triggerNodes = nodes; |
|
823 this._triggerNodes.addClass(this._triggerClassName); |
|
824 } |
|
825 }, |
|
826 </textarea> |
|
827 |
|
828 <p>Similarly the <code>_afterSetDelegate</code> method is the default attributechange listener for the <code>delegate</code> attribute, |
|
829 and invokes <code>_bindDelegate</code> to set up the listeners when a new delegate node is set.</p> |
|
830 |
|
831 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
832 _afterSetDelegate : function(e) { |
|
833 this._bindDelegate(e.newVal); |
|
834 }, |
|
835 |
|
836 _bindDelegate : function() { |
|
837 var eventHandles = this._eventHandles; |
|
838 |
|
839 if (eventHandles.delegate) { |
|
840 eventHandles.delegate.detach(); |
|
841 eventHandles.delegate = null; |
|
842 } |
|
843 eventHandles.delegate = Y.on("mouseover", Y.bind(this._onDelegateMouseOver, this), this.get("delegate")); |
|
844 }, |
|
845 </textarea> |
|
846 |
|
847 <h4>DOM Event Handlers</h4> |
|
848 |
|
849 <p>Tooltips interaction revolves around the <code>mouseover</code>, <code>mousemove</code> and <code>mouseout</code> DOM events. The mouseover listener is the only listener set up initially, on the <code>delegate</code> node:</p> |
|
850 |
|
851 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
852 _onDelegateMouseOver : function(e) { |
|
853 var node = this.getParentTrigger(e.target); |
|
854 if (node && (!this._currTrigger.node || !node.compareTo(this._currTrigger.node))) { |
|
855 this._enterTrigger(node, e.pageX, e.pageY); |
|
856 } |
|
857 } |
|
858 </textarea> |
|
859 |
|
860 <p>It attempts to determine if the mouse is entering a trigger node. It ignores mouseover events generated from elements |
|
861 inside the current trigger node (for example when mousing out of a child element of a trigger node). If it determines that the mouse is entering a trigger node, |
|
862 the delegates to the <code>_enterTrigger</code> method to setup the current trigger state and attaches mousemove and mouseout listeners on the current trigger node.</p> |
|
863 |
|
864 <p>The mouse out listener delegates to the <code>_leaveTrigger</code> method, if it determines the mouse is leaving the trigger node:</p> |
|
865 |
|
866 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
867 _onNodeMouseOut : function(e) { |
|
868 var to = e.relatedTarget; |
|
869 var trigger = e.currentTarget; |
|
870 |
|
871 if (!trigger.contains(to)) { |
|
872 this._leaveTrigger(trigger); |
|
873 } |
|
874 } |
|
875 </textarea> |
|
876 |
|
877 <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> |
|
878 |
|
879 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
880 _onNodeMouseMove : function(e) { |
|
881 this._overTrigger(e.pageX, e.pageY); |
|
882 } |
|
883 </textarea> |
|
884 |
|
885 <h4>Trigger Event Delegates: _enterTrigger, _leaveTrigger, _overTrigger</h4> |
|
886 |
|
887 <p>As seen above, the DOM event handlers delegate to the <code>_enterTrigger, _leaveTrigger and _overTrigger</code> methods to update the |
|
888 Tooltip state based on the currently active trigger node.</p> |
|
889 |
|
890 <p>The <code>_enterTrigger</code> method sets the current trigger state (which node is the current tooltip trigger, |
|
891 what the current mouse XY position is, etc.). The method also fires the <code>triggerEnter</code> event, whose default function actually handles |
|
892 showing the tooltip after the configured <code>showDelay</code> period. The <code>triggerEnter</code> event can be prevented by listeners, allowing |
|
893 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> |
|
894 |
|
895 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
896 _enterTrigger : function(node, x, y) { |
|
897 this._setCurrentTrigger(node, x, y); |
|
898 this.fire("triggerEnter", null, node, x, y); |
|
899 }, |
|
900 |
|
901 _defTriggerEnterFn : function(e) { |
|
902 var node = e.node; |
|
903 if (!this.get("disabled")) { |
|
904 this._clearTimers(); |
|
905 var delay = (this.get("visible")) ? 0 : this.get("showDelay"); |
|
906 this._timers.show = Y.later(delay, this, this._showTooltip, [node]); |
|
907 } |
|
908 }, |
|
909 </textarea> |
|
910 |
|
911 <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 |
|
912 the <code>hideDelay</code> timer. It fires a <code>triggerLeave</code> event, but cannot be prevented, and has no default behavior to prevent:</p> |
|
913 |
|
914 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
915 _leaveTrigger : function(node) { |
|
916 this.fire("triggerLeave"); |
|
917 |
|
918 this._clearCurrentTrigger(); |
|
919 this._clearTimers(); |
|
920 |
|
921 this._timers.hide = Y.later(this.get("hideDelay"), this, this._hideTooltip); |
|
922 }, |
|
923 </textarea> |
|
924 |
|
925 <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> |
|
926 |
|
927 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
928 _overTrigger : function(x, y) { |
|
929 this._currTrigger.mouseX = x; |
|
930 this._currTrigger.mouseY = y; |
|
931 } |
|
932 </textarea> |
|
933 |
|
934 <h4>Setting Tooltip Content</h4> |
|
935 |
|
936 <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> |
|
937 |
|
938 <ol> |
|
939 <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 |
|
940 for all triggerNodes</li> |
|
941 <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 |
|
942 will be set using the map, when the tooltip is triggered for the node.</li> |
|
943 <li>Setting the title attribute on the trigger node. The value of the title attribute is used to set the tooltip content, |
|
944 when triggered for the node.</li> |
|
945 <li>By calling the <code>setTriggerContent</code> method to set content for a specific trigger node, in a <code>triggerEnter</code> event listener.</li> |
|
946 </ol> |
|
947 |
|
948 <p>The precedence of these methods is handled in the <code>_setTriggerContent</code> method, invoked when the mouse enters a trigger:</p> |
|
949 |
|
950 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
951 _setTriggerContent : function(node) { |
|
952 var content = this.get("content"); |
|
953 if (content && !(content instanceof Node || Lang.isString(content))) { |
|
954 content = content[node.get("id")] || node.getAttribute("title"); |
|
955 } |
|
956 this.setTriggerContent(content); |
|
957 }, |
|
958 |
|
959 setTriggerContent : function(content) { |
|
960 var contentBox = this.get("contentBox"); |
|
961 contentBox.set("innerHTML", ""); |
|
962 |
|
963 if (content) { |
|
964 if (content instanceof Node) { |
|
965 for (var i = 0, l = content.size(); i < l; ++i) { |
|
966 contentBox.appendChild(content.item(i)); |
|
967 } |
|
968 } else if (Lang.isString(content)) { |
|
969 contentBox.set("innerHTML", content); |
|
970 } |
|
971 } |
|
972 } |
|
973 </textarea> |
|
974 |
|
975 <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> |
|
976 |
|
977 <h4>Using Tooltip</h4> |
|
978 |
|
979 <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="delegate"</code> which will act as the <code>delegate</code> node.</p> |
|
980 |
|
981 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
982 var tt = new Tooltip({ |
|
983 triggerNodes:".yui-hastooltip", |
|
984 delegate: "#delegate", |
|
985 content: { |
|
986 tt3: "Tooltip 3 (from lookup)" |
|
987 }, |
|
988 shim:false, |
|
989 zIndex:2 |
|
990 }); |
|
991 tt.render(); |
|
992 </textarea> |
|
993 |
|
994 <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> |
|
995 |
|
996 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
997 tt.on("triggerEnter", function(e) { |
|
998 var node = e.node; |
|
999 if (node && node.get("id") == "tt2") { |
|
1000 this.setTriggerContent("Tooltip 2 (from triggerEvent)"); |
|
1001 } |
|
1002 }); |
|
1003 </textarea> |
|
1004 |
|
1005 <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> |
|
1006 |
|
1007 <textarea name="code" class="JScript" rows="1" cols="60"> |
|
1008 var prevent = Y.Node.get("#prevent"); |
|
1009 tt.on("triggerEnter", function(e) { |
|
1010 var node = e.node; |
|
1011 if (prevent.get("checked")) { |
|
1012 if (node && node.get("id") == "tt4") { |
|
1013 e.preventDefault(); |
|
1014 } |
|
1015 } |
|
1016 }); |
|
1017 </textarea> |
|
1018 </div> |
|
1019 <div class="yui-u sidebar"> |
|
1020 |
|
1021 |
|
1022 <div id="examples" class="mod box4"> |
|
1023 <div class="hd"> |
|
1024 <h4> |
|
1025 Widget Examples:</h4> |
|
1026 </div> |
|
1027 <div class="bd"> |
|
1028 <ul> |
|
1029 <li><a href='../widget/widget-extend.html'>Extending the base widget class</a></li><li><a href='../widget/widget-build.html'>Creating custom widget classes</a></li><li class='selected'><a href='../widget/widget-tooltip.html'>Creating a simple Tooltip widget</a></li> </ul> |
|
1030 </div> |
|
1031 </div> |
|
1032 |
|
1033 <div class="mod box4"> |
|
1034 <div class="hd"> |
|
1035 <h4>More Widget Resources:</h4> |
|
1036 </div> |
|
1037 <div class="bd"> |
|
1038 <ul> |
|
1039 <!-- <li><a href="http://developer.yahoo.com/yui/widget/">User's Guide</a> (external)</li> --> |
|
1040 <li><a href="../../api/module_widget.html">API Documentation</a></li> |
|
1041 </ul> |
|
1042 </div> |
|
1043 </div> |
|
1044 </div> |
|
1045 </div> |
|
1046 |
|
1047 </div> |
|
1048 </div> |
|
1049 |
|
1050 |
|
1051 <div class="yui-b toc3" id="tocWrapper"> |
|
1052 <!-- TABLE OF CONTENTS --> |
|
1053 <div id="toc"> |
|
1054 |
|
1055 <ul> |
|
1056 <li class="sect first">YUI 3.x Project</li><li class="item"><a title="The Yahoo! User Interface (YUI) Library, 3.x Branch, " href="http://developer.yahoo.com/yui/3/">YUI 3 Web Site (external)</a></li><li class="item"><a title="Examples of every YUI utility and control in action" href="../../examples/">YUI 3 Examples</a></li><li class="item"><a title="Instantly searchable API documentation for the entire YUI library." href="../../api/">YUI 3 API Docs</a></li><li class="item"><a title="The YUI Library can be downloaded from SourceForge" href="http://sourceforge.net/projects/yui/">YUI 3 on Sourceforge (external)</a></li><li class="item"><a title="YUI is free and open, offered under a BSD license." href="http://developer.yahoo.com/yui/3/license.html">YUI License (external)</a></li><li class="sect">YUI 3 Core - Examples</li><li class="item"><a title="YUI (Global Prerequisite) - Functional Examples" href="../../examples/yui/index.html">YUI (Global Prerequisite)</a></li><li class="item"><a title="Event - Functional Examples" href="../../examples/event/index.html">Event</a></li><li class="item"><a title="Node - Functional Examples" href="../../examples/node/index.html">Node</a></li><li class="sect">YUI 3 Component Infrastructure - Examples</li><li class="item"><a title="Attribute - Functional Examples" href="../../examples/attribute/index.html">Attribute</a></li><li class="item"><a title="Plugin - Functional Examples" href="../../examples/plugin/index.html">Plugin</a></li><li class="selected "><a title="Widget - Functional Examples" href="../../examples/widget/index.html">Widget</a></li><li class="sect">YUI 3 Utilities - Examples</li><li class="item"><a title="Animation - Functional Examples" href="../../examples/anim/index.html">Animation</a></li><li class="item"><a title="Cache - Functional Examples" href="../../examples/cache/index.html">Cache</a></li><li class="item"><a title="Cookie - Functional Examples" href="../../examples/cookie/index.html">Cookie</a></li><li class="item"><a title="DataSchema - Functional Examples" href="../../examples/dataschema/index.html">DataSchema</a></li><li class="item"><a title="DataSource - Functional Examples" href="../../examples/datasource/index.html">DataSource</a></li><li class="item"><a title="DataType - Functional Examples" href="../../examples/datatype/index.html">DataType</a></li><li class="item"><a title="Drag & Drop - Functional Examples" href="../../examples/dd/index.html">Drag & Drop</a></li><li class="item"><a title="Get - Functional Examples" href="../../examples/get/index.html">Get</a></li><li class="item"><a title="History - Functional Examples" href="../../examples/history/index.html">History</a></li><li class="item"><a title="ImageLoader - Functional Examples" href="../../examples/imageloader/index.html">ImageLoader</a></li><li class="item"><a title="IO - Functional Examples" href="../../examples/io/index.html">IO</a></li><li class="item"><a title="JSON (JavaScript Object Notation) - Functional Examples" href="../../examples/json/index.html">JSON</a></li><li class="item"><a title="Queue - Functional Examples" href="../../examples/queue/index.html">Queue</a></li><li class="item"><a title="Stylesheet - Functional Examples" href="../../examples/stylesheet/index.html">Stylesheet</a></li><li class="sect">YUI 3 Widgets - Examples</li><li class="item"><a title="Overlay - Functional Examples" href="../../examples/overlay/index.html">Overlay</a></li><li class="item"><a title="Slider - Functional Examples" href="../../examples/slider/index.html">Slider</a></li><li class="sect">YUI 3 Node Plugins - Examples</li><li class="item"><a title="FocusManager Node Plugin - Functional Examples" href="../../examples/node-focusmanager/index.html">FocusManager Node Plugin</a></li><li class="item"><a title="MenuNav Node Plugin - Functional Examples" href="../../examples/node-menunav/index.html">MenuNav Node Plugin</a></li><li class="sect">YUI 3 CSS - Examples</li><li class="item"><a title="YUI CSS Reset - Functional Examples" href="../../examples/cssreset/index.html">CSS Reset</a></li><li class="item"><a title="YUI Fonts - Functional Examples" href="../../examples/cssfonts/index.html">CSS Fonts</a></li><li class="item"><a title="YUI Base - Functional Examples" href="../../examples/cssbase/index.html">CSS Base</a></li><li class="sect">YUI 3 Developer Tools - Examples</li><li class="item"><a title="Console - Functional Examples" href="../../examples/console/index.html">Console</a></li><li class="item"><a title="Profiler - Functional Examples" href="../../examples/profiler/index.html">Profiler</a></li><li class="item"><a title="Test - Functional Examples" href="../../examples/test/index.html">Test</a></li><li class="sect">The YUI Community</li><li class="item"><a title="The Yahoo! User Interface Blog" href="http://yuiblog.com">YUI Blog (external)</a></li><li class="item"><a title="The Yahoo! Group YDN-JavaScript hosts the YUI community forum" href="http://tech.groups.yahoo.com/group/ydn-javascript/">YUI Forum (external)</a></li><li class="item"><a title="The Yahoo! Group yui3 is dedicated to the 3.x branch of the Yahoo! User Interface (YUI) Library." href="http://tech.groups.yahoo.com/group/yui3/">YUI 3 Forum (external)</a></li><li class="item"><a title="YUI is used by Yahoo! and by hundreds of other sites, including many you know and love." href="/yui/poweredby/">YUI Sightings (external)</a></li><li class="item"><a title="Videos and podcasts from the YUI Team and from the Yahoo! frontend engineering community." href="http://developer.yahoo.com/yui/theater/">YUI Theater (external)</a></li><li class="sect">YUI Articles on the YUI Website</li><li class="item"><a title="Answers to Frequently Asked Questions about the YUI Library" href="http://developer.yahoo.com/yui/articles/faq/">YUI FAQ (external)</a></li><li class="item"><a title="Yahoo!'s philosophy of Graded Browser Support" href="http://developer.yahoo.com/yui/articles/gbs/">Graded Browser Support (external)</a></li><li class="item"><a title="Reporting Bugs and Making Feature Requests for YUI Components" href="http://developer.yahoo.com/yui/articles/reportingbugs/">Bug Reports/Feature Requests (external)</a></li><li class="item"><a title="Serve YUI source files from Yahoo! -- free, fast, and simple" href="http://developer.yahoo.com/yui/3/articles/hosting/">Serving YUI Files from Yahoo! (external)</a></li></ul> |
|
1057 </div> |
|
1058 </div> |
|
1059 </div><!--closes bd--> |
|
1060 |
|
1061 <div id="ft"> |
|
1062 <p class="first">Copyright © 2009 Yahoo! Inc. All rights reserved.</p> |
|
1063 <p><a href="http://privacy.yahoo.com/privacy/us/devel/index.html">Privacy Policy</a> - |
|
1064 <a href="http://docs.yahoo.com/info/terms/">Terms of Service</a> - |
|
1065 <a href="http://docs.yahoo.com/info/copyright/copyright.html">Copyright Policy</a> - |
|
1066 <a href="http://careers.yahoo.com/">Job Openings</a></p> |
|
1067 </div> |
|
1068 </div> |
|
1069 <script src="../../assets/dpSyntaxHighlighter.js"></script> |
|
1070 <script language="javascript"> |
|
1071 dp.SyntaxHighlighter.HighlightAll('code'); |
|
1072 </script> |
|
1073 </body> |
|
1074 </html> |