summaryrefslogtreecommitdiff
path: root/frontend/beta/js/YUI
Unidiff
Diffstat (limited to 'frontend/beta/js/YUI') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/beta/js/YUI/animation.js1272
-rw-r--r--frontend/beta/js/YUI/autocomplete.js3066
-rw-r--r--frontend/beta/js/YUI/calendar.js4239
-rw-r--r--frontend/beta/js/YUI/connection.js960
-rw-r--r--frontend/beta/js/YUI/container.js4561
-rw-r--r--frontend/beta/js/YUI/dom.js881
-rw-r--r--frontend/beta/js/YUI/dragdrop.js2940
-rw-r--r--frontend/beta/js/YUI/event.js1738
-rw-r--r--frontend/beta/js/YUI/logger.js1559
-rw-r--r--frontend/beta/js/YUI/menu.js6780
-rw-r--r--frontend/beta/js/YUI/slider.js1113
-rw-r--r--frontend/beta/js/YUI/tabview.js1950
-rw-r--r--frontend/beta/js/YUI/treeview.js2182
-rw-r--r--frontend/beta/js/YUI/yahoo.js145
14 files changed, 33386 insertions, 0 deletions
diff --git a/frontend/beta/js/YUI/animation.js b/frontend/beta/js/YUI/animation.js
new file mode 100644
index 0000000..333f946
--- a/dev/null
+++ b/frontend/beta/js/YUI/animation.js
@@ -0,0 +1,1272 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9 * The animation module provides allows effects to be added to HTMLElements.
10 * @module animation
11 */
12
13/**
14 *
15 * Base animation class that provides the interface for building animated effects.
16 * <p>Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);</p>
17 * @class Anim
18 * @namespace YAHOO.util
19 * @requires YAHOO.util.AnimMgr
20 * @requires YAHOO.util.Easing
21 * @requires YAHOO.util.Dom
22 * @requires YAHOO.util.Event
23 * @requires YAHOO.util.CustomEvent
24 * @constructor
25 * @param {String | HTMLElement} el Reference to the element that will be animated
26 * @param {Object} attributes The attribute(s) to be animated.
27 * Each attribute is an object with at minimum a "to" or "by" member defined.
28 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
29 * All attribute names use camelCase.
30 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
31 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
32 */
33
34YAHOO.util.Anim = function(el, attributes, duration, method) {
35 if (el) {
36 this.init(el, attributes, duration, method);
37 }
38};
39
40YAHOO.util.Anim.prototype = {
41 /**
42 * Provides a readable name for the Anim instance.
43 * @method toString
44 * @return {String}
45 */
46 toString: function() {
47 var el = this.getEl();
48 var id = el.id || el.tagName;
49 return ("Anim " + id);
50 },
51
52 patterns: { // cached for performance
53 noNegatives: /width|height|opacity|padding/i, // keep at zero or above
54 offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default
55 defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default
56 offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset
57 },
58
59 /**
60 * Returns the value computed by the animation's "method".
61 * @method doMethod
62 * @param {String} attr The name of the attribute.
63 * @param {Number} start The value this attribute should start from for this animation.
64 * @param {Number} end The value this attribute should end at for this animation.
65 * @return {Number} The Value to be applied to the attribute.
66 */
67 doMethod: function(attr, start, end) {
68 return this.method(this.currentFrame, start, end - start, this.totalFrames);
69 },
70
71 /**
72 * Applies a value to an attribute.
73 * @method setAttribute
74 * @param {String} attr The name of the attribute.
75 * @param {Number} val The value to be applied to the attribute.
76 * @param {String} unit The unit ('px', '%', etc.) of the value.
77 */
78 setAttribute: function(attr, val, unit) {
79 if ( this.patterns.noNegatives.test(attr) ) {
80 val = (val > 0) ? val : 0;
81 }
82
83 YAHOO.util.Dom.setStyle(this.getEl(), attr, val + unit);
84 },
85
86 /**
87 * Returns current value of the attribute.
88 * @method getAttribute
89 * @param {String} attr The name of the attribute.
90 * @return {Number} val The current value of the attribute.
91 */
92 getAttribute: function(attr) {
93 var el = this.getEl();
94 var val = YAHOO.util.Dom.getStyle(el, attr);
95
96 if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
97 return parseFloat(val);
98 }
99
100 var a = this.patterns.offsetAttribute.exec(attr) || [];
101 var pos = !!( a[3] ); // top or left
102 var box = !!( a[2] ); // width or height
103
104 // use offsets for width/height and abs pos top/left
105 if ( box || (YAHOO.util.Dom.getStyle(el, 'position') == 'absolute' && pos) ) {
106 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
107 } else { // default to zero for other 'auto'
108 val = 0;
109 }
110
111 return val;
112 },
113
114 /**
115 * Returns the unit to use when none is supplied.
116 * @method getDefaultUnit
117 * @param {attr} attr The name of the attribute.
118 * @return {String} The default unit to be used.
119 */
120 getDefaultUnit: function(attr) {
121 if ( this.patterns.defaultUnit.test(attr) ) {
122 return 'px';
123 }
124
125 return '';
126 },
127
128 /**
129 * Sets the actual values to be used during the animation.
130 * @method setRuntimeAttribute
131 * Should only be needed for subclass use.
132 * @param {Object} attr The attribute object
133 * @private
134 */
135 setRuntimeAttribute: function(attr) {
136 var start;
137 var end;
138 var attributes = this.attributes;
139
140 this.runtimeAttributes[attr] = {};
141
142 var isset = function(prop) {
143 return (typeof prop !== 'undefined');
144 };
145
146 if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) {
147 return false; // note return; nothing to animate to
148 }
149
150 start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
151
152 // To beats by, per SMIL 2.1 spec
153 if ( isset(attributes[attr]['to']) ) {
154 end = attributes[attr]['to'];
155 } else if ( isset(attributes[attr]['by']) ) {
156 if (start.constructor == Array) {
157 end = [];
158 for (var i = 0, len = start.length; i < len; ++i) {
159 end[i] = start[i] + attributes[attr]['by'][i];
160 }
161 } else {
162 end = start + attributes[attr]['by'];
163 }
164 }
165
166 this.runtimeAttributes[attr].start = start;
167 this.runtimeAttributes[attr].end = end;
168
169 // set units if needed
170 this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
171 },
172
173 /**
174 * Constructor for Anim instance.
175 * @method init
176 * @param {String | HTMLElement} el Reference to the element that will be animated
177 * @param {Object} attributes The attribute(s) to be animated.
178 * Each attribute is an object with at minimum a "to" or "by" member defined.
179 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
180 * All attribute names use camelCase.
181 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
182 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
183 */
184 init: function(el, attributes, duration, method) {
185 /**
186 * Whether or not the animation is running.
187 * @property isAnimated
188 * @private
189 * @type Boolean
190 */
191 var isAnimated = false;
192
193 /**
194 * A Date object that is created when the animation begins.
195 * @property startTime
196 * @private
197 * @type Date
198 */
199 var startTime = null;
200
201 /**
202 * The number of frames this animation was able to execute.
203 * @property actualFrames
204 * @private
205 * @type Int
206 */
207 var actualFrames = 0;
208
209 /**
210 * The element to be animated.
211 * @property el
212 * @private
213 * @type HTMLElement
214 */
215 el = YAHOO.util.Dom.get(el);
216
217 /**
218 * The collection of attributes to be animated.
219 * Each attribute must have at least a "to" or "by" defined in order to animate.
220 * If "to" is supplied, the animation will end with the attribute at that value.
221 * If "by" is supplied, the animation will end at that value plus its starting value.
222 * If both are supplied, "to" is used, and "by" is ignored.
223 * Optional additional member include "from" (the value the attribute should start animating from, defaults to current value), and "unit" (the units to apply to the values).
224 * @property attributes
225 * @type Object
226 */
227 this.attributes = attributes || {};
228
229 /**
230 * The length of the animation. Defaults to "1" (second).
231 * @property duration
232 * @type Number
233 */
234 this.duration = duration || 1;
235
236 /**
237 * The method that will provide values to the attribute(s) during the animation.
238 * Defaults to "YAHOO.util.Easing.easeNone".
239 * @property method
240 * @type Function
241 */
242 this.method = method || YAHOO.util.Easing.easeNone;
243
244 /**
245 * Whether or not the duration should be treated as seconds.
246 * Defaults to true.
247 * @property useSeconds
248 * @type Boolean
249 */
250 this.useSeconds = true; // default to seconds
251
252 /**
253 * The location of the current animation on the timeline.
254 * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
255 * @property currentFrame
256 * @type Int
257 */
258 this.currentFrame = 0;
259
260 /**
261 * The total number of frames to be executed.
262 * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time.
263 * @property totalFrames
264 * @type Int
265 */
266 this.totalFrames = YAHOO.util.AnimMgr.fps;
267
268
269 /**
270 * Returns a reference to the animated element.
271 * @method getEl
272 * @return {HTMLElement}
273 */
274 this.getEl = function() { return el; };
275
276 /**
277 * Checks whether the element is currently animated.
278 * @method isAnimated
279 * @return {Boolean} current value of isAnimated.
280 */
281 this.isAnimated = function() {
282 return isAnimated;
283 };
284
285 /**
286 * Returns the animation start time.
287 * @method getStartTime
288 * @return {Date} current value of startTime.
289 */
290 this.getStartTime = function() {
291 return startTime;
292 };
293
294 this.runtimeAttributes = {};
295
296
297
298 /**
299 * Starts the animation by registering it with the animation manager.
300 * @method animate
301 */
302 this.animate = function() {
303 if ( this.isAnimated() ) { return false; }
304
305 this.currentFrame = 0;
306
307 this.totalFrames = ( this.useSeconds ) ? Math.ceil(YAHOO.util.AnimMgr.fps * this.duration) : this.duration;
308
309 YAHOO.util.AnimMgr.registerElement(this);
310 };
311
312 /**
313 * Stops the animation. Normally called by AnimMgr when animation completes.
314 * @method stop
315 * @param {Boolean} finish (optional) If true, animation will jump to final frame.
316 */
317 this.stop = function(finish) {
318 if (finish) {
319 this.currentFrame = this.totalFrames;
320 this._onTween.fire();
321 }
322 YAHOO.util.AnimMgr.stop(this);
323 };
324
325 var onStart = function() {
326 this.onStart.fire();
327
328 this.runtimeAttributes = {};
329 for (var attr in this.attributes) {
330 this.setRuntimeAttribute(attr);
331 }
332
333 isAnimated = true;
334 actualFrames = 0;
335 startTime = new Date();
336 };
337
338 /**
339 * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s).
340 * @private
341 */
342
343 var onTween = function() {
344 var data = {
345 duration: new Date() - this.getStartTime(),
346 currentFrame: this.currentFrame
347 };
348
349 data.toString = function() {
350 return (
351 'duration: ' + data.duration +
352 ', currentFrame: ' + data.currentFrame
353 );
354 };
355
356 this.onTween.fire(data);
357
358 var runtimeAttributes = this.runtimeAttributes;
359
360 for (var attr in runtimeAttributes) {
361 this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
362 }
363
364 actualFrames += 1;
365 };
366
367 var onComplete = function() {
368 var actual_duration = (new Date() - startTime) / 1000 ;
369
370 var data = {
371 duration: actual_duration,
372 frames: actualFrames,
373 fps: actualFrames / actual_duration
374 };
375
376 data.toString = function() {
377 return (
378 'duration: ' + data.duration +
379 ', frames: ' + data.frames +
380 ', fps: ' + data.fps
381 );
382 };
383
384 isAnimated = false;
385 actualFrames = 0;
386 this.onComplete.fire(data);
387 };
388
389 /**
390 * Custom event that fires after onStart, useful in subclassing
391 * @private
392 */
393 this._onStart = new YAHOO.util.CustomEvent('_start', this, true);
394
395 /**
396 * Custom event that fires when animation begins
397 * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction)
398 * @event onStart
399 */
400 this.onStart = new YAHOO.util.CustomEvent('start', this);
401
402 /**
403 * Custom event that fires between each frame
404 * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction)
405 * @event onTween
406 */
407 this.onTween = new YAHOO.util.CustomEvent('tween', this);
408
409 /**
410 * Custom event that fires after onTween
411 * @private
412 */
413 this._onTween = new YAHOO.util.CustomEvent('_tween', this, true);
414
415 /**
416 * Custom event that fires when animation ends
417 * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction)
418 * @event onComplete
419 */
420 this.onComplete = new YAHOO.util.CustomEvent('complete', this);
421 /**
422 * Custom event that fires after onComplete
423 * @private
424 */
425 this._onComplete = new YAHOO.util.CustomEvent('_complete', this, true);
426
427 this._onStart.subscribe(onStart);
428 this._onTween.subscribe(onTween);
429 this._onComplete.subscribe(onComplete);
430 }
431};
432
433/**
434 * Handles animation queueing and threading.
435 * Used by Anim and subclasses.
436 * @class AnimMgr
437 * @namespace YAHOO.util
438 */
439YAHOO.util.AnimMgr = new function() {
440 /**
441 * Reference to the animation Interval.
442 * @property thread
443 * @private
444 * @type Int
445 */
446 var thread = null;
447
448 /**
449 * The current queue of registered animation objects.
450 * @property queue
451 * @private
452 * @type Array
453 */
454 var queue = [];
455
456 /**
457 * The number of active animations.
458 * @property tweenCount
459 * @private
460 * @type Int
461 */
462 var tweenCount = 0;
463
464 /**
465 * Base frame rate (frames per second).
466 * Arbitrarily high for better x-browser calibration (slower browsers drop more frames).
467 * @property fps
468 * @type Int
469 *
470 */
471 this.fps = 200;
472
473 /**
474 * Interval delay in milliseconds, defaults to fastest possible.
475 * @property delay
476 * @type Int
477 *
478 */
479 this.delay = 1;
480
481 /**
482 * Adds an animation instance to the animation queue.
483 * All animation instances must be registered in order to animate.
484 * @method registerElement
485 * @param {object} tween The Anim instance to be be registered
486 */
487 this.registerElement = function(tween) {
488 queue[queue.length] = tween;
489 tweenCount += 1;
490 tween._onStart.fire();
491 this.start();
492 };
493
494 /**
495 * removes an animation instance from the animation queue.
496 * All animation instances must be registered in order to animate.
497 * @method unRegister
498 * @param {object} tween The Anim instance to be be registered
499 * @param {Int} index The index of the Anim instance
500 * @private
501 */
502 this.unRegister = function(tween, index) {
503 tween._onComplete.fire();
504 index = index || getIndex(tween);
505 if (index != -1) { queue.splice(index, 1); }
506
507 tweenCount -= 1;
508 if (tweenCount <= 0) { this.stop(); }
509 };
510
511 /**
512 * Starts the animation thread.
513 * Only one thread can run at a time.
514 * @method start
515 */
516 this.start = function() {
517 if (thread === null) { thread = setInterval(this.run, this.delay); }
518 };
519
520 /**
521 * Stops the animation thread or a specific animation instance.
522 * @method stop
523 * @param {object} tween A specific Anim instance to stop (optional)
524 * If no instance given, Manager stops thread and all animations.
525 */
526 this.stop = function(tween) {
527 if (!tween) {
528 clearInterval(thread);
529 for (var i = 0, len = queue.length; i < len; ++i) {
530 if (queue[i].isAnimated()) {
531 this.unRegister(tween, i);
532 }
533 }
534 queue = [];
535 thread = null;
536 tweenCount = 0;
537 }
538 else {
539 this.unRegister(tween);
540 }
541 };
542
543 /**
544 * Called per Interval to handle each animation frame.
545 * @method run
546 */
547 this.run = function() {
548 for (var i = 0, len = queue.length; i < len; ++i) {
549 var tween = queue[i];
550 if ( !tween || !tween.isAnimated() ) { continue; }
551
552 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
553 {
554 tween.currentFrame += 1;
555
556 if (tween.useSeconds) {
557 correctFrame(tween);
558 }
559 tween._onTween.fire();
560 }
561 else { YAHOO.util.AnimMgr.stop(tween, i); }
562 }
563 };
564
565 var getIndex = function(anim) {
566 for (var i = 0, len = queue.length; i < len; ++i) {
567 if (queue[i] == anim) {
568 return i; // note return;
569 }
570 }
571 return -1;
572 };
573
574 /**
575 * On the fly frame correction to keep animation on time.
576 * @method correctFrame
577 * @private
578 * @param {Object} tween The Anim instance being corrected.
579 */
580 var correctFrame = function(tween) {
581 var frames = tween.totalFrames;
582 var frame = tween.currentFrame;
583 var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
584 var elapsed = (new Date() - tween.getStartTime());
585 var tweak = 0;
586
587 if (elapsed < tween.duration * 1000) { // check if falling behind
588 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
589 } else { // went over duration, so jump to end
590 tweak = frames - (frame + 1);
591 }
592 if (tweak > 0 && isFinite(tweak)) { // adjust if needed
593 if (tween.currentFrame + tweak >= frames) {// dont go past last frame
594 tweak = frames - (frame + 1);
595 }
596
597 tween.currentFrame += tweak;
598 }
599 };
600};
601/**
602 * Used to calculate Bezier splines for any number of control points.
603 * @class Bezier
604 * @namespace YAHOO.util
605 *
606 */
607YAHOO.util.Bezier = new function()
608{
609 /**
610 * Get the current position of the animated element based on t.
611 * Each point is an array of "x" and "y" values (0 = x, 1 = y)
612 * At least 2 points are required (start and end).
613 * First point is start. Last point is end.
614 * Additional control points are optional.
615 * @method getPosition
616 * @param {Array} points An array containing Bezier points
617 * @param {Number} t A number between 0 and 1 which is the basis for determining current position
618 * @return {Array} An array containing int x and y member data
619 */
620 this.getPosition = function(points, t)
621 {
622 var n = points.length;
623 var tmp = [];
624
625 for (var i = 0; i < n; ++i){
626 tmp[i] = [points[i][0], points[i][1]]; // save input
627 }
628
629 for (var j = 1; j < n; ++j) {
630 for (i = 0; i < n - j; ++i) {
631 tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
632 tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
633 }
634 }
635
636 return [ tmp[0][0], tmp[0][1] ];
637
638 };
639};
640/**
641 * Anim subclass for color transitions.
642 * <p>Usage: <code>var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);</code> Color values can be specified with either 112233, #112233,
643 * [255,255,255], or rgb(255,255,255)</p>
644 * @class ColorAnim
645 * @namespace YAHOO.util
646 * @requires YAHOO.util.Anim
647 * @requires YAHOO.util.AnimMgr
648 * @requires YAHOO.util.Easing
649 * @requires YAHOO.util.Bezier
650 * @requires YAHOO.util.Dom
651 * @requires YAHOO.util.Event
652 * @constructor
653 * @extends YAHOO.util.Anim
654 * @param {HTMLElement | String} el Reference to the element that will be animated
655 * @param {Object} attributes The attribute(s) to be animated.
656 * Each attribute is an object with at minimum a "to" or "by" member defined.
657 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
658 * All attribute names use camelCase.
659 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
660 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
661 */
662(function() {
663 YAHOO.util.ColorAnim = function(el, attributes, duration, method) {
664 YAHOO.util.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
665 };
666
667 YAHOO.extend(YAHOO.util.ColorAnim, YAHOO.util.Anim);
668
669 // shorthand
670 var Y = YAHOO.util;
671 var superclass = Y.ColorAnim.superclass;
672 var proto = Y.ColorAnim.prototype;
673
674 proto.toString = function() {
675 var el = this.getEl();
676 var id = el.id || el.tagName;
677 return ("ColorAnim " + id);
678 };
679
680 proto.patterns.color = /color$/i;
681 proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
682 proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
683 proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
684 proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/; // need rgba for safari
685
686 /**
687 * Attempts to parse the given string and return a 3-tuple.
688 * @method parseColor
689 * @param {String} s The string to parse.
690 * @return {Array} The 3-tuple of rgb values.
691 */
692 proto.parseColor = function(s) {
693 if (s.length == 3) { return s; }
694
695 var c = this.patterns.hex.exec(s);
696 if (c && c.length == 4) {
697 return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
698 }
699
700 c = this.patterns.rgb.exec(s);
701 if (c && c.length == 4) {
702 return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
703 }
704
705 c = this.patterns.hex3.exec(s);
706 if (c && c.length == 4) {
707 return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
708 }
709
710 return null;
711 };
712
713 proto.getAttribute = function(attr) {
714 var el = this.getEl();
715 if ( this.patterns.color.test(attr) ) {
716 var val = YAHOO.util.Dom.getStyle(el, attr);
717
718 if (this.patterns.transparent.test(val)) { // bgcolor default
719 var parent = el.parentNode; // try and get from an ancestor
720 val = Y.Dom.getStyle(parent, attr);
721
722 while (parent && this.patterns.transparent.test(val)) {
723 parent = parent.parentNode;
724 val = Y.Dom.getStyle(parent, attr);
725 if (parent.tagName.toUpperCase() == 'HTML') {
726 val = '#fff';
727 }
728 }
729 }
730 } else {
731 val = superclass.getAttribute.call(this, attr);
732 }
733
734 return val;
735 };
736
737 proto.doMethod = function(attr, start, end) {
738 var val;
739
740 if ( this.patterns.color.test(attr) ) {
741 val = [];
742 for (var i = 0, len = start.length; i < len; ++i) {
743 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
744 }
745
746 val = 'rgb('+Math.floor(val[0])+','+Math.floor(val[1])+','+Math.floor(val[2])+')';
747 }
748 else {
749 val = superclass.doMethod.call(this, attr, start, end);
750 }
751
752 return val;
753 };
754
755 proto.setRuntimeAttribute = function(attr) {
756 superclass.setRuntimeAttribute.call(this, attr);
757
758 if ( this.patterns.color.test(attr) ) {
759 var attributes = this.attributes;
760 var start = this.parseColor(this.runtimeAttributes[attr].start);
761 var end = this.parseColor(this.runtimeAttributes[attr].end);
762 // fix colors if going "by"
763 if ( typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined' ) {
764 end = this.parseColor(attributes[attr].by);
765
766 for (var i = 0, len = start.length; i < len; ++i) {
767 end[i] = start[i] + end[i];
768 }
769 }
770
771 this.runtimeAttributes[attr].start = start;
772 this.runtimeAttributes[attr].end = end;
773 }
774 };
775})();/*
776TERMS OF USE - EASING EQUATIONS
777Open source under the BSD License.
778Copyright 2001 Robert Penner All rights reserved.
779
780Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
781
782 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
783 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
784 * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
785
786THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
787*/
788
789/**
790 * Singleton that determines how an animation proceeds from start to end.
791 * @class Easing
792 * @namespace YAHOO.util
793*/
794
795YAHOO.util.Easing = {
796
797 /**
798 * Uniform speed between points.
799 * @method easeNone
800 * @param {Number} t Time value used to compute current value
801 * @param {Number} b Starting value
802 * @param {Number} c Delta between start and end values
803 * @param {Number} d Total length of animation
804 * @return {Number} The computed value for the current animation frame
805 */
806 easeNone: function (t, b, c, d) {
807 return c*t/d + b;
808 },
809
810 /**
811 * Begins slowly and accelerates towards end. (quadratic)
812 * @method easeIn
813 * @param {Number} t Time value used to compute current value
814 * @param {Number} b Starting value
815 * @param {Number} c Delta between start and end values
816 * @param {Number} d Total length of animation
817 * @return {Number} The computed value for the current animation frame
818 */
819 easeIn: function (t, b, c, d) {
820 return c*(t/=d)*t + b;
821 },
822
823 /**
824 * Begins quickly and decelerates towards end. (quadratic)
825 * @method easeOut
826 * @param {Number} t Time value used to compute current value
827 * @param {Number} b Starting value
828 * @param {Number} c Delta between start and end values
829 * @param {Number} d Total length of animation
830 * @return {Number} The computed value for the current animation frame
831 */
832 easeOut: function (t, b, c, d) {
833 return -c *(t/=d)*(t-2) + b;
834 },
835
836 /**
837 * Begins slowly and decelerates towards end. (quadratic)
838 * @method easeBoth
839 * @param {Number} t Time value used to compute current value
840 * @param {Number} b Starting value
841 * @param {Number} c Delta between start and end values
842 * @param {Number} d Total length of animation
843 * @return {Number} The computed value for the current animation frame
844 */
845 easeBoth: function (t, b, c, d) {
846 if ((t/=d/2) < 1) return c/2*t*t + b;
847 return -c/2 * ((--t)*(t-2) - 1) + b;
848 },
849
850 /**
851 * Begins slowly and accelerates towards end. (quartic)
852 * @method easeInStrong
853 * @param {Number} t Time value used to compute current value
854 * @param {Number} b Starting value
855 * @param {Number} c Delta between start and end values
856 * @param {Number} d Total length of animation
857 * @return {Number} The computed value for the current animation frame
858 */
859 easeInStrong: function (t, b, c, d) {
860 return c*(t/=d)*t*t*t + b;
861 },
862
863 /**
864 * Begins quickly and decelerates towards end. (quartic)
865 * @method easeOutStrong
866 * @param {Number} t Time value used to compute current value
867 * @param {Number} b Starting value
868 * @param {Number} c Delta between start and end values
869 * @param {Number} d Total length of animation
870 * @return {Number} The computed value for the current animation frame
871 */
872 easeOutStrong: function (t, b, c, d) {
873 return -c * ((t=t/d-1)*t*t*t - 1) + b;
874 },
875
876 /**
877 * Begins slowly and decelerates towards end. (quartic)
878 * @method easeBothStrong
879 * @param {Number} t Time value used to compute current value
880 * @param {Number} b Starting value
881 * @param {Number} c Delta between start and end values
882 * @param {Number} d Total length of animation
883 * @return {Number} The computed value for the current animation frame
884 */
885 easeBothStrong: function (t, b, c, d) {
886 if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
887 return -c/2 * ((t-=2)*t*t*t - 2) + b;
888 },
889
890 /**
891 * Snap in elastic effect.
892 * @method elasticIn
893 * @param {Number} t Time value used to compute current value
894 * @param {Number} b Starting value
895 * @param {Number} c Delta between start and end values
896 * @param {Number} d Total length of animation
897 * @param {Number} p Period (optional)
898 * @return {Number} The computed value for the current animation frame
899 */
900
901 elasticIn: function (t, b, c, d, a, p) {
902 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
903 if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
904 else var s = p/(2*Math.PI) * Math.asin (c/a);
905 return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
906 },
907
908 /**
909 * Snap out elastic effect.
910 * @method elasticOut
911 * @param {Number} t Time value used to compute current value
912 * @param {Number} b Starting value
913 * @param {Number} c Delta between start and end values
914 * @param {Number} d Total length of animation
915 * @param {Number} p Period (optional)
916 * @return {Number} The computed value for the current animation frame
917 */
918 elasticOut: function (t, b, c, d, a, p) {
919 if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
920 if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
921 else var s = p/(2*Math.PI) * Math.asin (c/a);
922 return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
923 },
924
925 /**
926 * Snap both elastic effect.
927 * @method elasticBoth
928 * @param {Number} t Time value used to compute current value
929 * @param {Number} b Starting value
930 * @param {Number} c Delta between start and end values
931 * @param {Number} d Total length of animation
932 * @param {Number} p Period (optional)
933 * @return {Number} The computed value for the current animation frame
934 */
935 elasticBoth: function (t, b, c, d, a, p) {
936 if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
937 if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
938 else var s = p/(2*Math.PI) * Math.asin (c/a);
939 if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
940 return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
941 },
942
943 /**
944 * Backtracks slightly, then reverses direction and moves to end.
945 * @method backIn
946 * @param {Number} t Time value used to compute current value
947 * @param {Number} b Starting value
948 * @param {Number} c Delta between start and end values
949 * @param {Number} d Total length of animation
950 * @param {Number} s Overshoot (optional)
951 * @return {Number} The computed value for the current animation frame
952 */
953 backIn: function (t, b, c, d, s) {
954 if (typeof s == 'undefined') s = 1.70158;
955 return c*(t/=d)*t*((s+1)*t - s) + b;
956 },
957
958 /**
959 * Overshoots end, then reverses and comes back to end.
960 * @method backOut
961 * @param {Number} t Time value used to compute current value
962 * @param {Number} b Starting value
963 * @param {Number} c Delta between start and end values
964 * @param {Number} d Total length of animation
965 * @param {Number} s Overshoot (optional)
966 * @return {Number} The computed value for the current animation frame
967 */
968 backOut: function (t, b, c, d, s) {
969 if (typeof s == 'undefined') s = 1.70158;
970 return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
971 },
972
973 /**
974 * Backtracks slightly, then reverses direction, overshoots end,
975 * then reverses and comes back to end.
976 * @method backBoth
977 * @param {Number} t Time value used to compute current value
978 * @param {Number} b Starting value
979 * @param {Number} c Delta between start and end values
980 * @param {Number} d Total length of animation
981 * @param {Number} s Overshoot (optional)
982 * @return {Number} The computed value for the current animation frame
983 */
984 backBoth: function (t, b, c, d, s) {
985 if (typeof s == 'undefined') s = 1.70158;
986 if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
987 return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
988 },
989
990 /**
991 * Bounce off of start.
992 * @method bounceIn
993 * @param {Number} t Time value used to compute current value
994 * @param {Number} b Starting value
995 * @param {Number} c Delta between start and end values
996 * @param {Number} d Total length of animation
997 * @return {Number} The computed value for the current animation frame
998 */
999 bounceIn: function (t, b, c, d) {
1000 return c - YAHOO.util.Easing.bounceOut(d-t, 0, c, d) + b;
1001 },
1002
1003 /**
1004 * Bounces off end.
1005 * @method bounceOut
1006 * @param {Number} t Time value used to compute current value
1007 * @param {Number} b Starting value
1008 * @param {Number} c Delta between start and end values
1009 * @param {Number} d Total length of animation
1010 * @return {Number} The computed value for the current animation frame
1011 */
1012 bounceOut: function (t, b, c, d) {
1013 if ((t/=d) < (1/2.75)) {
1014 return c*(7.5625*t*t) + b;
1015 } else if (t < (2/2.75)) {
1016 return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
1017 } else if (t < (2.5/2.75)) {
1018 return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
1019 } else {
1020 return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
1021 }
1022 },
1023
1024 /**
1025 * Bounces off start and end.
1026 * @method bounceBoth
1027 * @param {Number} t Time value used to compute current value
1028 * @param {Number} b Starting value
1029 * @param {Number} c Delta between start and end values
1030 * @param {Number} d Total length of animation
1031 * @return {Number} The computed value for the current animation frame
1032 */
1033 bounceBoth: function (t, b, c, d) {
1034 if (t < d/2) return YAHOO.util.Easing.bounceIn(t*2, 0, c, d) * .5 + b;
1035 return YAHOO.util.Easing.bounceOut(t*2-d, 0, c, d) * .5 + c*.5 + b;
1036 }
1037};
1038
1039/**
1040 * Anim subclass for moving elements along a path defined by the "points"
1041 * member of "attributes". All "points" are arrays with x, y coordinates.
1042 * <p>Usage: <code>var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
1043 * @class Motion
1044 * @namespace YAHOO.util
1045 * @requires YAHOO.util.Anim
1046 * @requires YAHOO.util.AnimMgr
1047 * @requires YAHOO.util.Easing
1048 * @requires YAHOO.util.Bezier
1049 * @requires YAHOO.util.Dom
1050 * @requires YAHOO.util.Event
1051 * @requires YAHOO.util.CustomEvent
1052 * @constructor
1053 * @extends YAHOO.util.Anim
1054 * @param {String | HTMLElement} el Reference to the element that will be animated
1055 * @param {Object} attributes The attribute(s) to be animated.
1056 * Each attribute is an object with at minimum a "to" or "by" member defined.
1057 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
1058 * All attribute names use camelCase.
1059 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
1060 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
1061 */
1062(function() {
1063 YAHOO.util.Motion = function(el, attributes, duration, method) {
1064 if (el) { // dont break existing subclasses not using YAHOO.extend
1065 YAHOO.util.Motion.superclass.constructor.call(this, el, attributes, duration, method);
1066 }
1067 };
1068
1069 YAHOO.extend(YAHOO.util.Motion, YAHOO.util.ColorAnim);
1070
1071 // shorthand
1072 var Y = YAHOO.util;
1073 var superclass = Y.Motion.superclass;
1074 var proto = Y.Motion.prototype;
1075
1076 proto.toString = function() {
1077 var el = this.getEl();
1078 var id = el.id || el.tagName;
1079 return ("Motion " + id);
1080 };
1081
1082 proto.patterns.points = /^points$/i;
1083
1084 proto.setAttribute = function(attr, val, unit) {
1085 if ( this.patterns.points.test(attr) ) {
1086 unit = unit || 'px';
1087 superclass.setAttribute.call(this, 'left', val[0], unit);
1088 superclass.setAttribute.call(this, 'top', val[1], unit);
1089 } else {
1090 superclass.setAttribute.call(this, attr, val, unit);
1091 }
1092 };
1093
1094 proto.getAttribute = function(attr) {
1095 if ( this.patterns.points.test(attr) ) {
1096 var val = [
1097 superclass.getAttribute.call(this, 'left'),
1098 superclass.getAttribute.call(this, 'top')
1099 ];
1100 } else {
1101 val = superclass.getAttribute.call(this, attr);
1102 }
1103
1104 return val;
1105 };
1106
1107 proto.doMethod = function(attr, start, end) {
1108 var val = null;
1109
1110 if ( this.patterns.points.test(attr) ) {
1111 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
1112 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
1113 } else {
1114 val = superclass.doMethod.call(this, attr, start, end);
1115 }
1116 return val;
1117 };
1118
1119 proto.setRuntimeAttribute = function(attr) {
1120 if ( this.patterns.points.test(attr) ) {
1121 var el = this.getEl();
1122 var attributes = this.attributes;
1123 var start;
1124 var control = attributes['points']['control'] || [];
1125 var end;
1126 var i, len;
1127
1128 if (control.length > 0 && !(control[0] instanceof Array) ) { // could be single point or array of points
1129 control = [control];
1130 } else { // break reference to attributes.points.control
1131 var tmp = [];
1132 for (i = 0, len = control.length; i< len; ++i) {
1133 tmp[i] = control[i];
1134 }
1135 control = tmp;
1136 }
1137
1138 if (Y.Dom.getStyle(el, 'position') == 'static') { // default to relative
1139 Y.Dom.setStyle(el, 'position', 'relative');
1140 }
1141
1142 if ( isset(attributes['points']['from']) ) {
1143 Y.Dom.setXY(el, attributes['points']['from']); // set position to from point
1144 }
1145 else { Y.Dom.setXY( el, Y.Dom.getXY(el) ); } // set it to current position
1146
1147 start = this.getAttribute('points'); // get actual top & left
1148
1149 // TO beats BY, per SMIL 2.1 spec
1150 if ( isset(attributes['points']['to']) ) {
1151 end = translateValues.call(this, attributes['points']['to'], start);
1152
1153 var pageXY = Y.Dom.getXY(this.getEl());
1154 for (i = 0, len = control.length; i < len; ++i) {
1155 control[i] = translateValues.call(this, control[i], start);
1156 }
1157
1158
1159 } else if ( isset(attributes['points']['by']) ) {
1160 end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
1161
1162 for (i = 0, len = control.length; i < len; ++i) {
1163 control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
1164 }
1165 }
1166
1167 this.runtimeAttributes[attr] = [start];
1168
1169 if (control.length > 0) {
1170 this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
1171 }
1172
1173 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
1174 }
1175 else {
1176 superclass.setRuntimeAttribute.call(this, attr);
1177 }
1178 };
1179
1180 var translateValues = function(val, start) {
1181 var pageXY = Y.Dom.getXY(this.getEl());
1182 val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
1183
1184 return val;
1185 };
1186
1187 var isset = function(prop) {
1188 return (typeof prop !== 'undefined');
1189 };
1190})();
1191/**
1192 * Anim subclass for scrolling elements to a position defined by the "scroll"
1193 * member of "attributes". All "scroll" members are arrays with x, y scroll positions.
1194 * <p>Usage: <code>var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);</code></p>
1195 * @class Scroll
1196 * @namespace YAHOO.util
1197 * @requires YAHOO.util.Anim
1198 * @requires YAHOO.util.AnimMgr
1199 * @requires YAHOO.util.Easing
1200 * @requires YAHOO.util.Bezier
1201 * @requires YAHOO.util.Dom
1202 * @requires YAHOO.util.Event
1203 * @requires YAHOO.util.CustomEvent
1204 * @extends YAHOO.util.Anim
1205 * @constructor
1206 * @param {String or HTMLElement} el Reference to the element that will be animated
1207 * @param {Object} attributes The attribute(s) to be animated.
1208 * Each attribute is an object with at minimum a "to" or "by" member defined.
1209 * Additional optional members are "from" (defaults to current value), "units" (defaults to "px").
1210 * All attribute names use camelCase.
1211 * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based
1212 * @param {Function} method (optional, defaults to YAHOO.util.Easing.easeNone) Computes the values that are applied to the attributes per frame (generally a YAHOO.util.Easing method)
1213 */
1214(function() {
1215 YAHOO.util.Scroll = function(el, attributes, duration, method) {
1216 if (el) { // dont break existing subclasses not using YAHOO.extend
1217 YAHOO.util.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
1218 }
1219 };
1220
1221 YAHOO.extend(YAHOO.util.Scroll, YAHOO.util.ColorAnim);
1222
1223 // shorthand
1224 var Y = YAHOO.util;
1225 var superclass = Y.Scroll.superclass;
1226 var proto = Y.Scroll.prototype;
1227
1228 proto.toString = function() {
1229 var el = this.getEl();
1230 var id = el.id || el.tagName;
1231 return ("Scroll " + id);
1232 };
1233
1234 proto.doMethod = function(attr, start, end) {
1235 var val = null;
1236
1237 if (attr == 'scroll') {
1238 val = [
1239 this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
1240 this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
1241 ];
1242
1243 } else {
1244 val = superclass.doMethod.call(this, attr, start, end);
1245 }
1246 return val;
1247 };
1248
1249 proto.getAttribute = function(attr) {
1250 var val = null;
1251 var el = this.getEl();
1252
1253 if (attr == 'scroll') {
1254 val = [ el.scrollLeft, el.scrollTop ];
1255 } else {
1256 val = superclass.getAttribute.call(this, attr);
1257 }
1258
1259 return val;
1260 };
1261
1262 proto.setAttribute = function(attr, val, unit) {
1263 var el = this.getEl();
1264
1265 if (attr == 'scroll') {
1266 el.scrollLeft = val[0];
1267 el.scrollTop = val[1];
1268 } else {
1269 superclass.setAttribute.call(this, attr, val, unit);
1270 }
1271 };
1272})();
diff --git a/frontend/beta/js/YUI/autocomplete.js b/frontend/beta/js/YUI/autocomplete.js
new file mode 100644
index 0000000..a5722cc
--- a/dev/null
+++ b/frontend/beta/js/YUI/autocomplete.js
@@ -0,0 +1,3066 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.com/yui/license.txt
5version: 0.12.0
6*/
7
8 /**
9 * The AutoComplete control provides the front-end logic for text-entry suggestion and
10 * completion functionality.
11 *
12 * @module autocomplete
13 * @requires yahoo, dom, event, datasource
14 * @optional animation, connection, json
15 * @namespace YAHOO.widget
16 * @title AutoComplete Widget
17 */
18
19/****************************************************************************/
20/****************************************************************************/
21/****************************************************************************/
22
23/**
24 * The AutoComplete class provides the customizable functionality of a plug-and-play DHTML
25 * auto completion widget. Some key features:
26 * <ul>
27 * <li>Navigate with up/down arrow keys and/or mouse to pick a selection</li>
28 * <li>The drop down container can "roll down" or "fly out" via configurable
29 * animation</li>
30 * <li>UI look-and-feel customizable through CSS, including container
31 * attributes, borders, position, fonts, etc</li>
32 * </ul>
33 *
34 * @class AutoComplete
35 * @constructor
36 * @param elInput {HTMLElement} DOM element reference of an input field
37 * @param elInput {String} String ID of an input field
38 * @param elContainer {HTMLElement} DOM element reference of an existing DIV
39 * @param elContainer {String} String ID of an existing DIV
40 * @param oDataSource {Object} Instance of YAHOO.widget.DataSource for query/results
41 * @param oConfigs {Object} (optional) Object literal of configuration params
42 */
43YAHOO.widget.AutoComplete = function(elInput,elContainer,oDataSource,oConfigs) {
44 if(elInput && elContainer && oDataSource) {
45 // Validate DataSource
46 if (oDataSource && (oDataSource instanceof YAHOO.widget.DataSource)) {
47 this.dataSource = oDataSource;
48 }
49 else {
50 return;
51 }
52
53 // Validate input element
54 if(YAHOO.util.Dom.inDocument(elInput)) {
55 if(typeof elInput == "string") {
56 this._sName = "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput;
57 this._oTextbox = document.getElementById(elInput);
58 }
59 else {
60 this._sName = (elInput.id) ?
61 "instance" + YAHOO.widget.AutoComplete._nIndex + " " + elInput.id:
62 "instance" + YAHOO.widget.AutoComplete._nIndex;
63 this._oTextbox = elInput;
64 }
65 }
66 else {
67 return;
68 }
69
70 // Validate container element
71 if(YAHOO.util.Dom.inDocument(elContainer)) {
72 if(typeof elContainer == "string") {
73 this._oContainer = document.getElementById(elContainer);
74 }
75 else {
76 this._oContainer = elContainer;
77 }
78 if(this._oContainer.style.display == "none") {
79 }
80 }
81 else {
82 return;
83 }
84
85 // Set any config params passed in to override defaults
86 if (typeof oConfigs == "object") {
87 for(var sConfig in oConfigs) {
88 if (sConfig) {
89 this[sConfig] = oConfigs[sConfig];
90 }
91 }
92 }
93
94 // Initialization sequence
95 this._initContainer();
96 this._initProps();
97 this._initList();
98 this._initContainerHelpers();
99
100 // Set up events
101 var oSelf = this;
102 var oTextbox = this._oTextbox;
103 // Events are actually for the content module within the container
104 var oContent = this._oContainer._oContent;
105
106 // Dom events
107 YAHOO.util.Event.addListener(oTextbox,"keyup",oSelf._onTextboxKeyUp,oSelf);
108 YAHOO.util.Event.addListener(oTextbox,"keydown",oSelf._onTextboxKeyDown,oSelf);
109 YAHOO.util.Event.addListener(oTextbox,"focus",oSelf._onTextboxFocus,oSelf);
110 YAHOO.util.Event.addListener(oTextbox,"blur",oSelf._onTextboxBlur,oSelf);
111 YAHOO.util.Event.addListener(oContent,"mouseover",oSelf._onContainerMouseover,oSelf);
112 YAHOO.util.Event.addListener(oContent,"mouseout",oSelf._onContainerMouseout,oSelf);
113 YAHOO.util.Event.addListener(oContent,"scroll",oSelf._onContainerScroll,oSelf);
114 YAHOO.util.Event.addListener(oContent,"resize",oSelf._onContainerResize,oSelf);
115 if(oTextbox.form) {
116 YAHOO.util.Event.addListener(oTextbox.form,"submit",oSelf._onFormSubmit,oSelf);
117 }
118 YAHOO.util.Event.addListener(oTextbox,"keypress",oSelf._onTextboxKeyPress,oSelf);
119
120 // Custom events
121 this.textboxFocusEvent = new YAHOO.util.CustomEvent("textboxFocus", this);
122 this.textboxKeyEvent = new YAHOO.util.CustomEvent("textboxKey", this);
123 this.dataRequestEvent = new YAHOO.util.CustomEvent("dataRequest", this);
124 this.dataReturnEvent = new YAHOO.util.CustomEvent("dataReturn", this);
125 this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);
126 this.containerExpandEvent = new YAHOO.util.CustomEvent("containerExpand", this);
127 this.typeAheadEvent = new YAHOO.util.CustomEvent("typeAhead", this);
128 this.itemMouseOverEvent = new YAHOO.util.CustomEvent("itemMouseOver", this);
129 this.itemMouseOutEvent = new YAHOO.util.CustomEvent("itemMouseOut", this);
130 this.itemArrowToEvent = new YAHOO.util.CustomEvent("itemArrowTo", this);
131 this.itemArrowFromEvent = new YAHOO.util.CustomEvent("itemArrowFrom", this);
132 this.itemSelectEvent = new YAHOO.util.CustomEvent("itemSelect", this);
133 this.unmatchedItemSelectEvent = new YAHOO.util.CustomEvent("unmatchedItemSelect", this);
134 this.selectionEnforceEvent = new YAHOO.util.CustomEvent("selectionEnforce", this);
135 this.containerCollapseEvent = new YAHOO.util.CustomEvent("containerCollapse", this);
136 this.textboxBlurEvent = new YAHOO.util.CustomEvent("textboxBlur", this);
137
138 // Finish up
139 oTextbox.setAttribute("autocomplete","off");
140 YAHOO.widget.AutoComplete._nIndex++;
141 }
142 // Required arguments were not found
143 else {
144 }
145};
146
147/////////////////////////////////////////////////////////////////////////////
148//
149// Public member variables
150//
151/////////////////////////////////////////////////////////////////////////////
152
153/**
154 * The DataSource object that encapsulates the data used for auto completion.
155 * This object should be an inherited object from YAHOO.widget.DataSource.
156 *
157 * @property dataSource
158 * @type Object
159 */
160YAHOO.widget.AutoComplete.prototype.dataSource = null;
161
162/**
163 * Number of characters that must be entered before querying for results. A negative value
164 * effectively turns off the widget. A value of 0 allows queries of null or empty string
165 * values.
166 *
167 * @property minQueryLength
168 * @type Number
169 * @default 1
170 */
171YAHOO.widget.AutoComplete.prototype.minQueryLength = 1;
172
173/**
174 * Maximum number of results to display in results container.
175 *
176 * @property maxResultsDisplayed
177 * @type Number
178 * @default 10
179 */
180YAHOO.widget.AutoComplete.prototype.maxResultsDisplayed = 10;
181
182/**
183 * Number of seconds to delay before submitting a query request. If a query
184 * request is received before a previous one has completed its delay, the
185 * previous request is cancelled and the new request is set to the delay.
186 *
187 * @property queryDelay
188 * @type Number
189 * @default 0.5
190 */
191YAHOO.widget.AutoComplete.prototype.queryDelay = 0.5;
192
193/**
194 * Class name of a highlighted item within results container.
195 *
196 * @property highlighClassName
197 * @type String
198 * @default "yui-ac-highlight"
199 */
200YAHOO.widget.AutoComplete.prototype.highlightClassName = "yui-ac-highlight";
201
202/**
203 * Class name of a pre-highlighted item within results container.
204 *
205 * @property prehighlightClassName
206 * @type String
207 */
208YAHOO.widget.AutoComplete.prototype.prehighlightClassName = null;
209
210/**
211 * Query delimiter. A single character separator for multiple delimited
212 * selections. Multiple delimiter characteres may be defined as an array of
213 * strings. A null value or empty string indicates that query results cannot
214 * be delimited. This feature is not recommended if you need forceSelection to
215 * be true.
216 *
217 * @property delimChar
218 * @type String | String[]
219 */
220YAHOO.widget.AutoComplete.prototype.delimChar = null;
221
222/**
223 * Whether or not the first item in results container should be automatically highlighted
224 * on expand.
225 *
226 * @property autoHighlight
227 * @type Boolean
228 * @default true
229 */
230YAHOO.widget.AutoComplete.prototype.autoHighlight = true;
231
232/**
233 * Whether or not the input field should be automatically updated
234 * with the first query result as the user types, auto-selecting the substring
235 * that the user has not typed.
236 *
237 * @property typeAhead
238 * @type Boolean
239 * @default false
240 */
241YAHOO.widget.AutoComplete.prototype.typeAhead = false;
242
243/**
244 * Whether or not to animate the expansion/collapse of the results container in the
245 * horizontal direction.
246 *
247 * @property animHoriz
248 * @type Boolean
249 * @default false
250 */
251YAHOO.widget.AutoComplete.prototype.animHoriz = false;
252
253/**
254 * Whether or not to animate the expansion/collapse of the results container in the
255 * vertical direction.
256 *
257 * @property animVert
258 * @type Boolean
259 * @default true
260 */
261YAHOO.widget.AutoComplete.prototype.animVert = true;
262
263/**
264 * Speed of container expand/collapse animation, in seconds..
265 *
266 * @property animSpeed
267 * @type Number
268 * @default 0.3
269 */
270YAHOO.widget.AutoComplete.prototype.animSpeed = 0.3;
271
272/**
273 * Whether or not to force the user's selection to match one of the query
274 * results. Enabling this feature essentially transforms the input field into a
275 * &lt;select&gt; field. This feature is not recommended with delimiter character(s)
276 * defined.
277 *
278 * @property forceSelection
279 * @type Boolean
280 * @default false
281 */
282YAHOO.widget.AutoComplete.prototype.forceSelection = false;
283
284/**
285 * Whether or not to allow browsers to cache user-typed input in the input
286 * field. Disabling this feature will prevent the widget from setting the
287 * autocomplete="off" on the input field. When autocomplete="off"
288 * and users click the back button after form submission, user-typed input can
289 * be prefilled by the browser from its cache. This caching of user input may
290 * not be desired for sensitive data, such as credit card numbers, in which
291 * case, implementers should consider setting allowBrowserAutocomplete to false.
292 *
293 * @property allowBrowserAutocomplete
294 * @type Boolean
295 * @default true
296 */
297YAHOO.widget.AutoComplete.prototype.allowBrowserAutocomplete = true;
298
299/**
300 * Whether or not the results container should always be displayed.
301 * Enabling this feature displays the container when the widget is instantiated
302 * and prevents the toggling of the container to a collapsed state.
303 *
304 * @property alwaysShowContainer
305 * @type Boolean
306 * @default false
307 */
308YAHOO.widget.AutoComplete.prototype.alwaysShowContainer = false;
309
310/**
311 * Whether or not to use an iFrame to layer over Windows form elements in
312 * IE. Set to true only when the results container will be on top of a
313 * &lt;select&gt; field in IE and thus exposed to the IE z-index bug (i.e.,
314 * 5.5 < IE < 7).
315 *
316 * @property useIFrame
317 * @type Boolean
318 * @default false
319 */
320YAHOO.widget.AutoComplete.prototype.useIFrame = false;
321
322/**
323 * Whether or not the results container should have a shadow.
324 *
325 * @property useShadow
326 * @type Boolean
327 * @default false
328 */
329YAHOO.widget.AutoComplete.prototype.useShadow = false;
330
331/////////////////////////////////////////////////////////////////////////////
332//
333// Public methods
334//
335/////////////////////////////////////////////////////////////////////////////
336
337 /**
338 * Public accessor to the unique name of the AutoComplete instance.
339 *
340 * @method toString
341 * @return {String} Unique name of the AutoComplete instance.
342 */
343YAHOO.widget.AutoComplete.prototype.toString = function() {
344 return "AutoComplete " + this._sName;
345};
346
347 /**
348 * Returns true if container is in an expanded state, false otherwise.
349 *
350 * @method isContainerOpen
351 * @return {Boolean} Returns true if container is in an expanded state, false otherwise.
352 */
353YAHOO.widget.AutoComplete.prototype.isContainerOpen = function() {
354 return this._bContainerOpen;
355};
356
357/**
358 * Public accessor to the internal array of DOM &lt;li&gt; elements that
359 * display query results within the results container.
360 *
361 * @method getListItems
362 * @return {HTMLElement[]} Array of &lt;li&gt; elements within the results container.
363 */
364YAHOO.widget.AutoComplete.prototype.getListItems = function() {
365 return this._aListItems;
366};
367
368/**
369 * Public accessor to the data held in an &lt;li&gt; element of the
370 * results container.
371 *
372 * @method getListItemData
373 * @return {Object | Array} Object or array of result data or null
374 */
375YAHOO.widget.AutoComplete.prototype.getListItemData = function(oListItem) {
376 if(oListItem._oResultData) {
377 return oListItem._oResultData;
378 }
379 else {
380 return false;
381 }
382};
383
384/**
385 * Sets HTML markup for the results container header. This markup will be
386 * inserted within a &lt;div&gt; tag with a class of "ac_hd".
387 *
388 * @method setHeader
389 * @param sHeader {String} HTML markup for results container header.
390 */
391YAHOO.widget.AutoComplete.prototype.setHeader = function(sHeader) {
392 if(sHeader) {
393 if(this._oContainer._oContent._oHeader) {
394 this._oContainer._oContent._oHeader.innerHTML = sHeader;
395 this._oContainer._oContent._oHeader.style.display = "block";
396 }
397 }
398 else {
399 this._oContainer._oContent._oHeader.innerHTML = "";
400 this._oContainer._oContent._oHeader.style.display = "none";
401 }
402};
403
404/**
405 * Sets HTML markup for the results container footer. This markup will be
406 * inserted within a &lt;div&gt; tag with a class of "ac_ft".
407 *
408 * @method setFooter
409 * @param sFooter {String} HTML markup for results container footer.
410 */
411YAHOO.widget.AutoComplete.prototype.setFooter = function(sFooter) {
412 if(sFooter) {
413 if(this._oContainer._oContent._oFooter) {
414 this._oContainer._oContent._oFooter.innerHTML = sFooter;
415 this._oContainer._oContent._oFooter.style.display = "block";
416 }
417 }
418 else {
419 this._oContainer._oContent._oFooter.innerHTML = "";
420 this._oContainer._oContent._oFooter.style.display = "none";
421 }
422};
423
424/**
425 * Sets HTML markup for the results container body. This markup will be
426 * inserted within a &lt;div&gt; tag with a class of "ac_bd".
427 *
428 * @method setBody
429 * @param sHeader {String} HTML markup for results container body.
430 */
431YAHOO.widget.AutoComplete.prototype.setBody = function(sBody) {
432 if(sBody) {
433 if(this._oContainer._oContent._oBody) {
434 this._oContainer._oContent._oBody.innerHTML = sBody;
435 this._oContainer._oContent._oBody.style.display = "block";
436 this._oContainer._oContent.style.display = "block";
437 }
438 }
439 else {
440 this._oContainer._oContent._oBody.innerHTML = "";
441 this._oContainer._oContent.style.display = "none";
442 }
443 this._maxResultsDisplayed = 0;
444};
445
446/**
447 * Overridable method that converts a result item object into HTML markup
448 * for display. Return data values are accessible via the oResultItem object,
449 * and the key return value will always be oResultItem[0]. Markup will be
450 * displayed within &lt;li&gt; element tags in the container.
451 *
452 * @method formatResult
453 * @param oResultItem {Object} Result item representing one query result. Data is held in an array.
454 * @param sQuery {String} The current query string.
455 * @return {String} HTML markup of formatted result data.
456 */
457YAHOO.widget.AutoComplete.prototype.formatResult = function(oResultItem, sQuery) {
458 var sResult = oResultItem[0];
459 if(sResult) {
460 return sResult;
461 }
462 else {
463 return "";
464 }
465};
466
467/**
468 * Overridable method called before container expands allows implementers to access data
469 * and DOM elements.
470 *
471 * @method doBeforeExpandContainer
472 * @return {Boolean} Return true to continue expanding container, false to cancel the expand.
473 */
474YAHOO.widget.AutoComplete.prototype.doBeforeExpandContainer = function(oResultItem, sQuery) {
475 return true;
476};
477
478/**
479 * Makes query request to the DataSource.
480 *
481 * @method sendQuery
482 * @param sQuery {String} Query string.
483 */
484YAHOO.widget.AutoComplete.prototype.sendQuery = function(sQuery) {
485 this._sendQuery(sQuery);
486};
487
488/////////////////////////////////////////////////////////////////////////////
489//
490// Public events
491//
492/////////////////////////////////////////////////////////////////////////////
493
494/**
495 * Fired when the input field receives focus.
496 *
497 * @event textboxFocusEvent
498 * @param oSelf {Object} The AutoComplete instance.
499 */
500YAHOO.widget.AutoComplete.prototype.textboxFocusEvent = null;
501
502/**
503 * Fired when the input field receives key input.
504 *
505 * @event textboxKeyEvent
506 * @param oSelf {Object} The AutoComplete instance.
507 * @param nKeycode {Number} The keycode number.
508 */
509YAHOO.widget.AutoComplete.prototype.textboxKeyEvent = null;
510
511/**
512 * Fired when the AutoComplete instance makes a query to the DataSource.
513 *
514 * @event dataRequestEvent
515 * @param oSelf {Object} The AutoComplete instance.
516 * @param sQuery {String} The query string.
517 */
518YAHOO.widget.AutoComplete.prototype.dataRequestEvent = null;
519
520/**
521 * Fired when the AutoComplete instance receives query results from the data
522 * source.
523 *
524 * @event dataReturnEvent
525 * @param oSelf {Object} The AutoComplete instance.
526 * @param sQuery {String} The query string.
527 * @param aResults {Array} Results array.
528 */
529YAHOO.widget.AutoComplete.prototype.dataReturnEvent = null;
530
531/**
532 * Fired when the AutoComplete instance does not receive query results from the
533 * DataSource due to an error.
534 *
535 * @event dataErrorEvent
536 * @param oSelf {Object} The AutoComplete instance.
537 * @param sQuery {String} The query string.
538 */
539YAHOO.widget.AutoComplete.prototype.dataErrorEvent = null;
540
541/**
542 * Fired when the results container is expanded.
543 *
544 * @event containerExpandEvent
545 * @param oSelf {Object} The AutoComplete instance.
546 */
547YAHOO.widget.AutoComplete.prototype.containerExpandEvent = null;
548
549/**
550 * Fired when the input field has been prefilled by the type-ahead
551 * feature.
552 *
553 * @event typeAheadEvent
554 * @param oSelf {Object} The AutoComplete instance.
555 * @param sQuery {String} The query string.
556 * @param sPrefill {String} The prefill string.
557 */
558YAHOO.widget.AutoComplete.prototype.typeAheadEvent = null;
559
560/**
561 * Fired when result item has been moused over.
562 *
563 * @event itemMouseOverEvent
564 * @param oSelf {Object} The AutoComplete instance.
565 * @param elItem {HTMLElement} The &lt;li&gt element item moused to.
566 */
567YAHOO.widget.AutoComplete.prototype.itemMouseOverEvent = null;
568
569/**
570 * Fired when result item has been moused out.
571 *
572 * @event itemMouseOutEvent
573 * @param oSelf {Object} The AutoComplete instance.
574 * @param elItem {HTMLElement} The &lt;li&gt; element item moused from.
575 */
576YAHOO.widget.AutoComplete.prototype.itemMouseOutEvent = null;
577
578/**
579 * Fired when result item has been arrowed to.
580 *
581 * @event itemArrowToEvent
582 * @param oSelf {Object} The AutoComplete instance.
583 * @param elItem {HTMLElement} The &lt;li&gt; element item arrowed to.
584 */
585YAHOO.widget.AutoComplete.prototype.itemArrowToEvent = null;
586
587/**
588 * Fired when result item has been arrowed away from.
589 *
590 * @event itemArrowFromEvent
591 * @param oSelf {Object} The AutoComplete instance.
592 * @param elItem {HTMLElement} The &lt;li&gt; element item arrowed from.
593 */
594YAHOO.widget.AutoComplete.prototype.itemArrowFromEvent = null;
595
596/**
597 * Fired when an item is selected via mouse click, ENTER key, or TAB key.
598 *
599 * @event itemSelectEvent
600 * @param oSelf {Object} The AutoComplete instance.
601 * @param elItem {HTMLElement} The selected &lt;li&gt; element item.
602 * @param oData {Object} The data returned for the item, either as an object,
603 * or mapped from the schema into an array.
604 */
605YAHOO.widget.AutoComplete.prototype.itemSelectEvent = null;
606
607/**
608 * Fired when a user selection does not match any of the displayed result items.
609 * Note that this event may not behave as expected when delimiter characters
610 * have been defined.
611 *
612 * @event unmatchedItemSelectEvent
613 * @param oSelf {Object} The AutoComplete instance.
614 * @param sQuery {String} The user-typed query string.
615 */
616YAHOO.widget.AutoComplete.prototype.unmatchedItemSelectEvent = null;
617
618/**
619 * Fired if forceSelection is enabled and the user's input has been cleared
620 * because it did not match one of the returned query results.
621 *
622 * @event selectionEnforceEvent
623 * @param oSelf {Object} The AutoComplete instance.
624 */
625YAHOO.widget.AutoComplete.prototype.selectionEnforceEvent = null;
626
627/**
628 * Fired when the results container is collapsed.
629 *
630 * @event containerCollapseEvent
631 * @param oSelf {Object} The AutoComplete instance.
632 */
633YAHOO.widget.AutoComplete.prototype.containerCollapseEvent = null;
634
635/**
636 * Fired when the input field loses focus.
637 *
638 * @event textboxBlurEvent
639 * @param oSelf {Object} The AutoComplete instance.
640 */
641YAHOO.widget.AutoComplete.prototype.textboxBlurEvent = null;
642
643/////////////////////////////////////////////////////////////////////////////
644//
645// Private member variables
646//
647/////////////////////////////////////////////////////////////////////////////
648
649/**
650 * Internal class variable to index multiple AutoComplete instances.
651 *
652 * @property _nIndex
653 * @type Number
654 * @default 0
655 * @private
656 */
657YAHOO.widget.AutoComplete._nIndex = 0;
658
659/**
660 * Name of AutoComplete instance.
661 *
662 * @property _sName
663 * @type String
664 * @private
665 */
666YAHOO.widget.AutoComplete.prototype._sName = null;
667
668/**
669 * Text input field DOM element.
670 *
671 * @property _oTextbox
672 * @type HTMLElement
673 * @private
674 */
675YAHOO.widget.AutoComplete.prototype._oTextbox = null;
676
677/**
678 * Whether or not the input field is currently in focus. If query results come back
679 * but the user has already moved on, do not proceed with auto complete behavior.
680 *
681 * @property _bFocused
682 * @type Boolean
683 * @private
684 */
685YAHOO.widget.AutoComplete.prototype._bFocused = true;
686
687/**
688 * Animation instance for container expand/collapse.
689 *
690 * @property _oAnim
691 * @type Boolean
692 * @private
693 */
694YAHOO.widget.AutoComplete.prototype._oAnim = null;
695
696/**
697 * Container DOM element.
698 *
699 * @property _oContainer
700 * @type HTMLElement
701 * @private
702 */
703YAHOO.widget.AutoComplete.prototype._oContainer = null;
704
705/**
706 * Whether or not the results container is currently open.
707 *
708 * @property _bContainerOpen
709 * @type Boolean
710 * @private
711 */
712YAHOO.widget.AutoComplete.prototype._bContainerOpen = false;
713
714/**
715 * Whether or not the mouse is currently over the results
716 * container. This is necessary in order to prevent clicks on container items
717 * from being text input field blur events.
718 *
719 * @property _bOverContainer
720 * @type Boolean
721 * @private
722 */
723YAHOO.widget.AutoComplete.prototype._bOverContainer = false;
724
725/**
726 * Array of &lt;li&gt; elements references that contain query results within the
727 * results container.
728 *
729 * @property _aListItems
730 * @type Array
731 * @private
732 */
733YAHOO.widget.AutoComplete.prototype._aListItems = null;
734
735/**
736 * Number of &lt;li&gt; elements currently displayed in results container.
737 *
738 * @property _nDisplayedItems
739 * @type Number
740 * @private
741 */
742YAHOO.widget.AutoComplete.prototype._nDisplayedItems = 0;
743
744/**
745 * Internal count of &lt;li&gt; elements displayed and hidden in results container.
746 *
747 * @property _maxResultsDisplayed
748 * @type Number
749 * @private
750 */
751YAHOO.widget.AutoComplete.prototype._maxResultsDisplayed = 0;
752
753/**
754 * Current query string
755 *
756 * @property _sCurQuery
757 * @type String
758 * @private
759 */
760YAHOO.widget.AutoComplete.prototype._sCurQuery = null;
761
762/**
763 * Past queries this session (for saving delimited queries).
764 *
765 * @property _sSavedQuery
766 * @type String
767 * @private
768 */
769YAHOO.widget.AutoComplete.prototype._sSavedQuery = null;
770
771/**
772 * Pointer to the currently highlighted &lt;li&gt; element in the container.
773 *
774 * @property _oCurItem
775 * @type HTMLElement
776 * @private
777 */
778YAHOO.widget.AutoComplete.prototype._oCurItem = null;
779
780/**
781 * Whether or not an item has been selected since the container was populated
782 * with results. Reset to false by _populateList, and set to true when item is
783 * selected.
784 *
785 * @property _bItemSelected
786 * @type Boolean
787 * @private
788 */
789YAHOO.widget.AutoComplete.prototype._bItemSelected = false;
790
791/**
792 * Key code of the last key pressed in textbox.
793 *
794 * @property _nKeyCode
795 * @type Number
796 * @private
797 */
798YAHOO.widget.AutoComplete.prototype._nKeyCode = null;
799
800/**
801 * Delay timeout ID.
802 *
803 * @property _nDelayID
804 * @type Number
805 * @private
806 */
807YAHOO.widget.AutoComplete.prototype._nDelayID = -1;
808
809/**
810 * Src to iFrame used when useIFrame = true. Supports implementations over SSL
811 * as well.
812 *
813 * @property _iFrameSrc
814 * @type String
815 * @private
816 */
817YAHOO.widget.AutoComplete.prototype._iFrameSrc = "javascript:false;";
818
819/**
820 * For users typing via certain IMEs, queries must be triggered by intervals,
821 * since key events yet supported across all browsers for all IMEs.
822 *
823 * @property _queryInterval
824 * @type Object
825 * @private
826 */
827YAHOO.widget.AutoComplete.prototype._queryInterval = null;
828
829/**
830 * Internal tracker to last known textbox value, used to determine whether or not
831 * to trigger a query via interval for certain IME users.
832 *
833 * @event _sLastTextboxValue
834 * @type String
835 * @private
836 */
837YAHOO.widget.AutoComplete.prototype._sLastTextboxValue = null;
838
839/////////////////////////////////////////////////////////////////////////////
840//
841// Private methods
842//
843/////////////////////////////////////////////////////////////////////////////
844
845/**
846 * Updates and validates latest public config properties.
847 *
848 * @method __initProps
849 * @private
850 */
851YAHOO.widget.AutoComplete.prototype._initProps = function() {
852 // Correct any invalid values
853 var minQueryLength = this.minQueryLength;
854 if(isNaN(minQueryLength) || (minQueryLength < 1)) {
855 minQueryLength = 1;
856 }
857 var maxResultsDisplayed = this.maxResultsDisplayed;
858 if(isNaN(this.maxResultsDisplayed) || (this.maxResultsDisplayed < 1)) {
859 this.maxResultsDisplayed = 10;
860 }
861 var queryDelay = this.queryDelay;
862 if(isNaN(this.queryDelay) || (this.queryDelay < 0)) {
863 this.queryDelay = 0.5;
864 }
865 var aDelimChar = (this.delimChar) ? this.delimChar : null;
866 if(aDelimChar) {
867 if(typeof aDelimChar == "string") {
868 this.delimChar = [aDelimChar];
869 }
870 else if(aDelimChar.constructor != Array) {
871 this.delimChar = null;
872 }
873 }
874 var animSpeed = this.animSpeed;
875 if((this.animHoriz || this.animVert) && YAHOO.util.Anim) {
876 if(isNaN(animSpeed) || (animSpeed < 0)) {
877 animSpeed = 0.3;
878 }
879 if(!this._oAnim ) {
880 oAnim = new YAHOO.util.Anim(this._oContainer._oContent, {}, this.animSpeed);
881 this._oAnim = oAnim;
882 }
883 else {
884 this._oAnim.duration = animSpeed;
885 }
886 }
887 if(this.forceSelection && this.delimChar) {
888 }
889};
890
891/**
892 * Initializes the results container helpers if they are enabled and do
893 * not exist
894 *
895 * @method _initContainerHelpers
896 * @private
897 */
898YAHOO.widget.AutoComplete.prototype._initContainerHelpers = function() {
899 if(this.useShadow && !this._oContainer._oShadow) {
900 var oShadow = document.createElement("div");
901 oShadow.className = "yui-ac-shadow";
902 this._oContainer._oShadow = this._oContainer.appendChild(oShadow);
903 }
904 if(this.useIFrame && !this._oContainer._oIFrame) {
905 var oIFrame = document.createElement("iframe");
906 oIFrame.src = this._iFrameSrc;
907 oIFrame.frameBorder = 0;
908 oIFrame.scrolling = "no";
909 oIFrame.style.position = "absolute";
910 oIFrame.style.width = "100%";
911 oIFrame.style.height = "100%";
912 oIFrame.tabIndex = -1;
913 this._oContainer._oIFrame = this._oContainer.appendChild(oIFrame);
914 }
915};
916
917/**
918 * Initializes the results container once at object creation
919 *
920 * @method _initContainer
921 * @private
922 */
923YAHOO.widget.AutoComplete.prototype._initContainer = function() {
924 if(!this._oContainer._oContent) {
925 // The oContent div helps size the iframe and shadow properly
926 var oContent = document.createElement("div");
927 oContent.className = "yui-ac-content";
928 oContent.style.display = "none";
929 this._oContainer._oContent = this._oContainer.appendChild(oContent);
930
931 var oHeader = document.createElement("div");
932 oHeader.className = "yui-ac-hd";
933 oHeader.style.display = "none";
934 this._oContainer._oContent._oHeader = this._oContainer._oContent.appendChild(oHeader);
935
936 var oBody = document.createElement("div");
937 oBody.className = "yui-ac-bd";
938 this._oContainer._oContent._oBody = this._oContainer._oContent.appendChild(oBody);
939
940 var oFooter = document.createElement("div");
941 oFooter.className = "yui-ac-ft";
942 oFooter.style.display = "none";
943 this._oContainer._oContent._oFooter = this._oContainer._oContent.appendChild(oFooter);
944 }
945 else {
946 }
947};
948
949/**
950 * Clears out contents of container body and creates up to
951 * YAHOO.widget.AutoComplete#maxResultsDisplayed &lt;li&gt; elements in an
952 * &lt;ul&gt; element.
953 *
954 * @method _initList
955 * @private
956 */
957YAHOO.widget.AutoComplete.prototype._initList = function() {
958 this._aListItems = [];
959 while(this._oContainer._oContent._oBody.hasChildNodes()) {
960 var oldListItems = this.getListItems();
961 if(oldListItems) {
962 for(var oldi = oldListItems.length-1; oldi >= 0; i--) {
963 oldListItems[oldi] = null;
964 }
965 }
966 this._oContainer._oContent._oBody.innerHTML = "";
967 }
968
969 var oList = document.createElement("ul");
970 oList = this._oContainer._oContent._oBody.appendChild(oList);
971 for(var i=0; i<this.maxResultsDisplayed; i++) {
972 var oItem = document.createElement("li");
973 oItem = oList.appendChild(oItem);
974 this._aListItems[i] = oItem;
975 this._initListItem(oItem, i);
976 }
977 this._maxResultsDisplayed = this.maxResultsDisplayed;
978};
979
980/**
981 * Initializes each &lt;li&gt; element in the container list.
982 *
983 * @method _initListItem
984 * @param oItem {HTMLElement} The &lt;li&gt; DOM element.
985 * @param nItemIndex {Number} The index of the element.
986 * @private
987 */
988YAHOO.widget.AutoComplete.prototype._initListItem = function(oItem, nItemIndex) {
989 var oSelf = this;
990 oItem.style.display = "none";
991 oItem._nItemIndex = nItemIndex;
992
993 oItem.mouseover = oItem.mouseout = oItem.onclick = null;
994 YAHOO.util.Event.addListener(oItem,"mouseover",oSelf._onItemMouseover,oSelf);
995 YAHOO.util.Event.addListener(oItem,"mouseout",oSelf._onItemMouseout,oSelf);
996 YAHOO.util.Event.addListener(oItem,"click",oSelf._onItemMouseclick,oSelf);
997};
998
999/**
1000 * Enables interval detection for Korean IME support.
1001 *
1002 * @method _onIMEDetected
1003 * @param oSelf {Object} The AutoComplete instance.
1004 * @private
1005 */
1006YAHOO.widget.AutoComplete.prototype._onIMEDetected = function(oSelf) {
1007 oSelf._enableIntervalDetection();
1008};
1009
1010/**
1011 * Enables query triggers based on text input detection by intervals (rather
1012 * than by key events).
1013 *
1014 * @method _enableIntervalDetection
1015 * @private
1016 */
1017YAHOO.widget.AutoComplete.prototype._enableIntervalDetection = function() {
1018 var currValue = this._oTextbox.value;
1019 var lastValue = this._sLastTextboxValue;
1020 if(currValue != lastValue) {
1021 this._sLastTextboxValue = currValue;
1022 this._sendQuery(currValue);
1023 }
1024};
1025
1026/**
1027 * Cancels text input detection by intervals.
1028 *
1029 * @method _cancelIntervalDetection
1030 * @param oSelf {Object} The AutoComplete instance.
1031 * @private
1032 */
1033YAHOO.widget.AutoComplete.prototype._cancelIntervalDetection = function(oSelf) {
1034 if(oSelf._queryInterval) {
1035 clearInterval(oSelf._queryInterval);
1036 }
1037};
1038
1039/**
1040 * Whether or not key is functional or should be ignored. Note that the right
1041 * arrow key is NOT an ignored key since it triggers queries for certain intl
1042 * charsets.
1043 *
1044 * @method _isIgnoreKey
1045 * @param nKeycode {Number} Code of key pressed.
1046 * @return {Boolean} True if key should be ignored, false otherwise.
1047 * @private
1048 */
1049YAHOO.widget.AutoComplete.prototype._isIgnoreKey = function(nKeyCode) {
1050 if ((nKeyCode == 9) || (nKeyCode == 13) || // tab, enter
1051 (nKeyCode == 16) || (nKeyCode == 17) || // shift, ctl
1052 (nKeyCode >= 18 && nKeyCode <= 20) || // alt,pause/break,caps lock
1053 (nKeyCode == 27) || // esc
1054 (nKeyCode >= 33 && nKeyCode <= 35) || // page up,page down,end
1055 (nKeyCode >= 36 && nKeyCode <= 38) || // home,left,up
1056 (nKeyCode == 40) || // down
1057 (nKeyCode >= 44 && nKeyCode <= 45)) { // print screen,insert
1058 return true;
1059 }
1060 return false;
1061};
1062
1063/**
1064 * Makes query request to the DataSource.
1065 *
1066 * @method _sendQuery
1067 * @param sQuery {String} Query string.
1068 * @private
1069 */
1070YAHOO.widget.AutoComplete.prototype._sendQuery = function(sQuery) {
1071 // Widget has been effectively turned off
1072 if(this.minQueryLength == -1) {
1073 this._toggleContainer(false);
1074 return;
1075 }
1076 // Delimiter has been enabled
1077 var aDelimChar = (this.delimChar) ? this.delimChar : null;
1078 if(aDelimChar) {
1079 // Loop through all possible delimiters and find the latest one
1080 // A " " may be a false positive if they are defined as delimiters AND
1081 // are used to separate delimited queries
1082 var nDelimIndex = -1;
1083 for(var i = aDelimChar.length-1; i >= 0; i--) {
1084 var nNewIndex = sQuery.lastIndexOf(aDelimChar[i]);
1085 if(nNewIndex > nDelimIndex) {
1086 nDelimIndex = nNewIndex;
1087 }
1088 }
1089 // If we think the last delimiter is a space (" "), make sure it is NOT
1090 // a false positive by also checking the char directly before it
1091 if(aDelimChar[i] == " ") {
1092 for (var j = aDelimChar.length-1; j >= 0; j--) {
1093 if(sQuery[nDelimIndex - 1] == aDelimChar[j]) {
1094 nDelimIndex--;
1095 break;
1096 }
1097 }
1098 }
1099 // A delimiter has been found so extract the latest query
1100 if (nDelimIndex > -1) {
1101 var nQueryStart = nDelimIndex + 1;
1102 // Trim any white space from the beginning...
1103 while(sQuery.charAt(nQueryStart) == " ") {
1104 nQueryStart += 1;
1105 }
1106 // ...and save the rest of the string for later
1107 this._sSavedQuery = sQuery.substring(0,nQueryStart);
1108 // Here is the query itself
1109 sQuery = sQuery.substr(nQueryStart);
1110 }
1111 else if(sQuery.indexOf(this._sSavedQuery) < 0){
1112 this._sSavedQuery = null;
1113 }
1114 }
1115
1116 // Don't search queries that are too short
1117 if (sQuery && (sQuery.length < this.minQueryLength) || (!sQuery && this.minQueryLength > 0)) {
1118 if (this._nDelayID != -1) {
1119 clearTimeout(this._nDelayID);
1120 }
1121 this._toggleContainer(false);
1122 return;
1123 }
1124
1125 sQuery = encodeURIComponent(sQuery);
1126 this._nDelayID = -1; // Reset timeout ID because request has been made
1127 this.dataRequestEvent.fire(this, sQuery);
1128 this.dataSource.getResults(this._populateList, sQuery, this);
1129};
1130
1131/**
1132 * Populates the array of &lt;li&gt; elements in the container with query
1133 * results. This method is passed to YAHOO.widget.DataSource#getResults as a
1134 * callback function so results from the DataSource instance are returned to the
1135 * AutoComplete instance.
1136 *
1137 * @method _populateList
1138 * @param sQuery {String} The query string.
1139 * @param aResults {Array} An array of query result objects from the DataSource.
1140 * @param oSelf {Object} The AutoComplete instance.
1141 * @private
1142 */
1143YAHOO.widget.AutoComplete.prototype._populateList = function(sQuery, aResults, oSelf) {
1144 if(aResults === null) {
1145 oSelf.dataErrorEvent.fire(oSelf, sQuery);
1146 }
1147 if (!oSelf._bFocused || !aResults) {
1148 return;
1149 }
1150
1151 var isOpera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1);
1152 var contentStyle = oSelf._oContainer._oContent.style;
1153 contentStyle.width = (!isOpera) ? null : "";
1154 contentStyle.height = (!isOpera) ? null : "";
1155
1156 var sCurQuery = decodeURIComponent(sQuery);
1157 oSelf._sCurQuery = sCurQuery;
1158 oSelf._bItemSelected = false;
1159
1160 if(oSelf._maxResultsDisplayed != oSelf.maxResultsDisplayed) {
1161 oSelf._initList();
1162 }
1163
1164 var nItems = Math.min(aResults.length,oSelf.maxResultsDisplayed);
1165 oSelf._nDisplayedItems = nItems;
1166 if (nItems > 0) {
1167 oSelf._initContainerHelpers();
1168 var aItems = oSelf._aListItems;
1169
1170 // Fill items with data
1171 for(var i = nItems-1; i >= 0; i--) {
1172 var oItemi = aItems[i];
1173 var oResultItemi = aResults[i];
1174 oItemi.innerHTML = oSelf.formatResult(oResultItemi, sCurQuery);
1175 oItemi.style.display = "list-item";
1176 oItemi._sResultKey = oResultItemi[0];
1177 oItemi._oResultData = oResultItemi;
1178
1179 }
1180
1181 // Empty out remaining items if any
1182 for(var j = aItems.length-1; j >= nItems ; j--) {
1183 var oItemj = aItems[j];
1184 oItemj.innerHTML = null;
1185 oItemj.style.display = "none";
1186 oItemj._sResultKey = null;
1187 oItemj._oResultData = null;
1188 }
1189
1190 if(oSelf.autoHighlight) {
1191 // Go to the first item
1192 var oFirstItem = aItems[0];
1193 oSelf._toggleHighlight(oFirstItem,"to");
1194 oSelf.itemArrowToEvent.fire(oSelf, oFirstItem);
1195 oSelf._typeAhead(oFirstItem,sQuery);
1196 }
1197 else {
1198 oSelf._oCurItem = null;
1199 }
1200
1201 // Expand the container
1202 var ok = oSelf.doBeforeExpandContainer(oSelf._oTextbox, oSelf._oContainer, sQuery, aResults);
1203 oSelf._toggleContainer(ok);
1204 }
1205 else {
1206 oSelf._toggleContainer(false);
1207 }
1208 oSelf.dataReturnEvent.fire(oSelf, sQuery, aResults);
1209};
1210
1211/**
1212 * When forceSelection is true and the user attempts
1213 * leave the text input box without selecting an item from the query results,
1214 * the user selection is cleared.
1215 *
1216 * @method _clearSelection
1217 * @private
1218 */
1219YAHOO.widget.AutoComplete.prototype._clearSelection = function() {
1220 var sValue = this._oTextbox.value;
1221 var sChar = (this.delimChar) ? this.delimChar[0] : null;
1222 var nIndex = (sChar) ? sValue.lastIndexOf(sChar, sValue.length-2) : -1;
1223 if(nIndex > -1) {
1224 this._oTextbox.value = sValue.substring(0,nIndex);
1225 }
1226 else {
1227 this._oTextbox.value = "";
1228 }
1229 this._sSavedQuery = this._oTextbox.value;
1230
1231 // Fire custom event
1232 this.selectionEnforceEvent.fire(this);
1233};
1234
1235/**
1236 * Whether or not user-typed value in the text input box matches any of the
1237 * query results.
1238 *
1239 * @method _textMatchesOption
1240 * @return {Boolean} True if user-input text matches a result, false otherwise.
1241 * @private
1242 */
1243YAHOO.widget.AutoComplete.prototype._textMatchesOption = function() {
1244 var foundMatch = false;
1245
1246 for(var i = this._nDisplayedItems-1; i >= 0 ; i--) {
1247 var oItem = this._aListItems[i];
1248 var sMatch = oItem._sResultKey.toLowerCase();
1249 if (sMatch == this._sCurQuery.toLowerCase()) {
1250 foundMatch = true;
1251 break;
1252 }
1253 }
1254 return(foundMatch);
1255};
1256
1257/**
1258 * Updates in the text input box with the first query result as the user types,
1259 * selecting the substring that the user has not typed.
1260 *
1261 * @method _typeAhead
1262 * @param oItem {HTMLElement} The &lt;li&gt; element item whose data populates the input field.
1263 * @param sQuery {String} Query string.
1264 * @private
1265 */
1266YAHOO.widget.AutoComplete.prototype._typeAhead = function(oItem, sQuery) {
1267 // Don't update if turned off
1268 if (!this.typeAhead) {
1269 return;
1270 }
1271
1272 var oTextbox = this._oTextbox;
1273 var sValue = this._oTextbox.value; // any saved queries plus what user has typed
1274
1275 // Don't update with type-ahead if text selection is not supported
1276 if(!oTextbox.setSelectionRange && !oTextbox.createTextRange) {
1277 return;
1278 }
1279
1280 // Select the portion of text that the user has not typed
1281 var nStart = sValue.length;
1282 this._updateValue(oItem);
1283 var nEnd = oTextbox.value.length;
1284 this._selectText(oTextbox,nStart,nEnd);
1285 var sPrefill = oTextbox.value.substr(nStart,nEnd);
1286 this.typeAheadEvent.fire(this,sQuery,sPrefill);
1287};
1288
1289/**
1290 * Selects text in the input field.
1291 *
1292 * @method _selectText
1293 * @param oTextbox {HTMLElement} Text input box element in which to select text.
1294 * @param nStart {Number} Starting index of text string to select.
1295 * @param nEnd {Number} Ending index of text selection.
1296 * @private
1297 */
1298YAHOO.widget.AutoComplete.prototype._selectText = function(oTextbox, nStart, nEnd) {
1299 if (oTextbox.setSelectionRange) { // For Mozilla
1300 oTextbox.setSelectionRange(nStart,nEnd);
1301 }
1302 else if (oTextbox.createTextRange) { // For IE
1303 var oTextRange = oTextbox.createTextRange();
1304 oTextRange.moveStart("character", nStart);
1305 oTextRange.moveEnd("character", nEnd-oTextbox.value.length);
1306 oTextRange.select();
1307 }
1308 else {
1309 oTextbox.select();
1310 }
1311};
1312
1313/**
1314 * Syncs results container with its helpers.
1315 *
1316 * @method _toggleContainerHelpers
1317 * @param bShow {Boolean} True if container is expanded, false if collapsed
1318 * @private
1319 */
1320YAHOO.widget.AutoComplete.prototype._toggleContainerHelpers = function(bShow) {
1321 var bFireEvent = false;
1322 var width = this._oContainer._oContent.offsetWidth + "px";
1323 var height = this._oContainer._oContent.offsetHeight + "px";
1324
1325 if(this.useIFrame && this._oContainer._oIFrame) {
1326 bFireEvent = true;
1327 if(bShow) {
1328 this._oContainer._oIFrame.style.width = width;
1329 this._oContainer._oIFrame.style.height = height;
1330 }
1331 else {
1332 this._oContainer._oIFrame.style.width = 0;
1333 this._oContainer._oIFrame.style.height = 0;
1334 }
1335 }
1336 if(this.useShadow && this._oContainer._oShadow) {
1337 bFireEvent = true;
1338 if(bShow) {
1339 this._oContainer._oShadow.style.width = width;
1340 this._oContainer._oShadow.style.height = height;
1341 }
1342 else {
1343 this._oContainer._oShadow.style.width = 0;
1344 this._oContainer._oShadow.style.height = 0;
1345 }
1346 }
1347};
1348
1349/**
1350 * Animates expansion or collapse of the container.
1351 *
1352 * @method _toggleContainer
1353 * @param bShow {Boolean} True if container should be expanded, false if container should be collapsed
1354 * @private
1355 */
1356YAHOO.widget.AutoComplete.prototype._toggleContainer = function(bShow) {
1357 var oContainer = this._oContainer;
1358
1359 // Implementer has container always open so don't mess with it
1360 if(this.alwaysShowContainer && this._bContainerOpen) {
1361 return;
1362 }
1363
1364 // Clear contents of container
1365 if(!bShow) {
1366 this._oContainer._oContent.scrollTop = 0;
1367 var aItems = this._aListItems;
1368
1369 if(aItems && (aItems.length > 0)) {
1370 for(var i = aItems.length-1; i >= 0 ; i--) {
1371 aItems[i].style.display = "none";
1372 }
1373 }
1374
1375 if (this._oCurItem) {
1376 this._toggleHighlight(this._oCurItem,"from");
1377 }
1378
1379 this._oCurItem = null;
1380 this._nDisplayedItems = 0;
1381 this._sCurQuery = null;
1382 }
1383
1384 // Container is already closed
1385 if (!bShow && !this._bContainerOpen) {
1386 oContainer._oContent.style.display = "none";
1387 return;
1388 }
1389
1390 // If animation is enabled...
1391 var oAnim = this._oAnim;
1392 if (oAnim && oAnim.getEl() && (this.animHoriz || this.animVert)) {
1393 // If helpers need to be collapsed, do it right away...
1394 // but if helpers need to be expanded, wait until after the container expands
1395 if(!bShow) {
1396 this._toggleContainerHelpers(bShow);
1397 }
1398
1399 if(oAnim.isAnimated()) {
1400 oAnim.stop();
1401 }
1402
1403 // Clone container to grab current size offscreen
1404 var oClone = oContainer._oContent.cloneNode(true);
1405 oContainer.appendChild(oClone);
1406 oClone.style.top = "-9000px";
1407 oClone.style.display = "block";
1408
1409 // Current size of the container is the EXPANDED size
1410 var wExp = oClone.offsetWidth;
1411 var hExp = oClone.offsetHeight;
1412
1413 // Calculate COLLAPSED sizes based on horiz and vert anim
1414 var wColl = (this.animHoriz) ? 0 : wExp;
1415 var hColl = (this.animVert) ? 0 : hExp;
1416
1417 // Set animation sizes
1418 oAnim.attributes = (bShow) ?
1419 {width: { to: wExp }, height: { to: hExp }} :
1420 {width: { to: wColl}, height: { to: hColl }};
1421
1422 // If opening anew, set to a collapsed size...
1423 if(bShow && !this._bContainerOpen) {
1424 oContainer._oContent.style.width = wColl+"px";
1425 oContainer._oContent.style.height = hColl+"px";
1426 }
1427 // Else, set it to its last known size.
1428 else {
1429 oContainer._oContent.style.width = wExp+"px";
1430 oContainer._oContent.style.height = hExp+"px";
1431 }
1432
1433 oContainer.removeChild(oClone);
1434 oClone = null;
1435
1436 var oSelf = this;
1437 var onAnimComplete = function() {
1438 // Finish the collapse
1439 oAnim.onComplete.unsubscribeAll();
1440
1441 if(bShow) {
1442 oSelf.containerExpandEvent.fire(oSelf);
1443 }
1444 else {
1445 oContainer._oContent.style.display = "none";
1446 oSelf.containerCollapseEvent.fire(oSelf);
1447 }
1448 oSelf._toggleContainerHelpers(bShow);
1449 };
1450
1451 // Display container and animate it
1452 oContainer._oContent.style.display = "block";
1453 oAnim.onComplete.subscribe(onAnimComplete);
1454 oAnim.animate();
1455 this._bContainerOpen = bShow;
1456 }
1457 // Else don't animate, just show or hide
1458 else {
1459 if(bShow) {
1460 oContainer._oContent.style.display = "block";
1461 this.containerExpandEvent.fire(this);
1462 }
1463 else {
1464 oContainer._oContent.style.display = "none";
1465 this.containerCollapseEvent.fire(this);
1466 }
1467 this._toggleContainerHelpers(bShow);
1468 this._bContainerOpen = bShow;
1469 }
1470
1471};
1472
1473/**
1474 * Toggles the highlight on or off for an item in the container, and also cleans
1475 * up highlighting of any previous item.
1476 *
1477 * @method _toggleHighlight
1478 * @param oNewItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.
1479 * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.
1480 * @private
1481 */
1482YAHOO.widget.AutoComplete.prototype._toggleHighlight = function(oNewItem, sType) {
1483 var sHighlight = this.highlightClassName;
1484 if(this._oCurItem) {
1485 // Remove highlight from old item
1486 YAHOO.util.Dom.removeClass(this._oCurItem, sHighlight);
1487 }
1488
1489 if((sType == "to") && sHighlight) {
1490 // Apply highlight to new item
1491 YAHOO.util.Dom.addClass(oNewItem, sHighlight);
1492 this._oCurItem = oNewItem;
1493 }
1494};
1495
1496/**
1497 * Toggles the pre-highlight on or off for an item in the container.
1498 *
1499 * @method _togglePrehighlight
1500 * @param oNewItem {HTMLElement} The &lt;li&gt; element item to receive highlight behavior.
1501 * @param sType {String} Type "mouseover" will toggle highlight on, and "mouseout" will toggle highlight off.
1502 * @private
1503 */
1504YAHOO.widget.AutoComplete.prototype._togglePrehighlight = function(oNewItem, sType) {
1505 if(oNewItem == this._oCurItem) {
1506 return;
1507 }
1508
1509 var sPrehighlight = this.prehighlightClassName;
1510 if((sType == "mouseover") && sPrehighlight) {
1511 // Apply prehighlight to new item
1512 YAHOO.util.Dom.addClass(oNewItem, sPrehighlight);
1513 }
1514 else {
1515 // Remove prehighlight from old item
1516 YAHOO.util.Dom.removeClass(oNewItem, sPrehighlight);
1517 }
1518};
1519
1520/**
1521 * Updates the text input box value with selected query result. If a delimiter
1522 * has been defined, then the value gets appended with the delimiter.
1523 *
1524 * @method _updateValue
1525 * @param oItem {HTMLElement} The &lt;li&gt; element item with which to update the value.
1526 * @private
1527 */
1528YAHOO.widget.AutoComplete.prototype._updateValue = function(oItem) {
1529 var oTextbox = this._oTextbox;
1530 var sDelimChar = (this.delimChar) ? (this.delimChar[0] || this.delimChar) : null;
1531 var sSavedQuery = this._sSavedQuery;
1532 var sResultKey = oItem._sResultKey;
1533 oTextbox.focus();
1534
1535 // First clear text field
1536 oTextbox.value = "";
1537 // Grab data to put into text field
1538 if(sDelimChar) {
1539 if(sSavedQuery) {
1540 oTextbox.value = sSavedQuery;
1541 }
1542 oTextbox.value += sResultKey + sDelimChar;
1543 if(sDelimChar != " ") {
1544 oTextbox.value += " ";
1545 }
1546 }
1547 else { oTextbox.value = sResultKey; }
1548
1549 // scroll to bottom of textarea if necessary
1550 if(oTextbox.type == "textarea") {
1551 oTextbox.scrollTop = oTextbox.scrollHeight;
1552 }
1553
1554 // move cursor to end
1555 var end = oTextbox.value.length;
1556 this._selectText(oTextbox,end,end);
1557
1558 this._oCurItem = oItem;
1559};
1560
1561/**
1562 * Selects a result item from the container
1563 *
1564 * @method _selectItem
1565 * @param oItem {HTMLElement} The selected &lt;li&gt; element item.
1566 * @private
1567 */
1568YAHOO.widget.AutoComplete.prototype._selectItem = function(oItem) {
1569 this._bItemSelected = true;
1570 this._updateValue(oItem);
1571 this._cancelIntervalDetection(this);
1572 this.itemSelectEvent.fire(this, oItem, oItem._oResultData);
1573 this._toggleContainer(false);
1574};
1575
1576/**
1577 * For values updated by type-ahead, the right arrow key jumps to the end
1578 * of the textbox, otherwise the container is closed.
1579 *
1580 * @method _jumpSelection
1581 * @private
1582 */
1583YAHOO.widget.AutoComplete.prototype._jumpSelection = function() {
1584 if(!this.typeAhead) {
1585 return;
1586 }
1587 else {
1588 this._toggleContainer(false);
1589 }
1590};
1591
1592/**
1593 * Triggered by up and down arrow keys, changes the current highlighted
1594 * &lt;li&gt; element item. Scrolls container if necessary.
1595 *
1596 * @method _moveSelection
1597 * @param nKeyCode {Number} Code of key pressed.
1598 * @private
1599 */
1600YAHOO.widget.AutoComplete.prototype._moveSelection = function(nKeyCode) {
1601 if(this._bContainerOpen) {
1602 // Determine current item's id number
1603 var oCurItem = this._oCurItem;
1604 var nCurItemIndex = -1;
1605
1606 if (oCurItem) {
1607 nCurItemIndex = oCurItem._nItemIndex;
1608 }
1609
1610 var nNewItemIndex = (nKeyCode == 40) ?
1611 (nCurItemIndex + 1) : (nCurItemIndex - 1);
1612
1613 // Out of bounds
1614 if (nNewItemIndex < -2 || nNewItemIndex >= this._nDisplayedItems) {
1615 return;
1616 }
1617
1618 if (oCurItem) {
1619 // Unhighlight current item
1620 this._toggleHighlight(oCurItem, "from");
1621 this.itemArrowFromEvent.fire(this, oCurItem);
1622 }
1623 if (nNewItemIndex == -1) {
1624 // Go back to query (remove type-ahead string)
1625 if(this.delimChar && this._sSavedQuery) {
1626 if (!this._textMatchesOption()) {
1627 this._oTextbox.value = this._sSavedQuery;
1628 }
1629 else {
1630 this._oTextbox.value = this._sSavedQuery + this._sCurQuery;
1631 }
1632 }
1633 else {
1634 this._oTextbox.value = this._sCurQuery;
1635 }
1636 this._oCurItem = null;
1637 return;
1638 }
1639 if (nNewItemIndex == -2) {
1640 // Close container
1641 this._toggleContainer(false);
1642 return;
1643 }
1644
1645 var oNewItem = this._aListItems[nNewItemIndex];
1646
1647 // Scroll the container if necessary
1648 var oContent = this._oContainer._oContent;
1649 var scrollOn = ((YAHOO.util.Dom.getStyle(oContent,"overflow") == "auto") ||
1650 (YAHOO.util.Dom.getStyle(oContent,"overflowY") == "auto"));
1651 if(scrollOn && (nNewItemIndex > -1) &&
1652 (nNewItemIndex < this._nDisplayedItems)) {
1653 // User is keying down
1654 if(nKeyCode == 40) {
1655 // Bottom of selected item is below scroll area...
1656 if((oNewItem.offsetTop+oNewItem.offsetHeight) > (oContent.scrollTop + oContent.offsetHeight)) {
1657 // Set bottom of scroll area to bottom of selected item
1658 oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight;
1659 }
1660 // Bottom of selected item is above scroll area...
1661 else if((oNewItem.offsetTop+oNewItem.offsetHeight) < oContent.scrollTop) {
1662 // Set top of selected item to top of scroll area
1663 oContent.scrollTop = oNewItem.offsetTop;
1664
1665 }
1666 }
1667 // User is keying up
1668 else {
1669 // Top of selected item is above scroll area
1670 if(oNewItem.offsetTop < oContent.scrollTop) {
1671 // Set top of scroll area to top of selected item
1672 this._oContainer._oContent.scrollTop = oNewItem.offsetTop;
1673 }
1674 // Top of selected item is below scroll area
1675 else if(oNewItem.offsetTop > (oContent.scrollTop + oContent.offsetHeight)) {
1676 // Set bottom of selected item to bottom of scroll area
1677 this._oContainer._oContent.scrollTop = (oNewItem.offsetTop+oNewItem.offsetHeight) - oContent.offsetHeight;
1678 }
1679 }
1680 }
1681
1682 this._toggleHighlight(oNewItem, "to");
1683 this.itemArrowToEvent.fire(this, oNewItem);
1684 if(this.typeAhead) {
1685 this._updateValue(oNewItem);
1686 }
1687 }
1688};
1689
1690/////////////////////////////////////////////////////////////////////////////
1691//
1692// Private event handlers
1693//
1694/////////////////////////////////////////////////////////////////////////////
1695
1696/**
1697 * Handles &lt;li&gt; element mouseover events in the container.
1698 *
1699 * @method _onItemMouseover
1700 * @param v {HTMLEvent} The mouseover event.
1701 * @param oSelf {Object} The AutoComplete instance.
1702 * @private
1703 */
1704YAHOO.widget.AutoComplete.prototype._onItemMouseover = function(v,oSelf) {
1705 if(oSelf.prehighlightClassName) {
1706 oSelf._togglePrehighlight(this,"mouseover");
1707 }
1708 else {
1709 oSelf._toggleHighlight(this,"to");
1710 }
1711
1712 oSelf.itemMouseOverEvent.fire(oSelf, this);
1713};
1714
1715/**
1716 * Handles &lt;li&gt; element mouseout events in the container.
1717 *
1718 * @method _onItemMouseout
1719 * @param v {HTMLEvent} The mouseout event.
1720 * @param oSelf {Object} The AutoComplete instance.
1721 * @private
1722 */
1723YAHOO.widget.AutoComplete.prototype._onItemMouseout = function(v,oSelf) {
1724 if(oSelf.prehighlightClassName) {
1725 oSelf._togglePrehighlight(this,"mouseout");
1726 }
1727 else {
1728 oSelf._toggleHighlight(this,"from");
1729 }
1730
1731 oSelf.itemMouseOutEvent.fire(oSelf, this);
1732};
1733
1734/**
1735 * Handles &lt;li&gt; element click events in the container.
1736 *
1737 * @method _onItemMouseclick
1738 * @param v {HTMLEvent} The click event.
1739 * @param oSelf {Object} The AutoComplete instance.
1740 * @private
1741 */
1742YAHOO.widget.AutoComplete.prototype._onItemMouseclick = function(v,oSelf) {
1743 // In case item has not been moused over
1744 oSelf._toggleHighlight(this,"to");
1745 oSelf._selectItem(this);
1746};
1747
1748/**
1749 * Handles container mouseover events.
1750 *
1751 * @method _onContainerMouseover
1752 * @param v {HTMLEvent} The mouseover event.
1753 * @param oSelf {Object} The AutoComplete instance.
1754 * @private
1755 */
1756YAHOO.widget.AutoComplete.prototype._onContainerMouseover = function(v,oSelf) {
1757 oSelf._bOverContainer = true;
1758};
1759
1760/**
1761 * Handles container mouseout events.
1762 *
1763 * @method _onContainerMouseout
1764 * @param v {HTMLEvent} The mouseout event.
1765 * @param oSelf {Object} The AutoComplete instance.
1766 * @private
1767 */
1768YAHOO.widget.AutoComplete.prototype._onContainerMouseout = function(v,oSelf) {
1769 oSelf._bOverContainer = false;
1770 // If container is still active
1771 if(oSelf._oCurItem) {
1772 oSelf._toggleHighlight(oSelf._oCurItem,"to");
1773 }
1774};
1775
1776/**
1777 * Handles container scroll events.
1778 *
1779 * @method _onContainerScroll
1780 * @param v {HTMLEvent} The scroll event.
1781 * @param oSelf {Object} The AutoComplete instance.
1782 * @private
1783 */
1784YAHOO.widget.AutoComplete.prototype._onContainerScroll = function(v,oSelf) {
1785 oSelf._oTextbox.focus();
1786};
1787
1788/**
1789 * Handles container resize events.
1790 *
1791 * @method _onContainerResize
1792 * @param v {HTMLEvent} The resize event.
1793 * @param oSelf {Object} The AutoComplete instance.
1794 * @private
1795 */
1796YAHOO.widget.AutoComplete.prototype._onContainerResize = function(v,oSelf) {
1797 oSelf._toggleContainerHelpers(oSelf._bContainerOpen);
1798};
1799
1800/**
1801 * Handles textbox keydown events of functional keys, mainly for UI behavior.
1802 *
1803 * @method _onTextboxKeyDown
1804 * @param v {HTMLEvent} The keydown event.
1805 * @param oSelf {object} The AutoComplete instance.
1806 * @private
1807 */
1808YAHOO.widget.AutoComplete.prototype._onTextboxKeyDown = function(v,oSelf) {
1809 var nKeyCode = v.keyCode;
1810
1811 switch (nKeyCode) {
1812 case 9: // tab
1813 if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) {
1814 if(oSelf._bContainerOpen) {
1815 YAHOO.util.Event.stopEvent(v);
1816 }
1817 }
1818 // select an item or clear out
1819 if(oSelf._oCurItem) {
1820 oSelf._selectItem(oSelf._oCurItem);
1821 }
1822 else {
1823 oSelf._toggleContainer(false);
1824 }
1825 break;
1826 case 13: // enter
1827 if(oSelf._nKeyCode != nKeyCode) {
1828 if(oSelf._bContainerOpen) {
1829 YAHOO.util.Event.stopEvent(v);
1830 }
1831 }
1832 if(oSelf._oCurItem) {
1833 oSelf._selectItem(oSelf._oCurItem);
1834 }
1835 else {
1836 oSelf._toggleContainer(false);
1837 }
1838 break;
1839 case 27: // esc
1840 oSelf._toggleContainer(false);
1841 return;
1842 case 39: // right
1843 oSelf._jumpSelection();
1844 break;
1845 case 38: // up
1846 YAHOO.util.Event.stopEvent(v);
1847 oSelf._moveSelection(nKeyCode);
1848 break;
1849 case 40: // down
1850 YAHOO.util.Event.stopEvent(v);
1851 oSelf._moveSelection(nKeyCode);
1852 break;
1853 default:
1854 break;
1855 }
1856};
1857
1858/**
1859 * Handles textbox keypress events.
1860 * @method _onTextboxKeyPress
1861 * @param v {HTMLEvent} The keypress event.
1862 * @param oSelf {Object} The AutoComplete instance.
1863 * @private
1864 */
1865YAHOO.widget.AutoComplete.prototype._onTextboxKeyPress = function(v,oSelf) {
1866 var nKeyCode = v.keyCode;
1867
1868 //Expose only to Mac browsers, where stopEvent is ineffective on keydown events (bug 790337)
1869 var isMac = (navigator.userAgent.toLowerCase().indexOf("mac") != -1);
1870 if(isMac) {
1871 switch (nKeyCode) {
1872 case 9: // tab
1873 if(oSelf.delimChar && (oSelf._nKeyCode != nKeyCode)) {
1874 if(oSelf._bContainerOpen) {
1875 YAHOO.util.Event.stopEvent(v);
1876 }
1877 }
1878 break;
1879 case 13: // enter
1880 if(oSelf._nKeyCode != nKeyCode) {
1881 if(oSelf._bContainerOpen) {
1882 YAHOO.util.Event.stopEvent(v);
1883 }
1884 }
1885 break;
1886 case 38: // up
1887 case 40: // down
1888 YAHOO.util.Event.stopEvent(v);
1889 break;
1890 default:
1891 break;
1892 }
1893 }
1894
1895 //TODO: (?) limit only to non-IE, non-Mac-FF for Korean IME support (bug 811948)
1896 // Korean IME detected
1897 else if(nKeyCode == 229) {
1898 oSelf._queryInterval = setInterval(function() { oSelf._onIMEDetected(oSelf); },500);
1899 }
1900};
1901
1902/**
1903 * Handles textbox keyup events that trigger queries.
1904 *
1905 * @method _onTextboxKeyUp
1906 * @param v {HTMLEvent} The keyup event.
1907 * @param oSelf {Object} The AutoComplete instance.
1908 * @private
1909 */
1910YAHOO.widget.AutoComplete.prototype._onTextboxKeyUp = function(v,oSelf) {
1911 // Check to see if any of the public properties have been updated
1912 oSelf._initProps();
1913
1914 var nKeyCode = v.keyCode;
1915 oSelf._nKeyCode = nKeyCode;
1916 var sText = this.value; //string in textbox
1917
1918 // Filter out chars that don't trigger queries
1919 if (oSelf._isIgnoreKey(nKeyCode) || (sText.toLowerCase() == oSelf._sCurQuery)) {
1920 return;
1921 }
1922 else {
1923 oSelf.textboxKeyEvent.fire(oSelf, nKeyCode);
1924 }
1925
1926 // Set timeout on the request
1927 if (oSelf.queryDelay > 0) {
1928 var nDelayID =
1929 setTimeout(function(){oSelf._sendQuery(sText);},(oSelf.queryDelay * 1000));
1930
1931 if (oSelf._nDelayID != -1) {
1932 clearTimeout(oSelf._nDelayID);
1933 }
1934
1935 oSelf._nDelayID = nDelayID;
1936 }
1937 else {
1938 // No delay so send request immediately
1939 oSelf._sendQuery(sText);
1940 }
1941};
1942
1943/**
1944 * Handles text input box receiving focus.
1945 *
1946 * @method _onTextboxFocus
1947 * @param v {HTMLEvent} The focus event.
1948 * @param oSelf {Object} The AutoComplete instance.
1949 * @private
1950 */
1951YAHOO.widget.AutoComplete.prototype._onTextboxFocus = function (v,oSelf) {
1952 oSelf._oTextbox.setAttribute("autocomplete","off");
1953 oSelf._bFocused = true;
1954 oSelf.textboxFocusEvent.fire(oSelf);
1955};
1956
1957/**
1958 * Handles text input box losing focus.
1959 *
1960 * @method _onTextboxBlur
1961 * @param v {HTMLEvent} The focus event.
1962 * @param oSelf {Object} The AutoComplete instance.
1963 * @private
1964 */
1965YAHOO.widget.AutoComplete.prototype._onTextboxBlur = function (v,oSelf) {
1966 // Don't treat as a blur if it was a selection via mouse click
1967 if(!oSelf._bOverContainer || (oSelf._nKeyCode == 9)) {
1968 // Current query needs to be validated
1969 if(!oSelf._bItemSelected) {
1970 if(!oSelf._bContainerOpen || (oSelf._bContainerOpen && !oSelf._textMatchesOption())) {
1971 if(oSelf.forceSelection) {
1972 oSelf._clearSelection();
1973 }
1974 else {
1975 oSelf.unmatchedItemSelectEvent.fire(oSelf, oSelf._sCurQuery);
1976 }
1977 }
1978 }
1979
1980 if(oSelf._bContainerOpen) {
1981 oSelf._toggleContainer(false);
1982 }
1983 oSelf._cancelIntervalDetection(oSelf);
1984 oSelf._bFocused = false;
1985 oSelf.textboxBlurEvent.fire(oSelf);
1986 }
1987};
1988
1989/**
1990 * Handles form submission event.
1991 *
1992 * @method _onFormSubmit
1993 * @param v {HTMLEvent} The submit event.
1994 * @param oSelf {Object} The AutoComplete instance.
1995 * @private
1996 */
1997YAHOO.widget.AutoComplete.prototype._onFormSubmit = function(v,oSelf) {
1998 if(oSelf.allowBrowserAutocomplete) {
1999 oSelf._oTextbox.setAttribute("autocomplete","on");
2000 }
2001 else {
2002 oSelf._oTextbox.setAttribute("autocomplete","off");
2003 }
2004};
2005/****************************************************************************/
2006/****************************************************************************/
2007/****************************************************************************/
2008
2009/**
2010 * The DataSource classes manages sending a request and returning response from a live
2011 * database. Supported data include local JavaScript arrays and objects and databases
2012 * accessible via XHR connections. Supported response formats include JavaScript arrays,
2013 * JSON, XML, and flat-file textual data.
2014 *
2015 * @class DataSource
2016 * @constructor
2017 */
2018YAHOO.widget.DataSource = function() {
2019 /* abstract class */
2020};
2021
2022
2023/////////////////////////////////////////////////////////////////////////////
2024//
2025// Public constants
2026//
2027/////////////////////////////////////////////////////////////////////////////
2028
2029/**
2030 * Error message for null data responses.
2031 *
2032 * @property ERROR_DATANULL
2033 * @type String
2034 * @static
2035 * @final
2036 */
2037YAHOO.widget.DataSource.ERROR_DATANULL = "Response data was null";
2038
2039/**
2040 * Error message for data responses with parsing errors.
2041 *
2042 * @property ERROR_DATAPARSE
2043 * @type String
2044 * @static
2045 * @final
2046 */
2047YAHOO.widget.DataSource.ERROR_DATAPARSE = "Response data could not be parsed";
2048
2049
2050/////////////////////////////////////////////////////////////////////////////
2051//
2052// Public member variables
2053//
2054/////////////////////////////////////////////////////////////////////////////
2055
2056/**
2057 * Max size of the local cache. Set to 0 to turn off caching. Caching is
2058 * useful to reduce the number of server connections. Recommended only for data
2059 * sources that return comprehensive results for queries or when stale data is
2060 * not an issue.
2061 *
2062 * @property maxCacheEntries
2063 * @type Number
2064 * @default 15
2065 */
2066YAHOO.widget.DataSource.prototype.maxCacheEntries = 15;
2067
2068/**
2069 * Use this to equate cache matching with the type of matching done by your live
2070 * data source. If caching is on and queryMatchContains is true, the cache
2071 * returns results that "contain" the query string. By default,
2072 * queryMatchContains is set to false, meaning the cache only returns results
2073 * that "start with" the query string.
2074 *
2075 * @property queryMatchContains
2076 * @type Boolean
2077 * @default false
2078 */
2079YAHOO.widget.DataSource.prototype.queryMatchContains = false;
2080
2081/**
2082 * Enables query subset matching. If caching is on and queryMatchSubset is
2083 * true, substrings of queries will return matching cached results. For
2084 * instance, if the first query is for "abc" susequent queries that start with
2085 * "abc", like "abcd", will be queried against the cache, and not the live data
2086 * source. Recommended only for DataSources that return comprehensive results
2087 * for queries with very few characters.
2088 *
2089 * @property queryMatchSubset
2090 * @type Boolean
2091 * @default false
2092 *
2093 */
2094YAHOO.widget.DataSource.prototype.queryMatchSubset = false;
2095
2096/**
2097 * Enables query case-sensitivity matching. If caching is on and
2098 * queryMatchCase is true, queries will only return results for case-sensitive
2099 * matches.
2100 *
2101 * @property queryMatchCase
2102 * @type Boolean
2103 * @default false
2104 */
2105YAHOO.widget.DataSource.prototype.queryMatchCase = false;
2106
2107
2108/////////////////////////////////////////////////////////////////////////////
2109//
2110// Public methods
2111//
2112/////////////////////////////////////////////////////////////////////////////
2113
2114 /**
2115 * Public accessor to the unique name of the DataSource instance.
2116 *
2117 * @method toString
2118 * @return {String} Unique name of the DataSource instance
2119 */
2120YAHOO.widget.DataSource.prototype.toString = function() {
2121 return "DataSource " + this._sName;
2122};
2123
2124/**
2125 * Retrieves query results, first checking the local cache, then making the
2126 * query request to the live data source as defined by the function doQuery.
2127 *
2128 * @method getResults
2129 * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.
2130 * @param sQuery {String} Query string.
2131 * @param oParent {Object} The object instance that has requested data.
2132 */
2133YAHOO.widget.DataSource.prototype.getResults = function(oCallbackFn, sQuery, oParent) {
2134
2135 // First look in cache
2136 var aResults = this._doQueryCache(oCallbackFn,sQuery,oParent);
2137
2138 // Not in cache, so get results from server
2139 if(aResults.length === 0) {
2140 this.queryEvent.fire(this, oParent, sQuery);
2141 this.doQuery(oCallbackFn, sQuery, oParent);
2142 }
2143};
2144
2145/**
2146 * Abstract method implemented by subclasses to make a query to the live data
2147 * source. Must call the callback function with the response returned from the
2148 * query. Populates cache (if enabled).
2149 *
2150 * @method doQuery
2151 * @param oCallbackFn {HTMLFunction} Callback function implemented by oParent to which to return results.
2152 * @param sQuery {String} Query string.
2153 * @param oParent {Object} The object instance that has requested data.
2154 */
2155YAHOO.widget.DataSource.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {
2156 /* override this */
2157};
2158
2159/**
2160 * Flushes cache.
2161 *
2162 * @method flushCache
2163 */
2164YAHOO.widget.DataSource.prototype.flushCache = function() {
2165 if(this._aCache) {
2166 this._aCache = [];
2167 }
2168 if(this._aCacheHelper) {
2169 this._aCacheHelper = [];
2170 }
2171 this.cacheFlushEvent.fire(this);
2172};
2173
2174/////////////////////////////////////////////////////////////////////////////
2175//
2176// Public events
2177//
2178/////////////////////////////////////////////////////////////////////////////
2179
2180/**
2181 * Fired when a query is made to the live data source.
2182 *
2183 * @event queryEvent
2184 * @param oSelf {Object} The DataSource instance.
2185 * @param oParent {Object} The requesting object.
2186 * @param sQuery {String} The query string.
2187 */
2188YAHOO.widget.DataSource.prototype.queryEvent = null;
2189
2190/**
2191 * Fired when a query is made to the local cache.
2192 *
2193 * @event cacheQueryEvent
2194 * @param oSelf {Object} The DataSource instance.
2195 * @param oParent {Object} The requesting object.
2196 * @param sQuery {String} The query string.
2197 */
2198YAHOO.widget.DataSource.prototype.cacheQueryEvent = null;
2199
2200/**
2201 * Fired when data is retrieved from the live data source.
2202 *
2203 * @event getResultsEvent
2204 * @param oSelf {Object} The DataSource instance.
2205 * @param oParent {Object} The requesting object.
2206 * @param sQuery {String} The query string.
2207 * @param aResults {Object[]} Array of result objects.
2208 */
2209YAHOO.widget.DataSource.prototype.getResultsEvent = null;
2210
2211/**
2212 * Fired when data is retrieved from the local cache.
2213 *
2214 * @event getCachedResultsEvent
2215 * @param oSelf {Object} The DataSource instance.
2216 * @param oParent {Object} The requesting object.
2217 * @param sQuery {String} The query string.
2218 * @param aResults {Object[]} Array of result objects.
2219 */
2220YAHOO.widget.DataSource.prototype.getCachedResultsEvent = null;
2221
2222/**
2223 * Fired when an error is encountered with the live data source.
2224 *
2225 * @event dataErrorEvent
2226 * @param oSelf {Object} The DataSource instance.
2227 * @param oParent {Object} The requesting object.
2228 * @param sQuery {String} The query string.
2229 * @param sMsg {String} Error message string
2230 */
2231YAHOO.widget.DataSource.prototype.dataErrorEvent = null;
2232
2233/**
2234 * Fired when the local cache is flushed.
2235 *
2236 * @event cacheFlushEvent
2237 * @param oSelf {Object} The DataSource instance
2238 */
2239YAHOO.widget.DataSource.prototype.cacheFlushEvent = null;
2240
2241/////////////////////////////////////////////////////////////////////////////
2242//
2243// Private member variables
2244//
2245/////////////////////////////////////////////////////////////////////////////
2246
2247/**
2248 * Internal class variable to index multiple DataSource instances.
2249 *
2250 * @property _nIndex
2251 * @type Number
2252 * @private
2253 * @static
2254 */
2255YAHOO.widget.DataSource._nIndex = 0;
2256
2257/**
2258 * Name of DataSource instance.
2259 *
2260 * @property _sName
2261 * @type String
2262 * @private
2263 */
2264YAHOO.widget.DataSource.prototype._sName = null;
2265
2266/**
2267 * Local cache of data result objects indexed chronologically.
2268 *
2269 * @property _aCache
2270 * @type Object[]
2271 * @private
2272 */
2273YAHOO.widget.DataSource.prototype._aCache = null;
2274
2275
2276/////////////////////////////////////////////////////////////////////////////
2277//
2278// Private methods
2279//
2280/////////////////////////////////////////////////////////////////////////////
2281
2282/**
2283 * Initializes DataSource instance.
2284 *
2285 * @method _init
2286 * @private
2287 */
2288YAHOO.widget.DataSource.prototype._init = function() {
2289 // Validate and initialize public configs
2290 var maxCacheEntries = this.maxCacheEntries;
2291 if(isNaN(maxCacheEntries) || (maxCacheEntries < 0)) {
2292 maxCacheEntries = 0;
2293 }
2294 // Initialize local cache
2295 if(maxCacheEntries > 0 && !this._aCache) {
2296 this._aCache = [];
2297 }
2298
2299 this._sName = "instance" + YAHOO.widget.DataSource._nIndex;
2300 YAHOO.widget.DataSource._nIndex++;
2301
2302 this.queryEvent = new YAHOO.util.CustomEvent("query", this);
2303 this.cacheQueryEvent = new YAHOO.util.CustomEvent("cacheQuery", this);
2304 this.getResultsEvent = new YAHOO.util.CustomEvent("getResults", this);
2305 this.getCachedResultsEvent = new YAHOO.util.CustomEvent("getCachedResults", this);
2306 this.dataErrorEvent = new YAHOO.util.CustomEvent("dataError", this);
2307 this.cacheFlushEvent = new YAHOO.util.CustomEvent("cacheFlush", this);
2308};
2309
2310/**
2311 * Adds a result object to the local cache, evicting the oldest element if the
2312 * cache is full. Newer items will have higher indexes, the oldest item will have
2313 * index of 0.
2314 *
2315 * @method _addCacheElem
2316 * @param oResult {Object} Data result object, including array of results.
2317 * @private
2318 */
2319YAHOO.widget.DataSource.prototype._addCacheElem = function(oResult) {
2320 var aCache = this._aCache;
2321 // Don't add if anything important is missing.
2322 if(!aCache || !oResult || !oResult.query || !oResult.results) {
2323 return;
2324 }
2325
2326 // If the cache is full, make room by removing from index=0
2327 if(aCache.length >= this.maxCacheEntries) {
2328 aCache.shift();
2329 }
2330
2331 // Add to cache, at the end of the array
2332 aCache.push(oResult);
2333};
2334
2335/**
2336 * Queries the local cache for results. If query has been cached, the callback
2337 * function is called with the results, and the cached is refreshed so that it
2338 * is now the newest element.
2339 *
2340 * @method _doQueryCache
2341 * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.
2342 * @param sQuery {String} Query string.
2343 * @param oParent {Object} The object instance that has requested data.
2344 * @return aResults {Object[]} Array of results from local cache if found, otherwise null.
2345 * @private
2346 */
2347YAHOO.widget.DataSource.prototype._doQueryCache = function(oCallbackFn, sQuery, oParent) {
2348 var aResults = [];
2349 var bMatchFound = false;
2350 var aCache = this._aCache;
2351 var nCacheLength = (aCache) ? aCache.length : 0;
2352 var bMatchContains = this.queryMatchContains;
2353
2354 // If cache is enabled...
2355 if((this.maxCacheEntries > 0) && aCache && (nCacheLength > 0)) {
2356 this.cacheQueryEvent.fire(this, oParent, sQuery);
2357 // If case is unimportant, normalize query now instead of in loops
2358 if(!this.queryMatchCase) {
2359 var sOrigQuery = sQuery;
2360 sQuery = sQuery.toLowerCase();
2361 }
2362
2363 // Loop through each cached element's query property...
2364 for(var i = nCacheLength-1; i >= 0; i--) {
2365 var resultObj = aCache[i];
2366 var aAllResultItems = resultObj.results;
2367 // If case is unimportant, normalize match key for comparison
2368 var matchKey = (!this.queryMatchCase) ?
2369 encodeURIComponent(resultObj.query).toLowerCase():
2370 encodeURIComponent(resultObj.query);
2371
2372 // If a cached match key exactly matches the query...
2373 if(matchKey == sQuery) {
2374 // Stash all result objects into aResult[] and stop looping through the cache.
2375 bMatchFound = true;
2376 aResults = aAllResultItems;
2377
2378 // The matching cache element was not the most recent,
2379 // so now we need to refresh the cache.
2380 if(i != nCacheLength-1) {
2381 // Remove element from its original location
2382 aCache.splice(i,1);
2383 // Add element as newest
2384 this._addCacheElem(resultObj);
2385 }
2386 break;
2387 }
2388 // Else if this query is not an exact match and subset matching is enabled...
2389 else if(this.queryMatchSubset) {
2390 // Loop through substrings of each cached element's query property...
2391 for(var j = sQuery.length-1; j >= 0 ; j--) {
2392 var subQuery = sQuery.substr(0,j);
2393
2394 // If a substring of a cached sQuery exactly matches the query...
2395 if(matchKey == subQuery) {
2396 bMatchFound = true;
2397
2398 // Go through each cached result object to match against the query...
2399 for(var k = aAllResultItems.length-1; k >= 0; k--) {
2400 var aRecord = aAllResultItems[k];
2401 var sKeyIndex = (this.queryMatchCase) ?
2402 encodeURIComponent(aRecord[0]).indexOf(sQuery):
2403 encodeURIComponent(aRecord[0]).toLowerCase().indexOf(sQuery);
2404
2405 // A STARTSWITH match is when the query is found at the beginning of the key string...
2406 if((!bMatchContains && (sKeyIndex === 0)) ||
2407 // A CONTAINS match is when the query is found anywhere within the key string...
2408 (bMatchContains && (sKeyIndex > -1))) {
2409 // Stash a match into aResults[].
2410 aResults.unshift(aRecord);
2411 }
2412 }
2413
2414 // Add the subset match result set object as the newest element to cache,
2415 // and stop looping through the cache.
2416 resultObj = {};
2417 resultObj.query = sQuery;
2418 resultObj.results = aResults;
2419 this._addCacheElem(resultObj);
2420 break;
2421 }
2422 }
2423 if(bMatchFound) {
2424 break;
2425 }
2426 }
2427 }
2428
2429 // If there was a match, send along the results.
2430 if(bMatchFound) {
2431 this.getCachedResultsEvent.fire(this, oParent, sOrigQuery, aResults);
2432 oCallbackFn(sOrigQuery, aResults, oParent);
2433 }
2434 }
2435 return aResults;
2436};
2437
2438
2439/****************************************************************************/
2440/****************************************************************************/
2441/****************************************************************************/
2442
2443/**
2444 * Implementation of YAHOO.widget.DataSource using XML HTTP requests that return
2445 * query results.
2446 *
2447 * @class DS_XHR
2448 * @extends YAHOO.widget.DataSource
2449 * @requires connection
2450 * @constructor
2451 * @param sScriptURI {String} Absolute or relative URI to script that returns query
2452 * results as JSON, XML, or delimited flat-file data.
2453 * @param aSchema {String[]} Data schema definition of results.
2454 * @param oConfigs {Object} (optional) Object literal of config params.
2455 */
2456YAHOO.widget.DS_XHR = function(sScriptURI, aSchema, oConfigs) {
2457 // Set any config params passed in to override defaults
2458 if(typeof oConfigs == "object") {
2459 for(var sConfig in oConfigs) {
2460 this[sConfig] = oConfigs[sConfig];
2461 }
2462 }
2463
2464 // Initialization sequence
2465 if(!aSchema || (aSchema.constructor != Array)) {
2466 return;
2467 }
2468 else {
2469 this.schema = aSchema;
2470 }
2471 this.scriptURI = sScriptURI;
2472 this._init();
2473};
2474
2475YAHOO.widget.DS_XHR.prototype = new YAHOO.widget.DataSource();
2476
2477/////////////////////////////////////////////////////////////////////////////
2478//
2479// Public constants
2480//
2481/////////////////////////////////////////////////////////////////////////////
2482
2483/**
2484 * JSON data type.
2485 *
2486 * @property TYPE_JSON
2487 * @type Number
2488 * @static
2489 * @final
2490 */
2491YAHOO.widget.DS_XHR.TYPE_JSON = 0;
2492
2493/**
2494 * XML data type.
2495 *
2496 * @property TYPE_XML
2497 * @type Number
2498 * @static
2499 * @final
2500 */
2501YAHOO.widget.DS_XHR.TYPE_XML = 1;
2502
2503/**
2504 * Flat-file data type.
2505 *
2506 * @property TYPE_FLAT
2507 * @type Number
2508 * @static
2509 * @final
2510 */
2511YAHOO.widget.DS_XHR.TYPE_FLAT = 2;
2512
2513/**
2514 * Error message for XHR failure.
2515 *
2516 * @property ERROR_DATAXHR
2517 * @type String
2518 * @static
2519 * @final
2520 */
2521YAHOO.widget.DS_XHR.ERROR_DATAXHR = "XHR response failed";
2522
2523/////////////////////////////////////////////////////////////////////////////
2524//
2525// Public member variables
2526//
2527/////////////////////////////////////////////////////////////////////////////
2528
2529/**
2530 * Alias to YUI Connection Manager. Allows implementers to specify their own
2531 * subclasses of the YUI Connection Manager utility.
2532 *
2533 * @property connMgr
2534 * @type Object
2535 * @default YAHOO.util.Connect
2536 */
2537YAHOO.widget.DS_XHR.prototype.connMgr = YAHOO.util.Connect;
2538
2539/**
2540 * Number of milliseconds the XHR connection will wait for a server response. A
2541 * a value of zero indicates the XHR connection will wait forever. Any value
2542 * greater than zero will use the Connection utility's Auto-Abort feature.
2543 *
2544 * @property connTimeout
2545 * @type Number
2546 * @default 0
2547 */
2548YAHOO.widget.DS_XHR.prototype.connTimeout = 0;
2549
2550/**
2551 * Absolute or relative URI to script that returns query results. For instance,
2552 * queries will be sent to &#60;scriptURI&#62;?&#60;scriptQueryParam&#62;=userinput
2553 *
2554 * @property scriptURI
2555 * @type String
2556 */
2557YAHOO.widget.DS_XHR.prototype.scriptURI = null;
2558
2559/**
2560 * Query string parameter name sent to scriptURI. For instance, queries will be
2561 * sent to &#60;scriptURI&#62;?&#60;scriptQueryParam&#62;=userinput
2562 *
2563 * @property scriptQueryParam
2564 * @type String
2565 * @default "query"
2566 */
2567YAHOO.widget.DS_XHR.prototype.scriptQueryParam = "query";
2568
2569/**
2570 * String of key/value pairs to append to requests made to scriptURI. Define
2571 * this string when you want to send additional query parameters to your script.
2572 * When defined, queries will be sent to
2573 * &#60;scriptURI&#62;?&#60;scriptQueryParam&#62;=userinput&#38;&#60;scriptQueryAppend&#62;
2574 *
2575 * @property scriptQueryAppend
2576 * @type String
2577 * @default ""
2578 */
2579YAHOO.widget.DS_XHR.prototype.scriptQueryAppend = "";
2580
2581/**
2582 * XHR response data type. Other types that may be defined are YAHOO.widget.DS_XHR.TYPE_XML
2583 * and YAHOO.widget.DS_XHR.TYPE_FLAT.
2584 *
2585 * @property responseType
2586 * @type String
2587 * @default YAHOO.widget.DS_XHR.TYPE_JSON
2588 */
2589YAHOO.widget.DS_XHR.prototype.responseType = YAHOO.widget.DS_XHR.TYPE_JSON;
2590
2591/**
2592 * String after which to strip results. If the results from the XHR are sent
2593 * back as HTML, the gzip HTML comment appears at the end of the data and should
2594 * be ignored.
2595 *
2596 * @property responseStripAfter
2597 * @type String
2598 * @default "\n&#60;!-"
2599 */
2600YAHOO.widget.DS_XHR.prototype.responseStripAfter = "\n<!-";
2601
2602/////////////////////////////////////////////////////////////////////////////
2603//
2604// Public methods
2605//
2606/////////////////////////////////////////////////////////////////////////////
2607
2608/**
2609 * Queries the live data source defined by scriptURI for results. Results are
2610 * passed back to a callback function.
2611 *
2612 * @method doQuery
2613 * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.
2614 * @param sQuery {String} Query string.
2615 * @param oParent {Object} The object instance that has requested data.
2616 */
2617YAHOO.widget.DS_XHR.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {
2618 var isXML = (this.responseType == YAHOO.widget.DS_XHR.TYPE_XML);
2619 var sUri = this.scriptURI+"?"+this.scriptQueryParam+"="+sQuery;
2620 if(this.scriptQueryAppend.length > 0) {
2621 sUri += "&" + this.scriptQueryAppend;
2622 }
2623 var oResponse = null;
2624
2625 var oSelf = this;
2626 /*
2627 * Sets up ajax request callback
2628 *
2629 * @param {object} oReq HTTPXMLRequest object
2630 * @private
2631 */
2632 var responseSuccess = function(oResp) {
2633 // Response ID does not match last made request ID.
2634 if(!oSelf._oConn || (oResp.tId != oSelf._oConn.tId)) {
2635 oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATANULL);
2636 return;
2637 }
2638//DEBUG
2639for(var foo in oResp) {
2640}
2641 if(!isXML) {
2642 oResp = oResp.responseText;
2643 }
2644 else {
2645 oResp = oResp.responseXML;
2646 }
2647 if(oResp === null) {
2648 oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATANULL);
2649 return;
2650 }
2651
2652 var aResults = oSelf.parseResponse(sQuery, oResp, oParent);
2653 var resultObj = {};
2654 resultObj.query = decodeURIComponent(sQuery);
2655 resultObj.results = aResults;
2656 if(aResults === null) {
2657 oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATAPARSE);
2658 aResults = [];
2659 }
2660 else {
2661 oSelf.getResultsEvent.fire(oSelf, oParent, sQuery, aResults);
2662 oSelf._addCacheElem(resultObj);
2663 }
2664 oCallbackFn(sQuery, aResults, oParent);
2665 };
2666
2667 var responseFailure = function(oResp) {
2668 oSelf.dataErrorEvent.fire(oSelf, oParent, sQuery, YAHOO.widget.DS_XHR.ERROR_DATAXHR);
2669 return;
2670 };
2671
2672 var oCallback = {
2673 success:responseSuccess,
2674 failure:responseFailure
2675 };
2676
2677 if(!isNaN(this.connTimeout) && this.connTimeout > 0) {
2678 oCallback.timeout = this.connTimeout;
2679 }
2680
2681 if(this._oConn) {
2682 this.connMgr.abort(this._oConn);
2683 }
2684
2685 oSelf._oConn = this.connMgr.asyncRequest("GET", sUri, oCallback, null);
2686};
2687
2688/**
2689 * Parses raw response data into an array of result objects. The result data key
2690 * is always stashed in the [0] element of each result object.
2691 *
2692 * @method parseResponse
2693 * @param sQuery {String} Query string.
2694 * @param oResponse {Object} The raw response data to parse.
2695 * @param oParent {Object} The object instance that has requested data.
2696 * @returns {Object[]} Array of result objects.
2697 */
2698YAHOO.widget.DS_XHR.prototype.parseResponse = function(sQuery, oResponse, oParent) {
2699 var aSchema = this.schema;
2700 var aResults = [];
2701 var bError = false;
2702
2703 // Strip out comment at the end of results
2704 var nEnd = ((this.responseStripAfter !== "") && (oResponse.indexOf)) ?
2705 oResponse.indexOf(this.responseStripAfter) : -1;
2706 if(nEnd != -1) {
2707 oResponse = oResponse.substring(0,nEnd);
2708 }
2709
2710 switch (this.responseType) {
2711 case YAHOO.widget.DS_XHR.TYPE_JSON:
2712 var jsonList;
2713 // Divert KHTML clients from JSON lib
2714 if(window.JSON && (navigator.userAgent.toLowerCase().indexOf('khtml')== -1)) {
2715 // Use the JSON utility if available
2716 var jsonObjParsed = JSON.parse(oResponse);
2717 if(!jsonObjParsed) {
2718 bError = true;
2719 break;
2720 }
2721 else {
2722 try {
2723 // eval is necessary here since aSchema[0] is of unknown depth
2724 jsonList = eval("jsonObjParsed." + aSchema[0]);
2725 }
2726 catch(e) {
2727 bError = true;
2728 break;
2729 }
2730 }
2731 }
2732 else {
2733 // Parse the JSON response as a string
2734 try {
2735 // Trim leading spaces
2736 while (oResponse.substring(0,1) == " ") {
2737 oResponse = oResponse.substring(1, oResponse.length);
2738 }
2739
2740 // Invalid JSON response
2741 if(oResponse.indexOf("{") < 0) {
2742 bError = true;
2743 break;
2744 }
2745
2746 // Empty (but not invalid) JSON response
2747 if(oResponse.indexOf("{}") === 0) {
2748 break;
2749 }
2750
2751 // Turn the string into an object literal...
2752 // ...eval is necessary here
2753 var jsonObjRaw = eval("(" + oResponse + ")");
2754 if(!jsonObjRaw) {
2755 bError = true;
2756 break;
2757 }
2758
2759 // Grab the object member that contains an array of all reponses...
2760 // ...eval is necessary here since aSchema[0] is of unknown depth
2761 jsonList = eval("(jsonObjRaw." + aSchema[0]+")");
2762 }
2763 catch(e) {
2764 bError = true;
2765 break;
2766 }
2767 }
2768
2769 if(!jsonList) {
2770 bError = true;
2771 break;
2772 }
2773
2774 if(jsonList.constructor != Array) {
2775 jsonList = [jsonList];
2776 }
2777
2778 // Loop through the array of all responses...
2779 for(var i = jsonList.length-1; i >= 0 ; i--) {
2780 var aResultItem = [];
2781 var jsonResult = jsonList[i];
2782 // ...and loop through each data field value of each response
2783 for(var j = aSchema.length-1; j >= 1 ; j--) {
2784 // ...and capture data into an array mapped according to the schema...
2785 var dataFieldValue = jsonResult[aSchema[j]];
2786 if(!dataFieldValue) {
2787 dataFieldValue = "";
2788 }
2789 aResultItem.unshift(dataFieldValue);
2790 }
2791 // If schema isn't well defined, pass along the entire result object
2792 if(aResultItem.length == 1) {
2793 aResultItem.push(jsonResult);
2794 }
2795 // Capture the array of data field values in an array of results
2796 aResults.unshift(aResultItem);
2797 }
2798 break;
2799 case YAHOO.widget.DS_XHR.TYPE_XML:
2800 // Get the collection of results
2801 var xmlList = oResponse.getElementsByTagName(aSchema[0]);
2802 if(!xmlList) {
2803 bError = true;
2804 break;
2805 }
2806 // Loop through each result
2807 for(var k = xmlList.length-1; k >= 0 ; k--) {
2808 var result = xmlList.item(k);
2809 var aFieldSet = [];
2810 // Loop through each data field in each result using the schema
2811 for(var m = aSchema.length-1; m >= 1 ; m--) {
2812 var sValue = null;
2813 // Values may be held in an attribute...
2814 var xmlAttr = result.attributes.getNamedItem(aSchema[m]);
2815 if(xmlAttr) {
2816 sValue = xmlAttr.value;
2817 }
2818 // ...or in a node
2819 else{
2820 var xmlNode = result.getElementsByTagName(aSchema[m]);
2821 if(xmlNode && xmlNode.item(0) && xmlNode.item(0).firstChild) {
2822 sValue = xmlNode.item(0).firstChild.nodeValue;
2823 }
2824 else {
2825 sValue = "";
2826 }
2827 }
2828 // Capture the schema-mapped data field values into an array
2829 aFieldSet.unshift(sValue);
2830 }
2831 // Capture each array of values into an array of results
2832 aResults.unshift(aFieldSet);
2833 }
2834 break;
2835 case YAHOO.widget.DS_XHR.TYPE_FLAT:
2836 if(oResponse.length > 0) {
2837 // Delete the last line delimiter at the end of the data if it exists
2838 var newLength = oResponse.length-aSchema[0].length;
2839 if(oResponse.substr(newLength) == aSchema[0]) {
2840 oResponse = oResponse.substr(0, newLength);
2841 }
2842 var aRecords = oResponse.split(aSchema[0]);
2843 for(var n = aRecords.length-1; n >= 0; n--) {
2844 aResults[n] = aRecords[n].split(aSchema[1]);
2845 }
2846 }
2847 break;
2848 default:
2849 break;
2850 }
2851 sQuery = null;
2852 oResponse = null;
2853 oParent = null;
2854 if(bError) {
2855 return null;
2856 }
2857 else {
2858 return aResults;
2859 }
2860};
2861
2862/////////////////////////////////////////////////////////////////////////////
2863//
2864// Private member variables
2865//
2866/////////////////////////////////////////////////////////////////////////////
2867
2868/**
2869 * XHR connection object.
2870 *
2871 * @property _oConn
2872 * @type Object
2873 * @private
2874 */
2875YAHOO.widget.DS_XHR.prototype._oConn = null;
2876
2877
2878/****************************************************************************/
2879/****************************************************************************/
2880/****************************************************************************/
2881
2882/**
2883 * Implementation of YAHOO.widget.DataSource using a native Javascript function as
2884 * its live data source.
2885 *
2886 * @class DS_JSFunction
2887 * @constructor
2888 * @extends YAHOO.widget.DataSource
2889 * @param oFunction {String} In-memory Javascript function that returns query results as an array of objects.
2890 * @param oConfigs {Object} (optional) Object literal of config params.
2891 */
2892YAHOO.widget.DS_JSFunction = function(oFunction, oConfigs) {
2893 // Set any config params passed in to override defaults
2894 if(typeof oConfigs == "object") {
2895 for(var sConfig in oConfigs) {
2896 this[sConfig] = oConfigs[sConfig];
2897 }
2898 }
2899
2900 // Initialization sequence
2901 if(!oFunction || (oFunction.constructor != Function)) {
2902 return;
2903 }
2904 else {
2905 this.dataFunction = oFunction;
2906 this._init();
2907 }
2908};
2909
2910YAHOO.widget.DS_JSFunction.prototype = new YAHOO.widget.DataSource();
2911
2912/////////////////////////////////////////////////////////////////////////////
2913//
2914// Public member variables
2915//
2916/////////////////////////////////////////////////////////////////////////////
2917
2918/**
2919 * In-memory Javascript function that returns query results.
2920 *
2921 * @property dataFunction
2922 * @type HTMLFunction
2923 */
2924YAHOO.widget.DS_JSFunction.prototype.dataFunction = null;
2925
2926/////////////////////////////////////////////////////////////////////////////
2927//
2928// Public methods
2929//
2930/////////////////////////////////////////////////////////////////////////////
2931
2932/**
2933 * Queries the live data source defined by function for results. Results are
2934 * passed back to a callback function.
2935 *
2936 * @method doQuery
2937 * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.
2938 * @param sQuery {String} Query string.
2939 * @param oParent {Object} The object instance that has requested data.
2940 */
2941YAHOO.widget.DS_JSFunction.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {
2942 var oFunction = this.dataFunction;
2943 var aResults = [];
2944
2945 aResults = oFunction(sQuery);
2946 if(aResults === null) {
2947 this.dataErrorEvent.fire(this, oParent, sQuery, YAHOO.widget.DataSource.ERROR_DATANULL);
2948 return;
2949 }
2950
2951 var resultObj = {};
2952 resultObj.query = decodeURIComponent(sQuery);
2953 resultObj.results = aResults;
2954 this._addCacheElem(resultObj);
2955
2956 this.getResultsEvent.fire(this, oParent, sQuery, aResults);
2957 oCallbackFn(sQuery, aResults, oParent);
2958 return;
2959};
2960
2961/****************************************************************************/
2962/****************************************************************************/
2963/****************************************************************************/
2964
2965/**
2966 * Implementation of YAHOO.widget.DataSource using a native Javascript array as
2967 * its live data source.
2968 *
2969 * @class DS_JSArray
2970 * @constructor
2971 * @extends YAHOO.widget.DataSource
2972 * @param aData {String[]} In-memory Javascript array of simple string data.
2973 * @param oConfigs {Object} (optional) Object literal of config params.
2974 */
2975YAHOO.widget.DS_JSArray = function(aData, oConfigs) {
2976 // Set any config params passed in to override defaults
2977 if(typeof oConfigs == "object") {
2978 for(var sConfig in oConfigs) {
2979 this[sConfig] = oConfigs[sConfig];
2980 }
2981 }
2982
2983 // Initialization sequence
2984 if(!aData || (aData.constructor != Array)) {
2985 return;
2986 }
2987 else {
2988 this.data = aData;
2989 this._init();
2990 }
2991};
2992
2993YAHOO.widget.DS_JSArray.prototype = new YAHOO.widget.DataSource();
2994
2995/////////////////////////////////////////////////////////////////////////////
2996//
2997// Public member variables
2998//
2999/////////////////////////////////////////////////////////////////////////////
3000
3001/**
3002 * In-memory Javascript array of strings.
3003 *
3004 * @property data
3005 * @type Array
3006 */
3007YAHOO.widget.DS_JSArray.prototype.data = null;
3008
3009/////////////////////////////////////////////////////////////////////////////
3010//
3011// Public methods
3012//
3013/////////////////////////////////////////////////////////////////////////////
3014
3015/**
3016 * Queries the live data source defined by data for results. Results are passed
3017 * back to a callback function.
3018 *
3019 * @method doQuery
3020 * @param oCallbackFn {HTMLFunction} Callback function defined by oParent object to which to return results.
3021 * @param sQuery {String} Query string.
3022 * @param oParent {Object} The object instance that has requested data.
3023 */
3024YAHOO.widget.DS_JSArray.prototype.doQuery = function(oCallbackFn, sQuery, oParent) {
3025 var aData = this.data; // the array
3026 var aResults = []; // container for results
3027 var bMatchFound = false;
3028 var bMatchContains = this.queryMatchContains;
3029 if(sQuery) {
3030 if(!this.queryMatchCase) {
3031 sQuery = sQuery.toLowerCase();
3032 }
3033
3034 // Loop through each element of the array...
3035 // which can be a string or an array of strings
3036 for(var i = aData.length-1; i >= 0; i--) {
3037 var aDataset = [];
3038
3039 if(aData[i]) {
3040 if(aData[i].constructor == String) {
3041 aDataset[0] = aData[i];
3042 }
3043 else if(aData[i].constructor == Array) {
3044 aDataset = aData[i];
3045 }
3046 }
3047
3048 if(aDataset[0] && (aDataset[0].constructor == String)) {
3049 var sKeyIndex = (this.queryMatchCase) ?
3050 encodeURIComponent(aDataset[0]).indexOf(sQuery):
3051 encodeURIComponent(aDataset[0]).toLowerCase().indexOf(sQuery);
3052
3053 // A STARTSWITH match is when the query is found at the beginning of the key string...
3054 if((!bMatchContains && (sKeyIndex === 0)) ||
3055 // A CONTAINS match is when the query is found anywhere within the key string...
3056 (bMatchContains && (sKeyIndex > -1))) {
3057 // Stash a match into aResults[].
3058 aResults.unshift(aDataset);
3059 }
3060 }
3061 }
3062 }
3063
3064 this.getResultsEvent.fire(this, oParent, sQuery, aResults);
3065 oCallbackFn(sQuery, aResults, oParent);
3066};
diff --git a/frontend/beta/js/YUI/calendar.js b/frontend/beta/js/YUI/calendar.js
new file mode 100644
index 0000000..3593551
--- a/dev/null
+++ b/frontend/beta/js/YUI/calendar.js
@@ -0,0 +1,4239 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version 0.12.0
6*/
7
8/**
9* Config is a utility used within an Object to allow the implementer to maintain a list of local configuration properties and listen for changes to those properties dynamically using CustomEvent. The initial values are also maintained so that the configuration can be reset at any given point to its initial state.
10* @class YAHOO.util.Config
11* @constructor
12 * @param {Object} ownerThe owner Object to which this Config Object belongs
13*/
14YAHOO.util.Config = function(owner) {
15 if (owner) {
16 this.init(owner);
17 }
18};
19
20YAHOO.util.Config.prototype = {
21
22 /**
23 * Object reference to the owner of this Config Object
24 * @property owner
25 * @type Object
26 */
27 owner : null,
28
29 /**
30 * Boolean flag that specifies whether a queue is currently being executed
31 * @property queueInProgress
32 * @type Boolean
33 */
34 queueInProgress : false,
35
36
37 /**
38 * Validates that the value passed in is a Boolean.
39 * @method checkBoolean
40 * @param {Object} valThe value to validate
41 * @return {Boolean}true, if the value is valid
42 */
43 checkBoolean: function(val) {
44 if (typeof val == 'boolean') {
45 return true;
46 } else {
47 return false;
48 }
49 },
50
51 /**
52 * Validates that the value passed in is a number.
53 * @method checkNumber
54 * @param {Object} valThe value to validate
55 * @return {Boolean}true, if the value is valid
56 */
57 checkNumber: function(val) {
58 if (isNaN(val)) {
59 return false;
60 } else {
61 return true;
62 }
63 }
64};
65
66
67/**
68* Initializes the configuration Object and all of its local members.
69* @method init
70 * @param {Object} ownerThe owner Object to which this Config Object belongs
71*/
72YAHOO.util.Config.prototype.init = function(owner) {
73
74 this.owner = owner;
75
76 /**
77 * Object reference to the owner of this Config Object
78 * @event configChangedEvent
79 */
80 this.configChangedEvent = new YAHOO.util.CustomEvent("configChanged");
81
82 this.queueInProgress = false;
83
84 /* Private Members */
85
86 /**
87 * Maintains the local collection of configuration property objects and their specified values
88 * @property config
89 * @private
90 * @type Object
91 */
92 var config = {};
93
94 /**
95 * Maintains the local collection of configuration property objects as they were initially applied.
96 * This object is used when resetting a property.
97 * @property initialConfig
98 * @private
99 * @type Object
100 */
101 var initialConfig = {};
102
103 /**
104 * Maintains the local, normalized CustomEvent queue
105 * @property eventQueue
106 * @private
107 * @type Object
108 */
109 var eventQueue = [];
110
111 /**
112 * Fires a configuration property event using the specified value.
113 * @method fireEvent
114 * @private
115 * @param {String} key The configuration property's name
116 * @param {value} Object The value of the correct type for the property
117 */
118 var fireEvent = function( key, value ) {
119 key = key.toLowerCase();
120
121 var property = config[key];
122
123 if (typeof property != 'undefined' && property.event) {
124 property.event.fire(value);
125 }
126 };
127 /* End Private Members */
128
129 /**
130 * Adds a property to the Config Object's private config hash.
131 * @method addProperty
132 * @param {String} keyThe configuration property's name
133 * @param {Object} propertyObjectThe Object containing all of this property's arguments
134 */
135 this.addProperty = function( key, propertyObject ) {
136 key = key.toLowerCase();
137
138 config[key] = propertyObject;
139
140 propertyObject.event = new YAHOO.util.CustomEvent(key);
141 propertyObject.key = key;
142
143 if (propertyObject.handler) {
144 propertyObject.event.subscribe(propertyObject.handler, this.owner, true);
145 }
146
147 this.setProperty(key, propertyObject.value, true);
148
149 if (! propertyObject.suppressEvent) {
150 this.queueProperty(key, propertyObject.value);
151 }
152 };
153
154 /**
155 * Returns a key-value configuration map of the values currently set in the Config Object.
156 * @method getConfig
157 * @return {Object} The current config, represented in a key-value map
158 */
159 this.getConfig = function() {
160 var cfg = {};
161
162 for (var prop in config) {
163 var property = config[prop];
164 if (typeof property != 'undefined' && property.event) {
165 cfg[prop] = property.value;
166 }
167 }
168
169 return cfg;
170 };
171
172 /**
173 * Returns the value of specified property.
174 * @method getProperty
175 * @param {String} keyThe name of the property
176 * @return {Object} The value of the specified property
177 */
178 this.getProperty = function(key) {
179 key = key.toLowerCase();
180
181 var property = config[key];
182 if (typeof property != 'undefined' && property.event) {
183 return property.value;
184 } else {
185 return undefined;
186 }
187 };
188
189 /**
190 * Resets the specified property's value to its initial value.
191 * @method resetProperty
192 * @param {String} keyThe name of the property
193 * @return {Boolean} True is the property was reset, false if not
194 */
195 this.resetProperty = function(key) {
196 key = key.toLowerCase();
197
198 var property = config[key];
199 if (typeof property != 'undefined' && property.event) {
200 if (initialConfig[key] && initialConfig[key] != 'undefined'){
201 this.setProperty(key, initialConfig[key]);
202 }
203 return true;
204 } else {
205 return false;
206 }
207 };
208
209 /**
210 * Sets the value of a property. If the silent property is passed as true, the property's event will not be fired.
211 * @method setProperty
212 * @param {String} key The name of the property
213 * @param {String} value The value to set the property to
214 * @param {Boolean} silentWhether the value should be set silently, without firing the property event.
215 * @return {Boolean} True, if the set was successful, false if it failed.
216 */
217 this.setProperty = function(key, value, silent) {
218 key = key.toLowerCase();
219
220 if (this.queueInProgress && ! silent) {
221 this.queueProperty(key,value); // Currently running through a queue...
222 return true;
223 } else {
224 var property = config[key];
225 if (typeof property != 'undefined' && property.event) {
226 if (property.validator && ! property.validator(value)) { // validator
227 return false;
228 } else {
229 property.value = value;
230 if (! silent) {
231 fireEvent(key, value);
232 this.configChangedEvent.fire([key, value]);
233 }
234 return true;
235 }
236 } else {
237 return false;
238 }
239 }
240 };
241
242 /**
243 * Sets the value of a property and queues its event to execute. If the event is already scheduled to execute, it is
244 * moved from its current position to the end of the queue.
245 * @method queueProperty
246 * @param {String} keyThe name of the property
247 * @param {String} valueThe value to set the property to
248 * @return {Boolean} true, if the set was successful, false if it failed.
249 */
250 this.queueProperty = function(key, value) {
251 key = key.toLowerCase();
252
253 var property = config[key];
254
255 if (typeof property != 'undefined' && property.event) {
256 if (typeof value != 'undefined' && property.validator && ! property.validator(value)) { // validator
257 return false;
258 } else {
259
260 if (typeof value != 'undefined') {
261 property.value = value;
262 } else {
263 value = property.value;
264 }
265
266 var foundDuplicate = false;
267
268 for (var i=0;i<eventQueue.length;i++) {
269 var queueItem = eventQueue[i];
270
271 if (queueItem) {
272 var queueItemKey = queueItem[0];
273 var queueItemValue = queueItem[1];
274
275 if (queueItemKey.toLowerCase() == key) {
276 // found a dupe... push to end of queue, null current item, and break
277 eventQueue[i] = null;
278 eventQueue.push([key, (typeof value != 'undefined' ? value : queueItemValue)]);
279 foundDuplicate = true;
280 break;
281 }
282 }
283 }
284
285 if (! foundDuplicate && typeof value != 'undefined') { // this is a refire, or a new property in the queue
286 eventQueue.push([key, value]);
287 }
288 }
289
290 if (property.supercedes) {
291 for (var s=0;s<property.supercedes.length;s++) {
292 var supercedesCheck = property.supercedes[s];
293
294 for (var q=0;q<eventQueue.length;q++) {
295 var queueItemCheck = eventQueue[q];
296
297 if (queueItemCheck) {
298 var queueItemCheckKey = queueItemCheck[0];
299 var queueItemCheckValue = queueItemCheck[1];
300
301 if ( queueItemCheckKey.toLowerCase() == supercedesCheck.toLowerCase() ) {
302 eventQueue.push([queueItemCheckKey, queueItemCheckValue]);
303 eventQueue[q] = null;
304 break;
305 }
306 }
307 }
308 }
309 }
310
311 return true;
312 } else {
313 return false;
314 }
315 };
316
317 /**
318 * Fires the event for a property using the property's current value.
319 * @method refireEvent
320 * @param {String} keyThe name of the property
321 */
322 this.refireEvent = function(key) {
323 key = key.toLowerCase();
324
325 var property = config[key];
326 if (typeof property != 'undefined' && property.event && typeof property.value != 'undefined') {
327 if (this.queueInProgress) {
328 this.queueProperty(key);
329 } else {
330 fireEvent(key, property.value);
331 }
332 }
333 };
334
335 /**
336 * Applies a key-value Object literal to the configuration, replacing any existing values, and queueing the property events.
337 * Although the values will be set, fireQueue() must be called for their associated events to execute.
338 * @method applyConfig
339 * @param {Object} userConfigThe configuration Object literal
340 * @param {Boolean} init When set to true, the initialConfig will be set to the userConfig passed in, so that calling a reset will reset the properties to the passed values.
341 */
342 this.applyConfig = function(userConfig, init) {
343 if (init) {
344 initialConfig = userConfig;
345 }
346 for (var prop in userConfig) {
347 this.queueProperty(prop, userConfig[prop]);
348 }
349 };
350
351 /**
352 * Refires the events for all configuration properties using their current values.
353 * @method refresh
354 */
355 this.refresh = function() {
356 for (var prop in config) {
357 this.refireEvent(prop);
358 }
359 };
360
361 /**
362 * Fires the normalized list of queued property change events
363 * @method fireQueue
364 */
365 this.fireQueue = function() {
366 this.queueInProgress = true;
367 for (var i=0;i<eventQueue.length;i++) {
368 var queueItem = eventQueue[i];
369 if (queueItem) {
370 var key = queueItem[0];
371 var value = queueItem[1];
372
373 var property = config[key];
374 property.value = value;
375
376 fireEvent(key,value);
377 }
378 }
379
380 this.queueInProgress = false;
381 eventQueue = [];
382 };
383
384 /**
385 * Subscribes an external handler to the change event for any given property.
386 * @method subscribeToConfigEvent
387 * @param {String} key The property name
388 * @param {Function} handler The handler function to use subscribe to the property's event
389 * @param {Object} obj The Object to use for scoping the event handler (see CustomEvent documentation)
390 * @param {Boolean} overrideOptional. If true, will override "this" within the handler to map to the scope Object passed into the method.
391 * @return {Boolean} True, if the subscription was successful, otherwise false.
392 */
393 this.subscribeToConfigEvent = function(key, handler, obj, override) {
394 key = key.toLowerCase();
395
396 var property = config[key];
397 if (typeof property != 'undefined' && property.event) {
398 if (! YAHOO.util.Config.alreadySubscribed(property.event, handler, obj)) {
399 property.event.subscribe(handler, obj, override);
400 }
401 return true;
402 } else {
403 return false;
404 }
405 };
406
407 /**
408 * Unsubscribes an external handler from the change event for any given property.
409 * @method unsubscribeFromConfigEvent
410 * @param {String} key The property name
411 * @param {Function} handler The handler function to use subscribe to the property's event
412 * @param {Object} obj The Object to use for scoping the event handler (see CustomEvent documentation)
413 * @return {Boolean} True, if the unsubscription was successful, otherwise false.
414 */
415 this.unsubscribeFromConfigEvent = function(key, handler, obj) {
416 key = key.toLowerCase();
417
418 var property = config[key];
419 if (typeof property != 'undefined' && property.event) {
420 return property.event.unsubscribe(handler, obj);
421 } else {
422 return false;
423 }
424 };
425
426 /**
427 * Returns a string representation of the Config object
428 * @method toString
429 * @return {String}The Config object in string format.
430 */
431 this.toString = function() {
432 var output = "Config";
433 if (this.owner) {
434 output += " [" + this.owner.toString() + "]";
435 }
436 return output;
437 };
438
439 /**
440 * Returns a string representation of the Config object's current CustomEvent queue
441 * @method outputEventQueue
442 * @return {String}The string list of CustomEvents currently queued for execution
443 */
444 this.outputEventQueue = function() {
445 var output = "";
446 for (var q=0;q<eventQueue.length;q++) {
447 var queueItem = eventQueue[q];
448 if (queueItem) {
449 output += queueItem[0] + "=" + queueItem[1] + ", ";
450 }
451 }
452 return output;
453 };
454};
455
456/**
457* Checks to determine if a particular function/Object pair are already subscribed to the specified CustomEvent
458* @method YAHOO.util.Config.alreadySubscribed
459* @static
460 * @param {YAHOO.util.CustomEvent} evtThe CustomEvent for which to check the subscriptions
461 * @param {Function} fnThe function to look for in the subscribers list
462 * @param {Object} objThe execution scope Object for the subscription
463 * @return {Boolean}true, if the function/Object pair is already subscribed to the CustomEvent passed in
464*/
465YAHOO.util.Config.alreadySubscribed = function(evt, fn, obj) {
466 for (var e=0;e<evt.subscribers.length;e++) {
467 var subsc = evt.subscribers[e];
468 if (subsc && subsc.obj == obj && subsc.fn == fn) {
469 return true;
470 }
471 }
472 return false;
473};
474
475/**
476* YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
477* used for adding, subtracting, and comparing dates.
478* @class YAHOO.widget.DateMath
479*/
480YAHOO.widget.DateMath = {
481 /**
482 * Constant field representing Day
483 * @property DAY
484 * @static
485 * @final
486 * @type String
487 */
488 DAY : "D",
489
490 /**
491 * Constant field representing Week
492 * @property WEEK
493 * @static
494 * @final
495 * @type String
496 */
497 WEEK : "W",
498
499 /**
500 * Constant field representing Year
501 * @property YEAR
502 * @static
503 * @final
504 * @type String
505 */
506 YEAR : "Y",
507
508 /**
509 * Constant field representing Month
510 * @property MONTH
511 * @static
512 * @final
513 * @type String
514 */
515 MONTH : "M",
516
517 /**
518 * Constant field representing one day, in milliseconds
519 * @property ONE_DAY_MS
520 * @static
521 * @final
522 * @type Number
523 */
524 ONE_DAY_MS : 1000*60*60*24,
525
526 /**
527 * Adds the specified amount of time to the this instance.
528 * @method add
529 * @param {Date} dateThe JavaScript Date object to perform addition on
530 * @param {String} fieldThe field constant to be used for performing addition.
531 * @param {Number} amountThe number of units (measured in the field constant) to add to the date.
532 * @return {Date} The resulting Date object
533 */
534 add : function(date, field, amount) {
535 var d = new Date(date.getTime());
536 switch (field) {
537 case this.MONTH:
538 var newMonth = date.getMonth() + amount;
539 var years = 0;
540
541
542 if (newMonth < 0) {
543 while (newMonth < 0) {
544 newMonth += 12;
545 years -= 1;
546 }
547 } else if (newMonth > 11) {
548 while (newMonth > 11) {
549 newMonth -= 12;
550 years += 1;
551 }
552 }
553
554 d.setMonth(newMonth);
555 d.setFullYear(date.getFullYear() + years);
556 break;
557 case this.DAY:
558 d.setDate(date.getDate() + amount);
559 break;
560 case this.YEAR:
561 d.setFullYear(date.getFullYear() + amount);
562 break;
563 case this.WEEK:
564 d.setDate(date.getDate() + (amount * 7));
565 break;
566 }
567 return d;
568 },
569
570 /**
571 * Subtracts the specified amount of time from the this instance.
572 * @method subtract
573 * @param {Date} dateThe JavaScript Date object to perform subtraction on
574 * @param {Number} fieldThe this field constant to be used for performing subtraction.
575 * @param {Number} amountThe number of units (measured in the field constant) to subtract from the date.
576 * @return {Date} The resulting Date object
577 */
578 subtract : function(date, field, amount) {
579 return this.add(date, field, (amount*-1));
580 },
581
582 /**
583 * Determines whether a given date is before another date on the calendar.
584 * @method before
585 * @param {Date} date The Date object to compare with the compare argument
586 * @param {Date} compareToThe Date object to use for the comparison
587 * @return {Boolean} true if the date occurs before the compared date; false if not.
588 */
589 before : function(date, compareTo) {
590 var ms = compareTo.getTime();
591 if (date.getTime() < ms) {
592 return true;
593 } else {
594 return false;
595 }
596 },
597
598 /**
599 * Determines whether a given date is after another date on the calendar.
600 * @method after
601 * @param {Date} date The Date object to compare with the compare argument
602 * @param {Date} compareToThe Date object to use for the comparison
603 * @return {Boolean} true if the date occurs after the compared date; false if not.
604 */
605 after : function(date, compareTo) {
606 var ms = compareTo.getTime();
607 if (date.getTime() > ms) {
608 return true;
609 } else {
610 return false;
611 }
612 },
613
614 /**
615 * Determines whether a given date is between two other dates on the calendar.
616 * @method between
617 * @param {Date} date The date to check for
618 * @param {Date} dateBeginThe start of the range
619 * @param {Date} dateEnd The end of the range
620 * @return {Boolean} true if the date occurs between the compared dates; false if not.
621 */
622 between : function(date, dateBegin, dateEnd) {
623 if (this.after(date, dateBegin) && this.before(date, dateEnd)) {
624 return true;
625 } else {
626 return false;
627 }
628 },
629
630 /**
631 * Retrieves a JavaScript Date object representing January 1 of any given year.
632 * @method getJan1
633 * @param {Number} calendarYear The calendar year for which to retrieve January 1
634 * @return {Date}January 1 of the calendar year specified.
635 */
636 getJan1 : function(calendarYear) {
637 return new Date(calendarYear,0,1);
638 },
639
640 /**
641 * Calculates the number of days the specified date is from January 1 of the specified calendar year.
642 * Passing January 1 to this function would return an offset value of zero.
643 * @method getDayOffset
644 * @param {Date} dateThe JavaScript date for which to find the offset
645 * @param {Number} calendarYearThe calendar year to use for determining the offset
646 * @return {Number}The number of days since January 1 of the given year
647 */
648 getDayOffset : function(date, calendarYear) {
649 var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
650
651 // Find the number of days the passed in date is away from the calendar year start
652 var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
653 return dayOffset;
654 },
655
656 /**
657 * Calculates the week number for the given date. This function assumes that week 1 is the
658 * week in which January 1 appears, regardless of whether the week consists of a full 7 days.
659 * The calendar year can be specified to help find what a the week number would be for a given
660 * date if the date overlaps years. For instance, a week may be considered week 1 of 2005, or
661 * week 53 of 2004. Specifying the optional calendarYear allows one to make this distinction
662 * easily.
663 * @method getWeekNumber
664 * @param {Date} dateThe JavaScript date for which to find the week number
665 * @param {Number} calendarYearOPTIONAL - The calendar year to use for determining the week number. Default is
666 * the calendar year of parameter "date".
667 * @param {Number} weekStartsOnOPTIONAL - The integer (0-6) representing which day a week begins on. Default is 0 (for Sunday).
668 * @return {Number}The week number of the given date.
669 */
670 getWeekNumber : function(date, calendarYear) {
671 date = this.clearTime(date);
672 var nearestThurs = new Date(date.getTime() + (4 * this.ONE_DAY_MS) - ((date.getDay()) * this.ONE_DAY_MS));
673
674 var jan1 = new Date(nearestThurs.getFullYear(),0,1);
675 var dayOfYear = ((nearestThurs.getTime() - jan1.getTime()) / this.ONE_DAY_MS) - 1;
676
677 var weekNum = Math.ceil((dayOfYear)/ 7);
678 return weekNum;
679 },
680
681 /**
682 * Determines if a given week overlaps two different years.
683 * @method isYearOverlapWeek
684 * @param {Date} weekBeginDateThe JavaScript Date representing the first day of the week.
685 * @return {Boolean}true if the date overlaps two different years.
686 */
687 isYearOverlapWeek : function(weekBeginDate) {
688 var overlaps = false;
689 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
690 if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
691 overlaps = true;
692 }
693 return overlaps;
694 },
695
696 /**
697 * Determines if a given week overlaps two different months.
698 * @method isMonthOverlapWeek
699 * @param {Date} weekBeginDateThe JavaScript Date representing the first day of the week.
700 * @return {Boolean}true if the date overlaps two different months.
701 */
702 isMonthOverlapWeek : function(weekBeginDate) {
703 var overlaps = false;
704 var nextWeek = this.add(weekBeginDate, this.DAY, 6);
705 if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
706 overlaps = true;
707 }
708 return overlaps;
709 },
710
711 /**
712 * Gets the first day of a month containing a given date.
713 * @method findMonthStart
714 * @param {Date} dateThe JavaScript Date used to calculate the month start
715 * @return {Date} The JavaScript Date representing the first day of the month
716 */
717 findMonthStart : function(date) {
718 var start = new Date(date.getFullYear(), date.getMonth(), 1);
719 return start;
720 },
721
722 /**
723 * Gets the last day of a month containing a given date.
724 * @method findMonthEnd
725 * @param {Date} dateThe JavaScript Date used to calculate the month end
726 * @return {Date} The JavaScript Date representing the last day of the month
727 */
728 findMonthEnd : function(date) {
729 var start = this.findMonthStart(date);
730 var nextMonth = this.add(start, this.MONTH, 1);
731 var end = this.subtract(nextMonth, this.DAY, 1);
732 return end;
733 },
734
735 /**
736 * Clears the time fields from a given date, effectively setting the time to midnight.
737 * @method clearTime
738 * @param {Date} dateThe JavaScript Date for which the time fields will be cleared
739 * @return {Date} The JavaScript Date cleared of all time fields
740 */
741 clearTime : function(date) {
742 date.setHours(12,0,0,0);
743 return date;
744 }
745};
746
747/**
748* The Calendar component is a UI control that enables users to choose one or more dates from a graphical calendar presented in a one-month ("one-up") or two-month ("two-up") interface. Calendars are generated entirely via script and can be navigated without any page refreshes.
749* @module Calendar
750* @title Calendar Widget
751* @namespace YAHOO.widget
752* @requires yahoo,dom,event
753*/
754
755/**
756* Calendar is the base class for the Calendar widget. In its most basic
757* implementation, it has the ability to render a calendar widget on the page
758* that can be manipulated to select a single date, move back and forth between
759* months and years.
760* <p>To construct the placeholder for the calendar widget, the code is as
761* follows:
762 *<xmp>
763 * <div id="cal1Container"></div>
764 *</xmp>
765* Note that the table can be replaced with any kind of element.
766* </p>
767* @namespace YAHOO.widget
768* @class Calendar
769* @constructor
770 * @param {String} id The id of the table element that will represent the calendar widget
771 * @param {String} containerIdThe id of the container div element that will wrap the calendar table
772 * @param {Object} config The configuration object containing the Calendar's arguments
773*/
774YAHOO.widget.Calendar = function(id, containerId, config) {
775 this.init(id, containerId, config);
776};
777
778/**
779* The path to be used for images loaded for the Calendar
780* @property YAHOO.widget.Calendar.IMG_ROOT
781* @static
782* @type String
783*/
784YAHOO.widget.Calendar.IMG_ROOT = (window.location.href.toLowerCase().indexOf("https") === 0 ? "https://a248.e.akamai.net/sec.yimg.com/i/" : "http://us.i1.yimg.com/us.yimg.com/i/");
785
786/**
787* Type constant used for renderers to represent an individual date (M/D/Y)
788* @property YAHOO.widget.Calendar.DATE
789* @static
790* @final
791* @type String
792*/
793YAHOO.widget.Calendar.DATE = "D";
794
795/**
796* Type constant used for renderers to represent an individual date across any year (M/D)
797* @property YAHOO.widget.Calendar.MONTH_DAY
798* @static
799* @final
800* @type String
801*/
802YAHOO.widget.Calendar.MONTH_DAY = "MD";
803
804/**
805* Type constant used for renderers to represent a weekday
806* @property YAHOO.widget.Calendar.WEEKDAY
807* @static
808* @final
809* @type String
810*/
811YAHOO.widget.Calendar.WEEKDAY = "WD";
812
813/**
814* Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
815* @property YAHOO.widget.Calendar.RANGE
816* @static
817* @final
818* @type String
819*/
820YAHOO.widget.Calendar.RANGE = "R";
821
822/**
823* Type constant used for renderers to represent a month across any year
824* @property YAHOO.widget.Calendar.MONTH
825* @static
826* @final
827* @type String
828*/
829YAHOO.widget.Calendar.MONTH = "M";
830
831/**
832* Constant that represents the total number of date cells that are displayed in a given month
833* @property YAHOO.widget.Calendar.DISPLAY_DAYS
834* @static
835* @final
836* @type Number
837*/
838YAHOO.widget.Calendar.DISPLAY_DAYS = 42;
839
840/**
841* Constant used for halting the execution of the remainder of the render stack
842* @property YAHOO.widget.Calendar.STOP_RENDER
843* @static
844* @final
845* @type String
846*/
847YAHOO.widget.Calendar.STOP_RENDER = "S";
848
849YAHOO.widget.Calendar.prototype = {
850
851 /**
852 * The configuration object used to set up the calendars various locale and style options.
853 * @property Config
854 * @private
855 * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
856 * @type Object
857 */
858 Config : null,
859
860 /**
861 * The parent CalendarGroup, only to be set explicitly by the parent group
862 * @property parent
863 * @type CalendarGroup
864 */
865 parent : null,
866
867 /**
868 * The index of this item in the parent group
869 * @property index
870 * @type Number
871 */
872 index : -1,
873
874 /**
875 * The collection of calendar table cells
876 * @property cells
877 * @type HTMLTableCellElement[]
878 */
879 cells : null,
880
881 /**
882 * The collection of calendar cell dates that is parallel to the cells collection. The array contains dates field arrays in the format of [YYYY, M, D].
883 * @property cellDates
884 * @type Array[](Number[])
885 */
886 cellDates : null,
887
888 /**
889 * The id that uniquely identifies this calendar. This id should match the id of the placeholder element on the page.
890 * @property id
891 * @type String
892 */
893 id : null,
894
895 /**
896 * The DOM element reference that points to this calendar's container element. The calendar will be inserted into this element when the shell is rendered.
897 * @property oDomContainer
898 * @type HTMLElement
899 */
900 oDomContainer : null,
901
902 /**
903 * A Date object representing today's date.
904 * @property today
905 * @type Date
906 */
907 today : null,
908
909 /**
910 * The list of render functions, along with required parameters, used to render cells.
911 * @property renderStack
912 * @type Array[]
913 */
914 renderStack : null,
915
916 /**
917 * A copy of the initial render functions created before rendering.
918 * @property _renderStack
919 * @private
920 * @type Array
921 */
922 _renderStack : null,
923
924 /**
925 * A Date object representing the month/year that the calendar is initially set to
926 * @property _pageDate
927 * @private
928 * @type Date
929 */
930 _pageDate : null,
931
932 /**
933 * The private list of initially selected dates.
934 * @property _selectedDates
935 * @private
936 * @type Array
937 */
938 _selectedDates : null,
939
940 /**
941 * A map of DOM event handlers to attach to cells associated with specific CSS class names
942 * @property domEventMap
943 * @type Object
944 */
945 domEventMap : null
946};
947
948
949
950/**
951* Initializes the Calendar widget.
952* @method init
953 * @param {String} id The id of the table element that will represent the calendar widget
954 * @param {String} containerIdThe id of the container div element that will wrap the calendar table
955 * @param {Object} config The configuration object containing the Calendar's arguments
956*/
957YAHOO.widget.Calendar.prototype.init = function(id, containerId, config) {
958 this.initEvents();
959 this.today = new Date();
960 YAHOO.widget.DateMath.clearTime(this.today);
961
962 this.id = id;
963 this.oDomContainer = document.getElementById(containerId);
964
965 /**
966 * The Config object used to hold the configuration variables for the Calendar
967 * @property cfg
968 * @type YAHOO.util.Config
969 */
970 this.cfg = new YAHOO.util.Config(this);
971
972 /**
973 * The local object which contains the Calendar's options
974 * @property Options
975 * @type Object
976 */
977 this.Options = {};
978
979 /**
980 * The local object which contains the Calendar's locale settings
981 * @property Locale
982 * @type Object
983 */
984 this.Locale = {};
985
986 this.initStyles();
987
988 YAHOO.util.Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
989 YAHOO.util.Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
990
991 this.cellDates = [];
992 this.cells = [];
993 this.renderStack = [];
994 this._renderStack = [];
995
996 this.setupConfig();
997
998 if (config) {
999 this.cfg.applyConfig(config, true);
1000 }
1001
1002 this.cfg.fireQueue();
1003};
1004
1005/**
1006* Renders the built-in IFRAME shim for the IE6 and below
1007* @method configIframe
1008*/
1009YAHOO.widget.Calendar.prototype.configIframe = function(type, args, obj) {
1010 var useIframe = args[0];
1011
1012 if (YAHOO.util.Dom.inDocument(this.oDomContainer)) {
1013 if (useIframe) {
1014 var pos = YAHOO.util.Dom.getStyle(this.oDomContainer, "position");
1015
1016 if (this.browser == "ie" && (pos == "absolute" || pos == "relative")) {
1017 if (! YAHOO.util.Dom.inDocument(this.iframe)) {
1018 this.iframe = document.createElement("iframe");
1019 this.iframe.src = "javascript:false;";
1020 YAHOO.util.Dom.setStyle(this.iframe, "opacity", "0");
1021 this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
1022 }
1023 }
1024 } else {
1025 if (this.iframe) {
1026 if (this.iframe.parentNode) {
1027 this.iframe.parentNode.removeChild(this.iframe);
1028 }
1029 this.iframe = null;
1030 }
1031 }
1032 }
1033};
1034
1035/**
1036* Default handler for the "title" property
1037* @method configTitle
1038*/
1039YAHOO.widget.Calendar.prototype.configTitle = function(type, args, obj) {
1040 var title = args[0];
1041 var close = this.cfg.getProperty("close");
1042
1043 var titleDiv;
1044
1045 if (title && title !== "") {
1046 titleDiv = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || document.createElement("div");
1047 titleDiv.className = YAHOO.widget.CalendarGroup.CSS_2UPTITLE;
1048 titleDiv.innerHTML = title;
1049 this.oDomContainer.insertBefore(titleDiv, this.oDomContainer.firstChild);
1050 YAHOO.util.Dom.addClass(this.oDomContainer, "withtitle");
1051 } else {
1052 titleDiv = YAHOO.util.Dom.getElementsByClassName(YAHOO.widget.CalendarGroup.CSS_2UPTITLE, "div", this.oDomContainer)[0] || null;
1053
1054 if (titleDiv) {
1055 YAHOO.util.Event.purgeElement(titleDiv);
1056 this.oDomContainer.removeChild(titleDiv);
1057 }
1058 if (! close) {
1059 YAHOO.util.Dom.removeClass(this.oDomContainer, "withtitle");
1060 }
1061 }
1062};
1063
1064/**
1065* Default handler for the "close" property
1066* @method configClose
1067*/
1068YAHOO.widget.Calendar.prototype.configClose = function(type, args, obj) {
1069 var close = args[0];
1070 var title = this.cfg.getProperty("title");
1071
1072 var linkClose;
1073
1074 if (close === true) {
1075 linkClose = YAHOO.util.Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0] || document.createElement("a");
1076 linkClose.href = "javascript:void(null);";
1077 linkClose.className = "link-close";
1078 YAHOO.util.Event.addListener(linkClose, "click", this.hide, this, true);
1079 var imgClose = document.createElement("img");
1080 imgClose.src = YAHOO.widget.Calendar.IMG_ROOT + "us/my/bn/x_d.gif";
1081 imgClose.className = YAHOO.widget.CalendarGroup.CSS_2UPCLOSE;
1082 linkClose.appendChild(imgClose);
1083 this.oDomContainer.appendChild(linkClose);
1084 YAHOO.util.Dom.addClass(this.oDomContainer, "withtitle");
1085 } else {
1086 linkClose = YAHOO.util.Dom.getElementsByClassName("link-close", "a", this.oDomContainer)[0] || null;
1087
1088 if (linkClose) {
1089 YAHOO.util.Event.purgeElement(linkClose);
1090 this.oDomContainer.removeChild(linkClose);
1091 }
1092 if (! title || title === "") {
1093 YAHOO.util.Dom.removeClass(this.oDomContainer, "withtitle");
1094 }
1095 }
1096};
1097
1098/**
1099* Initializes Calendar's built-in CustomEvents
1100* @method initEvents
1101*/
1102YAHOO.widget.Calendar.prototype.initEvents = function() {
1103
1104 /**
1105 * Fired before a selection is made
1106 * @event beforeSelectEvent
1107 */
1108 this.beforeSelectEvent = new YAHOO.util.CustomEvent("beforeSelect");
1109
1110 /**
1111 * Fired when a selection is made
1112 * @event selectEvent
1113 * @param {Array}Array of Date field arrays in the format [YYYY, MM, DD].
1114 */
1115 this.selectEvent = new YAHOO.util.CustomEvent("select");
1116
1117 /**
1118 * Fired before a selection is made
1119 * @event beforeDeselectEvent
1120 */
1121 this.beforeDeselectEvent = new YAHOO.util.CustomEvent("beforeDeselect");
1122
1123 /**
1124 * Fired when a selection is made
1125 * @event deselectEvent
1126 * @param {Array}Array of Date field arrays in the format [YYYY, MM, DD].
1127 */
1128 this.deselectEvent = new YAHOO.util.CustomEvent("deselect");
1129
1130 /**
1131 * Fired when the Calendar page is changed
1132 * @event changePageEvent
1133 */
1134 this.changePageEvent = new YAHOO.util.CustomEvent("changePage");
1135
1136 /**
1137 * Fired before the Calendar is rendered
1138 * @event beforeRenderEvent
1139 */
1140 this.beforeRenderEvent = new YAHOO.util.CustomEvent("beforeRender");
1141
1142 /**
1143 * Fired when the Calendar is rendered
1144 * @event renderEvent
1145 */
1146 this.renderEvent = new YAHOO.util.CustomEvent("render");
1147
1148 /**
1149 * Fired when the Calendar is reset
1150 * @event resetEvent
1151 */
1152 this.resetEvent = new YAHOO.util.CustomEvent("reset");
1153
1154 /**
1155 * Fired when the Calendar is cleared
1156 * @event clearEvent
1157 */
1158 this.clearEvent = new YAHOO.util.CustomEvent("clear");
1159
1160 this.beforeSelectEvent.subscribe(this.onBeforeSelect, this, true);
1161 this.selectEvent.subscribe(this.onSelect, this, true);
1162 this.beforeDeselectEvent.subscribe(this.onBeforeDeselect, this, true);
1163 this.deselectEvent.subscribe(this.onDeselect, this, true);
1164 this.changePageEvent.subscribe(this.onChangePage, this, true);
1165 this.renderEvent.subscribe(this.onRender, this, true);
1166 this.resetEvent.subscribe(this.onReset, this, true);
1167 this.clearEvent.subscribe(this.onClear, this, true);
1168};
1169
1170
1171/**
1172* The default event function that is attached to a date link within a calendar cell
1173* when the calendar is rendered.
1174* @method doSelectCell
1175 * @param {DOMEvent} eThe event
1176 * @param {Calendar} calA reference to the calendar passed by the Event utility
1177*/
1178YAHOO.widget.Calendar.prototype.doSelectCell = function(e, cal) {
1179 var target = YAHOO.util.Event.getTarget(e);
1180
1181 var cell,index,d,date;
1182
1183 while (target.tagName.toLowerCase() != "td" && ! YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1184 target = target.parentNode;
1185 if (target.tagName.toLowerCase() == "html") {
1186 return;
1187 }
1188 }
1189
1190 cell = target;
1191
1192 if (YAHOO.util.Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
1193 index = cell.id.split("cell")[1];
1194 d = cal.cellDates[index];
1195 date = new Date(d[0],d[1]-1,d[2]);
1196
1197 var link;
1198
1199 if (cal.Options.MULTI_SELECT) {
1200 link = cell.getElementsByTagName("a")[0];
1201 if (link) {
1202 link.blur();
1203 }
1204
1205 var cellDate = cal.cellDates[index];
1206 var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
1207
1208 if (cellDateIndex > -1) {
1209 cal.deselectCell(index);
1210 } else {
1211 cal.selectCell(index);
1212 }
1213
1214 } else {
1215 link = cell.getElementsByTagName("a")[0];
1216 if (link) {
1217 link.blur();
1218 }
1219 cal.selectCell(index);
1220 }
1221 }
1222};
1223
1224/**
1225* The event that is executed when the user hovers over a cell
1226* @method doCellMouseOver
1227 * @param {DOMEvent} eThe event
1228 * @param {Calendar} calA reference to the calendar passed by the Event utility
1229*/
1230YAHOO.widget.Calendar.prototype.doCellMouseOver = function(e, cal) {
1231 var target;
1232 if (e) {
1233 target = YAHOO.util.Event.getTarget(e);
1234 } else {
1235 target = this;
1236 }
1237
1238 while (target.tagName.toLowerCase() != "td") {
1239 target = target.parentNode;
1240 if (target.tagName.toLowerCase() == "html") {
1241 return;
1242 }
1243 }
1244
1245 if (YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1246 YAHOO.util.Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
1247 }
1248};
1249
1250/**
1251* The event that is executed when the user moves the mouse out of a cell
1252* @method doCellMouseOut
1253 * @param {DOMEvent} eThe event
1254 * @param {Calendar} calA reference to the calendar passed by the Event utility
1255*/
1256YAHOO.widget.Calendar.prototype.doCellMouseOut = function(e, cal) {
1257 var target;
1258 if (e) {
1259 target = YAHOO.util.Event.getTarget(e);
1260 } else {
1261 target = this;
1262 }
1263
1264 while (target.tagName.toLowerCase() != "td") {
1265 target = target.parentNode;
1266 if (target.tagName.toLowerCase() == "html") {
1267 return;
1268 }
1269 }
1270
1271 if (YAHOO.util.Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
1272 YAHOO.util.Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
1273 }
1274};
1275
1276YAHOO.widget.Calendar.prototype.setupConfig = function() {
1277
1278 /**
1279 * The month/year representing the current visible Calendar date (mm/yyyy)
1280 * @config pagedate
1281 * @type String
1282 * @default today's date
1283 */
1284 this.cfg.addProperty("pagedate", { value:new Date(), handler:this.configPageDate } );
1285
1286 /**
1287 * The date or range of dates representing the current Calendar selection
1288 * @config selected
1289 * @type String
1290 * @default []
1291 */
1292 this.cfg.addProperty("selected", { value:[], handler:this.configSelected } );
1293
1294 /**
1295 * The title to display above the Calendar's month header
1296 * @config title
1297 * @type String
1298 * @default ""
1299 */
1300 this.cfg.addProperty("title", { value:"", handler:this.configTitle } );
1301
1302 /**
1303 * Whether or not a close button should be displayed for this Calendar
1304 * @config close
1305 * @type Boolean
1306 * @default false
1307 */
1308 this.cfg.addProperty("close", { value:false, handler:this.configClose } );
1309
1310 /**
1311 * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below.
1312 * @config iframe
1313 * @type Boolean
1314 * @default true
1315 */
1316 this.cfg.addProperty("iframe", { value:true, handler:this.configIframe, validator:this.cfg.checkBoolean } );
1317
1318 /**
1319 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
1320 * @config mindate
1321 * @type String
1322 * @default null
1323 */
1324 this.cfg.addProperty("mindate", { value:null, handler:this.configMinDate } );
1325
1326 /**
1327 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
1328 * @config maxdate
1329 * @type String
1330 * @default null
1331 */
1332 this.cfg.addProperty("maxdate", { value:null, handler:this.configMaxDate } );
1333
1334
1335 // Options properties
1336
1337 /**
1338 * True if the Calendar should allow multiple selections. False by default.
1339 * @config MULTI_SELECT
1340 * @type Boolean
1341 * @default false
1342 */
1343 this.cfg.addProperty("MULTI_SELECT",{ value:false, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1344
1345 /**
1346 * The weekday the week begins on. Default is 0 (Sunday).
1347 * @config START_WEEKDAY
1348 * @type number
1349 * @default 0
1350 */
1351 this.cfg.addProperty("START_WEEKDAY",{ value:0, handler:this.configOptions, validator:this.cfg.checkNumber } );
1352
1353 /**
1354 * True if the Calendar should show weekday labels. True by default.
1355 * @config SHOW_WEEKDAYS
1356 * @type Boolean
1357 * @default true
1358 */
1359 this.cfg.addProperty("SHOW_WEEKDAYS",{ value:true, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1360
1361 /**
1362 * True if the Calendar should show week row headers. False by default.
1363 * @config SHOW_WEEK_HEADER
1364 * @type Boolean
1365 * @default false
1366 */
1367 this.cfg.addProperty("SHOW_WEEK_HEADER",{ value:false, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1368
1369 /**
1370 * True if the Calendar should show week row footers. False by default.
1371 * @config SHOW_WEEK_FOOTER
1372 * @type Boolean
1373 * @default false
1374 */
1375 this.cfg.addProperty("SHOW_WEEK_FOOTER",{ value:false, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1376
1377 /**
1378 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
1379 * @config HIDE_BLANK_WEEKS
1380 * @type Boolean
1381 * @default false
1382 */
1383 this.cfg.addProperty("HIDE_BLANK_WEEKS",{ value:false, handler:this.configOptions, validator:this.cfg.checkBoolean } );
1384
1385 /**
1386 * The image that should be used for the left navigation arrow.
1387 * @config NAV_ARROW_LEFT
1388 * @type String
1389 * @default YAHOO.widget.Calendar.IMG_ROOT + "us/tr/callt.gif"
1390 */
1391 this.cfg.addProperty("NAV_ARROW_LEFT",{ value:YAHOO.widget.Calendar.IMG_ROOT + "us/tr/callt.gif", handler:this.configOptions } );
1392
1393 /**
1394 * The image that should be used for the left navigation arrow.
1395 * @config NAV_ARROW_RIGHT
1396 * @type String
1397 * @default YAHOO.widget.Calendar.IMG_ROOT + "us/tr/calrt.gif"
1398 */
1399 this.cfg.addProperty("NAV_ARROW_RIGHT",{ value:YAHOO.widget.Calendar.IMG_ROOT + "us/tr/calrt.gif", handler:this.configOptions } );
1400
1401 // Locale properties
1402
1403 /**
1404 * The short month labels for the current locale.
1405 * @config MONTHS_SHORT
1406 * @type String[]
1407 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
1408 */
1409 this.cfg.addProperty("MONTHS_SHORT",{ value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], handler:this.configLocale } );
1410
1411 /**
1412 * The long month labels for the current locale.
1413 * @config MONTHS_LONG
1414 * @type String[]
1415 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
1416 */
1417 this.cfg.addProperty("MONTHS_LONG", { value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], handler:this.configLocale } );
1418
1419 /**
1420 * The 1-character weekday labels for the current locale.
1421 * @config WEEKDAYS_1CHAR
1422 * @type String[]
1423 * @default ["S", "M", "T", "W", "T", "F", "S"]
1424 */
1425 this.cfg.addProperty("WEEKDAYS_1CHAR",{ value:["S", "M", "T", "W", "T", "F", "S"], handler:this.configLocale } );
1426
1427 /**
1428 * The short weekday labels for the current locale.
1429 * @config WEEKDAYS_SHORT
1430 * @type String[]
1431 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
1432 */
1433 this.cfg.addProperty("WEEKDAYS_SHORT",{ value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"], handler:this.configLocale } );
1434
1435 /**
1436 * The medium weekday labels for the current locale.
1437 * @config WEEKDAYS_MEDIUM
1438 * @type String[]
1439 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
1440 */
1441 this.cfg.addProperty("WEEKDAYS_MEDIUM",{ value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], handler:this.configLocale } );
1442
1443 /**
1444 * The long weekday labels for the current locale.
1445 * @config WEEKDAYS_LONG
1446 * @type String[]
1447 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
1448 */
1449 this.cfg.addProperty("WEEKDAYS_LONG",{ value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], handler:this.configLocale } );
1450
1451 /**
1452 * Refreshes the locale values used to build the Calendar.
1453 * @method refreshLocale
1454 * @private
1455 */
1456 var refreshLocale = function() {
1457 this.cfg.refireEvent("LOCALE_MONTHS");
1458 this.cfg.refireEvent("LOCALE_WEEKDAYS");
1459 };
1460
1461 this.cfg.subscribeToConfigEvent("START_WEEKDAY", refreshLocale, this, true);
1462 this.cfg.subscribeToConfigEvent("MONTHS_SHORT", refreshLocale, this, true);
1463 this.cfg.subscribeToConfigEvent("MONTHS_LONG", refreshLocale, this, true);
1464 this.cfg.subscribeToConfigEvent("WEEKDAYS_1CHAR", refreshLocale, this, true);
1465 this.cfg.subscribeToConfigEvent("WEEKDAYS_SHORT", refreshLocale, this, true);
1466 this.cfg.subscribeToConfigEvent("WEEKDAYS_MEDIUM", refreshLocale, this, true);
1467 this.cfg.subscribeToConfigEvent("WEEKDAYS_LONG", refreshLocale, this, true);
1468
1469 /**
1470 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
1471 * @config LOCALE_MONTHS
1472 * @type String
1473 * @default "long"
1474 */
1475 this.cfg.addProperty("LOCALE_MONTHS",{ value:"long", handler:this.configLocaleValues } );
1476
1477 /**
1478 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
1479 * @config LOCALE_WEEKDAYS
1480 * @type String
1481 * @default "short"
1482 */
1483 this.cfg.addProperty("LOCALE_WEEKDAYS",{ value:"short", handler:this.configLocaleValues } );
1484
1485 /**
1486 * The value used to delimit individual dates in a date string passed to various Calendar functions.
1487 * @config DATE_DELIMITER
1488 * @type String
1489 * @default ","
1490 */
1491 this.cfg.addProperty("DATE_DELIMITER", { value:",", handler:this.configLocale } );
1492
1493 /**
1494 * The value used to delimit date fields in a date string passed to various Calendar functions.
1495 * @config DATE_FIELD_DELIMITER
1496 * @type String
1497 * @default "/"
1498 */
1499 this.cfg.addProperty("DATE_FIELD_DELIMITER",{ value:"/", handler:this.configLocale } );
1500
1501 /**
1502 * The value used to delimit date ranges in a date string passed to various Calendar functions.
1503 * @config DATE_RANGE_DELIMITER
1504 * @type String
1505 * @default "-"
1506 */
1507 this.cfg.addProperty("DATE_RANGE_DELIMITER",{ value:"-", handler:this.configLocale } );
1508
1509 /**
1510 * The position of the month in a month/year date string
1511 * @config MY_MONTH_POSITION
1512 * @type Number
1513 * @default 1
1514 */
1515 this.cfg.addProperty("MY_MONTH_POSITION",{ value:1, handler:this.configLocale, validator:this.cfg.checkNumber } );
1516
1517 /**
1518 * The position of the year in a month/year date string
1519 * @config MY_YEAR_POSITION
1520 * @type Number
1521 * @default 2
1522 */
1523 this.cfg.addProperty("MY_YEAR_POSITION",{ value:2, handler:this.configLocale, validator:this.cfg.checkNumber } );
1524
1525 /**
1526 * The position of the month in a month/day date string
1527 * @config MD_MONTH_POSITION
1528 * @type Number
1529 * @default 1
1530 */
1531 this.cfg.addProperty("MD_MONTH_POSITION",{ value:1, handler:this.configLocale, validator:this.cfg.checkNumber } );
1532
1533 /**
1534 * The position of the day in a month/year date string
1535 * @config MD_DAY_POSITION
1536 * @type Number
1537 * @default 2
1538 */
1539 this.cfg.addProperty("MD_DAY_POSITION", { value:2, handler:this.configLocale, validator:this.cfg.checkNumber } );
1540
1541 /**
1542 * The position of the month in a month/day/year date string
1543 * @config MDY_MONTH_POSITION
1544 * @type Number
1545 * @default 1
1546 */
1547 this.cfg.addProperty("MDY_MONTH_POSITION",{ value:1, handler:this.configLocale, validator:this.cfg.checkNumber } );
1548
1549 /**
1550 * The position of the day in a month/day/year date string
1551 * @config MDY_DAY_POSITION
1552 * @type Number
1553 * @default 2
1554 */
1555 this.cfg.addProperty("MDY_DAY_POSITION",{ value:2, handler:this.configLocale, validator:this.cfg.checkNumber } );
1556
1557 /**
1558 * The position of the year in a month/day/year date string
1559 * @config MDY_YEAR_POSITION
1560 * @type Number
1561 * @default 3
1562 */
1563 this.cfg.addProperty("MDY_YEAR_POSITION",{ value:3, handler:this.configLocale, validator:this.cfg.checkNumber } );
1564};
1565
1566/**
1567* The default handler for the "pagedate" property
1568* @method configPageDate
1569*/
1570YAHOO.widget.Calendar.prototype.configPageDate = function(type, args, obj) {
1571 var val = args[0];
1572 var month, year, aMonthYear;
1573
1574 if (val) {
1575 if (val instanceof Date) {
1576 val = YAHOO.widget.DateMath.findMonthStart(val);
1577 this.cfg.setProperty("pagedate", val, true);
1578 if (! this._pageDate) {
1579 this._pageDate = this.cfg.getProperty("pagedate");
1580 }
1581 return;
1582 } else {
1583 aMonthYear = val.split(this.cfg.getProperty("DATE_FIELD_DELIMITER"));
1584 month = parseInt(aMonthYear[this.cfg.getProperty("MY_MONTH_POSITION")-1], 10)-1;
1585 year = parseInt(aMonthYear[this.cfg.getProperty("MY_YEAR_POSITION")-1], 10);
1586 }
1587 } else {
1588 month = this.today.getMonth();
1589 year = this.today.getFullYear();
1590 }
1591
1592 this.cfg.setProperty("pagedate", new Date(year, month, 1), true);
1593 if (! this._pageDate) {
1594 this._pageDate = this.cfg.getProperty("pagedate");
1595 }
1596};
1597
1598/**
1599* The default handler for the "mindate" property
1600* @method configMinDate
1601*/
1602YAHOO.widget.Calendar.prototype.configMinDate = function(type, args, obj) {
1603 var val = args[0];
1604 if (typeof val == 'string') {
1605 val = this._parseDate(val);
1606 this.cfg.setProperty("mindate", new Date(val[0],(val[1]-1),val[2]));
1607 }
1608};
1609
1610/**
1611* The default handler for the "maxdate" property
1612* @method configMaxDate
1613*/
1614YAHOO.widget.Calendar.prototype.configMaxDate = function(type, args, obj) {
1615 var val = args[0];
1616 if (typeof val == 'string') {
1617 val = this._parseDate(val);
1618 this.cfg.setProperty("maxdate", new Date(val[0],(val[1]-1),val[2]));
1619 }
1620};
1621
1622/**
1623* The default handler for the "selected" property
1624* @method configSelected
1625*/
1626YAHOO.widget.Calendar.prototype.configSelected = function(type, args, obj) {
1627 var selected = args[0];
1628
1629 if (selected) {
1630 if (typeof selected == 'string') {
1631 this.cfg.setProperty("selected", this._parseDates(selected), true);
1632 }
1633 }
1634 if (! this._selectedDates) {
1635 this._selectedDates = this.cfg.getProperty("selected");
1636 }
1637};
1638
1639/**
1640* The default handler for all configuration options properties
1641* @method configOptions
1642*/
1643YAHOO.widget.Calendar.prototype.configOptions = function(type, args, obj) {
1644 type = type.toUpperCase();
1645 var val = args[0];
1646 this.Options[type] = val;
1647};
1648
1649/**
1650* The default handler for all configuration locale properties
1651* @method configLocale
1652*/
1653YAHOO.widget.Calendar.prototype.configLocale = function(type, args, obj) {
1654 type = type.toUpperCase();
1655 var val = args[0];
1656 this.Locale[type] = val;
1657
1658 this.cfg.refireEvent("LOCALE_MONTHS");
1659 this.cfg.refireEvent("LOCALE_WEEKDAYS");
1660
1661};
1662
1663/**
1664* The default handler for all configuration locale field length properties
1665* @method configLocaleValues
1666*/
1667YAHOO.widget.Calendar.prototype.configLocaleValues = function(type, args, obj) {
1668 type = type.toUpperCase();
1669 var val = args[0];
1670
1671 switch (type) {
1672 case "LOCALE_MONTHS":
1673 switch (val) {
1674 case "short":
1675 this.Locale.LOCALE_MONTHS = this.cfg.getProperty("MONTHS_SHORT").concat();
1676 break;
1677 case "long":
1678 this.Locale.LOCALE_MONTHS = this.cfg.getProperty("MONTHS_LONG").concat();
1679 break;
1680 }
1681 break;
1682 case "LOCALE_WEEKDAYS":
1683 switch (val) {
1684 case "1char":
1685 this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty("WEEKDAYS_1CHAR").concat();
1686 break;
1687 case "short":
1688 this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty("WEEKDAYS_SHORT").concat();
1689 break;
1690 case "medium":
1691 this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty("WEEKDAYS_MEDIUM").concat();
1692 break;
1693 case "long":
1694 this.Locale.LOCALE_WEEKDAYS = this.cfg.getProperty("WEEKDAYS_LONG").concat();
1695 break;
1696 }
1697
1698 var START_WEEKDAY = this.cfg.getProperty("START_WEEKDAY");
1699
1700 if (START_WEEKDAY > 0) {
1701 for (var w=0;w<START_WEEKDAY;++w) {
1702 this.Locale.LOCALE_WEEKDAYS.push(this.Locale.LOCALE_WEEKDAYS.shift());
1703 }
1704 }
1705 break;
1706 }
1707};
1708
1709/**
1710* Defines the style constants for the Calendar
1711* @method initStyles
1712*/
1713YAHOO.widget.Calendar.prototype.initStyles = function() {
1714
1715 /**
1716 * Collection of Style constants for the Calendar
1717 * @property Style
1718 */
1719 this.Style = {
1720 /**
1721 * @property Style.CSS_ROW_HEADER
1722 */
1723 CSS_ROW_HEADER: "calrowhead",
1724 /**
1725 * @property Style.CSS_ROW_FOOTER
1726 */
1727 CSS_ROW_FOOTER: "calrowfoot",
1728 /**
1729 * @property Style.CSS_CELL
1730 */
1731 CSS_CELL : "calcell",
1732 /**
1733 * @property Style.CSS_CELL_SELECTED
1734 */
1735 CSS_CELL_SELECTED : "selected",
1736 /**
1737 * @property Style.CSS_CELL_SELECTABLE
1738 */
1739 CSS_CELL_SELECTABLE : "selectable",
1740 /**
1741 * @property Style.CSS_CELL_RESTRICTED
1742 */
1743 CSS_CELL_RESTRICTED : "restricted",
1744 /**
1745 * @property Style.CSS_CELL_TODAY
1746 */
1747 CSS_CELL_TODAY : "today",
1748 /**
1749 * @property Style.CSS_CELL_OOM
1750 */
1751 CSS_CELL_OOM : "oom",
1752 /**
1753 * @property Style.CSS_CELL_OOB
1754 */
1755 CSS_CELL_OOB : "previous",
1756 /**
1757 * @property Style.CSS_HEADER
1758 */
1759 CSS_HEADER : "calheader",
1760 /**
1761 * @property Style.CSS_HEADER_TEXT
1762 */
1763 CSS_HEADER_TEXT : "calhead",
1764 /**
1765 * @property Style.CSS_WEEKDAY_CELL
1766 */
1767 CSS_WEEKDAY_CELL : "calweekdaycell",
1768 /**
1769 * @property Style.CSS_WEEKDAY_ROW
1770 */
1771 CSS_WEEKDAY_ROW : "calweekdayrow",
1772 /**
1773 * @property Style.CSS_FOOTER
1774 */
1775 CSS_FOOTER : "calfoot",
1776 /**
1777 * @property Style.CSS_CALENDAR
1778 */
1779 CSS_CALENDAR : "yui-calendar",
1780 /**
1781 * @property Style.CSS_SINGLE
1782 */
1783 CSS_SINGLE : "single",
1784 /**
1785 * @property Style.CSS_CONTAINER
1786 */
1787 CSS_CONTAINER : "yui-calcontainer",
1788 /**
1789 * @property Style.CSS_NAV_LEFT
1790 */
1791 CSS_NAV_LEFT : "calnavleft",
1792 /**
1793 * @property Style.CSS_NAV_RIGHT
1794 */
1795 CSS_NAV_RIGHT : "calnavright",
1796 /**
1797 * @property Style.CSS_CELL_TOP
1798 */
1799 CSS_CELL_TOP : "calcelltop",
1800 /**
1801 * @property Style.CSS_CELL_LEFT
1802 */
1803 CSS_CELL_LEFT : "calcellleft",
1804 /**
1805 * @property Style.CSS_CELL_RIGHT
1806 */
1807 CSS_CELL_RIGHT : "calcellright",
1808 /**
1809 * @property Style.CSS_CELL_BOTTOM
1810 */
1811 CSS_CELL_BOTTOM : "calcellbottom",
1812 /**
1813 * @property Style.CSS_CELL_HOVER
1814 */
1815 CSS_CELL_HOVER : "calcellhover",
1816 /**
1817 * @property Style.CSS_CELL_HIGHLIGHT1
1818 */
1819 CSS_CELL_HIGHLIGHT1 : "highlight1",
1820 /**
1821 * @property Style.CSS_CELL_HIGHLIGHT2
1822 */
1823 CSS_CELL_HIGHLIGHT2 : "highlight2",
1824 /**
1825 * @property Style.CSS_CELL_HIGHLIGHT3
1826 */
1827 CSS_CELL_HIGHLIGHT3 : "highlight3",
1828 /**
1829 * @property Style.CSS_CELL_HIGHLIGHT4
1830 */
1831 CSS_CELL_HIGHLIGHT4 : "highlight4"
1832 };
1833};
1834
1835/**
1836* Builds the date label that will be displayed in the calendar header or
1837* footer, depending on configuration.
1838* @method buildMonthLabel
1839 * @return {String}The formatted calendar month label
1840*/
1841YAHOO.widget.Calendar.prototype.buildMonthLabel = function() {
1842 var text = this.Locale.LOCALE_MONTHS[this.cfg.getProperty("pagedate").getMonth()] + " " + this.cfg.getProperty("pagedate").getFullYear();
1843 return text;
1844};
1845
1846/**
1847* Builds the date digit that will be displayed in calendar cells
1848* @method buildDayLabel
1849 * @param {Date} workingDateThe current working date
1850 * @return {String}The formatted day label
1851*/
1852YAHOO.widget.Calendar.prototype.buildDayLabel = function(workingDate) {
1853 var day = workingDate.getDate();
1854 return day;
1855};
1856
1857/**
1858* Renders the calendar header.
1859* @method renderHeader
1860 * @param {Array} htmlThe current working HTML array
1861* @return {Array} The current working HTML array
1862*/
1863YAHOO.widget.Calendar.prototype.renderHeader = function(html) {
1864 var colSpan = 7;
1865
1866 if (this.cfg.getProperty("SHOW_WEEK_HEADER")) {
1867 colSpan += 1;
1868 }
1869
1870 if (this.cfg.getProperty("SHOW_WEEK_FOOTER")) {
1871 colSpan += 1;
1872 }
1873
1874 html[html.length] = "<thead>";
1875 html[html.length] = "<tr>";
1876 html[html.length] = '<th colspan="' + colSpan + '" class="' + this.Style.CSS_HEADER_TEXT + '">';
1877 html[html.length] = '<div class="' + this.Style.CSS_HEADER + '">';
1878
1879 var renderLeft, renderRight = false;
1880
1881 if (this.parent) {
1882 if (this.index === 0) {
1883 renderLeft = true;
1884 }
1885 if (this.index == (this.parent.cfg.getProperty("pages") -1)) {
1886 renderRight = true;
1887 }
1888 } else {
1889 renderLeft = true;
1890 renderRight = true;
1891 }
1892
1893 var cal = this.parent || this;
1894
1895 if (renderLeft) {
1896 html[html.length] = '<a class="' + this.Style.CSS_NAV_LEFT + '" style="background-image:url(' + this.cfg.getProperty("NAV_ARROW_LEFT") + ')">&#160;</a>';
1897 }
1898
1899 html[html.length] = this.buildMonthLabel();
1900
1901 if (renderRight) {
1902 html[html.length] = '<a class="' + this.Style.CSS_NAV_RIGHT + '" style="background-image:url(' + this.cfg.getProperty("NAV_ARROW_RIGHT") + ')">&#160;</a>';
1903 }
1904
1905
1906 html[html.length] = '</div>';
1907 html[html.length] = '</th>';
1908 html[html.length] = '</tr>';
1909
1910 if (this.cfg.getProperty("SHOW_WEEKDAYS")) {
1911 html = this.buildWeekdays(html);
1912 }
1913
1914 html[html.length] = '</thead>';
1915
1916 return html;
1917};
1918
1919/**
1920* Renders the Calendar's weekday headers.
1921* @method buildWeekdays
1922 * @param {Array} htmlThe current working HTML array
1923* @return {Array} The current working HTML array
1924*/
1925YAHOO.widget.Calendar.prototype.buildWeekdays = function(html) {
1926
1927 html[html.length] = '<tr class="' + this.Style.CSS_WEEKDAY_ROW + '">';
1928
1929 if (this.cfg.getProperty("SHOW_WEEK_HEADER")) {
1930 html[html.length] = '<th>&#160;</th>';
1931 }
1932
1933 for(var i=0;i<this.Locale.LOCALE_WEEKDAYS.length;++i) {
1934 html[html.length] = '<th class="calweekdaycell">' + this.Locale.LOCALE_WEEKDAYS[i] + '</th>';
1935 }
1936
1937 if (this.cfg.getProperty("SHOW_WEEK_FOOTER")) {
1938 html[html.length] = '<th>&#160;</th>';
1939 }
1940
1941 html[html.length] = '</tr>';
1942
1943 return html;
1944};
1945
1946/**
1947* Renders the calendar body.
1948* @method renderBody
1949 * @param {Date} workingDateThe current working Date being used for the render process
1950 * @param {Array} htmlThe current working HTML array
1951* @return {Array} The current working HTML array
1952*/
1953YAHOO.widget.Calendar.prototype.renderBody = function(workingDate, html) {
1954
1955 var startDay = this.cfg.getProperty("START_WEEKDAY");
1956
1957 this.preMonthDays = workingDate.getDay();
1958 if (startDay > 0) {
1959 this.preMonthDays -= startDay;
1960 }
1961 if (this.preMonthDays < 0) {
1962 this.preMonthDays += 7;
1963 }
1964
1965 this.monthDays = YAHOO.widget.DateMath.findMonthEnd(workingDate).getDate();
1966 this.postMonthDays = YAHOO.widget.Calendar.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
1967
1968 workingDate = YAHOO.widget.DateMath.subtract(workingDate, YAHOO.widget.DateMath.DAY, this.preMonthDays);
1969
1970 var useDate,weekNum,weekClass;
1971 useDate = this.cfg.getProperty("pagedate");
1972
1973 html[html.length] = '<tbody class="m' + (useDate.getMonth()+1) + '">';
1974
1975 var i = 0;
1976
1977 var tempDiv = document.createElement("div");
1978 var cell = document.createElement("td");
1979 tempDiv.appendChild(cell);
1980
1981 var jan1 = new Date(useDate.getFullYear(),0,1);
1982
1983 var cal = this.parent || this;
1984
1985 for (var r=0;r<6;r++) {
1986
1987 weekNum = YAHOO.widget.DateMath.getWeekNumber(workingDate, useDate.getFullYear(), startDay);
1988
1989 weekClass = "w" + weekNum;
1990
1991 if (r !== 0 && this.isDateOOM(workingDate) && this.cfg.getProperty("HIDE_BLANK_WEEKS") === true) {
1992 break;
1993 } else {
1994
1995 html[html.length] = '<tr class="' + weekClass + '">';
1996
1997 if (this.cfg.getProperty("SHOW_WEEK_HEADER")) { html = this.renderRowHeader(weekNum, html); }
1998
1999 for (var d=0;d<7;d++){ // Render actual days
2000
2001 var cellRenderers = [];
2002
2003 this.clearElement(cell);
2004
2005 YAHOO.util.Dom.addClass(cell, "calcell");
2006
2007 cell.id = this.id + "_cell" + i;
2008
2009 cell.innerHTML = i;
2010
2011 var renderer = null;
2012
2013 if (workingDate.getFullYear()== this.today.getFullYear() &&
2014 workingDate.getMonth() == this.today.getMonth() &&
2015 workingDate.getDate() == this.today.getDate()) {
2016 cellRenderers[cellRenderers.length]=cal.renderCellStyleToday;
2017 }
2018
2019 this.cellDates[this.cellDates.length]=[workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]; // Add this date to cellDates
2020
2021 if (this.isDateOOM(workingDate)) {
2022 cellRenderers[cellRenderers.length]=cal.renderCellNotThisMonth;
2023 } else {
2024
2025 YAHOO.util.Dom.addClass(cell, "wd" + workingDate.getDay());
2026 YAHOO.util.Dom.addClass(cell, "d" + workingDate.getDate());
2027
2028 for (var s=0;s<this.renderStack.length;++s) {
2029
2030 var rArray = this.renderStack[s];
2031 var type = rArray[0];
2032
2033 var month;
2034 var day;
2035 var year;
2036
2037 switch (type) {
2038 case YAHOO.widget.Calendar.DATE:
2039 month = rArray[1][1];
2040 day = rArray[1][2];
2041 year = rArray[1][0];
2042
2043 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year) {
2044 renderer = rArray[2];
2045 this.renderStack.splice(s,1);
2046 }
2047 break;
2048 case YAHOO.widget.Calendar.MONTH_DAY:
2049 month = rArray[1][0];
2050 day = rArray[1][1];
2051
2052 if (workingDate.getMonth()+1 == month && workingDate.getDate() == day) {
2053 renderer = rArray[2];
2054 this.renderStack.splice(s,1);
2055 }
2056 break;
2057 case YAHOO.widget.Calendar.RANGE:
2058 var date1 = rArray[1][0];
2059 var date2 = rArray[1][1];
2060
2061 var d1month = date1[1];
2062 var d1day = date1[2];
2063 var d1year = date1[0];
2064
2065 var d1 = new Date(d1year, d1month-1, d1day);
2066
2067 var d2month = date2[1];
2068 var d2day = date2[2];
2069 var d2year = date2[0];
2070
2071 var d2 = new Date(d2year, d2month-1, d2day);
2072
2073 if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime()) {
2074 renderer = rArray[2];
2075
2076 if (workingDate.getTime()==d2.getTime()) {
2077 this.renderStack.splice(s,1);
2078 }
2079 }
2080 break;
2081 case YAHOO.widget.Calendar.WEEKDAY:
2082
2083 var weekday = rArray[1][0];
2084 if (workingDate.getDay()+1 == weekday) {
2085 renderer = rArray[2];
2086 }
2087 break;
2088 case YAHOO.widget.Calendar.MONTH:
2089
2090 month = rArray[1][0];
2091 if (workingDate.getMonth()+1 == month) {
2092 renderer = rArray[2];
2093 }
2094 break;
2095 }
2096
2097 if (renderer) {
2098 cellRenderers[cellRenderers.length]=renderer;
2099 }
2100 }
2101
2102 }
2103
2104 if (this._indexOfSelectedFieldArray([workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]) > -1) {
2105 cellRenderers[cellRenderers.length]=cal.renderCellStyleSelected;
2106 }
2107
2108 var mindate = this.cfg.getProperty("mindate");
2109 var maxdate = this.cfg.getProperty("maxdate");
2110
2111 if (mindate) {
2112 mindate = YAHOO.widget.DateMath.clearTime(mindate);
2113 }
2114 if (maxdate) {
2115 maxdate = YAHOO.widget.DateMath.clearTime(maxdate);
2116 }
2117
2118 if (
2119 (mindate && (workingDate.getTime() < mindate.getTime())) ||
2120 (maxdate && (workingDate.getTime() > maxdate.getTime()))
2121 ) {
2122 cellRenderers[cellRenderers.length]=cal.renderOutOfBoundsDate;
2123 } else {
2124 cellRenderers[cellRenderers.length]=cal.styleCellDefault;
2125 cellRenderers[cellRenderers.length]=cal.renderCellDefault;
2126 }
2127
2128
2129
2130 for (var x=0;x<cellRenderers.length;++x) {
2131 var ren = cellRenderers[x];
2132 if (ren.call((this.parent || this),workingDate,cell) == YAHOO.widget.Calendar.STOP_RENDER) {
2133 break;
2134 }
2135 }
2136
2137 workingDate.setTime(workingDate.getTime() + YAHOO.widget.DateMath.ONE_DAY_MS);
2138
2139 if (i >= 0 && i <= 6) {
2140 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TOP);
2141 }
2142 if ((i % 7) === 0) {
2143 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_LEFT);
2144 }
2145 if (((i+1) % 7) === 0) {
2146 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_RIGHT);
2147 }
2148
2149 var postDays = this.postMonthDays;
2150 if (postDays >= 7 && this.cfg.getProperty("HIDE_BLANK_WEEKS")) {
2151 var blankWeeks = Math.floor(postDays/7);
2152 for (var p=0;p<blankWeeks;++p) {
2153 postDays -= 7;
2154 }
2155 }
2156
2157 if (i >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
2158 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_BOTTOM);
2159 }
2160
2161 html[html.length] = tempDiv.innerHTML;
2162
2163 i++;
2164 }
2165
2166 if (this.cfg.getProperty("SHOW_WEEK_FOOTER")) { html = this.renderRowFooter(weekNum, html); }
2167
2168 html[html.length] = '</tr>';
2169 }
2170 }
2171
2172 html[html.length] = '</tbody>';
2173
2174 return html;
2175};
2176
2177/**
2178* Renders the calendar footer. In the default implementation, there is
2179* no footer.
2180* @method renderFooter
2181 * @param {Array} htmlThe current working HTML array
2182* @return {Array} The current working HTML array
2183*/
2184YAHOO.widget.Calendar.prototype.renderFooter = function(html) { return html; };
2185
2186/**
2187* Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
2188* when the method is called: renderHeader, renderBody, renderFooter.
2189* Refer to the documentation for those methods for information on
2190* individual render tasks.
2191* @method render
2192*/
2193YAHOO.widget.Calendar.prototype.render = function() {
2194 this.beforeRenderEvent.fire();
2195
2196 // Find starting day of the current month
2197 var workingDate = YAHOO.widget.DateMath.findMonthStart(this.cfg.getProperty("pagedate"));
2198
2199 this.resetRenderers();
2200 this.cellDates.length = 0;
2201
2202 YAHOO.util.Event.purgeElement(this.oDomContainer, true);
2203
2204 var html = [];
2205
2206 html[html.length] = '<table cellSpacing="0" class="' + this.Style.CSS_CALENDAR + ' y' + workingDate.getFullYear() + '" id="' + this.id + '">';
2207 html = this.renderHeader(html);
2208 html = this.renderBody(workingDate, html);
2209 html = this.renderFooter(html);
2210 html[html.length] = '</table>';
2211
2212 this.oDomContainer.innerHTML = html.join("\n");
2213
2214 this.applyListeners();
2215 this.cells = this.oDomContainer.getElementsByTagName("td");
2216
2217 this.cfg.refireEvent("title");
2218 this.cfg.refireEvent("close");
2219 this.cfg.refireEvent("iframe");
2220
2221 this.renderEvent.fire();
2222};
2223
2224/**
2225* Applies the Calendar's DOM listeners to applicable elements.
2226* @method applyListeners
2227*/
2228YAHOO.widget.Calendar.prototype.applyListeners = function() {
2229
2230 var root = this.oDomContainer;
2231 var cal = this.parent || this;
2232
2233 var linkLeft, linkRight;
2234
2235 linkLeft = YAHOO.util.Dom.getElementsByClassName(this.Style.CSS_NAV_LEFT, "a", root);
2236 linkRight = YAHOO.util.Dom.getElementsByClassName(this.Style.CSS_NAV_RIGHT, "a", root);
2237
2238 if (linkLeft) {
2239 this.linkLeft = linkLeft[0];
2240 YAHOO.util.Event.addListener(this.linkLeft, "mousedown", cal.previousMonth, cal, true);
2241 }
2242
2243 if (linkRight) {
2244 this.linkRight = linkRight[0];
2245 YAHOO.util.Event.addListener(this.linkRight, "mousedown", cal.nextMonth, cal, true);
2246 }
2247
2248 if (this.domEventMap) {
2249 var el,elements;
2250 for (var cls in this.domEventMap) {
2251 if (this.domEventMap.hasOwnProperty(cls)) {
2252 var items = this.domEventMap[cls];
2253
2254 if (! (items instanceof Array)) {
2255 items = [items];
2256 }
2257
2258 for (var i=0;i<items.length;i++){
2259 var item = items[i];
2260 elements = YAHOO.util.Dom.getElementsByClassName(cls, item.tag, this.oDomContainer);
2261
2262 for (var c=0;c<elements.length;c++) {
2263 el = elements[c];
2264 YAHOO.util.Event.addListener(el, item.event, item.handler, item.scope, item.correct );
2265 }
2266 }
2267 }
2268 }
2269 }
2270
2271 YAHOO.util.Event.addListener(this.oDomContainer, "click", this.doSelectCell, this);
2272 YAHOO.util.Event.addListener(this.oDomContainer, "mouseover", this.doCellMouseOver, this);
2273 YAHOO.util.Event.addListener(this.oDomContainer, "mouseout", this.doCellMouseOut, this);
2274};
2275
2276/**
2277* Retrieves the Date object for the specified Calendar cell
2278* @method getDateByCellId
2279 * @param {String} idThe id of the cell
2280* @return {Date} The Date object for the specified Calendar cell
2281*/
2282YAHOO.widget.Calendar.prototype.getDateByCellId = function(id) {
2283 var date = this.getDateFieldsByCellId(id);
2284 return new Date(date[0],date[1]-1,date[2]);
2285};
2286
2287/**
2288* Retrieves the Date object for the specified Calendar cell
2289* @method getDateFieldsByCellId
2290 * @param {String} idThe id of the cell
2291 * @return {Array}The array of Date fields for the specified Calendar cell
2292*/
2293YAHOO.widget.Calendar.prototype.getDateFieldsByCellId = function(id) {
2294 id = id.toLowerCase().split("_cell")[1];
2295 id = parseInt(id, 10);
2296 return this.cellDates[id];
2297};
2298
2299// BEGIN BUILT-IN TABLE CELL RENDERERS
2300
2301/**
2302* Renders a cell that falls before the minimum date or after the maximum date.
2303* widget class.
2304* @method renderOutOfBoundsDate
2305 * @param {Date} workingDate The current working Date object being used to generate the calendar
2306 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2307* @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2308 * should not be terminated
2309*/
2310YAHOO.widget.Calendar.prototype.renderOutOfBoundsDate = function(workingDate, cell) {
2311 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOB);
2312 cell.innerHTML = workingDate.getDate();
2313 return YAHOO.widget.Calendar.STOP_RENDER;
2314};
2315
2316/**
2317* Renders the row header for a week.
2318* @method renderRowHeader
2319 * @param {Number} weekNumThe week number of the current row
2320 * @param {Array} cellThe current working HTML array
2321*/
2322YAHOO.widget.Calendar.prototype.renderRowHeader = function(weekNum, html) {
2323 html[html.length] = '<th class="calrowhead">' + weekNum + '</th>';
2324 return html;
2325};
2326
2327/**
2328* Renders the row footer for a week.
2329* @method renderRowFooter
2330 * @param {Number} weekNumThe week number of the current row
2331 * @param {Array} cellThe current working HTML array
2332*/
2333YAHOO.widget.Calendar.prototype.renderRowFooter = function(weekNum, html) {
2334 html[html.length] = '<th class="calrowfoot">' + weekNum + '</th>';
2335 return html;
2336};
2337
2338/**
2339* Renders a single standard calendar cell in the calendar widget table.
2340* All logic for determining how a standard default cell will be rendered is
2341* encapsulated in this method, and must be accounted for when extending the
2342* widget class.
2343* @method renderCellDefault
2344 * @param {Date} workingDate The current working Date object being used to generate the calendar
2345 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2346*/
2347YAHOO.widget.Calendar.prototype.renderCellDefault = function(workingDate, cell) {
2348 cell.innerHTML = '<a href="javascript:void(null);" >' + this.buildDayLabel(workingDate) + "</a>";
2349};
2350
2351/**
2352* Styles a selectable cell.
2353* @method styleCellDefault
2354 * @param {Date} workingDate The current working Date object being used to generate the calendar
2355 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2356*/
2357YAHOO.widget.Calendar.prototype.styleCellDefault = function(workingDate, cell) {
2358 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_SELECTABLE);
2359};
2360
2361
2362/**
2363* Renders a single standard calendar cell using the CSS hightlight1 style
2364* @method renderCellStyleHighlight1
2365 * @param {Date} workingDate The current working Date object being used to generate the calendar
2366 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2367*/
2368YAHOO.widget.Calendar.prototype.renderCellStyleHighlight1 = function(workingDate, cell) {
2369 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
2370};
2371
2372/**
2373* Renders a single standard calendar cell using the CSS hightlight2 style
2374* @method renderCellStyleHighlight2
2375 * @param {Date} workingDate The current working Date object being used to generate the calendar
2376 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2377*/
2378YAHOO.widget.Calendar.prototype.renderCellStyleHighlight2 = function(workingDate, cell) {
2379 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
2380};
2381
2382/**
2383* Renders a single standard calendar cell using the CSS hightlight3 style
2384* @method renderCellStyleHighlight3
2385 * @param {Date} workingDate The current working Date object being used to generate the calendar
2386 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2387*/
2388YAHOO.widget.Calendar.prototype.renderCellStyleHighlight3 = function(workingDate, cell) {
2389 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
2390};
2391
2392/**
2393* Renders a single standard calendar cell using the CSS hightlight4 style
2394* @method renderCellStyleHighlight4
2395 * @param {Date} workingDate The current working Date object being used to generate the calendar
2396 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2397*/
2398YAHOO.widget.Calendar.prototype.renderCellStyleHighlight4 = function(workingDate, cell) {
2399 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
2400};
2401
2402/**
2403* Applies the default style used for rendering today's date to the current calendar cell
2404* @method renderCellStyleToday
2405 * @param {Date} workingDate The current working Date object being used to generate the calendar
2406 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2407*/
2408YAHOO.widget.Calendar.prototype.renderCellStyleToday = function(workingDate, cell) {
2409 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_TODAY);
2410};
2411
2412/**
2413* Applies the default style used for rendering selected dates to the current calendar cell
2414* @method renderCellStyleSelected
2415 * @param {Date} workingDate The current working Date object being used to generate the calendar
2416 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2417* @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2418 * should not be terminated
2419*/
2420YAHOO.widget.Calendar.prototype.renderCellStyleSelected = function(workingDate, cell) {
2421 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_SELECTED);
2422};
2423
2424/**
2425* Applies the default style used for rendering dates that are not a part of the current
2426* month (preceding or trailing the cells for the current month)
2427* @method renderCellNotThisMonth
2428 * @param {Date} workingDate The current working Date object being used to generate the calendar
2429 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2430* @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2431 * should not be terminated
2432*/
2433YAHOO.widget.Calendar.prototype.renderCellNotThisMonth = function(workingDate, cell) {
2434 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_OOM);
2435 cell.innerHTML=workingDate.getDate();
2436 return YAHOO.widget.Calendar.STOP_RENDER;
2437};
2438
2439/**
2440* Renders the current calendar cell as a non-selectable "black-out" date using the default
2441* restricted style.
2442* @method renderBodyCellRestricted
2443 * @param {Date} workingDate The current working Date object being used to generate the calendar
2444 * @param {HTMLTableCellElement} cell The current working cell in the calendar
2445* @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
2446 * should not be terminated
2447*/
2448YAHOO.widget.Calendar.prototype.renderBodyCellRestricted = function(workingDate, cell) {
2449 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL);
2450 YAHOO.util.Dom.addClass(cell, this.Style.CSS_CELL_RESTRICTED);
2451 cell.innerHTML=workingDate.getDate();
2452 return YAHOO.widget.Calendar.STOP_RENDER;
2453};
2454
2455// END BUILT-IN TABLE CELL RENDERERS
2456
2457// BEGIN MONTH NAVIGATION METHODS
2458
2459/**
2460* Adds the designated number of months to the current calendar month, and sets the current
2461* calendar page date to the new month.
2462* @method addMonths
2463 * @param {Number} countThe number of months to add to the current calendar
2464*/
2465YAHOO.widget.Calendar.prototype.addMonths = function(count) {
2466 this.cfg.setProperty("pagedate", YAHOO.widget.DateMath.add(this.cfg.getProperty("pagedate"), YAHOO.widget.DateMath.MONTH, count));
2467 this.resetRenderers();
2468 this.changePageEvent.fire();
2469};
2470
2471/**
2472* Subtracts the designated number of months from the current calendar month, and sets the current
2473* calendar page date to the new month.
2474* @method subtractMonths
2475 * @param {Number} countThe number of months to subtract from the current calendar
2476*/
2477YAHOO.widget.Calendar.prototype.subtractMonths = function(count) {
2478 this.cfg.setProperty("pagedate", YAHOO.widget.DateMath.subtract(this.cfg.getProperty("pagedate"), YAHOO.widget.DateMath.MONTH, count));
2479 this.resetRenderers();
2480 this.changePageEvent.fire();
2481};
2482
2483/**
2484* Adds the designated number of years to the current calendar, and sets the current
2485* calendar page date to the new month.
2486* @method addYears
2487 * @param {Number} countThe number of years to add to the current calendar
2488*/
2489YAHOO.widget.Calendar.prototype.addYears = function(count) {
2490 this.cfg.setProperty("pagedate", YAHOO.widget.DateMath.add(this.cfg.getProperty("pagedate"), YAHOO.widget.DateMath.YEAR, count));
2491 this.resetRenderers();
2492 this.changePageEvent.fire();
2493};
2494
2495/**
2496* Subtcats the designated number of years from the current calendar, and sets the current
2497* calendar page date to the new month.
2498* @method subtractYears
2499 * @param {Number} countThe number of years to subtract from the current calendar
2500*/
2501YAHOO.widget.Calendar.prototype.subtractYears = function(count) {
2502 this.cfg.setProperty("pagedate", YAHOO.widget.DateMath.subtract(this.cfg.getProperty("pagedate"), YAHOO.widget.DateMath.YEAR, count));
2503 this.resetRenderers();
2504 this.changePageEvent.fire();
2505};
2506
2507/**
2508* Navigates to the next month page in the calendar widget.
2509* @method nextMonth
2510*/
2511YAHOO.widget.Calendar.prototype.nextMonth = function() {
2512 this.addMonths(1);
2513};
2514
2515/**
2516* Navigates to the previous month page in the calendar widget.
2517* @method previousMonth
2518*/
2519YAHOO.widget.Calendar.prototype.previousMonth = function() {
2520 this.subtractMonths(1);
2521};
2522
2523/**
2524* Navigates to the next year in the currently selected month in the calendar widget.
2525* @method nextYear
2526*/
2527YAHOO.widget.Calendar.prototype.nextYear = function() {
2528 this.addYears(1);
2529};
2530
2531/**
2532* Navigates to the previous year in the currently selected month in the calendar widget.
2533* @method previousYear
2534*/
2535YAHOO.widget.Calendar.prototype.previousYear = function() {
2536 this.subtractYears(1);
2537};
2538
2539// END MONTH NAVIGATION METHODS
2540
2541// BEGIN SELECTION METHODS
2542
2543/**
2544* Resets the calendar widget to the originally selected month and year, and
2545* sets the calendar to the initial selection(s).
2546* @method reset
2547*/
2548YAHOO.widget.Calendar.prototype.reset = function() {
2549 this.cfg.resetProperty("selected");
2550 this.cfg.resetProperty("pagedate");
2551 this.resetEvent.fire();
2552};
2553
2554/**
2555* Clears the selected dates in the current calendar widget and sets the calendar
2556* to the current month and year.
2557* @method clear
2558*/
2559YAHOO.widget.Calendar.prototype.clear = function() {
2560 this.cfg.setProperty("selected", []);
2561 this.cfg.setProperty("pagedate", new Date(this.today.getTime()));
2562 this.clearEvent.fire();
2563};
2564
2565/**
2566* Selects a date or a collection of dates on the current calendar. This method, by default,
2567* does not call the render method explicitly. Once selection has completed, render must be
2568* called for the changes to be reflected visually.
2569* @method select
2570 * @param {String/Date/Date[]} dateThe date string of dates to select in the current calendar. Valid formats are
2571 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
2572 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
2573 * This method can also take a JavaScript Date object or an array of Date objects.
2574 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
2575*/
2576YAHOO.widget.Calendar.prototype.select = function(date) {
2577 this.beforeSelectEvent.fire();
2578
2579 var selected = this.cfg.getProperty("selected");
2580 var aToBeSelected = this._toFieldArray(date);
2581
2582 for (var a=0;a<aToBeSelected.length;++a) {
2583 var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
2584 if (this._indexOfSelectedFieldArray(toSelect) == -1) { // not already selected?
2585 selected[selected.length]=toSelect;
2586 }
2587 }
2588
2589 if (this.parent) {
2590 this.parent.cfg.setProperty("selected", selected);
2591 } else {
2592 this.cfg.setProperty("selected", selected);
2593 }
2594
2595 this.selectEvent.fire(aToBeSelected);
2596
2597 return this.getSelectedDates();
2598};
2599
2600/**
2601* Selects a date on the current calendar by referencing the index of the cell that should be selected.
2602* This method is used to easily select a single cell (usually with a mouse click) without having to do
2603* a full render. The selected style is applied to the cell directly.
2604* @method selectCell
2605 * @param {Number} cellIndexThe index of the cell to select in the current calendar.
2606 * @return {Date[]}Array of JavaScript Date objects representing all individual dates that are currently selected.
2607*/
2608YAHOO.widget.Calendar.prototype.selectCell = function(cellIndex) {
2609 this.beforeSelectEvent.fire();
2610
2611 var selected = this.cfg.getProperty("selected");
2612
2613 var cell = this.cells[cellIndex];
2614 var cellDate = this.cellDates[cellIndex];
2615
2616 var dCellDate = this._toDate(cellDate);
2617
2618 var selectDate = cellDate.concat();
2619
2620 selected[selected.length] = selectDate;
2621
2622 if (this.parent) {
2623 this.parent.cfg.setProperty("selected", selected);
2624 } else {
2625 this.cfg.setProperty("selected", selected);
2626 }
2627
2628 this.renderCellStyleSelected(dCellDate,cell);
2629
2630 this.selectEvent.fire([selectDate]);
2631
2632 this.doCellMouseOut.call(cell, null, this);
2633
2634 return this.getSelectedDates();
2635};
2636
2637/**
2638* Deselects a date or a collection of dates on the current calendar. This method, by default,
2639* does not call the render method explicitly. Once deselection has completed, render must be
2640* called for the changes to be reflected visually.
2641* @method deselect
2642 * @param {String/Date/Date[]} dateThe date string of dates to deselect in the current calendar. Valid formats are
2643 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
2644 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
2645 * This method can also take a JavaScript Date object or an array of Date objects.
2646 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
2647*/
2648YAHOO.widget.Calendar.prototype.deselect = function(date) {
2649 this.beforeDeselectEvent.fire();
2650
2651 var selected = this.cfg.getProperty("selected");
2652
2653 var aToBeSelected = this._toFieldArray(date);
2654
2655 for (var a=0;a<aToBeSelected.length;++a) {
2656 var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
2657 var index = this._indexOfSelectedFieldArray(toSelect);
2658
2659 if (index != -1) {
2660 selected.splice(index,1);
2661 }
2662 }
2663
2664 if (this.parent) {
2665 this.parent.cfg.setProperty("selected", selected);
2666 } else {
2667 this.cfg.setProperty("selected", selected);
2668 }
2669
2670 this.deselectEvent.fire(aToBeSelected);
2671
2672 return this.getSelectedDates();
2673};
2674
2675/**
2676* Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
2677* This method is used to easily deselect a single cell (usually with a mouse click) without having to do
2678* a full render. The selected style is removed from the cell directly.
2679* @method deselectCell
2680 * @param {Number} cellIndexThe index of the cell to deselect in the current calendar.
2681 * @return {Date[]}Array of JavaScript Date objects representing all individual dates that are currently selected.
2682*/
2683YAHOO.widget.Calendar.prototype.deselectCell = function(i) {
2684 this.beforeDeselectEvent.fire();
2685
2686 var selected = this.cfg.getProperty("selected");
2687
2688 var cell = this.cells[i];
2689 var cellDate = this.cellDates[i];
2690 var cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
2691
2692 var dCellDate = this._toDate(cellDate);
2693
2694 var selectDate = cellDate.concat();
2695
2696 if (cellDateIndex > -1) {
2697 if (this.cfg.getProperty("pagedate").getMonth() == dCellDate.getMonth() &&
2698 this.cfg.getProperty("pagedate").getFullYear() == dCellDate.getFullYear()) {
2699 YAHOO.util.Dom.removeClass(cell, this.Style.CSS_CELL_SELECTED);
2700 }
2701
2702 selected.splice(cellDateIndex, 1);
2703 }
2704
2705
2706 if (this.parent) {
2707 this.parent.cfg.setProperty("selected", selected);
2708 } else {
2709 this.cfg.setProperty("selected", selected);
2710 }
2711
2712 this.deselectEvent.fire(selectDate);
2713 return this.getSelectedDates();
2714};
2715
2716/**
2717* Deselects all dates on the current calendar.
2718* @method deselectAll
2719 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
2720 * Assuming that this function executes properly, the return value should be an empty array.
2721 * However, the empty array is returned for the sake of being able to check the selection status
2722 * of the calendar.
2723*/
2724YAHOO.widget.Calendar.prototype.deselectAll = function() {
2725 this.beforeDeselectEvent.fire();
2726
2727 var selected = this.cfg.getProperty("selected");
2728 var count = selected.length;
2729 var sel = selected.concat();
2730
2731 if (this.parent) {
2732 this.parent.cfg.setProperty("selected", []);
2733 } else {
2734 this.cfg.setProperty("selected", []);
2735 }
2736
2737 if (count > 0) {
2738 this.deselectEvent.fire(sel);
2739 }
2740
2741 return this.getSelectedDates();
2742};
2743
2744// END SELECTION METHODS
2745
2746// BEGIN TYPE CONVERSION METHODS
2747
2748/**
2749* Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
2750* used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
2751* @method _toFieldArray
2752* @private
2753 * @param {String/Date/Date[]} dateThe date string of dates to deselect in the current calendar. Valid formats are
2754 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
2755 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
2756 * This method can also take a JavaScript Date object or an array of Date objects.
2757 * @return {Array[](Number[])}Array of date field arrays
2758*/
2759YAHOO.widget.Calendar.prototype._toFieldArray = function(date) {
2760 var returnDate = [];
2761
2762 if (date instanceof Date) {
2763 returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
2764 } else if (typeof date == 'string') {
2765 returnDate = this._parseDates(date);
2766 } else if (date instanceof Array) {
2767 for (var i=0;i<date.length;++i) {
2768 var d = date[i];
2769 returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
2770 }
2771 }
2772
2773 return returnDate;
2774};
2775
2776/**
2777* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
2778* @method _toDate
2779* @private
2780 * @param {Number[]} dateFieldArrayThe date field array to convert to a JavaScript Date.
2781 * @return {Date}JavaScript Date object representing the date field array
2782*/
2783YAHOO.widget.Calendar.prototype._toDate = function(dateFieldArray) {
2784 if (dateFieldArray instanceof Date) {
2785 return dateFieldArray;
2786 } else {
2787 return new Date(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
2788 }
2789};
2790
2791// END TYPE CONVERSION METHODS
2792
2793// BEGIN UTILITY METHODS
2794
2795/**
2796* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
2797* @method _fieldArraysAreEqual
2798* @private
2799 * @param {Number[]} array1The first date field array to compare
2800 * @param {Number[]} array2The first date field array to compare
2801 * @return {Boolean}The boolean that represents the equality of the two arrays
2802*/
2803YAHOO.widget.Calendar.prototype._fieldArraysAreEqual = function(array1, array2) {
2804 var match = false;
2805
2806 if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2]) {
2807 match=true;
2808 }
2809
2810 return match;
2811};
2812
2813/**
2814* Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
2815 * @method_indexOfSelectedFieldArray
2816* @private
2817 * @param {Number[]} findThe date field array to search for
2818 * @return {Number} The index of the date field array within the collection of selected dates.
2819 * -1 will be returned if the date is not found.
2820*/
2821YAHOO.widget.Calendar.prototype._indexOfSelectedFieldArray = function(find) {
2822 var selected = -1;
2823 var seldates = this.cfg.getProperty("selected");
2824
2825 for (var s=0;s<seldates.length;++s) {
2826 var sArray = seldates[s];
2827 if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2]) {
2828 selected = s;
2829 break;
2830 }
2831 }
2832
2833 return selected;
2834};
2835
2836/**
2837* Determines whether a given date is OOM (out of month).
2838 * @methodisDateOOM
2839 * @param {Date} dateThe JavaScript Date object for which to check the OOM status
2840 * @return {Boolean}true if the date is OOM
2841*/
2842YAHOO.widget.Calendar.prototype.isDateOOM = function(date) {
2843 var isOOM = false;
2844 if (date.getMonth() != this.cfg.getProperty("pagedate").getMonth()) {
2845 isOOM = true;
2846 }
2847 return isOOM;
2848};
2849
2850// END UTILITY METHODS
2851
2852// BEGIN EVENT HANDLERS
2853
2854/**
2855* Event executed before a date is selected in the calendar widget.
2856* @deprecated Event handlers for this event should be susbcribed to beforeSelectEvent.
2857*/
2858YAHOO.widget.Calendar.prototype.onBeforeSelect = function() {
2859 if (this.cfg.getProperty("MULTI_SELECT") === false) {
2860 if (this.parent) {
2861 this.parent.callChildFunction("clearAllBodyCellStyles", this.Style.CSS_CELL_SELECTED);
2862 this.parent.deselectAll();
2863 } else {
2864 this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
2865 this.deselectAll();
2866 }
2867 }
2868};
2869
2870/**
2871* Event executed when a date is selected in the calendar widget.
2872 * @param {Array} selectedAn array of date field arrays representing which date or dates were selected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
2873* @deprecated Event handlers for this event should be susbcribed to selectEvent.
2874*/
2875YAHOO.widget.Calendar.prototype.onSelect = function(selected) { };
2876
2877/**
2878* Event executed before a date is deselected in the calendar widget.
2879* @deprecated Event handlers for this event should be susbcribed to beforeDeselectEvent.
2880*/
2881YAHOO.widget.Calendar.prototype.onBeforeDeselect = function() { };
2882
2883/**
2884* Event executed when a date is deselected in the calendar widget.
2885 * @param {Array} selectedAn array of date field arrays representing which date or dates were deselected. Example: [ [2006,8,6],[2006,8,7],[2006,8,8] ]
2886* @deprecated Event handlers for this event should be susbcribed to deselectEvent.
2887*/
2888YAHOO.widget.Calendar.prototype.onDeselect = function(deselected) { };
2889
2890/**
2891* Event executed when the user navigates to a different calendar page.
2892* @deprecated Event handlers for this event should be susbcribed to changePageEvent.
2893*/
2894YAHOO.widget.Calendar.prototype.onChangePage = function() {
2895 this.render();
2896};
2897
2898/**
2899* Event executed when the calendar widget is rendered.
2900* @deprecated Event handlers for this event should be susbcribed to renderEvent.
2901*/
2902YAHOO.widget.Calendar.prototype.onRender = function() { };
2903
2904/**
2905* Event executed when the calendar widget is reset to its original state.
2906* @deprecated Event handlers for this event should be susbcribed to resetEvemt.
2907*/
2908YAHOO.widget.Calendar.prototype.onReset = function() { this.render(); };
2909
2910/**
2911* Event executed when the calendar widget is completely cleared to the current month with no selections.
2912* @deprecated Event handlers for this event should be susbcribed to clearEvent.
2913*/
2914YAHOO.widget.Calendar.prototype.onClear = function() { this.render(); };
2915
2916/**
2917* Validates the calendar widget. This method has no default implementation
2918* and must be extended by subclassing the widget.
2919 * @returnShould return true if the widget validates, and false if
2920* it doesn't.
2921* @type Boolean
2922*/
2923YAHOO.widget.Calendar.prototype.validate = function() { return true; };
2924
2925// END EVENT HANDLERS
2926
2927// BEGIN DATE PARSE METHODS
2928
2929/**
2930* Converts a date string to a date field array
2931* @private
2932 * @param {String} sDate Date string. Valid formats are mm/dd and mm/dd/yyyy.
2933 * @return A date field array representing the string passed to the method
2934* @type Array[](Number[])
2935*/
2936YAHOO.widget.Calendar.prototype._parseDate = function(sDate) {
2937 var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER);
2938 var rArray;
2939
2940 if (aDate.length == 2) {
2941 rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
2942 rArray.type = YAHOO.widget.Calendar.MONTH_DAY;
2943 } else {
2944 rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1],aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
2945 rArray.type = YAHOO.widget.Calendar.DATE;
2946 }
2947
2948 for (var i=0;i<rArray.length;i++) {
2949 rArray[i] = parseInt(rArray[i], 10);
2950 }
2951
2952 return rArray;
2953};
2954
2955/**
2956* Converts a multi or single-date string to an array of date field arrays
2957* @private
2958 * @param {String} sDates Date string with one or more comma-delimited dates. Valid formats are mm/dd, mm/dd/yyyy, mm/dd/yyyy-mm/dd/yyyy
2959 * @return An array of date field arrays
2960* @type Array[](Number[])
2961*/
2962YAHOO.widget.Calendar.prototype._parseDates = function(sDates) {
2963 var aReturn = [];
2964
2965 var aDates = sDates.split(this.Locale.DATE_DELIMITER);
2966
2967 for (var d=0;d<aDates.length;++d) {
2968 var sDate = aDates[d];
2969
2970 if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
2971 // This is a range
2972 var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER);
2973
2974 var dateStart = this._parseDate(aRange[0]);
2975 var dateEnd = this._parseDate(aRange[1]);
2976
2977 var fullRange = this._parseRange(dateStart, dateEnd);
2978 aReturn = aReturn.concat(fullRange);
2979 } else {
2980 // This is not a range
2981 var aDate = this._parseDate(sDate);
2982 aReturn.push(aDate);
2983 }
2984 }
2985 return aReturn;
2986};
2987
2988/**
2989* Converts a date range to the full list of included dates
2990* @private
2991 * @param {Number[]} startDateDate field array representing the first date in the range
2992 * @param {Number[]} endDate Date field array representing the last date in the range
2993 * @return An array of date field arrays
2994* @type Array[](Number[])
2995*/
2996YAHOO.widget.Calendar.prototype._parseRange = function(startDate, endDate) {
2997 var dStart = new Date(startDate[0],startDate[1]-1,startDate[2]);
2998 var dCurrent = YAHOO.widget.DateMath.add(new Date(startDate[0],startDate[1]-1,startDate[2]),YAHOO.widget.DateMath.DAY,1);
2999 var dEnd = new Date(endDate[0], endDate[1]-1, endDate[2]);
3000
3001 var results = [];
3002 results.push(startDate);
3003 while (dCurrent.getTime() <= dEnd.getTime()) {
3004 results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
3005 dCurrent = YAHOO.widget.DateMath.add(dCurrent,YAHOO.widget.DateMath.DAY,1);
3006 }
3007 return results;
3008};
3009
3010// END DATE PARSE METHODS
3011
3012// BEGIN RENDERER METHODS
3013
3014/**
3015* Resets the render stack of the current calendar to its original pre-render value.
3016*/
3017YAHOO.widget.Calendar.prototype.resetRenderers = function() {
3018 this.renderStack = this._renderStack.concat();
3019};
3020
3021/**
3022* Clears the inner HTML, CSS class and style information from the specified cell.
3023* @method clearElement
3024 * @param {HTMLTableCellElement}The cell to clear
3025*/
3026YAHOO.widget.Calendar.prototype.clearElement = function(cell) {
3027 cell.innerHTML = "&#160;";
3028 cell.className="";
3029};
3030
3031/**
3032* Adds a renderer to the render stack. The function reference passed to this method will be executed
3033* when a date cell matches the conditions specified in the date string for this renderer.
3034* @method addRenderer
3035 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
3036 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
3037 * @param {Function} fnRenderThe function executed to render cells that match the render rules for this renderer.
3038*/
3039YAHOO.widget.Calendar.prototype.addRenderer = function(sDates, fnRender) {
3040 var aDates = this._parseDates(sDates);
3041 for (var i=0;i<aDates.length;++i) {
3042 var aDate = aDates[i];
3043
3044 if (aDate.length == 2) { // this is either a range or a month/day combo
3045 if (aDate[0] instanceof Array) { // this is a range
3046 this._addRenderer(YAHOO.widget.Calendar.RANGE,aDate,fnRender);
3047 } else { // this is a month/day combo
3048 this._addRenderer(YAHOO.widget.Calendar.MONTH_DAY,aDate,fnRender);
3049 }
3050 } else if (aDate.length == 3) {
3051 this._addRenderer(YAHOO.widget.Calendar.DATE,aDate,fnRender);
3052 }
3053 }
3054};
3055
3056/**
3057* The private method used for adding cell renderers to the local render stack.
3058* This method is called by other methods that set the renderer type prior to the method call.
3059* @method _addRenderer
3060* @private
3061 * @param {String} type The type string that indicates the type of date renderer being added.
3062 * Values are YAHOO.widget.Calendar.DATE, YAHOO.widget.Calendar.MONTH_DAY, YAHOO.widget.Calendar.WEEKDAY,
3063 * YAHOO.widget.Calendar.RANGE, YAHOO.widget.Calendar.MONTH
3064 * @param {Array} aDates An array of dates used to construct the renderer. The format varies based
3065 * on the renderer type
3066 * @param {Function} fnRenderThe function executed to render cells that match the render rules for this renderer.
3067*/
3068YAHOO.widget.Calendar.prototype._addRenderer = function(type, aDates, fnRender) {
3069 var add = [type,aDates,fnRender];
3070 this.renderStack.unshift(add);
3071 this._renderStack = this.renderStack.concat();
3072};
3073
3074/**
3075* Adds a month to the render stack. The function reference passed to this method will be executed
3076* when a date cell matches the month passed to this method.
3077* @method addMonthRenderer
3078 * @param {Number} month The month (1-12) to associate with this renderer
3079 * @param {Function} fnRenderThe function executed to render cells that match the render rules for this renderer.
3080*/
3081YAHOO.widget.Calendar.prototype.addMonthRenderer = function(month, fnRender) {
3082 this._addRenderer(YAHOO.widget.Calendar.MONTH,[month],fnRender);
3083};
3084
3085/**
3086* Adds a weekday to the render stack. The function reference passed to this method will be executed
3087* when a date cell matches the weekday passed to this method.
3088* @method addWeekdayRenderer
3089 * @param {Number} weekday The weekday (0-6) to associate with this renderer
3090 * @param {Function} fnRenderThe function executed to render cells that match the render rules for this renderer.
3091*/
3092YAHOO.widget.Calendar.prototype.addWeekdayRenderer = function(weekday, fnRender) {
3093 this._addRenderer(YAHOO.widget.Calendar.WEEKDAY,[weekday],fnRender);
3094};
3095
3096// END RENDERER METHODS
3097
3098// BEGIN CSS METHODS
3099
3100/**
3101* Removes all styles from all body cells in the current calendar table.
3102* @method clearAllBodyCellStyles
3103 * @param {style} The CSS class name to remove from all calendar body cells
3104*/
3105YAHOO.widget.Calendar.prototype.clearAllBodyCellStyles = function(style) {
3106 for (var c=0;c<this.cells.length;++c) {
3107 YAHOO.util.Dom.removeClass(this.cells[c],style);
3108 }
3109};
3110
3111// END CSS METHODS
3112
3113// BEGIN GETTER/SETTER METHODS
3114/**
3115* Sets the calendar's month explicitly
3116* @method setMonth
3117 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
3118*/
3119YAHOO.widget.Calendar.prototype.setMonth = function(month) {
3120 var current = this.cfg.getProperty("pagedate");
3121 current.setMonth(month);
3122 this.cfg.setProperty("pagedate", current);
3123};
3124
3125/**
3126* Sets the calendar's year explicitly.
3127* @method setYear
3128 * @param {Number} year The numeric 4-digit year
3129*/
3130YAHOO.widget.Calendar.prototype.setYear = function(year) {
3131 var current = this.cfg.getProperty("pagedate");
3132 current.setFullYear(year);
3133 this.cfg.setProperty("pagedate", current);
3134};
3135
3136/**
3137* Gets the list of currently selected dates from the calendar.
3138* @method getSelectedDates
3139* @return {Date[]} An array of currently selected JavaScript Date objects.
3140*/
3141YAHOO.widget.Calendar.prototype.getSelectedDates = function() {
3142 var returnDates = [];
3143 var selected = this.cfg.getProperty("selected");
3144
3145 for (var d=0;d<selected.length;++d) {
3146 var dateArray = selected[d];
3147
3148 var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
3149 returnDates.push(date);
3150 }
3151
3152 returnDates.sort( function(a,b) { return a-b; } );
3153 return returnDates;
3154};
3155
3156/// END GETTER/SETTER METHODS ///
3157
3158/**
3159* Hides the Calendar's outer container from view.
3160* @method hide
3161*/
3162YAHOO.widget.Calendar.prototype.hide = function() {
3163 this.oDomContainer.style.display = "none";
3164};
3165
3166/**
3167* Shows the Calendar's outer container.
3168* @method show
3169*/
3170YAHOO.widget.Calendar.prototype.show = function() {
3171 this.oDomContainer.style.display = "block";
3172};
3173
3174/**
3175* Returns a string representing the current browser.
3176* @property browser
3177* @type String
3178*/
3179YAHOO.widget.Calendar.prototype.browser = function() {
3180 var ua = navigator.userAgent.toLowerCase();
3181 if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
3182 return 'opera';
3183 } else if (ua.indexOf('msie 7')!=-1) { // IE7
3184 return 'ie7';
3185 } else if (ua.indexOf('msie') !=-1) { // IE
3186 return 'ie';
3187 } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
3188 return 'safari';
3189 } else if (ua.indexOf('gecko') != -1) { // Gecko
3190 return 'gecko';
3191 } else {
3192 return false;
3193 }
3194 }();
3195/**
3196* Returns a string representation of the object.
3197* @method toString
3198 * @return {String}A string representation of the Calendar object.
3199*/
3200YAHOO.widget.Calendar.prototype.toString = function() {
3201 return "Calendar " + this.id;
3202};
3203
3204/**
3205* @namespace YAHOO.widget
3206* @class Calendar_Core
3207* @extends YAHOO.widget.Calendar
3208* @deprecated The old Calendar_Core class is no longer necessary.
3209*/
3210YAHOO.widget.Calendar_Core = YAHOO.widget.Calendar;
3211
3212YAHOO.widget.Cal_Core = YAHOO.widget.Calendar;
3213
3214/**
3215* YAHOO.widget.CalendarGroup is a special container class for YAHOO.widget.Calendar. This class facilitates
3216* the ability to have multi-page calendar views that share a single dataset and are
3217* dependent on each other.
3218*
3219* The calendar group instance will refer to each of its elements using a 0-based index.
3220* For example, to construct the placeholder for a calendar group widget with id "cal1" and
3221* containerId of "cal1Container", the markup would be as follows:
3222 *<xmp>
3223 * <div id="cal1Container_0"></div>
3224 * <div id="cal1Container_1"></div>
3225 *</xmp>
3226* The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
3227* @namespace YAHOO.widget
3228* @class CalendarGroup
3229* @constructor
3230 * @param {String} id The id of the table element that will represent the calendar widget
3231 * @param {String} containerIdThe id of the container div element that will wrap the calendar table
3232 * @param {Object} config The configuration object containing the Calendar's arguments
3233*/
3234YAHOO.widget.CalendarGroup = function(id, containerId, config) {
3235 if (arguments.length > 0) {
3236 this.init(id, containerId, config);
3237 }
3238};
3239
3240/**
3241* Initializes the calendar group. All subclasses must call this method in order for the
3242* group to be initialized properly.
3243* @method init
3244 * @param {String} id The id of the table element that will represent the calendar widget
3245 * @param {String} containerIdThe id of the container div element that will wrap the calendar table
3246 * @param {Object} config The configuration object containing the Calendar's arguments
3247*/
3248YAHOO.widget.CalendarGroup.prototype.init = function(id, containerId, config) {
3249 this.initEvents();
3250 this.initStyles();
3251
3252 /**
3253 * The collection of Calendar pages contained within the CalendarGroup
3254 * @property pages
3255 * @type YAHOO.widget.Calendar[]
3256 */
3257 this.pages = [];
3258
3259 /**
3260 * The unique id associated with the CalendarGroup
3261 * @property id
3262 * @type String
3263 */
3264 this.id = id;
3265
3266 /**
3267 * The unique id associated with the CalendarGroup container
3268 * @property containerId
3269 * @type String
3270 */
3271 this.containerId = containerId;
3272
3273 /**
3274 * The outer containing element for the CalendarGroup
3275 * @property oDomContainer
3276 * @type HTMLElement
3277 */
3278 this.oDomContainer = document.getElementById(containerId);
3279
3280 YAHOO.util.Dom.addClass(this.oDomContainer, YAHOO.widget.CalendarGroup.CSS_CONTAINER);
3281 YAHOO.util.Dom.addClass(this.oDomContainer, YAHOO.widget.CalendarGroup.CSS_MULTI_UP);
3282
3283 /**
3284 * The Config object used to hold the configuration variables for the CalendarGroup
3285 * @property cfg
3286 * @type YAHOO.util.Config
3287 */
3288 this.cfg = new YAHOO.util.Config(this);
3289
3290 /**
3291 * The local object which contains the CalendarGroup's options
3292 * @property Options
3293 * @type Object
3294 */
3295 this.Options = {};
3296
3297 /**
3298 * The local object which contains the CalendarGroup's locale settings
3299 * @property Locale
3300 * @type Object
3301 */
3302 this.Locale = {};
3303
3304 this.setupConfig();
3305
3306 if (config) {
3307 this.cfg.applyConfig(config, true);
3308 }
3309
3310 this.cfg.fireQueue();
3311
3312 // OPERA HACK FOR MISWRAPPED FLOATS
3313 if (this.browser == "opera"){
3314 var fixWidth = function() {
3315 var startW = this.oDomContainer.offsetWidth;
3316 var w = 0;
3317 for (var p=0;p<this.pages.length;++p) {
3318 var cal = this.pages[p];
3319 w += cal.oDomContainer.offsetWidth;
3320 }
3321 if (w > 0) {
3322 this.oDomContainer.style.width = w + "px";
3323 }
3324 };
3325 this.renderEvent.subscribe(fixWidth,this,true);
3326 }
3327};
3328
3329
3330YAHOO.widget.CalendarGroup.prototype.setupConfig = function() {
3331 /**
3332 * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments.
3333 * @config pages
3334 * @type Number
3335 * @default 2
3336 */
3337 this.cfg.addProperty("pages", { value:2, validator:this.cfg.checkNumber, handler:this.configPages } );
3338
3339 /**
3340 * The month/year representing the current visible Calendar date (mm/yyyy)
3341 * @config pagedate
3342 * @type String
3343 * @default today's date
3344 */
3345 this.cfg.addProperty("pagedate", { value:new Date(), handler:this.configPageDate } );
3346
3347 /**
3348 * The date or range of dates representing the current Calendar selection
3349 * @config selected
3350 * @type String
3351 * @default []
3352 */
3353 this.cfg.addProperty("selected", { value:[], handler:this.delegateConfig } );
3354
3355 /**
3356 * The title to display above the CalendarGroup's month header
3357 * @config title
3358 * @type String
3359 * @default ""
3360 */
3361 this.cfg.addProperty("title", { value:"", handler:this.configTitle } );
3362
3363 /**
3364 * Whether or not a close button should be displayed for this CalendarGroup
3365 * @config close
3366 * @type Boolean
3367 * @default false
3368 */
3369 this.cfg.addProperty("close", { value:false, handler:this.configClose } );
3370
3371 /**
3372 * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below.
3373 * @config iframe
3374 * @type Boolean
3375 * @default true
3376 */
3377 this.cfg.addProperty("iframe", { value:true, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3378
3379 /**
3380 * The minimum selectable date in the current Calendar (mm/dd/yyyy)
3381 * @config mindate
3382 * @type String
3383 * @default null
3384 */
3385 this.cfg.addProperty("mindate", { value:null, handler:this.delegateConfig } );
3386
3387 /**
3388 * The maximum selectable date in the current Calendar (mm/dd/yyyy)
3389 * @config maxdate
3390 * @type String
3391 * @default null
3392 */
3393 this.cfg.addProperty("maxdate", { value:null, handler:this.delegateConfig } );
3394
3395 // Options properties
3396
3397 /**
3398 * True if the Calendar should allow multiple selections. False by default.
3399 * @config MULTI_SELECT
3400 * @type Boolean
3401 * @default false
3402 */
3403 this.cfg.addProperty("MULTI_SELECT",{ value:false, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3404
3405 /**
3406 * The weekday the week begins on. Default is 0 (Sunday).
3407 * @config START_WEEKDAY
3408 * @type number
3409 * @default 0
3410 */
3411 this.cfg.addProperty("START_WEEKDAY",{ value:0, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3412
3413 /**
3414 * True if the Calendar should show weekday labels. True by default.
3415 * @config SHOW_WEEKDAYS
3416 * @type Boolean
3417 * @default true
3418 */
3419 this.cfg.addProperty("SHOW_WEEKDAYS",{ value:true, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3420
3421 /**
3422 * True if the Calendar should show week row headers. False by default.
3423 * @config SHOW_WEEK_HEADER
3424 * @type Boolean
3425 * @default false
3426 */
3427 this.cfg.addProperty("SHOW_WEEK_HEADER",{ value:false, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3428
3429 /**
3430 * True if the Calendar should show week row footers. False by default.
3431 * @config SHOW_WEEK_FOOTER
3432 * @type Boolean
3433 * @default false
3434 */
3435 this.cfg.addProperty("SHOW_WEEK_FOOTER",{ value:false, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3436
3437 /**
3438 * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
3439 * @config HIDE_BLANK_WEEKS
3440 * @type Boolean
3441 * @default false
3442 */
3443 this.cfg.addProperty("HIDE_BLANK_WEEKS",{ value:false, handler:this.delegateConfig, validator:this.cfg.checkBoolean } );
3444
3445 /**
3446 * The image that should be used for the left navigation arrow.
3447 * @config NAV_ARROW_LEFT
3448 * @type String
3449 * @default YAHOO.widget.Calendar.IMG_ROOT + "us/tr/callt.gif"
3450 */
3451 this.cfg.addProperty("NAV_ARROW_LEFT",{ value:YAHOO.widget.Calendar.IMG_ROOT + "us/tr/callt.gif", handler:this.delegateConfig } );
3452
3453 /**
3454 * The image that should be used for the left navigation arrow.
3455 * @config NAV_ARROW_RIGHT
3456 * @type String
3457 * @default YAHOO.widget.Calendar.IMG_ROOT + "us/tr/calrt.gif"
3458 */
3459 this.cfg.addProperty("NAV_ARROW_RIGHT",{ value:YAHOO.widget.Calendar.IMG_ROOT + "us/tr/calrt.gif", handler:this.delegateConfig } );
3460
3461 // Locale properties
3462
3463 /**
3464 * The short month labels for the current locale.
3465 * @config MONTHS_SHORT
3466 * @type String[]
3467 * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
3468 */
3469 this.cfg.addProperty("MONTHS_SHORT",{ value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], handler:this.delegateConfig } );
3470
3471 /**
3472 * The long month labels for the current locale.
3473 * @config MONTHS_LONG
3474 * @type String[]
3475 * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
3476 */
3477 this.cfg.addProperty("MONTHS_LONG", { value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], handler:this.delegateConfig } );
3478
3479 /**
3480 * The 1-character weekday labels for the current locale.
3481 * @config WEEKDAYS_1CHAR
3482 * @type String[]
3483 * @default ["S", "M", "T", "W", "T", "F", "S"]
3484 */
3485 this.cfg.addProperty("WEEKDAYS_1CHAR",{ value:["S", "M", "T", "W", "T", "F", "S"], handler:this.delegateConfig } );
3486
3487 /**
3488 * The short weekday labels for the current locale.
3489 * @config WEEKDAYS_SHORT
3490 * @type String[]
3491 * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
3492 */
3493 this.cfg.addProperty("WEEKDAYS_SHORT",{ value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"], handler:this.delegateConfig } );
3494
3495 /**
3496 * The medium weekday labels for the current locale.
3497 * @config WEEKDAYS_MEDIUM
3498 * @type String[]
3499 * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
3500 */
3501 this.cfg.addProperty("WEEKDAYS_MEDIUM",{ value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], handler:this.delegateConfig } );
3502
3503 /**
3504 * The long weekday labels for the current locale.
3505 * @config WEEKDAYS_LONG
3506 * @type String[]
3507 * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
3508 */
3509 this.cfg.addProperty("WEEKDAYS_LONG",{ value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], handler:this.delegateConfig } );
3510
3511 /**
3512 * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
3513 * @config LOCALE_MONTHS
3514 * @type String
3515 * @default "long"
3516 */
3517 this.cfg.addProperty("LOCALE_MONTHS",{ value:"long", handler:this.delegateConfig } );
3518
3519 /**
3520 * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
3521 * @config LOCALE_WEEKDAYS
3522 * @type String
3523 * @default "short"
3524 */
3525 this.cfg.addProperty("LOCALE_WEEKDAYS",{ value:"short", handler:this.delegateConfig } );
3526
3527 /**
3528 * The value used to delimit individual dates in a date string passed to various Calendar functions.
3529 * @config DATE_DELIMITER
3530 * @type String
3531 * @default ","
3532 */
3533 this.cfg.addProperty("DATE_DELIMITER", { value:",", handler:this.delegateConfig } );
3534
3535 /**
3536 * The value used to delimit date fields in a date string passed to various Calendar functions.
3537 * @config DATE_FIELD_DELIMITER
3538 * @type String
3539 * @default "/"
3540 */
3541 this.cfg.addProperty("DATE_FIELD_DELIMITER",{ value:"/", handler:this.delegateConfig } );
3542
3543 /**
3544 * The value used to delimit date ranges in a date string passed to various Calendar functions.
3545 * @config DATE_RANGE_DELIMITER
3546 * @type String
3547 * @default "-"
3548 */
3549 this.cfg.addProperty("DATE_RANGE_DELIMITER",{ value:"-", handler:this.delegateConfig } );
3550
3551 /**
3552 * The position of the month in a month/year date string
3553 * @config MY_MONTH_POSITION
3554 * @type Number
3555 * @default 1
3556 */
3557 this.cfg.addProperty("MY_MONTH_POSITION",{ value:1, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3558
3559 /**
3560 * The position of the year in a month/year date string
3561 * @config MY_YEAR_POSITION
3562 * @type Number
3563 * @default 2
3564 */
3565 this.cfg.addProperty("MY_YEAR_POSITION",{ value:2, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3566
3567 /**
3568 * The position of the month in a month/day date string
3569 * @config MD_MONTH_POSITION
3570 * @type Number
3571 * @default 1
3572 */
3573 this.cfg.addProperty("MD_MONTH_POSITION",{ value:1, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3574
3575 /**
3576 * The position of the day in a month/year date string
3577 * @config MD_DAY_POSITION
3578 * @type Number
3579 * @default 2
3580 */
3581 this.cfg.addProperty("MD_DAY_POSITION", { value:2, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3582
3583 /**
3584 * The position of the month in a month/day/year date string
3585 * @config MDY_MONTH_POSITION
3586 * @type Number
3587 * @default 1
3588 */
3589 this.cfg.addProperty("MDY_MONTH_POSITION",{ value:1, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3590
3591 /**
3592 * The position of the day in a month/day/year date string
3593 * @config MDY_DAY_POSITION
3594 * @type Number
3595 * @default 2
3596 */
3597 this.cfg.addProperty("MDY_DAY_POSITION",{ value:2, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3598
3599 /**
3600 * The position of the year in a month/day/year date string
3601 * @config MDY_YEAR_POSITION
3602 * @type Number
3603 * @default 3
3604 */
3605 this.cfg.addProperty("MDY_YEAR_POSITION",{ value:3, handler:this.delegateConfig, validator:this.cfg.checkNumber } );
3606
3607};
3608
3609/**
3610* Initializes CalendarGroup's built-in CustomEvents
3611* @method initEvents
3612*/
3613YAHOO.widget.CalendarGroup.prototype.initEvents = function() {
3614 var me = this;
3615
3616 /**
3617 * Proxy subscriber to subscribe to the CalendarGroup's child Calendars' CustomEvents
3618 * @method sub
3619 * @private
3620 * @param {Function} fnThe function to subscribe to this CustomEvent
3621 * @param {Object} objThe CustomEvent's scope object
3622 * @param {Boolean} bOverrideWhether or not to apply scope correction
3623 */
3624 var sub = function(fn, obj, bOverride) {
3625 for (var p=0;p<me.pages.length;++p) {
3626 var cal = me.pages[p];
3627 cal[this.type + "Event"].subscribe(fn, obj, bOverride);
3628 }
3629 };
3630
3631 /**
3632 * Proxy unsubscriber to unsubscribe from the CalendarGroup's child Calendars' CustomEvents
3633 * @method unsub
3634 * @private
3635 * @param {Function} fnThe function to subscribe to this CustomEvent
3636 * @param {Object} objThe CustomEvent's scope object
3637 */
3638 var unsub = function(fn, obj) {
3639 for (var p=0;p<me.pages.length;++p) {
3640 var cal = me.pages[p];
3641 cal[this.type + "Event"].unsubscribe(fn, obj);
3642 }
3643 };
3644
3645 /**
3646 * Fired before a selection is made
3647 * @event beforeSelectEvent
3648 */
3649 this.beforeSelectEvent = new YAHOO.util.CustomEvent("beforeSelect");
3650 this.beforeSelectEvent.subscribe = sub; this.beforeSelectEvent.unsubscribe = unsub;
3651
3652 /**
3653 * Fired when a selection is made
3654 * @event selectEvent
3655 * @param {Array}Array of Date field arrays in the format [YYYY, MM, DD].
3656 */
3657 this.selectEvent = new YAHOO.util.CustomEvent("select");
3658 this.selectEvent.subscribe = sub; this.selectEvent.unsubscribe = unsub;
3659
3660 /**
3661 * Fired before a selection is made
3662 * @event beforeDeselectEvent
3663 */
3664 this.beforeDeselectEvent = new YAHOO.util.CustomEvent("beforeDeselect");
3665 this.beforeDeselectEvent.subscribe = sub; this.beforeDeselectEvent.unsubscribe = unsub;
3666
3667 /**
3668 * Fired when a selection is made
3669 * @event deselectEvent
3670 * @param {Array}Array of Date field arrays in the format [YYYY, MM, DD].
3671 */
3672 this.deselectEvent = new YAHOO.util.CustomEvent("deselect");
3673 this.deselectEvent.subscribe = sub; this.deselectEvent.unsubscribe = unsub;
3674
3675 /**
3676 * Fired when the Calendar page is changed
3677 * @event changePageEvent
3678 */
3679 this.changePageEvent = new YAHOO.util.CustomEvent("changePage");
3680 this.changePageEvent.subscribe = sub; this.changePageEvent.unsubscribe = unsub;
3681
3682 /**
3683 * Fired before the Calendar is rendered
3684 * @event beforeRenderEvent
3685 */
3686 this.beforeRenderEvent = new YAHOO.util.CustomEvent("beforeRender");
3687 this.beforeRenderEvent.subscribe = sub; this.beforeRenderEvent.unsubscribe = unsub;
3688
3689 /**
3690 * Fired when the Calendar is rendered
3691 * @event renderEvent
3692 */
3693 this.renderEvent = new YAHOO.util.CustomEvent("render");
3694 this.renderEvent.subscribe = sub; this.renderEvent.unsubscribe = unsub;
3695
3696 /**
3697 * Fired when the Calendar is reset
3698 * @event resetEvent
3699 */
3700 this.resetEvent = new YAHOO.util.CustomEvent("reset");
3701 this.resetEvent.subscribe = sub; this.resetEvent.unsubscribe = unsub;
3702
3703 /**
3704 * Fired when the Calendar is cleared
3705 * @event clearEvent
3706 */
3707 this.clearEvent = new YAHOO.util.CustomEvent("clear");
3708 this.clearEvent.subscribe = sub; this.clearEvent.unsubscribe = unsub;
3709
3710};
3711
3712/**
3713* The default Config handler for the "pages" property
3714* @method configPages
3715 * @param {String} typeThe CustomEvent type (usually the property name)
3716 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3717 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3718*/
3719YAHOO.widget.CalendarGroup.prototype.configPages = function(type, args, obj) {
3720 var pageCount = args[0];
3721
3722 for (var p=0;p<pageCount;++p) {
3723 var calId = this.id + "_" + p;
3724 var calContainerId = this.containerId + "_" + p;
3725
3726 var childConfig = this.cfg.getConfig();
3727 childConfig.close = false;
3728 childConfig.title = false;
3729
3730 var cal = this.constructChild(calId, calContainerId, childConfig);
3731 var caldate = cal.cfg.getProperty("pagedate");
3732 caldate.setMonth(caldate.getMonth()+p);
3733 cal.cfg.setProperty("pagedate", caldate);
3734
3735 YAHOO.util.Dom.removeClass(cal.oDomContainer, this.Style.CSS_SINGLE);
3736 YAHOO.util.Dom.addClass(cal.oDomContainer, "groupcal");
3737
3738 if (p===0) {
3739 YAHOO.util.Dom.addClass(cal.oDomContainer, "first");
3740 }
3741
3742 if (p==(pageCount-1)) {
3743 YAHOO.util.Dom.addClass(cal.oDomContainer, "last");
3744 }
3745
3746 cal.parent = this;
3747 cal.index = p;
3748
3749 this.pages[this.pages.length] = cal;
3750 }
3751};
3752
3753/**
3754* The default Config handler for the "pagedate" property
3755* @method configPageDate
3756 * @param {String} typeThe CustomEvent type (usually the property name)
3757 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3758 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3759*/
3760YAHOO.widget.CalendarGroup.prototype.configPageDate = function(type, args, obj) {
3761 var val = args[0];
3762
3763 for (var p=0;p<this.pages.length;++p) {
3764 var cal = this.pages[p];
3765 cal.cfg.setProperty("pagedate", val);
3766 var calDate = cal.cfg.getProperty("pagedate");
3767 calDate.setMonth(calDate.getMonth()+p);
3768 }
3769};
3770
3771/**
3772* Delegates a configuration property to the CustomEvents associated with the CalendarGroup's children
3773* @method delegateConfig
3774 * @param {String} typeThe CustomEvent type (usually the property name)
3775 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3776 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3777*/
3778YAHOO.widget.CalendarGroup.prototype.delegateConfig = function(type, args, obj) {
3779 var val = args[0];
3780 var cal;
3781
3782 for (var p=0;p<this.pages.length;p++) {
3783 cal = this.pages[p];
3784 cal.cfg.setProperty(type, val);
3785 }
3786};
3787
3788
3789/**
3790* Adds a function to all child Calendars within this CalendarGroup.
3791* @method setChildFunction
3792 * @param {String} fnName The name of the function
3793 * @param {Function} fn The function to apply to each Calendar page object
3794*/
3795YAHOO.widget.CalendarGroup.prototype.setChildFunction = function(fnName, fn) {
3796 var pageCount = this.cfg.getProperty("pages");
3797
3798 for (var p=0;p<pageCount;++p) {
3799 this.pages[p][fnName] = fn;
3800 }
3801};
3802
3803/**
3804* Calls a function within all child Calendars within this CalendarGroup.
3805* @method callChildFunction
3806 * @param {String} fnName The name of the function
3807 * @param {Array} args The arguments to pass to the function
3808*/
3809YAHOO.widget.CalendarGroup.prototype.callChildFunction = function(fnName, args) {
3810 var pageCount = this.cfg.getProperty("pages");
3811
3812 for (var p=0;p<pageCount;++p) {
3813 var page = this.pages[p];
3814 if (page[fnName]) {
3815 var fn = page[fnName];
3816 fn.call(page, args);
3817 }
3818 }
3819};
3820
3821/**
3822* Constructs a child calendar. This method can be overridden if a subclassed version of the default
3823* calendar is to be used.
3824* @method constructChild
3825 * @param {String} id The id of the table element that will represent the calendar widget
3826 * @param {String} containerIdThe id of the container div element that will wrap the calendar table
3827 * @param {Object} config The configuration object containing the Calendar's arguments
3828 * @return {YAHOO.widget.Calendar}The YAHOO.widget.Calendar instance that is constructed
3829*/
3830YAHOO.widget.CalendarGroup.prototype.constructChild = function(id,containerId,config) {
3831 var container = document.getElementById(containerId);
3832 if (! container) {
3833 container = document.createElement("div");
3834 container.id = containerId;
3835 this.oDomContainer.appendChild(container);
3836 }
3837 return new YAHOO.widget.Calendar(id,containerId,config);
3838};
3839
3840
3841/**
3842* Sets the calendar group's month explicitly. This month will be set into the first
3843* @method setMonth
3844* page of the multi-page calendar, and all other months will be iterated appropriately.
3845 * @param {Number} month The numeric month, from 0 (January) to 11 (December)
3846*/
3847YAHOO.widget.CalendarGroup.prototype.setMonth = function(month) {
3848 for (var p=0;p<this.pages.length;++p) {
3849 var cal = this.pages[p];
3850 cal.setMonth(month+p);
3851 }
3852};
3853
3854/**
3855* Sets the calendar group's year explicitly. This year will be set into the first
3856* @method setYear
3857* page of the multi-page calendar, and all other months will be iterated appropriately.
3858 * @param {Number} year The numeric 4-digit year
3859*/
3860YAHOO.widget.CalendarGroup.prototype.setYear = function(year) {
3861 for (var p=0;p<this.pages.length;++p) {
3862 var cal = this.pages[p];
3863 var pageDate = cal.cfg.getProperty("pageDate");
3864
3865 if ((pageDate.getMonth()+1) == 1 && p>0) {
3866 year+=1;
3867 }
3868 cal.setYear(year);
3869 }
3870};
3871/**
3872* Calls the render function of all child calendars within the group.
3873* @method render
3874*/
3875YAHOO.widget.CalendarGroup.prototype.render = function() {
3876 this.renderHeader();
3877 for (var p=0;p<this.pages.length;++p) {
3878 var cal = this.pages[p];
3879 cal.render();
3880 }
3881 this.renderFooter();
3882};
3883
3884/**
3885* Selects a date or a collection of dates on the current calendar. This method, by default,
3886* does not call the render method explicitly. Once selection has completed, render must be
3887* called for the changes to be reflected visually.
3888* @method select
3889 * @param {String/Date/Date[]} dateThe date string of dates to select in the current calendar. Valid formats are
3890 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3891 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3892 * This method can also take a JavaScript Date object or an array of Date objects.
3893 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3894*/
3895YAHOO.widget.CalendarGroup.prototype.select = function(date) {
3896 for (var p=0;p<this.pages.length;++p) {
3897 var cal = this.pages[p];
3898 cal.select(date);
3899 }
3900 return this.getSelectedDates();
3901};
3902
3903/**
3904* Selects a date on the current calendar by referencing the index of the cell that should be selected.
3905* This method is used to easily select a single cell (usually with a mouse click) without having to do
3906* a full render. The selected style is applied to the cell directly.
3907* @method selectCell
3908 * @param {Number} cellIndexThe index of the cell to select in the current calendar.
3909 * @return {Date[]}Array of JavaScript Date objects representing all individual dates that are currently selected.
3910*/
3911YAHOO.widget.CalendarGroup.prototype.selectCell = function(cellIndex) {
3912 for (var p=0;p<this.pages.length;++p) {
3913 var cal = this.pages[p];
3914 cal.selectCell(cellIndex);
3915 }
3916 return this.getSelectedDates();
3917};
3918
3919/**
3920* Deselects a date or a collection of dates on the current calendar. This method, by default,
3921* does not call the render method explicitly. Once deselection has completed, render must be
3922* called for the changes to be reflected visually.
3923* @method deselect
3924 * @param {String/Date/Date[]} dateThe date string of dates to deselect in the current calendar. Valid formats are
3925 * individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
3926 * Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
3927 * This method can also take a JavaScript Date object or an array of Date objects.
3928 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3929*/
3930YAHOO.widget.CalendarGroup.prototype.deselect = function(date) {
3931 for (var p=0;p<this.pages.length;++p) {
3932 var cal = this.pages[p];
3933 cal.deselect(date);
3934 }
3935 return this.getSelectedDates();
3936};
3937
3938/**
3939* Deselects all dates on the current calendar.
3940* @method deselectAll
3941 * @return {Date[]} Array of JavaScript Date objects representing all individual dates that are currently selected.
3942 * Assuming that this function executes properly, the return value should be an empty array.
3943 * However, the empty array is returned for the sake of being able to check the selection status
3944 * of the calendar.
3945*/
3946YAHOO.widget.CalendarGroup.prototype.deselectAll = function() {
3947 for (var p=0;p<this.pages.length;++p) {
3948 var cal = this.pages[p];
3949 cal.deselectAll();
3950 }
3951 return this.getSelectedDates();
3952};
3953
3954/**
3955* Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
3956* This method is used to easily deselect a single cell (usually with a mouse click) without having to do
3957* a full render. The selected style is removed from the cell directly.
3958* @method deselectCell
3959 * @param {Number} cellIndexThe index of the cell to deselect in the current calendar.
3960 * @return {Date[]}Array of JavaScript Date objects representing all individual dates that are currently selected.
3961*/
3962YAHOO.widget.CalendarGroup.prototype.deselectCell = function(cellIndex) {
3963 for (var p=0;p<this.pages.length;++p) {
3964 var cal = this.pages[p];
3965 cal.deselectCell(cellIndex);
3966 }
3967 return this.getSelectedDates();
3968};
3969
3970/**
3971* Resets the calendar widget to the originally selected month and year, and
3972* sets the calendar to the initial selection(s).
3973* @method reset
3974*/
3975YAHOO.widget.CalendarGroup.prototype.reset = function() {
3976 for (var p=0;p<this.pages.length;++p) {
3977 var cal = this.pages[p];
3978 cal.reset();
3979 }
3980};
3981
3982/**
3983* Clears the selected dates in the current calendar widget and sets the calendar
3984* to the current month and year.
3985* @method clear
3986*/
3987YAHOO.widget.CalendarGroup.prototype.clear = function() {
3988 for (var p=0;p<this.pages.length;++p) {
3989 var cal = this.pages[p];
3990 cal.clear();
3991 }
3992};
3993
3994/**
3995* Navigates to the next month page in the calendar widget.
3996* @method nextMonth
3997*/
3998YAHOO.widget.CalendarGroup.prototype.nextMonth = function() {
3999 for (var p=0;p<this.pages.length;++p) {
4000 var cal = this.pages[p];
4001 cal.nextMonth();
4002 }
4003};
4004
4005/**
4006* Navigates to the previous month page in the calendar widget.
4007* @method previousMonth
4008*/
4009YAHOO.widget.CalendarGroup.prototype.previousMonth = function() {
4010 for (var p=this.pages.length-1;p>=0;--p) {
4011 var cal = this.pages[p];
4012 cal.previousMonth();
4013 }
4014};
4015
4016/**
4017* Navigates to the next year in the currently selected month in the calendar widget.
4018* @method nextYear
4019*/
4020YAHOO.widget.CalendarGroup.prototype.nextYear = function() {
4021 for (var p=0;p<this.pages.length;++p) {
4022 var cal = this.pages[p];
4023 cal.nextYear();
4024 }
4025};
4026
4027/**
4028* Navigates to the previous year in the currently selected month in the calendar widget.
4029* @method previousYear
4030*/
4031YAHOO.widget.CalendarGroup.prototype.previousYear = function() {
4032 for (var p=0;p<this.pages.length;++p) {
4033 var cal = this.pages[p];
4034 cal.previousYear();
4035 }
4036};
4037
4038
4039/**
4040* Gets the list of currently selected dates from the calendar.
4041 * @return An array of currently selected JavaScript Date objects.
4042* @type Date[]
4043*/
4044YAHOO.widget.CalendarGroup.prototype.getSelectedDates = function() {
4045 var returnDates = [];
4046 var selected = this.cfg.getProperty("selected");
4047
4048 for (var d=0;d<selected.length;++d) {
4049 var dateArray = selected[d];
4050
4051 var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
4052 returnDates.push(date);
4053 }
4054
4055 returnDates.sort( function(a,b) { return a-b; } );
4056 return returnDates;
4057};
4058
4059/**
4060* Adds a renderer to the render stack. The function reference passed to this method will be executed
4061* when a date cell matches the conditions specified in the date string for this renderer.
4062* @method addRenderer
4063 * @param {String} sDates A date string to associate with the specified renderer. Valid formats
4064 * include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
4065 * @param {Function} fnRenderThe function executed to render cells that match the render rules for this renderer.
4066*/
4067YAHOO.widget.CalendarGroup.prototype.addRenderer = function(sDates, fnRender) {
4068 for (var p=0;p<this.pages.length;++p) {
4069 var cal = this.pages[p];
4070 cal.addRenderer(sDates, fnRender);
4071 }
4072};
4073
4074/**
4075* Adds a month to the render stack. The function reference passed to this method will be executed
4076* when a date cell matches the month passed to this method.
4077* @method addMonthRenderer
4078 * @param {Number} month The month (1-12) to associate with this renderer
4079 * @param {Function} fnRenderThe function executed to render cells that match the render rules for this renderer.
4080*/
4081YAHOO.widget.CalendarGroup.prototype.addMonthRenderer = function(month, fnRender) {
4082 for (var p=0;p<this.pages.length;++p) {
4083 var cal = this.pages[p];
4084 cal.addMonthRenderer(month, fnRender);
4085 }
4086};
4087
4088/**
4089* Adds a weekday to the render stack. The function reference passed to this method will be executed
4090* when a date cell matches the weekday passed to this method.
4091* @method addWeekdayRenderer
4092 * @param {Number} weekday The weekday (0-6) to associate with this renderer
4093 * @param {Function} fnRenderThe function executed to render cells that match the render rules for this renderer.
4094*/
4095YAHOO.widget.CalendarGroup.prototype.addWeekdayRenderer = function(weekday, fnRender) {
4096 for (var p=0;p<this.pages.length;++p) {
4097 var cal = this.pages[p];
4098 cal.addWeekdayRenderer(weekday, fnRender);
4099 }
4100};
4101
4102/**
4103* Renders the header for the CalendarGroup.
4104* @method renderHeader
4105*/
4106YAHOO.widget.CalendarGroup.prototype.renderHeader = function() {};
4107
4108/**
4109* Renders a footer for the 2-up calendar container. By default, this method is
4110* unimplemented.
4111* @method renderFooter
4112*/
4113YAHOO.widget.CalendarGroup.prototype.renderFooter = function() {};
4114
4115/**
4116* Adds the designated number of months to the current calendar month, and sets the current
4117* calendar page date to the new month.
4118* @method addMonths
4119 * @param {Number} countThe number of months to add to the current calendar
4120*/
4121YAHOO.widget.CalendarGroup.prototype.addMonths = function(count) {
4122 this.callChildFunction("addMonths", count);
4123};
4124
4125
4126/**
4127* Subtracts the designated number of months from the current calendar month, and sets the current
4128* calendar page date to the new month.
4129* @method subtractMonths
4130 * @param {Number} countThe number of months to subtract from the current calendar
4131*/
4132YAHOO.widget.CalendarGroup.prototype.subtractMonths = function(count) {
4133 this.callChildFunction("subtractMonths", count);
4134};
4135
4136/**
4137* Adds the designated number of years to the current calendar, and sets the current
4138* calendar page date to the new month.
4139* @method addYears
4140 * @param {Number} countThe number of years to add to the current calendar
4141*/
4142YAHOO.widget.CalendarGroup.prototype.addYears = function(count) {
4143 this.callChildFunction("addYears", count);
4144};
4145
4146/**
4147* Subtcats the designated number of years from the current calendar, and sets the current
4148* calendar page date to the new month.
4149* @method subtractYears
4150 * @param {Number} countThe number of years to subtract from the current calendar
4151*/
4152YAHOO.widget.CalendarGroup.prototype.subtractYears = function(count) {
4153 this.callChildFunction("subtractYears", count);
4154};
4155
4156/**
4157* CSS class representing the container for the calendar
4158* @property YAHOO.widget.CalendarGroup.CSS_CONTAINER
4159* @static
4160* @final
4161* @type String
4162*/
4163YAHOO.widget.CalendarGroup.CSS_CONTAINER = "yui-calcontainer";
4164
4165/**
4166* CSS class representing the container for the calendar
4167* @property YAHOO.widget.CalendarGroup.CSS_MULTI_UP
4168* @static
4169* @final
4170* @type String
4171*/
4172YAHOO.widget.CalendarGroup.CSS_MULTI_UP = "multi";
4173
4174/**
4175* CSS class representing the title for the 2-up calendar
4176* @property YAHOO.widget.CalendarGroup.CSS_2UPTITLE
4177* @static
4178* @final
4179* @type String
4180*/
4181YAHOO.widget.CalendarGroup.CSS_2UPTITLE = "title";
4182
4183/**
4184* CSS class representing the close icon for the 2-up calendar
4185* @property YAHOO.widget.CalendarGroup.CSS_2UPCLOSE
4186* @static
4187* @final
4188* @type String
4189*/
4190YAHOO.widget.CalendarGroup.CSS_2UPCLOSE = "close-icon";
4191
4192YAHOO.augment(YAHOO.widget.CalendarGroup, YAHOO.widget.Calendar, "buildDayLabel",
4193 "buildMonthLabel",
4194 "renderOutOfBoundsDate",
4195 "renderRowHeader",
4196 "renderRowFooter",
4197 "renderCellDefault",
4198 "styleCellDefault",
4199 "renderCellStyleHighlight1",
4200 "renderCellStyleHighlight2",
4201 "renderCellStyleHighlight3",
4202 "renderCellStyleHighlight4",
4203 "renderCellStyleToday",
4204 "renderCellStyleSelected",
4205 "renderCellNotThisMonth",
4206 "renderBodyCellRestricted",
4207 "initStyles",
4208 "configTitle",
4209 "configClose",
4210 "hide",
4211 "show",
4212 "browser");
4213
4214/**
4215* Returns a string representation of the object.
4216* @method toString
4217 * @return {String}A string representation of the CalendarGroup object.
4218*/
4219YAHOO.widget.CalendarGroup.prototype.toString = function() {
4220 return "CalendarGroup " + this.id;
4221};
4222
4223YAHOO.widget.CalGrp = YAHOO.widget.CalendarGroup;
4224
4225/**
4226* @class YAHOO.widget.Calendar2up
4227* @extends YAHOO.widget.CalendarGroup
4228* @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
4229*/
4230YAHOO.widget.Calendar2up = function(id, containerId, config) {
4231 this.init(id, containerId, config);
4232};
4233
4234YAHOO.extend(YAHOO.widget.Calendar2up, YAHOO.widget.CalendarGroup);
4235
4236/**
4237* @deprecated The old Calendar2up class is no longer necessary, since CalendarGroup renders in a 2up view by default.
4238*/
4239YAHOO.widget.Cal2up = YAHOO.widget.Calendar2up; \ No newline at end of file
diff --git a/frontend/beta/js/YUI/connection.js b/frontend/beta/js/YUI/connection.js
new file mode 100644
index 0000000..58f6d0f
--- a/dev/null
+++ b/frontend/beta/js/YUI/connection.js
@@ -0,0 +1,960 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9 * @description
10 * The Connection Manager provides a simplified interface to the XMLHttpRequest
11 * object. It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
12 * interactive states and server response, returning the results to a pre-defined
13 * callback you create.
14 *
15 * @namespace YAHOO.util
16 * @module Connection
17 * @Class Connect
18 */
19YAHOO.util.Connect =
20{
21 /**
22 * @description Array of MSFT ActiveX ids for XMLHttpRequest.
23 * @property _msxml_progid
24 * @private
25 * @static
26 * @type array
27 */
28 _msxml_progid:[
29 'MSXML2.XMLHTTP.3.0',
30 'MSXML2.XMLHTTP',
31 'Microsoft.XMLHTTP'
32 ],
33
34 /**
35 * @description Object literal of HTTP header(s)
36 * @property _http_header
37 * @private
38 * @static
39 * @type object
40 */
41 _http_header:{},
42
43 /**
44 * @description Determines if HTTP headers are set.
45 * @property _has_http_headers
46 * @private
47 * @static
48 * @type boolean
49 */
50 _has_http_headers:false,
51
52 /**
53 * @description Determines if a default header of
54 * Content-Type of 'application/x-www-form-urlencoded'
55 * will be added to any client HTTP headers sent for POST
56 * transactions.
57 * @property _use_default_post_header
58 * @private
59 * @static
60 * @type boolean
61 */
62 _use_default_post_header:true,
63
64 /**
65 * @description Determines if a default header of
66 * Content-Type of 'application/x-www-form-urlencoded'
67 * will be added to any client HTTP headers sent for POST
68 * transactions.
69 * @property _default_post_header
70 * @private
71 * @static
72 * @type boolean
73 */
74 _default_post_header:'application/x-www-form-urlencoded',
75
76 /**
77 * @description Property modified by setForm() to determine if the data
78 * should be submitted as an HTML form.
79 * @property _isFormSubmit
80 * @private
81 * @static
82 * @type boolean
83 */
84 _isFormSubmit:false,
85
86 /**
87 * @description Property modified by setForm() to determine if a file(s)
88 * upload is expected.
89 * @property _isFileUpload
90 * @private
91 * @static
92 * @type boolean
93 */
94 _isFileUpload:false,
95
96 /**
97 * @description Property modified by setForm() to set a reference to the HTML
98 * form node if the desired action is file upload.
99 * @property _formNode
100 * @private
101 * @static
102 * @type object
103 */
104 _formNode:null,
105
106 /**
107 * @description Property modified by setForm() to set the HTML form data
108 * for each transaction.
109 * @property _sFormData
110 * @private
111 * @static
112 * @type string
113 */
114 _sFormData:null,
115
116 /**
117 * @description Collection of polling references to the polling mechanism in handleReadyState.
118 * @property _poll
119 * @private
120 * @static
121 * @type object
122 */
123 _poll:{},
124
125 /**
126 * @description Queue of timeout values for each transaction callback with a defined timeout value.
127 * @property _timeOut
128 * @private
129 * @static
130 * @type object
131 */
132 _timeOut:{},
133
134 /**
135 * @description The polling frequency, in milliseconds, for HandleReadyState.
136 * when attempting to determine a transaction's XHR readyState.
137 * The default is 50 milliseconds.
138 * @property _polling_interval
139 * @private
140 * @static
141 * @type int
142 */
143 _polling_interval:50,
144
145 /**
146 * @description A transaction counter that increments the transaction id for each transaction.
147 * @property _transaction_id
148 * @private
149 * @static
150 * @type int
151 */
152 _transaction_id:0,
153
154 /**
155 * @description Member to add an ActiveX id to the existing xml_progid array.
156 * In the event(unlikely) a new ActiveX id is introduced, it can be added
157 * without internal code modifications.
158 * @method setProgId
159 * @public
160 * @static
161 * @param {string} id The ActiveX id to be added to initialize the XHR object.
162 * @return void
163 */
164 setProgId:function(id)
165 {
166 this._msxml_progid.unshift(id);
167 },
168
169 /**
170 * @description Member to enable or disable the default POST header.
171 * @method setDefaultPostHeader
172 * @public
173 * @static
174 * @param {boolean} b Set and use default header - true or false .
175 * @return void
176 */
177 setDefaultPostHeader:function(b)
178 {
179 this._use_default_post_header = b;
180 },
181
182 /**
183 * @description Member to modify the default polling interval.
184 * @method setPollingInterval
185 * @public
186 * @static
187 * @param {int} i The polling interval in milliseconds.
188 * @return void
189 */
190 setPollingInterval:function(i)
191 {
192 if(typeof i == 'number' && isFinite(i)){
193 this._polling_interval = i;
194 }
195 },
196
197 /**
198 * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
199 * the XMLHttpRequest instance and the transaction id.
200 * @method createXhrObject
201 * @private
202 * @static
203 * @param {int} transactionId Property containing the transaction id for this transaction.
204 * @return object
205 */
206 createXhrObject:function(transactionId)
207 {
208 var obj,http;
209 try
210 {
211 // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
212 http = new XMLHttpRequest();
213 // Object literal with http and tId properties
214 obj = { conn:http, tId:transactionId };
215 }
216 catch(e)
217 {
218 for(var i=0; i<this._msxml_progid.length; ++i){
219 try
220 {
221 // Instantiates XMLHttpRequest for IE and assign to http.
222 http = new ActiveXObject(this._msxml_progid[i]);
223 // Object literal with conn and tId properties
224 obj = { conn:http, tId:transactionId };
225 break;
226 }
227 catch(e){}
228 }
229 }
230 finally
231 {
232 return obj;
233 }
234 },
235
236 /**
237 * @description This method is called by asyncRequest to create a
238 * valid connection object for the transaction. It also passes a
239 * transaction id and increments the transaction id counter.
240 * @method getConnectionObject
241 * @private
242 * @static
243 * @return {object}
244 */
245 getConnectionObject:function()
246 {
247 var o;
248 var tId = this._transaction_id;
249
250 try
251 {
252 o = this.createXhrObject(tId);
253 if(o){
254 this._transaction_id++;
255 }
256 }
257 catch(e){}
258 finally
259 {
260 return o;
261 }
262 },
263
264 /**
265 * @description Method for initiating an asynchronous request via the XHR object.
266 * @method asyncRequest
267 * @public
268 * @static
269 * @param {string} method HTTP transaction method
270 * @param {string} uri Fully qualified path of resource
271 * @param {callback} callback User-defined callback function or object
272 * @param {string} postData POST body
273 * @return {object} Returns the connection object
274 */
275 asyncRequest:function(method, uri, callback, postData)
276 {
277 var o = this.getConnectionObject();
278
279 if(!o){
280 return null;
281 }
282 else{
283 if(this._isFormSubmit){
284 if(this._isFileUpload){
285 this.uploadFile(o.tId, callback, uri, postData);
286 this.releaseObject(o);
287
288 return;
289 }
290
291 //If the specified HTTP method is GET, setForm() will return an
292 //encoded string that is concatenated to the uri to
293 //create a querystring.
294 if(method == 'GET'){
295 if(this._sFormData.length != 0){
296 // If the URI already contains a querystring, append an ampersand
297 // and then concatenate _sFormData to the URI.
298 uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
299 }
300 else{
301 uri += "?" + this._sFormData;
302 }
303 }
304 else if(method == 'POST'){
305 //If POST data exist in addition to the HTML form data,
306 //it will be concatenated to the form data.
307 postData = postData?this._sFormData + "&" + postData:this._sFormData;
308 }
309 }
310
311 o.conn.open(method, uri, true);
312
313 if(this._isFormSubmit || (postData && this._use_default_post_header)){
314 this.initHeader('Content-Type', this._default_post_header);
315 if(this._isFormSubmit){
316 this.resetFormState();
317 }
318 }
319
320 if(this._has_http_headers){
321 this.setHeader(o);
322 }
323
324 this.handleReadyState(o, callback);
325 o.conn.send(postData || null);
326
327 return o;
328 }
329 },
330
331 /**
332 * @description This method serves as a timer that polls the XHR object's readyState
333 * property during a transaction, instead of binding a callback to the
334 * onreadystatechange event. Upon readyState 4, handleTransactionResponse
335 * will process the response, and the timer will be cleared.
336 * @method handleReadyState
337 * @private
338 * @static
339 * @param {object} o The connection object
340 * @param {callback} callback The user-defined callback object
341 * @return {void}
342 */
343 handleReadyState:function(o, callback)
344 {
345 var oConn = this;
346
347 if(callback && callback.timeout){
348 this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
349 }
350
351 this._poll[o.tId] = window.setInterval(
352 function(){
353 if(o.conn && o.conn.readyState == 4){
354 window.clearInterval(oConn._poll[o.tId]);
355 delete oConn._poll[o.tId];
356
357 if(callback && callback.timeout){
358 delete oConn._timeOut[o.tId];
359 }
360
361 oConn.handleTransactionResponse(o, callback);
362 }
363 }
364 ,this._polling_interval);
365 },
366
367 /**
368 * @description This method attempts to interpret the server response and
369 * determine whether the transaction was successful, or if an error or
370 * exception was encountered.
371 * @method handleTransactionResponse
372 * @private
373 * @static
374 * @param {object} o The connection object
375 * @param {object} callback The sser-defined callback object
376 * @param {boolean} isAbort Determines if the transaction was aborted.
377 * @return {void}
378 */
379 handleTransactionResponse:function(o, callback, isAbort)
380 {
381 // If no valid callback is provided, then do not process any callback handling.
382 if(!callback){
383 this.releaseObject(o);
384 return;
385 }
386
387 var httpStatus, responseObject;
388
389 try
390 {
391 if(o.conn.status !== undefined && o.conn.status != 0){
392 httpStatus = o.conn.status;
393 }
394 else{
395 httpStatus = 13030;
396 }
397 }
398 catch(e){
399 // 13030 is the custom code to indicate the condition -- in Mozilla/FF --
400 // when the o object's status and statusText properties are
401 // unavailable, and a query attempt throws an exception.
402 httpStatus = 13030;
403 }
404
405 if(httpStatus >= 200 && httpStatus < 300){
406 try
407 {
408 responseObject = this.createResponseObject(o, callback.argument);
409 if(callback.success){
410 if(!callback.scope){
411 callback.success(responseObject);
412 }
413 else{
414 // If a scope property is defined, the callback will be fired from
415 // the context of the object.
416 callback.success.apply(callback.scope, [responseObject]);
417 }
418 }
419 }
420 catch(e){}
421 }
422 else{
423 try
424 {
425 switch(httpStatus){
426 // The following cases are wininet.dll error codes that may be encountered.
427 case 12002: // Server timeout
428 case 12029: // 12029 to 12031 correspond to dropped connections.
429 case 12030:
430 case 12031:
431 case 12152: // Connection closed by server.
432 case 13030: // See above comments for variable status.
433 responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort?isAbort:false));
434 if(callback.failure){
435 if(!callback.scope){
436 callback.failure(responseObject);
437 }
438 else{
439 callback.failure.apply(callback.scope, [responseObject]);
440 }
441 }
442 break;
443 default:
444 responseObject = this.createResponseObject(o, callback.argument);
445 if(callback.failure){
446 if(!callback.scope){
447 callback.failure(responseObject);
448 }
449 else{
450 callback.failure.apply(callback.scope, [responseObject]);
451 }
452 }
453 }
454 }
455 catch(e){}
456 }
457
458 this.releaseObject(o);
459 responseObject = null;
460 },
461
462 /**
463 * @description This method evaluates the server response, creates and returns the results via
464 * its properties. Success and failure cases will differ in the response
465 * object's property values.
466 * @method createResponseObject
467 * @private
468 * @static
469 * @param {object} o The connection object
470 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
471 * @return {object}
472 */
473 createResponseObject:function(o, callbackArg)
474 {
475 var obj = {};
476 var headerObj = {};
477
478 try
479 {
480 var headerStr = o.conn.getAllResponseHeaders();
481 var header = headerStr.split('\n');
482 for(var i=0; i<header.length; i++){
483 var delimitPos = header[i].indexOf(':');
484 if(delimitPos != -1){
485 headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
486 }
487 }
488 }
489 catch(e){}
490
491 obj.tId = o.tId;
492 obj.status = o.conn.status;
493 obj.statusText = o.conn.statusText;
494 obj.getResponseHeader = headerObj;
495 obj.getAllResponseHeaders = headerStr;
496 obj.responseText = o.conn.responseText;
497 obj.responseXML = o.conn.responseXML;
498
499 if(typeof callbackArg !== undefined){
500 obj.argument = callbackArg;
501 }
502
503 return obj;
504 },
505
506 /**
507 * @description If a transaction cannot be completed due to dropped or closed connections,
508 * there may be not be enough information to build a full response object.
509 * The failure callback will be fired and this specific condition can be identified
510 * by a status property value of 0.
511 *
512 * If an abort was successful, the status property will report a value of -1.
513 *
514 * @method createExceptionObject
515 * @private
516 * @static
517 * @param {int} tId The Transaction Id
518 * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
519 * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
520 * @return {object}
521 */
522 createExceptionObject:function(tId, callbackArg, isAbort)
523 {
524 var COMM_CODE = 0;
525 var COMM_ERROR = 'communication failure';
526 var ABORT_CODE = -1;
527 var ABORT_ERROR = 'transaction aborted';
528
529 var obj = {};
530
531 obj.tId = tId;
532 if(isAbort){
533 obj.status = ABORT_CODE;
534 obj.statusText = ABORT_ERROR;
535 }
536 else{
537 obj.status = COMM_CODE;
538 obj.statusText = COMM_ERROR;
539 }
540
541 if(callbackArg){
542 obj.argument = callbackArg;
543 }
544
545 return obj;
546 },
547
548 /**
549 * @description Public method that stores the custom HTTP headers for each transaction.
550 * @method initHeader
551 * @public
552 * @static
553 * @param {string} label The HTTP header label
554 * @param {string} value The HTTP header value
555 * @return {void}
556 */
557 initHeader:function(label,value)
558 {
559 if(this._http_header[label] === undefined){
560 this._http_header[label] = value;
561 }
562 else{
563 // Concatenate multiple values, comma-delimited,
564 // for the same header label,
565 this._http_header[label] = value + "," + this._http_header[label];
566 }
567
568 this._has_http_headers = true;
569 },
570
571 /**
572 * @description Accessor that sets the HTTP headers for each transaction.
573 * @method setHeader
574 * @private
575 * @static
576 * @param {object} o The connection object for the transaction.
577 * @return {void}
578 */
579 setHeader:function(o)
580 {
581 for(var prop in this._http_header){
582 if(this._http_header.hasOwnProperty(prop)){
583 o.conn.setRequestHeader(prop, this._http_header[prop]);
584 }
585 }
586 delete this._http_header;
587
588 this._http_header = {};
589 this._has_http_headers = false;
590 },
591
592 /**
593 * @description This method assembles the form label and value pairs and
594 * constructs an encoded string.
595 * asyncRequest() will automatically initialize the
596 * transaction with a HTTP header Content-Type of
597 * application/x-www-form-urlencoded.
598 * @method setForm
599 * @public
600 * @static
601 * @param {string || object} form id or name attribute, or form object.
602 * @param {string} optional boolean to indicate SSL environment.
603 * @param {string || boolean} optional qualified path of iframe resource for SSL in IE.
604 * @return {string} string of the HTML form field name and value pairs..
605 */
606 setForm:function(formId, isUpload, secureUri)
607 {
608 this.resetFormState();
609 var oForm;
610 if(typeof formId == 'string'){
611 // Determine if the argument is a form id or a form name.
612 // Note form name usage is deprecated by supported
613 // here for legacy reasons.
614 oForm = (document.getElementById(formId) || document.forms[formId]);
615 }
616 else if(typeof formId == 'object'){
617 // Treat argument as an HTML form object.
618 oForm = formId;
619 }
620 else{
621 return;
622 }
623
624 // If the isUpload argument is true, setForm will call createFrame to initialize
625 // an iframe as the form target.
626 //
627 // The argument secureURI is also required by IE in SSL environments
628 // where the secureURI string is a fully qualified HTTP path, used to set the source
629 // of the iframe, to a stub resource in the same domain.
630 if(isUpload){
631
632 // Create iframe in preparation for file upload.
633 this.createFrame(secureUri?secureUri:null);
634
635 // Set form reference and file upload properties to true.
636 this._isFormSubmit = true;
637 this._isFileUpload = true;
638 this._formNode = oForm;
639
640 return;
641 }
642
643 var oElement, oName, oValue, oDisabled;
644 var hasSubmit = false;
645
646 // Iterate over the form elements collection to construct the
647 // label-value pairs.
648 for (var i=0; i<oForm.elements.length; i++){
649 oElement = oForm.elements[i];
650 oDisabled = oForm.elements[i].disabled;
651 oName = oForm.elements[i].name;
652 oValue = oForm.elements[i].value;
653
654 // Do not submit fields that are disabled or
655 // do not have a name attribute value.
656 if(!oDisabled && oName)
657 {
658 switch (oElement.type)
659 {
660 case 'select-one':
661 case 'select-multiple':
662 for(var j=0; j<oElement.options.length; j++){
663 if(oElement.options[j].selected){
664 if(window.ActiveXObject){
665 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
666 }
667 else{
668 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
669 }
670
671 }
672 }
673 break;
674 case 'radio':
675 case 'checkbox':
676 if(oElement.checked){
677 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
678 }
679 break;
680 case 'file':
681 // stub case as XMLHttpRequest will only send the file path as a string.
682 case undefined:
683 // stub case for fieldset element which returns undefined.
684 case 'reset':
685 // stub case for input type reset button.
686 case 'button':
687 // stub case for input type button elements.
688 break;
689 case 'submit':
690 if(hasSubmit == false){
691 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
692 hasSubmit = true;
693 }
694 break;
695 default:
696 this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
697 break;
698 }
699 }
700 }
701
702 this._isFormSubmit = true;
703 this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
704
705 return this._sFormData;
706 },
707
708 /**
709 * @description Resets HTML form properties when an HTML form or HTML form
710 * with file upload transaction is sent.
711 * @method resetFormState
712 * @private
713 * @static
714 * @return {void}
715 */
716 resetFormState:function(){
717 this._isFormSubmit = false;
718 this._isFileUpload = false;
719 this._formNode = null;
720 this._sFormData = "";
721 },
722
723 /**
724 * @description Creates an iframe to be used for form file uploads. It is remove from the
725 * document upon completion of the upload transaction.
726 * @method createFrame
727 * @private
728 * @static
729 * @param {string} secureUri Optional qualified path of iframe resource for SSL in IE.
730 * @return {void}
731 */
732 createFrame:function(secureUri){
733
734 // IE does not allow the setting of id and name attributes as object
735 // properties via createElement(). A different iframe creation
736 // pattern is required for IE.
737 var frameId = 'yuiIO' + this._transaction_id;
738 if(window.ActiveXObject){
739 var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
740
741 // IE will throw a security exception in an SSL environment if the
742 // iframe source is undefined.
743 if(typeof secureUri == 'boolean'){
744 io.src = 'javascript:false';
745 }
746 else if(typeof secureURI == 'string'){
747 // Deprecated
748 io.src = secureUri;
749 }
750 }
751 else{
752 var io = document.createElement('iframe');
753 io.id = frameId;
754 io.name = frameId;
755 }
756
757 io.style.position = 'absolute';
758 io.style.top = '-1000px';
759 io.style.left = '-1000px';
760
761 document.body.appendChild(io);
762 },
763
764 /**
765 * @description Parses the POST data and creates hidden form elements
766 * for each key-value, and appends them to the HTML form object.
767 * @method appendPostData
768 * @private
769 * @static
770 * @param {string} postData The HTTP POST data
771 * @return {array} formElements Collection of hidden fields.
772 */
773 appendPostData:function(postData)
774 {
775 var formElements = new Array();
776 var postMessage = postData.split('&');
777 for(var i=0; i < postMessage.length; i++){
778 var delimitPos = postMessage[i].indexOf('=');
779 if(delimitPos != -1){
780 formElements[i] = document.createElement('input');
781 formElements[i].type = 'hidden';
782 formElements[i].name = postMessage[i].substring(0,delimitPos);
783 formElements[i].value = postMessage[i].substring(delimitPos+1);
784 this._formNode.appendChild(formElements[i]);
785 }
786 }
787
788 return formElements;
789 },
790
791 /**
792 * @description Uploads HTML form, including files/attachments, to the
793 * iframe created in createFrame.
794 * @method uploadFile
795 * @private
796 * @static
797 * @param {int} id The transaction id.
798 * @param {object} callback - User-defined callback object.
799 * @param {string} uri Fully qualified path of resource.
800 * @return {void}
801 */
802 uploadFile:function(id, callback, uri, postData){
803
804 // Each iframe has an id prefix of "yuiIO" followed
805 // by the unique transaction id.
806 var frameId = 'yuiIO' + id;
807 var io = document.getElementById(frameId);
808
809 // Initialize the HTML form properties in case they are
810 // not defined in the HTML form.
811 this._formNode.action = uri;
812 this._formNode.method = 'POST';
813 this._formNode.target = frameId;
814
815 if(this._formNode.encoding){
816 // IE does not respect property enctype for HTML forms.
817 // Instead use property encoding.
818 this._formNode.encoding = 'multipart/form-data';
819 }
820 else{
821 this._formNode.enctype = 'multipart/form-data';
822 }
823
824 if(postData){
825 var oElements = this.appendPostData(postData);
826 }
827
828 this._formNode.submit();
829
830 if(oElements && oElements.length > 0){
831 try
832 {
833 for(var i=0; i < oElements.length; i++){
834 this._formNode.removeChild(oElements[i]);
835 }
836 }
837 catch(e){}
838 }
839
840 // Reset HTML form status properties.
841 this.resetFormState();
842
843 // Create the upload callback handler that fires when the iframe
844 // receives the load event. Subsequently, the event handler is detached
845 // and the iframe removed from the document.
846
847 var uploadCallback = function()
848 {
849 var obj = {};
850 obj.tId = id;
851 obj.argument = callback.argument;
852
853 try
854 {
855 obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;
856 obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
857 }
858 catch(e){}
859
860 if(callback.upload){
861 if(!callback.scope){
862 callback.upload(obj);
863 }
864 else{
865 callback.upload.apply(callback.scope, [obj]);
866 }
867 }
868
869 if(YAHOO.util.Event){
870 YAHOO.util.Event.removeListener(io, "load", uploadCallback);
871 }
872 else if(window.detachEvent){
873 io.detachEvent('onload', uploadCallback);
874 }
875 else{
876 io.removeEventListener('load', uploadCallback, false);
877 }
878 setTimeout(function(){ document.body.removeChild(io); }, 100);
879 };
880
881
882 // Bind the onload handler to the iframe to detect the file upload response.
883 if(YAHOO.util.Event){
884 YAHOO.util.Event.addListener(io, "load", uploadCallback);
885 }
886 else if(window.attachEvent){
887 io.attachEvent('onload', uploadCallback);
888 }
889 else{
890 io.addEventListener('load', uploadCallback, false);
891 }
892 },
893
894 /**
895 * @description Method to terminate a transaction, if it has not reached readyState 4.
896 * @method abort
897 * @public
898 * @static
899 * @param {object} o The connection object returned by asyncRequest.
900 * @param {object} callback User-defined callback object.
901 * @param {string} isTimeout boolean to indicate if abort was a timeout.
902 * @return {boolean}
903 */
904 abort:function(o, callback, isTimeout)
905 {
906 if(this.isCallInProgress(o)){
907 o.conn.abort();
908 window.clearInterval(this._poll[o.tId]);
909 delete this._poll[o.tId];
910 if(isTimeout){
911 delete this._timeOut[o.tId];
912 }
913
914 this.handleTransactionResponse(o, callback, true);
915
916 return true;
917 }
918 else{
919 return false;
920 }
921 },
922
923 /**
924 * Public method to check if the transaction is still being processed.
925 *
926 * @method isCallInProgress
927 * @public
928 * @static
929 * @param {object} o The connection object returned by asyncRequest
930 * @return {boolean}
931 */
932 isCallInProgress:function(o)
933 {
934 // if the XHR object assigned to the transaction has not been dereferenced,
935 // then check its readyState status. Otherwise, return false.
936 if(o.conn){
937 return o.conn.readyState != 4 && o.conn.readyState != 0;
938 }
939 else{
940 //The XHR object has been destroyed.
941 return false;
942 }
943 },
944
945 /**
946 * @description Dereference the XHR instance and the connection object after the transaction is completed.
947 * @method releaseObject
948 * @private
949 * @static
950 * @param {object} o The connection object
951 * @return {void}
952 */
953 releaseObject:function(o)
954 {
955 //dereference the XHR instance.
956 o.conn = null;
957 //dereference the connection object.
958 o = null;
959 }
960}; \ No newline at end of file
diff --git a/frontend/beta/js/YUI/container.js b/frontend/beta/js/YUI/container.js
new file mode 100644
index 0000000..ec0f864
--- a/dev/null
+++ b/frontend/beta/js/YUI/container.js
@@ -0,0 +1,4561 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version 0.12.0
6*/
7
8/**
9* Config is a utility used within an Object to allow the implementer to maintain a list of local configuration properties and listen for changes to those properties dynamically using CustomEvent. The initial values are also maintained so that the configuration can be reset at any given point to its initial state.
10* @class YAHOO.util.Config
11* @constructor
12 * @param {Object} ownerThe owner Object to which this Config Object belongs
13*/
14YAHOO.util.Config = function(owner) {
15 if (owner) {
16 this.init(owner);
17 }
18};
19
20YAHOO.util.Config.prototype = {
21
22 /**
23 * Object reference to the owner of this Config Object
24 * @property owner
25 * @type Object
26 */
27 owner : null,
28
29 /**
30 * Boolean flag that specifies whether a queue is currently being executed
31 * @property queueInProgress
32 * @type Boolean
33 */
34 queueInProgress : false,
35
36
37 /**
38 * Validates that the value passed in is a Boolean.
39 * @method checkBoolean
40 * @param {Object} valThe value to validate
41 * @return {Boolean}true, if the value is valid
42 */
43 checkBoolean: function(val) {
44 if (typeof val == 'boolean') {
45 return true;
46 } else {
47 return false;
48 }
49 },
50
51 /**
52 * Validates that the value passed in is a number.
53 * @method checkNumber
54 * @param {Object} valThe value to validate
55 * @return {Boolean}true, if the value is valid
56 */
57 checkNumber: function(val) {
58 if (isNaN(val)) {
59 return false;
60 } else {
61 return true;
62 }
63 }
64};
65
66
67/**
68* Initializes the configuration Object and all of its local members.
69* @method init
70 * @param {Object} ownerThe owner Object to which this Config Object belongs
71*/
72YAHOO.util.Config.prototype.init = function(owner) {
73
74 this.owner = owner;
75
76 /**
77 * Object reference to the owner of this Config Object
78 * @event configChangedEvent
79 */
80 this.configChangedEvent = new YAHOO.util.CustomEvent("configChanged");
81
82 this.queueInProgress = false;
83
84 /* Private Members */
85
86 /**
87 * Maintains the local collection of configuration property objects and their specified values
88 * @property config
89 * @private
90 * @type Object
91 */
92 var config = {};
93
94 /**
95 * Maintains the local collection of configuration property objects as they were initially applied.
96 * This object is used when resetting a property.
97 * @property initialConfig
98 * @private
99 * @type Object
100 */
101 var initialConfig = {};
102
103 /**
104 * Maintains the local, normalized CustomEvent queue
105 * @property eventQueue
106 * @private
107 * @type Object
108 */
109 var eventQueue = [];
110
111 /**
112 * Fires a configuration property event using the specified value.
113 * @method fireEvent
114 * @private
115 * @param {String} key The configuration property's name
116 * @param {value} Object The value of the correct type for the property
117 */
118 var fireEvent = function( key, value ) {
119 key = key.toLowerCase();
120
121 var property = config[key];
122
123 if (typeof property != 'undefined' && property.event) {
124 property.event.fire(value);
125 }
126 };
127 /* End Private Members */
128
129 /**
130 * Adds a property to the Config Object's private config hash.
131 * @method addProperty
132 * @param {String} keyThe configuration property's name
133 * @param {Object} propertyObjectThe Object containing all of this property's arguments
134 */
135 this.addProperty = function( key, propertyObject ) {
136 key = key.toLowerCase();
137
138 config[key] = propertyObject;
139
140 propertyObject.event = new YAHOO.util.CustomEvent(key);
141 propertyObject.key = key;
142
143 if (propertyObject.handler) {
144 propertyObject.event.subscribe(propertyObject.handler, this.owner, true);
145 }
146
147 this.setProperty(key, propertyObject.value, true);
148
149 if (! propertyObject.suppressEvent) {
150 this.queueProperty(key, propertyObject.value);
151 }
152 };
153
154 /**
155 * Returns a key-value configuration map of the values currently set in the Config Object.
156 * @method getConfig
157 * @return {Object} The current config, represented in a key-value map
158 */
159 this.getConfig = function() {
160 var cfg = {};
161
162 for (var prop in config) {
163 var property = config[prop];
164 if (typeof property != 'undefined' && property.event) {
165 cfg[prop] = property.value;
166 }
167 }
168
169 return cfg;
170 };
171
172 /**
173 * Returns the value of specified property.
174 * @method getProperty
175 * @param {String} keyThe name of the property
176 * @return {Object} The value of the specified property
177 */
178 this.getProperty = function(key) {
179 key = key.toLowerCase();
180
181 var property = config[key];
182 if (typeof property != 'undefined' && property.event) {
183 return property.value;
184 } else {
185 return undefined;
186 }
187 };
188
189 /**
190 * Resets the specified property's value to its initial value.
191 * @method resetProperty
192 * @param {String} keyThe name of the property
193 * @return {Boolean} True is the property was reset, false if not
194 */
195 this.resetProperty = function(key) {
196 key = key.toLowerCase();
197
198 var property = config[key];
199 if (typeof property != 'undefined' && property.event) {
200 if (initialConfig[key] && initialConfig[key] != 'undefined'){
201 this.setProperty(key, initialConfig[key]);
202 }
203 return true;
204 } else {
205 return false;
206 }
207 };
208
209 /**
210 * Sets the value of a property. If the silent property is passed as true, the property's event will not be fired.
211 * @method setProperty
212 * @param {String} key The name of the property
213 * @param {String} value The value to set the property to
214 * @param {Boolean} silentWhether the value should be set silently, without firing the property event.
215 * @return {Boolean} True, if the set was successful, false if it failed.
216 */
217 this.setProperty = function(key, value, silent) {
218 key = key.toLowerCase();
219
220 if (this.queueInProgress && ! silent) {
221 this.queueProperty(key,value); // Currently running through a queue...
222 return true;
223 } else {
224 var property = config[key];
225 if (typeof property != 'undefined' && property.event) {
226 if (property.validator && ! property.validator(value)) { // validator
227 return false;
228 } else {
229 property.value = value;
230 if (! silent) {
231 fireEvent(key, value);
232 this.configChangedEvent.fire([key, value]);
233 }
234 return true;
235 }
236 } else {
237 return false;
238 }
239 }
240 };
241
242 /**
243 * Sets the value of a property and queues its event to execute. If the event is already scheduled to execute, it is
244 * moved from its current position to the end of the queue.
245 * @method queueProperty
246 * @param {String} keyThe name of the property
247 * @param {String} valueThe value to set the property to
248 * @return {Boolean} true, if the set was successful, false if it failed.
249 */
250 this.queueProperty = function(key, value) {
251 key = key.toLowerCase();
252
253 var property = config[key];
254
255 if (typeof property != 'undefined' && property.event) {
256 if (typeof value != 'undefined' && property.validator && ! property.validator(value)) { // validator
257 return false;
258 } else {
259
260 if (typeof value != 'undefined') {
261 property.value = value;
262 } else {
263 value = property.value;
264 }
265
266 var foundDuplicate = false;
267
268 for (var i=0;i<eventQueue.length;i++) {
269 var queueItem = eventQueue[i];
270
271 if (queueItem) {
272 var queueItemKey = queueItem[0];
273 var queueItemValue = queueItem[1];
274
275 if (queueItemKey.toLowerCase() == key) {
276 // found a dupe... push to end of queue, null current item, and break
277 eventQueue[i] = null;
278 eventQueue.push([key, (typeof value != 'undefined' ? value : queueItemValue)]);
279 foundDuplicate = true;
280 break;
281 }
282 }
283 }
284
285 if (! foundDuplicate && typeof value != 'undefined') { // this is a refire, or a new property in the queue
286 eventQueue.push([key, value]);
287 }
288 }
289
290 if (property.supercedes) {
291 for (var s=0;s<property.supercedes.length;s++) {
292 var supercedesCheck = property.supercedes[s];
293
294 for (var q=0;q<eventQueue.length;q++) {
295 var queueItemCheck = eventQueue[q];
296
297 if (queueItemCheck) {
298 var queueItemCheckKey = queueItemCheck[0];
299 var queueItemCheckValue = queueItemCheck[1];
300
301 if ( queueItemCheckKey.toLowerCase() == supercedesCheck.toLowerCase() ) {
302 eventQueue.push([queueItemCheckKey, queueItemCheckValue]);
303 eventQueue[q] = null;
304 break;
305 }
306 }
307 }
308 }
309 }
310
311 return true;
312 } else {
313 return false;
314 }
315 };
316
317 /**
318 * Fires the event for a property using the property's current value.
319 * @method refireEvent
320 * @param {String} keyThe name of the property
321 */
322 this.refireEvent = function(key) {
323 key = key.toLowerCase();
324
325 var property = config[key];
326 if (typeof property != 'undefined' && property.event && typeof property.value != 'undefined') {
327 if (this.queueInProgress) {
328 this.queueProperty(key);
329 } else {
330 fireEvent(key, property.value);
331 }
332 }
333 };
334
335 /**
336 * Applies a key-value Object literal to the configuration, replacing any existing values, and queueing the property events.
337 * Although the values will be set, fireQueue() must be called for their associated events to execute.
338 * @method applyConfig
339 * @param {Object} userConfigThe configuration Object literal
340 * @param {Boolean} init When set to true, the initialConfig will be set to the userConfig passed in, so that calling a reset will reset the properties to the passed values.
341 */
342 this.applyConfig = function(userConfig, init) {
343 if (init) {
344 initialConfig = userConfig;
345 }
346 for (var prop in userConfig) {
347 this.queueProperty(prop, userConfig[prop]);
348 }
349 };
350
351 /**
352 * Refires the events for all configuration properties using their current values.
353 * @method refresh
354 */
355 this.refresh = function() {
356 for (var prop in config) {
357 this.refireEvent(prop);
358 }
359 };
360
361 /**
362 * Fires the normalized list of queued property change events
363 * @method fireQueue
364 */
365 this.fireQueue = function() {
366 this.queueInProgress = true;
367 for (var i=0;i<eventQueue.length;i++) {
368 var queueItem = eventQueue[i];
369 if (queueItem) {
370 var key = queueItem[0];
371 var value = queueItem[1];
372
373 var property = config[key];
374 property.value = value;
375
376 fireEvent(key,value);
377 }
378 }
379
380 this.queueInProgress = false;
381 eventQueue = [];
382 };
383
384 /**
385 * Subscribes an external handler to the change event for any given property.
386 * @method subscribeToConfigEvent
387 * @param {String} key The property name
388 * @param {Function} handler The handler function to use subscribe to the property's event
389 * @param {Object} obj The Object to use for scoping the event handler (see CustomEvent documentation)
390 * @param {Boolean} overrideOptional. If true, will override "this" within the handler to map to the scope Object passed into the method.
391 * @return {Boolean} True, if the subscription was successful, otherwise false.
392 */
393 this.subscribeToConfigEvent = function(key, handler, obj, override) {
394 key = key.toLowerCase();
395
396 var property = config[key];
397 if (typeof property != 'undefined' && property.event) {
398 if (! YAHOO.util.Config.alreadySubscribed(property.event, handler, obj)) {
399 property.event.subscribe(handler, obj, override);
400 }
401 return true;
402 } else {
403 return false;
404 }
405 };
406
407 /**
408 * Unsubscribes an external handler from the change event for any given property.
409 * @method unsubscribeFromConfigEvent
410 * @param {String} key The property name
411 * @param {Function} handler The handler function to use subscribe to the property's event
412 * @param {Object} obj The Object to use for scoping the event handler (see CustomEvent documentation)
413 * @return {Boolean} True, if the unsubscription was successful, otherwise false.
414 */
415 this.unsubscribeFromConfigEvent = function(key, handler, obj) {
416 key = key.toLowerCase();
417
418 var property = config[key];
419 if (typeof property != 'undefined' && property.event) {
420 return property.event.unsubscribe(handler, obj);
421 } else {
422 return false;
423 }
424 };
425
426 /**
427 * Returns a string representation of the Config object
428 * @method toString
429 * @return {String}The Config object in string format.
430 */
431 this.toString = function() {
432 var output = "Config";
433 if (this.owner) {
434 output += " [" + this.owner.toString() + "]";
435 }
436 return output;
437 };
438
439 /**
440 * Returns a string representation of the Config object's current CustomEvent queue
441 * @method outputEventQueue
442 * @return {String}The string list of CustomEvents currently queued for execution
443 */
444 this.outputEventQueue = function() {
445 var output = "";
446 for (var q=0;q<eventQueue.length;q++) {
447 var queueItem = eventQueue[q];
448 if (queueItem) {
449 output += queueItem[0] + "=" + queueItem[1] + ", ";
450 }
451 }
452 return output;
453 };
454};
455
456/**
457* Checks to determine if a particular function/Object pair are already subscribed to the specified CustomEvent
458* @method YAHOO.util.Config.alreadySubscribed
459* @static
460 * @param {YAHOO.util.CustomEvent} evtThe CustomEvent for which to check the subscriptions
461 * @param {Function} fnThe function to look for in the subscribers list
462 * @param {Object} objThe execution scope Object for the subscription
463 * @return {Boolean}true, if the function/Object pair is already subscribed to the CustomEvent passed in
464*/
465YAHOO.util.Config.alreadySubscribed = function(evt, fn, obj) {
466 for (var e=0;e<evt.subscribers.length;e++) {
467 var subsc = evt.subscribers[e];
468 if (subsc && subsc.obj == obj && subsc.fn == fn) {
469 return true;
470 }
471 }
472 return false;
473};
474
475/**
476* The Container family of components is designed to enable developers to create different kinds of content-containing modules on the web. Module and Overlay are the most basic containers, and they can be used directly or extended to build custom containers. Also part of the Container family are four UI controls that extend Module and Overlay: Tooltip, Panel, Dialog, and SimpleDialog.
477* @module Container
478* @requires yahoo,dom,event,dragdrop,animation
479*/
480
481/**
482* Module is a JavaScript representation of the Standard Module Format. Standard Module Format is a simple standard for markup containers where child nodes representing the header, body, and footer of the content are denoted using the CSS classes "hd", "bd", and "ft" respectively. Module is the base class for all other classes in the YUI Container package.
483* @class Module
484* @namespace YAHOO.widget
485* @constructor
486 * @param {String} el The element ID representing the Module <em>OR</em>
487 * @param {HTMLElement} el The element representing the Module
488 * @param {Object} userConfigThe configuration Object literal containing the configuration that should be set for this module. See configuration documentation for more details.
489*/
490YAHOO.widget.Module = function(el, userConfig) {
491 if (el) {
492 this.init(el, userConfig);
493 }
494};
495
496/**
497* Constant representing the prefix path to use for non-secure images
498* @property YAHOO.widget.Module.IMG_ROOT
499* @static
500* @final
501* @type String
502*/
503YAHOO.widget.Module.IMG_ROOT = "http://us.i1.yimg.com/us.yimg.com/i/";
504
505/**
506* Constant representing the prefix path to use for securely served images
507* @property YAHOO.widget.Module.IMG_ROOT_SSL
508* @static
509* @final
510* @type String
511*/
512YAHOO.widget.Module.IMG_ROOT_SSL = "https://a248.e.akamai.net/sec.yimg.com/i/";
513
514/**
515* Constant for the default CSS class name that represents a Module
516* @property YAHOO.widget.Module.CSS_MODULE
517* @static
518* @final
519* @type String
520*/
521YAHOO.widget.Module.CSS_MODULE = "module";
522
523/**
524* Constant representing the module header
525* @property YAHOO.widget.Module.CSS_HEADER
526* @static
527* @final
528* @type String
529*/
530YAHOO.widget.Module.CSS_HEADER = "hd";
531
532/**
533* Constant representing the module body
534* @property YAHOO.widget.Module.CSS_BODY
535* @static
536* @final
537* @type String
538*/
539YAHOO.widget.Module.CSS_BODY = "bd";
540
541/**
542* Constant representing the module footer
543* @property YAHOO.widget.Module.CSS_FOOTER
544* @static
545* @final
546* @type String
547*/
548YAHOO.widget.Module.CSS_FOOTER = "ft";
549
550/**
551* Constant representing the url for the "src" attribute of the iframe used to monitor changes to the browser's base font size
552* @property YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL
553* @static
554* @final
555* @type String
556*/
557YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL = "javascript:false;";
558
559YAHOO.widget.Module.prototype = {
560 /**
561 * The class's constructor function
562 * @property contructor
563 * @type Function
564 */
565 constructor : YAHOO.widget.Module,
566
567 /**
568 * The main module element that contains the header, body, and footer
569 * @property element
570 * @type HTMLElement
571 */
572 element : null,
573
574 /**
575 * The header element, denoted with CSS class "hd"
576 * @property header
577 * @type HTMLElement
578 */
579 header : null,
580
581 /**
582 * The body element, denoted with CSS class "bd"
583 * @property body
584 * @type HTMLElement
585 */
586 body : null,
587
588 /**
589 * The footer element, denoted with CSS class "ft"
590 * @property footer
591 * @type HTMLElement
592 */
593 footer : null,
594
595 /**
596 * The id of the element
597 * @property id
598 * @type String
599 */
600 id : null,
601
602 /**
603 * The String representing the image root
604 * @property imageRoot
605 * @type String
606 */
607 imageRoot : YAHOO.widget.Module.IMG_ROOT,
608
609 /**
610 * Initializes the custom events for Module which are fired automatically at appropriate times by the Module class.
611 * @method initEvents
612 */
613 initEvents : function() {
614
615 /**
616 * CustomEvent fired prior to class initalization.
617 * @event beforeInitEvent
618 * @param {class} classRefclass reference of the initializing class, such as this.beforeInitEvent.fire(YAHOO.widget.Module)
619 */
620 this.beforeInitEvent = new YAHOO.util.CustomEvent("beforeInit");
621
622 /**
623 * CustomEvent fired after class initalization.
624 * @event initEvent
625 * @param {class} classRefclass reference of the initializing class, such as this.beforeInitEvent.fire(YAHOO.widget.Module)
626 */
627 this.initEvent = new YAHOO.util.CustomEvent("init");
628
629 /**
630 * CustomEvent fired when the Module is appended to the DOM
631 * @event appendEvent
632 */
633 this.appendEvent = new YAHOO.util.CustomEvent("append");
634
635 /**
636 * CustomEvent fired before the Module is rendered
637 * @event beforeRenderEvent
638 */
639 this.beforeRenderEvent = new YAHOO.util.CustomEvent("beforeRender");
640
641 /**
642 * CustomEvent fired after the Module is rendered
643 * @event renderEvent
644 */
645 this.renderEvent = new YAHOO.util.CustomEvent("render");
646
647 /**
648 * CustomEvent fired when the header content of the Module is modified
649 * @event changeHeaderEvent
650 * @param {String/HTMLElement} contentString/element representing the new header content
651 */
652 this.changeHeaderEvent = new YAHOO.util.CustomEvent("changeHeader");
653
654 /**
655 * CustomEvent fired when the body content of the Module is modified
656 * @event changeBodyEvent
657 * @param {String/HTMLElement} contentString/element representing the new body content
658 */
659 this.changeBodyEvent = new YAHOO.util.CustomEvent("changeBody");
660
661 /**
662 * CustomEvent fired when the footer content of the Module is modified
663 * @event changeFooterEvent
664 * @param {String/HTMLElement} contentString/element representing the new footer content
665 */
666 this.changeFooterEvent = new YAHOO.util.CustomEvent("changeFooter");
667
668 /**
669 * CustomEvent fired when the content of the Module is modified
670 * @event changeContentEvent
671 */
672 this.changeContentEvent = new YAHOO.util.CustomEvent("changeContent");
673
674 /**
675 * CustomEvent fired when the Module is destroyed
676 * @event destroyEvent
677 */
678 this.destroyEvent = new YAHOO.util.CustomEvent("destroy");
679
680 /**
681 * CustomEvent fired before the Module is shown
682 * @event beforeShowEvent
683 */
684 this.beforeShowEvent = new YAHOO.util.CustomEvent("beforeShow");
685
686 /**
687 * CustomEvent fired after the Module is shown
688 * @event showEvent
689 */
690 this.showEvent = new YAHOO.util.CustomEvent("show");
691
692 /**
693 * CustomEvent fired before the Module is hidden
694 * @event beforeHideEvent
695 */
696 this.beforeHideEvent = new YAHOO.util.CustomEvent("beforeHide");
697
698 /**
699 * CustomEvent fired after the Module is hidden
700 * @event hideEvent
701 */
702 this.hideEvent = new YAHOO.util.CustomEvent("hide");
703 },
704
705 /**
706 * String representing the current user-agent platform
707 * @property platform
708 * @type String
709 */
710 platform : function() {
711 var ua = navigator.userAgent.toLowerCase();
712 if (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1) {
713 return "windows";
714 } else if (ua.indexOf("macintosh") != -1) {
715 return "mac";
716 } else {
717 return false;
718 }
719 }(),
720
721 /**
722 * String representing the current user-agent browser
723 * @property browser
724 * @type String
725 */
726 browser : function() {
727 var ua = navigator.userAgent.toLowerCase();
728 if (ua.indexOf('opera')!=-1) { // Opera (check first in case of spoof)
729 return 'opera';
730 } else if (ua.indexOf('msie 7')!=-1) { // IE7
731 return 'ie7';
732 } else if (ua.indexOf('msie') !=-1) { // IE
733 return 'ie';
734 } else if (ua.indexOf('safari')!=-1) { // Safari (check before Gecko because it includes "like Gecko")
735 return 'safari';
736 } else if (ua.indexOf('gecko') != -1) { // Gecko
737 return 'gecko';
738 } else {
739 return false;
740 }
741 }(),
742
743 /**
744 * Boolean representing whether or not the current browsing context is secure (https)
745 * @property isSecure
746 * @type Boolean
747 */
748 isSecure : function() {
749 if (window.location.href.toLowerCase().indexOf("https") === 0) {
750 return true;
751 } else {
752 return false;
753 }
754 }(),
755
756 /**
757 * Initializes the custom events for Module which are fired automatically at appropriate times by the Module class.
758 */
759 initDefaultConfig : function() {
760 // Add properties //
761
762 /**
763 * Specifies whether the Module is visible on the page.
764 * @config visible
765 * @type Boolean
766 * @default true
767 */
768 this.cfg.addProperty("visible", { value:true, handler:this.configVisible, validator:this.cfg.checkBoolean } );
769
770 /**
771 * Object or array of objects representing the ContainerEffect classes that are active for animating the container.
772 * @config effect
773 * @type Object
774 * @default null
775 */
776 this.cfg.addProperty("effect", { suppressEvent:true, supercedes:["visible"] } );
777
778 /**
779 * Specifies whether to create a special proxy iframe to monitor for user font resizing in the document
780 * @config monitorresize
781 * @type Boolean
782 * @default true
783 */
784 this.cfg.addProperty("monitorresize", { value:true, handler:this.configMonitorResize } );
785 },
786
787 /**
788 * The Module class's initialization method, which is executed for Module and all of its subclasses. This method is automatically called by the constructor, and sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
789 * @method init
790 * @param {String} elThe element ID representing the Module <em>OR</em>
791 * @param {HTMLElement} elThe element representing the Module
792 * @param {Object} userConfigThe configuration Object literal containing the configuration that should be set for this module. See configuration documentation for more details.
793 */
794 init : function(el, userConfig) {
795
796 this.initEvents();
797
798 this.beforeInitEvent.fire(YAHOO.widget.Module);
799
800 /**
801 * The Module's Config object used for monitoring configuration properties.
802 * @property cfg
803 * @type YAHOO.util.Config
804 */
805 this.cfg = new YAHOO.util.Config(this);
806
807 if (this.isSecure) {
808 this.imageRoot = YAHOO.widget.Module.IMG_ROOT_SSL;
809 }
810
811 if (typeof el == "string") {
812 var elId = el;
813
814 el = document.getElementById(el);
815 if (! el) {
816 el = document.createElement("DIV");
817 el.id = elId;
818 }
819 }
820
821 this.element = el;
822
823 if (el.id) {
824 this.id = el.id;
825 }
826
827 var childNodes = this.element.childNodes;
828
829 if (childNodes) {
830 for (var i=0;i<childNodes.length;i++) {
831 var child = childNodes[i];
832 switch (child.className) {
833 case YAHOO.widget.Module.CSS_HEADER:
834 this.header = child;
835 break;
836 case YAHOO.widget.Module.CSS_BODY:
837 this.body = child;
838 break;
839 case YAHOO.widget.Module.CSS_FOOTER:
840 this.footer = child;
841 break;
842 }
843 }
844 }
845
846 this.initDefaultConfig();
847
848 YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Module.CSS_MODULE);
849
850 if (userConfig) {
851 this.cfg.applyConfig(userConfig, true);
852 }
853
854 // Subscribe to the fireQueue() method of Config so that any queued configuration changes are
855 // excecuted upon render of the Module
856 if (! YAHOO.util.Config.alreadySubscribed(this.renderEvent, this.cfg.fireQueue, this.cfg)) {
857 this.renderEvent.subscribe(this.cfg.fireQueue, this.cfg, true);
858 }
859
860 this.initEvent.fire(YAHOO.widget.Module);
861 },
862
863 /**
864 * Initialized an empty IFRAME that is placed out of the visible area that can be used to detect text resize.
865 * @method initResizeMonitor
866 */
867 initResizeMonitor : function() {
868
869 if(this.browser != "opera") {
870
871 var resizeMonitor = document.getElementById("_yuiResizeMonitor");
872
873 if (! resizeMonitor) {
874
875 resizeMonitor = document.createElement("iframe");
876
877 var bIE = (this.browser.indexOf("ie") === 0);
878
879 if(this.isSecure &&
880 YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL &&
881 bIE) {
882
883 resizeMonitor.src =
884 YAHOO.widget.Module.RESIZE_MONITOR_SECURE_URL;
885
886 }
887
888 resizeMonitor.id = "_yuiResizeMonitor";
889 resizeMonitor.style.visibility = "hidden";
890
891 document.body.appendChild(resizeMonitor);
892
893 resizeMonitor.style.width = "10em";
894 resizeMonitor.style.height = "10em";
895 resizeMonitor.style.position = "absolute";
896
897 var nLeft = -1 * resizeMonitor.offsetWidth,
898 nTop = -1 * resizeMonitor.offsetHeight;
899
900 resizeMonitor.style.top = nTop + "px";
901 resizeMonitor.style.left = nLeft + "px";
902 resizeMonitor.style.borderStyle = "none";
903 resizeMonitor.style.borderWidth = "0";
904 YAHOO.util.Dom.setStyle(resizeMonitor, "opacity", "0");
905
906 resizeMonitor.style.visibility = "visible";
907
908 if(!bIE) {
909
910 var doc = resizeMonitor.contentWindow.document;
911
912 doc.open();
913 doc.close();
914
915 }
916
917 }
918
919 if(resizeMonitor && resizeMonitor.contentWindow) {
920
921 this.resizeMonitor = resizeMonitor;
922
923 YAHOO.util.Event.addListener(this.resizeMonitor.contentWindow, "resize", this.onDomResize, this, true);
924
925 }
926
927 }
928
929 },
930
931 /**
932 * Event handler fired when the resize monitor element is resized.
933 * @method onDomResize
934 * @param {DOMEvent} eThe DOM resize event
935 * @param {Object} objThe scope object passed to the handler
936 */
937 onDomResize : function(e, obj) {
938
939 var nLeft = -1 * this.resizeMonitor.offsetWidth,
940 nTop = -1 * this.resizeMonitor.offsetHeight;
941
942 this.resizeMonitor.style.top = nTop + "px";
943 this.resizeMonitor.style.left = nLeft + "px";
944
945 },
946
947 /**
948 * Sets the Module's header content to the HTML specified, or appends the passed element to the header. If no header is present, one will be automatically created.
949 * @method setHeader
950 * @param {String} headerContentThe HTML used to set the header <em>OR</em>
951 * @param {HTMLElement} headerContentThe HTMLElement to append to the header
952 */
953 setHeader : function(headerContent) {
954 if (! this.header) {
955 this.header = document.createElement("DIV");
956 this.header.className = YAHOO.widget.Module.CSS_HEADER;
957 }
958
959 if (typeof headerContent == "string") {
960 this.header.innerHTML = headerContent;
961 } else {
962 this.header.innerHTML = "";
963 this.header.appendChild(headerContent);
964 }
965
966 this.changeHeaderEvent.fire(headerContent);
967 this.changeContentEvent.fire();
968 },
969
970 /**
971 * Appends the passed element to the header. If no header is present, one will be automatically created.
972 * @method appendToHeader
973 * @param {HTMLElement} elementThe element to append to the header
974 */
975 appendToHeader : function(element) {
976 if (! this.header) {
977 this.header = document.createElement("DIV");
978 this.header.className = YAHOO.widget.Module.CSS_HEADER;
979 }
980
981 this.header.appendChild(element);
982 this.changeHeaderEvent.fire(element);
983 this.changeContentEvent.fire();
984 },
985
986 /**
987 * Sets the Module's body content to the HTML specified, or appends the passed element to the body. If no body is present, one will be automatically created.
988 * @method setBody
989 * @param {String} bodyContentThe HTML used to set the body <em>OR</em>
990 * @param {HTMLElement} bodyContentThe HTMLElement to append to the body
991 */
992 setBody : function(bodyContent) {
993 if (! this.body) {
994 this.body = document.createElement("DIV");
995 this.body.className = YAHOO.widget.Module.CSS_BODY;
996 }
997
998 if (typeof bodyContent == "string")
999 {
1000 this.body.innerHTML = bodyContent;
1001 } else {
1002 this.body.innerHTML = "";
1003 this.body.appendChild(bodyContent);
1004 }
1005
1006 this.changeBodyEvent.fire(bodyContent);
1007 this.changeContentEvent.fire();
1008 },
1009
1010 /**
1011 * Appends the passed element to the body. If no body is present, one will be automatically created.
1012 * @method appendToBody
1013 * @param {HTMLElement} elementThe element to append to the body
1014 */
1015 appendToBody : function(element) {
1016 if (! this.body) {
1017 this.body = document.createElement("DIV");
1018 this.body.className = YAHOO.widget.Module.CSS_BODY;
1019 }
1020
1021 this.body.appendChild(element);
1022 this.changeBodyEvent.fire(element);
1023 this.changeContentEvent.fire();
1024 },
1025
1026 /**
1027 * Sets the Module's footer content to the HTML specified, or appends the passed element to the footer. If no footer is present, one will be automatically created.
1028 * @method setFooter
1029 * @param {String} footerContentThe HTML used to set the footer <em>OR</em>
1030 * @param {HTMLElement} footerContentThe HTMLElement to append to the footer
1031 */
1032 setFooter : function(footerContent) {
1033 if (! this.footer) {
1034 this.footer = document.createElement("DIV");
1035 this.footer.className = YAHOO.widget.Module.CSS_FOOTER;
1036 }
1037
1038 if (typeof footerContent == "string") {
1039 this.footer.innerHTML = footerContent;
1040 } else {
1041 this.footer.innerHTML = "";
1042 this.footer.appendChild(footerContent);
1043 }
1044
1045 this.changeFooterEvent.fire(footerContent);
1046 this.changeContentEvent.fire();
1047 },
1048
1049 /**
1050 * Appends the passed element to the footer. If no footer is present, one will be automatically created.
1051 * @method appendToFooter
1052 * @param {HTMLElement} elementThe element to append to the footer
1053 */
1054 appendToFooter : function(element) {
1055 if (! this.footer) {
1056 this.footer = document.createElement("DIV");
1057 this.footer.className = YAHOO.widget.Module.CSS_FOOTER;
1058 }
1059
1060 this.footer.appendChild(element);
1061 this.changeFooterEvent.fire(element);
1062 this.changeContentEvent.fire();
1063 },
1064
1065 /**
1066 * Renders the Module by inserting the elements that are not already in the main Module into their correct places. Optionally appends the Module to the specified node prior to the render's execution. NOTE: For Modules without existing markup, the appendToNode argument is REQUIRED. If this argument is ommitted and the current element is not present in the document, the function will return false, indicating that the render was a failure.
1067 * @method render
1068 * @param {String} appendToNodeThe element id to which the Module should be appended to prior to rendering <em>OR</em>
1069 * @param {HTMLElement} appendToNodeThe element to which the Module should be appended to prior to rendering
1070 * @param {HTMLElement} moduleElementOPTIONAL. The element that represents the actual Standard Module container.
1071 * @return {Boolean} Success or failure of the render
1072 */
1073 render : function(appendToNode, moduleElement) {
1074 this.beforeRenderEvent.fire();
1075
1076 if (! moduleElement) {
1077 moduleElement = this.element;
1078 }
1079
1080 var me = this;
1081 var appendTo = function(element) {
1082 if (typeof element == "string") {
1083 element = document.getElementById(element);
1084 }
1085
1086 if (element) {
1087 element.appendChild(me.element);
1088 me.appendEvent.fire();
1089 }
1090 };
1091
1092 if (appendToNode) {
1093 appendTo(appendToNode);
1094 } else { // No node was passed in. If the element is not pre-marked up, this fails
1095 if (! YAHOO.util.Dom.inDocument(this.element)) {
1096 return false;
1097 }
1098 }
1099
1100 // Need to get everything into the DOM if it isn't already
1101
1102 if (this.header && ! YAHOO.util.Dom.inDocument(this.header)) {
1103 // There is a header, but it's not in the DOM yet... need to add it
1104 var firstChild = moduleElement.firstChild;
1105 if (firstChild) { // Insert before first child if exists
1106 moduleElement.insertBefore(this.header, firstChild);
1107 } else { // Append to empty body because there are no children
1108 moduleElement.appendChild(this.header);
1109 }
1110 }
1111
1112 if (this.body && ! YAHOO.util.Dom.inDocument(this.body)) {
1113 // There is a body, but it's not in the DOM yet... need to add it
1114 if (this.footer && YAHOO.util.Dom.isAncestor(this.moduleElement, this.footer)) { // Insert before footer if exists in DOM
1115 moduleElement.insertBefore(this.body, this.footer);
1116 } else { // Append to element because there is no footer
1117 moduleElement.appendChild(this.body);
1118 }
1119 }
1120
1121 if (this.footer && ! YAHOO.util.Dom.inDocument(this.footer)) {
1122 // There is a footer, but it's not in the DOM yet... need to add it
1123 moduleElement.appendChild(this.footer);
1124 }
1125
1126 this.renderEvent.fire();
1127 return true;
1128 },
1129
1130 /**
1131 * Removes the Module element from the DOM and sets all child elements to null.
1132 * @method destroy
1133 */
1134 destroy : function() {
1135 if (this.element) {
1136 var parent = this.element.parentNode;
1137 }
1138 if (parent) {
1139 parent.removeChild(this.element);
1140 }
1141
1142 this.element = null;
1143 this.header = null;
1144 this.body = null;
1145 this.footer = null;
1146
1147 this.destroyEvent.fire();
1148 },
1149
1150 /**
1151 * Shows the Module element by setting the visible configuration property to true. Also fires two events: beforeShowEvent prior to the visibility change, and showEvent after.
1152 * @method show
1153 */
1154 show : function() {
1155 this.cfg.setProperty("visible", true);
1156 },
1157
1158 /**
1159 * Hides the Module element by setting the visible configuration property to false. Also fires two events: beforeHideEvent prior to the visibility change, and hideEvent after.
1160 * @method hide
1161 */
1162 hide : function() {
1163 this.cfg.setProperty("visible", false);
1164 },
1165
1166 // BUILT-IN EVENT HANDLERS FOR MODULE //
1167
1168 /**
1169 * Default event handler for changing the visibility property of a Module. By default, this is achieved by switching the "display" style between "block" and "none".
1170 * This method is responsible for firing showEvent and hideEvent.
1171 * @param {String} typeThe CustomEvent type (usually the property name)
1172 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1173 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1174 * @method configVisible
1175 */
1176 configVisible : function(type, args, obj) {
1177 var visible = args[0];
1178 if (visible) {
1179 this.beforeShowEvent.fire();
1180 YAHOO.util.Dom.setStyle(this.element, "display", "block");
1181 this.showEvent.fire();
1182 } else {
1183 this.beforeHideEvent.fire();
1184 YAHOO.util.Dom.setStyle(this.element, "display", "none");
1185 this.hideEvent.fire();
1186 }
1187 },
1188
1189 /**
1190 * Default event handler for the "monitorresize" configuration property
1191 * @param {String} typeThe CustomEvent type (usually the property name)
1192 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1193 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1194 * @method configMonitorResize
1195 */
1196 configMonitorResize : function(type, args, obj) {
1197 var monitor = args[0];
1198 if (monitor) {
1199 this.initResizeMonitor();
1200 } else {
1201 YAHOO.util.Event.removeListener(this.resizeMonitor, "resize", this.onDomResize);
1202 this.resizeMonitor = null;
1203 }
1204 }
1205};
1206
1207/**
1208* Returns a String representation of the Object.
1209* @method toString
1210 * @return {String}The string representation of the Module
1211*/
1212YAHOO.widget.Module.prototype.toString = function() {
1213 return "Module " + this.id;
1214};
1215
1216/**
1217* Overlay is a Module that is absolutely positioned above the page flow. It has convenience methods for positioning and sizing, as well as options for controlling zIndex and constraining the Overlay's position to the current visible viewport. Overlay also contains a dynamicly generated IFRAME which is placed beneath it for Internet Explorer 6 and 5.x so that it will be properly rendered above SELECT elements.
1218* @class Overlay
1219* @namespace YAHOO.widget
1220* @extends YAHOO.widget.Module
1221 * @param {String} elThe element ID representing the Overlay <em>OR</em>
1222 * @param {HTMLElement} elThe element representing the Overlay
1223 * @param {Object} userConfigThe configuration object literal containing 10/23/2006the configuration that should be set for this Overlay. See configuration documentation for more details.
1224* @constructor
1225*/
1226YAHOO.widget.Overlay = function(el, userConfig) {
1227 YAHOO.widget.Overlay.superclass.constructor.call(this, el, userConfig);
1228};
1229
1230YAHOO.extend(YAHOO.widget.Overlay, YAHOO.widget.Module);
1231
1232/**
1233* The URL that will be placed in the iframe
1234* @property YAHOO.widget.Overlay.IFRAME_SRC
1235* @static
1236* @final
1237* @type String
1238*/
1239YAHOO.widget.Overlay.IFRAME_SRC = "javascript:false;"
1240
1241/**
1242* Constant representing the top left corner of an element, used for configuring the context element alignment
1243* @property YAHOO.widget.Overlay.TOP_LEFT
1244* @static
1245* @final
1246* @type String
1247*/
1248YAHOO.widget.Overlay.TOP_LEFT = "tl";
1249
1250/**
1251* Constant representing the top right corner of an element, used for configuring the context element alignment
1252* @property YAHOO.widget.Overlay.TOP_RIGHT
1253* @static
1254* @final
1255* @type String
1256*/
1257YAHOO.widget.Overlay.TOP_RIGHT = "tr";
1258
1259/**
1260* Constant representing the top bottom left corner of an element, used for configuring the context element alignment
1261* @property YAHOO.widget.Overlay.BOTTOM_LEFT
1262* @static
1263* @final
1264* @type String
1265*/
1266YAHOO.widget.Overlay.BOTTOM_LEFT = "bl";
1267
1268/**
1269* Constant representing the bottom right corner of an element, used for configuring the context element alignment
1270* @property YAHOO.widget.Overlay.BOTTOM_RIGHT
1271* @static
1272* @final
1273* @type String
1274*/
1275YAHOO.widget.Overlay.BOTTOM_RIGHT = "br";
1276
1277/**
1278* Constant representing the default CSS class used for an Overlay
1279* @property YAHOO.widget.Overlay.CSS_OVERLAY
1280* @static
1281* @final
1282* @type String
1283*/
1284YAHOO.widget.Overlay.CSS_OVERLAY = "overlay";
1285
1286/**
1287* The Overlay initialization method, which is executed for Overlay and all of its subclasses. This method is automatically called by the constructor, and sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
1288* @method init
1289 * @param {String} elThe element ID representing the Overlay <em>OR</em>
1290 * @param {HTMLElement} elThe element representing the Overlay
1291 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this Overlay. See configuration documentation for more details.
1292*/
1293YAHOO.widget.Overlay.prototype.init = function(el, userConfig) {
1294 YAHOO.widget.Overlay.superclass.init.call(this, el/*, userConfig*/); // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level
1295
1296 this.beforeInitEvent.fire(YAHOO.widget.Overlay);
1297
1298 YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Overlay.CSS_OVERLAY);
1299
1300 if (userConfig) {
1301 this.cfg.applyConfig(userConfig, true);
1302 }
1303
1304 if (this.platform == "mac" && this.browser == "gecko") {
1305 if (! YAHOO.util.Config.alreadySubscribed(this.showEvent,this.showMacGeckoScrollbars,this)) {
1306 this.showEvent.subscribe(this.showMacGeckoScrollbars,this,true);
1307 }
1308 if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent,this.hideMacGeckoScrollbars,this)) {
1309 this.hideEvent.subscribe(this.hideMacGeckoScrollbars,this,true);
1310 }
1311 }
1312
1313 this.initEvent.fire(YAHOO.widget.Overlay);
1314};
1315
1316/**
1317* Initializes the custom events for Overlay which are fired automatically at appropriate times by the Overlay class.
1318* @method initEvents
1319*/
1320YAHOO.widget.Overlay.prototype.initEvents = function() {
1321 YAHOO.widget.Overlay.superclass.initEvents.call(this);
1322
1323 /**
1324 * CustomEvent fired before the Overlay is moved.
1325 * @event beforeMoveEvent
1326 * @param {Number} xx coordinate
1327 * @param {Number} yy coordinate
1328 */
1329 this.beforeMoveEvent = new YAHOO.util.CustomEvent("beforeMove", this);
1330
1331 /**
1332 * CustomEvent fired after the Overlay is moved.
1333 * @event moveEvent
1334 * @param {Number} xx coordinate
1335 * @param {Number} yy coordinate
1336 */
1337 this.moveEvent = new YAHOO.util.CustomEvent("move", this);
1338};
1339
1340/**
1341* Initializes the class's configurable properties which can be changed using the Overlay's Config object (cfg).
1342* @method initDefaultConfig
1343*/
1344YAHOO.widget.Overlay.prototype.initDefaultConfig = function() {
1345 YAHOO.widget.Overlay.superclass.initDefaultConfig.call(this);
1346
1347 // Add overlay config properties //
1348
1349 /**
1350 * The absolute x-coordinate position of the Overlay
1351 * @config x
1352 * @type Number
1353 * @default null
1354 */
1355 this.cfg.addProperty("x", { handler:this.configX, validator:this.cfg.checkNumber, suppressEvent:true, supercedes:["iframe"] } );
1356
1357 /**
1358 * The absolute y-coordinate position of the Overlay
1359 * @config y
1360 * @type Number
1361 * @default null
1362 */
1363 this.cfg.addProperty("y", { handler:this.configY, validator:this.cfg.checkNumber, suppressEvent:true, supercedes:["iframe"] } );
1364
1365 /**
1366 * An array with the absolute x and y positions of the Overlay
1367 * @config xy
1368 * @type Number[]
1369 * @default null
1370 */
1371 this.cfg.addProperty("xy",{ handler:this.configXY, suppressEvent:true, supercedes:["iframe"] } );
1372
1373 /**
1374 * The array of context arguments for context-sensitive positioning. The format is: [id or element, element corner, context corner]. For example, setting this property to ["img1", "tl", "bl"] would align the Overlay's top left corner to the context element's bottom left corner.
1375 * @config context
1376 * @type Array
1377 * @default null
1378 */
1379 this.cfg.addProperty("context",{ handler:this.configContext, suppressEvent:true, supercedes:["iframe"] } );
1380
1381 /**
1382 * True if the Overlay should be anchored to the center of the viewport.
1383 * @config fixedcenter
1384 * @type Boolean
1385 * @default false
1386 */
1387 this.cfg.addProperty("fixedcenter", { value:false, handler:this.configFixedCenter, validator:this.cfg.checkBoolean, supercedes:["iframe","visible"] } );
1388
1389 /**
1390 * CSS width of the Overlay.
1391 * @config width
1392 * @type String
1393 * @default null
1394 */
1395 this.cfg.addProperty("width", { handler:this.configWidth, suppressEvent:true, supercedes:["iframe"] } );
1396
1397 /**
1398 * CSS height of the Overlay.
1399 * @config height
1400 * @type String
1401 * @default null
1402 */
1403 this.cfg.addProperty("height", { handler:this.configHeight, suppressEvent:true, supercedes:["iframe"] } );
1404
1405 /**
1406 * CSS z-index of the Overlay.
1407 * @config zIndex
1408 * @type Number
1409 * @default null
1410 */
1411 this.cfg.addProperty("zIndex", { value:null, handler:this.configzIndex } );
1412
1413 /**
1414 * True if the Overlay should be prevented from being positioned out of the viewport.
1415 * @config constraintoviewport
1416 * @type Boolean
1417 * @default false
1418 */
1419 this.cfg.addProperty("constraintoviewport", { value:false, handler:this.configConstrainToViewport, validator:this.cfg.checkBoolean, supercedes:["iframe","x","y","xy"] } );
1420
1421 /**
1422 * True if the Overlay should have an IFRAME shim (for correcting the select z-index bug in IE6 and below).
1423 * @config iframe
1424 * @type Boolean
1425 * @default true for IE6 and below, false for all others
1426 */
1427 this.cfg.addProperty("iframe", { value:(this.browser == "ie" ? true : false), handler:this.configIframe, validator:this.cfg.checkBoolean, supercedes:["zIndex"] } );
1428};
1429
1430/**
1431* Moves the Overlay to the specified position. This function is identical to calling this.cfg.setProperty("xy", [x,y]);
1432* @method moveTo
1433 * @param {Number} xThe Overlay's new x position
1434 * @param {Number} yThe Overlay's new y position
1435*/
1436YAHOO.widget.Overlay.prototype.moveTo = function(x, y) {
1437 this.cfg.setProperty("xy",[x,y]);
1438};
1439
1440/**
1441* Adds a special CSS class to the Overlay when Mac/Gecko is in use, to work around a Gecko bug where
1442* scrollbars cannot be hidden. See https://bugzilla.mozilla.org/show_bug.cgi?id=187435
1443* @method hideMacGeckoScrollbars
1444*/
1445YAHOO.widget.Overlay.prototype.hideMacGeckoScrollbars = function() {
1446 YAHOO.util.Dom.removeClass(this.element, "show-scrollbars");
1447 YAHOO.util.Dom.addClass(this.element, "hide-scrollbars");
1448};
1449
1450/**
1451* Removes a special CSS class from the Overlay when Mac/Gecko is in use, to work around a Gecko bug where
1452* scrollbars cannot be hidden. See https://bugzilla.mozilla.org/show_bug.cgi?id=187435
1453* @method showMacGeckoScrollbars
1454*/
1455YAHOO.widget.Overlay.prototype.showMacGeckoScrollbars = function() {
1456 YAHOO.util.Dom.removeClass(this.element, "hide-scrollbars");
1457 YAHOO.util.Dom.addClass(this.element, "show-scrollbars");
1458};
1459
1460// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //
1461
1462/**
1463* The default event handler fired when the "visible" property is changed. This method is responsible for firing showEvent and hideEvent.
1464* @method configVisible
1465 * @param {String} typeThe CustomEvent type (usually the property name)
1466 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1467 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1468*/
1469YAHOO.widget.Overlay.prototype.configVisible = function(type, args, obj) {
1470 var visible = args[0];
1471
1472 var currentVis = YAHOO.util.Dom.getStyle(this.element, "visibility");
1473
1474 if (currentVis == "inherit") {
1475 var e = this.element.parentNode;
1476 while (e.nodeType != 9 && e.nodeType != 11) {
1477 currentVis = YAHOO.util.Dom.getStyle(e, "visibility");
1478 if (currentVis != "inherit") { break; }
1479 e = e.parentNode;
1480 }
1481 if (currentVis == "inherit") {
1482 currentVis = "visible";
1483 }
1484 }
1485
1486 var effect = this.cfg.getProperty("effect");
1487
1488 var effectInstances = [];
1489 if (effect) {
1490 if (effect instanceof Array) {
1491 for (var i=0;i<effect.length;i++) {
1492 var eff = effect[i];
1493 effectInstances[effectInstances.length] = eff.effect(this, eff.duration);
1494 }
1495 } else {
1496 effectInstances[effectInstances.length] = effect.effect(this, effect.duration);
1497 }
1498 }
1499
1500 var isMacGecko = (this.platform == "mac" && this.browser == "gecko");
1501
1502 if (visible) { // Show
1503 if (isMacGecko) {
1504 this.showMacGeckoScrollbars();
1505 }
1506
1507 if (effect) { // Animate in
1508 if (visible) { // Animate in if not showing
1509 if (currentVis != "visible" || currentVis === "") {
1510 this.beforeShowEvent.fire();
1511 for (var j=0;j<effectInstances.length;j++) {
1512 var ei = effectInstances[j];
1513 if (j === 0 && ! YAHOO.util.Config.alreadySubscribed(ei.animateInCompleteEvent,this.showEvent.fire,this.showEvent)) {
1514 ei.animateInCompleteEvent.subscribe(this.showEvent.fire,this.showEvent,true); // Delegate showEvent until end of animateInComplete
1515 }
1516 ei.animateIn();
1517 }
1518 }
1519 }
1520 } else { // Show
1521 if (currentVis != "visible" || currentVis === "") {
1522 this.beforeShowEvent.fire();
1523 YAHOO.util.Dom.setStyle(this.element, "visibility", "visible");
1524 this.cfg.refireEvent("iframe");
1525 this.showEvent.fire();
1526 }
1527 }
1528
1529 } else { // Hide
1530 if (isMacGecko) {
1531 this.hideMacGeckoScrollbars();
1532 }
1533
1534 if (effect) { // Animate out if showing
1535 if (currentVis == "visible") {
1536 this.beforeHideEvent.fire();
1537 for (var k=0;k<effectInstances.length;k++) {
1538 var h = effectInstances[k];
1539 if (k === 0 && ! YAHOO.util.Config.alreadySubscribed(h.animateOutCompleteEvent,this.hideEvent.fire,this.hideEvent)) {
1540 h.animateOutCompleteEvent.subscribe(this.hideEvent.fire,this.hideEvent,true); // Delegate hideEvent until end of animateOutComplete
1541 }
1542 h.animateOut();
1543 }
1544 } else if (currentVis === "") {
1545 YAHOO.util.Dom.setStyle(this.element, "visibility", "hidden");
1546 }
1547 } else { // Simple hide
1548 if (currentVis == "visible" || currentVis === "") {
1549 this.beforeHideEvent.fire();
1550 YAHOO.util.Dom.setStyle(this.element, "visibility", "hidden");
1551 this.cfg.refireEvent("iframe");
1552 this.hideEvent.fire();
1553 }
1554 }
1555 }
1556};
1557
1558/**
1559* Center event handler used for centering on scroll/resize, but only if the Overlay is visible
1560* @method doCenterOnDOMEvent
1561*/
1562YAHOO.widget.Overlay.prototype.doCenterOnDOMEvent = function() {
1563 if (this.cfg.getProperty("visible")) {
1564 this.center();
1565 }
1566};
1567
1568/**
1569* The default event handler fired when the "fixedcenter" property is changed.
1570* @method configFixedCenter
1571 * @param {String} typeThe CustomEvent type (usually the property name)
1572 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1573 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1574*/
1575YAHOO.widget.Overlay.prototype.configFixedCenter = function(type, args, obj) {
1576 var val = args[0];
1577
1578 if (val) {
1579 this.center();
1580
1581 if (! YAHOO.util.Config.alreadySubscribed(this.beforeShowEvent, this.center, this)) {
1582 this.beforeShowEvent.subscribe(this.center, this, true);
1583 }
1584
1585 if (! YAHOO.util.Config.alreadySubscribed(YAHOO.widget.Overlay.windowResizeEvent, this.doCenterOnDOMEvent, this)) {
1586 YAHOO.widget.Overlay.windowResizeEvent.subscribe(this.doCenterOnDOMEvent, this, true);
1587 }
1588
1589 if (! YAHOO.util.Config.alreadySubscribed(YAHOO.widget.Overlay.windowScrollEvent, this.doCenterOnDOMEvent, this)) {
1590 YAHOO.widget.Overlay.windowScrollEvent.subscribe( this.doCenterOnDOMEvent, this, true);
1591 }
1592 } else {
1593 YAHOO.widget.Overlay.windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent, this);
1594 YAHOO.widget.Overlay.windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent, this);
1595 }
1596};
1597
1598/**
1599* The default event handler fired when the "height" property is changed.
1600* @method configHeight
1601 * @param {String} typeThe CustomEvent type (usually the property name)
1602 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1603 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1604*/
1605YAHOO.widget.Overlay.prototype.configHeight = function(type, args, obj) {
1606 var height = args[0];
1607 var el = this.element;
1608 YAHOO.util.Dom.setStyle(el, "height", height);
1609 this.cfg.refireEvent("iframe");
1610};
1611
1612/**
1613* The default event handler fired when the "width" property is changed.
1614* @method configWidth
1615 * @param {String} typeThe CustomEvent type (usually the property name)
1616 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1617 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1618*/
1619YAHOO.widget.Overlay.prototype.configWidth = function(type, args, obj) {
1620 var width = args[0];
1621 var el = this.element;
1622 YAHOO.util.Dom.setStyle(el, "width", width);
1623 this.cfg.refireEvent("iframe");
1624};
1625
1626/**
1627* The default event handler fired when the "zIndex" property is changed.
1628* @method configzIndex
1629 * @param {String} typeThe CustomEvent type (usually the property name)
1630 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1631 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1632*/
1633YAHOO.widget.Overlay.prototype.configzIndex = function(type, args, obj) {
1634 var zIndex = args[0];
1635
1636 var el = this.element;
1637
1638 if (! zIndex) {
1639 zIndex = YAHOO.util.Dom.getStyle(el, "zIndex");
1640 if (! zIndex || isNaN(zIndex)) {
1641 zIndex = 0;
1642 }
1643 }
1644
1645 if (this.iframe) {
1646 if (zIndex <= 0) {
1647 zIndex = 1;
1648 }
1649 YAHOO.util.Dom.setStyle(this.iframe, "zIndex", (zIndex-1));
1650 }
1651
1652 YAHOO.util.Dom.setStyle(el, "zIndex", zIndex);
1653 this.cfg.setProperty("zIndex", zIndex, true);
1654};
1655
1656/**
1657* The default event handler fired when the "xy" property is changed.
1658* @method configXY
1659 * @param {String} typeThe CustomEvent type (usually the property name)
1660 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1661 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1662*/
1663YAHOO.widget.Overlay.prototype.configXY = function(type, args, obj) {
1664 var pos = args[0];
1665 var x = pos[0];
1666 var y = pos[1];
1667
1668 this.cfg.setProperty("x", x);
1669 this.cfg.setProperty("y", y);
1670
1671 this.beforeMoveEvent.fire([x,y]);
1672
1673 x = this.cfg.getProperty("x");
1674 y = this.cfg.getProperty("y");
1675
1676 this.cfg.refireEvent("iframe");
1677 this.moveEvent.fire([x,y]);
1678};
1679
1680/**
1681* The default event handler fired when the "x" property is changed.
1682* @method configX
1683 * @param {String} typeThe CustomEvent type (usually the property name)
1684 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1685 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1686*/
1687YAHOO.widget.Overlay.prototype.configX = function(type, args, obj) {
1688 var x = args[0];
1689 var y = this.cfg.getProperty("y");
1690
1691 this.cfg.setProperty("x", x, true);
1692 this.cfg.setProperty("y", y, true);
1693
1694 this.beforeMoveEvent.fire([x,y]);
1695
1696 x = this.cfg.getProperty("x");
1697 y = this.cfg.getProperty("y");
1698
1699 YAHOO.util.Dom.setX(this.element, x, true);
1700
1701 this.cfg.setProperty("xy", [x, y], true);
1702
1703 this.cfg.refireEvent("iframe");
1704 this.moveEvent.fire([x, y]);
1705};
1706
1707/**
1708* The default event handler fired when the "y" property is changed.
1709* @method configY
1710 * @param {String} typeThe CustomEvent type (usually the property name)
1711 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1712 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1713*/
1714YAHOO.widget.Overlay.prototype.configY = function(type, args, obj) {
1715 var x = this.cfg.getProperty("x");
1716 var y = args[0];
1717
1718 this.cfg.setProperty("x", x, true);
1719 this.cfg.setProperty("y", y, true);
1720
1721 this.beforeMoveEvent.fire([x,y]);
1722
1723 x = this.cfg.getProperty("x");
1724 y = this.cfg.getProperty("y");
1725
1726 YAHOO.util.Dom.setY(this.element, y, true);
1727
1728 this.cfg.setProperty("xy", [x, y], true);
1729
1730 this.cfg.refireEvent("iframe");
1731 this.moveEvent.fire([x, y]);
1732};
1733
1734/**
1735* Shows the iframe shim, if it has been enabled
1736* @method showIframe
1737*/
1738YAHOO.widget.Overlay.prototype.showIframe = function() {
1739 if (this.iframe) {
1740 this.iframe.style.display = "block";
1741 }
1742};
1743
1744/**
1745* Hides the iframe shim, if it has been enabled
1746* @method hideIframe
1747*/
1748YAHOO.widget.Overlay.prototype.hideIframe = function() {
1749 if (this.iframe) {
1750 this.iframe.style.display = "none";
1751 }
1752};
1753
1754/**
1755* The default event handler fired when the "iframe" property is changed.
1756* @method configIframe
1757 * @param {String} typeThe CustomEvent type (usually the property name)
1758 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1759 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1760*/
1761YAHOO.widget.Overlay.prototype.configIframe = function(type, args, obj) {
1762
1763 var val = args[0];
1764
1765 if (val) { // IFRAME shim is enabled
1766
1767 if (! YAHOO.util.Config.alreadySubscribed(this.showEvent, this.showIframe, this)) {
1768 this.showEvent.subscribe(this.showIframe, this, true);
1769 }
1770 if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent, this.hideIframe, this)) {
1771 this.hideEvent.subscribe(this.hideIframe, this, true);
1772 }
1773
1774 var x = this.cfg.getProperty("x");
1775 var y = this.cfg.getProperty("y");
1776
1777 if (! x || ! y) {
1778 this.syncPosition();
1779 x = this.cfg.getProperty("x");
1780 y = this.cfg.getProperty("y");
1781 }
1782
1783 if (! isNaN(x) && ! isNaN(y)) {
1784 if (! this.iframe) {
1785 this.iframe = document.createElement("iframe");
1786 if (this.isSecure) {
1787 this.iframe.src = this.imageRoot + YAHOO.widget.Overlay.IFRAME_SRC;
1788 }
1789
1790 var parent = this.element.parentNode;
1791 if (parent) {
1792 parent.appendChild(this.iframe);
1793 } else {
1794 document.body.appendChild(this.iframe);
1795 }
1796
1797 YAHOO.util.Dom.setStyle(this.iframe, "position", "absolute");
1798 YAHOO.util.Dom.setStyle(this.iframe, "border", "none");
1799 YAHOO.util.Dom.setStyle(this.iframe, "margin", "0");
1800 YAHOO.util.Dom.setStyle(this.iframe, "padding", "0");
1801 YAHOO.util.Dom.setStyle(this.iframe, "opacity", "0");
1802 if (this.cfg.getProperty("visible")) {
1803 this.showIframe();
1804 } else {
1805 this.hideIframe();
1806 }
1807 }
1808
1809 var iframeDisplay = YAHOO.util.Dom.getStyle(this.iframe, "display");
1810
1811 if (iframeDisplay == "none") {
1812 this.iframe.style.display = "block";
1813 }
1814
1815 YAHOO.util.Dom.setXY(this.iframe, [x,y]);
1816
1817 var width = this.element.clientWidth;
1818 var height = this.element.clientHeight;
1819
1820 YAHOO.util.Dom.setStyle(this.iframe, "width", (width+2) + "px");
1821 YAHOO.util.Dom.setStyle(this.iframe, "height", (height+2) + "px");
1822
1823 if (iframeDisplay == "none") {
1824 this.iframe.style.display = "none";
1825 }
1826 }
1827 } else {
1828 if (this.iframe) {
1829 this.iframe.style.display = "none";
1830 }
1831 this.showEvent.unsubscribe(this.showIframe, this);
1832 this.hideEvent.unsubscribe(this.hideIframe, this);
1833 }
1834};
1835
1836
1837/**
1838* The default event handler fired when the "constraintoviewport" property is changed.
1839* @method configConstrainToViewport
1840 * @param {String} typeThe CustomEvent type (usually the property name)
1841 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1842 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1843*/
1844YAHOO.widget.Overlay.prototype.configConstrainToViewport = function(type, args, obj) {
1845 var val = args[0];
1846 if (val) {
1847 if (! YAHOO.util.Config.alreadySubscribed(this.beforeMoveEvent, this.enforceConstraints, this)) {
1848 this.beforeMoveEvent.subscribe(this.enforceConstraints, this, true);
1849 }
1850 } else {
1851 this.beforeMoveEvent.unsubscribe(this.enforceConstraints, this);
1852 }
1853};
1854
1855/**
1856* The default event handler fired when the "context" property is changed.
1857* @method configContext
1858 * @param {String} typeThe CustomEvent type (usually the property name)
1859 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1860 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1861*/
1862YAHOO.widget.Overlay.prototype.configContext = function(type, args, obj) {
1863 var contextArgs = args[0];
1864
1865 if (contextArgs) {
1866 var contextEl = contextArgs[0];
1867 var elementMagnetCorner = contextArgs[1];
1868 var contextMagnetCorner = contextArgs[2];
1869
1870 if (contextEl) {
1871 if (typeof contextEl == "string") {
1872 this.cfg.setProperty("context", [document.getElementById(contextEl),elementMagnetCorner,contextMagnetCorner], true);
1873 }
1874
1875 if (elementMagnetCorner && contextMagnetCorner) {
1876 this.align(elementMagnetCorner, contextMagnetCorner);
1877 }
1878 }
1879 }
1880};
1881
1882
1883// END BUILT-IN PROPERTY EVENT HANDLERS //
1884
1885/**
1886* Aligns the Overlay to its context element using the specified corner points (represented by the constants TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, and BOTTOM_RIGHT.
1887* @method align
1888 * @param {String} elementAlign The String representing the corner of the Overlay that should be aligned to the context element
1889 * @param {String} contextAlign The corner of the context element that the elementAlign corner should stick to.
1890*/
1891YAHOO.widget.Overlay.prototype.align = function(elementAlign, contextAlign) {
1892 var contextArgs = this.cfg.getProperty("context");
1893 if (contextArgs) {
1894 var context = contextArgs[0];
1895
1896 var element = this.element;
1897 var me = this;
1898
1899 if (! elementAlign) {
1900 elementAlign = contextArgs[1];
1901 }
1902
1903 if (! contextAlign) {
1904 contextAlign = contextArgs[2];
1905 }
1906
1907 if (element && context) {
1908 var elementRegion = YAHOO.util.Dom.getRegion(element);
1909 var contextRegion = YAHOO.util.Dom.getRegion(context);
1910
1911 var doAlign = function(v,h) {
1912 switch (elementAlign) {
1913 case YAHOO.widget.Overlay.TOP_LEFT:
1914 me.moveTo(h,v);
1915 break;
1916 case YAHOO.widget.Overlay.TOP_RIGHT:
1917 me.moveTo(h-element.offsetWidth,v);
1918 break;
1919 case YAHOO.widget.Overlay.BOTTOM_LEFT:
1920 me.moveTo(h,v-element.offsetHeight);
1921 break;
1922 case YAHOO.widget.Overlay.BOTTOM_RIGHT:
1923 me.moveTo(h-element.offsetWidth,v-element.offsetHeight);
1924 break;
1925 }
1926 };
1927
1928 switch (contextAlign) {
1929 case YAHOO.widget.Overlay.TOP_LEFT:
1930 doAlign(contextRegion.top, contextRegion.left);
1931 break;
1932 case YAHOO.widget.Overlay.TOP_RIGHT:
1933 doAlign(contextRegion.top, contextRegion.right);
1934 break;
1935 case YAHOO.widget.Overlay.BOTTOM_LEFT:
1936 doAlign(contextRegion.bottom, contextRegion.left);
1937 break;
1938 case YAHOO.widget.Overlay.BOTTOM_RIGHT:
1939 doAlign(contextRegion.bottom, contextRegion.right);
1940 break;
1941 }
1942 }
1943 }
1944};
1945
1946/**
1947* The default event handler executed when the moveEvent is fired, if the "constraintoviewport" is set to true.
1948* @method enforceConstraints
1949 * @param {String} typeThe CustomEvent type (usually the property name)
1950 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
1951 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
1952*/
1953YAHOO.widget.Overlay.prototype.enforceConstraints = function(type, args, obj) {
1954 var pos = args[0];
1955
1956 var x = pos[0];
1957 var y = pos[1];
1958
1959 var offsetHeight = this.element.offsetHeight;
1960 var offsetWidth = this.element.offsetWidth;
1961
1962 var viewPortWidth = YAHOO.util.Dom.getViewportWidth();
1963 var viewPortHeight = YAHOO.util.Dom.getViewportHeight();
1964
1965 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
1966 var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
1967
1968 var topConstraint = scrollY + 10;
1969 var leftConstraint = scrollX + 10;
1970 var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10;
1971 var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10;
1972
1973 if (x < leftConstraint) {
1974 x = leftConstraint;
1975 } else if (x > rightConstraint) {
1976 x = rightConstraint;
1977 }
1978
1979 if (y < topConstraint) {
1980 y = topConstraint;
1981 } else if (y > bottomConstraint) {
1982 y = bottomConstraint;
1983 }
1984
1985 this.cfg.setProperty("x", x, true);
1986 this.cfg.setProperty("y", y, true);
1987 this.cfg.setProperty("xy", [x,y], true);
1988};
1989
1990/**
1991* Centers the container in the viewport.
1992* @method center
1993*/
1994YAHOO.widget.Overlay.prototype.center = function() {
1995 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
1996 var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
1997
1998 var viewPortWidth = YAHOO.util.Dom.getClientWidth();
1999 var viewPortHeight = YAHOO.util.Dom.getClientHeight();
2000
2001 var elementWidth = this.element.offsetWidth;
2002 var elementHeight = this.element.offsetHeight;
2003
2004 var x = (viewPortWidth / 2) - (elementWidth / 2) + scrollX;
2005 var y = (viewPortHeight / 2) - (elementHeight / 2) + scrollY;
2006
2007 this.cfg.setProperty("xy", [parseInt(x, 10), parseInt(y, 10)]);
2008
2009 this.cfg.refireEvent("iframe");
2010};
2011
2012/**
2013* Synchronizes the Panel's "xy", "x", and "y" properties with the Panel's position in the DOM. This is primarily used to update position information during drag & drop.
2014* @method syncPosition
2015*/
2016YAHOO.widget.Overlay.prototype.syncPosition = function() {
2017 var pos = YAHOO.util.Dom.getXY(this.element);
2018 this.cfg.setProperty("x", pos[0], true);
2019 this.cfg.setProperty("y", pos[1], true);
2020 this.cfg.setProperty("xy", pos, true);
2021};
2022
2023/**
2024* Event handler fired when the resize monitor element is resized.
2025* @method onDomResize
2026 * @param {DOMEvent} eThe resize DOM event
2027 * @param {Object} objThe scope object
2028*/
2029YAHOO.widget.Overlay.prototype.onDomResize = function(e, obj) {
2030 YAHOO.widget.Overlay.superclass.onDomResize.call(this, e, obj);
2031 var me = this;
2032 setTimeout(function() {
2033 me.syncPosition();
2034 me.cfg.refireEvent("iframe");
2035 me.cfg.refireEvent("context");
2036 }, 0);
2037};
2038
2039/**
2040* Removes the Overlay element from the DOM and sets all child elements to null.
2041* @method destroy
2042*/
2043YAHOO.widget.Overlay.prototype.destroy = function() {
2044 if (this.iframe) {
2045 this.iframe.parentNode.removeChild(this.iframe);
2046 }
2047
2048 this.iframe = null;
2049
2050 YAHOO.widget.Overlay.superclass.destroy.call(this);
2051};
2052
2053/**
2054* Returns a String representation of the object.
2055* @method toString
2056* @return {String} The string representation of the Overlay.
2057*/
2058YAHOO.widget.Overlay.prototype.toString = function() {
2059 return "Overlay " + this.id;
2060};
2061
2062/**
2063* A singleton CustomEvent used for reacting to the DOM event for window scroll
2064* @event YAHOO.widget.Overlay.windowScrollEvent
2065*/
2066YAHOO.widget.Overlay.windowScrollEvent = new YAHOO.util.CustomEvent("windowScroll");
2067
2068/**
2069* A singleton CustomEvent used for reacting to the DOM event for window resize
2070* @event YAHOO.widget.Overlay.windowResizeEvent
2071*/
2072YAHOO.widget.Overlay.windowResizeEvent = new YAHOO.util.CustomEvent("windowResize");
2073
2074/**
2075* The DOM event handler used to fire the CustomEvent for window scroll
2076* @method YAHOO.widget.Overlay.windowScrollHandler
2077* @static
2078* @param {DOMEvent} e The DOM scroll event
2079*/
2080YAHOO.widget.Overlay.windowScrollHandler = function(e) {
2081 if (YAHOO.widget.Module.prototype.browser == "ie" || YAHOO.widget.Module.prototype.browser == "ie7") {
2082 if (! window.scrollEnd) {
2083 window.scrollEnd = -1;
2084 }
2085 clearTimeout(window.scrollEnd);
2086 window.scrollEnd = setTimeout(function() { YAHOO.widget.Overlay.windowScrollEvent.fire(); }, 1);
2087 } else {
2088 YAHOO.widget.Overlay.windowScrollEvent.fire();
2089 }
2090};
2091
2092/**
2093* The DOM event handler used to fire the CustomEvent for window resize
2094* @method YAHOO.widget.Overlay.windowResizeHandler
2095* @static
2096* @param {DOMEvent} e The DOM resize event
2097*/
2098YAHOO.widget.Overlay.windowResizeHandler = function(e) {
2099 if (YAHOO.widget.Module.prototype.browser == "ie" || YAHOO.widget.Module.prototype.browser == "ie7") {
2100 if (! window.resizeEnd) {
2101 window.resizeEnd = -1;
2102 }
2103 clearTimeout(window.resizeEnd);
2104 window.resizeEnd = setTimeout(function() { YAHOO.widget.Overlay.windowResizeEvent.fire(); }, 100);
2105 } else {
2106 YAHOO.widget.Overlay.windowResizeEvent.fire();
2107 }
2108};
2109
2110/**
2111* A boolean that indicated whether the window resize and scroll events have already been subscribed to.
2112* @property YAHOO.widget.Overlay._initialized
2113* @private
2114* @type Boolean
2115*/
2116YAHOO.widget.Overlay._initialized = null;
2117
2118if (YAHOO.widget.Overlay._initialized === null) {
2119 YAHOO.util.Event.addListener(window, "scroll", YAHOO.widget.Overlay.windowScrollHandler);
2120 YAHOO.util.Event.addListener(window, "resize", YAHOO.widget.Overlay.windowResizeHandler);
2121
2122 YAHOO.widget.Overlay._initialized = true;
2123}
2124
2125/**
2126* OverlayManager is used for maintaining the focus status of multiple Overlays.* @namespace YAHOO.widget
2127* @namespace YAHOO.widget
2128* @class OverlayManager
2129* @constructor
2130 * @param {Array} overlaysOptional. A collection of Overlays to register with the manager.
2131 * @param {Object} userConfig The object literal representing the user configuration of the OverlayManager
2132*/
2133YAHOO.widget.OverlayManager = function(userConfig) {
2134 this.init(userConfig);
2135};
2136
2137/**
2138* The CSS class representing a focused Overlay
2139* @property YAHOO.widget.OverlayManager.CSS_FOCUSED
2140* @static
2141* @final
2142* @type String
2143*/
2144YAHOO.widget.OverlayManager.CSS_FOCUSED = "focused";
2145
2146YAHOO.widget.OverlayManager.prototype = {
2147 /**
2148 * The class's constructor function
2149 * @property contructor
2150 * @type Function
2151 */
2152 constructor : YAHOO.widget.OverlayManager,
2153
2154 /**
2155 * The array of Overlays that are currently registered
2156 * @property overlays
2157 * @type YAHOO.widget.Overlay[]
2158 */
2159 overlays : null,
2160
2161 /**
2162 * Initializes the default configuration of the OverlayManager
2163 * @method initDefaultConfig
2164 */
2165 initDefaultConfig : function() {
2166 /**
2167 * The collection of registered Overlays in use by the OverlayManager
2168 * @config overlays
2169 * @type YAHOO.widget.Overlay[]
2170 * @default null
2171 */
2172 this.cfg.addProperty("overlays", { suppressEvent:true } );
2173
2174 /**
2175 * The default DOM event that should be used to focus an Overlay
2176 * @config focusevent
2177 * @type String
2178 * @default "mousedown"
2179 */
2180 this.cfg.addProperty("focusevent", { value:"mousedown" } );
2181 },
2182
2183 /**
2184 * Initializes the OverlayManager
2185 * @method init
2186 * @param {YAHOO.widget.Overlay[]} overlaysOptional. A collection of Overlays to register with the manager.
2187 * @param {Object} userConfig The object literal representing the user configuration of the OverlayManager
2188 */
2189 init : function(userConfig) {
2190 /**
2191 * The OverlayManager's Config object used for monitoring configuration properties.
2192 * @property cfg
2193 * @type YAHOO.util.Config
2194 */
2195 this.cfg = new YAHOO.util.Config(this);
2196
2197 this.initDefaultConfig();
2198
2199 if (userConfig) {
2200 this.cfg.applyConfig(userConfig, true);
2201 }
2202 this.cfg.fireQueue();
2203
2204 /**
2205 * The currently activated Overlay
2206 * @property activeOverlay
2207 * @private
2208 * @type YAHOO.widget.Overlay
2209 */
2210 var activeOverlay = null;
2211
2212 /**
2213 * Returns the currently focused Overlay
2214 * @method getActive
2215 * @return {YAHOO.widget.Overlay}The currently focused Overlay
2216 */
2217 this.getActive = function() {
2218 return activeOverlay;
2219 };
2220
2221 /**
2222 * Focuses the specified Overlay
2223 * @method focus
2224 * @param {YAHOO.widget.Overlay} overlayThe Overlay to focus
2225 * @param {String} overlayThe id of the Overlay to focus
2226 */
2227 this.focus = function(overlay) {
2228 var o = this.find(overlay);
2229 if (o) {
2230 this.blurAll();
2231 activeOverlay = o;
2232 YAHOO.util.Dom.addClass(activeOverlay.element, YAHOO.widget.OverlayManager.CSS_FOCUSED);
2233 this.overlays.sort(this.compareZIndexDesc);
2234 var topZIndex = YAHOO.util.Dom.getStyle(this.overlays[0].element, "zIndex");
2235 if (! isNaN(topZIndex) && this.overlays[0] != overlay) {
2236 activeOverlay.cfg.setProperty("zIndex", (parseInt(topZIndex, 10) + 2));
2237 }
2238 this.overlays.sort(this.compareZIndexDesc);
2239 }
2240 };
2241
2242 /**
2243 * Removes the specified Overlay from the manager
2244 * @method remove
2245 * @param {YAHOO.widget.Overlay} overlayThe Overlay to remove
2246 * @param {String} overlayThe id of the Overlay to remove
2247 */
2248 this.remove = function(overlay) {
2249 var o = this.find(overlay);
2250 if (o) {
2251 var originalZ = YAHOO.util.Dom.getStyle(o.element, "zIndex");
2252 o.cfg.setProperty("zIndex", -1000, true);
2253 this.overlays.sort(this.compareZIndexDesc);
2254 this.overlays = this.overlays.slice(0, this.overlays.length-1);
2255 o.cfg.setProperty("zIndex", originalZ, true);
2256
2257 o.cfg.setProperty("manager", null);
2258 o.focusEvent = null;
2259 o.blurEvent = null;
2260 o.focus = null;
2261 o.blur = null;
2262 }
2263 };
2264
2265 /**
2266 * Removes focus from all registered Overlays in the manager
2267 * @method blurAll
2268 */
2269 this.blurAll = function() {
2270 activeOverlay = null;
2271 for (var o=0;o<this.overlays.length;o++) {
2272 YAHOO.util.Dom.removeClass(this.overlays[o].element, YAHOO.widget.OverlayManager.CSS_FOCUSED);
2273 }
2274 };
2275
2276 var overlays = this.cfg.getProperty("overlays");
2277
2278 if (! this.overlays) {
2279 this.overlays = [];
2280 }
2281
2282 if (overlays) {
2283 this.register(overlays);
2284 this.overlays.sort(this.compareZIndexDesc);
2285 }
2286 },
2287
2288 /**
2289 * Registers an Overlay or an array of Overlays with the manager. Upon registration, the Overlay receives functions for focus and blur, along with CustomEvents for each.
2290 * @method register
2291 * @param {YAHOO.widget.Overlay} overlay An Overlay to register with the manager.
2292 * @param {YAHOO.widget.Overlay[]} overlay An array of Overlays to register with the manager.
2293 * @return {Boolean}True if any Overlays are registered.
2294 */
2295 register : function(overlay) {
2296 if (overlay instanceof YAHOO.widget.Overlay) {
2297 overlay.cfg.addProperty("manager", { value:this } );
2298
2299 overlay.focusEvent = new YAHOO.util.CustomEvent("focus");
2300 overlay.blurEvent = new YAHOO.util.CustomEvent("blur");
2301
2302 var mgr=this;
2303
2304 overlay.focus = function() {
2305 mgr.focus(this);
2306 this.focusEvent.fire();
2307 };
2308
2309 overlay.blur = function() {
2310 mgr.blurAll();
2311 this.blurEvent.fire();
2312 };
2313
2314 var focusOnDomEvent = function(e,obj) {
2315 overlay.focus();
2316 };
2317
2318 var focusevent = this.cfg.getProperty("focusevent");
2319 YAHOO.util.Event.addListener(overlay.element,focusevent,focusOnDomEvent,this,true);
2320
2321 var zIndex = YAHOO.util.Dom.getStyle(overlay.element, "zIndex");
2322 if (! isNaN(zIndex)) {
2323 overlay.cfg.setProperty("zIndex", parseInt(zIndex, 10));
2324 } else {
2325 overlay.cfg.setProperty("zIndex", 0);
2326 }
2327
2328 this.overlays.push(overlay);
2329 return true;
2330 } else if (overlay instanceof Array) {
2331 var regcount = 0;
2332 for (var i=0;i<overlay.length;i++) {
2333 if (this.register(overlay[i])) {
2334 regcount++;
2335 }
2336 }
2337 if (regcount > 0) {
2338 return true;
2339 }
2340 } else {
2341 return false;
2342 }
2343 },
2344
2345 /**
2346 * Attempts to locate an Overlay by instance or ID.
2347 * @method find
2348 * @param {YAHOO.widget.Overlay} overlay An Overlay to locate within the manager
2349 * @param {String} overlay An Overlay id to locate within the manager
2350 * @return {YAHOO.widget.Overlay}The requested Overlay, if found, or null if it cannot be located.
2351 */
2352 find : function(overlay) {
2353 if (overlay instanceof YAHOO.widget.Overlay) {
2354 for (var o=0;o<this.overlays.length;o++) {
2355 if (this.overlays[o] == overlay) {
2356 return this.overlays[o];
2357 }
2358 }
2359 } else if (typeof overlay == "string") {
2360 for (var p=0;p<this.overlays.length;p++) {
2361 if (this.overlays[p].id == overlay) {
2362 return this.overlays[p];
2363 }
2364 }
2365 }
2366 return null;
2367 },
2368
2369 /**
2370 * Used for sorting the manager's Overlays by z-index.
2371 * @method compareZIndexDesc
2372 * @private
2373 * @return {Number}0, 1, or -1, depending on where the Overlay should fall in the stacking order.
2374 */
2375 compareZIndexDesc : function(o1, o2) {
2376 var zIndex1 = o1.cfg.getProperty("zIndex");
2377 var zIndex2 = o2.cfg.getProperty("zIndex");
2378
2379 if (zIndex1 > zIndex2) {
2380 return -1;
2381 } else if (zIndex1 < zIndex2) {
2382 return 1;
2383 } else {
2384 return 0;
2385 }
2386 },
2387
2388 /**
2389 * Shows all Overlays in the manager.
2390 * @method showAll
2391 */
2392 showAll : function() {
2393 for (var o=0;o<this.overlays.length;o++) {
2394 this.overlays[o].show();
2395 }
2396 },
2397
2398 /**
2399 * Hides all Overlays in the manager.
2400 * @method hideAll
2401 */
2402 hideAll : function() {
2403 for (var o=0;o<this.overlays.length;o++) {
2404 this.overlays[o].hide();
2405 }
2406 },
2407
2408
2409 /**
2410 * Returns a string representation of the object.
2411 * @method toString
2412 * @return {String}The string representation of the OverlayManager
2413 */
2414 toString : function() {
2415 return "OverlayManager";
2416 }
2417
2418};
2419
2420/**
2421* KeyListener is a utility that provides an easy interface for listening for keydown/keyup events fired against DOM elements.
2422* @namespace YAHOO.util
2423* @class KeyListener
2424* @constructor
2425 * @param {HTMLElement} attachToThe element or element ID to which the key event should be attached
2426 * @param {String} attachToThe element or element ID to which the key event should be attached
2427 * @param {Object} keyData The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
2428 * @param {Function} handler The CustomEvent handler to fire when the key event is detected
2429 * @param {Object} handler An object literal representing the handler.
2430 * @param {String} event Optional. The event (keydown or keyup) to listen for. Defaults automatically to keydown.
2431*/
2432YAHOO.util.KeyListener = function(attachTo, keyData, handler, event) {
2433 if (! event) {
2434 event = YAHOO.util.KeyListener.KEYDOWN;
2435 }
2436
2437 /**
2438 * The CustomEvent fired internally when a key is pressed
2439 * @event keyEvent
2440 * @private
2441 * @param {Object} keyData The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
2442 */
2443 var keyEvent = new YAHOO.util.CustomEvent("keyPressed");
2444
2445 /**
2446 * The CustomEvent fired when the KeyListener is enabled via the enable() function
2447 * @event enabledEvent
2448 * @param {Object} keyData The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
2449 */
2450 this.enabledEvent = new YAHOO.util.CustomEvent("enabled");
2451
2452 /**
2453 * The CustomEvent fired when the KeyListener is disabled via the disable() function
2454 * @event disabledEvent
2455 * @param {Object} keyData The object literal representing the key(s) to detect. Possible attributes are shift(boolean), alt(boolean), ctrl(boolean) and keys(either an int or an array of ints representing keycodes).
2456 */
2457 this.disabledEvent = new YAHOO.util.CustomEvent("disabled");
2458
2459 if (typeof attachTo == 'string') {
2460 attachTo = document.getElementById(attachTo);
2461 }
2462
2463 if (typeof handler == 'function') {
2464 keyEvent.subscribe(handler);
2465 } else {
2466 keyEvent.subscribe(handler.fn, handler.scope, handler.correctScope);
2467 }
2468
2469 /**
2470 * Handles the key event when a key is pressed.
2471 * @method handleKeyPress
2472 * @param {DOMEvent} eThe keypress DOM event
2473 * @param {Object} objThe DOM event scope object
2474 * @private
2475 */
2476 function handleKeyPress(e, obj) {
2477 if (! keyData.shift) {
2478 keyData.shift = false;
2479 }
2480 if (! keyData.alt) {
2481 keyData.alt = false;
2482 }
2483 if (! keyData.ctrl) {
2484 keyData.ctrl = false;
2485 }
2486
2487 // check held down modifying keys first
2488 if (e.shiftKey == keyData.shift &&
2489 e.altKey == keyData.alt &&
2490 e.ctrlKey == keyData.ctrl) { // if we pass this, all modifiers match
2491
2492 var dataItem;
2493 var keyPressed;
2494
2495 if (keyData.keys instanceof Array) {
2496 for (var i=0;i<keyData.keys.length;i++) {
2497 dataItem = keyData.keys[i];
2498
2499 if (dataItem == e.charCode ) {
2500 keyEvent.fire(e.charCode, e);
2501 break;
2502 } else if (dataItem == e.keyCode) {
2503 keyEvent.fire(e.keyCode, e);
2504 break;
2505 }
2506 }
2507 } else {
2508 dataItem = keyData.keys;
2509
2510 if (dataItem == e.charCode ) {
2511 keyEvent.fire(e.charCode, e);
2512 } else if (dataItem == e.keyCode) {
2513 keyEvent.fire(e.keyCode, e);
2514 }
2515 }
2516 }
2517 }
2518
2519 /**
2520 * Enables the KeyListener by attaching the DOM event listeners to the target DOM element
2521 * @method enable
2522 */
2523 this.enable = function() {
2524 if (! this.enabled) {
2525 YAHOO.util.Event.addListener(attachTo, event, handleKeyPress);
2526 this.enabledEvent.fire(keyData);
2527 }
2528 /**
2529 * Boolean indicating the enabled/disabled state of the Tooltip
2530 * @property enabled
2531 * @type Boolean
2532 */
2533 this.enabled = true;
2534 };
2535
2536 /**
2537 * Disables the KeyListener by removing the DOM event listeners from the target DOM element
2538 * @method disable
2539 */
2540 this.disable = function() {
2541 if (this.enabled) {
2542 YAHOO.util.Event.removeListener(attachTo, event, handleKeyPress);
2543 this.disabledEvent.fire(keyData);
2544 }
2545 this.enabled = false;
2546 };
2547
2548 /**
2549 * Returns a String representation of the object.
2550 * @method toString
2551 * @return {String}The string representation of the KeyListener
2552 */
2553 this.toString = function() {
2554 return "KeyListener [" + keyData.keys + "] " + attachTo.tagName + (attachTo.id ? "[" + attachTo.id + "]" : "");
2555 };
2556
2557};
2558
2559/**
2560* Constant representing the DOM "keydown" event.
2561* @property YAHOO.util.KeyListener.KEYDOWN
2562* @static
2563* @final
2564* @type String
2565*/
2566YAHOO.util.KeyListener.KEYDOWN = "keydown";
2567
2568/**
2569* Constant representing the DOM "keyup" event.
2570* @property YAHOO.util.KeyListener.KEYUP
2571* @static
2572* @final
2573* @type String
2574*/
2575YAHOO.util.KeyListener.KEYUP = "keyup";
2576
2577/**
2578* Tooltip is an implementation of Overlay that behaves like an OS tooltip, displaying when the user mouses over a particular element, and disappearing on mouse out.
2579* @namespace YAHOO.widget
2580* @class Tooltip
2581* @extends YAHOO.widget.Overlay
2582* @constructor
2583 * @param {String} elThe element ID representing the Tooltip <em>OR</em>
2584 * @param {HTMLElement} elThe element representing the Tooltip
2585 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this Overlay. See configuration documentation for more details.
2586*/
2587YAHOO.widget.Tooltip = function(el, userConfig) {
2588 YAHOO.widget.Tooltip.superclass.constructor.call(this, el, userConfig);
2589};
2590
2591YAHOO.extend(YAHOO.widget.Tooltip, YAHOO.widget.Overlay);
2592
2593/**
2594* Constant representing the Tooltip CSS class
2595* @property YAHOO.widget.Tooltip.CSS_TOOLTIP
2596* @static
2597* @final
2598* @type String
2599*/
2600YAHOO.widget.Tooltip.CSS_TOOLTIP = "tt";
2601
2602/**
2603* The Tooltip initialization method. This method is automatically called by the constructor. A Tooltip is automatically rendered by the init method, and it also is set to be invisible by default, and constrained to viewport by default as well.
2604* @method init
2605 * @param {String} elThe element ID representing the Tooltip <em>OR</em>
2606 * @param {HTMLElement} elThe element representing the Tooltip
2607 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this Tooltip. See configuration documentation for more details.
2608*/
2609YAHOO.widget.Tooltip.prototype.init = function(el, userConfig) {
2610 if (document.readyState && document.readyState != "complete") {
2611 var deferredInit = function() {
2612 this.init(el, userConfig);
2613 };
2614 YAHOO.util.Event.addListener(window, "load", deferredInit, this, true);
2615 } else {
2616 YAHOO.widget.Tooltip.superclass.init.call(this, el);
2617
2618 this.beforeInitEvent.fire(YAHOO.widget.Tooltip);
2619
2620 YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Tooltip.CSS_TOOLTIP);
2621
2622 if (userConfig) {
2623 this.cfg.applyConfig(userConfig, true);
2624 }
2625
2626 this.cfg.queueProperty("visible",false);
2627 this.cfg.queueProperty("constraintoviewport",true);
2628
2629 this.setBody("");
2630 this.render(this.cfg.getProperty("container"));
2631
2632 this.initEvent.fire(YAHOO.widget.Tooltip);
2633 }
2634};
2635
2636/**
2637* Initializes the class's configurable properties which can be changed using the Overlay's Config object (cfg).
2638* @method initDefaultConfig
2639*/
2640YAHOO.widget.Tooltip.prototype.initDefaultConfig = function() {
2641 YAHOO.widget.Tooltip.superclass.initDefaultConfig.call(this);
2642
2643 /**
2644 * Specifies whether the Tooltip should be kept from overlapping its context element.
2645 * @config preventoverlap
2646 * @type Boolean
2647 * @default true
2648 */
2649 this.cfg.addProperty("preventoverlap", { value:true, validator:this.cfg.checkBoolean, supercedes:["x","y","xy"] } );
2650
2651 /**
2652 * The number of milliseconds to wait before showing a Tooltip on mouseover.
2653 * @config showdelay
2654 * @type Number
2655 * @default 200
2656 */
2657 this.cfg.addProperty("showdelay", { value:200, handler:this.configShowDelay, validator:this.cfg.checkNumber } );
2658
2659 /**
2660 * The number of milliseconds to wait before automatically dismissing a Tooltip after the mouse has been resting on the context element.
2661 * @config autodismissdelay
2662 * @type Number
2663 * @default 5000
2664 */
2665 this.cfg.addProperty("autodismissdelay",{ value:5000, handler:this.configAutoDismissDelay, validator:this.cfg.checkNumber } );
2666
2667 /**
2668 * The number of milliseconds to wait before hiding a Tooltip on mouseover.
2669 * @config hidedelay
2670 * @type Number
2671 * @default 250
2672 */
2673 this.cfg.addProperty("hidedelay", { value:250, handler:this.configHideDelay, validator:this.cfg.checkNumber } );
2674
2675 /**
2676 * Specifies the Tooltip's text.
2677 * @config text
2678 * @type String
2679 * @default null
2680 */
2681 this.cfg.addProperty("text", { handler:this.configText, suppressEvent:true } );
2682
2683 /**
2684 * Specifies the container element that the Tooltip's markup should be rendered into.
2685 * @config container
2686 * @type HTMLElement/String
2687 * @default document.body
2688 */
2689 this.cfg.addProperty("container", { value:document.body, handler:this.configContainer } );
2690
2691 /**
2692 * Specifies the element or elements that the Tooltip should be anchored to on mouseover.
2693 * @config context
2694 * @type HTMLElement[]/String[]
2695 * @default null
2696 */
2697
2698};
2699
2700// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //
2701
2702/**
2703* The default event handler fired when the "text" property is changed.
2704* @method configText
2705 * @param {String} typeThe CustomEvent type (usually the property name)
2706 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
2707 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
2708*/
2709YAHOO.widget.Tooltip.prototype.configText = function(type, args, obj) {
2710 var text = args[0];
2711 if (text) {
2712 this.setBody(text);
2713 }
2714};
2715
2716/**
2717* The default event handler fired when the "container" property is changed.
2718* @method configContainer
2719 * @param {String} typeThe CustomEvent type (usually the property name)
2720 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
2721 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
2722*/
2723YAHOO.widget.Tooltip.prototype.configContainer = function(type, args, obj) {
2724 var container = args[0];
2725 if (typeof container == 'string') {
2726 this.cfg.setProperty("container", document.getElementById(container), true);
2727 }
2728};
2729
2730/**
2731* The default event handler fired when the "context" property is changed.
2732* @method configContext
2733 * @param {String} typeThe CustomEvent type (usually the property name)
2734 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
2735 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
2736*/
2737YAHOO.widget.Tooltip.prototype.configContext = function(type, args, obj) {
2738 var context = args[0];
2739 if (context) {
2740
2741 // Normalize parameter into an array
2742 if (! (context instanceof Array)) {
2743 if (typeof context == "string") {
2744 this.cfg.setProperty("context", [document.getElementById(context)], true);
2745 } else { // Assuming this is an element
2746 this.cfg.setProperty("context", [context], true);
2747 }
2748 context = this.cfg.getProperty("context");
2749 }
2750
2751
2752 // Remove any existing mouseover/mouseout listeners
2753 if (this._context) {
2754 for (var c=0;c<this._context.length;++c) {
2755 var el = this._context[c];
2756 YAHOO.util.Event.removeListener(el, "mouseover", this.onContextMouseOver);
2757 YAHOO.util.Event.removeListener(el, "mousemove", this.onContextMouseMove);
2758 YAHOO.util.Event.removeListener(el, "mouseout", this.onContextMouseOut);
2759 }
2760 }
2761
2762 // Add mouseover/mouseout listeners to context elements
2763 this._context = context;
2764 for (var d=0;d<this._context.length;++d) {
2765 var el2 = this._context[d];
2766 YAHOO.util.Event.addListener(el2, "mouseover", this.onContextMouseOver, this);
2767 YAHOO.util.Event.addListener(el2, "mousemove", this.onContextMouseMove, this);
2768 YAHOO.util.Event.addListener(el2, "mouseout", this.onContextMouseOut, this);
2769 }
2770 }
2771};
2772
2773// END BUILT-IN PROPERTY EVENT HANDLERS //
2774
2775// BEGIN BUILT-IN DOM EVENT HANDLERS //
2776
2777/**
2778* The default event handler fired when the user moves the mouse while over the context element.
2779* @method onContextMouseMove
2780 * @param {DOMEvent} eThe current DOM event
2781 * @param {Object} objThe object argument
2782*/
2783YAHOO.widget.Tooltip.prototype.onContextMouseMove = function(e, obj) {
2784 obj.pageX = YAHOO.util.Event.getPageX(e);
2785 obj.pageY = YAHOO.util.Event.getPageY(e);
2786
2787};
2788
2789/**
2790* The default event handler fired when the user mouses over the context element.
2791* @method onContextMouseOver
2792 * @param {DOMEvent} eThe current DOM event
2793 * @param {Object} objThe object argument
2794*/
2795YAHOO.widget.Tooltip.prototype.onContextMouseOver = function(e, obj) {
2796
2797 if (obj.hideProcId) {
2798 clearTimeout(obj.hideProcId);
2799 obj.hideProcId = null;
2800 }
2801
2802 var context = this;
2803 YAHOO.util.Event.addListener(context, "mousemove", obj.onContextMouseMove, obj);
2804
2805 if (context.title) {
2806 obj._tempTitle = context.title;
2807 context.title = "";
2808 }
2809
2810 /**
2811 * The unique process ID associated with the thread responsible for showing the Tooltip.
2812 * @type int
2813 */
2814 obj.showProcId = obj.doShow(e, context);
2815};
2816
2817/**
2818* The default event handler fired when the user mouses out of the context element.
2819* @method onContextMouseOut
2820 * @param {DOMEvent} eThe current DOM event
2821 * @param {Object} objThe object argument
2822*/
2823YAHOO.widget.Tooltip.prototype.onContextMouseOut = function(e, obj) {
2824 var el = this;
2825
2826 if (obj._tempTitle) {
2827 el.title = obj._tempTitle;
2828 obj._tempTitle = null;
2829 }
2830
2831 if (obj.showProcId) {
2832 clearTimeout(obj.showProcId);
2833 obj.showProcId = null;
2834 }
2835
2836 if (obj.hideProcId) {
2837 clearTimeout(obj.hideProcId);
2838 obj.hideProcId = null;
2839 }
2840
2841
2842 obj.hideProcId = setTimeout(function() {
2843 obj.hide();
2844 }, obj.cfg.getProperty("hidedelay"));
2845};
2846
2847// END BUILT-IN DOM EVENT HANDLERS //
2848
2849/**
2850* Processes the showing of the Tooltip by setting the timeout delay and offset of the Tooltip.
2851* @method doShow
2852 * @param {DOMEvent} eThe current DOM event
2853 * @return {Number}The process ID of the timeout function associated with doShow
2854*/
2855YAHOO.widget.Tooltip.prototype.doShow = function(e, context) {
2856
2857 var yOffset = 25;
2858 if (this.browser == "opera" && context.tagName == "A") {
2859 yOffset += 12;
2860 }
2861
2862 var me = this;
2863 return setTimeout(
2864 function() {
2865 if (me._tempTitle) {
2866 me.setBody(me._tempTitle);
2867 } else {
2868 me.cfg.refireEvent("text");
2869 }
2870
2871 me.moveTo(me.pageX, me.pageY + yOffset);
2872 if (me.cfg.getProperty("preventoverlap")) {
2873 me.preventOverlap(me.pageX, me.pageY);
2874 }
2875
2876 YAHOO.util.Event.removeListener(context, "mousemove", me.onContextMouseMove);
2877
2878 me.show();
2879 me.hideProcId = me.doHide();
2880 },
2881 this.cfg.getProperty("showdelay"));
2882};
2883
2884/**
2885* Sets the timeout for the auto-dismiss delay, which by default is 5 seconds, meaning that a tooltip will automatically dismiss itself after 5 seconds of being displayed.
2886* @method doHide
2887*/
2888YAHOO.widget.Tooltip.prototype.doHide = function() {
2889 var me = this;
2890 return setTimeout(
2891 function() {
2892 me.hide();
2893 },
2894 this.cfg.getProperty("autodismissdelay"));
2895};
2896
2897/**
2898* Fired when the Tooltip is moved, this event handler is used to prevent the Tooltip from overlapping with its context element.
2899* @method preventOverlay
2900 * @param {Number} pageXThe x coordinate position of the mouse pointer
2901 * @param {Number} pageYThe y coordinate position of the mouse pointer
2902*/
2903YAHOO.widget.Tooltip.prototype.preventOverlap = function(pageX, pageY) {
2904
2905 var height = this.element.offsetHeight;
2906
2907 var elementRegion = YAHOO.util.Dom.getRegion(this.element);
2908
2909 elementRegion.top -= 5;
2910 elementRegion.left -= 5;
2911 elementRegion.right += 5;
2912 elementRegion.bottom += 5;
2913
2914 var mousePoint = new YAHOO.util.Point(pageX, pageY);
2915
2916 if (elementRegion.contains(mousePoint)) {
2917 this.cfg.setProperty("y", (pageY-height-5));
2918 }
2919};
2920
2921/**
2922* Returns a string representation of the object.
2923* @method toString
2924 * @return {String}The string representation of the Tooltip
2925*/
2926YAHOO.widget.Tooltip.prototype.toString = function() {
2927 return "Tooltip " + this.id;
2928};
2929
2930/**
2931* Panel is an implementation of Overlay that behaves like an OS window, with a draggable header and an optional close icon at the top right.
2932* @namespace YAHOO.widget
2933* @class Panel
2934* @extends YAHOO.widget.Overlay
2935* @constructor
2936 * @param {String} elThe element ID representing the Panel <em>OR</em>
2937 * @param {HTMLElement} elThe element representing the Panel
2938 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this Panel. See configuration documentation for more details.
2939*/
2940YAHOO.widget.Panel = function(el, userConfig) {
2941 YAHOO.widget.Panel.superclass.constructor.call(this, el, userConfig);
2942};
2943
2944YAHOO.extend(YAHOO.widget.Panel, YAHOO.widget.Overlay);
2945
2946/**
2947* Constant representing the default CSS class used for a Panel
2948* @property YAHOO.widget.Panel.CSS_PANEL
2949* @static
2950* @final
2951* @type String
2952*/
2953YAHOO.widget.Panel.CSS_PANEL = "panel";
2954
2955/**
2956* Constant representing the default CSS class used for a Panel's wrapping container
2957* @property YAHOO.widget.Panel.CSS_PANEL_CONTAINER
2958* @static
2959* @final
2960* @type String
2961*/
2962YAHOO.widget.Panel.CSS_PANEL_CONTAINER = "panel-container";
2963
2964/**
2965* The Overlay initialization method, which is executed for Overlay and all of its subclasses. This method is automatically called by the constructor, and sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
2966* @method init
2967 * @param {String} elThe element ID representing the Overlay <em>OR</em>
2968 * @param {HTMLElement} elThe element representing the Overlay
2969 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this Overlay. See configuration documentation for more details.
2970*/
2971YAHOO.widget.Panel.prototype.init = function(el, userConfig) {
2972 YAHOO.widget.Panel.superclass.init.call(this, el/*, userConfig*/); // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level
2973
2974 this.beforeInitEvent.fire(YAHOO.widget.Panel);
2975
2976 YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Panel.CSS_PANEL);
2977
2978 this.buildWrapper();
2979
2980 if (userConfig) {
2981 this.cfg.applyConfig(userConfig, true);
2982 }
2983
2984 this.beforeRenderEvent.subscribe(function() {
2985 var draggable = this.cfg.getProperty("draggable");
2986 if (draggable) {
2987 if (! this.header) {
2988 this.setHeader("&nbsp;");
2989 }
2990 }
2991 }, this, true);
2992
2993 var me = this;
2994
2995 this.showMaskEvent.subscribe(function() {
2996 var checkFocusable = function(el) {
2997 if (el.tagName == "A" || el.tagName == "BUTTON" || el.tagName == "SELECT" || el.tagName == "INPUT" || el.tagName == "TEXTAREA" || el.tagName == "FORM") {
2998 if (! YAHOO.util.Dom.isAncestor(me.element, el)) {
2999 YAHOO.util.Event.addListener(el, "focus", el.blur);
3000 return true;
3001 }
3002 } else {
3003 return false;
3004 }
3005 };
3006
3007 this.focusableElements = YAHOO.util.Dom.getElementsBy(checkFocusable);
3008 }, this, true);
3009
3010 this.hideMaskEvent.subscribe(function() {
3011 for (var i=0;i<this.focusableElements.length;i++) {
3012 var el2 = this.focusableElements[i];
3013 YAHOO.util.Event.removeListener(el2, "focus", el2.blur);
3014 }
3015 }, this, true);
3016
3017 this.beforeShowEvent.subscribe(function() {
3018 this.cfg.refireEvent("underlay");
3019 }, this, true);
3020
3021 this.initEvent.fire(YAHOO.widget.Panel);
3022};
3023
3024/**
3025* Initializes the custom events for Module which are fired automatically at appropriate times by the Module class.
3026*/
3027YAHOO.widget.Panel.prototype.initEvents = function() {
3028 YAHOO.widget.Panel.superclass.initEvents.call(this);
3029
3030 /**
3031 * CustomEvent fired after the modality mask is shown
3032 * @event showMaskEvent
3033 */
3034 this.showMaskEvent = new YAHOO.util.CustomEvent("showMask");
3035
3036 /**
3037 * CustomEvent fired after the modality mask is hidden
3038 * @event hideMaskEvent
3039 */
3040 this.hideMaskEvent = new YAHOO.util.CustomEvent("hideMask");
3041
3042 /**
3043 * CustomEvent when the Panel is dragged
3044 * @event dragEvent
3045 */
3046 this.dragEvent = new YAHOO.util.CustomEvent("drag");
3047};
3048
3049/**
3050* Initializes the class's configurable properties which can be changed using the Panel's Config object (cfg).
3051* @method initDefaultConfig
3052*/
3053YAHOO.widget.Panel.prototype.initDefaultConfig = function() {
3054 YAHOO.widget.Panel.superclass.initDefaultConfig.call(this);
3055
3056 // Add panel config properties //
3057
3058 /**
3059 * True if the Panel should display a "close" button
3060 * @config close
3061 * @type Boolean
3062 * @default true
3063 */
3064 this.cfg.addProperty("close", { value:true, handler:this.configClose, validator:this.cfg.checkBoolean, supercedes:["visible"] } );
3065
3066 /**
3067 * True if the Panel should be draggable
3068 * @config draggable
3069 * @type Boolean
3070 * @default true
3071 */
3072 this.cfg.addProperty("draggable", { value:true,handler:this.configDraggable, validator:this.cfg.checkBoolean, supercedes:["visible"] } );
3073
3074 /**
3075 * Sets the type of underlay to display for the Panel. Valid values are "shadow", "matte", and "none".
3076 * @config underlay
3077 * @type String
3078 * @default shadow
3079 */
3080 this.cfg.addProperty("underlay", { value:"shadow", handler:this.configUnderlay, supercedes:["visible"] } );
3081
3082 /**
3083 * True if the Panel should be displayed in a modal fashion, automatically creating a transparent mask over the document that will not be removed until the Panel is dismissed.
3084 * @config modal
3085 * @type Boolean
3086 * @default false
3087 */
3088 this.cfg.addProperty("modal",{ value:false, handler:this.configModal, validator:this.cfg.checkBoolean, supercedes:["visible"] } );
3089
3090 /**
3091 * A KeyListener (or array of KeyListeners) that will be enabled when the Panel is shown, and disabled when the Panel is hidden.
3092 * @config keylisteners
3093 * @type YAHOO.util.KeyListener[]
3094 * @default null
3095 */
3096 this.cfg.addProperty("keylisteners", { handler:this.configKeyListeners, suppressEvent:true, supercedes:["visible"] } );
3097};
3098
3099// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //
3100
3101/**
3102* The default event handler fired when the "close" property is changed. The method controls the appending or hiding of the close icon at the top right of the Panel.
3103* @method configClose
3104 * @param {String} typeThe CustomEvent type (usually the property name)
3105 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3106 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3107*/
3108YAHOO.widget.Panel.prototype.configClose = function(type, args, obj) {
3109 var val = args[0];
3110
3111 var doHide = function(e, obj) {
3112 obj.hide();
3113 };
3114
3115 if (val) {
3116 if (! this.close) {
3117 this.close = document.createElement("DIV");
3118 YAHOO.util.Dom.addClass(this.close, "close");
3119
3120 if (this.isSecure) {
3121 YAHOO.util.Dom.addClass(this.close, "secure");
3122 } else {
3123 YAHOO.util.Dom.addClass(this.close, "nonsecure");
3124 }
3125
3126 this.close.innerHTML = "&nbsp;";
3127 this.innerElement.appendChild(this.close);
3128 YAHOO.util.Event.addListener(this.close, "click", doHide, this);
3129 } else {
3130 this.close.style.display = "block";
3131 }
3132 } else {
3133 if (this.close) {
3134 this.close.style.display = "none";
3135 }
3136 }
3137};
3138
3139/**
3140* The default event handler fired when the "draggable" property is changed.
3141* @method configDraggable
3142 * @param {String} typeThe CustomEvent type (usually the property name)
3143 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3144 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3145*/
3146YAHOO.widget.Panel.prototype.configDraggable = function(type, args, obj) {
3147 var val = args[0];
3148 if (val) {
3149 if (this.header) {
3150 YAHOO.util.Dom.setStyle(this.header,"cursor","move");
3151 this.registerDragDrop();
3152 }
3153 } else {
3154 if (this.dd) {
3155 this.dd.unreg();
3156 }
3157 if (this.header) {
3158 YAHOO.util.Dom.setStyle(this.header,"cursor","auto");
3159 }
3160 }
3161};
3162
3163/**
3164* The default event handler fired when the "underlay" property is changed.
3165* @method configUnderlay
3166 * @param {String} typeThe CustomEvent type (usually the property name)
3167 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3168 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3169*/
3170YAHOO.widget.Panel.prototype.configUnderlay = function(type, args, obj) {
3171 var val = args[0];
3172
3173 switch (val.toLowerCase()) {
3174 case "shadow":
3175 YAHOO.util.Dom.removeClass(this.element, "matte");
3176 YAHOO.util.Dom.addClass(this.element, "shadow");
3177
3178 if (! this.underlay) { // create if not already in DOM
3179 this.underlay = document.createElement("DIV");
3180 this.underlay.className = "underlay";
3181 this.underlay.innerHTML = "&nbsp;";
3182 this.element.appendChild(this.underlay);
3183 }
3184
3185 this.sizeUnderlay();
3186 break;
3187 case "matte":
3188 YAHOO.util.Dom.removeClass(this.element, "shadow");
3189 YAHOO.util.Dom.addClass(this.element, "matte");
3190 break;
3191 default:
3192 YAHOO.util.Dom.removeClass(this.element, "shadow");
3193 YAHOO.util.Dom.removeClass(this.element, "matte");
3194 break;
3195 }
3196};
3197
3198/**
3199* The default event handler fired when the "modal" property is changed. This handler subscribes or unsubscribes to the show and hide events to handle the display or hide of the modality mask.
3200* @method configModal
3201 * @param {String} typeThe CustomEvent type (usually the property name)
3202 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3203 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3204*/
3205YAHOO.widget.Panel.prototype.configModal = function(type, args, obj) {
3206 var modal = args[0];
3207
3208 if (modal) {
3209 this.buildMask();
3210
3211 if (! YAHOO.util.Config.alreadySubscribed( this.beforeShowEvent, this.showMask, this ) ) {
3212 this.beforeShowEvent.subscribe(this.showMask, this, true);
3213 }
3214 if (! YAHOO.util.Config.alreadySubscribed( this.hideEvent, this.hideMask, this) ) {
3215 this.hideEvent.subscribe(this.hideMask, this, true);
3216 }
3217 if (! YAHOO.util.Config.alreadySubscribed( YAHOO.widget.Overlay.windowResizeEvent, this.sizeMask, this ) ) {
3218 YAHOO.widget.Overlay.windowResizeEvent.subscribe(this.sizeMask, this, true);
3219 }
3220 if (! YAHOO.util.Config.alreadySubscribed( this.destroyEvent, this.removeMask, this) ) {
3221 this.destroyEvent.subscribe(this.removeMask, this, true);
3222 }
3223
3224 this.cfg.refireEvent("zIndex");
3225 } else {
3226 this.beforeShowEvent.unsubscribe(this.showMask, this);
3227 this.hideEvent.unsubscribe(this.hideMask, this);
3228 YAHOO.widget.Overlay.windowResizeEvent.unsubscribe(this.sizeMask, this);
3229 this.destroyEvent.unsubscribe(this.removeMask, this);
3230 }
3231};
3232
3233/**
3234* Removes the modality mask.
3235* @method removeMask
3236*/
3237YAHOO.widget.Panel.prototype.removeMask = function() {
3238 if (this.mask) {
3239 if (this.mask.parentNode) {
3240 this.mask.parentNode.removeChild(this.mask);
3241 }
3242 this.mask = null;
3243 }
3244};
3245
3246/**
3247* The default event handler fired when the "keylisteners" property is changed.
3248* @method configKeyListeners
3249 * @param {String} typeThe CustomEvent type (usually the property name)
3250 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3251 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3252*/
3253YAHOO.widget.Panel.prototype.configKeyListeners = function(type, args, obj) {
3254 var listeners = args[0];
3255
3256 if (listeners) {
3257 if (listeners instanceof Array) {
3258 for (var i=0;i<listeners.length;i++) {
3259 var listener = listeners[i];
3260
3261 if (! YAHOO.util.Config.alreadySubscribed(this.showEvent, listener.enable, listener)) {
3262 this.showEvent.subscribe(listener.enable, listener, true);
3263 }
3264 if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent, listener.disable, listener)) {
3265 this.hideEvent.subscribe(listener.disable, listener, true);
3266 this.destroyEvent.subscribe(listener.disable, listener, true);
3267 }
3268 }
3269 } else {
3270 if (! YAHOO.util.Config.alreadySubscribed(this.showEvent, listeners.enable, listeners)) {
3271 this.showEvent.subscribe(listeners.enable, listeners, true);
3272 }
3273 if (! YAHOO.util.Config.alreadySubscribed(this.hideEvent, listeners.disable, listeners)) {
3274 this.hideEvent.subscribe(listeners.disable, listeners, true);
3275 this.destroyEvent.subscribe(listeners.disable, listeners, true);
3276 }
3277 }
3278 }
3279};
3280
3281/**
3282* The default event handler fired when the "height" property is changed.
3283* @method configHeight
3284 * @param {String} typeThe CustomEvent type (usually the property name)
3285 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3286 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3287*/
3288YAHOO.widget.Panel.prototype.configHeight = function(type, args, obj) {
3289 var height = args[0];
3290 var el = this.innerElement;
3291 YAHOO.util.Dom.setStyle(el, "height", height);
3292 this.cfg.refireEvent("underlay");
3293 this.cfg.refireEvent("iframe");
3294};
3295
3296/**
3297* The default event handler fired when the "width" property is changed.
3298* @method configWidth
3299 * @param {String} typeThe CustomEvent type (usually the property name)
3300 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3301 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3302*/
3303YAHOO.widget.Panel.prototype.configWidth = function(type, args, obj) {
3304 var width = args[0];
3305 var el = this.innerElement;
3306 YAHOO.util.Dom.setStyle(el, "width", width);
3307 this.cfg.refireEvent("underlay");
3308 this.cfg.refireEvent("iframe");
3309};
3310
3311/**
3312* The default event handler fired when the "zIndex" property is changed.
3313* @method configzIndex
3314 * @param {String} typeThe CustomEvent type (usually the property name)
3315 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3316 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3317*/
3318YAHOO.widget.Panel.prototype.configzIndex = function(type, args, obj) {
3319 YAHOO.widget.Panel.superclass.configzIndex.call(this, type, args, obj);
3320
3321 var maskZ = 0;
3322 var currentZ = YAHOO.util.Dom.getStyle(this.element, "zIndex");
3323
3324 if (this.mask) {
3325 if (! currentZ || isNaN(currentZ)) {
3326 currentZ = 0;
3327 }
3328
3329 if (currentZ === 0) {
3330 this.cfg.setProperty("zIndex", 1);
3331 } else {
3332 maskZ = currentZ - 1;
3333 YAHOO.util.Dom.setStyle(this.mask, "zIndex", maskZ);
3334 }
3335
3336 }
3337};
3338
3339// END BUILT-IN PROPERTY EVENT HANDLERS //
3340
3341/**
3342* Builds the wrapping container around the Panel that is used for positioning the shadow and matte underlays. The container element is assigned to a local instance variable called container, and the element is reinserted inside of it.
3343* @method buildWrapper
3344*/
3345YAHOO.widget.Panel.prototype.buildWrapper = function() {
3346 var elementParent = this.element.parentNode;
3347 var originalElement = this.element;
3348
3349 var wrapper = document.createElement("DIV");
3350 wrapper.className = YAHOO.widget.Panel.CSS_PANEL_CONTAINER;
3351 wrapper.id = originalElement.id + "_c";
3352
3353 if (elementParent) {
3354 elementParent.insertBefore(wrapper, originalElement);
3355 }
3356
3357 wrapper.appendChild(originalElement);
3358
3359 this.element = wrapper;
3360 this.innerElement = originalElement;
3361
3362 YAHOO.util.Dom.setStyle(this.innerElement, "visibility", "inherit");
3363};
3364
3365/**
3366* Adjusts the size of the shadow based on the size of the element.
3367* @method sizeUnderlay
3368*/
3369YAHOO.widget.Panel.prototype.sizeUnderlay = function() {
3370 if (this.underlay && this.browser != "gecko" && this.browser != "safari") {
3371 this.underlay.style.width = this.innerElement.offsetWidth + "px";
3372 this.underlay.style.height = this.innerElement.offsetHeight + "px";
3373 }
3374};
3375
3376/**
3377* Event handler fired when the resize monitor element is resized.
3378* @method onDomResize
3379 * @param {DOMEvent} eThe resize DOM event
3380 * @param {Object} objThe scope object
3381*/
3382YAHOO.widget.Panel.prototype.onDomResize = function(e, obj) {
3383 YAHOO.widget.Panel.superclass.onDomResize.call(this, e, obj);
3384 var me = this;
3385 setTimeout(function() {
3386 me.sizeUnderlay();
3387 }, 0);
3388};
3389
3390/**
3391* Registers the Panel's header for drag & drop capability.
3392* @method registerDragDrop
3393*/
3394YAHOO.widget.Panel.prototype.registerDragDrop = function() {
3395 if (this.header) {
3396 this.dd = new YAHOO.util.DD(this.element.id, this.id);
3397
3398 if (! this.header.id) {
3399 this.header.id = this.id + "_h";
3400 }
3401
3402 var me = this;
3403
3404 this.dd.startDrag = function() {
3405
3406 if (me.browser == "ie") {
3407 YAHOO.util.Dom.addClass(me.element,"drag");
3408 }
3409
3410 if (me.cfg.getProperty("constraintoviewport")) {
3411 var offsetHeight = me.element.offsetHeight;
3412 var offsetWidth = me.element.offsetWidth;
3413
3414 var viewPortWidth = YAHOO.util.Dom.getViewportWidth();
3415 var viewPortHeight = YAHOO.util.Dom.getViewportHeight();
3416
3417 var scrollX = window.scrollX || document.documentElement.scrollLeft;
3418 var scrollY = window.scrollY || document.documentElement.scrollTop;
3419
3420 var topConstraint = scrollY + 10;
3421 var leftConstraint = scrollX + 10;
3422 var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10;
3423 var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10;
3424
3425 this.minX = leftConstraint;
3426 this.maxX = rightConstraint;
3427 this.constrainX = true;
3428
3429 this.minY = topConstraint;
3430 this.maxY = bottomConstraint;
3431 this.constrainY = true;
3432 } else {
3433 this.constrainX = false;
3434 this.constrainY = false;
3435 }
3436
3437 me.dragEvent.fire("startDrag", arguments);
3438 };
3439
3440 this.dd.onDrag = function() {
3441 me.syncPosition();
3442 me.cfg.refireEvent("iframe");
3443 if (this.platform == "mac" && this.browser == "gecko") {
3444 this.showMacGeckoScrollbars();
3445 }
3446
3447 me.dragEvent.fire("onDrag", arguments);
3448 };
3449
3450 this.dd.endDrag = function() {
3451 if (me.browser == "ie") {
3452 YAHOO.util.Dom.removeClass(me.element,"drag");
3453 }
3454
3455 me.dragEvent.fire("endDrag", arguments);
3456 };
3457
3458 this.dd.setHandleElId(this.header.id);
3459 this.dd.addInvalidHandleType("INPUT");
3460 this.dd.addInvalidHandleType("SELECT");
3461 this.dd.addInvalidHandleType("TEXTAREA");
3462 }
3463};
3464
3465/**
3466* Builds the mask that is laid over the document when the Panel is configured to be modal.
3467* @method buildMask
3468*/
3469YAHOO.widget.Panel.prototype.buildMask = function() {
3470 if (! this.mask) {
3471 this.mask = document.createElement("DIV");
3472 this.mask.id = this.id + "_mask";
3473 this.mask.className = "mask";
3474 this.mask.innerHTML = "&nbsp;";
3475
3476 var maskClick = function(e, obj) {
3477 YAHOO.util.Event.stopEvent(e);
3478 };
3479
3480 var firstChild = document.body.firstChild;
3481 if (firstChild){
3482 document.body.insertBefore(this.mask, document.body.firstChild);
3483 } else {
3484 document.body.appendChild(this.mask);
3485 }
3486 }
3487};
3488
3489/**
3490* Hides the modality mask.
3491* @method hideMask
3492*/
3493YAHOO.widget.Panel.prototype.hideMask = function() {
3494 if (this.cfg.getProperty("modal") && this.mask) {
3495 this.mask.style.display = "none";
3496 this.hideMaskEvent.fire();
3497 YAHOO.util.Dom.removeClass(document.body, "masked");
3498 }
3499};
3500
3501/**
3502* Shows the modality mask.
3503* @method showMask
3504*/
3505YAHOO.widget.Panel.prototype.showMask = function() {
3506 if (this.cfg.getProperty("modal") && this.mask) {
3507 YAHOO.util.Dom.addClass(document.body, "masked");
3508 this.sizeMask();
3509 this.mask.style.display = "block";
3510 this.showMaskEvent.fire();
3511 }
3512};
3513
3514/**
3515* Sets the size of the modality mask to cover the entire scrollable area of the document
3516* @method sizeMask
3517*/
3518YAHOO.widget.Panel.prototype.sizeMask = function() {
3519 if (this.mask) {
3520 this.mask.style.height = YAHOO.util.Dom.getDocumentHeight()+"px";
3521 this.mask.style.width = YAHOO.util.Dom.getDocumentWidth()+"px";
3522 }
3523};
3524
3525/**
3526* Renders the Panel by inserting the elements that are not already in the main Panel into their correct places. Optionally appends the Panel to the specified node prior to the render's execution. NOTE: For Panels without existing markup, the appendToNode argument is REQUIRED. If this argument is ommitted and the current element is not present in the document, the function will return false, indicating that the render was a failure.
3527* @method render
3528 * @param {String} appendToNodeThe element id to which the Module should be appended to prior to rendering <em>OR</em>
3529 * @param {HTMLElement} appendToNodeThe element to which the Module should be appended to prior to rendering
3530* @return {boolean} Success or failure of the render
3531*/
3532YAHOO.widget.Panel.prototype.render = function(appendToNode) {
3533 return YAHOO.widget.Panel.superclass.render.call(this, appendToNode, this.innerElement);
3534};
3535
3536/**
3537* Returns a String representation of the object.
3538* @method toString
3539* @return {String} The string representation of the Panel.
3540*/
3541YAHOO.widget.Panel.prototype.toString = function() {
3542 return "Panel " + this.id;
3543};
3544
3545/**
3546* Dialog is an implementation of Panel that can be used to submit form data. Built-in functionality for buttons with event handlers is included, and button sets can be build dynamically, or the preincluded ones for Submit/Cancel and OK/Cancel can be utilized. Forms can be processed in 3 ways -- via an asynchronous Connection utility call, a simple form POST or GET, or manually.
3547* @namespace YAHOO.widget
3548* @class Dialog
3549* @extends YAHOO.widget.Panel
3550* @constructor
3551 * @param {String} elThe element ID representing the Dialog <em>OR</em>
3552 * @param {HTMLElement} elThe element representing the Dialog
3553 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this Dialog. See configuration documentation for more details.
3554*/
3555YAHOO.widget.Dialog = function(el, userConfig) {
3556 YAHOO.widget.Dialog.superclass.constructor.call(this, el, userConfig);
3557};
3558
3559YAHOO.extend(YAHOO.widget.Dialog, YAHOO.widget.Panel);
3560
3561/**
3562* Constant representing the default CSS class used for a Dialog
3563* @property YAHOO.widget.Dialog.CSS_DIALOG
3564* @static
3565* @final
3566* @type String
3567*/
3568YAHOO.widget.Dialog.CSS_DIALOG = "dialog";
3569
3570/**
3571* Initializes the class's configurable properties which can be changed using the Dialog's Config object (cfg).
3572* @method initDefaultConfig
3573*/
3574YAHOO.widget.Dialog.prototype.initDefaultConfig = function() {
3575 YAHOO.widget.Dialog.superclass.initDefaultConfig.call(this);
3576
3577 /**
3578 * The internally maintained callback object for use with the Connection utility
3579 * @property callback
3580 * @type Object
3581 */
3582 this.callback = {
3583 /**
3584 * The function to execute upon success of the Connection submission
3585 * @property callback.success
3586 * @type Function
3587 */
3588 success : null,
3589 /**
3590 * The function to execute upon failure of the Connection submission
3591 * @property callback.failure
3592 * @type Function
3593 */
3594 failure : null,
3595 /**
3596 * The arbitraty argument or arguments to pass to the Connection callback functions
3597 * @property callback.argument
3598 * @type Object
3599 */
3600 argument: null
3601 };
3602
3603 // Add form dialog config properties //
3604
3605 /**
3606 * The method to use for posting the Dialog's form. Possible values are "async", "form", and "manual".
3607 * @config postmethod
3608 * @type String
3609 * @default async
3610 */
3611 this.cfg.addProperty("postmethod", { value:"async", validator:function(val) {
3612 if (val != "form" && val != "async" && val != "none" && val != "manual") {
3613 return false;
3614 } else {
3615 return true;
3616 }
3617 } });
3618
3619 /**
3620 * Object literal(s) defining the buttons for the Dialog's footer.
3621 * @config buttons
3622 * @type Object[]
3623 * @default "none"
3624 */
3625 this.cfg.addProperty("buttons", { value:"none",handler:this.configButtons } );
3626};
3627
3628/**
3629* Initializes the custom events for Dialog which are fired automatically at appropriate times by the Dialog class.
3630* @method initEvents
3631*/
3632YAHOO.widget.Dialog.prototype.initEvents = function() {
3633 YAHOO.widget.Dialog.superclass.initEvents.call(this);
3634
3635 /**
3636 * CustomEvent fired prior to submission
3637 * @event beforeSumitEvent
3638 */
3639 this.beforeSubmitEvent= new YAHOO.util.CustomEvent("beforeSubmit");
3640
3641 /**
3642 * CustomEvent fired after submission
3643 * @event submitEvent
3644 */
3645 this.submitEvent = new YAHOO.util.CustomEvent("submit");
3646
3647 /**
3648 * CustomEvent fired prior to manual submission
3649 * @event manualSubmitEvent
3650 */
3651 this.manualSubmitEvent= new YAHOO.util.CustomEvent("manualSubmit");
3652
3653 /**
3654 * CustomEvent fired prior to asynchronous submission
3655 * @event asyncSubmitEvent
3656 */
3657 this.asyncSubmitEvent= new YAHOO.util.CustomEvent("asyncSubmit");
3658
3659 /**
3660 * CustomEvent fired prior to form-based submission
3661 * @event formSubmitEvent
3662 */
3663 this.formSubmitEvent= new YAHOO.util.CustomEvent("formSubmit");
3664
3665 /**
3666 * CustomEvent fired after cancel
3667 * @event cancelEvent
3668 */
3669 this.cancelEvent = new YAHOO.util.CustomEvent("cancel");
3670};
3671
3672/**
3673* The Dialog initialization method, which is executed for Dialog and all of its subclasses. This method is automatically called by the constructor, and sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
3674* @method init
3675 * @param {String} elThe element ID representing the Dialog <em>OR</em>
3676 * @param {HTMLElement} elThe element representing the Dialog
3677 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this Dialog. See configuration documentation for more details.
3678*/
3679YAHOO.widget.Dialog.prototype.init = function(el, userConfig) {
3680 YAHOO.widget.Dialog.superclass.init.call(this, el/*, userConfig*/); // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level
3681
3682 this.beforeInitEvent.fire(YAHOO.widget.Dialog);
3683
3684 YAHOO.util.Dom.addClass(this.element, YAHOO.widget.Dialog.CSS_DIALOG);
3685
3686 this.cfg.setProperty("visible", false);
3687
3688 if (userConfig) {
3689 this.cfg.applyConfig(userConfig, true);
3690 }
3691
3692 this.renderEvent.subscribe(this.registerForm, this, true);
3693
3694 this.showEvent.subscribe(this.focusFirst, this, true);
3695 this.beforeHideEvent.subscribe(this.blurButtons, this, true);
3696
3697 this.beforeRenderEvent.subscribe(function() {
3698 var buttonCfg = this.cfg.getProperty("buttons");
3699 if (buttonCfg && buttonCfg != "none") {
3700 if (! this.footer) {
3701 this.setFooter("");
3702 }
3703 }
3704 }, this, true);
3705
3706 this.initEvent.fire(YAHOO.widget.Dialog);
3707};
3708
3709/**
3710* Performs the submission of the Dialog form depending on the value of "postmethod" property.
3711* @method doSubmit
3712*/
3713YAHOO.widget.Dialog.prototype.doSubmit = function() {
3714 var pm = this.cfg.getProperty("postmethod");
3715 switch (pm) {
3716 case "async":
3717 var method = this.form.getAttribute("method") || 'POST';
3718 method = method.toUpperCase();
3719 YAHOO.util.Connect.setForm(this.form);
3720 var cObj = YAHOO.util.Connect.asyncRequest(method, this.form.getAttribute("action"), this.callback);
3721 this.asyncSubmitEvent.fire();
3722 break;
3723 case "form":
3724 this.form.submit();
3725 this.formSubmitEvent.fire();
3726 break;
3727 case "none":
3728 case "manual":
3729 this.manualSubmitEvent.fire();
3730 break;
3731 }
3732};
3733
3734/**
3735* Prepares the Dialog's internal FORM object, creating one if one is not currently present.
3736* @method registerForm
3737*/
3738YAHOO.widget.Dialog.prototype.registerForm = function() {
3739 var form = this.element.getElementsByTagName("FORM")[0];
3740
3741 if (! form) {
3742 var formHTML = "<form name=\"frm_" + this.id + "\" action=\"\"></form>";
3743 this.body.innerHTML += formHTML;
3744 form = this.element.getElementsByTagName("FORM")[0];
3745 }
3746
3747 this.firstFormElement = function() {
3748 for (var f=0;f<form.elements.length;f++ ) {
3749 var el = form.elements[f];
3750 if (el.focus) {
3751 if (el.type && el.type != "hidden") {
3752 return el;
3753 }
3754 }
3755 }
3756 return null;
3757 }();
3758
3759 this.lastFormElement = function() {
3760 for (var f=form.elements.length-1;f>=0;f-- ) {
3761 var el = form.elements[f];
3762 if (el.focus) {
3763 if (el.type && el.type != "hidden") {
3764 return el;
3765 }
3766 }
3767 }
3768 return null;
3769 }();
3770
3771 this.form = form;
3772
3773 if (this.cfg.getProperty("modal") && this.form) {
3774
3775 var me = this;
3776
3777 var firstElement = this.firstFormElement || this.firstButton;
3778 if (firstElement) {
3779 this.preventBackTab = new YAHOO.util.KeyListener(firstElement, { shift:true, keys:9 }, {fn:me.focusLast, scope:me, correctScope:true} );
3780 this.showEvent.subscribe(this.preventBackTab.enable, this.preventBackTab, true);
3781 this.hideEvent.subscribe(this.preventBackTab.disable, this.preventBackTab, true);
3782 }
3783
3784 var lastElement = this.lastButton || this.lastFormElement;
3785 if (lastElement) {
3786 this.preventTabOut = new YAHOO.util.KeyListener(lastElement, { shift:false, keys:9 }, {fn:me.focusFirst, scope:me, correctScope:true} );
3787 this.showEvent.subscribe(this.preventTabOut.enable, this.preventTabOut, true);
3788 this.hideEvent.subscribe(this.preventTabOut.disable, this.preventTabOut, true);
3789 }
3790 }
3791};
3792
3793// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //
3794
3795/**
3796* The default event handler for the "buttons" configuration property
3797* @method configButtons
3798 * @param {String} typeThe CustomEvent type (usually the property name)
3799 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
3800 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
3801*/
3802YAHOO.widget.Dialog.prototype.configButtons = function(type, args, obj) {
3803 var buttons = args[0];
3804 if (buttons != "none") {
3805 this.buttonSpan = null;
3806 this.buttonSpan = document.createElement("SPAN");
3807 this.buttonSpan.className = "button-group";
3808
3809 for (var b=0;b<buttons.length;b++) {
3810 var button = buttons[b];
3811
3812 var htmlButton = document.createElement("BUTTON");
3813 htmlButton.setAttribute("type", "button");
3814
3815 if (button.isDefault) {
3816 htmlButton.className = "default";
3817 this.defaultHtmlButton = htmlButton;
3818 }
3819
3820 htmlButton.appendChild(document.createTextNode(button.text));
3821 YAHOO.util.Event.addListener(htmlButton, "click", button.handler, this, true);
3822
3823 this.buttonSpan.appendChild(htmlButton);
3824 button.htmlButton = htmlButton;
3825
3826 if (b === 0) {
3827 this.firstButton = button.htmlButton;
3828 }
3829
3830 if (b == (buttons.length-1)) {
3831 this.lastButton = button.htmlButton;
3832 }
3833
3834 }
3835
3836 this.setFooter(this.buttonSpan);
3837
3838 this.cfg.refireEvent("iframe");
3839 this.cfg.refireEvent("underlay");
3840 } else { // Do cleanup
3841 if (this.buttonSpan) {
3842 if (this.buttonSpan.parentNode) {
3843 this.buttonSpan.parentNode.removeChild(this.buttonSpan);
3844 }
3845
3846 this.buttonSpan = null;
3847 this.firstButton = null;
3848 this.lastButton = null;
3849 this.defaultHtmlButton = null;
3850 }
3851 }
3852};
3853
3854
3855/**
3856* The default event handler used to focus the first field of the form when the Dialog is shown.
3857* @method focusFirst
3858*/
3859YAHOO.widget.Dialog.prototype.focusFirst = function(type,args,obj) {
3860 if (args) {
3861 var e = args[1];
3862 if (e) {
3863 YAHOO.util.Event.stopEvent(e);
3864 }
3865 }
3866
3867 if (this.firstFormElement) {
3868 this.firstFormElement.focus();
3869 } else {
3870 this.focusDefaultButton();
3871 }
3872};
3873
3874/**
3875* Sets the focus to the last button in the button or form element in the Dialog
3876* @method focusLast
3877*/
3878YAHOO.widget.Dialog.prototype.focusLast = function(type,args,obj) {
3879 if (args) {
3880 var e = args[1];
3881 if (e) {
3882 YAHOO.util.Event.stopEvent(e);
3883 }
3884 }
3885
3886 var buttons = this.cfg.getProperty("buttons");
3887 if (buttons && buttons instanceof Array) {
3888 this.focusLastButton();
3889 } else {
3890 if (this.lastFormElement) {
3891 this.lastFormElement.focus();
3892 }
3893 }
3894};
3895
3896/**
3897* Sets the focus to the button that is designated as the default. By default, his handler is executed when the show event is fired.
3898* @method focusDefaultButton
3899*/
3900YAHOO.widget.Dialog.prototype.focusDefaultButton = function() {
3901 if (this.defaultHtmlButton) {
3902 this.defaultHtmlButton.focus();
3903 }
3904};
3905
3906/**
3907* Blurs all the html buttons
3908* @method blurButtons
3909*/
3910YAHOO.widget.Dialog.prototype.blurButtons = function() {
3911 var buttons = this.cfg.getProperty("buttons");
3912 if (buttons && buttons instanceof Array) {
3913 var html = buttons[0].htmlButton;
3914 if (html) {
3915 html.blur();
3916 }
3917 }
3918};
3919
3920/**
3921* Sets the focus to the first button in the button list
3922* @method focusFirstButton
3923*/
3924YAHOO.widget.Dialog.prototype.focusFirstButton = function() {
3925 var buttons = this.cfg.getProperty("buttons");
3926 if (buttons && buttons instanceof Array) {
3927 var html = buttons[0].htmlButton;
3928 if (html) {
3929 html.focus();
3930 }
3931 }
3932};
3933
3934/**
3935* Sets the focus to the first button in the button list
3936* @method focusLastButton
3937*/
3938YAHOO.widget.Dialog.prototype.focusLastButton = function() {
3939 var buttons = this.cfg.getProperty("buttons");
3940 if (buttons && buttons instanceof Array) {
3941 var html = buttons[buttons.length-1].htmlButton;
3942 if (html) {
3943 html.focus();
3944 }
3945 }
3946};
3947
3948// END BUILT-IN PROPERTY EVENT HANDLERS //
3949
3950/**
3951* Built-in function hook for writing a validation function that will be checked for a "true" value prior to a submit. This function, as implemented by default, always returns true, so it should be overridden if validation is necessary.
3952* @method validate
3953*/
3954YAHOO.widget.Dialog.prototype.validate = function() {
3955 return true;
3956};
3957
3958/**
3959* Executes a submit of the Dialog followed by a hide, if validation is successful.
3960* @method submit
3961*/
3962YAHOO.widget.Dialog.prototype.submit = function() {
3963 if (this.validate()) {
3964 this.beforeSubmitEvent.fire();
3965 this.doSubmit();
3966 this.submitEvent.fire();
3967 this.hide();
3968 return true;
3969 } else {
3970 return false;
3971 }
3972};
3973
3974/**
3975* Executes the cancel of the Dialog followed by a hide.
3976* @method cancel
3977*/
3978YAHOO.widget.Dialog.prototype.cancel = function() {
3979 this.cancelEvent.fire();
3980 this.hide();
3981};
3982
3983/**
3984* Returns a JSON-compatible data structure representing the data currently contained in the form.
3985* @method getData
3986* @return {Object} A JSON object reprsenting the data of the current form.
3987*/
3988YAHOO.widget.Dialog.prototype.getData = function() {
3989 var form = this.form;
3990 var data = {};
3991
3992 if (form) {
3993 for (var i in this.form) {
3994 var formItem = form[i];
3995 if (formItem) {
3996 if (formItem.tagName) { // Got a single form item
3997 switch (formItem.tagName) {
3998 case "INPUT":
3999 switch (formItem.type) {
4000 case "checkbox":
4001 data[i] = formItem.checked;
4002 break;
4003 case "textbox":
4004 case "text":
4005 case "hidden":
4006 data[i] = formItem.value;
4007 break;
4008 }
4009 break;
4010 case "TEXTAREA":
4011 data[i] = formItem.value;
4012 break;
4013 case "SELECT":
4014 var val = [];
4015 for (var x=0;x<formItem.options.length;x++){
4016 var option = formItem.options[x];
4017 if (option.selected) {
4018 var selval = option.value;
4019 if (! selval || selval === "") {
4020 selval = option.text;
4021 }
4022 val[val.length] = selval;
4023 }
4024 }
4025 data[i] = val;
4026 break;
4027 }
4028 } else if (formItem[0] && formItem[0].tagName) { // this is an array of form items
4029 if (formItem[0].tagName == "INPUT") {
4030 switch (formItem[0].type) {
4031 case "radio":
4032 for (var r=0; r<formItem.length; r++) {
4033 var radio = formItem[r];
4034 if (radio.checked) {
4035 data[radio.name] = radio.value;
4036 break;
4037 }
4038 }
4039 break;
4040 case "checkbox":
4041 var cbArray = [];
4042 for (var c=0; c<formItem.length; c++) {
4043 var check = formItem[c];
4044 if (check.checked) {
4045 cbArray[cbArray.length] = check.value;
4046 }
4047 }
4048 data[formItem[0].name] = cbArray;
4049 break;
4050 }
4051 }
4052 }
4053 }
4054 }
4055 }
4056 return data;
4057};
4058
4059/**
4060* Returns a string representation of the object.
4061* @method toString
4062 * @return {String}The string representation of the Dialog
4063*/
4064YAHOO.widget.Dialog.prototype.toString = function() {
4065 return "Dialog " + this.id;
4066};
4067
4068/**
4069* SimpleDialog is a simple implementation of Dialog that can be used to submit a single value. Forms can be processed in 3 ways -- via an asynchronous Connection utility call, a simple form POST or GET, or manually.
4070* @namespace YAHOO.widget
4071* @class SimpleDialog
4072* @extends YAHOO.widget.Dialog
4073* @constructor
4074 * @param {String} elThe element ID representing the SimpleDialog <em>OR</em>
4075 * @param {HTMLElement} elThe element representing the SimpleDialog
4076 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this SimpleDialog. See configuration documentation for more details.
4077*/
4078YAHOO.widget.SimpleDialog = function(el, userConfig) {
4079 YAHOO.widget.SimpleDialog.superclass.constructor.call(this, el, userConfig);
4080};
4081
4082YAHOO.extend(YAHOO.widget.SimpleDialog, YAHOO.widget.Dialog);
4083
4084/**
4085* Constant for the standard network icon for a blocking action
4086* @property YAHOO.widget.SimpleDialog.ICON_BLOCK
4087* @static
4088* @final
4089* @type String
4090*/
4091YAHOO.widget.SimpleDialog.ICON_BLOCK = "nt/ic/ut/bsc/blck16_1.gif";
4092
4093/**
4094* Constant for the standard network icon for alarm
4095* @property YAHOO.widget.SimpleDialog.ICON_ALARM
4096* @static
4097* @final
4098* @type String
4099*/
4100YAHOO.widget.SimpleDialog.ICON_ALARM = "nt/ic/ut/bsc/alrt16_1.gif";
4101
4102/**
4103* Constant for the standard network icon for help
4104* @property YAHOO.widget.SimpleDialog.ICON_HELP
4105* @static
4106* @final
4107* @type String
4108*/
4109YAHOO.widget.SimpleDialog.ICON_HELP = "nt/ic/ut/bsc/hlp16_1.gif";
4110
4111/**
4112* Constant for the standard network icon for info
4113* @property YAHOO.widget.SimpleDialog.ICON_INFO
4114* @static
4115* @final
4116* @type String
4117*/
4118YAHOO.widget.SimpleDialog.ICON_INFO = "nt/ic/ut/bsc/info16_1.gif";
4119
4120/**
4121* Constant for the standard network icon for warn
4122* @property YAHOO.widget.SimpleDialog.ICON_WARN
4123* @static
4124* @final
4125* @type String
4126*/
4127YAHOO.widget.SimpleDialog.ICON_WARN = "nt/ic/ut/bsc/warn16_1.gif";
4128
4129/**
4130* Constant for the standard network icon for a tip
4131* @property YAHOO.widget.SimpleDialog.ICON_TIP
4132* @static
4133* @final
4134* @type String
4135*/
4136YAHOO.widget.SimpleDialog.ICON_TIP = "nt/ic/ut/bsc/tip16_1.gif";
4137
4138/**
4139* Constant representing the default CSS class used for a SimpleDialog
4140* @property YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG
4141* @static
4142* @final
4143* @type String
4144*/
4145YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG = "simple-dialog";
4146
4147/**
4148* Initializes the class's configurable properties which can be changed using the SimpleDialog's Config object (cfg).
4149* @method initDefaultConfig
4150*/
4151YAHOO.widget.SimpleDialog.prototype.initDefaultConfig = function() {
4152 YAHOO.widget.SimpleDialog.superclass.initDefaultConfig.call(this);
4153
4154 // Add dialog config properties //
4155
4156 /**
4157 * Sets the informational icon for the SimpleDialog
4158 * @config icon
4159 * @type String
4160 * @default "none"
4161 */
4162 this.cfg.addProperty("icon", { value:"none",handler:this.configIcon, suppressEvent:true } );
4163
4164 /**
4165 * Sets the text for the SimpleDialog
4166 * @config text
4167 * @type String
4168 * @default ""
4169 */
4170 this.cfg.addProperty("text",{ value:"", handler:this.configText, suppressEvent:true, supercedes:["icon"] } );
4171};
4172
4173
4174/**
4175* The SimpleDialog initialization method, which is executed for SimpleDialog and all of its subclasses. This method is automatically called by the constructor, and sets up all DOM references for pre-existing markup, and creates required markup if it is not already present.
4176* @method init
4177 * @param {String} elThe element ID representing the SimpleDialog <em>OR</em>
4178 * @param {HTMLElement} elThe element representing the SimpleDialog
4179 * @param {Object} userConfigThe configuration object literal containing the configuration that should be set for this SimpleDialog. See configuration documentation for more details.
4180*/
4181YAHOO.widget.SimpleDialog.prototype.init = function(el, userConfig) {
4182 YAHOO.widget.SimpleDialog.superclass.init.call(this, el/*, userConfig*/); // Note that we don't pass the user config in here yet because we only want it executed once, at the lowest subclass level
4183
4184 this.beforeInitEvent.fire(YAHOO.widget.SimpleDialog);
4185
4186 YAHOO.util.Dom.addClass(this.element, YAHOO.widget.SimpleDialog.CSS_SIMPLEDIALOG);
4187
4188 this.cfg.queueProperty("postmethod", "manual");
4189
4190 if (userConfig) {
4191 this.cfg.applyConfig(userConfig, true);
4192 }
4193
4194 this.beforeRenderEvent.subscribe(function() {
4195 if (! this.body) {
4196 this.setBody("");
4197 }
4198 }, this, true);
4199
4200 this.initEvent.fire(YAHOO.widget.SimpleDialog);
4201
4202};
4203/**
4204* Prepares the SimpleDialog's internal FORM object, creating one if one is not currently present, and adding the value hidden field.
4205* @method registerForm
4206*/
4207YAHOO.widget.SimpleDialog.prototype.registerForm = function() {
4208 YAHOO.widget.SimpleDialog.superclass.registerForm.call(this);
4209 this.form.innerHTML += "<input type=\"hidden\" name=\"" + this.id + "\" value=\"\"/>";
4210};
4211
4212// BEGIN BUILT-IN PROPERTY EVENT HANDLERS //
4213
4214/**
4215* Fired when the "icon" property is set.
4216* @method configIcon
4217 * @param {String} typeThe CustomEvent type (usually the property name)
4218 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
4219 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
4220*/
4221YAHOO.widget.SimpleDialog.prototype.configIcon = function(type,args,obj) {
4222 var icon = args[0];
4223 if (icon && icon != "none") {
4224 var iconHTML = "<img src=\"" + this.imageRoot + icon + "\" class=\"icon\" />";
4225 this.body.innerHTML = iconHTML + this.body.innerHTML;
4226 }
4227};
4228
4229/**
4230* Fired when the "text" property is set.
4231* @method configText
4232 * @param {String} typeThe CustomEvent type (usually the property name)
4233 * @param {Object[]} argsThe CustomEvent arguments. For configuration handlers, args[0] will equal the newly applied value for the property.
4234 * @param {Object} objThe scope object. For configuration handlers, this will usually equal the owner.
4235*/
4236YAHOO.widget.SimpleDialog.prototype.configText = function(type,args,obj) {
4237 var text = args[0];
4238 if (text) {
4239 this.setBody(text);
4240 this.cfg.refireEvent("icon");
4241 }
4242};
4243// END BUILT-IN PROPERTY EVENT HANDLERS //
4244
4245/**
4246* Returns a string representation of the object.
4247* @method toString
4248 * @return {String}The string representation of the SimpleDialog
4249*/
4250YAHOO.widget.SimpleDialog.prototype.toString = function() {
4251 return "SimpleDialog " + this.id;
4252};
4253
4254/**
4255* ContainerEffect encapsulates animation transitions that are executed when an Overlay is shown or hidden.
4256* @namespace YAHOO.widget
4257* @class ContainerEffect
4258* @constructor
4259 * @param {YAHOO.widget.Overlay} overlay The Overlay that the animation should be associated with
4260 * @param {Object} attrIn The object literal representing the animation arguments to be used for the animate-in transition. The arguments for this literal are: attributes(object, see YAHOO.util.Anim for description), duration(Number), and method(i.e. YAHOO.util.Easing.easeIn).
4261 * @param {Object} attrOut The object literal representing the animation arguments to be used for the animate-out transition. The arguments for this literal are: attributes(object, see YAHOO.util.Anim for description), duration(Number), and method(i.e. YAHOO.util.Easing.easeIn).
4262 * @param {HTMLElement} targetElementOptional. The target element that should be animated during the transition. Defaults to overlay.element.
4263 * @param {class}Optional. The animation class to instantiate. Defaults to YAHOO.util.Anim. Other options include YAHOO.util.Motion.
4264*/
4265YAHOO.widget.ContainerEffect = function(overlay, attrIn, attrOut, targetElement, animClass) {
4266 if (! animClass) {
4267 animClass = YAHOO.util.Anim;
4268 }
4269
4270 /**
4271 * The overlay to animate
4272 * @property overlay
4273 * @type YAHOO.widget.Overlay
4274 */
4275 this.overlay = overlay;
4276 /**
4277 * The animation attributes to use when transitioning into view
4278 * @property attrIn
4279 * @type Object
4280 */
4281 this.attrIn = attrIn;
4282 /**
4283 * The animation attributes to use when transitioning out of view
4284 * @property attrOut
4285 * @type Object
4286 */
4287 this.attrOut = attrOut;
4288 /**
4289 * The target element to be animated
4290 * @property targetElement
4291 * @type HTMLElement
4292 */
4293 this.targetElement = targetElement || overlay.element;
4294 /**
4295 * The animation class to use for animating the overlay
4296 * @property animClass
4297 * @type class
4298 */
4299 this.animClass = animClass;
4300};
4301
4302/**
4303* Initializes the animation classes and events.
4304* @method init
4305*/
4306YAHOO.widget.ContainerEffect.prototype.init = function() {
4307 this.beforeAnimateInEvent = new YAHOO.util.CustomEvent("beforeAnimateIn");
4308 this.beforeAnimateOutEvent = new YAHOO.util.CustomEvent("beforeAnimateOut");
4309
4310 this.animateInCompleteEvent = new YAHOO.util.CustomEvent("animateInComplete");
4311 this.animateOutCompleteEvent = new YAHOO.util.CustomEvent("animateOutComplete");
4312
4313 this.animIn = new this.animClass(this.targetElement, this.attrIn.attributes, this.attrIn.duration, this.attrIn.method);
4314 this.animIn.onStart.subscribe(this.handleStartAnimateIn, this);
4315 this.animIn.onTween.subscribe(this.handleTweenAnimateIn, this);
4316 this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn, this);
4317
4318 this.animOut = new this.animClass(this.targetElement, this.attrOut.attributes, this.attrOut.duration, this.attrOut.method);
4319 this.animOut.onStart.subscribe(this.handleStartAnimateOut, this);
4320 this.animOut.onTween.subscribe(this.handleTweenAnimateOut, this);
4321 this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut, this);
4322};
4323
4324/**
4325* Triggers the in-animation.
4326* @method animateIn
4327*/
4328YAHOO.widget.ContainerEffect.prototype.animateIn = function() {
4329 this.beforeAnimateInEvent.fire();
4330 this.animIn.animate();
4331};
4332
4333/**
4334* Triggers the out-animation.
4335* @method animateOut
4336*/
4337YAHOO.widget.ContainerEffect.prototype.animateOut = function() {
4338 this.beforeAnimateOutEvent.fire();
4339 this.animOut.animate();
4340};
4341
4342/**
4343* The default onStart handler for the in-animation.
4344* @method handleStartAnimateIn
4345 * @param {String} typeThe CustomEvent type
4346 * @param {Object[]} argsThe CustomEvent arguments
4347 * @param {Object} objThe scope object
4348*/
4349YAHOO.widget.ContainerEffect.prototype.handleStartAnimateIn = function(type, args, obj) { };
4350/**
4351* The default onTween handler for the in-animation.
4352* @method handleTweenAnimateIn
4353 * @param {String} typeThe CustomEvent type
4354 * @param {Object[]} argsThe CustomEvent arguments
4355 * @param {Object} objThe scope object
4356*/
4357YAHOO.widget.ContainerEffect.prototype.handleTweenAnimateIn = function(type, args, obj) { };
4358/**
4359* The default onComplete handler for the in-animation.
4360* @method handleCompleteAnimateIn
4361 * @param {String} typeThe CustomEvent type
4362 * @param {Object[]} argsThe CustomEvent arguments
4363 * @param {Object} objThe scope object
4364*/
4365YAHOO.widget.ContainerEffect.prototype.handleCompleteAnimateIn = function(type, args, obj) { };
4366
4367/**
4368* The default onStart handler for the out-animation.
4369* @method handleStartAnimateOut
4370 * @param {String} typeThe CustomEvent type
4371 * @param {Object[]} argsThe CustomEvent arguments
4372 * @param {Object} objThe scope object
4373*/
4374YAHOO.widget.ContainerEffect.prototype.handleStartAnimateOut = function(type, args, obj) { };
4375/**
4376* The default onTween handler for the out-animation.
4377* @method handleTweenAnimateOut
4378 * @param {String} typeThe CustomEvent type
4379 * @param {Object[]} argsThe CustomEvent arguments
4380 * @param {Object} objThe scope object
4381*/
4382YAHOO.widget.ContainerEffect.prototype.handleTweenAnimateOut = function(type, args, obj) { };
4383/**
4384* The default onComplete handler for the out-animation.
4385* @method handleCompleteAnimateOut
4386 * @param {String} typeThe CustomEvent type
4387 * @param {Object[]} argsThe CustomEvent arguments
4388 * @param {Object} objThe scope object
4389*/
4390YAHOO.widget.ContainerEffect.prototype.handleCompleteAnimateOut = function(type, args, obj) { };
4391
4392/**
4393* Returns a string representation of the object.
4394* @method toString
4395 * @return {String}The string representation of the ContainerEffect
4396*/
4397YAHOO.widget.ContainerEffect.prototype.toString = function() {
4398 var output = "ContainerEffect";
4399 if (this.overlay) {
4400 output += " [" + this.overlay.toString() + "]";
4401 }
4402 return output;
4403};
4404
4405/**
4406* A pre-configured ContainerEffect instance that can be used for fading an overlay in and out.
4407* @method FADE
4408* @static
4409 * @param {Overlay}The Overlay object to animate
4410 * @param {Number}The duration of the animation
4411 * @return {ContainerEffect}The configured ContainerEffect object
4412*/
4413YAHOO.widget.ContainerEffect.FADE = function(overlay, dur) {
4414 var fade = new YAHOO.widget.ContainerEffect(overlay, { attributes:{opacity: {from:0, to:1}}, duration:dur, method:YAHOO.util.Easing.easeIn }, { attributes:{opacity: {to:0}}, duration:dur, method:YAHOO.util.Easing.easeOut}, overlay.element );
4415
4416 fade.handleStartAnimateIn = function(type,args,obj) {
4417 YAHOO.util.Dom.addClass(obj.overlay.element, "hide-select");
4418
4419 if (! obj.overlay.underlay) {
4420 obj.overlay.cfg.refireEvent("underlay");
4421 }
4422
4423 if (obj.overlay.underlay) {
4424 obj.initialUnderlayOpacity = YAHOO.util.Dom.getStyle(obj.overlay.underlay, "opacity");
4425 obj.overlay.underlay.style.filter = null;
4426 }
4427
4428 YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "visible");
4429 YAHOO.util.Dom.setStyle(obj.overlay.element, "opacity", 0);
4430 };
4431
4432 fade.handleCompleteAnimateIn = function(type,args,obj) {
4433 YAHOO.util.Dom.removeClass(obj.overlay.element, "hide-select");
4434
4435 if (obj.overlay.element.style.filter) {
4436 obj.overlay.element.style.filter = null;
4437 }
4438
4439 if (obj.overlay.underlay) {
4440 YAHOO.util.Dom.setStyle(obj.overlay.underlay, "opacity", obj.initialUnderlayOpacity);
4441 }
4442
4443 obj.overlay.cfg.refireEvent("iframe");
4444 obj.animateInCompleteEvent.fire();
4445 };
4446
4447 fade.handleStartAnimateOut = function(type, args, obj) {
4448 YAHOO.util.Dom.addClass(obj.overlay.element, "hide-select");
4449
4450 if (obj.overlay.underlay) {
4451 obj.overlay.underlay.style.filter = null;
4452 }
4453 };
4454
4455 fade.handleCompleteAnimateOut = function(type, args, obj) {
4456 YAHOO.util.Dom.removeClass(obj.overlay.element, "hide-select");
4457 if (obj.overlay.element.style.filter) {
4458 obj.overlay.element.style.filter = null;
4459 }
4460 YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "hidden");
4461 YAHOO.util.Dom.setStyle(obj.overlay.element, "opacity", 1);
4462
4463 obj.overlay.cfg.refireEvent("iframe");
4464
4465 obj.animateOutCompleteEvent.fire();
4466 };
4467
4468 fade.init();
4469 return fade;
4470};
4471
4472
4473/**
4474* A pre-configured ContainerEffect instance that can be used for sliding an overlay in and out.
4475* @method SLIDE
4476* @static
4477 * @param {Overlay}The Overlay object to animate
4478 * @param {Number}The duration of the animation
4479 * @return {ContainerEffect}The configured ContainerEffect object
4480*/
4481YAHOO.widget.ContainerEffect.SLIDE = function(overlay, dur) {
4482 var x = overlay.cfg.getProperty("x") || YAHOO.util.Dom.getX(overlay.element);
4483 var y = overlay.cfg.getProperty("y") || YAHOO.util.Dom.getY(overlay.element);
4484
4485 var clientWidth = YAHOO.util.Dom.getClientWidth();
4486 var offsetWidth = overlay.element.offsetWidth;
4487
4488 var slide = new YAHOO.widget.ContainerEffect(overlay, {
4489 attributes:{ points: { to:[x, y] } },
4490 duration:dur,
4491 method:YAHOO.util.Easing.easeIn
4492 },
4493 {
4494 attributes:{ points: { to:[(clientWidth+25), y] } },
4495 duration:dur,
4496 method:YAHOO.util.Easing.easeOut
4497 },
4498 overlay.element,
4499 YAHOO.util.Motion);
4500
4501
4502 slide.handleStartAnimateIn = function(type,args,obj) {
4503 obj.overlay.element.style.left = (-25-offsetWidth) + "px";
4504 obj.overlay.element.style.top = y + "px";
4505 };
4506
4507 slide.handleTweenAnimateIn = function(type, args, obj) {
4508
4509
4510 var pos = YAHOO.util.Dom.getXY(obj.overlay.element);
4511
4512 var currentX = pos[0];
4513 var currentY = pos[1];
4514
4515 if (YAHOO.util.Dom.getStyle(obj.overlay.element, "visibility") == "hidden" && currentX < x) {
4516 YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "visible");
4517 }
4518
4519 obj.overlay.cfg.setProperty("xy", [currentX,currentY], true);
4520 obj.overlay.cfg.refireEvent("iframe");
4521 };
4522
4523 slide.handleCompleteAnimateIn = function(type, args, obj) {
4524 obj.overlay.cfg.setProperty("xy", [x,y], true);
4525 obj.startX = x;
4526 obj.startY = y;
4527 obj.overlay.cfg.refireEvent("iframe");
4528 obj.animateInCompleteEvent.fire();
4529 };
4530
4531 slide.handleStartAnimateOut = function(type, args, obj) {
4532 var vw = YAHOO.util.Dom.getViewportWidth();
4533
4534 var pos = YAHOO.util.Dom.getXY(obj.overlay.element);
4535
4536 var yso = pos[1];
4537
4538 var currentTo = obj.animOut.attributes.points.to;
4539 obj.animOut.attributes.points.to = [(vw+25), yso];
4540 };
4541
4542 slide.handleTweenAnimateOut = function(type, args, obj) {
4543 var pos = YAHOO.util.Dom.getXY(obj.overlay.element);
4544
4545 var xto = pos[0];
4546 var yto = pos[1];
4547
4548 obj.overlay.cfg.setProperty("xy", [xto,yto], true);
4549 obj.overlay.cfg.refireEvent("iframe");
4550 };
4551
4552 slide.handleCompleteAnimateOut = function(type, args, obj) {
4553 YAHOO.util.Dom.setStyle(obj.overlay.element, "visibility", "hidden");
4554
4555 obj.overlay.cfg.setProperty("xy", [x,y]);
4556 obj.animateOutCompleteEvent.fire();
4557 };
4558
4559 slide.init();
4560 return slide;
4561}; \ No newline at end of file
diff --git a/frontend/beta/js/YUI/dom.js b/frontend/beta/js/YUI/dom.js
new file mode 100644
index 0000000..6f04c43
--- a/dev/null
+++ b/frontend/beta/js/YUI/dom.js
@@ -0,0 +1,881 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9 * The dom module provides helper methods for manipulating Dom elements.
10 * @module dom
11 *
12 */
13
14(function() {
15 var Y = YAHOO.util, // internal shorthand
16 getStyle, // for load time browser branching
17 setStyle, // ditto
18 id_counter = 0, // for use with generateId
19 propertyCache = {}; // for faster hyphen converts
20
21 // brower detection
22 var ua = navigator.userAgent.toLowerCase(),
23 isOpera = (ua.indexOf('opera') > -1),
24 isSafari = (ua.indexOf('safari') > -1),
25 isGecko = (!isOpera && !isSafari && ua.indexOf('gecko') > -1),
26 isIE = (!isOpera && ua.indexOf('msie') > -1);
27
28 // regex cache
29 var patterns = {
30 HYPHEN: /(-[a-z])/i
31 };
32
33
34 var toCamel = function(property) {
35 if ( !patterns.HYPHEN.test(property) ) {
36 return property; // no hyphens
37 }
38
39 if (propertyCache[property]) { // already converted
40 return propertyCache[property];
41 }
42
43 while( patterns.HYPHEN.exec(property) ) {
44 property = property.replace(RegExp.$1,
45 RegExp.$1.substr(1).toUpperCase());
46 }
47
48 propertyCache[property] = property;
49 return property;
50 //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug
51 };
52
53 // branching at load instead of runtime
54 if (document.defaultView && document.defaultView.getComputedStyle) { // W3C DOM method
55 getStyle = function(el, property) {
56 var value = null;
57
58 var computed = document.defaultView.getComputedStyle(el, '');
59 if (computed) { // test computed before touching for safari
60 value = computed[toCamel(property)];
61 }
62
63 return el.style[property] || value;
64 };
65 } else if (document.documentElement.currentStyle && isIE) { // IE method
66 getStyle = function(el, property) {
67 switch( toCamel(property) ) {
68 case 'opacity' :// IE opacity uses filter
69 var val = 100;
70 try { // will error if no DXImageTransform
71 val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
72
73 } catch(e) {
74 try { // make sure its in the document
75 val = el.filters('alpha').opacity;
76 } catch(e) {
77 }
78 }
79 return val / 100;
80 break;
81 default:
82 // test currentStyle before touching
83 var value = el.currentStyle ? el.currentStyle[property] : null;
84 return ( el.style[property] || value );
85 }
86 };
87 } else { // default to inline only
88 getStyle = function(el, property) { return el.style[property]; };
89 }
90
91 if (isIE) {
92 setStyle = function(el, property, val) {
93 switch (property) {
94 case 'opacity':
95 if ( typeof el.style.filter == 'string' ) { // in case not appended
96 el.style.filter = 'alpha(opacity=' + val * 100 + ')';
97
98 if (!el.currentStyle || !el.currentStyle.hasLayout) {
99 el.style.zoom = 1; // when no layout or cant tell
100 }
101 }
102 break;
103 default:
104 el.style[property] = val;
105 }
106 };
107 } else {
108 setStyle = function(el, property, val) {
109 el.style[property] = val;
110 };
111 }
112
113 /**
114 * Provides helper methods for DOM elements.
115 * @namespace YAHOO.util
116 * @class Dom
117 */
118 YAHOO.util.Dom = {
119 /**
120 * Returns an HTMLElement reference.
121 * @method get
122 * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements.
123 * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
124 */
125 get: function(el) {
126 if (!el) { return null; } // nothing to work with
127
128 if (typeof el != 'string' && !(el instanceof Array) ) { // assuming HTMLElement or HTMLCollection, so pass back as is
129 return el;
130 }
131
132 if (typeof el == 'string') { // ID
133 return document.getElementById(el);
134 }
135 else { // array of ID's and/or elements
136 var collection = [];
137 for (var i = 0, len = el.length; i < len; ++i) {
138 collection[collection.length] = Y.Dom.get(el[i]);
139 }
140
141 return collection;
142 }
143
144 return null; // safety, should never happen
145 },
146
147 /**
148 * Normalizes currentStyle and ComputedStyle.
149 * @method getStyle
150 * @param {String | HTMLElement |Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
151 * @param {String} property The style property whose value is returned.
152 * @return {String | Array} The current value of the style property for the element(s).
153 */
154 getStyle: function(el, property) {
155 property = toCamel(property);
156
157 var f = function(element) {
158 return getStyle(element, property);
159 };
160
161 return Y.Dom.batch(el, f, Y.Dom, true);
162 },
163
164 /**
165 * Wrapper for setting style properties of HTMLElements. Normalizes "opacity" across modern browsers.
166 * @method setStyle
167 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
168 * @param {String} property The style property to be set.
169 * @param {String} val The value to apply to the given property.
170 */
171 setStyle: function(el, property, val) {
172 property = toCamel(property);
173
174 var f = function(element) {
175 setStyle(element, property, val);
176
177 };
178
179 Y.Dom.batch(el, f, Y.Dom, true);
180 },
181
182 /**
183 * Gets the current position of an element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
184 * @method getXY
185 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
186 * @return {Array} The XY position of the element(s)
187 */
188 getXY: function(el) {
189 var f = function(el) {
190
191 // has to be part of document to have pageXY
192 if (el.parentNode === null || el.offsetParent === null ||
193 this.getStyle(el, 'display') == 'none') {
194 return false;
195 }
196
197 var parentNode = null;
198 var pos = [];
199 var box;
200
201 if (el.getBoundingClientRect) { // IE
202 box = el.getBoundingClientRect();
203 var doc = document;
204 if ( !this.inDocument(el) && parent.document != document) {// might be in a frame, need to get its scroll
205 doc = parent.document;
206
207 if ( !this.isAncestor(doc.documentElement, el) ) {
208 return false;
209 }
210
211 }
212
213 var scrollTop = Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
214 var scrollLeft = Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
215
216 return [box.left + scrollLeft, box.top + scrollTop];
217 }
218 else { // safari, opera, & gecko
219 pos = [el.offsetLeft, el.offsetTop];
220 parentNode = el.offsetParent;
221 if (parentNode != el) {
222 while (parentNode) {
223 pos[0] += parentNode.offsetLeft;
224 pos[1] += parentNode.offsetTop;
225 parentNode = parentNode.offsetParent;
226 }
227 }
228 if (isSafari && this.getStyle(el, 'position') == 'absolute' ) { // safari doubles in some cases
229 pos[0] -= document.body.offsetLeft;
230 pos[1] -= document.body.offsetTop;
231 }
232 }
233
234 if (el.parentNode) { parentNode = el.parentNode; }
235 else { parentNode = null; }
236
237 while (parentNode && parentNode.tagName.toUpperCase() != 'BODY' && parentNode.tagName.toUpperCase() != 'HTML')
238 { // account for any scrolled ancestors
239 if (Y.Dom.getStyle(parentNode, 'display') != 'inline') { // work around opera inline scrollLeft/Top bug
240 pos[0] -= parentNode.scrollLeft;
241 pos[1] -= parentNode.scrollTop;
242 }
243
244 if (parentNode.parentNode) {
245 parentNode = parentNode.parentNode;
246 } else { parentNode = null; }
247 }
248
249
250 return pos;
251 };
252
253 return Y.Dom.batch(el, f, Y.Dom, true);
254 },
255
256 /**
257 * Gets the current X position of an element based on page coordinates. The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
258 * @method getX
259 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
260 * @return {String | Array} The X position of the element(s)
261 */
262 getX: function(el) {
263 var f = function(el) {
264 return Y.Dom.getXY(el)[0];
265 };
266
267 return Y.Dom.batch(el, f, Y.Dom, true);
268 },
269
270 /**
271 * Gets the current Y position of an element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
272 * @method getY
273 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
274 * @return {String | Array} The Y position of the element(s)
275 */
276 getY: function(el) {
277 var f = function(el) {
278 return Y.Dom.getXY(el)[1];
279 };
280
281 return Y.Dom.batch(el, f, Y.Dom, true);
282 },
283
284 /**
285 * Set the position of an html element in page coordinates, regardless of how the element is positioned.
286 * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
287 * @method setXY
288 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
289 * @param {Array} pos Contains X & Y values for new position (coordinates are page-based)
290 * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
291 */
292 setXY: function(el, pos, noRetry) {
293 var f = function(el) {
294 var style_pos = this.getStyle(el, 'position');
295 if (style_pos == 'static') { // default to relative
296 this.setStyle(el, 'position', 'relative');
297 style_pos = 'relative';
298 }
299
300 var pageXY = this.getXY(el);
301 if (pageXY === false) { // has to be part of doc to have pageXY
302 return false;
303 }
304
305 var delta = [ // assuming pixels; if not we will have to retry
306 parseInt( this.getStyle(el, 'left'), 10 ),
307 parseInt( this.getStyle(el, 'top'), 10 )
308 ];
309
310 if ( isNaN(delta[0]) ) {// in case of 'auto'
311 delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft;
312 }
313 if ( isNaN(delta[1]) ) { // in case of 'auto'
314 delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop;
315 }
316
317 if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
318 if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }
319
320 var newXY = this.getXY(el);
321
322 // if retry is true, try one more time if we miss
323 if (!noRetry && (newXY[0] != pos[0] || newXY[1] != pos[1]) ) {
324 this.setXY(el, pos, true);
325 }
326
327 };
328
329 Y.Dom.batch(el, f, Y.Dom, true);
330 },
331
332 /**
333 * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
334 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
335 * @method setX
336 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
337 * @param {Int} x The value to use as the X coordinate for the element(s).
338 */
339 setX: function(el, x) {
340 Y.Dom.setXY(el, [x, null]);
341 },
342
343 /**
344 * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
345 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
346 * @method setY
347 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
348 * @param {Int} x To use as the Y coordinate for the element(s).
349 */
350 setY: function(el, y) {
351 Y.Dom.setXY(el, [null, y]);
352 },
353
354 /**
355 * Returns the region position of the given element.
356 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
357 * @method getRegion
358 * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
359 * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data.
360 */
361 getRegion: function(el) {
362 var f = function(el) {
363 var region = new Y.Region.getRegion(el);
364 return region;
365 };
366
367 return Y.Dom.batch(el, f, Y.Dom, true);
368 },
369
370 /**
371 * Returns the width of the client (viewport).
372 * @method getClientWidth
373 * @deprecated Now using getViewportWidth. This interface left intact for back compat.
374 * @return {Int} The width of the viewable area of the page.
375 */
376 getClientWidth: function() {
377 return Y.Dom.getViewportWidth();
378 },
379
380 /**
381 * Returns the height of the client (viewport).
382 * @method getClientHeight
383 * @deprecated Now using getViewportHeight. This interface left intact for back compat.
384 * @return {Int} The height of the viewable area of the page.
385 */
386 getClientHeight: function() {
387 return Y.Dom.getViewportHeight();
388 },
389
390 /**
391 * Returns a array of HTMLElements with the given class.
392 * For optimized performance, include a tag and/or root node when possible.
393 * @method getElementsByClassName
394 * @param {String} className The class name to match against
395 * @param {String} tag (optional) The tag name of the elements being collected
396 * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point
397 * @return {Array} An array of elements that have the given class name
398 */
399 getElementsByClassName: function(className, tag, root) {
400 var method = function(el) { return Y.Dom.hasClass(el, className); };
401 return Y.Dom.getElementsBy(method, tag, root);
402 },
403
404 /**
405 * Determines whether an HTMLElement has the given className.
406 * @method hasClass
407 * @param {String | HTMLElement | Array} el The element or collection to test
408 * @param {String} className the class name to search for
409 * @return {Boolean | Array} A boolean value or array of boolean values
410 */
411 hasClass: function(el, className) {
412 var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
413
414 var f = function(el) {
415 return re.test(el['className']);
416 };
417
418 return Y.Dom.batch(el, f, Y.Dom, true);
419 },
420
421 /**
422 * Adds a class name to a given element or collection of elements.
423 * @method addClass
424 * @param {String | HTMLElement | Array} el The element or collection to add the class to
425 * @param {String} className the class name to add to the class attribute
426 */
427 addClass: function(el, className) {
428 var f = function(el) {
429 if (this.hasClass(el, className)) { return; } // already present
430
431
432 el['className'] = [el['className'], className].join(' ');
433 };
434
435 Y.Dom.batch(el, f, Y.Dom, true);
436 },
437
438 /**
439 * Removes a class name from a given element or collection of elements.
440 * @method removeClass
441 * @param {String | HTMLElement | Array} el The element or collection to remove the class from
442 * @param {String} className the class name to remove from the class attribute
443 */
444 removeClass: function(el, className) {
445 var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', 'g');
446
447 var f = function(el) {
448 if (!this.hasClass(el, className)) { return; } // not present
449
450
451 var c = el['className'];
452 el['className'] = c.replace(re, ' ');
453 if ( this.hasClass(el, className) ) { // in case of multiple adjacent
454 this.removeClass(el, className);
455 }
456
457 };
458
459 Y.Dom.batch(el, f, Y.Dom, true);
460 },
461
462 /**
463 * Replace a class with another class for a given element or collection of elements.
464 * If no oldClassName is present, the newClassName is simply added.
465 * @method replaceClass
466 * @param {String | HTMLElement | Array} el The element or collection to remove the class from
467 * @param {String} oldClassName the class name to be replaced
468 * @param {String} newClassName the class name that will be replacing the old class name
469 */
470 replaceClass: function(el, oldClassName, newClassName) {
471 if (oldClassName === newClassName) { // avoid infinite loop
472 return false;
473 }
474
475 var re = new RegExp('(?:^|\\s+)' + oldClassName + '(?:\\s+|$)', 'g');
476
477 var f = function(el) {
478
479 if ( !this.hasClass(el, oldClassName) ) {
480 this.addClass(el, newClassName); // just add it if nothing to replace
481 return; // note return
482 }
483
484 el['className'] = el['className'].replace(re, ' ' + newClassName + ' ');
485
486 if ( this.hasClass(el, oldClassName) ) { // in case of multiple adjacent
487 this.replaceClass(el, oldClassName, newClassName);
488 }
489 };
490
491 Y.Dom.batch(el, f, Y.Dom, true);
492 },
493
494 /**
495 * Generates a unique ID
496 * @method generateId
497 * @param {String | HTMLElement | Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present).
498 * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen").
499 * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element)
500 */
501 generateId: function(el, prefix) {
502 prefix = prefix || 'yui-gen';
503 el = el || {};
504
505 var f = function(el) {
506 if (el) {
507 el = Y.Dom.get(el);
508 } else {
509 el = {}; // just generating ID in this case
510 }
511
512 if (!el.id) {
513 el.id = prefix + id_counter++;
514 } // dont override existing
515
516
517 return el.id;
518 };
519
520 return Y.Dom.batch(el, f, Y.Dom, true);
521 },
522
523 /**
524 * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy.
525 * @method isAncestor
526 * @param {String | HTMLElement} haystack The possible ancestor
527 * @param {String | HTMLElement} needle The possible descendent
528 * @return {Boolean} Whether or not the haystack is an ancestor of needle
529 */
530 isAncestor: function(haystack, needle) {
531 haystack = Y.Dom.get(haystack);
532 if (!haystack || !needle) { return false; }
533
534 var f = function(needle) {
535 if (haystack.contains && !isSafari) { // safari "contains" is broken
536 return haystack.contains(needle);
537 }
538 else if ( haystack.compareDocumentPosition ) {
539 return !!(haystack.compareDocumentPosition(needle) & 16);
540 }
541 else { // loop up and test each parent
542 var parent = needle.parentNode;
543
544 while (parent) {
545 if (parent == haystack) {
546 return true;
547 }
548 else if (!parent.tagName || parent.tagName.toUpperCase() == 'HTML') {
549 return false;
550 }
551
552 parent = parent.parentNode;
553 }
554 return false;
555 }
556 };
557
558 return Y.Dom.batch(needle, f, Y.Dom, true);
559 },
560
561 /**
562 * Determines whether an HTMLElement is present in the current document.
563 * @method inDocument
564 * @param {String | HTMLElement} el The element to search for
565 * @return {Boolean} Whether or not the element is present in the current document
566 */
567 inDocument: function(el) {
568 var f = function(el) {
569 return this.isAncestor(document.documentElement, el);
570 };
571
572 return Y.Dom.batch(el, f, Y.Dom, true);
573 },
574
575 /**
576 * Returns a array of HTMLElements that pass the test applied by supplied boolean method.
577 * For optimized performance, include a tag and/or root node when possible.
578 * @method getElementsBy
579 * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
580
581 * @param {String} tag (optional) The tag name of the elements being collected
582 * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point
583 */
584 getElementsBy: function(method, tag, root) {
585 tag = tag || '*';
586 root = Y.Dom.get(root) || document;
587
588 var nodes = [];
589 var elements = root.getElementsByTagName(tag);
590
591 if ( !elements.length && (tag == '*' && root.all) ) {
592 elements = root.all; // IE < 6
593 }
594
595 for (var i = 0, len = elements.length; i < len; ++i) {
596 if ( method(elements[i]) ) { nodes[nodes.length] = elements[i]; }
597 }
598
599
600 return nodes;
601 },
602
603 /**
604 * Returns an array of elements that have had the supplied method applied.
605 * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
606 * @method batch
607 * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
608 * @param {Function} method The method to apply to the element(s)
609 * @param {Any} o (optional) An optional arg that is passed to the supplied method
610 * @param {Boolean} override (optional) Whether or not to override the scope of "method" with "o"
611 * @return {HTMLElement | Array} The element(s) with the method applied
612 */
613 batch: function(el, method, o, override) {
614 var id = el;
615 el = Y.Dom.get(el);
616
617 var scope = (override) ? o : window;
618
619 if (!el || el.tagName || !el.length) { // is null or not a collection (tagName for SELECT and others that can be both an element and a collection)
620 if (!el) {
621 return false;
622 }
623 return method.call(scope, el, o);
624 }
625
626 var collection = [];
627
628 for (var i = 0, len = el.length; i < len; ++i) {
629 if (!el[i]) {
630 id = el[i];
631 }
632 collection[collection.length] = method.call(scope, el[i], o);
633 }
634
635 return collection;
636 },
637
638 /**
639 * Returns the height of the document.
640 * @method getDocumentHeight
641 * @return {Int} The height of the actual document (which includes the body and its margin).
642 */
643 getDocumentHeight: function() {
644 var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight;
645
646 var h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
647 return h;
648 },
649
650 /**
651 * Returns the width of the document.
652 * @method getDocumentWidth
653 * @return {Int} The width of the actual document (which includes the body and its margin).
654 */
655 getDocumentWidth: function() {
656 var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth;
657 var w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
658 return w;
659 },
660
661 /**
662 * Returns the current height of the viewport.
663 * @method getViewportHeight
664 * @return {Int} The height of the viewable area of the page (excludes scrollbars).
665 */
666 getViewportHeight: function() {
667 var height = self.innerHeight; // Safari, Opera
668 var mode = document.compatMode;
669
670 if ( (mode || isIE) && !isOpera ) { // IE, Gecko
671 height = (mode == 'CSS1Compat') ?
672 document.documentElement.clientHeight : // Standards
673 document.body.clientHeight; // Quirks
674 }
675
676 return height;
677 },
678
679 /**
680 * Returns the current width of the viewport.
681 * @method getViewportWidth
682 * @return {Int} The width of the viewable area of the page (excludes scrollbars).
683 */
684
685 getViewportWidth: function() {
686 var width = self.innerWidth; // Safari
687 var mode = document.compatMode;
688
689 if (mode || isIE) { // IE, Gecko, Opera
690 width = (mode == 'CSS1Compat') ?
691 document.documentElement.clientWidth : // Standards
692 document.body.clientWidth; // Quirks
693 }
694 return width;
695 }
696 };
697})();
698/**
699 * A region is a representation of an object on a grid. It is defined
700 * by the top, right, bottom, left extents, so is rectangular by default. If
701 * other shapes are required, this class could be extended to support it.
702 * @namespace YAHOO.util
703 * @class Region
704 * @param {Int} t the top extent
705 * @param {Int} r the right extent
706 * @param {Int} b the bottom extent
707 * @param {Int} l the left extent
708 * @constructor
709 */
710YAHOO.util.Region = function(t, r, b, l) {
711
712 /**
713 * The region's top extent
714 * @property top
715 * @type Int
716 */
717 this.top = t;
718
719 /**
720 * The region's top extent as index, for symmetry with set/getXY
721 * @property 1
722 * @type Int
723 */
724 this[1] = t;
725
726 /**
727 * The region's right extent
728 * @property right
729 * @type int
730 */
731 this.right = r;
732
733 /**
734 * The region's bottom extent
735 * @property bottom
736 * @type Int
737 */
738 this.bottom = b;
739
740 /**
741 * The region's left extent
742 * @property left
743 * @type Int
744 */
745 this.left = l;
746
747 /**
748 * The region's left extent as index, for symmetry with set/getXY
749 * @property 0
750 * @type Int
751 */
752 this[0] = l;
753};
754
755/**
756 * Returns true if this region contains the region passed in
757 * @method contains
758 * @param {Region} region The region to evaluate
759 * @return {Boolean} True if the region is contained with this region,
760 * else false
761 */
762YAHOO.util.Region.prototype.contains = function(region) {
763 return ( region.left >= this.left &&
764 region.right <= this.right &&
765 region.top >= this.top &&
766 region.bottom <= this.bottom );
767
768};
769
770/**
771 * Returns the area of the region
772 * @method getArea
773 * @return {Int} the region's area
774 */
775YAHOO.util.Region.prototype.getArea = function() {
776 return ( (this.bottom - this.top) * (this.right - this.left) );
777};
778
779/**
780 * Returns the region where the passed in region overlaps with this one
781 * @method intersect
782 * @param {Region} region The region that intersects
783 * @return {Region} The overlap region, or null if there is no overlap
784 */
785YAHOO.util.Region.prototype.intersect = function(region) {
786 var t = Math.max( this.top, region.top );
787 var r = Math.min( this.right, region.right );
788 var b = Math.min( this.bottom, region.bottom );
789 var l = Math.max( this.left, region.left );
790
791 if (b >= t && r >= l) {
792 return new YAHOO.util.Region(t, r, b, l);
793 } else {
794 return null;
795 }
796};
797
798/**
799 * Returns the region representing the smallest region that can contain both
800 * the passed in region and this region.
801 * @method union
802 * @param {Region} region The region that to create the union with
803 * @return {Region} The union region
804 */
805YAHOO.util.Region.prototype.union = function(region) {
806 var t = Math.min( this.top, region.top );
807 var r = Math.max( this.right, region.right );
808 var b = Math.max( this.bottom, region.bottom );
809 var l = Math.min( this.left, region.left );
810
811 return new YAHOO.util.Region(t, r, b, l);
812};
813
814/**
815 * toString
816 * @method toString
817 * @return string the region properties
818 */
819YAHOO.util.Region.prototype.toString = function() {
820 return ( "Region {" +
821 "top: " + this.top +
822 ", right: " + this.right +
823 ", bottom: " + this.bottom +
824 ", left: " + this.left +
825 "}" );
826};
827
828/**
829 * Returns a region that is occupied by the DOM element
830 * @method getRegion
831 * @param {HTMLElement} el The element
832 * @return {Region} The region that the element occupies
833 * @static
834 */
835YAHOO.util.Region.getRegion = function(el) {
836 var p = YAHOO.util.Dom.getXY(el);
837
838 var t = p[1];
839 var r = p[0] + el.offsetWidth;
840 var b = p[1] + el.offsetHeight;
841 var l = p[0];
842
843 return new YAHOO.util.Region(t, r, b, l);
844};
845
846/////////////////////////////////////////////////////////////////////////////
847
848/**
849 * A point is a region that is special in that it represents a single point on
850 * the grid.
851 * @namespace YAHOO.util
852 * @class Point
853 * @param {Int} x The X position of the point
854 * @param {Int} y The Y position of the point
855 * @constructor
856 * @extends YAHOO.util.Region
857 */
858YAHOO.util.Point = function(x, y) {
859 if (x instanceof Array) { // accept output from Dom.getXY
860 y = x[1];
861 x = x[0];
862 }
863
864 /**
865 * The X position of the point, which is also the right, left and index zero (for Dom.getXY symmetry)
866 * @property x
867 * @type Int
868 */
869
870 this.x = this.right = this.left = this[0] = x;
871
872 /**
873 * The Y position of the point, which is also the top, bottom and index one (for Dom.getXY symmetry)
874 * @property y
875 * @type Int
876 */
877 this.y = this.top = this.bottom = this[1] = y;
878};
879
880YAHOO.util.Point.prototype = new YAHOO.util.Region();
881
diff --git a/frontend/beta/js/YUI/dragdrop.js b/frontend/beta/js/YUI/dragdrop.js
new file mode 100644
index 0000000..43a0f61
--- a/dev/null
+++ b/frontend/beta/js/YUI/dragdrop.js
@@ -0,0 +1,2940 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8(function() {
9
10var Event=YAHOO.util.Event;
11var Dom=YAHOO.util.Dom;
12
13/**
14 * Defines the interface and base operation of items that that can be
15 * dragged or can be drop targets. It was designed to be extended, overriding
16 * the event handlers for startDrag, onDrag, onDragOver, onDragOut.
17 * Up to three html elements can be associated with a DragDrop instance:
18 * <ul>
19 * <li>linked element: the element that is passed into the constructor.
20 * This is the element which defines the boundaries for interaction with
21 * other DragDrop objects.</li>
22 * <li>handle element(s): The drag operation only occurs if the element that
23 * was clicked matches a handle element. By default this is the linked
24 * element, but there are times that you will want only a portion of the
25 * linked element to initiate the drag operation, and the setHandleElId()
26 * method provides a way to define this.</li>
27 * <li>drag element: this represents an the element that would be moved along
28 * with the cursor during a drag operation. By default, this is the linked
29 * element itself as in {@link YAHOO.util.DD}. setDragElId() lets you define
30 * a separate element that would be moved, as in {@link YAHOO.util.DDProxy}
31 * </li>
32 * </ul>
33 * This class should not be instantiated until the onload event to ensure that
34 * the associated elements are available.
35 * The following would define a DragDrop obj that would interact with any
36 * other DragDrop obj in the "group1" group:
37 * <pre>
38 * dd = new YAHOO.util.DragDrop("div1", "group1");
39 * </pre>
40 * Since none of the event handlers have been implemented, nothing would
41 * actually happen if you were to run the code above. Normally you would
42 * override this class or one of the default implementations, but you can
43 * also override the methods you want on an instance of the class...
44 * <pre>
45 * dd.onDragDrop = function(e, id) {
46 * &nbsp;&nbsp;alert("dd was dropped on " + id);
47 * }
48 * </pre>
49 * @namespace YAHOO.util
50 * @class DragDrop
51 * @constructor
52 * @param {String} id of the element that is linked to this instance
53 * @param {String} sGroup the group of related DragDrop objects
54 * @param {object} config an object containing configurable attributes
55 * Valid properties for DragDrop:
56 * padding, isTarget, maintainOffset, primaryButtonOnly
57 */
58YAHOO.util.DragDrop = function(id, sGroup, config) {
59 if (id) {
60 this.init(id, sGroup, config);
61 }
62};
63
64YAHOO.util.DragDrop.prototype = {
65
66 /**
67 * The id of the element associated with this object. This is what we
68 * refer to as the "linked element" because the size and position of
69 * this element is used to determine when the drag and drop objects have
70 * interacted.
71 * @property id
72 * @type String
73 */
74 id: null,
75
76 /**
77 * Configuration attributes passed into the constructor
78 * @property config
79 * @type object
80 */
81 config: null,
82
83 /**
84 * The id of the element that will be dragged. By default this is same
85 * as the linked element , but could be changed to another element. Ex:
86 * YAHOO.util.DDProxy
87 * @property dragElId
88 * @type String
89 * @private
90 */
91 dragElId: null,
92
93 /**
94 * the id of the element that initiates the drag operation. By default
95 * this is the linked element, but could be changed to be a child of this
96 * element. This lets us do things like only starting the drag when the
97 * header element within the linked html element is clicked.
98 * @property handleElId
99 * @type String
100 * @private
101 */
102 handleElId: null,
103
104 /**
105 * An associative array of HTML tags that will be ignored if clicked.
106 * @property invalidHandleTypes
107 * @type {string: string}
108 */
109 invalidHandleTypes: null,
110
111 /**
112 * An associative array of ids for elements that will be ignored if clicked
113 * @property invalidHandleIds
114 * @type {string: string}
115 */
116 invalidHandleIds: null,
117
118 /**
119 * An indexted array of css class names for elements that will be ignored
120 * if clicked.
121 * @property invalidHandleClasses
122 * @type string[]
123 */
124 invalidHandleClasses: null,
125
126 /**
127 * The linked element's absolute X position at the time the drag was
128 * started
129 * @property startPageX
130 * @type int
131 * @private
132 */
133 startPageX: 0,
134
135 /**
136 * The linked element's absolute X position at the time the drag was
137 * started
138 * @property startPageY
139 * @type int
140 * @private
141 */
142 startPageY: 0,
143
144 /**
145 * The group defines a logical collection of DragDrop objects that are
146 * related. Instances only get events when interacting with other
147 * DragDrop object in the same group. This lets us define multiple
148 * groups using a single DragDrop subclass if we want.
149 * @property groups
150 * @type {string: string}
151 */
152 groups: null,
153
154 /**
155 * Individual drag/drop instances can be locked. This will prevent
156 * onmousedown start drag.
157 * @property locked
158 * @type boolean
159 * @private
160 */
161 locked: false,
162
163 /**
164 * Lock this instance
165 * @method lock
166 */
167 lock: function() { this.locked = true; },
168
169 /**
170 * Unlock this instace
171 * @method unlock
172 */
173 unlock: function() { this.locked = false; },
174
175 /**
176 * By default, all insances can be a drop target. This can be disabled by
177 * setting isTarget to false.
178 * @method isTarget
179 * @type boolean
180 */
181 isTarget: true,
182
183 /**
184 * The padding configured for this drag and drop object for calculating
185 * the drop zone intersection with this object.
186 * @method padding
187 * @type int[]
188 */
189 padding: null,
190
191 /**
192 * Cached reference to the linked element
193 * @property _domRef
194 * @private
195 */
196 _domRef: null,
197
198 /**
199 * Internal typeof flag
200 * @property __ygDragDrop
201 * @private
202 */
203 __ygDragDrop: true,
204
205 /**
206 * Set to true when horizontal contraints are applied
207 * @property constrainX
208 * @type boolean
209 * @private
210 */
211 constrainX: false,
212
213 /**
214 * Set to true when vertical contraints are applied
215 * @property constrainY
216 * @type boolean
217 * @private
218 */
219 constrainY: false,
220
221 /**
222 * The left constraint
223 * @property minX
224 * @type int
225 * @private
226 */
227 minX: 0,
228
229 /**
230 * The right constraint
231 * @property maxX
232 * @type int
233 * @private
234 */
235 maxX: 0,
236
237 /**
238 * The up constraint
239 * @property minY
240 * @type int
241 * @type int
242 * @private
243 */
244 minY: 0,
245
246 /**
247 * The down constraint
248 * @property maxY
249 * @type int
250 * @private
251 */
252 maxY: 0,
253
254 /**
255 * Maintain offsets when we resetconstraints. Set to true when you want
256 * the position of the element relative to its parent to stay the same
257 * when the page changes
258 *
259 * @property maintainOffset
260 * @type boolean
261 */
262 maintainOffset: false,
263
264 /**
265 * Array of pixel locations the element will snap to if we specified a
266 * horizontal graduation/interval. This array is generated automatically
267 * when you define a tick interval.
268 * @property xTicks
269 * @type int[]
270 */
271 xTicks: null,
272
273 /**
274 * Array of pixel locations the element will snap to if we specified a
275 * vertical graduation/interval. This array is generated automatically
276 * when you define a tick interval.
277 * @property yTicks
278 * @type int[]
279 */
280 yTicks: null,
281
282 /**
283 * By default the drag and drop instance will only respond to the primary
284 * button click (left button for a right-handed mouse). Set to true to
285 * allow drag and drop to start with any mouse click that is propogated
286 * by the browser
287 * @property primaryButtonOnly
288 * @type boolean
289 */
290 primaryButtonOnly: true,
291
292 /**
293 * The availabe property is false until the linked dom element is accessible.
294 * @property available
295 * @type boolean
296 */
297 available: false,
298
299 /**
300 * By default, drags can only be initiated if the mousedown occurs in the
301 * region the linked element is. This is done in part to work around a
302 * bug in some browsers that mis-report the mousedown if the previous
303 * mouseup happened outside of the window. This property is set to true
304 * if outer handles are defined.
305 *
306 * @property hasOuterHandles
307 * @type boolean
308 * @default false
309 */
310 hasOuterHandles: false,
311
312 /**
313 * Code that executes immediately before the startDrag event
314 * @method b4StartDrag
315 * @private
316 */
317 b4StartDrag: function(x, y) { },
318
319 /**
320 * Abstract method called after a drag/drop object is clicked
321 * and the drag or mousedown time thresholds have beeen met.
322 * @method startDrag
323 * @param {int} X click location
324 * @param {int} Y click location
325 */
326 startDrag: function(x, y) { /* override this */ },
327
328 /**
329 * Code that executes immediately before the onDrag event
330 * @method b4Drag
331 * @private
332 */
333 b4Drag: function(e) { },
334
335 /**
336 * Abstract method called during the onMouseMove event while dragging an
337 * object.
338 * @method onDrag
339 * @param {Event} e the mousemove event
340 */
341 onDrag: function(e) { /* override this */ },
342
343 /**
344 * Abstract method called when this element fist begins hovering over
345 * another DragDrop obj
346 * @method onDragEnter
347 * @param {Event} e the mousemove event
348 * @param {String|DragDrop[]} id In POINT mode, the element
349 * id this is hovering over. In INTERSECT mode, an array of one or more
350 * dragdrop items being hovered over.
351 */
352 onDragEnter: function(e, id) { /* override this */ },
353
354 /**
355 * Code that executes immediately before the onDragOver event
356 * @method b4DragOver
357 * @private
358 */
359 b4DragOver: function(e) { },
360
361 /**
362 * Abstract method called when this element is hovering over another
363 * DragDrop obj
364 * @method onDragOver
365 * @param {Event} e the mousemove event
366 * @param {String|DragDrop[]} id In POINT mode, the element
367 * id this is hovering over. In INTERSECT mode, an array of dd items
368 * being hovered over.
369 */
370 onDragOver: function(e, id) { /* override this */ },
371
372 /**
373 * Code that executes immediately before the onDragOut event
374 * @method b4DragOut
375 * @private
376 */
377 b4DragOut: function(e) { },
378
379 /**
380 * Abstract method called when we are no longer hovering over an element
381 * @method onDragOut
382 * @param {Event} e the mousemove event
383 * @param {String|DragDrop[]} id In POINT mode, the element
384 * id this was hovering over. In INTERSECT mode, an array of dd items
385 * that the mouse is no longer over.
386 */
387 onDragOut: function(e, id) { /* override this */ },
388
389 /**
390 * Code that executes immediately before the onDragDrop event
391 * @method b4DragDrop
392 * @private
393 */
394 b4DragDrop: function(e) { },
395
396 /**
397 * Abstract method called when this item is dropped on another DragDrop
398 * obj
399 * @method onDragDrop
400 * @param {Event} e the mouseup event
401 * @param {String|DragDrop[]} id In POINT mode, the element
402 * id this was dropped on. In INTERSECT mode, an array of dd items this
403 * was dropped on.
404 */
405 onDragDrop: function(e, id) { /* override this */ },
406
407 /**
408 * Abstract method called when this item is dropped on an area with no
409 * drop target
410 * @method onInvalidDrop
411 * @param {Event} e the mouseup event
412 */
413 onInvalidDrop: function(e) { /* override this */ },
414
415 /**
416 * Code that executes immediately before the endDrag event
417 * @method b4EndDrag
418 * @private
419 */
420 b4EndDrag: function(e) { },
421
422 /**
423 * Fired when we are done dragging the object
424 * @method endDrag
425 * @param {Event} e the mouseup event
426 */
427 endDrag: function(e) { /* override this */ },
428
429 /**
430 * Code executed immediately before the onMouseDown event
431 * @method b4MouseDown
432 * @param {Event} e the mousedown event
433 * @private
434 */
435 b4MouseDown: function(e) { },
436
437 /**
438 * Event handler that fires when a drag/drop obj gets a mousedown
439 * @method onMouseDown
440 * @param {Event} e the mousedown event
441 */
442 onMouseDown: function(e) { /* override this */ },
443
444 /**
445 * Event handler that fires when a drag/drop obj gets a mouseup
446 * @method onMouseUp
447 * @param {Event} e the mouseup event
448 */
449 onMouseUp: function(e) { /* override this */ },
450
451 /**
452 * Override the onAvailable method to do what is needed after the initial
453 * position was determined.
454 * @method onAvailable
455 */
456 onAvailable: function () {
457 },
458
459 /**
460 * Returns a reference to the linked element
461 * @method getEl
462 * @return {HTMLElement} the html element
463 */
464 getEl: function() {
465 if (!this._domRef) {
466 this._domRef = Dom.get(this.id);
467 }
468
469 return this._domRef;
470 },
471
472 /**
473 * Returns a reference to the actual element to drag. By default this is
474 * the same as the html element, but it can be assigned to another
475 * element. An example of this can be found in YAHOO.util.DDProxy
476 * @method getDragEl
477 * @return {HTMLElement} the html element
478 */
479 getDragEl: function() {
480 return Dom.get(this.dragElId);
481 },
482
483 /**
484 * Sets up the DragDrop object. Must be called in the constructor of any
485 * YAHOO.util.DragDrop subclass
486 * @method init
487 * @param id the id of the linked element
488 * @param {String} sGroup the group of related items
489 * @param {object} config configuration attributes
490 */
491 init: function(id, sGroup, config) {
492 this.initTarget(id, sGroup, config);
493 Event.on(this.id, "mousedown", this.handleMouseDown, this, true);
494 // Event.on(this.id, "selectstart", Event.preventDefault);
495 },
496
497 /**
498 * Initializes Targeting functionality only... the object does not
499 * get a mousedown handler.
500 * @method initTarget
501 * @param id the id of the linked element
502 * @param {String} sGroup the group of related items
503 * @param {object} config configuration attributes
504 */
505 initTarget: function(id, sGroup, config) {
506
507 // configuration attributes
508 this.config = config || {};
509
510 // create a local reference to the drag and drop manager
511 this.DDM = YAHOO.util.DDM;
512 // initialize the groups array
513 this.groups = {};
514
515 // assume that we have an element reference instead of an id if the
516 // parameter is not a string
517 if (typeof id !== "string") {
518 YAHOO.log("id is not a string, assuming it is an HTMLElement");
519 id = Dom.generateId(id);
520 }
521
522 // set the id
523 this.id = id;
524
525 // add to an interaction group
526 this.addToGroup((sGroup) ? sGroup : "default");
527
528 // We don't want to register this as the handle with the manager
529 // so we just set the id rather than calling the setter.
530 this.handleElId = id;
531
532 Event.onAvailable(id, this.handleOnAvailable, this, true);
533
534
535 // the linked element is the element that gets dragged by default
536 this.setDragElId(id);
537
538 // by default, clicked anchors will not start drag operations.
539 // @TODO what else should be here? Probably form fields.
540 this.invalidHandleTypes = { A: "A" };
541 this.invalidHandleIds = {};
542 this.invalidHandleClasses = [];
543
544 this.applyConfig();
545 },
546
547 /**
548 * Applies the configuration parameters that were passed into the constructor.
549 * This is supposed to happen at each level through the inheritance chain. So
550 * a DDProxy implentation will execute apply config on DDProxy, DD, and
551 * DragDrop in order to get all of the parameters that are available in
552 * each object.
553 * @method applyConfig
554 */
555 applyConfig: function() {
556
557 // configurable properties:
558 // padding, isTarget, maintainOffset, primaryButtonOnly
559 this.padding = this.config.padding || [0, 0, 0, 0];
560 this.isTarget = (this.config.isTarget !== false);
561 this.maintainOffset = (this.config.maintainOffset);
562 this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
563
564 },
565
566 /**
567 * Executed when the linked element is available
568 * @method handleOnAvailable
569 * @private
570 */
571 handleOnAvailable: function() {
572 this.available = true;
573 this.resetConstraints();
574 this.onAvailable();
575 },
576
577 /**
578 * Configures the padding for the target zone in px. Effectively expands
579 * (or reduces) the virtual object size for targeting calculations.
580 * Supports css-style shorthand; if only one parameter is passed, all sides
581 * will have that padding, and if only two are passed, the top and bottom
582 * will have the first param, the left and right the second.
583 * @method setPadding
584 * @param {int} iTop Top pad
585 * @param {int} iRight Right pad
586 * @param {int} iBot Bot pad
587 * @param {int} iLeft Left pad
588 */
589 setPadding: function(iTop, iRight, iBot, iLeft) {
590 // this.padding = [iLeft, iRight, iTop, iBot];
591 if (!iRight && 0 !== iRight) {
592 this.padding = [iTop, iTop, iTop, iTop];
593 } else if (!iBot && 0 !== iBot) {
594 this.padding = [iTop, iRight, iTop, iRight];
595 } else {
596 this.padding = [iTop, iRight, iBot, iLeft];
597 }
598 },
599
600 /**
601 * Stores the initial placement of the linked element.
602 * @method setInitialPosition
603 * @param {int} diffX the X offset, default 0
604 * @param {int} diffY the Y offset, default 0
605 */
606 setInitPosition: function(diffX, diffY) {
607 var el = this.getEl();
608
609 if (!this.DDM.verifyEl(el)) {
610 return;
611 }
612
613 var dx = diffX || 0;
614 var dy = diffY || 0;
615
616 var p = Dom.getXY( el );
617
618 this.initPageX = p[0] - dx;
619 this.initPageY = p[1] - dy;
620
621 this.lastPageX = p[0];
622 this.lastPageY = p[1];
623
624
625 this.setStartPosition(p);
626 },
627
628 /**
629 * Sets the start position of the element. This is set when the obj
630 * is initialized, the reset when a drag is started.
631 * @method setStartPosition
632 * @param pos current position (from previous lookup)
633 * @private
634 */
635 setStartPosition: function(pos) {
636 var p = pos || Dom.getXY( this.getEl() );
637 this.deltaSetXY = null;
638
639 this.startPageX = p[0];
640 this.startPageY = p[1];
641 },
642
643 /**
644 * Add this instance to a group of related drag/drop objects. All
645 * instances belong to at least one group, and can belong to as many
646 * groups as needed.
647 * @method addToGroup
648 * @param sGroup {string} the name of the group
649 */
650 addToGroup: function(sGroup) {
651 this.groups[sGroup] = true;
652 this.DDM.regDragDrop(this, sGroup);
653 },
654
655 /**
656 * Remove's this instance from the supplied interaction group
657 * @method removeFromGroup
658 * @param {string} sGroup The group to drop
659 */
660 removeFromGroup: function(sGroup) {
661 if (this.groups[sGroup]) {
662 delete this.groups[sGroup];
663 }
664
665 this.DDM.removeDDFromGroup(this, sGroup);
666 },
667
668 /**
669 * Allows you to specify that an element other than the linked element
670 * will be moved with the cursor during a drag
671 * @method setDragElId
672 * @param id {string} the id of the element that will be used to initiate the drag
673 */
674 setDragElId: function(id) {
675 this.dragElId = id;
676 },
677
678 /**
679 * Allows you to specify a child of the linked element that should be
680 * used to initiate the drag operation. An example of this would be if
681 * you have a content div with text and links. Clicking anywhere in the
682 * content area would normally start the drag operation. Use this method
683 * to specify that an element inside of the content div is the element
684 * that starts the drag operation.
685 * @method setHandleElId
686 * @param id {string} the id of the element that will be used to
687 * initiate the drag.
688 */
689 setHandleElId: function(id) {
690 if (typeof id !== "string") {
691 YAHOO.log("id is not a string, assuming it is an HTMLElement");
692 id = Dom.generateId(id);
693 }
694 this.handleElId = id;
695 this.DDM.regHandle(this.id, id);
696 },
697
698 /**
699 * Allows you to set an element outside of the linked element as a drag
700 * handle
701 * @method setOuterHandleElId
702 * @param id the id of the element that will be used to initiate the drag
703 */
704 setOuterHandleElId: function(id) {
705 if (typeof id !== "string") {
706 YAHOO.log("id is not a string, assuming it is an HTMLElement");
707 id = Dom.generateId(id);
708 }
709 Event.on(id, "mousedown",
710 this.handleMouseDown, this, true);
711 this.setHandleElId(id);
712
713 this.hasOuterHandles = true;
714 },
715
716 /**
717 * Remove all drag and drop hooks for this element
718 * @method unreg
719 */
720 unreg: function() {
721 Event.removeListener(this.id, "mousedown",
722 this.handleMouseDown);
723 this._domRef = null;
724 this.DDM._remove(this);
725 },
726
727 /**
728 * Returns true if this instance is locked, or the drag drop mgr is locked
729 * (meaning that all drag/drop is disabled on the page.)
730 * @method isLocked
731 * @return {boolean} true if this obj or all drag/drop is locked, else
732 * false
733 */
734 isLocked: function() {
735 return (this.DDM.isLocked() || this.locked);
736 },
737
738 /**
739 * Fired when this object is clicked
740 * @method handleMouseDown
741 * @param {Event} e
742 * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj)
743 * @private
744 */
745 handleMouseDown: function(e, oDD) {
746
747 var button = e.which || e.button;
748
749 if (this.primaryButtonOnly && button > 1) {
750 return;
751 }
752
753 if (this.isLocked()) {
754 return;
755 }
756
757 this.DDM.refreshCache(this.groups);
758 // var self = this;
759 // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0);
760
761 // Only process the event if we really clicked within the linked
762 // element. The reason we make this check is that in the case that
763 // another element was moved between the clicked element and the
764 // cursor in the time between the mousedown and mouseup events. When
765 // this happens, the element gets the next mousedown event
766 // regardless of where on the screen it happened.
767 var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e));
768 if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) {
769 } else {
770 if (this.clickValidator(e)) {
771
772
773 // set the initial element position
774 this.setStartPosition();
775
776
777 this.b4MouseDown(e);
778 this.onMouseDown(e);
779 this.DDM.handleMouseDown(e, this);
780
781 this.DDM.stopEvent(e);
782 } else {
783
784
785 }
786 }
787 },
788
789 clickValidator: function(e) {
790 var target = Event.getTarget(e);
791 return ( this.isValidHandleChild(target) &&
792 (this.id == this.handleElId ||
793 this.DDM.handleWasClicked(target, this.id)) );
794 },
795
796 /**
797 * Allows you to specify a tag name that should not start a drag operation
798 * when clicked. This is designed to facilitate embedding links within a
799 * drag handle that do something other than start the drag.
800 * @method addInvalidHandleType
801 * @param {string} tagName the type of element to exclude
802 */
803 addInvalidHandleType: function(tagName) {
804 var type = tagName.toUpperCase();
805 this.invalidHandleTypes[type] = type;
806 },
807
808 /**
809 * Lets you to specify an element id for a child of a drag handle
810 * that should not initiate a drag
811 * @method addInvalidHandleId
812 * @param {string} id the element id of the element you wish to ignore
813 */
814 addInvalidHandleId: function(id) {
815 if (typeof id !== "string") {
816 YAHOO.log("id is not a string, assuming it is an HTMLElement");
817 id = Dom.generateId(id);
818 }
819 this.invalidHandleIds[id] = id;
820 },
821
822 /**
823 * Lets you specify a css class of elements that will not initiate a drag
824 * @method addInvalidHandleClass
825 * @param {string} cssClass the class of the elements you wish to ignore
826 */
827 addInvalidHandleClass: function(cssClass) {
828 this.invalidHandleClasses.push(cssClass);
829 },
830
831 /**
832 * Unsets an excluded tag name set by addInvalidHandleType
833 * @method removeInvalidHandleType
834 * @param {string} tagName the type of element to unexclude
835 */
836 removeInvalidHandleType: function(tagName) {
837 var type = tagName.toUpperCase();
838 // this.invalidHandleTypes[type] = null;
839 delete this.invalidHandleTypes[type];
840 },
841
842 /**
843 * Unsets an invalid handle id
844 * @method removeInvalidHandleId
845 * @param {string} id the id of the element to re-enable
846 */
847 removeInvalidHandleId: function(id) {
848 if (typeof id !== "string") {
849 YAHOO.log("id is not a string, assuming it is an HTMLElement");
850 id = Dom.generateId(id);
851 }
852 delete this.invalidHandleIds[id];
853 },
854
855 /**
856 * Unsets an invalid css class
857 * @method removeInvalidHandleClass
858 * @param {string} cssClass the class of the element(s) you wish to
859 * re-enable
860 */
861 removeInvalidHandleClass: function(cssClass) {
862 for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
863 if (this.invalidHandleClasses[i] == cssClass) {
864 delete this.invalidHandleClasses[i];
865 }
866 }
867 },
868
869 /**
870 * Checks the tag exclusion list to see if this click should be ignored
871 * @method isValidHandleChild
872 * @param {HTMLElement} node the HTMLElement to evaluate
873 * @return {boolean} true if this is a valid tag type, false if not
874 */
875 isValidHandleChild: function(node) {
876
877 var valid = true;
878 // var n = (node.nodeName == "#text") ? node.parentNode : node;
879 var nodeName;
880 try {
881 nodeName = node.nodeName.toUpperCase();
882 } catch(e) {
883 nodeName = node.nodeName;
884 }
885 valid = valid && !this.invalidHandleTypes[nodeName];
886 valid = valid && !this.invalidHandleIds[node.id];
887
888 for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
889 valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
890 }
891
892
893 return valid;
894
895 },
896
897 /**
898 * Create the array of horizontal tick marks if an interval was specified
899 * in setXConstraint().
900 * @method setXTicks
901 * @private
902 */
903 setXTicks: function(iStartX, iTickSize) {
904 this.xTicks = [];
905 this.xTickSize = iTickSize;
906
907 var tickMap = {};
908
909 for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
910 if (!tickMap[i]) {
911 this.xTicks[this.xTicks.length] = i;
912 tickMap[i] = true;
913 }
914 }
915
916 for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
917 if (!tickMap[i]) {
918 this.xTicks[this.xTicks.length] = i;
919 tickMap[i] = true;
920 }
921 }
922
923 this.xTicks.sort(this.DDM.numericSort) ;
924 },
925
926 /**
927 * Create the array of vertical tick marks if an interval was specified in
928 * setYConstraint().
929 * @method setYTicks
930 * @private
931 */
932 setYTicks: function(iStartY, iTickSize) {
933 this.yTicks = [];
934 this.yTickSize = iTickSize;
935
936 var tickMap = {};
937
938 for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
939 if (!tickMap[i]) {
940 this.yTicks[this.yTicks.length] = i;
941 tickMap[i] = true;
942 }
943 }
944
945 for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
946 if (!tickMap[i]) {
947 this.yTicks[this.yTicks.length] = i;
948 tickMap[i] = true;
949 }
950 }
951
952 this.yTicks.sort(this.DDM.numericSort) ;
953 },
954
955 /**
956 * By default, the element can be dragged any place on the screen. Use
957 * this method to limit the horizontal travel of the element. Pass in
958 * 0,0 for the parameters if you want to lock the drag to the y axis.
959 * @method setXConstraint
960 * @param {int} iLeft the number of pixels the element can move to the left
961 * @param {int} iRight the number of pixels the element can move to the
962 * right
963 * @param {int} iTickSize optional parameter for specifying that the
964 * element
965 * should move iTickSize pixels at a time.
966 */
967 setXConstraint: function(iLeft, iRight, iTickSize) {
968 this.leftConstraint = iLeft;
969 this.rightConstraint = iRight;
970
971 this.minX = this.initPageX - iLeft;
972 this.maxX = this.initPageX + iRight;
973 if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
974
975 this.constrainX = true;
976 },
977
978 /**
979 * Clears any constraints applied to this instance. Also clears ticks
980 * since they can't exist independent of a constraint at this time.
981 * @method clearConstraints
982 */
983 clearConstraints: function() {
984 this.constrainX = false;
985 this.constrainY = false;
986 this.clearTicks();
987 },
988
989 /**
990 * Clears any tick interval defined for this instance
991 * @method clearTicks
992 */
993 clearTicks: function() {
994 this.xTicks = null;
995 this.yTicks = null;
996 this.xTickSize = 0;
997 this.yTickSize = 0;
998 },
999
1000 /**
1001 * By default, the element can be dragged any place on the screen. Set
1002 * this to limit the vertical travel of the element. Pass in 0,0 for the
1003 * parameters if you want to lock the drag to the x axis.
1004 * @method setYConstraint
1005 * @param {int} iUp the number of pixels the element can move up
1006 * @param {int} iDown the number of pixels the element can move down
1007 * @param {int} iTickSize optional parameter for specifying that the
1008 * element should move iTickSize pixels at a time.
1009 */
1010 setYConstraint: function(iUp, iDown, iTickSize) {
1011 this.topConstraint = iUp;
1012 this.bottomConstraint = iDown;
1013
1014 this.minY = this.initPageY - iUp;
1015 this.maxY = this.initPageY + iDown;
1016 if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1017
1018 this.constrainY = true;
1019
1020 },
1021
1022 /**
1023 * resetConstraints must be called if you manually reposition a dd element.
1024 * @method resetConstraints
1025 * @param {boolean} maintainOffset
1026 */
1027 resetConstraints: function() {
1028
1029
1030 // Maintain offsets if necessary
1031 if (this.initPageX || this.initPageX === 0) {
1032 // figure out how much this thing has moved
1033 var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1034 var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1035
1036 this.setInitPosition(dx, dy);
1037
1038 // This is the first time we have detected the element's position
1039 } else {
1040 this.setInitPosition();
1041 }
1042
1043 if (this.constrainX) {
1044 this.setXConstraint( this.leftConstraint,
1045 this.rightConstraint,
1046 this.xTickSize );
1047 }
1048
1049 if (this.constrainY) {
1050 this.setYConstraint( this.topConstraint,
1051 this.bottomConstraint,
1052 this.yTickSize );
1053 }
1054 },
1055
1056 /**
1057 * Normally the drag element is moved pixel by pixel, but we can specify
1058 * that it move a number of pixels at a time. This method resolves the
1059 * location when we have it set up like this.
1060 * @method getTick
1061 * @param {int} val where we want to place the object
1062 * @param {int[]} tickArray sorted array of valid points
1063 * @return {int} the closest tick
1064 * @private
1065 */
1066 getTick: function(val, tickArray) {
1067
1068 if (!tickArray) {
1069 // If tick interval is not defined, it is effectively 1 pixel,
1070 // so we return the value passed to us.
1071 return val;
1072 } else if (tickArray[0] >= val) {
1073 // The value is lower than the first tick, so we return the first
1074 // tick.
1075 return tickArray[0];
1076 } else {
1077 for (var i=0, len=tickArray.length; i<len; ++i) {
1078 var next = i + 1;
1079 if (tickArray[next] && tickArray[next] >= val) {
1080 var diff1 = val - tickArray[i];
1081 var diff2 = tickArray[next] - val;
1082 return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1083 }
1084 }
1085
1086 // The value is larger than the last tick, so we return the last
1087 // tick.
1088 return tickArray[tickArray.length - 1];
1089 }
1090 },
1091
1092 /**
1093 * toString method
1094 * @method toString
1095 * @return {string} string representation of the dd obj
1096 */
1097 toString: function() {
1098 return ("DragDrop " + this.id);
1099 }
1100
1101};
1102
1103})();
1104/**
1105 * The drag and drop utility provides a framework for building drag and drop
1106 * applications. In addition to enabling drag and drop for specific elements,
1107 * the drag and drop elements are tracked by the manager class, and the
1108 * interactions between the various elements are tracked during the drag and
1109 * the implementing code is notified about these important moments.
1110 * @module dragdrop
1111 * @title Drag and Drop
1112 * @requires yahoo,dom,event
1113 * @namespace YAHOO.util
1114 */
1115
1116// Only load the library once. Rewriting the manager class would orphan
1117// existing drag and drop instances.
1118if (!YAHOO.util.DragDropMgr) {
1119
1120/**
1121 * DragDropMgr is a singleton that tracks the element interaction for
1122 * all DragDrop items in the window. Generally, you will not call
1123 * this class directly, but it does have helper methods that could
1124 * be useful in your DragDrop implementations.
1125 * @class DragDropMgr
1126 * @static
1127 */
1128YAHOO.util.DragDropMgr = function() {
1129
1130 var Event = YAHOO.util.Event;
1131
1132 return {
1133
1134 /**
1135 * Two dimensional Array of registered DragDrop objects. The first
1136 * dimension is the DragDrop item group, the second the DragDrop
1137 * object.
1138 * @property ids
1139 * @type {string: string}
1140 * @private
1141 * @static
1142 */
1143 ids: {},
1144
1145 /**
1146 * Array of element ids defined as drag handles. Used to determine
1147 * if the element that generated the mousedown event is actually the
1148 * handle and not the html element itself.
1149 * @property handleIds
1150 * @type {string: string}
1151 * @private
1152 * @static
1153 */
1154 handleIds: {},
1155
1156 /**
1157 * the DragDrop object that is currently being dragged
1158 * @property dragCurrent
1159 * @type DragDrop
1160 * @private
1161 * @static
1162 **/
1163 dragCurrent: null,
1164
1165 /**
1166 * the DragDrop object(s) that are being hovered over
1167 * @property dragOvers
1168 * @type Array
1169 * @private
1170 * @static
1171 */
1172 dragOvers: {},
1173
1174 /**
1175 * the X distance between the cursor and the object being dragged
1176 * @property deltaX
1177 * @type int
1178 * @private
1179 * @static
1180 */
1181 deltaX: 0,
1182
1183 /**
1184 * the Y distance between the cursor and the object being dragged
1185 * @property deltaY
1186 * @type int
1187 * @private
1188 * @static
1189 */
1190 deltaY: 0,
1191
1192 /**
1193 * Flag to determine if we should prevent the default behavior of the
1194 * events we define. By default this is true, but this can be set to
1195 * false if you need the default behavior (not recommended)
1196 * @property preventDefault
1197 * @type boolean
1198 * @static
1199 */
1200 preventDefault: true,
1201
1202 /**
1203 * Flag to determine if we should stop the propagation of the events
1204 * we generate. This is true by default but you may want to set it to
1205 * false if the html element contains other features that require the
1206 * mouse click.
1207 * @property stopPropagation
1208 * @type boolean
1209 * @static
1210 */
1211 stopPropagation: true,
1212
1213 /**
1214 * Internal flag that is set to true when drag and drop has been
1215 * intialized
1216 * @property initialized
1217 * @private
1218 * @static
1219 */
1220 initalized: false,
1221
1222 /**
1223 * All drag and drop can be disabled.
1224 * @property locked
1225 * @private
1226 * @static
1227 */
1228 locked: false,
1229
1230 /**
1231 * Called the first time an element is registered.
1232 * @method init
1233 * @private
1234 * @static
1235 */
1236 init: function() {
1237 this.initialized = true;
1238 },
1239
1240 /**
1241 * In point mode, drag and drop interaction is defined by the
1242 * location of the cursor during the drag/drop
1243 * @property POINT
1244 * @type int
1245 * @static
1246 */
1247 POINT: 0,
1248
1249 /**
1250 * In intersect mode, drag and drop interactio nis defined by the
1251 * overlap of two or more drag and drop objects.
1252 * @property INTERSECT
1253 * @type int
1254 * @static
1255 */
1256 INTERSECT: 1,
1257
1258 /**
1259 * The current drag and drop mode. Default: POINT
1260 * @property mode
1261 * @type int
1262 * @static
1263 */
1264 mode: 0,
1265
1266 /**
1267 * Runs method on all drag and drop objects
1268 * @method _execOnAll
1269 * @private
1270 * @static
1271 */
1272 _execOnAll: function(sMethod, args) {
1273 for (var i in this.ids) {
1274 for (var j in this.ids[i]) {
1275 var oDD = this.ids[i][j];
1276 if (! this.isTypeOfDD(oDD)) {
1277 continue;
1278 }
1279 oDD[sMethod].apply(oDD, args);
1280 }
1281 }
1282 },
1283
1284 /**
1285 * Drag and drop initialization. Sets up the global event handlers
1286 * @method _onLoad
1287 * @private
1288 * @static
1289 */
1290 _onLoad: function() {
1291
1292 this.init();
1293
1294
1295 Event.on(document, "mouseup", this.handleMouseUp, this, true);
1296 Event.on(document, "mousemove", this.handleMouseMove, this, true);
1297 Event.on(window, "unload", this._onUnload, this, true);
1298 Event.on(window, "resize", this._onResize, this, true);
1299 // Event.on(window, "mouseout", this._test);
1300
1301 },
1302
1303 /**
1304 * Reset constraints on all drag and drop objs
1305 * @method _onResize
1306 * @private
1307 * @static
1308 */
1309 _onResize: function(e) {
1310 this._execOnAll("resetConstraints", []);
1311 },
1312
1313 /**
1314 * Lock all drag and drop functionality
1315 * @method lock
1316 * @static
1317 */
1318 lock: function() { this.locked = true; },
1319
1320 /**
1321 * Unlock all drag and drop functionality
1322 * @method unlock
1323 * @static
1324 */
1325 unlock: function() { this.locked = false; },
1326
1327 /**
1328 * Is drag and drop locked?
1329 * @method isLocked
1330 * @return {boolean} True if drag and drop is locked, false otherwise.
1331 * @static
1332 */
1333 isLocked: function() { return this.locked; },
1334
1335 /**
1336 * Location cache that is set for all drag drop objects when a drag is
1337 * initiated, cleared when the drag is finished.
1338 * @property locationCache
1339 * @private
1340 * @static
1341 */
1342 locationCache: {},
1343
1344 /**
1345 * Set useCache to false if you want to force object the lookup of each
1346 * drag and drop linked element constantly during a drag.
1347 * @property useCache
1348 * @type boolean
1349 * @static
1350 */
1351 useCache: true,
1352
1353 /**
1354 * The number of pixels that the mouse needs to move after the
1355 * mousedown before the drag is initiated. Default=3;
1356 * @property clickPixelThresh
1357 * @type int
1358 * @static
1359 */
1360 clickPixelThresh: 3,
1361
1362 /**
1363 * The number of milliseconds after the mousedown event to initiate the
1364 * drag if we don't get a mouseup event. Default=1000
1365 * @property clickTimeThresh
1366 * @type int
1367 * @static
1368 */
1369 clickTimeThresh: 1000,
1370
1371 /**
1372 * Flag that indicates that either the drag pixel threshold or the
1373 * mousdown time threshold has been met
1374 * @property dragThreshMet
1375 * @type boolean
1376 * @private
1377 * @static
1378 */
1379 dragThreshMet: false,
1380
1381 /**
1382 * Timeout used for the click time threshold
1383 * @property clickTimeout
1384 * @type Object
1385 * @private
1386 * @static
1387 */
1388 clickTimeout: null,
1389
1390 /**
1391 * The X position of the mousedown event stored for later use when a
1392 * drag threshold is met.
1393 * @property startX
1394 * @type int
1395 * @private
1396 * @static
1397 */
1398 startX: 0,
1399
1400 /**
1401 * The Y position of the mousedown event stored for later use when a
1402 * drag threshold is met.
1403 * @property startY
1404 * @type int
1405 * @private
1406 * @static
1407 */
1408 startY: 0,
1409
1410 /**
1411 * Each DragDrop instance must be registered with the DragDropMgr.
1412 * This is executed in DragDrop.init()
1413 * @method regDragDrop
1414 * @param {DragDrop} oDD the DragDrop object to register
1415 * @param {String} sGroup the name of the group this element belongs to
1416 * @static
1417 */
1418 regDragDrop: function(oDD, sGroup) {
1419 if (!this.initialized) { this.init(); }
1420
1421 if (!this.ids[sGroup]) {
1422 this.ids[sGroup] = {};
1423 }
1424 this.ids[sGroup][oDD.id] = oDD;
1425 },
1426
1427 /**
1428 * Removes the supplied dd instance from the supplied group. Executed
1429 * by DragDrop.removeFromGroup, so don't call this function directly.
1430 * @method removeDDFromGroup
1431 * @private
1432 * @static
1433 */
1434 removeDDFromGroup: function(oDD, sGroup) {
1435 if (!this.ids[sGroup]) {
1436 this.ids[sGroup] = {};
1437 }
1438
1439 var obj = this.ids[sGroup];
1440 if (obj && obj[oDD.id]) {
1441 delete obj[oDD.id];
1442 }
1443 },
1444
1445 /**
1446 * Unregisters a drag and drop item. This is executed in
1447 * DragDrop.unreg, use that method instead of calling this directly.
1448 * @method _remove
1449 * @private
1450 * @static
1451 */
1452 _remove: function(oDD) {
1453 for (var g in oDD.groups) {
1454 if (g && this.ids[g][oDD.id]) {
1455 delete this.ids[g][oDD.id];
1456 }
1457 }
1458 delete this.handleIds[oDD.id];
1459 },
1460
1461 /**
1462 * Each DragDrop handle element must be registered. This is done
1463 * automatically when executing DragDrop.setHandleElId()
1464 * @method regHandle
1465 * @param {String} sDDId the DragDrop id this element is a handle for
1466 * @param {String} sHandleId the id of the element that is the drag
1467 * handle
1468 * @static
1469 */
1470 regHandle: function(sDDId, sHandleId) {
1471 if (!this.handleIds[sDDId]) {
1472 this.handleIds[sDDId] = {};
1473 }
1474 this.handleIds[sDDId][sHandleId] = sHandleId;
1475 },
1476
1477 /**
1478 * Utility function to determine if a given element has been
1479 * registered as a drag drop item.
1480 * @method isDragDrop
1481 * @param {String} id the element id to check
1482 * @return {boolean} true if this element is a DragDrop item,
1483 * false otherwise
1484 * @static
1485 */
1486 isDragDrop: function(id) {
1487 return ( this.getDDById(id) ) ? true : false;
1488 },
1489
1490 /**
1491 * Returns the drag and drop instances that are in all groups the
1492 * passed in instance belongs to.
1493 * @method getRelated
1494 * @param {DragDrop} p_oDD the obj to get related data for
1495 * @param {boolean} bTargetsOnly if true, only return targetable objs
1496 * @return {DragDrop[]} the related instances
1497 * @static
1498 */
1499 getRelated: function(p_oDD, bTargetsOnly) {
1500 var oDDs = [];
1501 for (var i in p_oDD.groups) {
1502 for (j in this.ids[i]) {
1503 var dd = this.ids[i][j];
1504 if (! this.isTypeOfDD(dd)) {
1505 continue;
1506 }
1507 if (!bTargetsOnly || dd.isTarget) {
1508 oDDs[oDDs.length] = dd;
1509 }
1510 }
1511 }
1512
1513 return oDDs;
1514 },
1515
1516 /**
1517 * Returns true if the specified dd target is a legal target for
1518 * the specifice drag obj
1519 * @method isLegalTarget
1520 * @param {DragDrop} the drag obj
1521 * @param {DragDrop} the target
1522 * @return {boolean} true if the target is a legal target for the
1523 * dd obj
1524 * @static
1525 */
1526 isLegalTarget: function (oDD, oTargetDD) {
1527 var targets = this.getRelated(oDD, true);
1528 for (var i=0, len=targets.length;i<len;++i) {
1529 if (targets[i].id == oTargetDD.id) {
1530 return true;
1531 }
1532 }
1533
1534 return false;
1535 },
1536
1537 /**
1538 * My goal is to be able to transparently determine if an object is
1539 * typeof DragDrop, and the exact subclass of DragDrop. typeof
1540 * returns "object", oDD.constructor.toString() always returns
1541 * "DragDrop" and not the name of the subclass. So for now it just
1542 * evaluates a well-known variable in DragDrop.
1543 * @method isTypeOfDD
1544 * @param {Object} the object to evaluate
1545 * @return {boolean} true if typeof oDD = DragDrop
1546 * @static
1547 */
1548 isTypeOfDD: function (oDD) {
1549 return (oDD && oDD.__ygDragDrop);
1550 },
1551
1552 /**
1553 * Utility function to determine if a given element has been
1554 * registered as a drag drop handle for the given Drag Drop object.
1555 * @method isHandle
1556 * @param {String} id the element id to check
1557 * @return {boolean} true if this element is a DragDrop handle, false
1558 * otherwise
1559 * @static
1560 */
1561 isHandle: function(sDDId, sHandleId) {
1562 return ( this.handleIds[sDDId] &&
1563 this.handleIds[sDDId][sHandleId] );
1564 },
1565
1566 /**
1567 * Returns the DragDrop instance for a given id
1568 * @method getDDById
1569 * @param {String} id the id of the DragDrop object
1570 * @return {DragDrop} the drag drop object, null if it is not found
1571 * @static
1572 */
1573 getDDById: function(id) {
1574 for (var i in this.ids) {
1575 if (this.ids[i][id]) {
1576 return this.ids[i][id];
1577 }
1578 }
1579 return null;
1580 },
1581
1582 /**
1583 * Fired after a registered DragDrop object gets the mousedown event.
1584 * Sets up the events required to track the object being dragged
1585 * @method handleMouseDown
1586 * @param {Event} e the event
1587 * @param oDD the DragDrop object being dragged
1588 * @private
1589 * @static
1590 */
1591 handleMouseDown: function(e, oDD) {
1592
1593 this.currentTarget = YAHOO.util.Event.getTarget(e);
1594
1595 this.dragCurrent = oDD;
1596
1597 var el = oDD.getEl();
1598
1599 // track start position
1600 this.startX = YAHOO.util.Event.getPageX(e);
1601 this.startY = YAHOO.util.Event.getPageY(e);
1602
1603 this.deltaX = this.startX - el.offsetLeft;
1604 this.deltaY = this.startY - el.offsetTop;
1605
1606 this.dragThreshMet = false;
1607
1608 this.clickTimeout = setTimeout(
1609 function() {
1610 var DDM = YAHOO.util.DDM;
1611 DDM.startDrag(DDM.startX, DDM.startY);
1612 },
1613 this.clickTimeThresh );
1614 },
1615
1616 /**
1617 * Fired when either the drag pixel threshol or the mousedown hold
1618 * time threshold has been met.
1619 * @method startDrag
1620 * @param x {int} the X position of the original mousedown
1621 * @param y {int} the Y position of the original mousedown
1622 * @static
1623 */
1624 startDrag: function(x, y) {
1625 clearTimeout(this.clickTimeout);
1626 if (this.dragCurrent) {
1627 this.dragCurrent.b4StartDrag(x, y);
1628 this.dragCurrent.startDrag(x, y);
1629 }
1630 this.dragThreshMet = true;
1631 },
1632
1633 /**
1634 * Internal function to handle the mouseup event. Will be invoked
1635 * from the context of the document.
1636 * @method handleMouseUp
1637 * @param {Event} e the event
1638 * @private
1639 * @static
1640 */
1641 handleMouseUp: function(e) {
1642
1643 if (! this.dragCurrent) {
1644 return;
1645 }
1646
1647 clearTimeout(this.clickTimeout);
1648
1649 if (this.dragThreshMet) {
1650 this.fireEvents(e, true);
1651 } else {
1652 }
1653
1654 this.stopDrag(e);
1655
1656 this.stopEvent(e);
1657 },
1658
1659 /**
1660 * Utility to stop event propagation and event default, if these
1661 * features are turned on.
1662 * @method stopEvent
1663 * @param {Event} e the event as returned by this.getEvent()
1664 * @static
1665 */
1666 stopEvent: function(e) {
1667 if (this.stopPropagation) {
1668 YAHOO.util.Event.stopPropagation(e);
1669 }
1670
1671 if (this.preventDefault) {
1672 YAHOO.util.Event.preventDefault(e);
1673 }
1674 },
1675
1676 /**
1677 * Internal function to clean up event handlers after the drag
1678 * operation is complete
1679 * @method stopDrag
1680 * @param {Event} e the event
1681 * @private
1682 * @static
1683 */
1684 stopDrag: function(e) {
1685
1686 // Fire the drag end event for the item that was dragged
1687 if (this.dragCurrent) {
1688 if (this.dragThreshMet) {
1689 this.dragCurrent.b4EndDrag(e);
1690 this.dragCurrent.endDrag(e);
1691 }
1692
1693 this.dragCurrent.onMouseUp(e);
1694 }
1695
1696 this.dragCurrent = null;
1697 this.dragOvers = {};
1698 },
1699
1700 /**
1701 * Internal function to handle the mousemove event. Will be invoked
1702 * from the context of the html element.
1703 *
1704 * @TODO figure out what we can do about mouse events lost when the
1705 * user drags objects beyond the window boundary. Currently we can
1706 * detect this in internet explorer by verifying that the mouse is
1707 * down during the mousemove event. Firefox doesn't give us the
1708 * button state on the mousemove event.
1709 * @method handleMouseMove
1710 * @param {Event} e the event
1711 * @private
1712 * @static
1713 */
1714 handleMouseMove: function(e) {
1715 if (! this.dragCurrent) {
1716 return true;
1717 }
1718
1719 // var button = e.which || e.button;
1720
1721 // check for IE mouseup outside of page boundary
1722 if (YAHOO.util.Event.isIE && !e.button) {
1723 this.stopEvent(e);
1724 return this.handleMouseUp(e);
1725 }
1726
1727 if (!this.dragThreshMet) {
1728 var diffX = Math.abs(this.startX - YAHOO.util.Event.getPageX(e));
1729 var diffY = Math.abs(this.startY - YAHOO.util.Event.getPageY(e));
1730 if (diffX > this.clickPixelThresh ||
1731 diffY > this.clickPixelThresh) {
1732 this.startDrag(this.startX, this.startY);
1733 }
1734 }
1735
1736 if (this.dragThreshMet) {
1737 this.dragCurrent.b4Drag(e);
1738 this.dragCurrent.onDrag(e);
1739 this.fireEvents(e, false);
1740 }
1741
1742 this.stopEvent(e);
1743
1744 return true;
1745 },
1746
1747 /**
1748 * Iterates over all of the DragDrop elements to find ones we are
1749 * hovering over or dropping on
1750 * @method fireEvents
1751 * @param {Event} e the event
1752 * @param {boolean} isDrop is this a drop op or a mouseover op?
1753 * @private
1754 * @static
1755 */
1756 fireEvents: function(e, isDrop) {
1757 var dc = this.dragCurrent;
1758
1759 // If the user did the mouse up outside of the window, we could
1760 // get here even though we have ended the drag.
1761 if (!dc || dc.isLocked()) {
1762 return;
1763 }
1764
1765 var x = YAHOO.util.Event.getPageX(e);
1766 var y = YAHOO.util.Event.getPageY(e);
1767 var pt = new YAHOO.util.Point(x,y);
1768
1769 // cache the previous dragOver array
1770 var oldOvers = [];
1771
1772 var outEvts = [];
1773 var overEvts = [];
1774 var dropEvts = [];
1775 var enterEvts = [];
1776
1777 // Check to see if the object(s) we were hovering over is no longer
1778 // being hovered over so we can fire the onDragOut event
1779 for (var i in this.dragOvers) {
1780
1781 var ddo = this.dragOvers[i];
1782
1783 if (! this.isTypeOfDD(ddo)) {
1784 continue;
1785 }
1786
1787 if (! this.isOverTarget(pt, ddo, this.mode)) {
1788 outEvts.push( ddo );
1789 }
1790
1791 oldOvers[i] = true;
1792 delete this.dragOvers[i];
1793 }
1794
1795 for (var sGroup in dc.groups) {
1796
1797 if ("string" != typeof sGroup) {
1798 continue;
1799 }
1800
1801 for (i in this.ids[sGroup]) {
1802 var oDD = this.ids[sGroup][i];
1803 if (! this.isTypeOfDD(oDD)) {
1804 continue;
1805 }
1806
1807 if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1808 if (this.isOverTarget(pt, oDD, this.mode)) {
1809 // look for drop interactions
1810 if (isDrop) {
1811 dropEvts.push( oDD );
1812 // look for drag enter and drag over interactions
1813 } else {
1814
1815 // initial drag over: dragEnter fires
1816 if (!oldOvers[oDD.id]) {
1817 enterEvts.push( oDD );
1818 // subsequent drag overs: dragOver fires
1819 } else {
1820 overEvts.push( oDD );
1821 }
1822
1823 this.dragOvers[oDD.id] = oDD;
1824 }
1825 }
1826 }
1827 }
1828 }
1829
1830 if (this.mode) {
1831 if (outEvts.length) {
1832 dc.b4DragOut(e, outEvts);
1833 dc.onDragOut(e, outEvts);
1834 }
1835
1836 if (enterEvts.length) {
1837 dc.onDragEnter(e, enterEvts);
1838 }
1839
1840 if (overEvts.length) {
1841 dc.b4DragOver(e, overEvts);
1842 dc.onDragOver(e, overEvts);
1843 }
1844
1845 if (dropEvts.length) {
1846 dc.b4DragDrop(e, dropEvts);
1847 dc.onDragDrop(e, dropEvts);
1848 }
1849
1850 } else {
1851 // fire dragout events
1852 var len = 0;
1853 for (i=0, len=outEvts.length; i<len; ++i) {
1854 dc.b4DragOut(e, outEvts[i].id);
1855 dc.onDragOut(e, outEvts[i].id);
1856 }
1857
1858 // fire enter events
1859 for (i=0,len=enterEvts.length; i<len; ++i) {
1860 // dc.b4DragEnter(e, oDD.id);
1861 dc.onDragEnter(e, enterEvts[i].id);
1862 }
1863
1864 // fire over events
1865 for (i=0,len=overEvts.length; i<len; ++i) {
1866 dc.b4DragOver(e, overEvts[i].id);
1867 dc.onDragOver(e, overEvts[i].id);
1868 }
1869
1870 // fire drop events
1871 for (i=0, len=dropEvts.length; i<len; ++i) {
1872 dc.b4DragDrop(e, dropEvts[i].id);
1873 dc.onDragDrop(e, dropEvts[i].id);
1874 }
1875
1876 }
1877
1878 // notify about a drop that did not find a target
1879 if (isDrop && !dropEvts.length) {
1880 dc.onInvalidDrop(e);
1881 }
1882
1883 },
1884
1885 /**
1886 * Helper function for getting the best match from the list of drag
1887 * and drop objects returned by the drag and drop events when we are
1888 * in INTERSECT mode. It returns either the first object that the
1889 * cursor is over, or the object that has the greatest overlap with
1890 * the dragged element.
1891 * @method getBestMatch
1892 * @param {DragDrop[]} dds The array of drag and drop objects
1893 * targeted
1894 * @return {DragDrop} The best single match
1895 * @static
1896 */
1897 getBestMatch: function(dds) {
1898 var winner = null;
1899 // Return null if the input is not what we expect
1900 //if (!dds || !dds.length || dds.length == 0) {
1901 // winner = null;
1902 // If there is only one item, it wins
1903 //} else if (dds.length == 1) {
1904
1905 var len = dds.length;
1906
1907 if (len == 1) {
1908 winner = dds[0];
1909 } else {
1910 // Loop through the targeted items
1911 for (var i=0; i<len; ++i) {
1912 var dd = dds[i];
1913 // If the cursor is over the object, it wins. If the
1914 // cursor is over multiple matches, the first one we come
1915 // to wins.
1916 if (dd.cursorIsOver) {
1917 winner = dd;
1918 break;
1919 // Otherwise the object with the most overlap wins
1920 } else {
1921 if (!winner ||
1922 winner.overlap.getArea() < dd.overlap.getArea()) {
1923 winner = dd;
1924 }
1925 }
1926 }
1927 }
1928
1929 return winner;
1930 },
1931
1932 /**
1933 * Refreshes the cache of the top-left and bottom-right points of the
1934 * drag and drop objects in the specified group(s). This is in the
1935 * format that is stored in the drag and drop instance, so typical
1936 * usage is:
1937 * <code>
1938 * YAHOO.util.DragDropMgr.refreshCache(ddinstance.groups);
1939 * </code>
1940 * Alternatively:
1941 * <code>
1942 * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
1943 * </code>
1944 * @TODO this really should be an indexed array. Alternatively this
1945 * method could accept both.
1946 * @method refreshCache
1947 * @param {Object} groups an associative array of groups to refresh
1948 * @static
1949 */
1950 refreshCache: function(groups) {
1951 for (var sGroup in groups) {
1952 if ("string" != typeof sGroup) {
1953 continue;
1954 }
1955 for (var i in this.ids[sGroup]) {
1956 var oDD = this.ids[sGroup][i];
1957
1958 if (this.isTypeOfDD(oDD)) {
1959 // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
1960 var loc = this.getLocation(oDD);
1961 if (loc) {
1962 this.locationCache[oDD.id] = loc;
1963 } else {
1964 delete this.locationCache[oDD.id];
1965 // this will unregister the drag and drop object if
1966 // the element is not in a usable state
1967 // oDD.unreg();
1968 }
1969 }
1970 }
1971 }
1972 },
1973
1974 /**
1975 * This checks to make sure an element exists and is in the DOM. The
1976 * main purpose is to handle cases where innerHTML is used to remove
1977 * drag and drop objects from the DOM. IE provides an 'unspecified
1978 * error' when trying to access the offsetParent of such an element
1979 * @method verifyEl
1980 * @param {HTMLElement} el the element to check
1981 * @return {boolean} true if the element looks usable
1982 * @static
1983 */
1984 verifyEl: function(el) {
1985 try {
1986 if (el) {
1987 var parent = el.offsetParent;
1988 if (parent) {
1989 return true;
1990 }
1991 }
1992 } catch(e) {
1993 }
1994
1995 return false;
1996 },
1997
1998 /**
1999 * Returns a Region object containing the drag and drop element's position
2000 * and size, including the padding configured for it
2001 * @method getLocation
2002 * @param {DragDrop} oDD the drag and drop object to get the
2003 * location for
2004 * @return {YAHOO.util.Region} a Region object representing the total area
2005 * the element occupies, including any padding
2006 * the instance is configured for.
2007 * @static
2008 */
2009 getLocation: function(oDD) {
2010 if (! this.isTypeOfDD(oDD)) {
2011 return null;
2012 }
2013
2014 var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2015
2016 try {
2017 pos= YAHOO.util.Dom.getXY(el);
2018 } catch (e) { }
2019
2020 if (!pos) {
2021 return null;
2022 }
2023
2024 x1 = pos[0];
2025 x2 = x1 + el.offsetWidth;
2026 y1 = pos[1];
2027 y2 = y1 + el.offsetHeight;
2028
2029 t = y1 - oDD.padding[0];
2030 r = x2 + oDD.padding[1];
2031 b = y2 + oDD.padding[2];
2032 l = x1 - oDD.padding[3];
2033
2034 return new YAHOO.util.Region( t, r, b, l );
2035 },
2036
2037 /**
2038 * Checks the cursor location to see if it over the target
2039 * @method isOverTarget
2040 * @param {YAHOO.util.Point} pt The point to evaluate
2041 * @param {DragDrop} oTarget the DragDrop object we are inspecting
2042 * @return {boolean} true if the mouse is over the target
2043 * @private
2044 * @static
2045 */
2046 isOverTarget: function(pt, oTarget, intersect) {
2047 // use cache if available
2048 var loc = this.locationCache[oTarget.id];
2049 if (!loc || !this.useCache) {
2050 loc = this.getLocation(oTarget);
2051 this.locationCache[oTarget.id] = loc;
2052
2053 }
2054
2055 if (!loc) {
2056 return false;
2057 }
2058
2059 oTarget.cursorIsOver = loc.contains( pt );
2060
2061 // DragDrop is using this as a sanity check for the initial mousedown
2062 // in this case we are done. In POINT mode, if the drag obj has no
2063 // contraints, we are also done. Otherwise we need to evaluate the
2064 // location of the target as related to the actual location of the
2065 // dragged element.
2066 var dc = this.dragCurrent;
2067 if (!dc || !dc.getTargetCoord ||
2068 (!intersect && !dc.constrainX && !dc.constrainY)) {
2069 return oTarget.cursorIsOver;
2070 }
2071
2072 oTarget.overlap = null;
2073
2074 // Get the current location of the drag element, this is the
2075 // location of the mouse event less the delta that represents
2076 // where the original mousedown happened on the element. We
2077 // need to consider constraints and ticks as well.
2078 var pos = dc.getTargetCoord(pt.x, pt.y);
2079
2080 var el = dc.getDragEl();
2081 var curRegion = new YAHOO.util.Region( pos.y,
2082 pos.x + el.offsetWidth,
2083 pos.y + el.offsetHeight,
2084 pos.x );
2085
2086 var overlap = curRegion.intersect(loc);
2087
2088 if (overlap) {
2089 oTarget.overlap = overlap;
2090 return (intersect) ? true : oTarget.cursorIsOver;
2091 } else {
2092 return false;
2093 }
2094 },
2095
2096 /**
2097 * unload event handler
2098 * @method _onUnload
2099 * @private
2100 * @static
2101 */
2102 _onUnload: function(e, me) {
2103 this.unregAll();
2104 },
2105
2106 /**
2107 * Cleans up the drag and drop events and objects.
2108 * @method unregAll
2109 * @private
2110 * @static
2111 */
2112 unregAll: function() {
2113
2114 if (this.dragCurrent) {
2115 this.stopDrag();
2116 this.dragCurrent = null;
2117 }
2118
2119 this._execOnAll("unreg", []);
2120
2121 for (i in this.elementCache) {
2122 delete this.elementCache[i];
2123 }
2124
2125 this.elementCache = {};
2126 this.ids = {};
2127 },
2128
2129 /**
2130 * A cache of DOM elements
2131 * @property elementCache
2132 * @private
2133 * @static
2134 */
2135 elementCache: {},
2136
2137 /**
2138 * Get the wrapper for the DOM element specified
2139 * @method getElWrapper
2140 * @param {String} id the id of the element to get
2141 * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
2142 * @private
2143 * @deprecated This wrapper isn't that useful
2144 * @static
2145 */
2146 getElWrapper: function(id) {
2147 var oWrapper = this.elementCache[id];
2148 if (!oWrapper || !oWrapper.el) {
2149 oWrapper = this.elementCache[id] =
2150 new this.ElementWrapper(YAHOO.util.Dom.get(id));
2151 }
2152 return oWrapper;
2153 },
2154
2155 /**
2156 * Returns the actual DOM element
2157 * @method getElement
2158 * @param {String} id the id of the elment to get
2159 * @return {Object} The element
2160 * @deprecated use YAHOO.util.Dom.get instead
2161 * @static
2162 */
2163 getElement: function(id) {
2164 return YAHOO.util.Dom.get(id);
2165 },
2166
2167 /**
2168 * Returns the style property for the DOM element (i.e.,
2169 * document.getElById(id).style)
2170 * @method getCss
2171 * @param {String} id the id of the elment to get
2172 * @return {Object} The style property of the element
2173 * @deprecated use YAHOO.util.Dom instead
2174 * @static
2175 */
2176 getCss: function(id) {
2177 var el = YAHOO.util.Dom.get(id);
2178 return (el) ? el.style : null;
2179 },
2180
2181 /**
2182 * Inner class for cached elements
2183 * @class DragDropMgr.ElementWrapper
2184 * @for DragDropMgr
2185 * @private
2186 * @deprecated
2187 */
2188 ElementWrapper: function(el) {
2189 /**
2190 * The element
2191 * @property el
2192 */
2193 this.el = el || null;
2194 /**
2195 * The element id
2196 * @property id
2197 */
2198 this.id = this.el && el.id;
2199 /**
2200 * A reference to the style property
2201 * @property css
2202 */
2203 this.css = this.el && el.style;
2204 },
2205
2206 /**
2207 * Returns the X position of an html element
2208 * @method getPosX
2209 * @param el the element for which to get the position
2210 * @return {int} the X coordinate
2211 * @for DragDropMgr
2212 * @deprecated use YAHOO.util.Dom.getX instead
2213 * @static
2214 */
2215 getPosX: function(el) {
2216 return YAHOO.util.Dom.getX(el);
2217 },
2218
2219 /**
2220 * Returns the Y position of an html element
2221 * @method getPosY
2222 * @param el the element for which to get the position
2223 * @return {int} the Y coordinate
2224 * @deprecated use YAHOO.util.Dom.getY instead
2225 * @static
2226 */
2227 getPosY: function(el) {
2228 return YAHOO.util.Dom.getY(el);
2229 },
2230
2231 /**
2232 * Swap two nodes. In IE, we use the native method, for others we
2233 * emulate the IE behavior
2234 * @method swapNode
2235 * @param n1 the first node to swap
2236 * @param n2 the other node to swap
2237 * @static
2238 */
2239 swapNode: function(n1, n2) {
2240 if (n1.swapNode) {
2241 n1.swapNode(n2);
2242 } else {
2243 var p = n2.parentNode;
2244 var s = n2.nextSibling;
2245
2246 if (s == n1) {
2247 p.insertBefore(n1, n2);
2248 } else if (n2 == n1.nextSibling) {
2249 p.insertBefore(n2, n1);
2250 } else {
2251 n1.parentNode.replaceChild(n2, n1);
2252 p.insertBefore(n1, s);
2253 }
2254 }
2255 },
2256
2257 /**
2258 * Returns the current scroll position
2259 * @method getScroll
2260 * @private
2261 * @static
2262 */
2263 getScroll: function () {
2264 var t, l, dde=document.documentElement, db=document.body;
2265 if (dde && (dde.scrollTop || dde.scrollLeft)) {
2266 t = dde.scrollTop;
2267 l = dde.scrollLeft;
2268 } else if (db) {
2269 t = db.scrollTop;
2270 l = db.scrollLeft;
2271 } else {
2272 YAHOO.log("could not get scroll property");
2273 }
2274 return { top: t, left: l };
2275 },
2276
2277 /**
2278 * Returns the specified element style property
2279 * @method getStyle
2280 * @param {HTMLElement} el the element
2281 * @param {string} styleProp the style property
2282 * @return {string} The value of the style property
2283 * @deprecated use YAHOO.util.Dom.getStyle
2284 * @static
2285 */
2286 getStyle: function(el, styleProp) {
2287 return YAHOO.util.Dom.getStyle(el, styleProp);
2288 },
2289
2290 /**
2291 * Gets the scrollTop
2292 * @method getScrollTop
2293 * @return {int} the document's scrollTop
2294 * @static
2295 */
2296 getScrollTop: function () { return this.getScroll().top; },
2297
2298 /**
2299 * Gets the scrollLeft
2300 * @method getScrollLeft
2301 * @return {int} the document's scrollTop
2302 * @static
2303 */
2304 getScrollLeft: function () { return this.getScroll().left; },
2305
2306 /**
2307 * Sets the x/y position of an element to the location of the
2308 * target element.
2309 * @method moveToEl
2310 * @param {HTMLElement} moveEl The element to move
2311 * @param {HTMLElement} targetEl The position reference element
2312 * @static
2313 */
2314 moveToEl: function (moveEl, targetEl) {
2315 var aCoord = YAHOO.util.Dom.getXY(targetEl);
2316 YAHOO.util.Dom.setXY(moveEl, aCoord);
2317 },
2318
2319 /**
2320 * Gets the client height
2321 * @method getClientHeight
2322 * @return {int} client height in px
2323 * @deprecated use YAHOO.util.Dom.getViewportHeight instead
2324 * @static
2325 */
2326 getClientHeight: function() {
2327 return YAHOO.util.Dom.getViewportHeight();
2328 },
2329
2330 /**
2331 * Gets the client width
2332 * @method getClientWidth
2333 * @return {int} client width in px
2334 * @deprecated use YAHOO.util.Dom.getViewportWidth instead
2335 * @static
2336 */
2337 getClientWidth: function() {
2338 return YAHOO.util.Dom.getViewportWidth();
2339 },
2340
2341 /**
2342 * Numeric array sort function
2343 * @method numericSort
2344 * @static
2345 */
2346 numericSort: function(a, b) { return (a - b); },
2347
2348 /**
2349 * Internal counter
2350 * @property _timeoutCount
2351 * @private
2352 * @static
2353 */
2354 _timeoutCount: 0,
2355
2356 /**
2357 * Trying to make the load order less important. Without this we get
2358 * an error if this file is loaded before the Event Utility.
2359 * @method _addListeners
2360 * @private
2361 * @static
2362 */
2363 _addListeners: function() {
2364 var DDM = YAHOO.util.DDM;
2365 if ( YAHOO.util.Event && document ) {
2366 DDM._onLoad();
2367 } else {
2368 if (DDM._timeoutCount > 2000) {
2369 } else {
2370 setTimeout(DDM._addListeners, 10);
2371 if (document && document.body) {
2372 DDM._timeoutCount += 1;
2373 }
2374 }
2375 }
2376 },
2377
2378 /**
2379 * Recursively searches the immediate parent and all child nodes for
2380 * the handle element in order to determine wheter or not it was
2381 * clicked.
2382 * @method handleWasClicked
2383 * @param node the html element to inspect
2384 * @static
2385 */
2386 handleWasClicked: function(node, id) {
2387 if (this.isHandle(id, node.id)) {
2388 return true;
2389 } else {
2390 // check to see if this is a text node child of the one we want
2391 var p = node.parentNode;
2392
2393 while (p) {
2394 if (this.isHandle(id, p.id)) {
2395 return true;
2396 } else {
2397 p = p.parentNode;
2398 }
2399 }
2400 }
2401
2402 return false;
2403 }
2404
2405 };
2406
2407}();
2408
2409// shorter alias, save a few bytes
2410YAHOO.util.DDM = YAHOO.util.DragDropMgr;
2411YAHOO.util.DDM._addListeners();
2412
2413}
2414
2415/**
2416 * A DragDrop implementation where the linked element follows the
2417 * mouse cursor during a drag.
2418 * @class DD
2419 * @extends YAHOO.util.DragDrop
2420 * @constructor
2421 * @param {String} id the id of the linked element
2422 * @param {String} sGroup the group of related DragDrop items
2423 * @param {object} config an object containing configurable attributes
2424 * Valid properties for DD:
2425 * scroll
2426 */
2427YAHOO.util.DD = function(id, sGroup, config) {
2428 if (id) {
2429 this.init(id, sGroup, config);
2430 }
2431};
2432
2433YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
2434
2435 /**
2436 * When set to true, the utility automatically tries to scroll the browser
2437 * window wehn a drag and drop element is dragged near the viewport boundary.
2438 * Defaults to true.
2439 * @property scroll
2440 * @type boolean
2441 */
2442 scroll: true,
2443
2444 /**
2445 * Sets the pointer offset to the distance between the linked element's top
2446 * left corner and the location the element was clicked
2447 * @method autoOffset
2448 * @param {int} iPageX the X coordinate of the click
2449 * @param {int} iPageY the Y coordinate of the click
2450 */
2451 autoOffset: function(iPageX, iPageY) {
2452 var x = iPageX - this.startPageX;
2453 var y = iPageY - this.startPageY;
2454 this.setDelta(x, y);
2455 },
2456
2457 /**
2458 * Sets the pointer offset. You can call this directly to force the
2459 * offset to be in a particular location (e.g., pass in 0,0 to set it
2460 * to the center of the object, as done in YAHOO.widget.Slider)
2461 * @method setDelta
2462 * @param {int} iDeltaX the distance from the left
2463 * @param {int} iDeltaY the distance from the top
2464 */
2465 setDelta: function(iDeltaX, iDeltaY) {
2466 this.deltaX = iDeltaX;
2467 this.deltaY = iDeltaY;
2468 },
2469
2470 /**
2471 * Sets the drag element to the location of the mousedown or click event,
2472 * maintaining the cursor location relative to the location on the element
2473 * that was clicked. Override this if you want to place the element in a
2474 * location other than where the cursor is.
2475 * @method setDragElPos
2476 * @param {int} iPageX the X coordinate of the mousedown or drag event
2477 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2478 */
2479 setDragElPos: function(iPageX, iPageY) {
2480 // the first time we do this, we are going to check to make sure
2481 // the element has css positioning
2482
2483 var el = this.getDragEl();
2484 this.alignElWithMouse(el, iPageX, iPageY);
2485 },
2486
2487 /**
2488 * Sets the element to the location of the mousedown or click event,
2489 * maintaining the cursor location relative to the location on the element
2490 * that was clicked. Override this if you want to place the element in a
2491 * location other than where the cursor is.
2492 * @method alignElWithMouse
2493 * @param {HTMLElement} el the element to move
2494 * @param {int} iPageX the X coordinate of the mousedown or drag event
2495 * @param {int} iPageY the Y coordinate of the mousedown or drag event
2496 */
2497 alignElWithMouse: function(el, iPageX, iPageY) {
2498 var oCoord = this.getTargetCoord(iPageX, iPageY);
2499
2500 if (!this.deltaSetXY) {
2501 var aCoord = [oCoord.x, oCoord.y];
2502 YAHOO.util.Dom.setXY(el, aCoord);
2503 var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
2504 var newTop = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
2505
2506 this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2507 } else {
2508 YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
2509 YAHOO.util.Dom.setStyle(el, "top", (oCoord.y + this.deltaSetXY[1]) + "px");
2510 }
2511
2512 this.cachePosition(oCoord.x, oCoord.y);
2513 this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2514 },
2515
2516 /**
2517 * Saves the most recent position so that we can reset the constraints and
2518 * tick marks on-demand. We need to know this so that we can calculate the
2519 * number of pixels the element is offset from its original position.
2520 * @method cachePosition
2521 * @param iPageX the current x position (optional, this just makes it so we
2522 * don't have to look it up again)
2523 * @param iPageY the current y position (optional, this just makes it so we
2524 * don't have to look it up again)
2525 */
2526 cachePosition: function(iPageX, iPageY) {
2527 if (iPageX) {
2528 this.lastPageX = iPageX;
2529 this.lastPageY = iPageY;
2530 } else {
2531 var aCoord = YAHOO.util.Dom.getXY(this.getEl());
2532 this.lastPageX = aCoord[0];
2533 this.lastPageY = aCoord[1];
2534 }
2535 },
2536
2537 /**
2538 * Auto-scroll the window if the dragged object has been moved beyond the
2539 * visible window boundary.
2540 * @method autoScroll
2541 * @param {int} x the drag element's x position
2542 * @param {int} y the drag element's y position
2543 * @param {int} h the height of the drag element
2544 * @param {int} w the width of the drag element
2545 * @private
2546 */
2547 autoScroll: function(x, y, h, w) {
2548
2549 if (this.scroll) {
2550 // The client height
2551 var clientH = this.DDM.getClientHeight();
2552
2553 // The client width
2554 var clientW = this.DDM.getClientWidth();
2555
2556 // The amt scrolled down
2557 var st = this.DDM.getScrollTop();
2558
2559 // The amt scrolled right
2560 var sl = this.DDM.getScrollLeft();
2561
2562 // Location of the bottom of the element
2563 var bot = h + y;
2564
2565 // Location of the right of the element
2566 var right = w + x;
2567
2568 // The distance from the cursor to the bottom of the visible area,
2569 // adjusted so that we don't scroll if the cursor is beyond the
2570 // element drag constraints
2571 var toBot = (clientH + st - y - this.deltaY);
2572
2573 // The distance from the cursor to the right of the visible area
2574 var toRight = (clientW + sl - x - this.deltaX);
2575
2576
2577 // How close to the edge the cursor must be before we scroll
2578 // var thresh = (document.all) ? 100 : 40;
2579 var thresh = 40;
2580
2581 // How many pixels to scroll per autoscroll op. This helps to reduce
2582 // clunky scrolling. IE is more sensitive about this ... it needs this
2583 // value to be higher.
2584 var scrAmt = (document.all) ? 80 : 30;
2585
2586 // Scroll down if we are near the bottom of the visible page and the
2587 // obj extends below the crease
2588 if ( bot > clientH && toBot < thresh ) {
2589 window.scrollTo(sl, st + scrAmt);
2590 }
2591
2592 // Scroll up if the window is scrolled down and the top of the object
2593 // goes above the top border
2594 if ( y < st && st > 0 && y - st < thresh ) {
2595 window.scrollTo(sl, st - scrAmt);
2596 }
2597
2598 // Scroll right if the obj is beyond the right border and the cursor is
2599 // near the border.
2600 if ( right > clientW && toRight < thresh ) {
2601 window.scrollTo(sl + scrAmt, st);
2602 }
2603
2604 // Scroll left if the window has been scrolled to the right and the obj
2605 // extends past the left border
2606 if ( x < sl && sl > 0 && x - sl < thresh ) {
2607 window.scrollTo(sl - scrAmt, st);
2608 }
2609 }
2610 },
2611
2612 /**
2613 * Finds the location the element should be placed if we want to move
2614 * it to where the mouse location less the click offset would place us.
2615 * @method getTargetCoord
2616 * @param {int} iPageX the X coordinate of the click
2617 * @param {int} iPageY the Y coordinate of the click
2618 * @return an object that contains the coordinates (Object.x and Object.y)
2619 * @private
2620 */
2621 getTargetCoord: function(iPageX, iPageY) {
2622
2623
2624 var x = iPageX - this.deltaX;
2625 var y = iPageY - this.deltaY;
2626
2627 if (this.constrainX) {
2628 if (x < this.minX) { x = this.minX; }
2629 if (x > this.maxX) { x = this.maxX; }
2630 }
2631
2632 if (this.constrainY) {
2633 if (y < this.minY) { y = this.minY; }
2634 if (y > this.maxY) { y = this.maxY; }
2635 }
2636
2637 x = this.getTick(x, this.xTicks);
2638 y = this.getTick(y, this.yTicks);
2639
2640
2641 return {x:x, y:y};
2642 },
2643
2644 /*
2645 * Sets up config options specific to this class. Overrides
2646 * YAHOO.util.DragDrop, but all versions of this method through the
2647 * inheritance chain are called
2648 */
2649 applyConfig: function() {
2650 YAHOO.util.DD.superclass.applyConfig.call(this);
2651 this.scroll = (this.config.scroll !== false);
2652 },
2653
2654 /*
2655 * Event that fires prior to the onMouseDown event. Overrides
2656 * YAHOO.util.DragDrop.
2657 */
2658 b4MouseDown: function(e) {
2659 // this.resetConstraints();
2660 this.autoOffset(YAHOO.util.Event.getPageX(e),
2661 YAHOO.util.Event.getPageY(e));
2662 },
2663
2664 /*
2665 * Event that fires prior to the onDrag event. Overrides
2666 * YAHOO.util.DragDrop.
2667 */
2668 b4Drag: function(e) {
2669 this.setDragElPos(YAHOO.util.Event.getPageX(e),
2670 YAHOO.util.Event.getPageY(e));
2671 },
2672
2673 toString: function() {
2674 return ("DD " + this.id);
2675 }
2676
2677 //////////////////////////////////////////////////////////////////////////
2678 // Debugging ygDragDrop events that can be overridden
2679 //////////////////////////////////////////////////////////////////////////
2680 /*
2681 startDrag: function(x, y) {
2682 },
2683
2684 onDrag: function(e) {
2685 },
2686
2687 onDragEnter: function(e, id) {
2688 },
2689
2690 onDragOver: function(e, id) {
2691 },
2692
2693 onDragOut: function(e, id) {
2694 },
2695
2696 onDragDrop: function(e, id) {
2697 },
2698
2699 endDrag: function(e) {
2700 }
2701
2702 */
2703
2704});
2705/**
2706 * A DragDrop implementation that inserts an empty, bordered div into
2707 * the document that follows the cursor during drag operations. At the time of
2708 * the click, the frame div is resized to the dimensions of the linked html
2709 * element, and moved to the exact location of the linked element.
2710 *
2711 * References to the "frame" element refer to the single proxy element that
2712 * was created to be dragged in place of all DDProxy elements on the
2713 * page.
2714 *
2715 * @class DDProxy
2716 * @extends YAHOO.util.DD
2717 * @constructor
2718 * @param {String} id the id of the linked html element
2719 * @param {String} sGroup the group of related DragDrop objects
2720 * @param {object} config an object containing configurable attributes
2721 * Valid properties for DDProxy in addition to those in DragDrop:
2722 * resizeFrame, centerFrame, dragElId
2723 */
2724YAHOO.util.DDProxy = function(id, sGroup, config) {
2725 if (id) {
2726 this.init(id, sGroup, config);
2727 this.initFrame();
2728 }
2729};
2730
2731/**
2732 * The default drag frame div id
2733 * @property YAHOO.util.DDProxy.dragElId
2734 * @type String
2735 * @static
2736 */
2737YAHOO.util.DDProxy.dragElId = "ygddfdiv";
2738
2739YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
2740
2741 /**
2742 * By default we resize the drag frame to be the same size as the element
2743 * we want to drag (this is to get the frame effect). We can turn it off
2744 * if we want a different behavior.
2745 * @property resizeFrame
2746 * @type boolean
2747 */
2748 resizeFrame: true,
2749
2750 /**
2751 * By default the frame is positioned exactly where the drag element is, so
2752 * we use the cursor offset provided by YAHOO.util.DD. Another option that works only if
2753 * you do not have constraints on the obj is to have the drag frame centered
2754 * around the cursor. Set centerFrame to true for this effect.
2755 * @property centerFrame
2756 * @type boolean
2757 */
2758 centerFrame: false,
2759
2760 /**
2761 * Creates the proxy element if it does not yet exist
2762 * @method createFrame
2763 */
2764 createFrame: function() {
2765 var self = this;
2766 var body = document.body;
2767
2768 if (!body || !body.firstChild) {
2769 setTimeout( function() { self.createFrame(); }, 50 );
2770 return;
2771 }
2772
2773 var div = this.getDragEl();
2774
2775 if (!div) {
2776 div = document.createElement("div");
2777 div.id = this.dragElId;
2778 var s = div.style;
2779
2780 s.position = "absolute";
2781 s.visibility = "hidden";
2782 s.cursor = "move";
2783 s.border = "2px solid #aaa";
2784 s.zIndex = 999;
2785
2786 // appendChild can blow up IE if invoked prior to the window load event
2787 // while rendering a table. It is possible there are other scenarios
2788 // that would cause this to happen as well.
2789 body.insertBefore(div, body.firstChild);
2790 }
2791 },
2792
2793 /**
2794 * Initialization for the drag frame element. Must be called in the
2795 * constructor of all subclasses
2796 * @method initFrame
2797 */
2798 initFrame: function() {
2799 this.createFrame();
2800 },
2801
2802 applyConfig: function() {
2803 YAHOO.util.DDProxy.superclass.applyConfig.call(this);
2804
2805 this.resizeFrame = (this.config.resizeFrame !== false);
2806 this.centerFrame = (this.config.centerFrame);
2807 this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
2808 },
2809
2810 /**
2811 * Resizes the drag frame to the dimensions of the clicked object, positions
2812 * it over the object, and finally displays it
2813 * @method showFrame
2814 * @param {int} iPageX X click position
2815 * @param {int} iPageY Y click position
2816 * @private
2817 */
2818 showFrame: function(iPageX, iPageY) {
2819 var el = this.getEl();
2820 var dragEl = this.getDragEl();
2821 var s = dragEl.style;
2822
2823 this._resizeProxy();
2824
2825 if (this.centerFrame) {
2826 this.setDelta( Math.round(parseInt(s.width, 10)/2),
2827 Math.round(parseInt(s.height, 10)/2) );
2828 }
2829
2830 this.setDragElPos(iPageX, iPageY);
2831
2832 YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible");
2833 },
2834
2835 /**
2836 * The proxy is automatically resized to the dimensions of the linked
2837 * element when a drag is initiated, unless resizeFrame is set to false
2838 * @method _resizeProxy
2839 * @private
2840 */
2841 _resizeProxy: function() {
2842 if (this.resizeFrame) {
2843 var DOM = YAHOO.util.Dom;
2844 var el = this.getEl();
2845 var dragEl = this.getDragEl();
2846
2847 var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth" ), 10);
2848 var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth" ), 10);
2849 var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
2850 var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth" ), 10);
2851
2852 if (isNaN(bt)) { bt = 0; }
2853 if (isNaN(br)) { br = 0; }
2854 if (isNaN(bb)) { bb = 0; }
2855 if (isNaN(bl)) { bl = 0; }
2856
2857
2858 var newWidth = Math.max(0, el.offsetWidth - br - bl);
2859 var newHeight = Math.max(0, el.offsetHeight - bt - bb);
2860
2861
2862 DOM.setStyle( dragEl, "width", newWidth + "px" );
2863 DOM.setStyle( dragEl, "height", newHeight + "px" );
2864 }
2865 },
2866
2867 // overrides YAHOO.util.DragDrop
2868 b4MouseDown: function(e) {
2869 var x = YAHOO.util.Event.getPageX(e);
2870 var y = YAHOO.util.Event.getPageY(e);
2871 this.autoOffset(x, y);
2872 this.setDragElPos(x, y);
2873 },
2874
2875 // overrides YAHOO.util.DragDrop
2876 b4StartDrag: function(x, y) {
2877 // show the drag frame
2878 this.showFrame(x, y);
2879 },
2880
2881 // overrides YAHOO.util.DragDrop
2882 b4EndDrag: function(e) {
2883 YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden");
2884 },
2885
2886 // overrides YAHOO.util.DragDrop
2887 // By default we try to move the element to the last location of the frame.
2888 // This is so that the default behavior mirrors that of YAHOO.util.DD.
2889 endDrag: function(e) {
2890 var DOM = YAHOO.util.Dom;
2891 var lel = this.getEl();
2892 var del = this.getDragEl();
2893
2894 // Show the drag frame briefly so we can get its position
2895 // del.style.visibility = "";
2896 DOM.setStyle(del, "visibility", "");
2897
2898 // Hide the linked element before the move to get around a Safari
2899 // rendering bug.
2900 //lel.style.visibility = "hidden";
2901 DOM.setStyle(lel, "visibility", "hidden");
2902 YAHOO.util.DDM.moveToEl(lel, del);
2903 //del.style.visibility = "hidden";
2904 DOM.setStyle(del, "visibility", "hidden");
2905 //lel.style.visibility = "";
2906 DOM.setStyle(lel, "visibility", "");
2907 },
2908
2909 toString: function() {
2910 return ("DDProxy " + this.id);
2911 }
2912
2913});
2914/**
2915 * A DragDrop implementation that does not move, but can be a drop
2916 * target. You would get the same result by simply omitting implementation
2917 * for the event callbacks, but this way we reduce the processing cost of the
2918 * event listener and the callbacks.
2919 * @class DDTarget
2920 * @extends YAHOO.util.DragDrop
2921 * @constructor
2922 * @param {String} id the id of the element that is a drop target
2923 * @param {String} sGroup the group of related DragDrop objects
2924 * @param {object} config an object containing configurable attributes
2925 * Valid properties for DDTarget in addition to those in
2926 * DragDrop:
2927 * none
2928 */
2929YAHOO.util.DDTarget = function(id, sGroup, config) {
2930 if (id) {
2931 this.initTarget(id, sGroup, config);
2932 }
2933};
2934
2935// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
2936YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
2937 toString: function() {
2938 return ("DDTarget " + this.id);
2939 }
2940});
diff --git a/frontend/beta/js/YUI/event.js b/frontend/beta/js/YUI/event.js
new file mode 100644
index 0000000..7bfac3b
--- a/dev/null
+++ b/frontend/beta/js/YUI/event.js
@@ -0,0 +1,1738 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9 * The CustomEvent class lets you define events for your application
10 * that can be subscribed to by one or more independent component.
11 *
12 * @param {String} type The type of event, which is passed to the callback
13 * when the event fires
14 * @param {Object} oScope The context the event will fire from. "this" will
15 * refer to this object in the callback. Default value:
16 * the window object. The listener can override this.
17 * @param {boolean} silent pass true to prevent the event from writing to
18 * the log system
19 * @namespace YAHOO.util
20 * @class CustomEvent
21 * @constructor
22 */
23YAHOO.util.CustomEvent = function(type, oScope, silent, signature) {
24
25 /**
26 * The type of event, returned to subscribers when the event fires
27 * @property type
28 * @type string
29 */
30 this.type = type;
31
32 /**
33 * The scope the the event will fire from by default. Defaults to the window
34 * obj
35 * @property scope
36 * @type object
37 */
38 this.scope = oScope || window;
39
40 /**
41 * By default all custom events are logged in the debug build, set silent
42 * to true to disable logging for this event.
43 * @property silent
44 * @type boolean
45 */
46 this.silent = silent;
47
48 /**
49 * Custom events support two styles of arguments provided to the event
50 * subscribers.
51 * <ul>
52 * <li>YAHOO.util.CustomEvent.LIST:
53 * <ul>
54 * <li>param1: event name</li>
55 * <li>param2: array of arguments sent to fire</li>
56 * <li>param3: <optional> a custom object supplied by the subscriber</li>
57 * </ul>
58 * </li>
59 * <li>YAHOO.util.CustomEvent.FLAT
60 * <ul>
61 * <li>param1: the first argument passed to fire. If you need to
62 * pass multiple parameters, use and array or object literal</li>
63 * <li>param2: <optional> a custom object supplied by the subscriber</li>
64 * </ul>
65 * </li>
66 * </ul>
67 * @property signature
68 * @type int
69 */
70 this.signature = signature || YAHOO.util.CustomEvent.LIST;
71
72 /**
73 * The subscribers to this event
74 * @property subscribers
75 * @type Subscriber[]
76 */
77 this.subscribers = [];
78
79 if (!this.silent) {
80 }
81
82 var onsubscribeType = "_YUICEOnSubscribe";
83
84 // Only add subscribe events for events that are not generated by
85 // CustomEvent
86 if (type !== onsubscribeType) {
87
88 /**
89 * Custom events provide a custom event that fires whenever there is
90 * a new subscriber to the event. This provides an opportunity to
91 * handle the case where there is a non-repeating event that has
92 * already fired has a new subscriber.
93 *
94 * @event subscribeEvent
95 * @type YAHOO.util.CustomEvent
96 * @param {Function} fn The function to execute
97 * @param {Object} obj An object to be passed along when the event
98 * fires
99 * @param {boolean|Object} override If true, the obj passed in becomes
100 * the execution scope of the listener.
101 * if an object, that object becomes the
102 * the execution scope.
103 */
104 this.subscribeEvent =
105 new YAHOO.util.CustomEvent(onsubscribeType, this, true);
106
107 }
108};
109
110/**
111 * Subscriber listener sigature constant. The LIST type returns three
112 * parameters: the event type, the array of args passed to fire, and
113 * the optional custom object
114 * @property YAHOO.util.CustomEvent.LIST
115 * @static
116 * @type int
117 */
118YAHOO.util.CustomEvent.LIST = 0;
119
120/**
121 * Subscriber listener sigature constant. The FLAT type returns two
122 * parameters: the first argument passed to fire and the optional
123 * custom object
124 * @property YAHOO.util.CustomEvent.FLAT
125 * @static
126 * @type int
127 */
128YAHOO.util.CustomEvent.FLAT = 1;
129
130YAHOO.util.CustomEvent.prototype = {
131
132 /**
133 * Subscribes the caller to this event
134 * @method subscribe
135 * @param {Function} fn The function to execute
136 * @param {Object} obj An object to be passed along when the event
137 * fires
138 * @param {boolean|Object} override If true, the obj passed in becomes
139 * the execution scope of the listener.
140 * if an object, that object becomes the
141 * the execution scope.
142 */
143 subscribe: function(fn, obj, override) {
144 if (this.subscribeEvent) {
145 this.subscribeEvent.fire(fn, obj, override);
146 }
147
148 this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, override) );
149 },
150
151 /**
152 * Unsubscribes the caller from this event
153 * @method unsubscribe
154 * @param {Function} fn The function to execute
155 * @param {Object} obj The custom object passed to subscribe (optional)
156 * @return {boolean} True if the subscriber was found and detached.
157 */
158 unsubscribe: function(fn, obj) {
159 var found = false;
160 for (var i=0, len=this.subscribers.length; i<len; ++i) {
161 var s = this.subscribers[i];
162 if (s && s.contains(fn, obj)) {
163 this._delete(i);
164 found = true;
165 }
166 }
167
168 return found;
169 },
170
171 /**
172 * Notifies the subscribers. The callback functions will be executed
173 * from the scope specified when the event was created, and with the
174 * following parameters:
175 * <ul>
176 * <li>The type of event</li>
177 * <li>All of the arguments fire() was executed with as an array</li>
178 * <li>The custom object (if any) that was passed into the subscribe()
179 * method</li>
180 * </ul>
181 * @method fire
182 * @param {Object*} arguments an arbitrary set of parameters to pass to
183 * the handler.
184 */
185 fire: function() {
186 var len=this.subscribers.length;
187 if (!len && this.silent) {
188 return true;
189 }
190
191 var args=[], ret=true, i;
192
193 for (i=0; i<arguments.length; ++i) {
194 args.push(arguments[i]);
195 }
196
197 var argslength = args.length;
198
199 if (!this.silent) {
200 }
201
202 for (i=0; i<len; ++i) {
203 var s = this.subscribers[i];
204 if (s) {
205 if (!this.silent) {
206 }
207
208 var scope = s.getScope(this.scope);
209
210 if (this.signature == YAHOO.util.CustomEvent.FLAT) {
211 var param = null;
212 if (args.length > 0) {
213 param = args[0];
214 }
215 ret = s.fn.call(scope, param, s.obj);
216 } else {
217 ret = s.fn.call(scope, this.type, args, s.obj);
218 }
219 if (false === ret) {
220 if (!this.silent) {
221 }
222
223 //break;
224 return false;
225 }
226 }
227 }
228
229 return true;
230 },
231
232 /**
233 * Removes all listeners
234 * @method unsubscribeAll
235 */
236 unsubscribeAll: function() {
237 for (var i=0, len=this.subscribers.length; i<len; ++i) {
238 this._delete(len - 1 - i);
239 }
240 },
241
242 /**
243 * @method _delete
244 * @private
245 */
246 _delete: function(index) {
247 var s = this.subscribers[index];
248 if (s) {
249 delete s.fn;
250 delete s.obj;
251 }
252
253 // delete this.subscribers[index];
254 this.subscribers.splice(index, 1);
255 },
256
257 /**
258 * @method toString
259 */
260 toString: function() {
261 return "CustomEvent: " + "'" + this.type + "', " +
262 "scope: " + this.scope;
263
264 }
265};
266
267/////////////////////////////////////////////////////////////////////
268
269/**
270 * Stores the subscriber information to be used when the event fires.
271 * @param {Function} fn The function to execute
272 * @param {Object} obj An object to be passed along when the event fires
273 * @param {boolean} override If true, the obj passed in becomes the execution
274 * scope of the listener
275 * @class Subscriber
276 * @constructor
277 */
278YAHOO.util.Subscriber = function(fn, obj, override) {
279
280 /**
281 * The callback that will be execute when the event fires
282 * @property fn
283 * @type function
284 */
285 this.fn = fn;
286
287 /**
288 * An optional custom object that will passed to the callback when
289 * the event fires
290 * @property obj
291 * @type object
292 */
293 this.obj = obj || null;
294
295 /**
296 * The default execution scope for the event listener is defined when the
297 * event is created (usually the object which contains the event).
298 * By setting override to true, the execution scope becomes the custom
299 * object passed in by the subscriber. If override is an object, that
300 * object becomes the scope.
301 * @property override
302 * @type boolean|object
303 */
304 this.override = override;
305
306};
307
308/**
309 * Returns the execution scope for this listener. If override was set to true
310 * the custom obj will be the scope. If override is an object, that is the
311 * scope, otherwise the default scope will be used.
312 * @method getScope
313 * @param {Object} defaultScope the scope to use if this listener does not
314 * override it.
315 */
316YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) {
317 if (this.override) {
318 if (this.override === true) {
319 return this.obj;
320 } else {
321 return this.override;
322 }
323 }
324 return defaultScope;
325};
326
327/**
328 * Returns true if the fn and obj match this objects properties.
329 * Used by the unsubscribe method to match the right subscriber.
330 *
331 * @method contains
332 * @param {Function} fn the function to execute
333 * @param {Object} obj an object to be passed along when the event fires
334 * @return {boolean} true if the supplied arguments match this
335 * subscriber's signature.
336 */
337YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
338 if (obj) {
339 return (this.fn == fn && this.obj == obj);
340 } else {
341 return (this.fn == fn);
342 }
343};
344
345/**
346 * @method toString
347 */
348YAHOO.util.Subscriber.prototype.toString = function() {
349 return "Subscriber { obj: " + (this.obj || "") +
350 ", override: " + (this.override || "no") + " }";
351};
352
353/**
354 * The Event Utility provides utilities for managing DOM Events and tools
355 * for building event systems
356 *
357 * @module event
358 * @title Event Utility
359 * @namespace YAHOO.util
360 * @requires yahoo
361 */
362
363// The first instance of Event will win if it is loaded more than once.
364if (!YAHOO.util.Event) {
365
366/**
367 * The event utility provides functions to add and remove event listeners,
368 * event cleansing. It also tries to automatically remove listeners it
369 * registers during the unload event.
370 *
371 * @class Event
372 * @static
373 */
374 YAHOO.util.Event = function() {
375
376 /**
377 * True after the onload event has fired
378 * @property loadComplete
379 * @type boolean
380 * @static
381 * @private
382 */
383 var loadComplete = false;
384
385 /**
386 * Cache of wrapped listeners
387 * @property listeners
388 * @type array
389 * @static
390 * @private
391 */
392 var listeners = [];
393
394 /**
395 * User-defined unload function that will be fired before all events
396 * are detached
397 * @property unloadListeners
398 * @type array
399 * @static
400 * @private
401 */
402 var unloadListeners = [];
403
404 /**
405 * Cache of DOM0 event handlers to work around issues with DOM2 events
406 * in Safari
407 * @property legacyEvents
408 * @static
409 * @private
410 */
411 var legacyEvents = [];
412
413 /**
414 * Listener stack for DOM0 events
415 * @property legacyHandlers
416 * @static
417 * @private
418 */
419 var legacyHandlers = [];
420
421 /**
422 * The number of times to poll after window.onload. This number is
423 * increased if additional late-bound handlers are requested after
424 * the page load.
425 * @property retryCount
426 * @static
427 * @private
428 */
429 var retryCount = 0;
430
431 /**
432 * onAvailable listeners
433 * @property onAvailStack
434 * @static
435 * @private
436 */
437 var onAvailStack = [];
438
439 /**
440 * Lookup table for legacy events
441 * @property legacyMap
442 * @static
443 * @private
444 */
445 var legacyMap = [];
446
447 /**
448 * Counter for auto id generation
449 * @property counter
450 * @static
451 * @private
452 */
453 var counter = 0;
454
455 return { // PREPROCESS
456
457 /**
458 * The number of times we should look for elements that are not
459 * in the DOM at the time the event is requested after the document
460 * has been loaded. The default is 200@amp;50 ms, so it will poll
461 * for 10 seconds or until all outstanding handlers are bound
462 * (whichever comes first).
463 * @property POLL_RETRYS
464 * @type int
465 * @static
466 * @final
467 */
468 POLL_RETRYS: 200,
469
470 /**
471 * The poll interval in milliseconds
472 * @property POLL_INTERVAL
473 * @type int
474 * @static
475 * @final
476 */
477 POLL_INTERVAL: 20,
478
479 /**
480 * Element to bind, int constant
481 * @property EL
482 * @type int
483 * @static
484 * @final
485 */
486 EL: 0,
487
488 /**
489 * Type of event, int constant
490 * @property TYPE
491 * @type int
492 * @static
493 * @final
494 */
495 TYPE: 1,
496
497 /**
498 * Function to execute, int constant
499 * @property FN
500 * @type int
501 * @static
502 * @final
503 */
504 FN: 2,
505
506 /**
507 * Function wrapped for scope correction and cleanup, int constant
508 * @property WFN
509 * @type int
510 * @static
511 * @final
512 */
513 WFN: 3,
514
515 /**
516 * Object passed in by the user that will be returned as a
517 * parameter to the callback, int constant
518 * @property OBJ
519 * @type int
520 * @static
521 * @final
522 */
523 OBJ: 3,
524
525 /**
526 * Adjusted scope, either the element we are registering the event
527 * on or the custom object passed in by the listener, int constant
528 * @property ADJ_SCOPE
529 * @type int
530 * @static
531 * @final
532 */
533 ADJ_SCOPE: 4,
534
535 /**
536 * Safari detection is necessary to work around the preventDefault
537 * bug that makes it so you can't cancel a href click from the
538 * handler. There is not a capabilities check we can use here.
539 * @property isSafari
540 * @private
541 * @static
542 */
543 isSafari: (/Safari|Konqueror|KHTML/gi).test(navigator.userAgent),
544
545 /**
546 * IE detection needed to properly calculate pageX and pageY.
547 * capabilities checking didn't seem to work because another
548 * browser that does not provide the properties have the values
549 * calculated in a different manner than IE.
550 * @property isIE
551 * @private
552 * @static
553 */
554 isIE: (!this.isSafari && !navigator.userAgent.match(/opera/gi) &&
555 navigator.userAgent.match(/msie/gi)),
556
557 /**
558 * poll handle
559 * @property _interval
560 * @private
561 */
562 _interval: null,
563
564 /**
565 * @method startInterval
566 * @static
567 * @private
568 */
569 startInterval: function() {
570 if (!this._interval) {
571 var self = this;
572 var callback = function() { self._tryPreloadAttach(); };
573 this._interval = setInterval(callback, this.POLL_INTERVAL);
574 // this.timeout = setTimeout(callback, i);
575 }
576 },
577
578 /**
579 * Executes the supplied callback when the item with the supplied
580 * id is found. This is meant to be used to execute behavior as
581 * soon as possible as the page loads. If you use this after the
582 * initial page load it will poll for a fixed time for the element.
583 * The number of times it will poll and the frequency are
584 * configurable. By default it will poll for 10 seconds.
585 *
586 * @method onAvailable
587 *
588 * @param {string} p_id the id of the element to look for.
589 * @param {function} p_fn what to execute when the element is found.
590 * @param {object} p_obj an optional object to be passed back as
591 * a parameter to p_fn.
592 * @param {boolean} p_override If set to true, p_fn will execute
593 * in the scope of p_obj
594 *
595 * @static
596 */
597 onAvailable: function(p_id, p_fn, p_obj, p_override) {
598 onAvailStack.push( { id: p_id,
599 fn: p_fn,
600 obj: p_obj,
601 override: p_override,
602 checkReady: false } );
603
604 retryCount = this.POLL_RETRYS;
605 this.startInterval();
606 },
607
608 /**
609 * Works the same way as onAvailable, but additionally checks the
610 * state of sibling elements to determine if the content of the
611 * available element is safe to modify.
612 *
613 * @method onContentReady
614 *
615 * @param {string} p_id the id of the element to look for.
616 * @param {function} p_fn what to execute when the element is ready.
617 * @param {object} p_obj an optional object to be passed back as
618 * a parameter to p_fn.
619 * @param {boolean} p_override If set to true, p_fn will execute
620 * in the scope of p_obj
621 *
622 * @static
623 */
624 onContentReady: function(p_id, p_fn, p_obj, p_override) {
625 onAvailStack.push( { id: p_id,
626 fn: p_fn,
627 obj: p_obj,
628 override: p_override,
629 checkReady: true } );
630
631 retryCount = this.POLL_RETRYS;
632 this.startInterval();
633 },
634
635 /**
636 * Appends an event handler
637 *
638 * @method addListener
639 *
640 * @param {Object} el The html element to assign the
641 * event to
642 * @param {String} sType The type of event to append
643 * @param {Function} fn The method the event invokes
644 * @param {Object} obj An arbitrary object that will be
645 * passed as a parameter to the handler
646 * @param {boolean} override If true, the obj passed in becomes
647 * the execution scope of the listener
648 * @return {boolean} True if the action was successful or defered,
649 * false if one or more of the elements
650 * could not have the event bound to it.
651 * @static
652 */
653 addListener: function(el, sType, fn, obj, override) {
654
655 if (!fn || !fn.call) {
656 return false;
657 }
658
659 // The el argument can be an array of elements or element ids.
660 if ( this._isValidCollection(el)) {
661 var ok = true;
662 for (var i=0,len=el.length; i<len; ++i) {
663 ok = this.on(el[i],
664 sType,
665 fn,
666 obj,
667 override) && ok;
668 }
669 return ok;
670
671 } else if (typeof el == "string") {
672 var oEl = this.getEl(el);
673 // If the el argument is a string, we assume it is
674 // actually the id of the element. If the page is loaded
675 // we convert el to the actual element, otherwise we
676 // defer attaching the event until onload event fires
677
678 // check to see if we need to delay hooking up the event
679 // until after the page loads.
680 if (oEl) {
681 el = oEl;
682 } else {
683 // defer adding the event until the element is available
684 this.onAvailable(el, function() {
685 YAHOO.util.Event.on(el, sType, fn, obj, override);
686 });
687
688 return true;
689 }
690 }
691
692 // Element should be an html element or an array if we get
693 // here.
694 if (!el) {
695 return false;
696 }
697
698 // we need to make sure we fire registered unload events
699 // prior to automatically unhooking them. So we hang on to
700 // these instead of attaching them to the window and fire the
701 // handles explicitly during our one unload event.
702 if ("unload" == sType && obj !== this) {
703 unloadListeners[unloadListeners.length] =
704 [el, sType, fn, obj, override];
705 return true;
706 }
707
708 // if the user chooses to override the scope, we use the custom
709 // object passed in, otherwise the executing scope will be the
710 // HTML element that the event is registered on
711 var scope = el;
712 if (override) {
713 if (override === true) {
714 scope = obj;
715 } else {
716 scope = override;
717 }
718 }
719
720 // wrap the function so we can return the obj object when
721 // the event fires;
722 var wrappedFn = function(e) {
723 return fn.call(scope, YAHOO.util.Event.getEvent(e),
724 obj);
725 };
726
727 var li = [el, sType, fn, wrappedFn, scope];
728 var index = listeners.length;
729 // cache the listener so we can try to automatically unload
730 listeners[index] = li;
731
732 if (this.useLegacyEvent(el, sType)) {
733 var legacyIndex = this.getLegacyIndex(el, sType);
734
735 // Add a new dom0 wrapper if one is not detected for this
736 // element
737 if ( legacyIndex == -1 ||
738 el != legacyEvents[legacyIndex][0] ) {
739
740 legacyIndex = legacyEvents.length;
741 legacyMap[el.id + sType] = legacyIndex;
742
743 // cache the signature for the DOM0 event, and
744 // include the existing handler for the event, if any
745 legacyEvents[legacyIndex] =
746 [el, sType, el["on" + sType]];
747 legacyHandlers[legacyIndex] = [];
748
749 el["on" + sType] =
750 function(e) {
751 YAHOO.util.Event.fireLegacyEvent(
752 YAHOO.util.Event.getEvent(e), legacyIndex);
753 };
754 }
755
756 // add a reference to the wrapped listener to our custom
757 // stack of events
758 //legacyHandlers[legacyIndex].push(index);
759 legacyHandlers[legacyIndex].push(li);
760
761 } else {
762 this._simpleAdd(el, sType, wrappedFn, false);
763 }
764
765 return true;
766
767 },
768
769 /**
770 * When using legacy events, the handler is routed to this object
771 * so we can fire our custom listener stack.
772 * @method fireLegacyEvent
773 * @static
774 * @private
775 */
776 fireLegacyEvent: function(e, legacyIndex) {
777 var ok = true;
778
779 var le = legacyHandlers[legacyIndex];
780 for (var i=0,len=le.length; i<len; ++i) {
781 var li = le[i];
782 if ( li && li[this.WFN] ) {
783 var scope = li[this.ADJ_SCOPE];
784 var ret = li[this.WFN].call(scope, e);
785 ok = (ok && ret);
786 }
787 }
788
789 return ok;
790 },
791
792 /**
793 * Returns the legacy event index that matches the supplied
794 * signature
795 * @method getLegacyIndex
796 * @static
797 * @private
798 */
799 getLegacyIndex: function(el, sType) {
800 var key = this.generateId(el) + sType;
801 if (typeof legacyMap[key] == "undefined") {
802 return -1;
803 } else {
804 return legacyMap[key];
805 }
806 },
807
808 /**
809 * Logic that determines when we should automatically use legacy
810 * events instead of DOM2 events.
811 * @method useLegacyEvent
812 * @static
813 * @private
814 */
815 useLegacyEvent: function(el, sType) {
816 if (!el.addEventListener && !el.attachEvent) {
817 return true;
818 } else if (this.isSafari) {
819 if ("click" == sType || "dblclick" == sType) {
820 return true;
821 }
822 }
823 return false;
824 },
825
826 /**
827 * Removes an event handler
828 *
829 * @method removeListener
830 *
831 * @param {Object} el the html element or the id of the element to
832 * assign the event to.
833 * @param {String} sType the type of event to remove.
834 * @param {Function} fn the method the event invokes. If fn is
835 * undefined, then all event handlers for the type of event are
836 * removed.
837 * @return {boolean} true if the unbind was successful, false
838 * otherwise.
839 * @static
840 */
841 removeListener: function(el, sType, fn) {
842 var i, len;
843
844 // The el argument can be a string
845 if (typeof el == "string") {
846 el = this.getEl(el);
847 // The el argument can be an array of elements or element ids.
848 } else if ( this._isValidCollection(el)) {
849 var ok = true;
850 for (i=0,len=el.length; i<len; ++i) {
851 ok = ( this.removeListener(el[i], sType, fn) && ok );
852 }
853 return ok;
854 }
855
856 if (!fn || !fn.call) {
857 //return false;
858 return this.purgeElement(el, false, sType);
859 }
860
861 if ("unload" == sType) {
862
863 for (i=0, len=unloadListeners.length; i<len; i++) {
864 var li = unloadListeners[i];
865 if (li &&
866 li[0] == el &&
867 li[1] == sType &&
868 li[2] == fn) {
869 unloadListeners.splice(i, 1);
870 return true;
871 }
872 }
873
874 return false;
875 }
876
877 var cacheItem = null;
878
879 // The index is a hidden parameter; needed to remove it from
880 // the method signature because it was tempting users to
881 // try and take advantage of it, which is not possible.
882 var index = arguments[3];
883
884 if ("undefined" == typeof index) {
885 index = this._getCacheIndex(el, sType, fn);
886 }
887
888 if (index >= 0) {
889 cacheItem = listeners[index];
890 }
891
892 if (!el || !cacheItem) {
893 return false;
894 }
895
896 if (this.useLegacyEvent(el, sType)) {
897 var legacyIndex = this.getLegacyIndex(el, sType);
898 var llist = legacyHandlers[legacyIndex];
899 if (llist) {
900 for (i=0, len=llist.length; i<len; ++i) {
901 li = llist[i];
902 if (li &&
903 li[this.EL] == el &&
904 li[this.TYPE] == sType &&
905 li[this.FN] == fn) {
906 llist.splice(i, 1);
907 break;
908 }
909 }
910 }
911
912 } else {
913 this._simpleRemove(el, sType, cacheItem[this.WFN], false);
914 }
915
916 // removed the wrapped handler
917 delete listeners[index][this.WFN];
918 delete listeners[index][this.FN];
919 listeners.splice(index, 1);
920
921 return true;
922
923 },
924
925 /**
926 * Returns the event's target element
927 * @method getTarget
928 * @param {Event} ev the event
929 * @param {boolean} resolveTextNode when set to true the target's
930 * parent will be returned if the target is a
931 * text node. @deprecated, the text node is
932 * now resolved automatically
933 * @return {HTMLElement} the event's target
934 * @static
935 */
936 getTarget: function(ev, resolveTextNode) {
937 var t = ev.target || ev.srcElement;
938 return this.resolveTextNode(t);
939 },
940
941 /**
942 * In some cases, some browsers will return a text node inside
943 * the actual element that was targeted. This normalizes the
944 * return value for getTarget and getRelatedTarget.
945 * @method resolveTextNode
946 * @param {HTMLElement} node node to resolve
947 * @return {HTMLElement} the normized node
948 * @static
949 */
950 resolveTextNode: function(node) {
951 // if (node && node.nodeName &&
952 // "#TEXT" == node.nodeName.toUpperCase()) {
953 if (node && 3 == node.nodeType) {
954 return node.parentNode;
955 } else {
956 return node;
957 }
958 },
959
960 /**
961 * Returns the event's pageX
962 * @method getPageX
963 * @param {Event} ev the event
964 * @return {int} the event's pageX
965 * @static
966 */
967 getPageX: function(ev) {
968 var x = ev.pageX;
969 if (!x && 0 !== x) {
970 x = ev.clientX || 0;
971
972 if ( this.isIE ) {
973 x += this._getScrollLeft();
974 }
975 }
976
977 return x;
978 },
979
980 /**
981 * Returns the event's pageY
982 * @method getPageY
983 * @param {Event} ev the event
984 * @return {int} the event's pageY
985 * @static
986 */
987 getPageY: function(ev) {
988 var y = ev.pageY;
989 if (!y && 0 !== y) {
990 y = ev.clientY || 0;
991
992 if ( this.isIE ) {
993 y += this._getScrollTop();
994 }
995 }
996
997 return y;
998 },
999
1000 /**
1001 * Returns the pageX and pageY properties as an indexed array.
1002 * @method getXY
1003 * @type int[]
1004 * @static
1005 */
1006 getXY: function(ev) {
1007 return [this.getPageX(ev), this.getPageY(ev)];
1008 },
1009
1010 /**
1011 * Returns the event's related target
1012 * @method getRelatedTarget
1013 * @param {Event} ev the event
1014 * @return {HTMLElement} the event's relatedTarget
1015 * @static
1016 */
1017 getRelatedTarget: function(ev) {
1018 var t = ev.relatedTarget;
1019 if (!t) {
1020 if (ev.type == "mouseout") {
1021 t = ev.toElement;
1022 } else if (ev.type == "mouseover") {
1023 t = ev.fromElement;
1024 }
1025 }
1026
1027 return this.resolveTextNode(t);
1028 },
1029
1030 /**
1031 * Returns the time of the event. If the time is not included, the
1032 * event is modified using the current time.
1033 * @method getTime
1034 * @param {Event} ev the event
1035 * @return {Date} the time of the event
1036 * @static
1037 */
1038 getTime: function(ev) {
1039 if (!ev.time) {
1040 var t = new Date().getTime();
1041 try {
1042 ev.time = t;
1043 } catch(e) {
1044 return t;
1045 }
1046 }
1047
1048 return ev.time;
1049 },
1050
1051 /**
1052 * Convenience method for stopPropagation + preventDefault
1053 * @method stopEvent
1054 * @param {Event} ev the event
1055 * @static
1056 */
1057 stopEvent: function(ev) {
1058 this.stopPropagation(ev);
1059 this.preventDefault(ev);
1060 },
1061
1062 /**
1063 * Stops event propagation
1064 * @method stopPropagation
1065 * @param {Event} ev the event
1066 * @static
1067 */
1068 stopPropagation: function(ev) {
1069 if (ev.stopPropagation) {
1070 ev.stopPropagation();
1071 } else {
1072 ev.cancelBubble = true;
1073 }
1074 },
1075
1076 /**
1077 * Prevents the default behavior of the event
1078 * @method preventDefault
1079 * @param {Event} ev the event
1080 * @static
1081 */
1082 preventDefault: function(ev) {
1083 if (ev.preventDefault) {
1084 ev.preventDefault();
1085 } else {
1086 ev.returnValue = false;
1087 }
1088 },
1089
1090 /**
1091 * Finds the event in the window object, the caller's arguments, or
1092 * in the arguments of another method in the callstack. This is
1093 * executed automatically for events registered through the event
1094 * manager, so the implementer should not normally need to execute
1095 * this function at all.
1096 * @method getEvent
1097 * @param {Event} e the event parameter from the handler
1098 * @return {Event} the event
1099 * @static
1100 */
1101 getEvent: function(e) {
1102 var ev = e || window.event;
1103
1104 if (!ev) {
1105 var c = this.getEvent.caller;
1106 while (c) {
1107 ev = c.arguments[0];
1108 if (ev && Event == ev.constructor) {
1109 break;
1110 }
1111 c = c.caller;
1112 }
1113 }
1114
1115 return ev;
1116 },
1117
1118 /**
1119 * Returns the charcode for an event
1120 * @method getCharCode
1121 * @param {Event} ev the event
1122 * @return {int} the event's charCode
1123 * @static
1124 */
1125 getCharCode: function(ev) {
1126 return ev.charCode || ev.keyCode || 0;
1127 },
1128
1129 /**
1130 * Locating the saved event handler data by function ref
1131 *
1132 * @method _getCacheIndex
1133 * @static
1134 * @private
1135 */
1136 _getCacheIndex: function(el, sType, fn) {
1137 for (var i=0,len=listeners.length; i<len; ++i) {
1138 var li = listeners[i];
1139 if ( li &&
1140 li[this.FN] == fn &&
1141 li[this.EL] == el &&
1142 li[this.TYPE] == sType ) {
1143 return i;
1144 }
1145 }
1146
1147 return -1;
1148 },
1149
1150 /**
1151 * Generates an unique ID for the element if it does not already
1152 * have one.
1153 * @method generateId
1154 * @param el the element to create the id for
1155 * @return {string} the resulting id of the element
1156 * @static
1157 */
1158 generateId: function(el) {
1159 var id = el.id;
1160
1161 if (!id) {
1162 id = "yuievtautoid-" + counter;
1163 ++counter;
1164 el.id = id;
1165 }
1166
1167 return id;
1168 },
1169
1170 /**
1171 * We want to be able to use getElementsByTagName as a collection
1172 * to attach a group of events to. Unfortunately, different
1173 * browsers return different types of collections. This function
1174 * tests to determine if the object is array-like. It will also
1175 * fail if the object is an array, but is empty.
1176 * @method _isValidCollection
1177 * @param o the object to test
1178 * @return {boolean} true if the object is array-like and populated
1179 * @static
1180 * @private
1181 */
1182 _isValidCollection: function(o) {
1183 // this.logger.debug(o.constructor.toString())
1184 // this.logger.debug(typeof o)
1185
1186 return ( o && // o is something
1187 o.length && // o is indexed
1188 typeof o != "string" && // o is not a string
1189 !o.tagName && // o is not an HTML element
1190 !o.alert && // o is not a window
1191 typeof o[0] != "undefined" );
1192
1193 },
1194
1195 /**
1196 * @private
1197 * @property elCache
1198 * DOM element cache
1199 * @static
1200 */
1201 elCache: {},
1202
1203 /**
1204 * We cache elements bound by id because when the unload event
1205 * fires, we can no longer use document.getElementById
1206 * @method getEl
1207 * @static
1208 * @private
1209 */
1210 getEl: function(id) {
1211 return document.getElementById(id);
1212 },
1213
1214 /**
1215 * Clears the element cache
1216 * @deprecated Elements are not cached any longer
1217 * @method clearCache
1218 * @static
1219 * @private
1220 */
1221 clearCache: function() { },
1222
1223 /**
1224 * hook up any deferred listeners
1225 * @method _load
1226 * @static
1227 * @private
1228 */
1229 _load: function(e) {
1230 loadComplete = true;
1231 var EU = YAHOO.util.Event;
1232 // Remove the listener to assist with the IE memory issue, but not
1233 // for other browsers because FF 1.0x does not like it.
1234 if (this.isIE) {
1235 EU._simpleRemove(window, "load", EU._load);
1236 }
1237 },
1238
1239 /**
1240 * Polling function that runs before the onload event fires,
1241 * attempting to attach to DOM Nodes as soon as they are
1242 * available
1243 * @method _tryPreloadAttach
1244 * @static
1245 * @private
1246 */
1247 _tryPreloadAttach: function() {
1248
1249 if (this.locked) {
1250 return false;
1251 }
1252
1253 this.locked = true;
1254
1255 // keep trying until after the page is loaded. We need to
1256 // check the page load state prior to trying to bind the
1257 // elements so that we can be certain all elements have been
1258 // tested appropriately
1259 var tryAgain = !loadComplete;
1260 if (!tryAgain) {
1261 tryAgain = (retryCount > 0);
1262 }
1263
1264 // onAvailable
1265 var notAvail = [];
1266 for (var i=0,len=onAvailStack.length; i<len ; ++i) {
1267 var item = onAvailStack[i];
1268 if (item) {
1269 var el = this.getEl(item.id);
1270
1271 if (el) {
1272 // The element is available, but not necessarily ready
1273
1274 if ( !item.checkReady ||
1275 loadComplete ||
1276 el.nextSibling ||
1277 (document && document.body) ) {
1278
1279 var scope = el;
1280 if (item.override) {
1281 if (item.override === true) {
1282 scope = item.obj;
1283 } else {
1284 scope = item.override;
1285 }
1286 }
1287 item.fn.call(scope, item.obj);
1288 delete onAvailStack[i];
1289 }
1290 } else {
1291 notAvail.push(item);
1292 }
1293 }
1294 }
1295
1296 retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
1297
1298 if (tryAgain) {
1299 this.startInterval();
1300 } else {
1301 clearInterval(this._interval);
1302 this._interval = null;
1303 }
1304
1305 this.locked = false;
1306
1307 return true;
1308
1309 },
1310
1311 /**
1312 * Removes all listeners attached to the given element via addListener.
1313 * Optionally, the node's children can also be purged.
1314 * Optionally, you can specify a specific type of event to remove.
1315 * @method purgeElement
1316 * @param {HTMLElement} el the element to purge
1317 * @param {boolean} recurse recursively purge this element's children
1318 * as well. Use with caution.
1319 * @param {string} sType optional type of listener to purge. If
1320 * left out, all listeners will be removed
1321 * @static
1322 */
1323 purgeElement: function(el, recurse, sType) {
1324 var elListeners = this.getListeners(el, sType);
1325 if (elListeners) {
1326 for (var i=0,len=elListeners.length; i<len ; ++i) {
1327 var l = elListeners[i];
1328 // can't use the index on the changing collection
1329 //this.removeListener(el, l.type, l.fn, l.index);
1330 this.removeListener(el, l.type, l.fn);
1331 }
1332 }
1333
1334 if (recurse && el && el.childNodes) {
1335 for (i=0,len=el.childNodes.length; i<len ; ++i) {
1336 this.purgeElement(el.childNodes[i], recurse, sType);
1337 }
1338 }
1339 },
1340
1341 /**
1342 * Returns all listeners attached to the given element via addListener.
1343 * Optionally, you can specify a specific type of event to return.
1344 * @method getListeners
1345 * @param el {HTMLElement} the element to inspect
1346 * @param sType {string} optional type of listener to return. If
1347 * left out, all listeners will be returned
1348 * @return {Object} the listener. Contains the following fields:
1349 * &nbsp;&nbsp;type: (string) the type of event
1350 * &nbsp;&nbsp;fn: (function) the callback supplied to addListener
1351 * &nbsp;&nbsp;obj: (object) the custom object supplied to addListener
1352 * &nbsp;&nbsp;adjust: (boolean) whether or not to adjust the default scope
1353 * &nbsp;&nbsp;index: (int) its position in the Event util listener cache
1354 * @static
1355 */
1356 getListeners: function(el, sType) {
1357 var elListeners = [];
1358 if (listeners && listeners.length > 0) {
1359 for (var i=0,len=listeners.length; i<len ; ++i) {
1360 var l = listeners[i];
1361 if ( l && l[this.EL] === el &&
1362 (!sType || sType === l[this.TYPE]) ) {
1363 elListeners.push({
1364 type: l[this.TYPE],
1365 fn: l[this.FN],
1366 obj: l[this.OBJ],
1367 adjust: l[this.ADJ_SCOPE],
1368 index: i
1369 });
1370 }
1371 }
1372 }
1373
1374 return (elListeners.length) ? elListeners : null;
1375 },
1376
1377 /**
1378 * Removes all listeners registered by pe.event. Called
1379 * automatically during the unload event.
1380 * @method _unload
1381 * @static
1382 * @private
1383 */
1384 _unload: function(e) {
1385
1386 var EU = YAHOO.util.Event, i, j, l, len, index;
1387
1388 for (i=0,len=unloadListeners.length; i<len; ++i) {
1389 l = unloadListeners[i];
1390 if (l) {
1391 var scope = window;
1392 if (l[EU.ADJ_SCOPE]) {
1393 if (l[EU.ADJ_SCOPE] === true) {
1394 scope = l[EU.OBJ];
1395 } else {
1396 scope = l[EU.ADJ_SCOPE];
1397 }
1398 }
1399 l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ] );
1400 delete unloadListeners[i];
1401 l=null;
1402 scope=null;
1403 }
1404 }
1405
1406 if (listeners && listeners.length > 0) {
1407 j = listeners.length;
1408 while (j) {
1409 index = j-1;
1410 l = listeners[index];
1411 if (l) {
1412 EU.removeListener(l[EU.EL], l[EU.TYPE],
1413 l[EU.FN], index);
1414 }
1415 j = j - 1;
1416 }
1417 l=null;
1418
1419 EU.clearCache();
1420 }
1421
1422 for (i=0,len=legacyEvents.length; i<len; ++i) {
1423 // dereference the element
1424 delete legacyEvents[i][0];
1425 // delete the array item
1426 delete legacyEvents[i];
1427 }
1428
1429 EU._simpleRemove(window, "unload", EU._unload);
1430
1431 },
1432
1433 /**
1434 * Returns scrollLeft
1435 * @method _getScrollLeft
1436 * @static
1437 * @private
1438 */
1439 _getScrollLeft: function() {
1440 return this._getScroll()[1];
1441 },
1442
1443 /**
1444 * Returns scrollTop
1445 * @method _getScrollTop
1446 * @static
1447 * @private
1448 */
1449 _getScrollTop: function() {
1450 return this._getScroll()[0];
1451 },
1452
1453 /**
1454 * Returns the scrollTop and scrollLeft. Used to calculate the
1455 * pageX and pageY in Internet Explorer
1456 * @method _getScroll
1457 * @static
1458 * @private
1459 */
1460 _getScroll: function() {
1461 var dd = document.documentElement, db = document.body;
1462 if (dd && (dd.scrollTop || dd.scrollLeft)) {
1463 return [dd.scrollTop, dd.scrollLeft];
1464 } else if (db) {
1465 return [db.scrollTop, db.scrollLeft];
1466 } else {
1467 return [0, 0];
1468 }
1469 },
1470
1471 /**
1472 * Adds a DOM event directly without the caching, cleanup, scope adj, etc
1473 *
1474 * @method _simpleAdd
1475 * @param {HTMLElement} el the element to bind the handler to
1476 * @param {string} sType the type of event handler
1477 * @param {function} fn the callback to invoke
1478 * @param {boolen} capture capture or bubble phase
1479 * @static
1480 * @private
1481 */
1482 _simpleAdd: function () {
1483 if (window.addEventListener) {
1484 return function(el, sType, fn, capture) {
1485 el.addEventListener(sType, fn, (capture));
1486 };
1487 } else if (window.attachEvent) {
1488 return function(el, sType, fn, capture) {
1489 el.attachEvent("on" + sType, fn);
1490 };
1491 } else {
1492 return function(){};
1493 }
1494 }(),
1495
1496 /**
1497 * Basic remove listener
1498 *
1499 * @method _simpleRemove
1500 * @param {HTMLElement} el the element to bind the handler to
1501 * @param {string} sType the type of event handler
1502 * @param {function} fn the callback to invoke
1503 * @param {boolen} capture capture or bubble phase
1504 * @static
1505 * @private
1506 */
1507 _simpleRemove: function() {
1508 if (window.removeEventListener) {
1509 return function (el, sType, fn, capture) {
1510 el.removeEventListener(sType, fn, (capture));
1511 };
1512 } else if (window.detachEvent) {
1513 return function (el, sType, fn) {
1514 el.detachEvent("on" + sType, fn);
1515 };
1516 } else {
1517 return function(){};
1518 }
1519 }()
1520 };
1521
1522 }();
1523
1524 (function() {
1525 var EU = YAHOO.util.Event;
1526
1527 /**
1528 * YAHOO.util.Event.on is an alias for addListener
1529 * @method on
1530 * @see addListener
1531 * @static
1532 */
1533 EU.on = EU.addListener;
1534
1535 // YAHOO.mix(EU, YAHOO.util.EventProvider.prototype);
1536 // EU.createEvent("DOMContentReady");
1537 // EU.subscribe("DOMContentReady", EU._load);
1538
1539 if (document && document.body) {
1540 EU._load();
1541 } else {
1542 // EU._simpleAdd(document, "DOMContentLoaded", EU._load);
1543 EU._simpleAdd(window, "load", EU._load);
1544 }
1545 EU._simpleAdd(window, "unload", EU._unload);
1546 EU._tryPreloadAttach();
1547 })();
1548}
1549
1550/**
1551 * EventProvider is designed to be used with YAHOO.augment to wrap
1552 * CustomEvents in an interface that allows events to be subscribed to
1553 * and fired by name. This makes it possible for implementing code to
1554 * subscribe to an event that either has not been created yet, or will
1555 * not be created at all.
1556 *
1557 * @Class EventProvider
1558 */
1559YAHOO.util.EventProvider = function() { };
1560
1561YAHOO.util.EventProvider.prototype = {
1562
1563 /**
1564 * Private storage of custom events
1565 * @property __yui_events
1566 * @type Object[]
1567 * @private
1568 */
1569 __yui_events: null,
1570
1571 /**
1572 * Private storage of custom event subscribers
1573 * @property __yui_subscribers
1574 * @type Object[]
1575 * @private
1576 */
1577 __yui_subscribers: null,
1578
1579 /**
1580 * Subscribe to a CustomEvent by event type
1581 *
1582 * @method subscribe
1583 * @param p_type {string} the type, or name of the event
1584 * @param p_fn {function} the function to exectute when the event fires
1585 * @param p_obj
1586 * @param p_obj {Object} An object to be passed along when the event
1587 * fires
1588 * @param p_override {boolean} If true, the obj passed in becomes the
1589 * execution scope of the listener
1590 */
1591 subscribe: function(p_type, p_fn, p_obj, p_override) {
1592
1593 this.__yui_events = this.__yui_events || {};
1594 var ce = this.__yui_events[p_type];
1595
1596 if (ce) {
1597 ce.subscribe(p_fn, p_obj, p_override);
1598 } else {
1599 this.__yui_subscribers = this.__yui_subscribers || {};
1600 var subs = this.__yui_subscribers;
1601 if (!subs[p_type]) {
1602 subs[p_type] = [];
1603 }
1604 subs[p_type].push(
1605 { fn: p_fn, obj: p_obj, override: p_override } );
1606 }
1607 },
1608
1609 /**
1610 * Unsubscribes the from the specified event
1611 * @method unsubscribe
1612 * @param p_type {string} The type, or name of the event
1613 * @param p_fn {Function} The function to execute
1614 * @param p_obj {Object} The custom object passed to subscribe (optional)
1615 * @return {boolean} true if the subscriber was found and detached.
1616 */
1617 unsubscribe: function(p_type, p_fn, p_obj) {
1618 this.__yui_events = this.__yui_events || {};
1619 var ce = this.__yui_events[p_type];
1620 if (ce) {
1621 return ce.unsubscribe(p_fn, p_obj);
1622 } else {
1623 return false;
1624 }
1625 },
1626
1627 /**
1628 * Creates a new custom event of the specified type. If a custom event
1629 * by that name already exists, it will not be re-created. In either
1630 * case the custom event is returned.
1631 *
1632 * @method createEvent
1633 *
1634 * @param p_type {string} the type, or name of the event
1635 * @param p_config {object} optional config params. Valid properties are:
1636 *
1637 * <ul>
1638 * <li>
1639 * scope: defines the default execution scope. If not defined
1640 * the default scope will be this instance.
1641 * </li>
1642 * <li>
1643 * silent: if true, the custom event will not generate log messages.
1644 * This is false by default.
1645 * </li>
1646 * <li>
1647 * onSubscribeCallback: specifies a callback to execute when the
1648 * event has a new subscriber. This will fire immediately for
1649 * each queued subscriber if any exist prior to the creation of
1650 * the event.
1651 * </li>
1652 * </ul>
1653 *
1654 * @return {CustomEvent} the custom event
1655 *
1656 */
1657 createEvent: function(p_type, p_config) {
1658
1659 this.__yui_events = this.__yui_events || {};
1660 var opts = p_config || {};
1661 var events = this.__yui_events;
1662
1663 if (events[p_type]) {
1664 } else {
1665
1666 var scope = opts.scope || this;
1667 var silent = opts.silent || null;
1668
1669 var ce = new YAHOO.util.CustomEvent(p_type, scope, silent,
1670 YAHOO.util.CustomEvent.FLAT);
1671 events[p_type] = ce;
1672
1673 if (opts.onSubscribeCallback) {
1674 ce.subscribeEvent.subscribe(opts.onSubscribeCallback);
1675 }
1676
1677 this.__yui_subscribers = this.__yui_subscribers || {};
1678 var qs = this.__yui_subscribers[p_type];
1679
1680 if (qs) {
1681 for (var i=0; i<qs.length; ++i) {
1682 ce.subscribe(qs[i].fn, qs[i].obj, qs[i].override);
1683 }
1684 }
1685 }
1686
1687 return events[p_type];
1688 },
1689
1690 /**
1691 * Fire a custom event by name. The callback functions will be executed
1692 * from the scope specified when the event was created, and with the
1693 * following parameters:
1694 * <ul>
1695 * <li>The first argument fire() was executed with</li>
1696 * <li>The custom object (if any) that was passed into the subscribe()
1697 * method</li>
1698 * </ul>
1699 * @method fireEvent
1700 * @param p_type {string} the type, or name of the event
1701 * @param arguments {Object*} an arbitrary set of parameters to pass to
1702 * the handler.
1703 * @return {boolean} the return value from CustomEvent.fire, or null if
1704 * the custom event does not exist.
1705 */
1706 fireEvent: function(p_type, arg1, arg2, etc) {
1707
1708 this.__yui_events = this.__yui_events || {};
1709 var ce = this.__yui_events[p_type];
1710
1711 if (ce) {
1712 var args = [];
1713 for (var i=1; i<arguments.length; ++i) {
1714 args.push(arguments[i]);
1715 }
1716 return ce.fire.apply(ce, args);
1717 } else {
1718 return null;
1719 }
1720 },
1721
1722 /**
1723 * Returns true if the custom event of the provided type has been created
1724 * with createEvent.
1725 * @method hasEvent
1726 * @param type {string} the type, or name of the event
1727 */
1728 hasEvent: function(type) {
1729 if (this.__yui_events) {
1730 if (this.__yui_events[type]) {
1731 return true;
1732 }
1733 }
1734 return false;
1735 }
1736
1737};
1738
diff --git a/frontend/beta/js/YUI/logger.js b/frontend/beta/js/YUI/logger.js
new file mode 100644
index 0000000..a2b40b2
--- a/dev/null
+++ b/frontend/beta/js/YUI/logger.js
@@ -0,0 +1,1559 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.com/yui/license.txt
5version: 0.12.0
6*/
7
8/****************************************************************************/
9/****************************************************************************/
10/****************************************************************************/
11
12/**
13 * The LogMsg class defines a single log message.
14 *
15 * @class LogMsg
16 * @constructor
17 * @param oConfigs {Object} Object literal of configuration params.
18 */
19 YAHOO.widget.LogMsg = function(oConfigs) {
20 // Parse configs
21 if (typeof oConfigs == "object") {
22 for(var param in oConfigs) {
23 this[param] = oConfigs[param];
24 }
25 }
26 };
27
28/////////////////////////////////////////////////////////////////////////////
29//
30// Public member variables
31//
32/////////////////////////////////////////////////////////////////////////////
33
34/**
35 * Log message.
36 *
37 * @property msg
38 * @type String
39 */
40YAHOO.widget.LogMsg.prototype.msg = null;
41
42/**
43 * Log timestamp.
44 *
45 * @property time
46 * @type Date
47 */
48YAHOO.widget.LogMsg.prototype.time = null;
49
50/**
51 * Log category.
52 *
53 * @property category
54 * @type String
55 */
56YAHOO.widget.LogMsg.prototype.category = null;
57
58/**
59 * Log source. The first word passed in as the source argument.
60 *
61 * @property source
62 * @type String
63 */
64YAHOO.widget.LogMsg.prototype.source = null;
65
66/**
67 * Log source detail. The remainder of the string passed in as the source argument, not
68 * including the first word (if any).
69 *
70 * @property sourceDetail
71 * @type String
72 */
73YAHOO.widget.LogMsg.prototype.sourceDetail = null;
74
75/****************************************************************************/
76/****************************************************************************/
77/****************************************************************************/
78
79/**
80 * The LogWriter class provides a mechanism to log messages through
81 * YAHOO.widget.Logger from a named source.
82 *
83 * @class LogWriter
84 * @constructor
85 * @param sSource {String} Source of LogWriter instance.
86 */
87YAHOO.widget.LogWriter = function(sSource) {
88 if(!sSource) {
89 YAHOO.log("Could not instantiate LogWriter due to invalid source.",
90 "error", "LogWriter");
91 return;
92 }
93 this._source = sSource;
94 };
95
96/////////////////////////////////////////////////////////////////////////////
97//
98// Public methods
99//
100/////////////////////////////////////////////////////////////////////////////
101
102 /**
103 * Public accessor to the unique name of the LogWriter instance.
104 *
105 * @method toString
106 * @return {String} Unique name of the LogWriter instance.
107 */
108YAHOO.widget.LogWriter.prototype.toString = function() {
109 return "LogWriter " + this._sSource;
110};
111
112/**
113 * Logs a message attached to the source of the LogWriter.
114 *
115 * @method log
116 * @param sMsg {String} The log message.
117 * @param sCategory {String} Category name.
118 */
119YAHOO.widget.LogWriter.prototype.log = function(sMsg, sCategory) {
120 YAHOO.widget.Logger.log(sMsg, sCategory, this._source);
121};
122
123/**
124 * Public accessor to get the source name.
125 *
126 * @method getSource
127 * @return {String} The LogWriter source.
128 */
129YAHOO.widget.LogWriter.prototype.getSource = function() {
130 return this._sSource;
131};
132
133/**
134 * Public accessor to set the source name.
135 *
136 * @method setSource
137 * @param sSource {String} Source of LogWriter instance.
138 */
139YAHOO.widget.LogWriter.prototype.setSource = function(sSource) {
140 if(!sSource) {
141 YAHOO.log("Could not set source due to invalid source.", "error", this.toString());
142 return;
143 }
144 else {
145 this._sSource = sSource;
146 }
147};
148
149/////////////////////////////////////////////////////////////////////////////
150//
151// Private member variables
152//
153/////////////////////////////////////////////////////////////////////////////
154
155/**
156 * Source of the LogWriter instance.
157 *
158 * @property _source
159 * @type String
160 * @private
161 */
162YAHOO.widget.LogWriter.prototype._source = null;
163
164
165
166/****************************************************************************/
167/****************************************************************************/
168/****************************************************************************/
169
170/**
171 * The LogReader class provides UI to read messages logged to YAHOO.widget.Logger.
172 *
173 * @class LogReader
174 * @constructor
175 * @param elContainer {HTMLElement} (optional) DOM element reference of an existing DIV.
176 * @param elContainer {String} (optional) String ID of an existing DIV.
177 * @param oConfigs {Object} (optional) Object literal of configuration params.
178 */
179YAHOO.widget.LogReader = function(elContainer, oConfigs) {
180 var oSelf = this;
181 this._sName = YAHOO.widget.LogReader._index;
182 YAHOO.widget.LogReader._index++;
183
184 // Parse config vars here
185 if (typeof oConfigs == "object") {
186 for(var param in oConfigs) {
187 this[param] = oConfigs[param];
188 }
189 }
190
191 // Attach container...
192 if(elContainer) {
193 if(typeof elContainer == "string") {
194 this._elContainer = document.getElementById(elContainer);
195 }
196 else if(elContainer.tagName) {
197 this._elContainer = elContainer;
198 }
199 this._elContainer.className = "yui-log";
200 }
201 // ...or create container from scratch
202 if(!this._elContainer) {
203 if(YAHOO.widget.LogReader._elDefaultContainer) {
204 this._elContainer = YAHOO.widget.LogReader._elDefaultContainer;
205 }
206 else {
207 this._elContainer = document.body.appendChild(document.createElement("div"));
208 this._elContainer.id = "yui-log";
209 this._elContainer.className = "yui-log";
210
211 YAHOO.widget.LogReader._elDefaultContainer = this._elContainer;
212 }
213
214 // If implementer has provided container values, trust and set those
215 var containerStyle = this._elContainer.style;
216 if(this.width) {
217 containerStyle.width = this.width;
218 }
219 if(this.left) {
220 containerStyle.left = this.left;
221 }
222 if(this.right) {
223 containerStyle.right = this.right;
224 }
225 if(this.bottom) {
226 containerStyle.bottom = this.bottom;
227 }
228 if(this.top) {
229 containerStyle.top = this.top;
230 }
231 if(this.fontSize) {
232 containerStyle.fontSize = this.fontSize;
233 }
234 }
235
236 if(this._elContainer) {
237 // Create header
238 if(!this._elHd) {
239 this._elHd = this._elContainer.appendChild(document.createElement("div"));
240 this._elHd.id = "yui-log-hd" + this._sName;
241 this._elHd.className = "yui-log-hd";
242
243 this._elCollapse = this._elHd.appendChild(document.createElement("div"));
244 this._elCollapse.className = "yui-log-btns";
245
246 this._btnCollapse = document.createElement("input");
247 this._btnCollapse.type = "button";
248 this._btnCollapse.style.fontSize =
249 YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");
250 this._btnCollapse.className = "yui-log-button";
251 this._btnCollapse.value = "Collapse";
252 this._btnCollapse = this._elCollapse.appendChild(this._btnCollapse);
253 YAHOO.util.Event.addListener(
254 oSelf._btnCollapse,'click',oSelf._onClickCollapseBtn,oSelf);
255
256 this._title = this._elHd.appendChild(document.createElement("h4"));
257 this._title.innerHTML = "Logger Console";
258
259 // If Drag and Drop utility is available...
260 // ...and this container was created from scratch...
261 // ...then make the header draggable
262 if(YAHOO.util.DD &&
263 (YAHOO.widget.LogReader._elDefaultContainer == this._elContainer)) {
264 var ylog_dd = new YAHOO.util.DD(this._elContainer.id);
265 ylog_dd.setHandleElId(this._elHd.id);
266 this._elHd.style.cursor = "move";
267 }
268 }
269 // Ceate console
270 if(!this._elConsole) {
271 this._elConsole =
272 this._elContainer.appendChild(document.createElement("div"));
273 this._elConsole.className = "yui-log-bd";
274
275 // If implementer has provided console, trust and set those
276 if(this.height) {
277 this._elConsole.style.height = this.height;
278 }
279 }
280 // Don't create footer if disabled
281 if(!this._elFt && this.footerEnabled) {
282 this._elFt = this._elContainer.appendChild(document.createElement("div"));
283 this._elFt.className = "yui-log-ft";
284
285 this._elBtns = this._elFt.appendChild(document.createElement("div"));
286 this._elBtns.className = "yui-log-btns";
287
288 this._btnPause = document.createElement("input");
289 this._btnPause.type = "button";
290 this._btnPause.style.fontSize =
291 YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");
292 this._btnPause.className = "yui-log-button";
293 this._btnPause.value = "Pause";
294 this._btnPause = this._elBtns.appendChild(this._btnPause);
295 YAHOO.util.Event.addListener(
296 oSelf._btnPause,'click',oSelf._onClickPauseBtn,oSelf);
297
298 this._btnClear = document.createElement("input");
299 this._btnClear.type = "button";
300 this._btnClear.style.fontSize =
301 YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");
302 this._btnClear.className = "yui-log-button";
303 this._btnClear.value = "Clear";
304 this._btnClear = this._elBtns.appendChild(this._btnClear);
305 YAHOO.util.Event.addListener(
306 oSelf._btnClear,'click',oSelf._onClickClearBtn,oSelf);
307
308 this._elCategoryFilters = this._elFt.appendChild(document.createElement("div"));
309 this._elCategoryFilters.className = "yui-log-categoryfilters";
310 this._elSourceFilters = this._elFt.appendChild(document.createElement("div"));
311 this._elSourceFilters.className = "yui-log-sourcefilters";
312 }
313 }
314
315 // Initialize internal vars
316 if(!this._buffer) {
317 this._buffer = []; // output buffer
318 }
319 // Timestamp of last log message to console
320 this._lastTime = YAHOO.widget.Logger.getStartTime();
321
322 // Subscribe to Logger custom events
323 YAHOO.widget.Logger.newLogEvent.subscribe(this._onNewLog, this);
324 YAHOO.widget.Logger.logResetEvent.subscribe(this._onReset, this);
325
326 // Initialize category filters
327 this._categoryFilters = [];
328 var catsLen = YAHOO.widget.Logger.categories.length;
329 if(this._elCategoryFilters) {
330 for(var i=0; i < catsLen; i++) {
331 this._createCategoryCheckbox(YAHOO.widget.Logger.categories[i]);
332 }
333 }
334 // Initialize source filters
335 this._sourceFilters = [];
336 var sourcesLen = YAHOO.widget.Logger.sources.length;
337 if(this._elSourceFilters) {
338 for(var j=0; j < sourcesLen; j++) {
339 this._createSourceCheckbox(YAHOO.widget.Logger.sources[j]);
340 }
341 }
342 YAHOO.widget.Logger.categoryCreateEvent.subscribe(this._onCategoryCreate, this);
343 YAHOO.widget.Logger.sourceCreateEvent.subscribe(this._onSourceCreate, this);
344
345 this._filterLogs();
346 YAHOO.log("LogReader initialized", null, this.toString());
347};
348
349/////////////////////////////////////////////////////////////////////////////
350//
351// Public member variables
352//
353/////////////////////////////////////////////////////////////////////////////
354
355/**
356 * Whether or not the log reader is enabled to output log messages.
357 *
358 * @property logReaderEnabled
359 * @type Boolean
360 * @default true
361 */
362YAHOO.widget.LogReader.prototype.logReaderEnabled = true;
363
364/**
365 * Public member to access CSS width of the log reader container.
366 *
367 * @property width
368 * @type String
369 */
370YAHOO.widget.LogReader.prototype.width = null;
371
372/**
373 * Public member to access CSS height of the log reader container.
374 *
375 * @property height
376 * @type String
377 */
378YAHOO.widget.LogReader.prototype.height = null;
379
380/**
381 * Public member to access CSS top position of the log reader container.
382 *
383 * @property top
384 * @type String
385 */
386YAHOO.widget.LogReader.prototype.top = null;
387
388/**
389 * Public member to access CSS left position of the log reader container.
390 *
391 * @property left
392 * @type String
393 */
394YAHOO.widget.LogReader.prototype.left = null;
395
396/**
397 * Public member to access CSS right position of the log reader container.
398 *
399 * @property right
400 * @type String
401 */
402YAHOO.widget.LogReader.prototype.right = null;
403
404/**
405 * Public member to access CSS bottom position of the log reader container.
406 *
407 * @property bottom
408 * @type String
409 */
410YAHOO.widget.LogReader.prototype.bottom = null;
411
412/**
413 * Public member to access CSS font size of the log reader container.
414 *
415 * @property fontSize
416 * @type String
417 */
418YAHOO.widget.LogReader.prototype.fontSize = null;
419
420/**
421 * Whether or not the footer UI is enabled for the log reader.
422 *
423 * @property footerEnabled
424 * @type Boolean
425 * @default true
426 */
427YAHOO.widget.LogReader.prototype.footerEnabled = true;
428
429/**
430 * Whether or not output is verbose (more readable). Setting to true will make
431 * output more compact (less readable).
432 *
433 * @property verboseOutput
434 * @type Boolean
435 * @default true
436 */
437YAHOO.widget.LogReader.prototype.verboseOutput = true;
438
439/**
440 * Whether or not newest message is printed on top.
441 *
442 * @property newestOnTop
443 * @type Boolean
444 */
445YAHOO.widget.LogReader.prototype.newestOnTop = true;
446
447/**
448 * Maximum number of messages a LogReader console will display.
449 *
450 * @property thresholdMax
451 * @type Number
452 * @default 500
453 */
454YAHOO.widget.LogReader.prototype.thresholdMax = 500;
455
456/**
457 * When a LogReader console reaches its thresholdMax, it will clear out messages
458 * and print out the latest thresholdMin number of messages.
459 *
460 * @property thresholdMin
461 * @type Number
462 * @default 100
463 */
464YAHOO.widget.LogReader.prototype.thresholdMin = 100;
465
466/////////////////////////////////////////////////////////////////////////////
467//
468// Public methods
469//
470/////////////////////////////////////////////////////////////////////////////
471
472 /**
473 * Public accessor to the unique name of the LogReader instance.
474 *
475 * @method toString
476 * @return {String} Unique name of the LogReader instance.
477 */
478YAHOO.widget.LogReader.prototype.toString = function() {
479 return "LogReader instance" + this._sName;
480};
481/**
482 * Pauses output of log messages. While paused, log messages are not lost, but
483 * get saved to a buffer and then output upon resume of log reader.
484 *
485 * @method pause
486 */
487YAHOO.widget.LogReader.prototype.pause = function() {
488 this._timeout = null;
489 this.logReaderEnabled = false;
490};
491
492/**
493 * Resumes output of log messages, including outputting any log messages that
494 * have been saved to buffer while paused.
495 *
496 * @method resume
497 */
498YAHOO.widget.LogReader.prototype.resume = function() {
499 this.logReaderEnabled = true;
500 this._printBuffer();
501};
502
503/**
504 * Hides UI of log reader. Logging functionality is not disrupted.
505 *
506 * @method hide
507 */
508YAHOO.widget.LogReader.prototype.hide = function() {
509 this._elContainer.style.display = "none";
510};
511
512/**
513 * Shows UI of log reader. Logging functionality is not disrupted.
514 *
515 * @method show
516 */
517YAHOO.widget.LogReader.prototype.show = function() {
518 this._elContainer.style.display = "block";
519};
520
521/**
522 * Updates title to given string.
523 *
524 * @method setTitle
525 * @param sTitle {String} New title.
526 */
527YAHOO.widget.LogReader.prototype.setTitle = function(sTitle) {
528 this._title.innerHTML = this.html2Text(sTitle);
529};
530
531/**
532 * Gets timestamp of the last log.
533 *
534 * @method getLastTime
535 * @return {Date} Timestamp of the last log.
536 */
537YAHOO.widget.LogReader.prototype.getLastTime = function() {
538 return this._lastTime;
539};
540
541/**
542 * Formats message string to HTML for output to console.
543 *
544 * @method formatMsg
545 * @param oLogMsg {Object} Log message object.
546 * @return {String} HTML-formatted message for output to console.
547 */
548YAHOO.widget.LogReader.prototype.formatMsg = function(oLogMsg) {
549 var category = oLogMsg.category;
550
551 // Label for color-coded display
552 var label = category.substring(0,4).toUpperCase();
553
554 // Calculate the elapsed time to be from the last item that passed through the filter,
555 // not the absolute previous item in the stack
556
557 var time = oLogMsg.time;
558 if (time.toLocaleTimeString) {
559 var localTime = time.toLocaleTimeString();
560 }
561 else {
562 localTime = time.toString();
563 }
564
565 var msecs = time.getTime();
566 var startTime = YAHOO.widget.Logger.getStartTime();
567 var totalTime = msecs - startTime;
568 var elapsedTime = msecs - this.getLastTime();
569
570 var source = oLogMsg.source;
571 var sourceDetail = oLogMsg.sourceDetail;
572 var sourceAndDetail = (sourceDetail) ?
573 source + " " + sourceDetail : source;
574
575 // Escape HTML entities in the log message itself for output to console
576 var msg = this.html2Text(oLogMsg.msg);
577
578 // Verbose output includes extra line breaks
579 var output = (this.verboseOutput) ?
580 ["<p><span class='", category, "'>", label, "</span> ",
581 totalTime, "ms (+", elapsedTime, ") ",
582 localTime, ": ",
583 "</p><p>",
584 sourceAndDetail,
585 ": </p><p>",
586 msg,
587 "</p>"] :
588
589 ["<p><span class='", category, "'>", label, "</span> ",
590 totalTime, "ms (+", elapsedTime, ") ",
591 localTime, ": ",
592 sourceAndDetail, ": ",
593 msg,"</p>"];
594
595 return output.join("");
596};
597
598/**
599 * Converts input chars "<", ">", and "&" to HTML entities.
600 *
601 * @method html2Text
602 * @param sHtml {String} String to convert.
603 * @private
604 */
605YAHOO.widget.LogReader.prototype.html2Text = function(sHtml) {
606 if(sHtml) {
607 sHtml += "";
608 return sHtml.replace(/&/g, "&#38;").replace(/</g, "&#60;").replace(/>/g, "&#62;");
609 }
610 return "";
611};
612
613/////////////////////////////////////////////////////////////////////////////
614//
615// Private member variables
616//
617/////////////////////////////////////////////////////////////////////////////
618
619/**
620 * Internal class member to index multiple log reader instances.
621 *
622 * @property _memberName
623 * @static
624 * @type Number
625 * @default 0
626 * @private
627 */
628YAHOO.widget.LogReader._index = 0;
629
630/**
631 * Name of LogReader instance.
632 *
633 * @property _sName
634 * @type String
635 * @private
636 */
637YAHOO.widget.LogReader.prototype._sName = null;
638
639/**
640 * A class member shared by all log readers if a container needs to be
641 * created during instantiation. Will be null if a container element never needs to
642 * be created on the fly, such as when the implementer passes in their own element.
643 *
644 * @property _elDefaultContainer
645 * @type HTMLElement
646 * @private
647 */
648YAHOO.widget.LogReader._elDefaultContainer = null;
649
650/**
651 * Buffer of log message objects for batch output.
652 *
653 * @property _buffer
654 * @type Object[]
655 * @private
656 */
657YAHOO.widget.LogReader.prototype._buffer = null;
658
659/**
660 * Number of log messages output to console.
661 *
662 * @property _consoleMsgCount
663 * @type Number
664 * @default 0
665 * @private
666 */
667YAHOO.widget.LogReader.prototype._consoleMsgCount = 0;
668
669/**
670 * Date of last output log message.
671 *
672 * @property _lastTime
673 * @type Date
674 * @private
675 */
676YAHOO.widget.LogReader.prototype._lastTime = null;
677
678/**
679 * Batched output timeout ID.
680 *
681 * @property _timeout
682 * @type Number
683 * @private
684 */
685YAHOO.widget.LogReader.prototype._timeout = null;
686
687/**
688 * Array of filters for log message categories.
689 *
690 * @property _categoryFilters
691 * @type String[]
692 * @private
693 */
694YAHOO.widget.LogReader.prototype._categoryFilters = null;
695
696/**
697 * Array of filters for log message sources.
698 *
699 * @property _sourceFilters
700 * @type String[]
701 * @private
702 */
703YAHOO.widget.LogReader.prototype._sourceFilters = null;
704
705/**
706 * Log reader container element.
707 *
708 * @property _elContainer
709 * @type HTMLElement
710 * @private
711 */
712YAHOO.widget.LogReader.prototype._elContainer = null;
713
714/**
715 * Log reader header element.
716 *
717 * @property _elHd
718 * @type HTMLElement
719 * @private
720 */
721YAHOO.widget.LogReader.prototype._elHd = null;
722
723/**
724 * Log reader collapse element.
725 *
726 * @property _elCollapse
727 * @type HTMLElement
728 * @private
729 */
730YAHOO.widget.LogReader.prototype._elCollapse = null;
731
732/**
733 * Log reader collapse button element.
734 *
735 * @property _btnCollapse
736 * @type HTMLElement
737 * @private
738 */
739YAHOO.widget.LogReader.prototype._btnCollapse = null;
740
741/**
742 * Log reader title header element.
743 *
744 * @property _title
745 * @type HTMLElement
746 * @private
747 */
748YAHOO.widget.LogReader.prototype._title = null;
749
750/**
751 * Log reader console element.
752 *
753 * @property _elConsole
754 * @type HTMLElement
755 * @private
756 */
757YAHOO.widget.LogReader.prototype._elConsole = null;
758
759/**
760 * Log reader footer element.
761 *
762 * @property _elFt
763 * @type HTMLElement
764 * @private
765 */
766YAHOO.widget.LogReader.prototype._elFt = null;
767
768/**
769 * Log reader buttons container element.
770 *
771 * @property _elBtns
772 * @type HTMLElement
773 * @private
774 */
775YAHOO.widget.LogReader.prototype._elBtns = null;
776
777/**
778 * Container element for log reader category filter checkboxes.
779 *
780 * @property _elCategoryFilters
781 * @type HTMLElement
782 * @private
783 */
784YAHOO.widget.LogReader.prototype._elCategoryFilters = null;
785
786/**
787 * Container element for log reader source filter checkboxes.
788 *
789 * @property _elSourceFilters
790 * @type HTMLElement
791 * @private
792 */
793YAHOO.widget.LogReader.prototype._elSourceFilters = null;
794
795/**
796 * Log reader pause button element.
797 *
798 * @property _btnPause
799 * @type HTMLElement
800 * @private
801 */
802YAHOO.widget.LogReader.prototype._btnPause = null;
803
804/**
805 * Clear button element.
806 *
807 * @property _btnClear
808 * @type HTMLElement
809 * @private
810 */
811YAHOO.widget.LogReader.prototype._btnClear = null;
812
813/////////////////////////////////////////////////////////////////////////////
814//
815// Private methods
816//
817/////////////////////////////////////////////////////////////////////////////
818
819/**
820 * Creates the UI for a category filter in the log reader footer element.
821 *
822 * @method _createCategoryCheckbox
823 * @param sCategory {String} Category name.
824 * @private
825 */
826YAHOO.widget.LogReader.prototype._createCategoryCheckbox = function(sCategory) {
827 var oSelf = this;
828
829 if(this._elFt) {
830 var elParent = this._elCategoryFilters;
831 var filters = this._categoryFilters;
832
833 var elFilter = elParent.appendChild(document.createElement("span"));
834 elFilter.className = "yui-log-filtergrp";
835 // Append el at the end so IE 5.5 can set "type" attribute
836 // and THEN set checked property
837 var chkCategory = document.createElement("input");
838 chkCategory.id = "yui-log-filter-" + sCategory + this._sName;
839 chkCategory.className = "yui-log-filter-" + sCategory;
840 chkCategory.type = "checkbox";
841 chkCategory.category = sCategory;
842 chkCategory = elFilter.appendChild(chkCategory);
843 chkCategory.checked = true;
844
845 // Add this checked filter to the internal array of filters
846 filters.push(sCategory);
847 // Subscribe to the click event
848 YAHOO.util.Event.addListener(chkCategory,'click',oSelf._onCheckCategory,oSelf);
849
850 // Create and class the text label
851 var lblCategory = elFilter.appendChild(document.createElement("label"));
852 lblCategory.htmlFor = chkCategory.id;
853 lblCategory.className = sCategory;
854 lblCategory.innerHTML = sCategory;
855 }
856};
857
858/**
859 * Creates a checkbox in the log reader footer element to filter by source.
860 *
861 * @method _createSourceCheckbox
862 * @param sSource {String} Source name.
863 * @private
864 */
865YAHOO.widget.LogReader.prototype._createSourceCheckbox = function(sSource) {
866 var oSelf = this;
867
868 if(this._elFt) {
869 var elParent = this._elSourceFilters;
870 var filters = this._sourceFilters;
871
872 var elFilter = elParent.appendChild(document.createElement("span"));
873 elFilter.className = "yui-log-filtergrp";
874
875 // Append el at the end so IE 5.5 can set "type" attribute
876 // and THEN set checked property
877 var chkSource = document.createElement("input");
878 chkSource.id = "yui-log-filter" + sSource + this._sName;
879 chkSource.className = "yui-log-filter" + sSource;
880 chkSource.type = "checkbox";
881 chkSource.source = sSource;
882 chkSource = elFilter.appendChild(chkSource);
883 chkSource.checked = true;
884
885 // Add this checked filter to the internal array of filters
886 filters.push(sSource);
887 // Subscribe to the click event
888 YAHOO.util.Event.addListener(chkSource,'click',oSelf._onCheckSource,oSelf);
889
890 // Create and class the text label
891 var lblSource = elFilter.appendChild(document.createElement("label"));
892 lblSource.htmlFor = chkSource.id;
893 lblSource.className = sSource;
894 lblSource.innerHTML = sSource;
895 }
896};
897
898/**
899 * Reprints all log messages in the stack through filters.
900 *
901 * @method _filterLogs
902 * @private
903 */
904YAHOO.widget.LogReader.prototype._filterLogs = function() {
905 // Reprint stack with new filters
906 if (this._elConsole !== null) {
907 this._clearConsole();
908 this._printToConsole(YAHOO.widget.Logger.getStack());
909 }
910};
911
912/**
913 * Clears all outputted log messages from the console and resets the time of the
914 * last output log message.
915 *
916 * @method _clearConsole
917 * @private
918 */
919YAHOO.widget.LogReader.prototype._clearConsole = function() {
920 // Clear the buffer of any pending messages
921 this._timeout = null;
922 this._buffer = [];
923 this._consoleMsgCount = 0;
924
925 // Reset the rolling timer
926 this._lastTime = YAHOO.widget.Logger.getStartTime();
927
928 var elConsole = this._elConsole;
929 while(elConsole.hasChildNodes()) {
930 elConsole.removeChild(elConsole.firstChild);
931 }
932};
933
934/**
935 * Sends buffer of log messages to output and clears buffer.
936 *
937 * @method _printBuffer
938 * @private
939 */
940YAHOO.widget.LogReader.prototype._printBuffer = function() {
941 this._timeout = null;
942
943 if(this._elConsole !== null) {
944 var thresholdMax = this.thresholdMax;
945 thresholdMax = (thresholdMax && !isNaN(thresholdMax)) ? thresholdMax : 500;
946 if(this._consoleMsgCount < thresholdMax) {
947 var entries = [];
948 for (var i=0; i<this._buffer.length; i++) {
949 entries[i] = this._buffer[i];
950 }
951 this._buffer = [];
952 this._printToConsole(entries);
953 }
954 else {
955 this._filterLogs();
956 }
957
958 if(!this.newestOnTop) {
959 this._elConsole.scrollTop = this._elConsole.scrollHeight;
960 }
961 }
962};
963
964/**
965 * Cycles through an array of log messages, and outputs each one to the console
966 * if its category has not been filtered out.
967 *
968 * @method _printToConsole
969 * @param aEntries {Object[]} Array of LogMsg objects to output to console.
970 * @private
971 */
972YAHOO.widget.LogReader.prototype._printToConsole = function(aEntries) {
973 // Manage the number of messages displayed in the console
974 var entriesLen = aEntries.length;
975 var thresholdMin = this.thresholdMin;
976 if(isNaN(thresholdMin) || (thresholdMin > this.thresholdMax)) {
977 thresholdMin = 0;
978 }
979 var entriesStartIndex = (entriesLen > thresholdMin) ? (entriesLen - thresholdMin) : 0;
980
981 // Iterate through all log entries
982 var sourceFiltersLen = this._sourceFilters.length;
983 var categoryFiltersLen = this._categoryFilters.length;
984 for(var i=entriesStartIndex; i<entriesLen; i++) {
985 // Print only the ones that filter through
986 var okToPrint = false;
987 var okToFilterCats = false;
988
989 // Get log message details
990 var entry = aEntries[i];
991 var source = entry.source;
992 var category = entry.category;
993
994 for(var j=0; j<sourceFiltersLen; j++) {
995 if(source == this._sourceFilters[j]) {
996 okToFilterCats = true;
997 break;
998 }
999 }
1000 if(okToFilterCats) {
1001 for(var k=0; k<categoryFiltersLen; k++) {
1002 if(category == this._categoryFilters[k]) {
1003 okToPrint = true;
1004 break;
1005 }
1006 }
1007 }
1008 if(okToPrint) {
1009 var output = this.formatMsg(entry);
1010
1011 // Verbose output uses <code> tag instead of <pre> tag (for wrapping)
1012 var container = (this.verboseOutput) ? "CODE" : "PRE";
1013 var oNewElement = (this.newestOnTop) ?
1014 this._elConsole.insertBefore(
1015 document.createElement(container),this._elConsole.firstChild):
1016 this._elConsole.appendChild(document.createElement(container));
1017
1018 oNewElement.innerHTML = output;
1019 this._consoleMsgCount++;
1020 this._lastTime = entry.time.getTime();
1021 }
1022 }
1023};
1024
1025/////////////////////////////////////////////////////////////////////////////
1026//
1027// Private event handlers
1028//
1029/////////////////////////////////////////////////////////////////////////////
1030
1031/**
1032 * Handles Logger's categoryCreateEvent.
1033 *
1034 * @method _onCategoryCreate
1035 * @param sType {String} The event.
1036 * @param aArgs {Object[]} Data passed from event firer.
1037 * @param oSelf {Object} The LogReader instance.
1038 * @private
1039 */
1040YAHOO.widget.LogReader.prototype._onCategoryCreate = function(sType, aArgs, oSelf) {
1041 var category = aArgs[0];
1042 if(oSelf._elFt) {
1043 oSelf._createCategoryCheckbox(category);
1044 }
1045};
1046
1047/**
1048 * Handles Logger's sourceCreateEvent.
1049 *
1050 * @method _onSourceCreate
1051 * @param sType {String} The event.
1052 * @param aArgs {Object[]} Data passed from event firer.
1053 * @param oSelf {Object} The LogReader instance.
1054 * @private
1055 */
1056YAHOO.widget.LogReader.prototype._onSourceCreate = function(sType, aArgs, oSelf) {
1057 var source = aArgs[0];
1058 if(oSelf._elFt) {
1059 oSelf._createSourceCheckbox(source);
1060 }
1061};
1062
1063/**
1064 * Handles check events on the category filter checkboxes.
1065 *
1066 * @method _onCheckCategory
1067 * @param v {HTMLEvent} The click event.
1068 * @param oSelf {Object} The LogReader instance.
1069 * @private
1070 */
1071YAHOO.widget.LogReader.prototype._onCheckCategory = function(v, oSelf) {
1072 var newFilter = this.category;
1073 var filtersArray = oSelf._categoryFilters;
1074
1075 if(!this.checked) { // Remove category from filters
1076 for(var i=0; i<filtersArray.length; i++) {
1077 if(newFilter == filtersArray[i]) {
1078 filtersArray.splice(i, 1);
1079 break;
1080 }
1081 }
1082 }
1083 else { // Add category to filters
1084 filtersArray.push(newFilter);
1085 }
1086 oSelf._filterLogs();
1087};
1088
1089/**
1090 * Handles check events on the category filter checkboxes.
1091 *
1092 * @method _onCheckSource
1093 * @param v {HTMLEvent} The click event.
1094 * @param oSelf {Object} The log reader instance.
1095 * @private
1096 */
1097YAHOO.widget.LogReader.prototype._onCheckSource = function(v, oSelf) {
1098 var newFilter = this.source;
1099 var filtersArray = oSelf._sourceFilters;
1100
1101 if(!this.checked) { // Remove category from filters
1102 for(var i=0; i<filtersArray.length; i++) {
1103 if(newFilter == filtersArray[i]) {
1104 filtersArray.splice(i, 1);
1105 break;
1106 }
1107 }
1108 }
1109 else { // Add category to filters
1110 filtersArray.push(newFilter);
1111 }
1112 oSelf._filterLogs();
1113};
1114
1115/**
1116 * Handles click events on the collapse button.
1117 *
1118 * @method _onClickCollapseBtn
1119 * @param v {HTMLEvent} The click event.
1120 * @param oSelf {Object} The LogReader instance
1121 * @private
1122 */
1123YAHOO.widget.LogReader.prototype._onClickCollapseBtn = function(v, oSelf) {
1124 var btn = oSelf._btnCollapse;
1125 if(btn.value == "Expand") {
1126 oSelf._elConsole.style.display = "block";
1127 if(oSelf._elFt) {
1128 oSelf._elFt.style.display = "block";
1129 }
1130 btn.value = "Collapse";
1131 }
1132 else {
1133 oSelf._elConsole.style.display = "none";
1134 if(oSelf._elFt) {
1135 oSelf._elFt.style.display = "none";
1136 }
1137 btn.value = "Expand";
1138 }
1139};
1140
1141/**
1142 * Handles click events on the pause button.
1143 *
1144 * @method _onClickPauseBtn
1145 * @param v {HTMLEvent} The click event.
1146 * @param oSelf {Object} The LogReader instance.
1147 * @private
1148 */
1149YAHOO.widget.LogReader.prototype._onClickPauseBtn = function(v, oSelf) {
1150 var btn = oSelf._btnPause;
1151 if(btn.value == "Resume") {
1152 oSelf.resume();
1153 btn.value = "Pause";
1154 }
1155 else {
1156 oSelf.pause();
1157 btn.value = "Resume";
1158 }
1159};
1160
1161/**
1162 * Handles click events on the clear button.
1163 *
1164 * @method _onClickClearBtn
1165 * @param v {HTMLEvent} The click event.
1166 * @param oSelf {Object} The LogReader instance.
1167 * @private
1168 */
1169YAHOO.widget.LogReader.prototype._onClickClearBtn = function(v, oSelf) {
1170 oSelf._clearConsole();
1171};
1172
1173/**
1174 * Handles Logger's newLogEvent.
1175 *
1176 * @method _onNewLog
1177 * @param sType {String} The event.
1178 * @param aArgs {Object[]} Data passed from event firer.
1179 * @param oSelf {Object} The LogReader instance.
1180 * @private
1181 */
1182YAHOO.widget.LogReader.prototype._onNewLog = function(sType, aArgs, oSelf) {
1183 var logEntry = aArgs[0];
1184 oSelf._buffer.push(logEntry);
1185
1186 if (oSelf.logReaderEnabled === true && oSelf._timeout === null) {
1187 oSelf._timeout = setTimeout(function(){oSelf._printBuffer();}, 100);
1188 }
1189};
1190
1191/**
1192 * Handles Logger's resetEvent.
1193 *
1194 * @method _onReset
1195 * @param sType {String} The event.
1196 * @param aArgs {Object[]} Data passed from event firer.
1197 * @param oSelf {Object} The LogReader instance.
1198 * @private
1199 */
1200YAHOO.widget.LogReader.prototype._onReset = function(sType, aArgs, oSelf) {
1201 oSelf._filterLogs();
1202};
1203 /**
1204 * The Logger widget provides a simple way to read or write log messages in
1205 * JavaScript code. Integration with the YUI Library's debug builds allow
1206 * implementers to access under-the-hood events, errors, and debugging messages.
1207 * Output may be read through a LogReader console and/or output to a browser
1208 * console.
1209 *
1210 * @module logger
1211 * @requires yahoo, event, dom
1212 * @optional dragdrop
1213 * @namespace YAHOO.widget
1214 * @title Logger Widget
1215 */
1216
1217/****************************************************************************/
1218/****************************************************************************/
1219/****************************************************************************/
1220
1221/**
1222 * The singleton Logger class provides core log management functionality. Saves
1223 * logs written through the global YAHOO.log function or written by a LogWriter
1224 * instance. Provides access to logs for reading by a LogReader instance or
1225 * native browser console such as the Firebug extension to Firefox or Safari's
1226 * JavaScript console through integration with the console.log() method.
1227 *
1228 * @class Logger
1229 * @static
1230 */
1231YAHOO.widget.Logger = {
1232 // Initialize members
1233 loggerEnabled: true,
1234 _browserConsoleEnabled: false,
1235 categories: ["info","warn","error","time","window"],
1236 sources: ["global"],
1237 _stack: [], // holds all log msgs
1238 maxStackEntries: 2500,
1239 _startTime: new Date().getTime(), // static start timestamp
1240 _lastTime: null // timestamp of last logged message
1241};
1242
1243/////////////////////////////////////////////////////////////////////////////
1244//
1245// Public methods
1246//
1247/////////////////////////////////////////////////////////////////////////////
1248/**
1249 * Saves a log message to the stack and fires newLogEvent. If the log message is
1250 * assigned to an unknown category, creates a new category. If the log message is
1251 * from an unknown source, creates a new source. If browser console is enabled,
1252 * outputs the log message to browser console.
1253 *
1254 * @method log
1255 * @param sMsg {String} The log message.
1256 * @param sCategory {String} Category of log message, or null.
1257 * @param sSource {String} Source of LogWriter, or null if global.
1258 */
1259YAHOO.widget.Logger.log = function(sMsg, sCategory, sSource) {
1260 if(this.loggerEnabled) {
1261 if(!sCategory) {
1262 sCategory = "info"; // default category
1263 }
1264 else {
1265 sCategory = sCategory.toLocaleLowerCase();
1266 if(this._isNewCategory(sCategory)) {
1267 this._createNewCategory(sCategory);
1268 }
1269 }
1270 var sClass = "global"; // default source
1271 var sDetail = null;
1272 if(sSource) {
1273 var spaceIndex = sSource.indexOf(" ");
1274 if(spaceIndex > 0) {
1275 // Substring until first space
1276 sClass = sSource.substring(0,spaceIndex);
1277 // The rest of the source
1278 sDetail = sSource.substring(spaceIndex,sSource.length);
1279 }
1280 else {
1281 sClass = sSource;
1282 }
1283 if(this._isNewSource(sClass)) {
1284 this._createNewSource(sClass);
1285 }
1286 }
1287
1288 var timestamp = new Date();
1289 var logEntry = new YAHOO.widget.LogMsg({
1290 msg: sMsg,
1291 time: timestamp,
1292 category: sCategory,
1293 source: sClass,
1294 sourceDetail: sDetail
1295 });
1296
1297 var stack = this._stack;
1298 var maxStackEntries = this.maxStackEntries;
1299 if(maxStackEntries && !isNaN(maxStackEntries) &&
1300 (stack.length >= maxStackEntries)) {
1301 stack.shift();
1302 }
1303 stack.push(logEntry);
1304 this.newLogEvent.fire(logEntry);
1305
1306 if(this._browserConsoleEnabled) {
1307 this._printToBrowserConsole(logEntry);
1308 }
1309 return true;
1310 }
1311 else {
1312 return false;
1313 }
1314};
1315
1316/**
1317 * Resets internal stack and startTime, enables Logger, and fires logResetEvent.
1318 *
1319 * @method reset
1320 */
1321YAHOO.widget.Logger.reset = function() {
1322 this._stack = [];
1323 this._startTime = new Date().getTime();
1324 this.loggerEnabled = true;
1325 this.log("Logger reset");
1326 this.logResetEvent.fire();
1327};
1328
1329/**
1330 * Public accessor to internal stack of log message objects.
1331 *
1332 * @method getStack
1333 * @return {Object[]} Array of log message objects.
1334 */
1335YAHOO.widget.Logger.getStack = function() {
1336 return this._stack;
1337};
1338
1339/**
1340 * Public accessor to internal start time.
1341 *
1342 * @method getStartTime
1343 * @return {Date} Internal date of when Logger singleton was initialized.
1344 */
1345YAHOO.widget.Logger.getStartTime = function() {
1346 return this._startTime;
1347};
1348
1349/**
1350 * Disables output to the browser's global console.log() function, which is used
1351 * by the Firebug extension to Firefox as well as Safari.
1352 *
1353 * @method disableBrowserConsole
1354 */
1355YAHOO.widget.Logger.disableBrowserConsole = function() {
1356 YAHOO.log("Logger output to the function console.log() has been disabled.");
1357 this._browserConsoleEnabled = false;
1358};
1359
1360/**
1361 * Enables output to the browser's global console.log() function, which is used
1362 * by the Firebug extension to Firefox as well as Safari.
1363 *
1364 * @method enableBrowserConsole
1365 */
1366YAHOO.widget.Logger.enableBrowserConsole = function() {
1367 this._browserConsoleEnabled = true;
1368 YAHOO.log("Logger output to the function console.log() has been enabled.");
1369};
1370
1371/////////////////////////////////////////////////////////////////////////////
1372//
1373// Public events
1374//
1375/////////////////////////////////////////////////////////////////////////////
1376
1377 /**
1378 * Fired when a new category has been created.
1379 *
1380 * @event categoryCreateEvent
1381 * @param sCategory {String} Category name.
1382 */
1383YAHOO.widget.Logger.categoryCreateEvent =
1384 new YAHOO.util.CustomEvent("categoryCreate", this, true);
1385
1386 /**
1387 * Fired when a new source has been named.
1388 *
1389 * @event sourceCreateEvent
1390 * @param sSource {String} Source name.
1391 */
1392YAHOO.widget.Logger.sourceCreateEvent =
1393 new YAHOO.util.CustomEvent("sourceCreate", this, true);
1394
1395 /**
1396 * Fired when a new log message has been created.
1397 *
1398 * @event newLogEvent
1399 * @param sMsg {String} Log message.
1400 */
1401YAHOO.widget.Logger.newLogEvent = new YAHOO.util.CustomEvent("newLog", this, true);
1402
1403/**
1404 * Fired when the Logger has been reset has been created.
1405 *
1406 * @event logResetEvent
1407 */
1408YAHOO.widget.Logger.logResetEvent = new YAHOO.util.CustomEvent("logReset", this, true);
1409
1410/////////////////////////////////////////////////////////////////////////////
1411//
1412// Private methods
1413//
1414/////////////////////////////////////////////////////////////////////////////
1415
1416/**
1417 * Creates a new category of log messages and fires categoryCreateEvent.
1418 *
1419 * @method _createNewCategory
1420 * @param sCategory {String} Category name.
1421 * @private
1422 */
1423YAHOO.widget.Logger._createNewCategory = function(sCategory) {
1424 this.categories.push(sCategory);
1425 this.categoryCreateEvent.fire(sCategory);
1426};
1427
1428/**
1429 * Checks to see if a category has already been created.
1430 *
1431 * @method _isNewCategory
1432 * @param sCategory {String} Category name.
1433 * @return {Boolean} Returns true if category is unknown, else returns false.
1434 * @private
1435 */
1436YAHOO.widget.Logger._isNewCategory = function(sCategory) {
1437 for(var i=0; i < this.categories.length; i++) {
1438 if(sCategory == this.categories[i]) {
1439 return false;
1440 }
1441 }
1442 return true;
1443};
1444
1445/**
1446 * Creates a new source of log messages and fires sourceCreateEvent.
1447 *
1448 * @method _createNewSource
1449 * @param sSource {String} Source name.
1450 * @private
1451 */
1452YAHOO.widget.Logger._createNewSource = function(sSource) {
1453 this.sources.push(sSource);
1454 this.sourceCreateEvent.fire(sSource);
1455};
1456
1457/**
1458 * Checks to see if a source already exists.
1459 *
1460 * @method _isNewSource
1461 * @param sSource {String} Source name.
1462 * @return {Boolean} Returns true if source is unknown, else returns false.
1463 * @private
1464 */
1465YAHOO.widget.Logger._isNewSource = function(sSource) {
1466 if(sSource) {
1467 for(var i=0; i < this.sources.length; i++) {
1468 if(sSource == this.sources[i]) {
1469 return false;
1470 }
1471 }
1472 return true;
1473 }
1474};
1475
1476/**
1477 * Outputs a log message to global console.log() function.
1478 *
1479 * @method _printToBrowserConsole
1480 * @param oEntry {Object} Log entry object.
1481 * @private
1482 */
1483YAHOO.widget.Logger._printToBrowserConsole = function(oEntry) {
1484 if(window.console && console.log) {
1485 var category = oEntry.category;
1486 var label = oEntry.category.substring(0,4).toUpperCase();
1487
1488 var time = oEntry.time;
1489 if (time.toLocaleTimeString) {
1490 var localTime = time.toLocaleTimeString();
1491 }
1492 else {
1493 localTime = time.toString();
1494 }
1495
1496 var msecs = time.getTime();
1497 var elapsedTime = (YAHOO.widget.Logger._lastTime) ?
1498 (msecs - YAHOO.widget.Logger._lastTime) : 0;
1499 YAHOO.widget.Logger._lastTime = msecs;
1500
1501 var output =
1502 localTime + " (" +
1503 elapsedTime + "ms): " +
1504 oEntry.source + ": " +
1505 oEntry.msg;
1506
1507 console.log(output);
1508 }
1509};
1510
1511/////////////////////////////////////////////////////////////////////////////
1512//
1513// Private event handlers
1514//
1515/////////////////////////////////////////////////////////////////////////////
1516
1517/**
1518 * Handles logging of messages due to window error events.
1519 *
1520 * @method _onWindowError
1521 * @param sMsg {String} The error message.
1522 * @param sUrl {String} URL of the error.
1523 * @param sLine {String} Line number of the error.
1524 * @private
1525 */
1526YAHOO.widget.Logger._onWindowError = function(sMsg,sUrl,sLine) {
1527 // Logger is not in scope of this event handler
1528 try {
1529 YAHOO.widget.Logger.log(sMsg+' ('+sUrl+', line '+sLine+')', "window");
1530 if(YAHOO.widget.Logger._origOnWindowError) {
1531 YAHOO.widget.Logger._origOnWindowError();
1532 }
1533 }
1534 catch(e) {
1535 return false;
1536 }
1537};
1538
1539/////////////////////////////////////////////////////////////////////////////
1540//
1541// Enable handling of native JavaScript errors
1542// NB: Not all browsers support the window.onerror event
1543//
1544/////////////////////////////////////////////////////////////////////////////
1545
1546if(window.onerror) {
1547 // Save any previously defined handler to call
1548 YAHOO.widget.Logger._origOnWindowError = window.onerror;
1549}
1550window.onerror = YAHOO.widget.Logger._onWindowError;
1551
1552/////////////////////////////////////////////////////////////////////////////
1553//
1554// First log
1555//
1556/////////////////////////////////////////////////////////////////////////////
1557
1558YAHOO.widget.Logger.log("Logger initialized");
1559
diff --git a/frontend/beta/js/YUI/menu.js b/frontend/beta/js/YUI/menu.js
new file mode 100644
index 0000000..50eb0cf
--- a/dev/null
+++ b/frontend/beta/js/YUI/menu.js
@@ -0,0 +1,6780 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.com/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9* @module menu
10* @description <p>The Menu Library features a collection of widgets that make
11* it easy to add menus to your website or web application. With the Menu
12* Library you can create website fly-out menus, customized context menus, or
13* application-style menu bars with just a small amount of scripting.</p>
14* <ul>
15* <li>Screen-reader accessibility.</li>
16* <li>Keyboard and mouse navigation.</li>
17* <li>A rich event model that provides access to all of a menu's
18* interesting moments.</li>
19* <li>Support for
20* <a href="http://en.wikipedia.org/wiki/Progressive_Enhancement">Progressive
21* Enhancement</a>; Menus can be created from simple,
22* semantic markup on the page or purely through JavaScript.</li>
23* </ul>
24* @title Menu Library
25* @namespace YAHOO.widget
26* @requires Event, Dom, Container
27*/
28(function() {
29
30var Dom = YAHOO.util.Dom;
31var Event = YAHOO.util.Event;
32
33/**
34* Singleton that manages a collection of all menus and menu items. Listens for
35* DOM events at the document level and dispatches the events to the
36* corresponding menu or menu item.
37*
38* @namespace YAHOO.widget
39* @class MenuManager
40* @static
41*/
42YAHOO.widget.MenuManager = new function() {
43
44 // Private member variables
45
46 // Flag indicating if the DOM event handlers have been attached
47
48 var m_bInitializedEventHandlers = false;
49
50 // Collection of menus
51
52 var m_oMenus = {};
53
54
55 // Collection of menu items
56
57 var m_oItems = {};
58
59 // Collection of visible menus
60
61 var m_oVisibleMenus = {};
62
63 // Logger
64
65
66 // Private methods
67
68 /**
69 * Adds an item to the collection of known menu items.
70 * @private
71 * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem
72 * instance to be added.
73 */
74 var addItem = function(p_oItem) {
75
76 var sYUIId = Dom.generateId();
77
78 if(p_oItem && m_oItems[sYUIId] != p_oItem) {
79
80 p_oItem.element.setAttribute("yuiid", sYUIId);
81
82 m_oItems[sYUIId] = p_oItem;
83
84 p_oItem.destroyEvent.subscribe(onItemDestroy, p_oItem);
85
86
87 }
88
89 };
90
91 /**
92 * Removes an item from the collection of known menu items.
93 * @private
94 * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem
95 * instance to be removed.
96 */
97 var removeItem = function(p_oItem) {
98
99 var sYUIId = p_oItem.element.getAttribute("yuiid");
100
101 if(sYUIId && m_oItems[sYUIId]) {
102
103 delete m_oItems[sYUIId];
104
105
106 }
107
108 };
109
110 /**
111 * Finds the root DIV node of a menu or the root LI node of a menu item.
112 * @private
113 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
114 * one-html.html#ID-58190037">HTMLElement</a>} p_oElement Object specifying
115 * an HTML element.
116 */
117 var getMenuRootElement = function(p_oElement) {
118
119 var oParentNode;
120
121 if(p_oElement && p_oElement.tagName) {
122
123 switch(p_oElement.tagName.toUpperCase()) {
124
125 case "DIV":
126
127 oParentNode = p_oElement.parentNode;
128
129 // Check if the DIV is the inner "body" node of a menu
130
131 if(
132 Dom.hasClass(p_oElement, "bd") &&
133 oParentNode &&
134 oParentNode.tagName &&
135 oParentNode.tagName.toUpperCase() == "DIV"
136 ) {
137
138 return oParentNode;
139
140 }
141 else {
142
143 return p_oElement;
144
145 }
146
147 break;
148
149 case "LI":
150
151 return p_oElement;
152
153 default:
154
155 oParentNode = p_oElement.parentNode;
156
157 if(oParentNode) {
158
159 return getMenuRootElement(oParentNode);
160
161 }
162
163 break;
164
165 }
166
167 }
168
169 };
170
171 // Private event handlers
172
173 /**
174 * Generic, global event handler for all of a menu's DOM-based
175 * events. This listens for events against the document object. If the
176 * target of a given event is a member of a menu or menu item's DOM, the
177 * instance's corresponding Custom Event is fired.
178 * @private
179 * @param {Event} p_oEvent Object representing the DOM event object passed
180 * back by the event utility (YAHOO.util.Event).
181 */
182 var onDOMEvent = function(p_oEvent) {
183
184 // Get the target node of the DOM event
185
186 var oTarget = Event.getTarget(p_oEvent);
187
188 // See if the target of the event was a menu, or a menu item
189
190 var oElement = getMenuRootElement(oTarget);
191
192 var oMenuItem;
193 var oMenu;
194
195 if(oElement) {
196
197 var sTagName = oElement.tagName.toUpperCase();
198
199 if(sTagName == "LI") {
200
201 var sYUIId = oElement.getAttribute("yuiid");
202
203 if(sYUIId) {
204
205 oMenuItem = m_oItems[sYUIId];
206 oMenu = oMenuItem.parent;
207
208 }
209
210 }
211 else if(sTagName == "DIV") {
212
213 if(oElement.id) {
214
215 oMenu = m_oMenus[oElement.id];
216
217 }
218
219 }
220
221 }
222
223 if(oMenu) {
224
225 // Map of DOM event names to CustomEvent names
226
227 var oEventTypes = {
228 "click": "clickEvent",
229 "mousedown": "mouseDownEvent",
230 "mouseup": "mouseUpEvent",
231 "mouseover": "mouseOverEvent",
232 "mouseout": "mouseOutEvent",
233 "keydown": "keyDownEvent",
234 "keyup": "keyUpEvent",
235 "keypress": "keyPressEvent"
236 };
237
238 var sCustomEventType = oEventTypes[p_oEvent.type];
239
240 // Fire the Custom Even that corresponds the current DOM event
241
242 if(oMenuItem && !oMenuItem.cfg.getProperty("disabled")) {
243
244 oMenuItem[sCustomEventType].fire(p_oEvent);
245
246 }
247
248 oMenu[sCustomEventType].fire(p_oEvent, oMenuItem);
249
250 }
251 else if(p_oEvent.type == "mousedown") {
252
253 /*
254 If the target of the event wasn't a menu, hide all
255 dynamically positioned menus
256 */
257
258 var oActiveItem;
259
260 for(var i in m_oMenus) {
261
262 if(m_oMenus.hasOwnProperty(i)) {
263
264 oMenu = m_oMenus[i];
265
266 if(
267 oMenu.cfg.getProperty("clicktohide") &&
268 oMenu.cfg.getProperty("position") == "dynamic"
269 ) {
270
271 oMenu.hide();
272
273 }
274 else {
275
276 oMenu.clearActiveItem(true);
277
278 }
279
280 }
281
282 }
283
284 }
285
286 };
287
288 /**
289 * "destroy" event handler for a menu.
290 * @private
291 * @param {String} p_sType String representing the name of the event that
292 * was fired.
293 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
294 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
295 * fired the event.
296 */
297 var onMenuDestroy = function(p_sType, p_aArgs, p_oMenu) {
298
299 this.removeMenu(p_oMenu);
300
301 };
302
303 /**
304 * "destroy" event handler for a MenuItem instance.
305 * @private
306 * @param {String} p_sType String representing the name of the event that
307 * was fired.
308 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
309 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
310 * that fired the event.
311 */
312 var onItemDestroy = function(p_sType, p_aArgs, p_oItem) {
313
314 var sYUIId = p_oItem.element.getAttribute("yuiid");
315
316 if(sYUIId) {
317
318 delete m_oItems[sYUIId];
319
320 }
321
322 };
323
324 /**
325 * Event handler for when the "visible" configuration property
326 * of a Menu instance changes.
327 * @private
328 * @param {String} p_sType String representing the name of the event that
329 * was fired.
330 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
331 * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
332 * fired the event.
333 */
334 var onMenuVisibleConfigChange = function(p_sType, p_aArgs, p_oMenu) {
335
336 var bVisible = p_aArgs[0];
337
338 if(bVisible) {
339
340 m_oVisibleMenus[p_oMenu.id] = p_oMenu;
341
342
343 }
344 else if(m_oVisibleMenus[p_oMenu.id]) {
345
346 delete m_oVisibleMenus[p_oMenu.id];
347
348
349 }
350
351 };
352
353 /**
354 * "itemadded" event handler for a Menu instance.
355 * @private
356 * @param {String} p_sType String representing the name of the event that
357 * was fired.
358 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
359 */
360 var onItemAdded = function(p_sType, p_aArgs) {
361
362 addItem(p_aArgs[0]);
363
364 };
365
366
367 /**
368 * "itemremoved" event handler for a Menu instance.
369 * @private
370 * @param {String} p_sType String representing the name of the event that
371 * was fired.
372 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
373 */
374 var onItemRemoved = function(p_sType, p_aArgs) {
375
376 removeItem(p_aArgs[0]);
377
378 };
379
380 // Privileged methods
381
382 /**
383 * @method addMenu
384 * @description Adds a menu to the collection of known menus.
385 * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu instance
386 * to be added.
387 */
388 this.addMenu = function(p_oMenu) {
389
390 if(p_oMenu && p_oMenu.id && !m_oMenus[p_oMenu.id]) {
391
392 m_oMenus[p_oMenu.id] = p_oMenu;
393
394
395 if(!m_bInitializedEventHandlers) {
396
397 var oDoc = document;
398
399 Event.addListener(oDoc, "mouseover", onDOMEvent, this, true);
400 Event.addListener(oDoc, "mouseout", onDOMEvent, this, true);
401 Event.addListener(oDoc, "mousedown", onDOMEvent, this, true);
402 Event.addListener(oDoc, "mouseup", onDOMEvent, this, true);
403 Event.addListener(oDoc, "click", onDOMEvent, this, true);
404 Event.addListener(oDoc, "keydown", onDOMEvent, this, true);
405 Event.addListener(oDoc, "keyup", onDOMEvent, this, true);
406 Event.addListener(oDoc, "keypress", onDOMEvent, this, true);
407
408 m_bInitializedEventHandlers = true;
409
410
411 }
412
413 p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this);
414
415 p_oMenu.cfg.subscribeToConfigEvent(
416 "visible",
417 onMenuVisibleConfigChange,
418 p_oMenu
419 );
420
421 p_oMenu.itemAddedEvent.subscribe(onItemAdded);
422 p_oMenu.itemRemovedEvent.subscribe(onItemRemoved);
423
424
425 }
426
427 };
428
429 /**
430 * @method removeMenu
431 * @description Removes a menu from the collection of known menus.
432 * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu instance
433 * to be removed.
434 */
435 this.removeMenu = function(p_oMenu) {
436
437 if(p_oMenu && m_oMenus[p_oMenu.id]) {
438
439 delete m_oMenus[p_oMenu.id];
440
441
442 }
443
444 };
445
446 /**
447 * @method hideVisible
448 * @description Hides all visible, dynamically positioned menus.
449 */
450 this.hideVisible = function() {
451
452 var oMenu;
453
454 for(var i in m_oVisibleMenus) {
455
456 if(m_oVisibleMenus.hasOwnProperty(i)) {
457
458 oMenu = m_oVisibleMenus[i];
459
460 if(oMenu.cfg.getProperty("position") == "dynamic") {
461
462 oMenu.hide();
463
464 }
465
466 }
467
468 }
469
470 };
471
472 /**
473 * @method getMenus
474 * @description Returns an array of all menus registered with the
475 * menu manger.
476 * @return {Array}
477 */
478 this.getMenus = function() {
479
480 return m_oMenus;
481
482 };
483
484 /**
485 * @method getMenu
486 * @description Returns a menu with the specified id.
487 * @param {String} p_sId String specifying the id of the menu to
488 * be retrieved.
489 * @return {YAHOO.widget.Menu}
490 */
491 this.getMenu = function(p_sId) {
492
493 if(m_oMenus[p_sId]) {
494
495 return m_oMenus[p_sId];
496
497 }
498
499 };
500
501
502 /**
503 * @method toString
504 * @description Returns a string representing the menu manager.
505 * @return {String}
506 */
507 this.toString = function() {
508
509 return ("MenuManager");
510
511 };
512
513};
514
515})();
516
517(function() {
518
519var Dom = YAHOO.util.Dom;
520var Event = YAHOO.util.Event;
521
522/**
523* The Menu class creates a container that holds a vertical list representing
524* a set of options or commands. Menu is the base class for all
525* menu containers.
526* @param {String} p_oElement String specifying the id attribute of the
527* <code>&#60;div&#62;</code> element of the menu.
528* @param {String} p_oElement String specifying the id attribute of the
529* <code>&#60;select&#62;</code> element to be used as the data source
530* for the menu.
531* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
532* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object
533* specifying the <code>&#60;div&#62;</code> element of the menu.
534* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
535* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement
536* Object specifying the <code>&#60;select&#62;</code> element to be used as
537* the data source for the menu.
538* @param {Object} p_oConfig Optional. Object literal specifying the
539* configuration for the menu. See configuration class documentation for
540* more details.
541* @namespace YAHOO.widget
542* @class Menu
543* @constructor
544* @extends YAHOO.widget.Overlay
545*/
546YAHOO.widget.Menu = function(p_oElement, p_oConfig) {
547
548 if(p_oConfig) {
549
550 this.parent = p_oConfig.parent;
551
552 this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload;
553
554 this.itemData = p_oConfig.itemData || p_oConfig.itemdata;
555
556 }
557
558 YAHOO.widget.Menu.superclass.constructor.call(
559 this,
560 p_oElement,
561 p_oConfig
562 );
563
564};
565
566YAHOO.extend(YAHOO.widget.Menu, YAHOO.widget.Overlay, {
567
568// Constants
569
570/**
571* @property CSS_CLASS_NAME
572* @description String representing the CSS class(es) to be applied to the
573* menu's <code>&#60;div&#62;</code> element.
574* @default "yuimenu"
575* @final
576* @type String
577*/
578CSS_CLASS_NAME: "yuimenu",
579
580/**
581* @property ITEM_TYPE
582* @description Object representing the type of menu item to instantiate and
583* add when parsing the child nodes (either <code>&#60;li&#62;</code> element,
584* <code>&#60;optgroup&#62;</code> element or <code>&#60;option&#62;</code>)
585* of the menu's source HTML element.
586* @default YAHOO.widget.MenuItem
587* @final
588* @type YAHOO.widget.MenuItem
589*/
590ITEM_TYPE: null,
591
592/**
593* @property GROUP_TITLE_TAG_NAME
594* @description String representing the tagname of the HTML element used to
595* title the menu's item groups.
596* @default H6
597* @final
598* @type String
599*/
600GROUP_TITLE_TAG_NAME: "h6",
601
602// Private properties
603
604/**
605* @property _nHideDelayId
606* @description Number representing the time-out setting used to cancel the
607* hiding of a menu.
608* @default null
609* @private
610* @type Number
611*/
612_nHideDelayId: null,
613
614/**
615* @property _nShowDelayId
616* @description Number representing the time-out setting used to cancel the
617* showing of a menu.
618* @default null
619* @private
620* @type Number
621*/
622_nShowDelayId: null,
623
624/**
625* @property _hideDelayEventHandlersAssigned
626* @description Boolean indicating if the "mouseover" and "mouseout" event
627* handlers used for hiding the menu via a call to "window.setTimeout" have
628* already been assigned.
629* @default false
630* @private
631* @type Boolean
632*/
633_hideDelayEventHandlersAssigned: false,
634
635/**
636* @property _bHandledMouseOverEvent
637* @description Boolean indicating the current state of the menu's
638* "mouseover" event.
639* @default false
640* @private
641* @type Boolean
642*/
643_bHandledMouseOverEvent: false,
644
645/**
646* @property _bHandledMouseOutEvent
647* @description Boolean indicating the current state of the menu's
648* "mouseout" event.
649* @default false
650* @private
651* @type Boolean
652*/
653_bHandledMouseOutEvent: false,
654
655/**
656* @property _aGroupTitleElements
657* @description Array of HTML element used to title groups of menu items.
658* @default []
659* @private
660* @type Array
661*/
662_aGroupTitleElements: null,
663
664/**
665* @property _aItemGroups
666* @description Array of menu items.
667* @default []
668* @private
669* @type Array
670*/
671_aItemGroups: null,
672
673/**
674* @property _aListElements
675* @description Array of <code>&#60;ul&#62;</code> elements, each of which is
676* the parent node for each item's <code>&#60;li&#62;</code> element.
677* @default []
678* @private
679* @type Array
680*/
681_aListElements: null,
682
683// Public properties
684
685/**
686* @property lazyLoad
687* @description Boolean indicating if the menu's "lazy load" feature is
688* enabled. If set to "true," initialization and rendering of the menu's
689* items will be deferred until the first time it is made visible. This
690* property should be set via the constructor using the configuration
691* object literal.
692* @default false
693* @type Boolean
694*/
695lazyLoad: false,
696
697/**
698* @property itemData
699* @description Array of items to be added to the menu. The array can contain
700* strings representing the text for each item to be created, object literals
701* representing the menu item configuration properties, or MenuItem instances.
702* This property should be set via the constructor using the configuration
703* object literal.
704* @default null
705* @type Array
706*/
707itemData: null,
708
709/**
710* @property activeItem
711* @description Object reference to the item in the menu that has focus.
712* @default null
713* @type YAHOO.widget.MenuItem
714*/
715activeItem: null,
716
717/**
718* @property parent
719* @description Object reference to the menu's parent menu or menu item.
720* This property can be set via the constructor using the configuration
721* object literal.
722* @default null
723* @type YAHOO.widget.MenuItem
724*/
725parent: null,
726
727/**
728* @property srcElement
729* @description Object reference to the HTML element (either
730* <code>&#60;select&#62;</code> or <code>&#60;div&#62;</code>) used to
731* create the menu.
732* @default null
733* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
734* level-one-html.html#ID-94282980">HTMLSelectElement</a>|<a
735* href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.
736* html#ID-22445964">HTMLDivElement</a>
737*/
738srcElement: null,
739
740// Events
741
742/**
743* @event mouseOverEvent
744* @description Fires when the mouse has entered the menu. Passes back
745* the DOM Event object as an argument.
746*/
747mouseOverEvent: null,
748
749/**
750* @event mouseOutEvent
751* @description Fires when the mouse has left the menu. Passes back the DOM
752* Event object as an argument.
753* @type YAHOO.util.CustomEvent
754*/
755mouseOutEvent: null,
756
757/**
758* @event mouseDownEvent
759* @description Fires when the user mouses down on the menu. Passes back the
760* DOM Event object as an argument.
761* @type YAHOO.util.CustomEvent
762*/
763mouseDownEvent: null,
764
765/**
766* @event mouseUpEvent
767* @description Fires when the user releases a mouse button while the mouse is
768* over the menu. Passes back the DOM Event object as an argument.
769* @type YAHOO.util.CustomEvent
770*/
771mouseUpEvent: null,
772
773/**
774* @event clickEvent
775* @description Fires when the user clicks the on the menu. Passes back the
776* DOM Event object as an argument.
777* @type YAHOO.util.CustomEvent
778*/
779clickEvent: null,
780
781/**
782* @event keyPressEvent
783* @description Fires when the user presses an alphanumeric key when one of the
784* menu's items has focus. Passes back the DOM Event object as an argument.
785* @type YAHOO.util.CustomEvent
786*/
787keyPressEvent: null,
788
789/**
790* @event keyDownEvent
791* @description Fires when the user presses a key when one of the menu's items
792* has focus. Passes back the DOM Event object as an argument.
793* @type YAHOO.util.CustomEvent
794*/
795keyDownEvent: null,
796
797/**
798* @event keyUpEvent
799* @description Fires when the user releases a key when one of the menu's items
800* has focus. Passes back the DOM Event object as an argument.
801* @type YAHOO.util.CustomEvent
802*/
803keyUpEvent: null,
804
805/**
806* @event itemAddedEvent
807* @description Fires when an item is added to the menu.
808* @type YAHOO.util.CustomEvent
809*/
810itemAddedEvent: null,
811
812/**
813* @event itemRemovedEvent
814* @description Fires when an item is removed to the menu.
815* @type YAHOO.util.CustomEvent
816*/
817itemRemovedEvent: null,
818
819/**
820* @method init
821* @description The Menu class's initialization method. This method is
822* automatically called by the constructor, and sets up all DOM references
823* for pre-existing markup, and creates required markup if it is not
824* already present.
825* @param {String} p_oElement String specifying the id attribute of the
826* <code>&#60;div&#62;</code> element of the menu.
827* @param {String} p_oElement String specifying the id attribute of the
828* <code>&#60;select&#62;</code> element to be used as the data source
829* for the menu.
830* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
831* level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object
832* specifying the <code>&#60;div&#62;</code> element of the menu.
833* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
834* level-one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement
835* Object specifying the <code>&#60;select&#62;</code> element to be used as
836* the data source for the menu.
837* @param {Object} p_oConfig Optional. Object literal specifying the
838* configuration for the menu. See configuration class documentation for
839* more details.
840*/
841init: function(p_oElement, p_oConfig) {
842
843 this._aItemGroups = [];
844 this._aListElements = [];
845 this._aGroupTitleElements = [];
846
847 if(!this.ITEM_TYPE) {
848
849 this.ITEM_TYPE = YAHOO.widget.MenuItem;
850
851 }
852
853 var oElement;
854
855 if(typeof p_oElement == "string") {
856
857 oElement = document.getElementById(p_oElement);
858
859 }
860 else if(p_oElement.tagName) {
861
862 oElement = p_oElement;
863
864 }
865
866 if(oElement && oElement.tagName) {
867
868 switch(oElement.tagName.toUpperCase()) {
869
870 case "DIV":
871
872 this.srcElement = oElement;
873
874 if(!oElement.id) {
875
876 oElement.setAttribute("id", Dom.generateId());
877
878 }
879
880 /*
881 Note: we don't pass the user config in here yet
882 because we only want it executed once, at the lowest
883 subclass level.
884 */
885
886 YAHOO.widget.Menu.superclass.init.call(this, oElement);
887
888 this.beforeInitEvent.fire(YAHOO.widget.Menu);
889
890
891 break;
892
893 case "SELECT":
894
895 this.srcElement = oElement;
896
897
898 /*
899 The source element is not something that we can use
900 outright, so we need to create a new Overlay
901
902 Note: we don't pass the user config in here yet
903 because we only want it executed once, at the lowest
904 subclass level.
905 */
906
907 YAHOO.widget.Menu.superclass.init.call(this, Dom.generateId());
908
909 this.beforeInitEvent.fire(YAHOO.widget.Menu);
910
911 break;
912
913 }
914
915 }
916 else {
917
918 /*
919 Note: we don't pass the user config in here yet
920 because we only want it executed once, at the lowest
921 subclass level.
922 */
923
924 YAHOO.widget.Menu.superclass.init.call(this, p_oElement);
925
926 this.beforeInitEvent.fire(YAHOO.widget.Menu);
927
928 }
929
930 if(this.element) {
931
932 var oEl = this.element;
933
934 Dom.addClass(oEl, this.CSS_CLASS_NAME);
935
936 // Subscribe to Custom Events
937
938 this.initEvent.subscribe(this._onInit, this, true);
939 this.beforeRenderEvent.subscribe(this._onBeforeRender, this, true);
940 this.renderEvent.subscribe(this._onRender, this, true);
941 this.beforeShowEvent.subscribe(this._onBeforeShow, this, true);
942 this.showEvent.subscribe(this._onShow, this, true);
943 this.beforeHideEvent.subscribe(this._onBeforeHide, this, true);
944 this.mouseOverEvent.subscribe(this._onMouseOver, this, true);
945 this.mouseOutEvent.subscribe(this._onMouseOut, this, true);
946 this.clickEvent.subscribe(this._onClick, this, true);
947 this.keyDownEvent.subscribe(this._onKeyDown, this, true);
948
949 if(p_oConfig) {
950
951 this.cfg.applyConfig(p_oConfig, true);
952
953 }
954
955 // Register the Menu instance with the MenuManager
956
957 YAHOO.widget.MenuManager.addMenu(this);
958
959
960 this.initEvent.fire(YAHOO.widget.Menu);
961
962 }
963
964},
965
966// Private methods
967
968/**
969* @method _initSubTree
970* @description Iterates the childNodes of the source element to find nodes
971* used to instantiate menu and menu items.
972* @private
973*/
974_initSubTree: function() {
975
976 var oNode;
977
978 if(this.srcElement.tagName == "DIV") {
979
980 /*
981 Populate the collection of item groups and item
982 group titles
983 */
984
985 oNode = this.body.firstChild;
986
987 var nGroup = 0;
988 var sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase();
989
990 do {
991
992 if(oNode && oNode.tagName) {
993
994 switch(oNode.tagName.toUpperCase()) {
995
996 case sGroupTitleTagName:
997
998 this._aGroupTitleElements[nGroup] = oNode;
999
1000 break;
1001
1002 case "UL":
1003
1004 this._aListElements[nGroup] = oNode;
1005 this._aItemGroups[nGroup] = [];
1006 nGroup++;
1007
1008 break;
1009
1010 }
1011
1012 }
1013
1014 }
1015 while((oNode = oNode.nextSibling));
1016
1017 /*
1018 Apply the "first-of-type" class to the first UL to mimic
1019 the "first-of-type" CSS3 psuedo class.
1020 */
1021
1022 if(this._aListElements[0]) {
1023
1024 Dom.addClass(this._aListElements[0], "first-of-type");
1025
1026 }
1027
1028 }
1029
1030 oNode = null;
1031
1032 if(this.srcElement.tagName) {
1033
1034 switch(this.srcElement.tagName.toUpperCase()) {
1035
1036 case "DIV":
1037
1038 if(this._aListElements.length > 0) {
1039
1040
1041 var i = this._aListElements.length - 1;
1042
1043 do {
1044
1045 oNode = this._aListElements[i].firstChild;
1046
1047
1048 do {
1049
1050 if(oNode && oNode.tagName) {
1051
1052 switch(oNode.tagName.toUpperCase()) {
1053
1054 case "LI":
1055
1056
1057 this.addItem(
1058 new this.ITEM_TYPE(
1059 oNode,
1060 { parent: this }
1061 ),
1062 i
1063 );
1064
1065 break;
1066
1067 }
1068
1069 }
1070
1071 }
1072 while((oNode = oNode.nextSibling));
1073
1074 }
1075 while(i--);
1076
1077 }
1078
1079 break;
1080
1081 case "SELECT":
1082
1083
1084 oNode = this.srcElement.firstChild;
1085
1086 do {
1087
1088 if(oNode && oNode.tagName) {
1089
1090 switch(oNode.tagName.toUpperCase()) {
1091
1092 case "OPTGROUP":
1093 case "OPTION":
1094
1095
1096 this.addItem(
1097 new this.ITEM_TYPE(
1098 oNode,
1099 { parent: this }
1100 )
1101 );
1102
1103 break;
1104
1105 }
1106
1107 }
1108
1109 }
1110 while((oNode = oNode.nextSibling));
1111
1112 break;
1113
1114 }
1115
1116 }
1117
1118},
1119
1120/**
1121* @method _getFirstEnabledItem
1122* @description Returns the first enabled item in the menu.
1123* @return {YAHOO.widget.MenuItem}
1124* @private
1125*/
1126_getFirstEnabledItem: function() {
1127
1128 var nGroups = this._aItemGroups.length;
1129 var oItem;
1130 var aItemGroup;
1131
1132 for(var i=0; i<nGroups; i++) {
1133
1134 aItemGroup = this._aItemGroups[i];
1135
1136 if(aItemGroup) {
1137
1138 var nItems = aItemGroup.length;
1139
1140 for(var n=0; n<nItems; n++) {
1141
1142 oItem = aItemGroup[n];
1143
1144 if(
1145 !oItem.cfg.getProperty("disabled") &&
1146 oItem.element.style.display != "none"
1147 ) {
1148
1149 return oItem;
1150
1151 }
1152
1153 oItem = null;
1154
1155 }
1156
1157 }
1158
1159 }
1160
1161},
1162
1163/**
1164* @method _checkPosition
1165* @description Checks to make sure that the value of the "position" property
1166* is one of the supported strings. Returns true if the position is supported.
1167* @private
1168* @param {Object} p_sPosition String specifying the position of the menu.
1169* @return {Boolean}
1170*/
1171_checkPosition: function(p_sPosition) {
1172
1173 if(typeof p_sPosition == "string") {
1174
1175 var sPosition = p_sPosition.toLowerCase();
1176
1177 return ("dynamic,static".indexOf(sPosition) != -1);
1178
1179 }
1180
1181},
1182
1183/**
1184* @method _addItemToGroup
1185* @description Adds a menu item to a group.
1186* @private
1187* @param {Number} p_nGroupIndex Number indicating the group to which the
1188* item belongs.
1189* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
1190* instance to be added to the menu.
1191* @param {String} p_oItem String specifying the text of the item to be added
1192* to the menu.
1193* @param {Object} p_oItem Object literal containing a set of menu item
1194* configuration properties.
1195* @param {Number} p_nItemIndex Optional. Number indicating the index at
1196* which the menu item should be added.
1197* @return {YAHOO.widget.MenuItem}
1198*/
1199_addItemToGroup: function(p_nGroupIndex, p_oItem, p_nItemIndex) {
1200
1201 var oItem;
1202
1203 if(p_oItem instanceof this.ITEM_TYPE) {
1204
1205 oItem = p_oItem;
1206 oItem.parent = this;
1207
1208 }
1209 else if(typeof p_oItem == "string") {
1210
1211 oItem = new this.ITEM_TYPE(p_oItem, { parent: this });
1212
1213 }
1214 else if(typeof p_oItem == "object" && p_oItem.text) {
1215
1216 var sText = p_oItem.text;
1217
1218 delete p_oItem["text"];
1219
1220 p_oItem.parent = this;
1221
1222 oItem = new this.ITEM_TYPE(sText, p_oItem);
1223
1224 }
1225
1226 if(oItem) {
1227
1228 var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0;
1229
1230 var aGroup = this._getItemGroup(nGroupIndex);
1231
1232 var oGroupItem;
1233
1234 if(!aGroup) {
1235
1236 aGroup = this._createItemGroup(nGroupIndex);
1237
1238 }
1239
1240 if(typeof p_nItemIndex == "number") {
1241
1242 var bAppend = (p_nItemIndex >= aGroup.length);
1243
1244 if(aGroup[p_nItemIndex]) {
1245
1246 aGroup.splice(p_nItemIndex, 0, oItem);
1247
1248 }
1249 else {
1250
1251 aGroup[p_nItemIndex] = oItem;
1252
1253 }
1254
1255 oGroupItem = aGroup[p_nItemIndex];
1256
1257 if(oGroupItem) {
1258
1259 if(
1260 bAppend &&
1261 (
1262 !oGroupItem.element.parentNode ||
1263 oGroupItem.element.parentNode.nodeType == 11
1264 )
1265 ) {
1266
1267 this._aListElements[nGroupIndex].appendChild(
1268 oGroupItem.element
1269 );
1270
1271 }
1272 else {
1273
1274
1275 /**
1276 * Returns the next sibling of an item in an array.
1277 * @private
1278 * @param {p_aArray} Array to search.
1279 * @param {p_nStartIndex} Number indicating the index to
1280 * start searching the array.
1281 * @return {Object}
1282 */
1283 var getNextItemSibling =
1284
1285 function(p_aArray, p_nStartIndex) {
1286
1287 return (
1288 p_aArray[p_nStartIndex] ||
1289 getNextItemSibling(
1290 p_aArray,
1291 (p_nStartIndex+1)
1292 )
1293 );
1294
1295 };
1296
1297
1298 var oNextItemSibling =
1299 getNextItemSibling(aGroup, (p_nItemIndex+1));
1300
1301 if(
1302 oNextItemSibling &&
1303 (
1304 !oGroupItem.element.parentNode ||
1305 oGroupItem.element.parentNode.nodeType == 11
1306 )
1307 ) {
1308
1309 this._aListElements[nGroupIndex].insertBefore(
1310 oGroupItem.element,
1311 oNextItemSibling.element
1312 );
1313
1314 }
1315
1316 }
1317
1318
1319 oGroupItem.parent = this;
1320
1321 this._subscribeToItemEvents(oGroupItem);
1322
1323 this._configureSubmenu(oGroupItem);
1324
1325 this._updateItemProperties(nGroupIndex);
1326
1327
1328 this.itemAddedEvent.fire(oGroupItem);
1329
1330 return oGroupItem;
1331
1332 }
1333
1334 }
1335 else {
1336
1337 var nItemIndex = aGroup.length;
1338
1339 aGroup[nItemIndex] = oItem;
1340
1341 oGroupItem = aGroup[nItemIndex];
1342
1343
1344 if(oGroupItem) {
1345
1346 if(
1347 !Dom.isAncestor(
1348 this._aListElements[nGroupIndex],
1349 oGroupItem.element
1350 )
1351 ) {
1352
1353 this._aListElements[nGroupIndex].appendChild(
1354 oGroupItem.element
1355 );
1356
1357 }
1358
1359 oGroupItem.element.setAttribute("groupindex", nGroupIndex);
1360 oGroupItem.element.setAttribute("index", nItemIndex);
1361
1362 oGroupItem.parent = this;
1363
1364 oGroupItem.index = nItemIndex;
1365 oGroupItem.groupIndex = nGroupIndex;
1366
1367 this._subscribeToItemEvents(oGroupItem);
1368
1369 this._configureSubmenu(oGroupItem);
1370
1371 if(nItemIndex === 0) {
1372
1373 Dom.addClass(oGroupItem.element, "first-of-type");
1374
1375 }
1376
1377
1378
1379 this.itemAddedEvent.fire(oGroupItem);
1380
1381 return oGroupItem;
1382
1383 }
1384
1385 }
1386
1387 }
1388
1389},
1390
1391/**
1392* @method _removeItemFromGroupByIndex
1393* @description Removes a menu item from a group by index. Returns the menu
1394* item that was removed.
1395* @private
1396* @param {Number} p_nGroupIndex Number indicating the group to which the menu
1397* item belongs.
1398* @param {Number} p_nItemIndex Number indicating the index of the menu item
1399* to be removed.
1400* @return {YAHOO.widget.MenuItem}
1401*/
1402_removeItemFromGroupByIndex: function(p_nGroupIndex, p_nItemIndex) {
1403
1404 var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0;
1405 var aGroup = this._getItemGroup(nGroupIndex);
1406
1407 if(aGroup) {
1408
1409 var aArray = aGroup.splice(p_nItemIndex, 1);
1410 var oItem = aArray[0];
1411
1412 if(oItem) {
1413
1414 // Update the index and className properties of each member
1415
1416 this._updateItemProperties(nGroupIndex);
1417
1418 if(aGroup.length === 0) {
1419
1420 // Remove the UL
1421
1422 var oUL = this._aListElements[nGroupIndex];
1423
1424 if(this.body && oUL) {
1425
1426 this.body.removeChild(oUL);
1427
1428 }
1429
1430 // Remove the group from the array of items
1431
1432 this._aItemGroups.splice(nGroupIndex, 1);
1433
1434
1435 // Remove the UL from the array of ULs
1436
1437 this._aListElements.splice(nGroupIndex, 1);
1438
1439
1440 /*
1441 Assign the "first-of-type" class to the new first UL
1442 in the collection
1443 */
1444
1445 oUL = this._aListElements[0];
1446
1447 if(oUL) {
1448
1449 Dom.addClass(oUL, "first-of-type");
1450
1451 }
1452
1453 }
1454
1455
1456 this.itemRemovedEvent.fire(oItem);
1457
1458 // Return a reference to the item that was removed
1459
1460 return oItem;
1461
1462 }
1463
1464 }
1465
1466},
1467
1468/**
1469* @method _removeItemFromGroupByValue
1470* @description Removes a menu item from a group by reference. Returns the
1471* menu item that was removed.
1472* @private
1473* @param {Number} p_nGroupIndex Number indicating the group to which the
1474* menu item belongs.
1475* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
1476* instance to be removed.
1477* @return {YAHOO.widget.MenuItem}
1478*/
1479_removeItemFromGroupByValue: function(p_nGroupIndex, p_oItem) {
1480
1481 var aGroup = this._getItemGroup(p_nGroupIndex);
1482
1483 if(aGroup) {
1484
1485 var nItems = aGroup.length;
1486 var nItemIndex = -1;
1487
1488 if(nItems > 0) {
1489
1490 var i = nItems-1;
1491
1492 do {
1493
1494 if(aGroup[i] == p_oItem) {
1495
1496 nItemIndex = i;
1497 break;
1498
1499 }
1500
1501 }
1502 while(i--);
1503
1504 if(nItemIndex > -1) {
1505
1506 return this._removeItemFromGroupByIndex(
1507 p_nGroupIndex,
1508 nItemIndex
1509 );
1510
1511 }
1512
1513 }
1514
1515 }
1516
1517},
1518
1519/**
1520* @method _updateItemProperties
1521* @description Updates the "index," "groupindex," and "className" properties
1522* of the menu items in the specified group.
1523* @private
1524* @param {Number} p_nGroupIndex Number indicating the group of items to update.
1525*/
1526_updateItemProperties: function(p_nGroupIndex) {
1527
1528 var aGroup = this._getItemGroup(p_nGroupIndex);
1529 var nItems = aGroup.length;
1530
1531 if(nItems > 0) {
1532
1533 var i = nItems - 1;
1534 var oItem;
1535 var oLI;
1536
1537 // Update the index and className properties of each member
1538
1539 do {
1540
1541 oItem = aGroup[i];
1542
1543 if(oItem) {
1544
1545 oLI = oItem.element;
1546
1547 oItem.index = i;
1548 oItem.groupIndex = p_nGroupIndex;
1549
1550 oLI.setAttribute("groupindex", p_nGroupIndex);
1551 oLI.setAttribute("index", i);
1552
1553 Dom.removeClass(oLI, "first-of-type");
1554
1555 }
1556
1557 }
1558 while(i--);
1559
1560 if(oLI) {
1561
1562 Dom.addClass(oLI, "first-of-type");
1563
1564 }
1565
1566 }
1567
1568},
1569
1570/**
1571* @method _createItemGroup
1572* @description Creates a new menu item group (array) and its associated
1573* <code>&#60;ul&#62;</code> element. Returns an aray of menu item groups.
1574* @private
1575* @param {Number} p_nIndex Number indicating the group to create.
1576* @return {Array}
1577*/
1578_createItemGroup: function(p_nIndex) {
1579
1580 if(!this._aItemGroups[p_nIndex]) {
1581
1582 this._aItemGroups[p_nIndex] = [];
1583
1584 var oUL = document.createElement("ul");
1585
1586 this._aListElements[p_nIndex] = oUL;
1587
1588 return this._aItemGroups[p_nIndex];
1589
1590 }
1591
1592},
1593
1594/**
1595* @method _getItemGroup
1596* @description Returns the menu item group at the specified index.
1597* @private
1598* @param {Number} p_nIndex Number indicating the index of the menu item group
1599* to be retrieved.
1600* @return {Array}
1601*/
1602_getItemGroup: function(p_nIndex) {
1603
1604 var nIndex = ((typeof p_nIndex == "number") ? p_nIndex : 0);
1605
1606 return this._aItemGroups[nIndex];
1607
1608},
1609
1610/**
1611* @method _configureSubmenu
1612* @description Subscribes the menu item's submenu to its parent menu's events.
1613* @private
1614* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
1615* instance with the submenu to be configured.
1616*/
1617_configureSubmenu: function(p_oItem) {
1618
1619 var oSubmenu = p_oItem.cfg.getProperty("submenu");
1620
1621 if(oSubmenu) {
1622
1623 /*
1624 Listen for configuration changes to the parent menu
1625 so they they can be applied to the submenu.
1626 */
1627
1628 this.cfg.configChangedEvent.subscribe(
1629 this._onParentMenuConfigChange,
1630 oSubmenu,
1631 true
1632 );
1633
1634 this.renderEvent.subscribe(
1635 this._onParentMenuRender,
1636 oSubmenu,
1637 true
1638 );
1639
1640 oSubmenu.beforeShowEvent.subscribe(
1641 this._onSubmenuBeforeShow,
1642 oSubmenu,
1643 true
1644 );
1645
1646 oSubmenu.showEvent.subscribe(
1647 this._onSubmenuShow,
1648 oSubmenu,
1649 true
1650 );
1651
1652 oSubmenu.hideEvent.subscribe(
1653 this._onSubmenuHide,
1654 oSubmenu,
1655 true
1656 );
1657
1658 }
1659
1660},
1661
1662/**
1663* @method _subscribeToItemEvents
1664* @description Subscribes a menu to a menu item's event.
1665* @private
1666* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
1667* instance whose events should be subscribed to.
1668*/
1669_subscribeToItemEvents: function(p_oItem) {
1670
1671 p_oItem.focusEvent.subscribe(this._onMenuItemFocus, p_oItem, this);
1672
1673 p_oItem.blurEvent.subscribe(this._onMenuItemBlur, this, true);
1674
1675 p_oItem.cfg.configChangedEvent.subscribe(
1676 this._onMenuItemConfigChange,
1677 p_oItem,
1678 this
1679 );
1680
1681},
1682
1683/**
1684* @method _getOffsetWidth
1685* @description Returns the offset width of the menu's
1686* <code>&#60;div&#62;</code> element.
1687* @private
1688*/
1689_getOffsetWidth: function() {
1690
1691 var oClone = this.element.cloneNode(true);
1692
1693 Dom.setStyle(oClone, "width", "");
1694
1695 document.body.appendChild(oClone);
1696
1697 var sWidth = oClone.offsetWidth;
1698
1699 document.body.removeChild(oClone);
1700
1701 return sWidth;
1702
1703},
1704
1705/**
1706* @method _cancelHideDelay
1707* @description Cancels the call to "hideMenu."
1708* @private
1709*/
1710_cancelHideDelay: function() {
1711
1712 var oRoot = this.getRoot();
1713
1714 if(oRoot._nHideDelayId) {
1715
1716 window.clearTimeout(oRoot._nHideDelayId);
1717
1718 }
1719
1720},
1721
1722/**
1723* @method _execHideDelay
1724* @description Hides the menu after the number of milliseconds specified by
1725* the "hidedelay" configuration property.
1726* @private
1727*/
1728_execHideDelay: function() {
1729
1730 this._cancelHideDelay();
1731
1732 var oRoot = this.getRoot();
1733 var me = this;
1734
1735 var hideMenu = function() {
1736
1737 if(oRoot.activeItem) {
1738
1739 oRoot.clearActiveItem();
1740
1741 }
1742
1743 if(oRoot == me && me.cfg.getProperty("position") == "dynamic") {
1744
1745 me.hide();
1746
1747 }
1748
1749 };
1750
1751 oRoot._nHideDelayId =
1752 window.setTimeout(hideMenu, oRoot.cfg.getProperty("hidedelay"));
1753
1754},
1755
1756/**
1757* @method _cancelShowDelay
1758* @description Cancels the call to the "showMenu."
1759* @private
1760*/
1761_cancelShowDelay: function() {
1762
1763 var oRoot = this.getRoot();
1764
1765 if(oRoot._nShowDelayId) {
1766
1767 window.clearTimeout(oRoot._nShowDelayId);
1768
1769 }
1770
1771},
1772
1773/**
1774* @method _execShowDelay
1775* @description Shows the menu after the number of milliseconds specified by
1776* the "showdelay" configuration property have ellapsed.
1777* @private
1778* @param {YAHOO.widget.Menu} p_oMenu Object specifying the menu that should
1779* be made visible.
1780*/
1781_execShowDelay: function(p_oMenu) {
1782
1783 this._cancelShowDelay();
1784
1785 var oRoot = this.getRoot();
1786
1787 var showMenu = function() {
1788
1789 p_oMenu.show();
1790
1791 };
1792
1793 oRoot._nShowDelayId =
1794 window.setTimeout(showMenu, oRoot.cfg.getProperty("showdelay"));
1795
1796},
1797
1798// Protected methods
1799
1800/**
1801* @method _onMouseOver
1802* @description "mouseover" event handler for the menu.
1803* @protected
1804* @param {String} p_sType String representing the name of the event that
1805* was fired.
1806* @param {Array} p_aArgs Array of arguments sent when the event was fired.
1807* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
1808* fired the event.
1809*/
1810_onMouseOver: function(p_sType, p_aArgs, p_oMenu) {
1811
1812 var oEvent = p_aArgs[0];
1813 var oItem = p_aArgs[1];
1814 var oTarget = Event.getTarget(oEvent);
1815
1816 if(
1817 !this._bHandledMouseOverEvent &&
1818 (oTarget == this.element || Dom.isAncestor(this.element, oTarget))
1819 ) {
1820
1821 // MENU MOUSEOVER LOGIC HERE
1822
1823 this.clearActiveItem();
1824
1825 this._bHandledMouseOverEvent = true;
1826 this._bHandledMouseOutEvent = false;
1827
1828 }
1829
1830 if(
1831 oItem && !oItem.handledMouseOverEvent &&
1832 (oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget))
1833 ) {
1834
1835 var oItemCfg = oItem.cfg;
1836
1837 // Select and focus the current menu item
1838
1839 oItemCfg.setProperty("selected", true);
1840 oItem.focus();
1841
1842 if(this.cfg.getProperty("autosubmenudisplay")) {
1843
1844 // Show the submenu this menu item
1845
1846 var oSubmenu = oItemCfg.getProperty("submenu");
1847
1848 if(oSubmenu) {
1849
1850 if(this.cfg.getProperty("showdelay") > 0) {
1851
1852 this._execShowDelay(oSubmenu);
1853
1854 }
1855 else {
1856
1857 oSubmenu.show();
1858
1859 }
1860
1861 }
1862
1863 }
1864
1865 oItem.handledMouseOverEvent = true;
1866 oItem.handledMouseOutEvent = false;
1867
1868 }
1869
1870},
1871
1872/**
1873* @method _onMouseOut
1874* @description "mouseout" event handler for the menu.
1875* @protected
1876* @param {String} p_sType String representing the name of the event that
1877* was fired.
1878* @param {Array} p_aArgs Array of arguments sent when the event was fired.
1879* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
1880* fired the event.
1881*/
1882_onMouseOut: function(p_sType, p_aArgs, p_oMenu) {
1883
1884 var oEvent = p_aArgs[0];
1885 var oItem = p_aArgs[1];
1886 var oRelatedTarget = Event.getRelatedTarget(oEvent);
1887 var bMovingToSubmenu = false;
1888
1889 if(oItem) {
1890
1891 var oItemCfg = oItem.cfg;
1892 var oSubmenu = oItemCfg.getProperty("submenu");
1893
1894 if(
1895 oSubmenu &&
1896 (
1897 oRelatedTarget == oSubmenu.element ||
1898 Dom.isAncestor(oSubmenu.element, oRelatedTarget)
1899 )
1900 ) {
1901
1902 bMovingToSubmenu = true;
1903
1904 }
1905
1906 if(
1907 !oItem.handledMouseOutEvent &&
1908 (
1909 (
1910 oRelatedTarget != oItem.element &&
1911 !Dom.isAncestor(oItem.element, oRelatedTarget)
1912 ) || bMovingToSubmenu
1913 )
1914 ) {
1915
1916 if(this.cfg.getProperty("showdelay") > 0) {
1917
1918 this._cancelShowDelay();
1919
1920 }
1921
1922 if(!bMovingToSubmenu) {
1923
1924 oItemCfg.setProperty("selected", false);
1925
1926 }
1927
1928 if(this.cfg.getProperty("autosubmenudisplay")) {
1929
1930 if(oSubmenu) {
1931
1932 if(
1933 !(
1934 oRelatedTarget == oSubmenu.element ||
1935 YAHOO.util.Dom.isAncestor(
1936 oSubmenu.element,
1937 oRelatedTarget
1938 )
1939 )
1940 ) {
1941
1942 oSubmenu.hide();
1943
1944 }
1945
1946 }
1947
1948 }
1949
1950 oItem.handledMouseOutEvent = true;
1951 oItem.handledMouseOverEvent = false;
1952
1953 }
1954
1955 }
1956
1957 if(
1958 !this._bHandledMouseOutEvent &&
1959 (
1960 (
1961 oRelatedTarget != this.element &&
1962 !Dom.isAncestor(this.element, oRelatedTarget)
1963 )
1964 || bMovingToSubmenu
1965 )
1966 ) {
1967
1968 this._bHandledMouseOutEvent = true;
1969 this._bHandledMouseOverEvent = false;
1970
1971 }
1972
1973},
1974
1975/**
1976* @method _onClick
1977* @description "click" event handler for the menu.
1978* @protected
1979* @param {String} p_sType String representing the name of the event that
1980* was fired.
1981* @param {Array} p_aArgs Array of arguments sent when the event was fired.
1982* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
1983* fired the event.
1984*/
1985_onClick: function(p_sType, p_aArgs, p_oMenu) {
1986
1987 var oEvent = p_aArgs[0];
1988 var oItem = p_aArgs[1];
1989 var oTarget = Event.getTarget(oEvent);
1990
1991 if(oItem) {
1992
1993 var oItemCfg = oItem.cfg;
1994 var oSubmenu = oItemCfg.getProperty("submenu");
1995
1996 /*
1997 ACCESSIBILITY FEATURE FOR SCREEN READERS:
1998 Expand/collapse the submenu when the user clicks
1999 on the submenu indicator image.
2000 */
2001
2002 if(oTarget == oItem.submenuIndicator && oSubmenu) {
2003
2004 if(oSubmenu.cfg.getProperty("visible")) {
2005
2006 oSubmenu.hide();
2007
2008 }
2009 else {
2010
2011 this.clearActiveItem();
2012
2013 this.activeItem = oItem;
2014
2015 oItem.cfg.setProperty("selected", true);
2016
2017 oSubmenu.show();
2018
2019 }
2020
2021 }
2022 else {
2023
2024 var sURL = oItemCfg.getProperty("url");
2025 var bCurrentPageURL = (sURL.substr((sURL.length-1),1) == "#");
2026 var sTarget = oItemCfg.getProperty("target");
2027 var bHasTarget = (sTarget && sTarget.length > 0);
2028
2029 /*
2030 Prevent the browser from following links
2031 equal to "#"
2032 */
2033
2034 if(
2035 oTarget.tagName.toUpperCase() == "A" &&
2036 bCurrentPageURL && !bHasTarget
2037 ) {
2038
2039 Event.preventDefault(oEvent);
2040
2041 }
2042
2043 if(
2044 oTarget.tagName.toUpperCase() != "A" &&
2045 !bCurrentPageURL && !bHasTarget
2046 ) {
2047
2048 /*
2049 Follow the URL of the item regardless of
2050 whether or not the user clicked specifically
2051 on the anchor element.
2052 */
2053
2054 document.location = sURL;
2055
2056 }
2057
2058 /*
2059 If the item doesn't navigate to a URL and it doesn't have
2060 a submenu, then collapse the menu tree.
2061 */
2062
2063 if(bCurrentPageURL && !oSubmenu) {
2064
2065 var oRoot = this.getRoot();
2066
2067 if(oRoot.cfg.getProperty("position") == "static") {
2068
2069 oRoot.clearActiveItem();
2070
2071 }
2072 else {
2073
2074 oRoot.hide();
2075
2076 }
2077
2078 }
2079
2080 }
2081
2082 }
2083
2084},
2085
2086/**
2087* @method _onKeyDown
2088* @description "keydown" event handler for the menu.
2089* @protected
2090* @param {String} p_sType String representing the name of the event that
2091* was fired.
2092* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2093* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
2094* fired the event.
2095*/
2096_onKeyDown: function(p_sType, p_aArgs, p_oMenu) {
2097
2098 var oEvent = p_aArgs[0];
2099 var oItem = p_aArgs[1];
2100 var oSubmenu;
2101
2102 if(oItem) {
2103
2104 var oItemCfg = oItem.cfg;
2105 var oParentItem = this.parent;
2106 var oRoot;
2107 var oNextItem;
2108
2109 switch(oEvent.keyCode) {
2110
2111 case 38: // Up arrow
2112 case 40: // Down arrow
2113
2114 if(
2115 oItem == this.activeItem &&
2116 !oItemCfg.getProperty("selected")
2117 ) {
2118
2119 oItemCfg.setProperty("selected", true);
2120
2121 }
2122 else {
2123
2124 oNextItem = (oEvent.keyCode == 38) ?
2125 oItem.getPreviousEnabledSibling() :
2126 oItem.getNextEnabledSibling();
2127
2128 if(oNextItem) {
2129
2130 this.clearActiveItem();
2131
2132 oNextItem.cfg.setProperty("selected", true);
2133 oNextItem.focus();
2134
2135 }
2136
2137 }
2138
2139 Event.preventDefault(oEvent);
2140
2141 break;
2142
2143
2144 case 39: // Right arrow
2145
2146 oSubmenu = oItemCfg.getProperty("submenu");
2147
2148 if(oSubmenu) {
2149
2150 if(!oItemCfg.getProperty("selected")) {
2151
2152 oItemCfg.setProperty("selected", true);
2153
2154 }
2155
2156 oSubmenu.show();
2157
2158 oSubmenu.setInitialSelection();
2159
2160 }
2161 else {
2162
2163 oRoot = this.getRoot();
2164
2165 if(oRoot instanceof YAHOO.widget.MenuBar) {
2166
2167 oNextItem = oRoot.activeItem.getNextEnabledSibling();
2168
2169 if(oNextItem) {
2170
2171 oRoot.clearActiveItem();
2172
2173 oNextItem.cfg.setProperty("selected", true);
2174
2175 oSubmenu = oNextItem.cfg.getProperty("submenu");
2176
2177 if(oSubmenu) {
2178
2179 oSubmenu.show();
2180
2181 }
2182
2183 oNextItem.focus();
2184
2185 }
2186
2187 }
2188
2189 }
2190
2191
2192 Event.preventDefault(oEvent);
2193
2194 break;
2195
2196
2197 case 37: // Left arrow
2198
2199 if(oParentItem) {
2200
2201 var oParentMenu = oParentItem.parent;
2202
2203 if(oParentMenu instanceof YAHOO.widget.MenuBar) {
2204
2205 oNextItem =
2206 oParentMenu.activeItem.getPreviousEnabledSibling();
2207
2208 if(oNextItem) {
2209
2210 oParentMenu.clearActiveItem();
2211
2212 oNextItem.cfg.setProperty("selected", true);
2213
2214 oSubmenu = oNextItem.cfg.getProperty("submenu");
2215
2216 if(oSubmenu) {
2217
2218 oSubmenu.show();
2219
2220 }
2221
2222 oNextItem.focus();
2223
2224 }
2225
2226 }
2227 else {
2228
2229 this.hide();
2230
2231 oParentItem.focus();
2232
2233 }
2234
2235 }
2236
2237 Event.preventDefault(oEvent);
2238
2239 break;
2240
2241 }
2242
2243 }
2244
2245 if(oEvent.keyCode == 27) { // Esc key
2246
2247 if(this.cfg.getProperty("position") == "dynamic") {
2248
2249 this.hide();
2250
2251 if(this.parent) {
2252
2253 this.parent.focus();
2254
2255 }
2256
2257 }
2258 else if(this.activeItem) {
2259
2260 oSubmenu = this.activeItem.cfg.getProperty("submenu");
2261
2262 if(oSubmenu && oSubmenu.cfg.getProperty("visible")) {
2263
2264 oSubmenu.hide();
2265 this.activeItem.focus();
2266
2267 }
2268 else {
2269
2270 this.activeItem.cfg.setProperty("selected", false);
2271 this.activeItem.blur();
2272
2273 }
2274
2275 }
2276
2277 Event.preventDefault(oEvent);
2278
2279 }
2280
2281},
2282
2283// Private methods
2284
2285/**
2286* @method _onInit
2287* @description "init" event handler for the menu.
2288* @private
2289* @param {String} p_sType String representing the name of the event that
2290* was fired.
2291* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2292* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
2293* fired the event.
2294*/
2295_onInit: function(p_sType, p_aArgs, p_oMenu) {
2296
2297 if(
2298 (
2299 (this.parent && !this.lazyLoad) ||
2300 (!this.parent && this.cfg.getProperty("position") == "static") ||
2301 (
2302 !this.parent &&
2303 !this.lazyLoad &&
2304 this.cfg.getProperty("position") == "dynamic"
2305 )
2306 ) &&
2307 this.getItemGroups().length === 0
2308 ) {
2309
2310 if(this.srcElement) {
2311
2312 this._initSubTree();
2313
2314 }
2315
2316 if(this.itemData) {
2317
2318 this.addItems(this.itemData);
2319
2320 }
2321
2322 }
2323 else if(this.lazyLoad) {
2324
2325 this.cfg.fireQueue();
2326
2327 }
2328
2329},
2330
2331/**
2332* @method _onBeforeRender
2333* @description "beforerender" event handler for the menu. Appends all of the
2334* <code>&#60;ul&#62;</code>, <code>&#60;li&#62;</code> and their accompanying
2335* title elements to the body element of the menu.
2336* @private
2337* @param {String} p_sType String representing the name of the event that
2338* was fired.
2339* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2340* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
2341* fired the event.
2342*/
2343_onBeforeRender: function(p_sType, p_aArgs, p_oMenu) {
2344
2345 var oConfig = this.cfg;
2346 var oEl = this.element;
2347 var nListElements = this._aListElements.length;
2348
2349 if(nListElements > 0) {
2350
2351 var i = 0;
2352 var bFirstList = true;
2353 var oUL;
2354 var oGroupTitle;
2355
2356 do {
2357
2358 oUL = this._aListElements[i];
2359
2360 if(oUL) {
2361
2362 if(bFirstList) {
2363
2364 Dom.addClass(oUL, "first-of-type");
2365 bFirstList = false;
2366
2367 }
2368
2369 if(!Dom.isAncestor(oEl, oUL)) {
2370
2371 this.appendToBody(oUL);
2372
2373 }
2374
2375 oGroupTitle = this._aGroupTitleElements[i];
2376
2377 if(oGroupTitle) {
2378
2379 if(!Dom.isAncestor(oEl, oGroupTitle)) {
2380
2381 oUL.parentNode.insertBefore(oGroupTitle, oUL);
2382
2383 }
2384
2385 Dom.addClass(oUL, "hastitle");
2386
2387 }
2388
2389 }
2390
2391 i++;
2392
2393 }
2394 while(i < nListElements);
2395
2396 }
2397
2398},
2399
2400/**
2401* @method _onRender
2402* @description "render" event handler for the menu.
2403* @private
2404* @param {String} p_sType String representing the name of the event that
2405* was fired.
2406* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2407* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
2408* fired the event.
2409*/
2410_onRender: function(p_sType, p_aArgs, p_oMenu) {
2411
2412 if(this.cfg.getProperty("position") == "dynamic") {
2413
2414 var sWidth =
2415 this.element.parentNode.tagName.toUpperCase() == "BODY" ?
2416 this.element.offsetWidth : this._getOffsetWidth();
2417
2418 this.cfg.setProperty("width", (sWidth + "px"));
2419
2420 }
2421
2422},
2423
2424/**
2425* @method _onBeforeShow
2426* @description "beforeshow" event handler for the menu.
2427* @private
2428* @param {String} p_sType String representing the name of the event that
2429* was fired.
2430* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2431* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
2432* fired the event.
2433*/
2434_onBeforeShow: function(p_sType, p_aArgs, p_oMenu) {
2435
2436 if(this.lazyLoad && this.getItemGroups().length === 0) {
2437
2438 if(this.srcElement) {
2439
2440 this._initSubTree();
2441
2442 }
2443
2444 if(this.itemData) {
2445
2446 if(
2447 this.parent && this.parent.parent &&
2448 this.parent.parent.srcElement &&
2449 this.parent.parent.srcElement.tagName.toUpperCase() == "SELECT"
2450 ) {
2451
2452 var nOptions = this.itemData.length;
2453
2454 for(var n=0; n<nOptions; n++) {
2455
2456 if(this.itemData[n].tagName) {
2457
2458 this.addItem((new this.ITEM_TYPE(this.itemData[n])));
2459
2460 }
2461
2462 }
2463
2464 }
2465 else {
2466
2467 this.addItems(this.itemData);
2468
2469 }
2470
2471 }
2472
2473 if(this.srcElement) {
2474
2475 this.render();
2476
2477 }
2478 else {
2479
2480 if(this.parent) {
2481
2482 this.render(this.parent.element);
2483
2484 }
2485 else {
2486
2487 this.render(this.cfg.getProperty("container"));
2488
2489 }
2490
2491 }
2492
2493 }
2494
2495},
2496
2497/**
2498* @method _onShow
2499* @description "show" event handler for the menu.
2500* @private
2501* @param {String} p_sType String representing the name of the event that
2502* was fired.
2503* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2504* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that fired
2505* the event.
2506*/
2507_onShow: function(p_sType, p_aArgs, p_oMenu) {
2508
2509 this.setInitialFocus();
2510
2511 var oParent = this.parent;
2512
2513 if(oParent) {
2514
2515 var oParentMenu = oParent.parent;
2516
2517 var aParentAlignment = oParentMenu.cfg.getProperty("submenualignment");
2518 var aAlignment = this.cfg.getProperty("submenualignment");
2519
2520 if(
2521 (aParentAlignment[0] != aAlignment[0]) &&
2522 (aParentAlignment[1] != aAlignment[1])
2523 ) {
2524
2525 this.cfg.setProperty(
2526 "submenualignment",
2527 [ aParentAlignment[0], aParentAlignment[1] ]
2528 );
2529
2530 }
2531
2532 if(
2533 !oParentMenu.cfg.getProperty("autosubmenudisplay") &&
2534 oParentMenu.cfg.getProperty("position") == "static"
2535 ) {
2536
2537 oParentMenu.cfg.setProperty("autosubmenudisplay", true);
2538
2539 /**
2540 * "click" event handler for the document
2541 * @private
2542 * @param {Event} p_oEvent Object reference for the DOM event object
2543 * passed back by the event utility (YAHOO.util.Event).
2544 */
2545 var disableAutoSubmenuDisplay = function(p_oEvent) {
2546
2547 if(
2548 p_oEvent.type == "mousedown" ||
2549 (p_oEvent.type == "keydown" && p_oEvent.keyCode == 27)
2550 ) {
2551
2552 /*
2553 Set the "autosubmenudisplay" to "false" if the user
2554 clicks outside the menu bar.
2555 */
2556
2557 var oTarget = Event.getTarget(p_oEvent);
2558
2559 if(
2560 oTarget != oParentMenu.element ||
2561 !YAHOO.util.Dom.isAncestor(oParentMenu.element, oTarget)
2562 ) {
2563
2564 oParentMenu.cfg.setProperty(
2565 "autosubmenudisplay",
2566 false
2567 );
2568
2569 Event.removeListener(
2570 document,
2571 "mousedown",
2572 disableAutoSubmenuDisplay
2573 );
2574
2575 Event.removeListener(
2576 document,
2577 "keydown",
2578 disableAutoSubmenuDisplay
2579 );
2580
2581 }
2582
2583 }
2584
2585 };
2586
2587 Event.addListener(document, "mousedown", disableAutoSubmenuDisplay);
2588 Event.addListener(document, "keydown", disableAutoSubmenuDisplay);
2589
2590 }
2591
2592 }
2593
2594},
2595
2596/**
2597* @method _onBeforeHide
2598* @description "beforehide" event handler for the menu.
2599* @private
2600* @param {String} p_sType String representing the name of the event that
2601* was fired.
2602* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2603* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that fired
2604* the event.
2605*/
2606_onBeforeHide: function(p_sType, p_aArgs, p_oMenu) {
2607
2608 this.clearActiveItem(true);
2609
2610},
2611
2612/**
2613* @method _onParentMenuConfigChange
2614* @description "configchange" event handler for a submenu.
2615* @private
2616* @param {String} p_sType String representing the name of the event that
2617* was fired.
2618* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2619* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that
2620* subscribed to the event.
2621*/
2622_onParentMenuConfigChange: function(p_sType, p_aArgs, p_oSubmenu) {
2623
2624 var sPropertyName = p_aArgs[0][0];
2625 var oPropertyValue = p_aArgs[0][1];
2626
2627 switch(sPropertyName) {
2628
2629 case "iframe":
2630 case "constraintoviewport":
2631 case "hidedelay":
2632 case "showdelay":
2633 case "clicktohide":
2634 case "effect":
2635
2636 p_oSubmenu.cfg.setProperty(sPropertyName, oPropertyValue);
2637
2638 break;
2639
2640 }
2641
2642},
2643
2644/**
2645* @method _onParentMenuRender
2646* @description "render" event handler for a submenu. Renders a
2647* submenu in response to the firing of its parent's "render" event.
2648* @private
2649* @param {String} p_sType String representing the name of the event that
2650* was fired.
2651* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2652* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that
2653* subscribed to the event.
2654*/
2655_onParentMenuRender: function(p_sType, p_aArgs, p_oSubmenu) {
2656
2657 /*
2658 Set the "constraintoviewport" configuration
2659 property to match the parent Menu
2660 */
2661
2662 var oParentMenu = p_oSubmenu.parent.parent;
2663
2664 var oConfig = {
2665
2666 constraintoviewport:
2667 oParentMenu.cfg.getProperty("constraintoviewport"),
2668
2669 xy: [0,0],
2670
2671 clicktohide:
2672 oParentMenu.cfg.getProperty("clicktohide"),
2673
2674 effect:
2675 oParentMenu.cfg.getProperty("effect")
2676
2677 };
2678
2679 var nShowDelay = oParentMenu.cfg.getProperty("showdelay");
2680
2681 if(nShowDelay > 0) {
2682
2683 oConfig.showdelay = nShowDelay;
2684
2685 }
2686
2687 var nHideDelay = oParentMenu.cfg.getProperty("hidedelay");
2688
2689 if(nHideDelay > 0) {
2690
2691 oConfig.hidedelay = nHideDelay;
2692
2693 }
2694
2695 /*
2696 Only sync the "iframe" configuration property if the parent
2697 menu's "position" configuration is the same.
2698 */
2699
2700 if(
2701 this.cfg.getProperty("position") ==
2702 oParentMenu.cfg.getProperty("position")
2703 ) {
2704
2705 oConfig.iframe = oParentMenu.cfg.getProperty("iframe");
2706
2707 }
2708
2709
2710 p_oSubmenu.cfg.applyConfig(oConfig);
2711
2712 if(!this.lazyLoad) {
2713
2714 if(Dom.inDocument(this.element)) {
2715
2716 this.render();
2717
2718 }
2719 else {
2720
2721 this.render(this.parent.element);
2722
2723 }
2724
2725 }
2726
2727},
2728
2729/**
2730* @method _onSubmenuBeforeShow
2731* @description "beforeshow" event handler for a submenu.
2732* @private
2733* @param {String} p_sType String representing the name of the event that
2734* was fired.
2735* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2736* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that
2737* subscribed to the event.
2738*/
2739_onSubmenuBeforeShow: function(p_sType, p_aArgs, p_oSubmenu) {
2740
2741 var oParent = this.parent;
2742 var aAlignment = oParent.parent.cfg.getProperty("submenualignment");
2743
2744 this.cfg.setProperty(
2745 "context",
2746 [oParent.element, aAlignment[0], aAlignment[1]]
2747 );
2748
2749 oParent.submenuIndicator.alt = oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT;
2750
2751},
2752
2753/**
2754* @method _onSubmenuShow
2755* @description "show" event handler for a submenu.
2756* @private
2757* @param {String} p_sType String representing the name of the event that
2758* was fired.
2759* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2760* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that
2761* subscribed to the event.
2762*/
2763_onSubmenuShow: function(p_sType, p_aArgs, p_oSubmenu) {
2764
2765 var oParent = this.parent;
2766
2767 oParent.submenuIndicator.alt = oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT;
2768
2769},
2770
2771/**
2772* @method _onSubmenuHide
2773* @description "hide" Custom Event handler for a submenu.
2774* @private
2775* @param {String} p_sType String representing the name of the event that
2776* was fired.
2777* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2778* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that
2779* subscribed to the event.
2780*/
2781_onSubmenuHide: function(p_sType, p_aArgs, p_oSubmenu) {
2782
2783 var oParent = this.parent;
2784
2785 oParent.submenuIndicator.alt = oParent.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;
2786
2787},
2788
2789/**
2790* @method _onMenuItemFocus
2791* @description "focus" event handler for the menu's items.
2792* @private
2793* @param {String} p_sType String representing the name of the event that
2794* was fired.
2795* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2796* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
2797* that fired the event.
2798*/
2799_onMenuItemFocus: function(p_sType, p_aArgs, p_oItem) {
2800
2801 this.activeItem = p_oItem;
2802
2803},
2804
2805/**
2806* @method _onMenuItemBlur
2807* @description "blur" event handler for the menu's items.
2808* @private
2809* @param {String} p_sType String representing the name of the event
2810* that was fired.
2811* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2812*/
2813_onMenuItemBlur: function(p_sType, p_aArgs) {
2814
2815 this.activeItem = null;
2816
2817},
2818
2819/**
2820* @method _onMenuItemConfigChange
2821* @description "configchange" event handler for the menu's items.
2822* @private
2823* @param {String} p_sType String representing the name of the event that
2824* was fired.
2825* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2826* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
2827* that fired the event.
2828*/
2829_onMenuItemConfigChange: function(p_sType, p_aArgs, p_oItem) {
2830
2831 var sProperty = p_aArgs[0][0];
2832
2833 switch(sProperty) {
2834
2835 case "submenu":
2836
2837 var oSubmenu = p_aArgs[0][1];
2838
2839 if(oSubmenu) {
2840
2841 this._configureSubmenu(p_oItem);
2842
2843 }
2844
2845 break;
2846
2847 case "text":
2848 case "helptext":
2849
2850 /*
2851 A change to an item's "text" or "helptext"
2852 configuration properties requires the width of the parent
2853 menu to be recalculated.
2854 */
2855
2856 if(this.element.style.width) {
2857
2858 var sWidth = this._getOffsetWidth() + "px";
2859
2860 Dom.setStyle(this.element, "width", sWidth);
2861
2862 }
2863
2864 break;
2865
2866 }
2867
2868},
2869
2870// Public event handlers for configuration properties
2871
2872/**
2873* @method enforceConstraints
2874* @description The default event handler executed when the moveEvent is fired,
2875* if the "constraintoviewport" configuration property is set to true.
2876* @param {String} type The name of the event that was fired.
2877* @param {Array} args Collection of arguments sent when the
2878* event was fired.
2879* @param {Array} obj Array containing the current Menu instance
2880* and the item that fired the event.
2881*/
2882enforceConstraints: function(type, args, obj) {
2883
2884 var oConfig = this.cfg;
2885
2886 var pos = args[0];
2887
2888 var x = pos[0];
2889 var y = pos[1];
2890
2891 var bod = document.getElementsByTagName('body')[0];
2892 var htm = document.getElementsByTagName('html')[0];
2893
2894 var bodyOverflow = Dom.getStyle(bod, "overflow");
2895 var htmOverflow = Dom.getStyle(htm, "overflow");
2896
2897 var offsetHeight = this.element.offsetHeight;
2898 var offsetWidth = this.element.offsetWidth;
2899
2900 var viewPortWidth = Dom.getClientWidth();
2901 var viewPortHeight = Dom.getClientHeight();
2902
2903 var scrollX = window.scrollX || document.body.scrollLeft;
2904 var scrollY = window.scrollY || document.body.scrollTop;
2905
2906 var topConstraint = scrollY + 10;
2907 var leftConstraint = scrollX + 10;
2908 var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10;
2909 var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10;
2910
2911 var aContext = oConfig.getProperty("context");
2912 var oContextElement = aContext ? aContext[0] : null;
2913
2914
2915 if (x < 10) {
2916
2917 x = leftConstraint;
2918
2919 } else if ((x + offsetWidth) > viewPortWidth) {
2920
2921 if(
2922 oContextElement &&
2923 ((x - oContextElement.offsetWidth) > offsetWidth)
2924 ) {
2925
2926 x = (x - (oContextElement.offsetWidth + offsetWidth));
2927
2928 }
2929 else {
2930
2931 x = rightConstraint;
2932
2933 }
2934
2935 }
2936
2937 if (y < 10) {
2938
2939 y = topConstraint;
2940
2941 } else if (y > bottomConstraint) {
2942
2943 if(oContextElement && (y > offsetHeight)) {
2944
2945 y = ((y + oContextElement.offsetHeight) - offsetHeight);
2946
2947 }
2948 else {
2949
2950 y = bottomConstraint;
2951
2952 }
2953
2954 }
2955
2956 oConfig.setProperty("x", x, true);
2957 oConfig.setProperty("y", y, true);
2958
2959},
2960
2961/**
2962* @method configVisible
2963* @description Event handler for when the "visible" configuration property
2964* the menu changes.
2965* @param {String} p_sType String representing the name of the event that
2966* was fired.
2967* @param {Array} p_aArgs Array of arguments sent when the event was fired.
2968* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
2969* fired the event.
2970*/
2971configVisible: function(p_sType, p_aArgs, p_oMenu) {
2972
2973 if(this.cfg.getProperty("position") == "dynamic") {
2974
2975 YAHOO.widget.Menu.superclass.configVisible.call(
2976 this,
2977 p_sType,
2978 p_aArgs,
2979 p_oMenu
2980 );
2981
2982 }
2983 else {
2984
2985 var bVisible = p_aArgs[0];
2986 var sDisplay = Dom.getStyle(this.element, "display");
2987
2988 if(bVisible) {
2989
2990 if(sDisplay != "block") {
2991 this.beforeShowEvent.fire();
2992 Dom.setStyle(this.element, "display", "block");
2993 this.showEvent.fire();
2994 }
2995
2996 }
2997 else {
2998
2999 if(sDisplay == "block") {
3000 this.beforeHideEvent.fire();
3001 Dom.setStyle(this.element, "display", "none");
3002 this.hideEvent.fire();
3003 }
3004
3005 }
3006
3007 }
3008
3009},
3010
3011/**
3012* @method configPosition
3013* @description Event handler for when the "position" configuration property
3014* of the menu changes.
3015* @param {String} p_sType String representing the name of the event that
3016* was fired.
3017* @param {Array} p_aArgs Array of arguments sent when the event was fired.
3018* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
3019* fired the event.
3020*/
3021configPosition: function(p_sType, p_aArgs, p_oMenu) {
3022
3023 var sCSSPosition = p_aArgs[0] == "static" ? "static" : "absolute";
3024 var oCfg = this.cfg;
3025
3026 Dom.setStyle(this.element, "position", sCSSPosition);
3027
3028 if(sCSSPosition == "static") {
3029
3030 /*
3031 Remove the iframe for statically positioned menus since it will
3032 intercept mouse events.
3033 */
3034
3035 oCfg.setProperty("iframe", false);
3036
3037 // Statically positioned menus are visible by default
3038
3039 Dom.setStyle(this.element, "display", "block");
3040
3041 oCfg.setProperty("visible", true);
3042
3043 }
3044 else {
3045
3046 /*
3047 Even though the "visible" property is queued to
3048 "false" by default, we need to set the "visibility" property to
3049 "hidden" since Overlay's "configVisible" implementation checks the
3050 element's "visibility" style property before deciding whether
3051 or not to show an Overlay instance.
3052 */
3053
3054 Dom.setStyle(this.element, "visibility", "hidden");
3055
3056 }
3057
3058 if(sCSSPosition == "absolute") {
3059
3060 var nZIndex = oCfg.getProperty("zindex");
3061
3062 if(!nZIndex || nZIndex === 0) {
3063
3064 nZIndex = this.parent ?
3065 (this.parent.parent.cfg.getProperty("zindex") + 1) : 1;
3066
3067 oCfg.setProperty("zindex", nZIndex);
3068
3069 }
3070
3071 }
3072
3073},
3074
3075/**
3076* @method configIframe
3077* @description Event handler for when the "iframe" configuration property of
3078* the menu changes.
3079* @param {String} p_sType String representing the name of the event that
3080* was fired.
3081* @param {Array} p_aArgs Array of arguments sent when the event was fired.
3082* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
3083* fired the event.
3084*/
3085configIframe: function(p_sType, p_aArgs, p_oMenu) {
3086
3087 if(this.cfg.getProperty("position") == "dynamic") {
3088
3089 YAHOO.widget.Menu.superclass.configIframe.call(
3090 this,
3091 p_sType,
3092 p_aArgs,
3093 p_oMenu
3094 );
3095
3096 }
3097
3098},
3099
3100/**
3101* @method configHideDelay
3102* @description Event handler for when the "hidedelay" configuration property
3103* of the menu changes.
3104* @param {String} p_sType String representing the name of the event that
3105* was fired.
3106* @param {Array} p_aArgs Array of arguments sent when the event was fired.
3107* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
3108* fired the event.
3109*/
3110configHideDelay: function(p_sType, p_aArgs, p_oMenu) {
3111
3112 var nHideDelay = p_aArgs[0];
3113 var oMouseOutEvent = this.mouseOutEvent;
3114 var oMouseOverEvent = this.mouseOverEvent;
3115 var oKeyDownEvent = this.keyDownEvent;
3116
3117 if(nHideDelay > 0) {
3118
3119 /*
3120 Only assign event handlers once. This way the user change
3121 the value for the hidedelay as many times as they want.
3122 */
3123
3124 if(!this._hideDelayEventHandlersAssigned) {
3125
3126 oMouseOutEvent.subscribe(this._execHideDelay, true);
3127 oMouseOverEvent.subscribe(this._cancelHideDelay, this, true);
3128 oKeyDownEvent.subscribe(this._cancelHideDelay, this, true);
3129
3130 this._hideDelayEventHandlersAssigned = true;
3131
3132 }
3133
3134 }
3135 else {
3136
3137 oMouseOutEvent.unsubscribe(this._execHideDelay, this);
3138 oMouseOverEvent.unsubscribe(this._cancelHideDelay, this);
3139 oKeyDownEvent.unsubscribe(this._cancelHideDelay, this);
3140
3141 this._hideDelayEventHandlersAssigned = false;
3142
3143 }
3144
3145},
3146
3147/**
3148* @method configContainer
3149* @description Event handler for when the "container" configuration property
3150of the menu changes.
3151* @param {String} p_sType String representing the name of the event that
3152* was fired.
3153* @param {Array} p_aArgs Array of arguments sent when the event was fired.
3154* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that
3155* fired the event.
3156*/
3157configContainer: function(p_sType, p_aArgs, p_oMenu) {
3158
3159 var oElement = p_aArgs[0];
3160
3161 if(typeof oElement == 'string') {
3162
3163 this.cfg.setProperty(
3164 "container",
3165 document.getElementById(oElement),
3166 true
3167 );
3168
3169 }
3170
3171},
3172
3173// Public methods
3174
3175/**
3176* Event handler called when the resize monitor element's "resize" evet is fired.
3177*/
3178onDomResize: function(e, obj) {
3179
3180 if(!this._handleResize) {
3181
3182 this._handleResize = true;
3183 return;
3184
3185 }
3186
3187 var oConfig = this.cfg;
3188
3189 if(oConfig.getProperty("position") == "dynamic") {
3190
3191 oConfig.setProperty("width", (this._getOffsetWidth() + "px"));
3192
3193 }
3194
3195 YAHOO.widget.Menu.superclass.onDomResize.call(this, e, obj);
3196
3197},
3198
3199/**
3200* @method initEvents
3201* @description Initializes the custom events for the menu.
3202*/
3203initEvents: function() {
3204
3205 YAHOO.widget.Menu.superclass.initEvents.call(this);
3206
3207 // Create custom events
3208
3209 var CustomEvent = YAHOO.util.CustomEvent;
3210
3211 this.mouseOverEvent = new CustomEvent("mouseOverEvent", this);
3212 this.mouseOutEvent = new CustomEvent("mouseOutEvent", this);
3213 this.mouseDownEvent = new CustomEvent("mouseDownEvent", this);
3214 this.mouseUpEvent = new CustomEvent("mouseUpEvent", this);
3215 this.clickEvent = new CustomEvent("clickEvent", this);
3216 this.keyPressEvent = new CustomEvent("keyPressEvent", this);
3217 this.keyDownEvent = new CustomEvent("keyDownEvent", this);
3218 this.keyUpEvent = new CustomEvent("keyUpEvent", this);
3219 this.itemAddedEvent = new CustomEvent("itemAddedEvent", this);
3220 this.itemRemovedEvent = new CustomEvent("itemRemovedEvent", this);
3221
3222},
3223
3224/**
3225* @method getRoot
3226* @description Finds the menu's root menu.
3227*/
3228getRoot: function() {
3229
3230 var oItem = this.parent;
3231
3232 if(oItem) {
3233
3234 var oParentMenu = oItem.parent;
3235
3236 return oParentMenu ? oParentMenu.getRoot() : this;
3237
3238 }
3239 else {
3240
3241 return this;
3242
3243 }
3244
3245},
3246
3247/**
3248* @method toString
3249* @description Returns a string representing the menu.
3250* @return {String}
3251*/
3252toString: function() {
3253
3254 return ("Menu " + this.id);
3255
3256},
3257
3258/**
3259* @method setItemGroupTitle
3260* @description Sets the title of a group of menu items.
3261* @param {String} p_sGroupTitle String specifying the title of the group.
3262* @param {Number} p_nGroupIndex Optional. Number specifying the group to which
3263* the title belongs.
3264*/
3265setItemGroupTitle: function(p_sGroupTitle, p_nGroupIndex) {
3266
3267 if(typeof p_sGroupTitle == "string" && p_sGroupTitle.length > 0) {
3268
3269 var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0;
3270 var oTitle = this._aGroupTitleElements[nGroupIndex];
3271
3272 if(oTitle) {
3273
3274 oTitle.innerHTML = p_sGroupTitle;
3275
3276 }
3277 else {
3278
3279 oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME);
3280
3281 oTitle.innerHTML = p_sGroupTitle;
3282
3283 this._aGroupTitleElements[nGroupIndex] = oTitle;
3284
3285 }
3286
3287 var i = this._aGroupTitleElements.length - 1;
3288 var nFirstIndex;
3289
3290 do {
3291
3292 if(this._aGroupTitleElements[i]) {
3293
3294 Dom.removeClass(this._aGroupTitleElements[i], "first-of-type");
3295
3296 nFirstIndex = i;
3297
3298 }
3299
3300 }
3301 while(i--);
3302
3303 if(nFirstIndex !== null) {
3304
3305 Dom.addClass(
3306 this._aGroupTitleElements[nFirstIndex],
3307 "first-of-type"
3308 );
3309
3310 }
3311
3312 }
3313
3314},
3315
3316/**
3317* @method addItem
3318* @description Appends an item to the menu.
3319* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
3320* instance to be added to the menu.
3321* @param {String} p_oItem String specifying the text of the item to be added
3322* to the menu.
3323* @param {Object} p_oItem Object literal containing a set of menu item
3324* configuration properties.
3325* @param {Number} p_nGroupIndex Optional. Number indicating the group to
3326* which the item belongs.
3327* @return {YAHOO.widget.MenuItem}
3328*/
3329addItem: function(p_oItem, p_nGroupIndex) {
3330
3331 if(p_oItem) {
3332
3333 return this._addItemToGroup(p_nGroupIndex, p_oItem);
3334
3335 }
3336
3337},
3338
3339/**
3340* @method addItems
3341* @description Adds an array of items to the menu.
3342* @param {Array} p_aItems Array of items to be added to the menu. The array
3343* can contain strings specifying the text for each item to be created, object
3344* literals specifying each of the menu item configuration properties,
3345* or MenuItem instances.
3346* @param {Number} p_nGroupIndex Optional. Number specifying the group to
3347* which the items belongs.
3348* @return {Array}
3349*/
3350addItems: function(p_aItems, p_nGroupIndex) {
3351
3352 function isArray(p_oValue) {
3353
3354 return (typeof p_oValue == "object" && p_oValue.constructor == Array);
3355
3356 }
3357
3358 if(isArray(p_aItems)) {
3359
3360 var nItems = p_aItems.length;
3361 var aItems = [];
3362 var oItem;
3363
3364 for(var i=0; i<nItems; i++) {
3365
3366 oItem = p_aItems[i];
3367
3368 if(isArray(oItem)) {
3369
3370 aItems[aItems.length] = this.addItems(oItem, i);
3371
3372 }
3373 else {
3374
3375 aItems[aItems.length] =
3376 this._addItemToGroup(p_nGroupIndex, oItem);
3377
3378 }
3379
3380 }
3381
3382 if(aItems.length) {
3383
3384 return aItems;
3385
3386 }
3387
3388 }
3389
3390},
3391
3392/**
3393* @method insertItem
3394* @description Inserts an item into the menu at the specified index.
3395* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem
3396* instance to be added to the menu.
3397* @param {String} p_oItem String specifying the text of the item to be added
3398* to the menu.
3399* @param {Object} p_oItem Object literal containing a set of menu item
3400* configuration properties.
3401* @param {Number} p_nItemIndex Number indicating the ordinal position at which
3402* the item should be added.
3403* @param {Number} p_nGroupIndex Optional. Number indicating the group to which
3404* the item belongs.
3405* @return {YAHOO.widget.MenuItem}
3406*/
3407insertItem: function(p_oItem, p_nItemIndex, p_nGroupIndex) {
3408
3409 if(p_oItem) {
3410
3411 return this._addItemToGroup(p_nGroupIndex, p_oItem, p_nItemIndex);
3412
3413 }
3414
3415},
3416
3417/**
3418* @method removeItem
3419* @description Removes the specified item from the menu.
3420* @param {YAHOO.widget.MenuItem} p_oObject Object reference for the MenuItem
3421* instance to be removed from the menu.
3422* @param {Number} p_oObject Number specifying the index of the item
3423* to be removed.
3424* @param {Number} p_nGroupIndex Optional. Number specifying the group to
3425* which the item belongs.
3426* @return {YAHOO.widget.MenuItem}
3427*/
3428removeItem: function(p_oObject, p_nGroupIndex) {
3429
3430 if(typeof p_oObject != "undefined") {
3431
3432 var oItem;
3433
3434 if(p_oObject instanceof YAHOO.widget.MenuItem) {
3435
3436 oItem = this._removeItemFromGroupByValue(p_nGroupIndex, p_oObject);
3437
3438 }
3439 else if(typeof p_oObject == "number") {
3440
3441 oItem = this._removeItemFromGroupByIndex(p_nGroupIndex, p_oObject);
3442
3443 }
3444
3445 if(oItem) {
3446
3447 oItem.destroy();
3448
3449 return oItem;
3450
3451 }
3452
3453 }
3454
3455},
3456
3457/**
3458* @method getItemGroups
3459* @description Returns a multi-dimensional array of all of the items in the menu.
3460* @return {Array}
3461*/
3462getItemGroups: function() {
3463
3464 return this._aItemGroups;
3465
3466},
3467
3468/**
3469* @method getItem
3470* @description Returns the item at the specified index.
3471* @param {Number} p_nItemIndex Number indicating the ordinal position of the
3472* item to be retrieved.
3473* @param {Number} p_nGroupIndex Optional. Number indicating the group to which
3474* the item belongs.
3475* @return {YAHOO.widget.MenuItem}
3476*/
3477getItem: function(p_nItemIndex, p_nGroupIndex) {
3478
3479 if(typeof p_nItemIndex == "number") {
3480
3481 var aGroup = this._getItemGroup(p_nGroupIndex);
3482
3483 if(aGroup) {
3484
3485 return aGroup[p_nItemIndex];
3486
3487 }
3488
3489 }
3490
3491},
3492
3493/**
3494* @method destroy
3495* @description Removes the menu's <code>&#60;div&#62;</code> element
3496* (and accompanying child nodes) from the document.
3497*/
3498destroy: function() {
3499
3500 // Remove Custom Event listeners
3501
3502 this.mouseOverEvent.unsubscribeAll();
3503 this.mouseOutEvent.unsubscribeAll();
3504 this.mouseDownEvent.unsubscribeAll();
3505 this.mouseUpEvent.unsubscribeAll();
3506 this.clickEvent.unsubscribeAll();
3507 this.keyPressEvent.unsubscribeAll();
3508 this.keyDownEvent.unsubscribeAll();
3509 this.keyUpEvent.unsubscribeAll();
3510
3511 var nItemGroups = this._aItemGroups.length;
3512 var nItems;
3513 var oItemGroup;
3514 var oItem;
3515 var i;
3516 var n;
3517
3518 // Remove all items
3519
3520 if(nItemGroups > 0) {
3521
3522 i = nItemGroups - 1;
3523
3524 do {
3525
3526 oItemGroup = this._aItemGroups[i];
3527
3528 if(oItemGroup) {
3529
3530 nItems = oItemGroup.length;
3531
3532 if(nItems > 0) {
3533
3534 n = nItems - 1;
3535
3536 do {
3537
3538 oItem = this._aItemGroups[i][n];
3539
3540 if(oItem) {
3541
3542 oItem.destroy();
3543 }
3544
3545 }
3546 while(n--);
3547
3548 }
3549
3550 }
3551
3552 }
3553 while(i--);
3554
3555 }
3556
3557 // Continue with the superclass implementation of this method
3558
3559 YAHOO.widget.Menu.superclass.destroy.call(this);
3560
3561
3562},
3563
3564/**
3565* @method setInitialFocus
3566* @description Sets focus to the menu's first enabled item.
3567*/
3568setInitialFocus: function() {
3569
3570 var oItem = this._getFirstEnabledItem();
3571
3572 if(oItem) {
3573
3574 oItem.focus();
3575 }
3576
3577},
3578
3579/**
3580* @method setInitialSelection
3581* @description Sets the "selected" configuration property of the menu's first
3582* enabled item to "true."
3583*/
3584setInitialSelection: function() {
3585
3586 var oItem = this._getFirstEnabledItem();
3587
3588 if(oItem) {
3589
3590 oItem.cfg.setProperty("selected", true);
3591 }
3592
3593},
3594
3595/**
3596* @method clearActiveItem
3597* @description Sets the "selected" configuration property of the menu's active
3598* item to "false" and hides the item's submenu.
3599* @param {Boolean} p_bBlur Boolean indicating if the menu's active item
3600* should be blurred.
3601*/
3602clearActiveItem: function(p_bBlur) {
3603
3604 if(this.cfg.getProperty("showdelay") > 0) {
3605
3606 this._cancelShowDelay();
3607
3608 }
3609
3610 var oActiveItem = this.activeItem;
3611
3612 if(oActiveItem) {
3613
3614 var oConfig = oActiveItem.cfg;
3615
3616 oConfig.setProperty("selected", false);
3617
3618 var oSubmenu = oConfig.getProperty("submenu");
3619
3620 if(oSubmenu) {
3621
3622 oSubmenu.hide();
3623
3624 }
3625
3626 if(p_bBlur) {
3627
3628 oActiveItem.blur();
3629
3630 }
3631
3632 }
3633
3634},
3635
3636/**
3637* @description Initializes the class's configurable properties which can be
3638* changed using the menu's Config object ("cfg").
3639* @method initDefaultConfig
3640*/
3641initDefaultConfig: function() {
3642
3643 YAHOO.widget.Menu.superclass.initDefaultConfig.call(this);
3644
3645 var oConfig = this.cfg;
3646
3647 // Add configuration properties
3648
3649 /*
3650 Change the default value for the "visible" configuration
3651 property to "false" by re-adding the property.
3652 */
3653
3654 /**
3655 * @config visible
3656 * @description Boolean indicating whether or not the menu is visible. If
3657 * the menu's "position" configuration property is set to "dynamic" (the
3658 * default), this property toggles the menu's <code>&#60;div&#62;</code>
3659 * element's "visibility" style property between "visible" (true) or
3660 * "hidden" (false). If the menu's "position" configuration property is
3661 * set to "static" this property toggles the menu's
3662 * <code>&#60;div&#62;</code> element's "display" style property
3663 * between "block" (true) or "none" (false).
3664 * @default false
3665 * @type Boolean
3666 */
3667 oConfig.addProperty(
3668 "visible",
3669 {
3670 value:false,
3671 handler:this.configVisible,
3672 validator:this.cfg.checkBoolean
3673 }
3674 );
3675
3676 /*
3677 Change the default value for the "constraintoviewport" configuration
3678 property to "true" by re-adding the property.
3679 */
3680
3681 /**
3682 * @config constraintoviewport
3683 * @description Boolean indicating if the menu will try to remain inside
3684 * the boundaries of the size of viewport.
3685 * @default true
3686 * @type Boolean
3687 */
3688 oConfig.addProperty(
3689 "constraintoviewport",
3690 {
3691 value:true,
3692 handler:this.configConstrainToViewport,
3693 validator:this.cfg.checkBoolean,
3694 supercedes:["iframe","x","y","xy"]
3695 }
3696 );
3697
3698 /**
3699 * @config position
3700 * @description String indicating how a menu should be positioned on the
3701 * screen. Possible values are "static" and "dynamic." Static menus are
3702 * visible by default and reside in the normal flow of the document
3703 * (CSS position: static). Dynamic menus are hidden by default, reside
3704 * out of the normal flow of the document (CSS position: absolute), and
3705 * can overlay other elements on the screen.
3706 * @default dynamic
3707 * @type String
3708 */
3709 oConfig.addProperty(
3710 "position",
3711 {
3712 value: "dynamic",
3713 handler: this.configPosition,
3714 validator: this._checkPosition,
3715 supercedes: ["visible"]
3716 }
3717 );
3718
3719 /**
3720 * @config submenualignment
3721 * @description Array defining how submenus should be aligned to their
3722 * parent menu item. The format is: [itemCorner, submenuCorner]. By default
3723 * a submenu's top left corner is aligned to its parent menu item's top
3724 * right corner.
3725 * @default ["tl","tr"]
3726 * @type Array
3727 */
3728 oConfig.addProperty("submenualignment", { value: ["tl","tr"] } );
3729
3730 /**
3731 * @config autosubmenudisplay
3732 * @description Boolean indicating if submenus are automatically made
3733 * visible when the user mouses over the menu's items.
3734 * @default true
3735 * @type Boolean
3736 */
3737 oConfig.addProperty(
3738 "autosubmenudisplay",
3739 {
3740 value: true,
3741 validator: oConfig.checkBoolean
3742 }
3743 );
3744
3745 /**
3746 * @config showdelay
3747 * @description Number indicating the time (in milliseconds) that should
3748 * expire before a submenu is made visible when the user mouses over
3749 * the menu's items.
3750 * @default 0
3751 * @type Number
3752 */
3753 oConfig.addProperty(
3754 "showdelay",
3755 {
3756 value: 0,
3757 validator: oConfig.checkNumber
3758 }
3759 );
3760
3761 /**
3762 * @config hidedelay
3763 * @description Number indicating the time (in milliseconds) that should
3764 * expire before the menu is hidden.
3765 * @default 0
3766 * @type Number
3767 */
3768 oConfig.addProperty(
3769 "hidedelay",
3770 {
3771 value: 0,
3772 validator: oConfig.checkNumber,
3773 handler: this.configHideDelay,
3774 suppressEvent: true
3775 }
3776 );
3777
3778 /**
3779 * @config clicktohide
3780 * @description Boolean indicating if the menu will automatically be
3781 * hidden if the user clicks outside of it.
3782 * @default true
3783 * @type Boolean
3784 */
3785 oConfig.addProperty(
3786 "clicktohide",
3787 {
3788 value: true,
3789 validator: oConfig.checkBoolean
3790 }
3791 );
3792
3793 /**
3794 * @config container
3795 * @description HTML element reference or string specifying the id
3796 * attribute of the HTML element that the menu's markup should be rendered into.
3797 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
3798 * level-one-html.html#ID-58190037">HTMLElement</a>|String
3799 * @default document.body
3800 */
3801 this.cfg.addProperty(
3802 "container",
3803 { value:document.body, handler:this.configContainer }
3804 );
3805
3806}
3807
3808}); // END YAHOO.extend
3809
3810})();
3811
3812/**
3813* The base class for all menuing containers.
3814*
3815* @param {String} p_oElement String specifying the id attribute of the
3816* <code>&#60;div&#62;</code> element of the menu module.
3817* @param {String} p_oElement String specifying the id attribute of the
3818* <code>&#60;select&#62;</code> element to be used as the data source for the
3819* menu module.
3820* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929
3821* /level-one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object
3822* specifying the <code>&#60;div&#62;</code> element of the menu module.
3823* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
3824* one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object
3825* specifying the <code>&#60;select&#62;</code> element to be used as the data
3826* source for the menu module.
3827* @param {Object} p_oConfig Optional. Object literal specifying the
3828* configuration for the menu module. See configuration class documentation for
3829* more details.
3830* @class MenuModule
3831* @constructor
3832* @extends YAHOO.widget.Overlay
3833* @deprecated As of version 0.12, all MenuModule functionality has been
3834* implemented directly in YAHOO.widget.Menu, making YAHOO.widget.Menu the base
3835* class for all menuing containers.
3836*/
3837YAHOO.widget.MenuModule = YAHOO.widget.Menu;
3838
3839(function() {
3840
3841var Dom = YAHOO.util.Dom;
3842var Module = YAHOO.widget.Module;
3843var Menu = YAHOO.widget.Menu;
3844
3845/**
3846* Creates an item for a menu.
3847*
3848* @param {String} p_oObject String specifying the text of the menu item.
3849* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
3850* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying
3851* the <code>&#60;li&#62;</code> element of the menu item.
3852* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
3853* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
3854* specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
3855* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
3856* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object
3857* specifying the <code>&#60;option&#62;</code> element of the menu item.
3858* @param {Object} p_oConfig Optional. Object literal specifying the
3859* configuration for the menu item. See configuration class documentation
3860* for more details.
3861* @class MenuItem
3862* @constructor
3863*/
3864YAHOO.widget.MenuItem = function(p_oObject, p_oConfig) {
3865
3866 if(p_oObject) {
3867
3868 if(p_oConfig) {
3869
3870 this.parent = p_oConfig.parent;
3871 this.value = p_oConfig.value;
3872
3873 }
3874
3875 this.init(p_oObject, p_oConfig);
3876
3877 }
3878
3879};
3880
3881YAHOO.widget.MenuItem.prototype = {
3882
3883 // Constants
3884
3885 /**
3886 * @property SUBMENU_INDICATOR_IMAGE_PATH
3887 * @description String representing the path to the image to be used for the
3888 * menu item's submenu arrow indicator.
3889 * @default "nt/ic/ut/alt1/menuarorght8_nrm_1.gif"
3890 * @final
3891 * @type String
3892 */
3893 SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarorght8_nrm_1.gif",
3894
3895 /**
3896 * @property SELECTED_SUBMENU_INDICATOR_IMAGE_PATH
3897 * @description String representing the path to the image to be used for the
3898 * submenu arrow indicator when the menu item is selected.
3899 * @default "nt/ic/ut/alt1/menuarorght8_hov_1.gif"
3900 * @final
3901 * @type String
3902 */
3903 SELECTED_SUBMENU_INDICATOR_IMAGE_PATH:
3904 "nt/ic/ut/alt1/menuarorght8_hov_1.gif",
3905
3906 /**
3907 * @property DISABLED_SUBMENU_INDICATOR_IMAGE_PATH
3908 * @description String representing the path to the image to be used for the
3909 * submenu arrow indicator when the menu item is disabled.
3910 * @default "nt/ic/ut/alt1/menuarorght8_dim_1.gif"
3911 * @final
3912 * @type String
3913 */
3914 DISABLED_SUBMENU_INDICATOR_IMAGE_PATH:
3915 "nt/ic/ut/alt1/menuarorght8_dim_1.gif",
3916
3917 /**
3918 * @property COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT
3919 * @description String representing the alt text for the image to be used
3920 * for the submenu arrow indicator.
3921 * @default "Collapsed. Click to expand."
3922 * @final
3923 * @type String
3924 */
3925 COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT: "Collapsed. Click to expand.",
3926
3927 /**
3928 * @property EXPANDED_SUBMENU_INDICATOR_ALT_TEXT
3929 * @description String representing the alt text for the image to be used
3930 * for the submenu arrow indicator when the submenu is visible.
3931 * @default "Expanded. Click to collapse."
3932 * @final
3933 * @type String
3934 */
3935 EXPANDED_SUBMENU_INDICATOR_ALT_TEXT: "Expanded. Click to collapse.",
3936
3937 /**
3938 * @property DISABLED_SUBMENU_INDICATOR_ALT_TEXT
3939 * @description String representing the alt text for the image to be used
3940 * for the submenu arrow indicator when the menu item is disabled.
3941 * @default "Disabled."
3942 * @final
3943 * @type String
3944 */
3945 DISABLED_SUBMENU_INDICATOR_ALT_TEXT: "Disabled.",
3946
3947 /**
3948 * @property CHECKED_IMAGE_PATH
3949 * @description String representing the path to the image to be used for
3950 * the checked state.
3951 * @default "nt/ic/ut/bsc/menuchk8_nrm_1.gif"
3952 * @final
3953 * @type String
3954 */
3955 CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_nrm_1.gif",
3956
3957
3958 /**
3959 * @property SELECTED_CHECKED_IMAGE_PATH
3960 * @description String representing the path to the image to be used for
3961 * the selected checked state.
3962 * @default "nt/ic/ut/bsc/menuchk8_hov_1.gif"
3963 * @final
3964 * @type String
3965 */
3966 SELECTED_CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_hov_1.gif",
3967
3968
3969 /**
3970 * @property DISABLED_CHECKED_IMAGE_PATH
3971 * @description String representing the path to the image to be used for
3972 * the disabled checked state.
3973 * @default "nt/ic/ut/bsc/menuchk8_dim_1.gif"
3974 * @final
3975 * @type String
3976 */
3977 DISABLED_CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_dim_1.gif",
3978
3979
3980 /**
3981 * @property CHECKED_IMAGE_ALT_TEXT
3982 * @description String representing the alt text for the image to be used
3983 * for the checked image.
3984 * @default "Checked."
3985 * @final
3986 * @type String
3987 */
3988 CHECKED_IMAGE_ALT_TEXT: "Checked.",
3989
3990
3991 /**
3992 * @property DISABLED_CHECKED_IMAGE_ALT_TEXT
3993 * @description String representing the alt text for the image to be used
3994 * for the checked image when the item is disabled.
3995 * @default "Checked. (Item disabled.)"
3996 * @final
3997 * @type String
3998 */
3999 DISABLED_CHECKED_IMAGE_ALT_TEXT: "Checked. (Item disabled.)",
4000
4001 /**
4002 * @property CSS_CLASS_NAME
4003 * @description String representing the CSS class(es) to be applied to the
4004 * <code>&#60;li&#62;</code> element of the menu item.
4005 * @default "yuimenuitem"
4006 * @final
4007 * @type String
4008 */
4009 CSS_CLASS_NAME: "yuimenuitem",
4010
4011 /**
4012 * @property SUBMENU_TYPE
4013 * @description Object representing the type of menu to instantiate and
4014 * add when parsing the child nodes of the menu item's source HTML element.
4015 * @final
4016 * @type YAHOO.widget.Menu
4017 */
4018 SUBMENU_TYPE: null,
4019
4020 /**
4021 * @property IMG_ROOT
4022 * @description String representing the prefix path to use for
4023 * non-secure images.
4024 * @default "http://us.i1.yimg.com/us.yimg.com/i/"
4025 * @type String
4026 */
4027 IMG_ROOT: "http://us.i1.yimg.com/us.yimg.com/i/",
4028
4029
4030 /**
4031 * @property IMG_ROOT_SSL
4032 * @description String representing the prefix path to use for securely
4033 * served images.
4034 * @default "https://a248.e.akamai.net/sec.yimg.com/i/"
4035 * @type String
4036 */
4037 IMG_ROOT_SSL: "https://a248.e.akamai.net/sec.yimg.com/i/",
4038
4039 // Private member variables
4040
4041 /**
4042 * @property _oAnchor
4043 * @description Object reference to the menu item's
4044 * <code>&#60;a&#62;</code> element.
4045 * @default null
4046 * @private
4047 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4048 * one-html.html#ID-48250443">HTMLAnchorElement</a>
4049 */
4050 _oAnchor: null,
4051
4052
4053 /**
4054 * @property _oText
4055 * @description Object reference to the menu item's text node.
4056 * @default null
4057 * @private
4058 * @type TextNode
4059 */
4060 _oText: null,
4061
4062
4063 /**
4064 * @property _oHelpTextEM
4065 * @description Object reference to the menu item's help text
4066 * <code>&#60;em&#62;</code> element.
4067 * @default null
4068 * @private
4069 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4070 * one-html.html#ID-58190037">HTMLElement</a>
4071 */
4072 _oHelpTextEM: null,
4073
4074
4075 /**
4076 * @property _oSubmenu
4077 * @description Object reference to the menu item's submenu.
4078 * @default null
4079 * @private
4080 * @type YAHOO.widget.Menu
4081 */
4082 _oSubmenu: null,
4083
4084 /**
4085 * @property _checkImage
4086 * @description Object reference to the menu item's checkmark image.
4087 * @default null
4088 * @private
4089 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4090 * one-html.html#ID-17701901">HTMLImageElement</a>
4091 */
4092 _checkImage: null,
4093
4094 // Public properties
4095
4096 /**
4097 * @property constructor
4098 * @description Object reference to the menu item's constructor function.
4099 * @default YAHOO.widget.MenuItem
4100 * @type YAHOO.widget.MenuItem
4101 */
4102 constructor: YAHOO.widget.MenuItem,
4103
4104 /**
4105 * @property imageRoot
4106 * @description String representing the root path for all of the menu
4107 * item's images.
4108 * @type String
4109 */
4110 imageRoot: null,
4111
4112 /**
4113 * @property isSecure
4114 * @description Boolean representing whether or not the current browsing
4115 * context is secure (HTTPS).
4116 * @type Boolean
4117 */
4118 isSecure: Module.prototype.isSecure,
4119
4120 /**
4121 * @property index
4122 * @description Number indicating the ordinal position of the menu item in
4123 * its group.
4124 * @default null
4125 * @type Number
4126 */
4127 index: null,
4128
4129 /**
4130 * @property groupIndex
4131 * @description Number indicating the index of the group to which the menu
4132 * item belongs.
4133 * @default null
4134 * @type Number
4135 */
4136 groupIndex: null,
4137
4138 /**
4139 * @property parent
4140 * @description Object reference to the menu item's parent menu.
4141 * @default null
4142 * @type YAHOO.widget.Menu
4143 */
4144 parent: null,
4145
4146 /**
4147 * @property element
4148 * @description Object reference to the menu item's
4149 * <code>&#60;li&#62;</code> element.
4150 * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level
4151 * -one-html.html#ID-74680021">HTMLLIElement</a>
4152 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4153 * one-html.html#ID-74680021">HTMLLIElement</a>
4154 */
4155 element: null,
4156
4157 /**
4158 * @property srcElement
4159 * @description Object reference to the HTML element (either
4160 * <code>&#60;li&#62;</code>, <code>&#60;optgroup&#62;</code> or
4161 * <code>&#60;option&#62;</code>) used create the menu item.
4162 * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
4163 * level-one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.
4164 * w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247"
4165 * >HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
4166 * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
4167 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4168 * one-html.html#ID-74680021">HTMLLIElement</a>|<a href="http://www.w3.
4169 * org/TR/2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-38450247">
4170 * HTMLOptGroupElement</a>|<a href="http://www.w3.org/TR/2000/WD-DOM-
4171 * Level-1-20000929/level-one-html.html#ID-70901257">HTMLOptionElement</a>
4172 */
4173 srcElement: null,
4174
4175 /**
4176 * @property value
4177 * @description Object reference to the menu item's value.
4178 * @default null
4179 * @type Object
4180 */
4181 value: null,
4182
4183 /**
4184 * @property submenuIndicator
4185 * @description Object reference to the <code>&#60;img&#62;</code> element
4186 * used to create the submenu indicator for the menu item.
4187 * @default <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
4188 * level-one-html.html#ID-17701901">HTMLImageElement</a>
4189 * @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
4190 * level-one-html.html#ID-17701901">HTMLImageElement</a>
4191 */
4192 submenuIndicator: null,
4193
4194 /**
4195 * @property browser
4196 * @description String representing the browser.
4197 * @type String
4198 */
4199 browser: Module.prototype.browser,
4200
4201 // Events
4202
4203 /**
4204 * @event destroyEvent
4205 * @description Fires when the menu item's <code>&#60;li&#62;</code>
4206 * element is removed from its parent <code>&#60;ul&#62;</code> element.
4207 * @type YAHOO.util.CustomEvent
4208 */
4209 destroyEvent: null,
4210
4211 /**
4212 * @event mouseOverEvent
4213 * @description Fires when the mouse has entered the menu item. Passes
4214 * back the DOM Event object as an argument.
4215 * @type YAHOO.util.CustomEvent
4216 */
4217 mouseOverEvent: null,
4218
4219 /**
4220 * @event mouseOutEvent
4221 * @description Fires when the mouse has left the menu item. Passes back
4222 * the DOM Event object as an argument.
4223 * @type YAHOO.util.CustomEvent
4224 */
4225 mouseOutEvent: null,
4226
4227 /**
4228 * @event mouseDownEvent
4229 * @description Fires when the user mouses down on the menu item. Passes
4230 * back the DOM Event object as an argument.
4231 * @type YAHOO.util.CustomEvent
4232 */
4233 mouseDownEvent: null,
4234
4235 /**
4236 * @event mouseUpEvent
4237 * @description Fires when the user releases a mouse button while the mouse
4238 * is over the menu item. Passes back the DOM Event object as an argument.
4239 * @type YAHOO.util.CustomEvent
4240 */
4241 mouseUpEvent: null,
4242
4243 /**
4244 * @event clickEvent
4245 * @description Fires when the user clicks the on the menu item. Passes
4246 * back the DOM Event object as an argument.
4247 * @type YAHOO.util.CustomEvent
4248 */
4249 clickEvent: null,
4250
4251 /**
4252 * @event keyPressEvent
4253 * @description Fires when the user presses an alphanumeric key when the
4254 * menu item has focus. Passes back the DOM Event object as an argument.
4255 * @type YAHOO.util.CustomEvent
4256 */
4257 keyPressEvent: null,
4258
4259 /**
4260 * @event keyDownEvent
4261 * @description Fires when the user presses a key when the menu item has
4262 * focus. Passes back the DOM Event object as an argument.
4263 * @type YAHOO.util.CustomEvent
4264 */
4265 keyDownEvent: null,
4266
4267 /**
4268 * @event keyUpEvent
4269 * @description Fires when the user releases a key when the menu item has
4270 * focus. Passes back the DOM Event object as an argument.
4271 * @type YAHOO.util.CustomEvent
4272 */
4273 keyUpEvent: null,
4274
4275 /**
4276 * @event focusEvent
4277 * @description Fires when the menu item receives focus.
4278 * @type YAHOO.util.CustomEvent
4279 */
4280 focusEvent: null,
4281
4282 /**
4283 * @event blurEvent
4284 * @description Fires when the menu item loses the input focus.
4285 * @type YAHOO.util.CustomEvent
4286 */
4287 blurEvent: null,
4288
4289 /**
4290 * @method init
4291 * @description The MenuItem class's initialization method. This method is
4292 * automatically called by the constructor, and sets up all DOM references
4293 * for pre-existing markup, and creates required markup if it is not
4294 * already present.
4295 * @param {String} p_oObject String specifying the text of the menu item.
4296 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4297 * one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying
4298 * the <code>&#60;li&#62;</code> element of the menu item.
4299 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4300 * one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
4301 * specifying the <code>&#60;optgroup&#62;</code> element of the menu item.
4302 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
4303 * one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object
4304 * specifying the <code>&#60;option&#62;</code> element of the menu item.
4305 * @param {Object} p_oConfig Optional. Object literal specifying the
4306 * configuration for the menu item. See configuration class documentation
4307 * for more details.
4308 */
4309 init: function(p_oObject, p_oConfig) {
4310
4311 this.imageRoot = (this.isSecure) ? this.IMG_ROOT_SSL : this.IMG_ROOT;
4312
4313 if(!this.SUBMENU_TYPE) {
4314
4315 this.SUBMENU_TYPE = Menu;
4316
4317 }
4318
4319 // Create the config object
4320
4321 this.cfg = new YAHOO.util.Config(this);
4322
4323 this.initDefaultConfig();
4324
4325 var oConfig = this.cfg;
4326
4327 if(this._checkString(p_oObject)) {
4328
4329 this._createRootNodeStructure();
4330
4331 oConfig.setProperty("text", p_oObject);
4332
4333 }
4334 else if(this._checkDOMNode(p_oObject)) {
4335
4336 switch(p_oObject.tagName.toUpperCase()) {
4337
4338 case "OPTION":
4339
4340 this._createRootNodeStructure();
4341
4342 oConfig.setProperty("text", p_oObject.text);
4343
4344 this.srcElement = p_oObject;
4345
4346 break;
4347
4348 case "OPTGROUP":
4349
4350 this._createRootNodeStructure();
4351
4352 oConfig.setProperty("text", p_oObject.label);
4353
4354 this.srcElement = p_oObject;
4355
4356 this._initSubTree();
4357
4358 break;
4359
4360 case "LI":
4361
4362 // Get the anchor node (if it exists)
4363
4364 var oAnchor = this._getFirstElement(p_oObject, "A");
4365 var sURL = "#";
4366 var sTarget = null;
4367 var sText = null;
4368
4369 // Capture the "text" and/or the "URL"
4370
4371 if(oAnchor) {
4372
4373 sURL = oAnchor.getAttribute("href");
4374 sTarget = oAnchor.getAttribute("target");
4375
4376 if(oAnchor.innerText) {
4377
4378 sText = oAnchor.innerText;
4379
4380 }
4381 else {
4382
4383 var oRange = oAnchor.ownerDocument.createRange();
4384
4385 oRange.selectNodeContents(oAnchor);
4386
4387 sText = oRange.toString();
4388
4389 }
4390
4391 }
4392 else {
4393
4394 var oText = p_oObject.firstChild;
4395
4396 sText = oText.nodeValue;
4397
4398 oAnchor = document.createElement("a");
4399
4400 oAnchor.setAttribute("href", sURL);
4401
4402 p_oObject.replaceChild(oAnchor, oText);
4403
4404 oAnchor.appendChild(oText);
4405
4406 }
4407
4408 this.srcElement = p_oObject;
4409 this.element = p_oObject;
4410 this._oAnchor = oAnchor;
4411
4412
4413 // Check if emphasis has been applied to the MenuItem
4414
4415 var oEmphasisNode = this._getFirstElement(oAnchor);
4416 var bEmphasis = false;
4417 var bStrongEmphasis = false;
4418
4419 if(oEmphasisNode) {
4420
4421 // Set a reference to the text node
4422
4423 this._oText = oEmphasisNode.firstChild;
4424
4425 switch(oEmphasisNode.tagName.toUpperCase()) {
4426
4427 case "EM":
4428
4429 bEmphasis = true;
4430
4431 break;
4432
4433 case "STRONG":
4434
4435 bStrongEmphasis = true;
4436
4437 break;
4438
4439 }
4440
4441 }
4442 else {
4443
4444 // Set a reference to the text node
4445
4446 this._oText = oAnchor.firstChild;
4447
4448 }
4449
4450 /*
4451 Set these properties silently to sync up the
4452 configuration object without making changes to the
4453 element's DOM
4454 */
4455
4456 oConfig.setProperty("text", sText, true);
4457 oConfig.setProperty("url", sURL, true);
4458 oConfig.setProperty("target", sTarget, true);
4459 oConfig.setProperty("emphasis", bEmphasis, true);
4460 oConfig.setProperty(
4461 "strongemphasis",
4462 bStrongEmphasis,
4463 true
4464 );
4465
4466 this._initSubTree();
4467
4468 break;
4469
4470 }
4471
4472 }
4473
4474 if(this.element) {
4475
4476 Dom.addClass(this.element, this.CSS_CLASS_NAME);
4477
4478 // Create custom events
4479
4480 var CustomEvent = YAHOO.util.CustomEvent;
4481
4482 this.destroyEvent = new CustomEvent("destroyEvent", this);
4483 this.mouseOverEvent = new CustomEvent("mouseOverEvent", this);
4484 this.mouseOutEvent = new CustomEvent("mouseOutEvent", this);
4485 this.mouseDownEvent = new CustomEvent("mouseDownEvent", this);
4486 this.mouseUpEvent = new CustomEvent("mouseUpEvent", this);
4487 this.clickEvent = new CustomEvent("clickEvent", this);
4488 this.keyPressEvent = new CustomEvent("keyPressEvent", this);
4489 this.keyDownEvent = new CustomEvent("keyDownEvent", this);
4490 this.keyUpEvent = new CustomEvent("keyUpEvent", this);
4491 this.focusEvent = new CustomEvent("focusEvent", this);
4492 this.blurEvent = new CustomEvent("blurEvent", this);
4493
4494 if(p_oConfig) {
4495
4496 oConfig.applyConfig(p_oConfig);
4497
4498 }
4499
4500 oConfig.fireQueue();
4501
4502 }
4503
4504 },
4505
4506 // Private methods
4507
4508 /**
4509 * @method _getFirstElement
4510 * @description Returns an HTML element's first HTML element node.
4511 * @private
4512 * @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
4513 * level-one-html.html#ID-58190037">HTMLElement</a>} p_oElement Object
4514 * reference specifying the element to be evaluated.
4515 * @param {String} p_sTagName Optional. String specifying the tagname of
4516 * the element to be retrieved.
4517 * @return {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
4518 * level-one-html.html#ID-58190037">HTMLElement</a>}
4519 */
4520 _getFirstElement: function(p_oElement, p_sTagName) {
4521
4522 var oElement;
4523
4524 if(p_oElement.firstChild && p_oElement.firstChild.nodeType == 1) {
4525
4526 oElement = p_oElement.firstChild;
4527
4528 }
4529 else if(
4530 p_oElement.firstChild &&
4531 p_oElement.firstChild.nextSibling &&
4532 p_oElement.firstChild.nextSibling.nodeType == 1
4533 ) {
4534
4535 oElement = p_oElement.firstChild.nextSibling;
4536
4537 }
4538
4539 if(p_sTagName) {
4540
4541 return (oElement && oElement.tagName.toUpperCase() == p_sTagName) ?
4542 oElement : false;
4543
4544 }
4545
4546 return oElement;
4547
4548 },
4549
4550 /**
4551 * @method _checkString
4552 * @description Determines if an object is a string.
4553 * @private
4554 * @param {Object} p_oObject Object to be evaluated.
4555 * @return {Boolean}
4556 */
4557 _checkString: function(p_oObject) {
4558
4559 return (typeof p_oObject == "string");
4560
4561 },
4562
4563 /**
4564 * @method _checkDOMNode
4565 * @description Determines if an object is an HTML element.
4566 * @private
4567 * @param {Object} p_oObject Object to be evaluated.
4568 * @return {Boolean}
4569 */
4570 _checkDOMNode: function(p_oObject) {
4571
4572 return (p_oObject && p_oObject.tagName);
4573
4574 },
4575
4576 /**
4577 * @method _createRootNodeStructure
4578 * @description Creates the core DOM structure for the menu item.
4579 * @private
4580 */
4581 _createRootNodeStructure: function () {
4582
4583 this.element = document.createElement("li");
4584
4585 this._oText = document.createTextNode("");
4586
4587 this._oAnchor = document.createElement("a");
4588 this._oAnchor.appendChild(this._oText);
4589
4590 this.cfg.refireEvent("url");
4591
4592 this.element.appendChild(this._oAnchor);
4593
4594 },
4595
4596 /**
4597 * @method _initSubTree
4598 * @description Iterates the source element's childNodes collection and uses
4599 * the child nodes to instantiate other menus.
4600 * @private
4601 */
4602 _initSubTree: function() {
4603
4604 var oSrcEl = this.srcElement;
4605 var oConfig = this.cfg;
4606
4607 if(oSrcEl.childNodes.length > 0) {
4608
4609 if(
4610 this.parent.lazyLoad &&
4611 this.parent.srcElement &&
4612 this.parent.srcElement.tagName.toUpperCase() == "SELECT"
4613 ) {
4614
4615 oConfig.setProperty(
4616 "submenu",
4617 { id: Dom.generateId(), itemdata: oSrcEl.childNodes }
4618 );
4619
4620 }
4621 else {
4622
4623 var oNode = oSrcEl.firstChild;
4624 var aOptions = [];
4625
4626 do {
4627
4628 if(oNode && oNode.tagName) {
4629
4630 switch(oNode.tagName.toUpperCase()) {
4631
4632 case "DIV":
4633
4634 oConfig.setProperty("submenu", oNode);
4635
4636 break;
4637
4638 case "OPTION":
4639
4640 aOptions[aOptions.length] = oNode;
4641
4642 break;
4643
4644 }
4645
4646 }
4647
4648 }
4649 while((oNode = oNode.nextSibling));
4650
4651
4652 var nOptions = aOptions.length;
4653
4654 if(nOptions > 0) {
4655
4656 var oMenu = new this.SUBMENU_TYPE(Dom.generateId());
4657
4658 oConfig.setProperty("submenu", oMenu);
4659
4660 for(var n=0; n<nOptions; n++) {
4661
4662 oMenu.addItem((new oMenu.ITEM_TYPE(aOptions[n])));
4663
4664 }
4665
4666 }
4667
4668 }
4669
4670 }
4671
4672 },
4673
4674 /**
4675 * @method _preloadImage
4676 * @description Preloads an image by creating an image element from the
4677 * specified path and appending the image to the body of the document.
4678 * @private
4679 * @param {String} p_sPath String specifying the path to the image.
4680 */
4681 _preloadImage: function(p_sPath) {
4682
4683 var sPath = this.imageRoot + p_sPath;
4684
4685 if(!document.images[sPath]) {
4686
4687 var oImage = document.createElement("img");
4688 oImage.src = sPath;
4689 oImage.name = sPath;
4690 oImage.id = sPath;
4691 oImage.style.display = "none";
4692
4693 document.body.appendChild(oImage);
4694
4695 }
4696
4697 },
4698
4699 // Event handlers for configuration properties
4700
4701 /**
4702 * @method configText
4703 * @description Event handler for when the "text" configuration property of
4704 * the menu item changes.
4705 * @param {String} p_sType String representing the name of the event that
4706 * was fired.
4707 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4708 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
4709 * that fired the event.
4710 */
4711 configText: function(p_sType, p_aArgs, p_oItem) {
4712
4713 var sText = p_aArgs[0];
4714
4715 if(this._oText) {
4716
4717 this._oText.nodeValue = sText;
4718
4719 }
4720
4721 },
4722
4723 /**
4724 * @method configHelpText
4725 * @description Event handler for when the "helptext" configuration property
4726 * of the menu item changes.
4727 * @param {String} p_sType String representing the name of the event that
4728 * was fired.
4729 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4730 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
4731 * that fired the event.
4732 */
4733 configHelpText: function(p_sType, p_aArgs, p_oItem) {
4734
4735 var me = this;
4736 var oHelpText = p_aArgs[0];
4737 var oEl = this.element;
4738 var oConfig = this.cfg;
4739 var aNodes = [oEl, this._oAnchor];
4740 var oImg = this.submenuIndicator;
4741
4742 /**
4743 * Adds the "hashelptext" class to the necessary nodes and refires the
4744 * "selected" and "disabled" configuration events.
4745 * @private
4746 */
4747 var initHelpText = function() {
4748
4749 Dom.addClass(aNodes, "hashelptext");
4750
4751 if(oConfig.getProperty("disabled")) {
4752
4753 oConfig.refireEvent("disabled");
4754
4755 }
4756
4757 if(oConfig.getProperty("selected")) {
4758
4759 oConfig.refireEvent("selected");
4760
4761 }
4762
4763 };
4764
4765 /**
4766 * Removes the "hashelptext" class and corresponding DOM element (EM).
4767 * @private
4768 */
4769 var removeHelpText = function() {
4770
4771 Dom.removeClass(aNodes, "hashelptext");
4772
4773 oEl.removeChild(me._oHelpTextEM);
4774 me._oHelpTextEM = null;
4775
4776 };
4777
4778 if(this._checkDOMNode(oHelpText)) {
4779
4780 if(this._oHelpTextEM) {
4781
4782 this._oHelpTextEM.parentNode.replaceChild(
4783 oHelpText,
4784 this._oHelpTextEM
4785 );
4786
4787 }
4788 else {
4789
4790 this._oHelpTextEM = oHelpText;
4791
4792 oEl.insertBefore(this._oHelpTextEM, oImg);
4793
4794 }
4795
4796 initHelpText();
4797
4798 }
4799 else if(this._checkString(oHelpText)) {
4800
4801 if(oHelpText.length === 0) {
4802
4803 removeHelpText();
4804
4805 }
4806 else {
4807
4808 if(!this._oHelpTextEM) {
4809
4810 this._oHelpTextEM = document.createElement("em");
4811
4812 oEl.insertBefore(this._oHelpTextEM, oImg);
4813
4814 }
4815
4816 this._oHelpTextEM.innerHTML = oHelpText;
4817
4818 initHelpText();
4819
4820 }
4821
4822 }
4823 else if(!oHelpText && this._oHelpTextEM) {
4824
4825 removeHelpText();
4826
4827 }
4828
4829 },
4830
4831 /**
4832 * @method configURL
4833 * @description Event handler for when the "url" configuration property of
4834 * the menu item changes.
4835 * @param {String} p_sType String representing the name of the event that
4836 * was fired.
4837 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4838 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
4839 * that fired the event.
4840 */
4841 configURL: function(p_sType, p_aArgs, p_oItem) {
4842
4843 var sURL = p_aArgs[0];
4844
4845 if(!sURL) {
4846
4847 sURL = "#";
4848
4849 }
4850
4851 this._oAnchor.setAttribute("href", sURL);
4852
4853 },
4854
4855 /**
4856 * @method configTarget
4857 * @description Event handler for when the "target" configuration property
4858 * of the menu item changes.
4859 * @param {String} p_sType String representing the name of the event that
4860 * was fired.
4861 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4862 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
4863 * that fired the event.
4864 */
4865 configTarget: function(p_sType, p_aArgs, p_oItem) {
4866
4867 var sTarget = p_aArgs[0];
4868 var oAnchor = this._oAnchor;
4869
4870 if(sTarget && sTarget.length > 0) {
4871
4872 oAnchor.setAttribute("target", sTarget);
4873
4874 }
4875 else {
4876
4877 oAnchor.removeAttribute("target");
4878
4879 }
4880
4881 },
4882
4883 /**
4884 * @method configEmphasis
4885 * @description Event handler for when the "emphasis" configuration property
4886 * of the menu item changes.
4887 * @param {String} p_sType String representing the name of the event that
4888 * was fired.
4889 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4890 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
4891 * that fired the event.
4892 */
4893 configEmphasis: function(p_sType, p_aArgs, p_oItem) {
4894
4895 var bEmphasis = p_aArgs[0];
4896 var oAnchor = this._oAnchor;
4897 var oText = this._oText;
4898 var oConfig = this.cfg;
4899 var oEM;
4900
4901 if(bEmphasis && oConfig.getProperty("strongemphasis")) {
4902
4903 oConfig.setProperty("strongemphasis", false);
4904
4905 }
4906
4907 if(oAnchor) {
4908
4909 if(bEmphasis) {
4910
4911 oEM = document.createElement("em");
4912 oEM.appendChild(oText);
4913
4914 oAnchor.appendChild(oEM);
4915
4916 }
4917 else {
4918
4919 oEM = this._getFirstElement(oAnchor, "EM");
4920
4921 oAnchor.removeChild(oEM);
4922 oAnchor.appendChild(oText);
4923
4924 }
4925
4926 }
4927
4928 },
4929
4930 /**
4931 * @method configStrongEmphasis
4932 * @description Event handler for when the "strongemphasis" configuration
4933 * property of the menu item changes.
4934 * @param {String} p_sType String representing the name of the event that
4935 * was fired.
4936 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4937 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
4938 * that fired the event.
4939 */
4940 configStrongEmphasis: function(p_sType, p_aArgs, p_oItem) {
4941
4942 var bStrongEmphasis = p_aArgs[0];
4943 var oAnchor = this._oAnchor;
4944 var oText = this._oText;
4945 var oConfig = this.cfg;
4946 var oStrong;
4947
4948 if(bStrongEmphasis && oConfig.getProperty("emphasis")) {
4949
4950 oConfig.setProperty("emphasis", false);
4951
4952 }
4953
4954 if(oAnchor) {
4955
4956 if(bStrongEmphasis) {
4957
4958 oStrong = document.createElement("strong");
4959 oStrong.appendChild(oText);
4960
4961 oAnchor.appendChild(oStrong);
4962
4963 }
4964 else {
4965
4966 oStrong = this._getFirstElement(oAnchor, "STRONG");
4967
4968 oAnchor.removeChild(oStrong);
4969 oAnchor.appendChild(oText);
4970
4971 }
4972
4973 }
4974
4975 },
4976
4977 /**
4978 * @method configChecked
4979 * @description Event handler for when the "checked" configuration property
4980 * of the menu item changes.
4981 * @param {String} p_sType String representing the name of the event that
4982 * was fired.
4983 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
4984 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
4985 * that fired the event.
4986 */
4987 configChecked: function(p_sType, p_aArgs, p_oItem) {
4988
4989 var bChecked = p_aArgs[0];
4990 var oEl = this.element;
4991 var oConfig = this.cfg;
4992 var oImg;
4993
4994
4995 if(bChecked) {
4996
4997 this._preloadImage(this.CHECKED_IMAGE_PATH);
4998 this._preloadImage(this.SELECTED_CHECKED_IMAGE_PATH);
4999 this._preloadImage(this.DISABLED_CHECKED_IMAGE_PATH);
5000
5001 oImg = document.createElement("img");
5002 oImg.src = (this.imageRoot + this.CHECKED_IMAGE_PATH);
5003 oImg.alt = this.CHECKED_IMAGE_ALT_TEXT;
5004
5005 var oSubmenu = this.cfg.getProperty("submenu");
5006
5007 if(oSubmenu) {
5008
5009 oEl.insertBefore(oImg, oSubmenu.element);
5010
5011 }
5012 else {
5013
5014 oEl.appendChild(oImg);
5015
5016 }
5017
5018 Dom.addClass([oEl, oImg], "checked");
5019
5020 this._checkImage = oImg;
5021
5022 if(oConfig.getProperty("disabled")) {
5023
5024 oConfig.refireEvent("disabled");
5025
5026 }
5027
5028 if(oConfig.getProperty("selected")) {
5029
5030 oConfig.refireEvent("selected");
5031
5032 }
5033
5034 }
5035 else {
5036
5037 oImg = this._checkImage;
5038
5039 Dom.removeClass([oEl, oImg], "checked");
5040
5041 if(oImg) {
5042
5043 oEl.removeChild(oImg);
5044
5045 }
5046
5047 this._checkImage = null;
5048
5049 }
5050
5051 },
5052
5053 /**
5054 * @method configDisabled
5055 * @description Event handler for when the "disabled" configuration property
5056 * of the menu item changes.
5057 * @param {String} p_sType String representing the name of the event that
5058 * was fired.
5059 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
5060 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
5061 * that fired the event.
5062 */
5063 configDisabled: function(p_sType, p_aArgs, p_oItem) {
5064
5065 var bDisabled = p_aArgs[0];
5066 var oAnchor = this._oAnchor;
5067 var aNodes = [this.element, oAnchor];
5068 var oEM = this._oHelpTextEM;
5069 var oConfig = this.cfg;
5070 var oImg;
5071 var sImgSrc;
5072 var sImgAlt;
5073
5074 if(oEM) {
5075
5076 aNodes[2] = oEM;
5077
5078 }
5079
5080 if(this.cfg.getProperty("checked")) {
5081
5082 sImgAlt = this.CHECKED_IMAGE_ALT_TEXT;
5083 sImgSrc = this.CHECKED_IMAGE_PATH;
5084 oImg = this._checkImage;
5085
5086 if(bDisabled) {
5087
5088 sImgAlt = this.DISABLED_CHECKED_IMAGE_ALT_TEXT;
5089 sImgSrc = this.DISABLED_CHECKED_IMAGE_PATH;
5090
5091 }
5092
5093 oImg.src = document.images[(this.imageRoot + sImgSrc)].src;
5094 oImg.alt = sImgAlt;
5095
5096 }
5097
5098 oImg = this.submenuIndicator;
5099
5100 if(bDisabled) {
5101
5102 if(oConfig.getProperty("selected")) {
5103
5104 oConfig.setProperty("selected", false);
5105
5106 }
5107
5108 oAnchor.removeAttribute("href");
5109
5110 Dom.addClass(aNodes, "disabled");
5111
5112 sImgSrc = this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH;
5113 sImgAlt = this.DISABLED_SUBMENU_INDICATOR_ALT_TEXT;
5114
5115 }
5116 else {
5117
5118 oAnchor.setAttribute("href", oConfig.getProperty("url"));
5119
5120 Dom.removeClass(aNodes, "disabled");
5121
5122 sImgSrc = this.SUBMENU_INDICATOR_IMAGE_PATH;
5123 sImgAlt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;
5124
5125 }
5126
5127 if(oImg) {
5128
5129 oImg.src = this.imageRoot + sImgSrc;
5130 oImg.alt = sImgAlt;
5131
5132 }
5133
5134 },
5135
5136 /**
5137 * @method configSelected
5138 * @description Event handler for when the "selected" configuration property
5139 * of the menu item changes.
5140 * @param {String} p_sType String representing the name of the event that
5141 * was fired.
5142 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
5143 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
5144 * that fired the event.
5145 */
5146 configSelected: function(p_sType, p_aArgs, p_oItem) {
5147
5148 if(!this.cfg.getProperty("disabled")) {
5149
5150 var bSelected = p_aArgs[0];
5151 var oEM = this._oHelpTextEM;
5152 var aNodes = [this.element, this._oAnchor];
5153 var oImg = this.submenuIndicator;
5154 var sImgSrc;
5155
5156 if(oEM) {
5157
5158 aNodes[aNodes.length] = oEM;
5159
5160 }
5161
5162 if(oImg) {
5163
5164 aNodes[aNodes.length] = oImg;
5165
5166 }
5167
5168
5169 if(this.cfg.getProperty("checked")) {
5170
5171 sImgSrc = this.imageRoot + (bSelected ?
5172 this.SELECTED_CHECKED_IMAGE_PATH : this.CHECKED_IMAGE_PATH);
5173
5174 this._checkImage.src = document.images[sImgSrc].src;
5175
5176 }
5177
5178 if(bSelected) {
5179
5180 Dom.addClass(aNodes, "selected");
5181 sImgSrc = this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH;
5182
5183 }
5184 else {
5185
5186 Dom.removeClass(aNodes, "selected");
5187 sImgSrc = this.SUBMENU_INDICATOR_IMAGE_PATH;
5188
5189 }
5190
5191 if(oImg) {
5192
5193 oImg.src = document.images[(this.imageRoot + sImgSrc)].src;
5194
5195 }
5196
5197 }
5198
5199 },
5200
5201 /**
5202 * @method configSubmenu
5203 * @description Event handler for when the "submenu" configuration property
5204 * of the menu item changes.
5205 * @param {String} p_sType String representing the name of the event that
5206 * was fired.
5207 * @param {Array} p_aArgs Array of arguments sent when the event was fired.
5208 * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item
5209 * that fired the event.
5210 */
5211 configSubmenu: function(p_sType, p_aArgs, p_oItem) {
5212
5213 var oEl = this.element;
5214 var oSubmenu = p_aArgs[0];
5215 var oImg = this.submenuIndicator;
5216 var oConfig = this.cfg;
5217 var aNodes = [this.element, this._oAnchor];
5218 var oMenu;
5219 var bLazyLoad = this.parent && this.parent.lazyLoad;
5220
5221 if(oSubmenu) {
5222
5223 if(oSubmenu instanceof Menu) {
5224
5225 oMenu = oSubmenu;
5226 oMenu.parent = this;
5227 oMenu.lazyLoad = bLazyLoad;
5228
5229 }
5230 else if(
5231 typeof oSubmenu == "object" &&
5232 oSubmenu.id &&
5233 !oSubmenu.nodeType
5234 ) {
5235
5236 var sSubmenuId = oSubmenu.id;
5237 var oSubmenuConfig = oSubmenu;
5238
5239 delete oSubmenu["id"];
5240
5241 oSubmenuConfig.lazyload = bLazyLoad;
5242 oSubmenuConfig.parent = this;
5243
5244 oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig);
5245
5246 // Set the value of the property to the Menu instance
5247
5248 this.cfg.setProperty("submenu", oMenu, true);
5249
5250 }
5251 else {
5252
5253 oMenu = new this.SUBMENU_TYPE(
5254 oSubmenu,
5255 { lazyload: bLazyLoad, parent: this }
5256 );
5257
5258 // Set the value of the property to the Menu instance
5259
5260 this.cfg.setProperty("submenu", oMenu, true);
5261
5262 }
5263
5264 if(oMenu) {
5265
5266 this._oSubmenu = oMenu;
5267
5268 if(!oImg) {
5269
5270 this._preloadImage(this.SUBMENU_INDICATOR_IMAGE_PATH);
5271 this._preloadImage(
5272 this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH
5273 );
5274
5275 this._preloadImage(
5276 this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH
5277 );
5278
5279 oImg = document.createElement("img");
5280
5281 oImg.src =
5282 (this.imageRoot + this.SUBMENU_INDICATOR_IMAGE_PATH);
5283
5284 oImg.alt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;
5285
5286 oEl.appendChild(oImg);
5287
5288 this.submenuIndicator = oImg;
5289
5290 Dom.addClass(aNodes, "hassubmenu");
5291
5292 if(oConfig.getProperty("disabled")) {
5293
5294 oConfig.refireEvent("disabled");
5295
5296 }
5297
5298 if(oConfig.getProperty("selected")) {
5299
5300 oConfig.refireEvent("selected");
5301
5302 }
5303
5304 }
5305
5306 }
5307
5308 }
5309 else {
5310
5311 Dom.removeClass(aNodes, "hassubmenu");
5312
5313 if(oImg) {
5314
5315 oEl.removeChild(oImg);
5316
5317 }
5318
5319 if(this._oSubmenu) {
5320
5321 this._oSubmenu.destroy();
5322
5323 }
5324
5325 }
5326
5327 },
5328
5329 // Public methods
5330
5331 /**
5332 * @method initDefaultConfig
5333 * @description Initializes an item's configurable properties.
5334 */
5335 initDefaultConfig : function() {
5336
5337 var oConfig = this.cfg;
5338 var CheckBoolean = oConfig.checkBoolean;
5339
5340 // Define the config properties
5341
5342 /**
5343 * @config text
5344 * @description String specifying the text label for the menu item.
5345 * When building a menu from existing HTML the value of this property
5346 * will be interpreted from the menu's markup.
5347 * @default ""
5348 * @type String
5349 */
5350 oConfig.addProperty(
5351 "text",
5352 {
5353 value: "",
5354 handler: this.configText,
5355 validator: this._checkString,
5356 suppressEvent: true
5357 }
5358 );
5359
5360
5361 /**
5362 * @config helptext
5363 * @description String specifying additional instructional text to
5364 * accompany the text for the nenu item.
5365 * @default null
5366 * @type String|<a href="http://www.w3.org/TR/
5367 * 2000/WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
5368 * HTMLElement</a>
5369 */
5370 oConfig.addProperty("helptext", { handler: this.configHelpText });
5371
5372 /**
5373 * @config url
5374 * @description String specifying the URL for the menu item's anchor's
5375 * "href" attribute. When building a menu from existing HTML the value
5376 * of this property will be interpreted from the menu's markup.
5377 * @default "#"
5378 * @type String
5379 */
5380 oConfig.addProperty(
5381 "url",
5382 { value: "#", handler: this.configURL, suppressEvent: true }
5383 );
5384
5385 /**
5386 * @config target
5387 * @description String specifying the value for the "target" attribute
5388 * of the menu item's anchor element. <strong>Specifying a target will
5389 * require the user to click directly on the menu item's anchor node in
5390 * order to cause the browser to navigate to the specified URL.</strong>
5391 * When building a menu from existing HTML the value of this property
5392 * will be interpreted from the menu's markup.
5393 * @default null
5394 * @type String
5395 */
5396 oConfig.addProperty(
5397 "target",
5398 { handler: this.configTarget, suppressEvent: true }
5399 );
5400
5401 /**
5402 * @config emphasis
5403 * @description Boolean indicating if the text of the menu item will be
5404 * rendered with emphasis. When building a menu from existing HTML the
5405 * value of this property will be interpreted from the menu's markup.
5406 * @default false
5407 * @type Boolean
5408 */
5409 oConfig.addProperty(
5410 "emphasis",
5411 {
5412 value: false,
5413 handler: this.configEmphasis,
5414 validator: CheckBoolean,
5415 suppressEvent: true
5416 }
5417 );
5418
5419 /**
5420 * @config strongemphasis
5421 * @description Boolean indicating if the text of the menu item will be
5422 * rendered with strong emphasis. When building a menu from existing
5423 * HTML the value of this property will be interpreted from the
5424 * menu's markup.
5425 * @default false
5426 * @type Boolean
5427 */
5428 oConfig.addProperty(
5429 "strongemphasis",
5430 {
5431 value: false,
5432 handler: this.configStrongEmphasis,
5433 validator: CheckBoolean,
5434 suppressEvent: true
5435 }
5436 );
5437
5438 /**
5439 * @config checked
5440 * @description Boolean indicating if the menu item should be rendered
5441 * with a checkmark.
5442 * @default false
5443 * @type Boolean
5444 */
5445 oConfig.addProperty(
5446 "checked",
5447 {
5448 value: false,
5449 handler: this.configChecked,
5450 validator: this.cfg.checkBoolean,
5451 suppressEvent: true,
5452 supercedes:["disabled"]
5453 }
5454 );
5455
5456 /**
5457 * @config disabled
5458 * @description Boolean indicating if the menu item should be disabled.
5459 * (Disabled menu items are dimmed and will not respond to user input
5460 * or fire events.)
5461 * @default false
5462 * @type Boolean
5463 */
5464 oConfig.addProperty(
5465 "disabled",
5466 {
5467 value: false,
5468 handler: this.configDisabled,
5469 validator: CheckBoolean,
5470 suppressEvent: true
5471 }
5472 );
5473
5474 /**
5475 * @config selected
5476 * @description Boolean indicating if the menu item should
5477 * be highlighted.
5478 * @default false
5479 * @type Boolean
5480 */
5481 oConfig.addProperty(
5482 "selected",
5483 {
5484 value: false,
5485 handler: this.configSelected,
5486 validator: CheckBoolean,
5487 suppressEvent: true
5488 }
5489 );
5490
5491 /**
5492 * @config submenu
5493 * @description Object specifying the submenu to be appended to the
5494 * menu item. The value can be one of the following: <ul><li>Object
5495 * specifying a Menu instance.</li><li>Object literal specifying the
5496 * menu to be created. Format: <code>{ id: [menu id], itemdata:
5497 * [<a href="YAHOO.widget.Menu.html#itemData">array of values for
5498 * items</a>] }</code>.</li><li>String specifying the id attribute
5499 * of the <code>&#60;div&#62;</code> element of the menu.</li><li>
5500 * Object specifying the <code>&#60;div&#62;</code> element of the
5501 * menu.</li></ul>
5502 * @default null
5503 * @type Menu|String|Object|<a href="http://www.w3.org/TR/2000/
5504 * WD-DOM-Level-1-20000929/level-one-html.html#ID-58190037">
5505 * HTMLElement</a>
5506 */
5507 oConfig.addProperty("submenu", { handler: this.configSubmenu });
5508
5509 },
5510
5511 /**
5512 * @method getNextEnabledSibling
5513 * @description Finds the menu item's next enabled sibling.
5514 * @return YAHOO.widget.MenuItem
5515 */
5516 getNextEnabledSibling: function() {
5517
5518 if(this.parent instanceof Menu) {
5519
5520 var nGroupIndex = this.groupIndex;
5521
5522 /**
5523 * Finds the next item in an array.
5524 * @private
5525 * @param {p_aArray} Array to search.
5526 * @param {p_nStartIndex} Number indicating the index to
5527 * start searching the array.
5528 * @return {Object}
5529 */
5530 var getNextArrayItem = function(p_aArray, p_nStartIndex) {
5531
5532 return p_aArray[p_nStartIndex] ||
5533 getNextArrayItem(p_aArray, (p_nStartIndex+1));
5534
5535 };
5536
5537
5538 var aItemGroups = this.parent.getItemGroups();
5539 var oNextItem;
5540
5541
5542 if(this.index < (aItemGroups[nGroupIndex].length - 1)) {
5543
5544 oNextItem = getNextArrayItem(
5545 aItemGroups[nGroupIndex],
5546 (this.index+1)
5547 );
5548
5549 }
5550 else {
5551
5552 var nNextGroupIndex;
5553
5554 if(nGroupIndex < (aItemGroups.length - 1)) {
5555
5556 nNextGroupIndex = nGroupIndex + 1;
5557
5558 }
5559 else {
5560
5561 nNextGroupIndex = 0;
5562
5563 }
5564
5565 var aNextGroup = getNextArrayItem(aItemGroups, nNextGroupIndex);
5566
5567 // Retrieve the first menu item in the next group
5568
5569 oNextItem = getNextArrayItem(aNextGroup, 0);
5570
5571 }
5572
5573 return (
5574 oNextItem.cfg.getProperty("disabled") ||
5575 oNextItem.element.style.display == "none"
5576 ) ?
5577 oNextItem.getNextEnabledSibling() : oNextItem;
5578
5579 }
5580
5581 },
5582
5583 /**
5584 * @method getPreviousEnabledSibling
5585 * @description Finds the menu item's previous enabled sibling.
5586 * @return {YAHOO.widget.MenuItem}
5587 */
5588 getPreviousEnabledSibling: function() {
5589
5590 if(this.parent instanceof Menu) {
5591
5592 var nGroupIndex = this.groupIndex;
5593
5594 /**
5595 * Returns the previous item in an array
5596 * @private
5597 * @param {p_aArray} Array to search.
5598 * @param {p_nStartIndex} Number indicating the index to
5599 * start searching the array.
5600 * @return {Object}
5601 */
5602 var getPreviousArrayItem = function(p_aArray, p_nStartIndex) {
5603
5604 return p_aArray[p_nStartIndex] ||
5605 getPreviousArrayItem(p_aArray, (p_nStartIndex-1));
5606
5607 };
5608
5609 /**
5610 * Get the index of the first item in an array
5611 * @private
5612 * @param {p_aArray} Array to search.
5613 * @param {p_nStartIndex} Number indicating the index to
5614 * start searching the array.
5615 * @return {Object}
5616 */
5617 var getFirstItemIndex = function(p_aArray, p_nStartIndex) {
5618
5619 return p_aArray[p_nStartIndex] ?
5620 p_nStartIndex :
5621 getFirstItemIndex(p_aArray, (p_nStartIndex+1));
5622
5623 };
5624
5625 var aItemGroups = this.parent.getItemGroups();
5626 var oPreviousItem;
5627
5628 if(
5629 this.index > getFirstItemIndex(aItemGroups[nGroupIndex], 0)
5630 ) {
5631
5632 oPreviousItem =
5633 getPreviousArrayItem(
5634 aItemGroups[nGroupIndex],
5635 (this.index-1)
5636 );
5637
5638 }
5639 else {
5640
5641 var nPreviousGroupIndex;
5642
5643 if(nGroupIndex > getFirstItemIndex(aItemGroups, 0)) {
5644
5645 nPreviousGroupIndex = nGroupIndex - 1;
5646
5647 }
5648 else {
5649
5650 nPreviousGroupIndex = aItemGroups.length - 1;
5651
5652 }
5653
5654 var aPreviousGroup =
5655 getPreviousArrayItem(aItemGroups, nPreviousGroupIndex);
5656
5657 oPreviousItem =
5658 getPreviousArrayItem(
5659 aPreviousGroup,
5660 (aPreviousGroup.length - 1)
5661 );
5662
5663 }
5664
5665 return (
5666 oPreviousItem.cfg.getProperty("disabled") ||
5667 oPreviousItem.element.style.display == "none"
5668 ) ?
5669 oPreviousItem.getPreviousEnabledSibling() : oPreviousItem;
5670
5671 }
5672
5673 },
5674
5675 /**
5676 * @method focus
5677 * @description Causes the menu item to receive the focus and fires the
5678 * focus event.
5679 */
5680 focus: function() {
5681
5682 var oParent = this.parent;
5683 var oAnchor = this._oAnchor;
5684 var oActiveItem = oParent.activeItem;
5685
5686 if(
5687 !this.cfg.getProperty("disabled") &&
5688 oParent &&
5689 oParent.cfg.getProperty("visible") &&
5690 this.element.style.display != "none"
5691 ) {
5692
5693 if(oActiveItem) {
5694
5695 oActiveItem.blur();
5696
5697 }
5698
5699 try {
5700
5701 oAnchor.focus();
5702
5703 }
5704 catch(e) {
5705
5706 }
5707
5708 this.focusEvent.fire();
5709
5710 }
5711
5712 },
5713
5714 /**
5715 * @method blur
5716 * @description Causes the menu item to lose focus and fires the
5717 * onblur event.
5718 */
5719 blur: function() {
5720
5721 var oParent = this.parent;
5722
5723 if(
5724 !this.cfg.getProperty("disabled") &&
5725 oParent &&
5726 Dom.getStyle(oParent.element, "visibility") == "visible"
5727 ) {
5728
5729 this._oAnchor.blur();
5730
5731 this.blurEvent.fire();
5732
5733 }
5734
5735 },
5736
5737 /**
5738 * @method destroy
5739 * @description Removes the menu item's <code>&#60;li&#62;</code> element
5740 * from its parent <code>&#60;ul&#62;</code> element.
5741 */
5742 destroy: function() {
5743
5744 var oEl = this.element;
5745
5746 if(oEl) {
5747
5748 // Remove CustomEvent listeners
5749
5750 this.mouseOverEvent.unsubscribeAll();
5751 this.mouseOutEvent.unsubscribeAll();
5752 this.mouseDownEvent.unsubscribeAll();
5753 this.mouseUpEvent.unsubscribeAll();
5754 this.clickEvent.unsubscribeAll();
5755 this.keyPressEvent.unsubscribeAll();
5756 this.keyDownEvent.unsubscribeAll();
5757 this.keyUpEvent.unsubscribeAll();
5758 this.focusEvent.unsubscribeAll();
5759 this.blurEvent.unsubscribeAll();
5760 this.cfg.configChangedEvent.unsubscribeAll();
5761
5762 // Remove the element from the parent node
5763
5764 var oParentNode = oEl.parentNode;
5765
5766 if(oParentNode) {
5767
5768 oParentNode.removeChild(oEl);
5769
5770 this.destroyEvent.fire();
5771
5772 }
5773
5774 this.destroyEvent.unsubscribeAll();
5775
5776 }
5777
5778 },
5779
5780 /**
5781 * @method toString
5782 * @description Returns a string representing the menu item.
5783 * @return {String}
5784 */
5785 toString: function() {
5786
5787 return ("MenuItem: " + this.cfg.getProperty("text"));
5788
5789 }
5790
5791};
5792
5793})();
5794
5795/**
5796* Creates an item for a menu module.
5797*
5798* @param {String} p_oObject String specifying the text of the menu module item.
5799* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5800* html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the
5801* <code>&#60;li&#62;</code> element of the menu module item.
5802* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5803* html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object specifying
5804* the <code>&#60;optgroup&#62;</code> element of the menu module item.
5805* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5806* html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying the
5807* <code>&#60;option&#62;</code> element of the menu module item.
5808* @param {Object} p_oConfig Optional. Object literal specifying the
5809* configuration for the menu module item. See configuration class documentation
5810* for more details.
5811* @class MenuModuleItem
5812* @constructor
5813* @deprecated As of version 0.12, all MenuModuleItem functionality has been
5814* implemented directly in YAHOO.widget.MenuItem, making YAHOO.widget.MenuItem
5815* the base class for all menu items.
5816*/
5817YAHOO.widget.MenuModuleItem = YAHOO.widget.MenuItem;
5818
5819/**
5820* Creates a list of options or commands which are made visible in response to
5821* an HTML element's "contextmenu" event ("mousedown" for Opera).
5822*
5823* @param {String} p_oElement String specifying the id attribute of the
5824* <code>&#60;div&#62;</code> element of the context menu.
5825* @param {String} p_oElement String specifying the id attribute of the
5826* <code>&#60;select&#62;</code> element to be used as the data source for the
5827* context menu.
5828* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5829* html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the
5830* <code>&#60;div&#62;</code> element of the context menu.
5831* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5832* html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying
5833* the <code>&#60;select&#62;</code> element to be used as the data source for
5834* the context menu.
5835* @param {Object} p_oConfig Optional. Object literal specifying the
5836* configuration for the context menu. See configuration class documentation
5837* for more details.
5838* @class ContextMenu
5839* @constructor
5840* @extends YAHOO.widget.Menu
5841* @namespace YAHOO.widget
5842*/
5843YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) {
5844
5845 YAHOO.widget.ContextMenu.superclass.constructor.call(
5846 this,
5847 p_oElement,
5848 p_oConfig
5849 );
5850
5851};
5852
5853YAHOO.extend(YAHOO.widget.ContextMenu, YAHOO.widget.Menu, {
5854
5855// Private properties
5856
5857/**
5858* @property _oTrigger
5859* @description Object reference to the current value of the "trigger"
5860* configuration property.
5861* @default null
5862* @private
5863* @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/leve
5864* l-one-html.html#ID-58190037">HTMLElement</a>|Array
5865*/
5866_oTrigger: null,
5867
5868// Public properties
5869
5870/**
5871* @property contextEventTarget
5872* @description Object reference for the HTML element that was the target of the
5873* "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of
5874* the context menu.
5875* @default null
5876* @type <a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5877* html.html#ID-58190037">HTMLElement</a>
5878*/
5879contextEventTarget: null,
5880
5881/**
5882* @method init
5883* @description The ContextMenu class's initialization method. This method is
5884* automatically called by the constructor, and sets up all DOM references for
5885* pre-existing markup, and creates required markup if it is not already present.
5886* @param {String} p_oElement String specifying the id attribute of the
5887* <code>&#60;div&#62;</code> element of the context menu.
5888* @param {String} p_oElement String specifying the id attribute of the
5889* <code>&#60;select&#62;</code> element to be used as the data source for
5890* the context menu.
5891* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5892* html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying the
5893* <code>&#60;div&#62;</code> element of the context menu.
5894* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-one-
5895* html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object specifying
5896* the <code>&#60;select&#62;</code> element to be used as the data source for
5897* the context menu.
5898* @param {Object} p_oConfig Optional. Object literal specifying the
5899* configuration for the context menu. See configuration class documentation
5900* for more details.
5901*/
5902init: function(p_oElement, p_oConfig) {
5903
5904 if(!this.ITEM_TYPE) {
5905
5906 this.ITEM_TYPE = YAHOO.widget.ContextMenuItem;
5907
5908 }
5909
5910 // Call the init of the superclass (YAHOO.widget.Menu)
5911
5912 YAHOO.widget.ContextMenu.superclass.init.call(this, p_oElement);
5913
5914 this.beforeInitEvent.fire(YAHOO.widget.ContextMenu);
5915
5916 if(p_oConfig) {
5917
5918 this.cfg.applyConfig(p_oConfig, true);
5919
5920 }
5921
5922
5923 this.initEvent.fire(YAHOO.widget.ContextMenu);
5924
5925},
5926
5927// Private methods
5928
5929/**
5930* @method _removeEventHandlers
5931* @description Removes all of the DOM event handlers from the HTML element(s)
5932* whose "context menu" event ("click" for Opera) trigger the display of
5933* the context menu.
5934* @private
5935*/
5936_removeEventHandlers: function() {
5937
5938 var Event = YAHOO.util.Event;
5939 var oTrigger = this._oTrigger;
5940 var bOpera = (this.browser == "opera");
5941
5942 // Remove the event handlers from the trigger(s)
5943
5944 Event.removeListener(
5945 oTrigger,
5946 (bOpera ? "mousedown" : "contextmenu"),
5947 this._onTriggerContextMenu
5948 );
5949
5950 if(bOpera) {
5951
5952 Event.removeListener(oTrigger, "click", this._onTriggerClick);
5953
5954 }
5955
5956},
5957
5958// Private event handlers
5959
5960/**
5961* @method _onTriggerClick
5962* @description "click" event handler for the HTML element(s) identified as the
5963* "trigger" for the context menu. Used to cancel default behaviors in Opera.
5964* @private
5965* @param {Event} p_oEvent Object representing the DOM event object passed back
5966* by the event utility (YAHOO.util.Event).
5967* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context
5968* menu that is handling the event.
5969*/
5970_onTriggerClick: function(p_oEvent, p_oMenu) {
5971
5972 if(p_oEvent.ctrlKey) {
5973
5974 YAHOO.util.Event.stopEvent(p_oEvent);
5975
5976 }
5977
5978},
5979
5980/**
5981* @method _onTriggerContextMenu
5982* @description "contextmenu" event handler ("mousedown" for Opera) for the HTML
5983* element(s) that trigger the display of the context menu.
5984* @private
5985* @param {Event} p_oEvent Object representing the DOM event object passed back
5986* by the event utility (YAHOO.util.Event).
5987* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context
5988* menu that is handling the event.
5989*/
5990_onTriggerContextMenu: function(p_oEvent, p_oMenu) {
5991
5992 // Hide any other ContextMenu instances that might be visible
5993
5994 YAHOO.widget.MenuManager.hideVisible();
5995
5996 var Event = YAHOO.util.Event;
5997 var oConfig = this.cfg;
5998
5999 if(p_oEvent.type == "mousedown" && !p_oEvent.ctrlKey) {
6000
6001 return;
6002
6003 }
6004
6005 this.contextEventTarget = Event.getTarget(p_oEvent);
6006
6007 // Position and display the context menu
6008
6009 var nX = Event.getPageX(p_oEvent);
6010 var nY = Event.getPageY(p_oEvent);
6011
6012 oConfig.applyConfig( { xy:[nX, nY], visible:true } );
6013 oConfig.fireQueue();
6014
6015 /*
6016 Prevent the browser's default context menu from appearing and
6017 stop the propagation of the "contextmenu" event so that
6018 other ContextMenu instances are not displayed.
6019 */
6020
6021 Event.stopEvent(p_oEvent);
6022
6023},
6024
6025// Public methods
6026
6027/**
6028* @method toString
6029* @description Returns a string representing the context menu.
6030* @return {String}
6031*/
6032toString: function() {
6033
6034 return ("ContextMenu " + this.id);
6035
6036},
6037
6038/**
6039* @method initDefaultConfig
6040* @description Initializes the class's configurable properties which can be
6041* changed using the context menu's Config object ("cfg").
6042*/
6043initDefaultConfig: function() {
6044
6045 YAHOO.widget.ContextMenu.superclass.initDefaultConfig.call(this);
6046
6047 /**
6048 * @config trigger
6049 * @description The HTML element(s) whose "contextmenu" event ("mousedown"
6050 * for Opera) trigger the display of the context menu. Can be a string
6051 * representing the id attribute of the HTML element, an object reference
6052 * for the HTML element, or an array of strings or HTML element references.
6053 * @default null
6054 * @type String|<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/
6055 * level-one-html.html#ID-58190037">HTMLElement</a>|Array
6056 */
6057 this.cfg.addProperty("trigger", { handler: this.configTrigger });
6058
6059},
6060
6061/**
6062* @method destroy
6063* @description Removes the context menu's <code>&#60;div&#62;</code> element
6064* (and accompanying child nodes) from the document.
6065*/
6066destroy: function() {
6067
6068 // Remove the DOM event handlers from the current trigger(s)
6069
6070 this._removeEventHandlers();
6071
6072
6073 // Continue with the superclass implementation of this method
6074
6075 YAHOO.widget.ContextMenu.superclass.destroy.call(this);
6076
6077},
6078
6079// Public event handlers for configuration properties
6080
6081/**
6082* @method configTrigger
6083* @description Event handler for when the value of the "trigger" configuration
6084* property changes.
6085* @param {String} p_sType String representing the name of the event that
6086* was fired.
6087* @param {Array} p_aArgs Array of arguments sent when the event was fired.
6088* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context
6089* menu that fired the event.
6090*/
6091configTrigger: function(p_sType, p_aArgs, p_oMenu) {
6092
6093 var Event = YAHOO.util.Event;
6094 var oTrigger = p_aArgs[0];
6095
6096 if(oTrigger) {
6097
6098 /*
6099 If there is a current "trigger" - remove the event handlers
6100 from that element(s) before assigning new ones
6101 */
6102
6103 if(this._oTrigger) {
6104
6105 this._removeEventHandlers();
6106
6107 }
6108
6109 this._oTrigger = oTrigger;
6110
6111 /*
6112 Listen for the "mousedown" event in Opera b/c it does not
6113 support the "contextmenu" event
6114 */
6115
6116 var bOpera = (this.browser == "opera");
6117
6118 Event.addListener(
6119 oTrigger,
6120 (bOpera ? "mousedown" : "contextmenu"),
6121 this._onTriggerContextMenu,
6122 this,
6123 true
6124 );
6125
6126 /*
6127 Assign a "click" event handler to the trigger element(s) for
6128 Opera to prevent default browser behaviors.
6129 */
6130
6131 if(bOpera) {
6132
6133 Event.addListener(
6134 oTrigger,
6135 "click",
6136 this._onTriggerClick,
6137 this,
6138 true
6139 );
6140
6141 }
6142
6143 }
6144 else {
6145
6146 this._removeEventHandlers();
6147
6148 }
6149
6150}
6151
6152}); // END YAHOO.extend
6153
6154/**
6155* Creates an item for a context menu.
6156*
6157* @param {String} p_oObject String specifying the text of the context menu item.
6158* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6159* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the
6160* <code>&#60;li&#62;</code> element of the context menu item.
6161* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6162* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
6163* specifying the <code>&#60;optgroup&#62;</code> element of the context
6164* menu item.
6165* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6166* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying
6167* the <code>&#60;option&#62;</code> element of the context menu item.
6168* @param {Object} p_oConfig Optional. Object literal specifying the
6169* configuration for the context menu item. See configuration class
6170* documentation for more details.
6171* @class ContextMenuItem
6172* @constructor
6173* @extends YAHOO.widget.MenuItem
6174*/
6175YAHOO.widget.ContextMenuItem = function(p_oObject, p_oConfig) {
6176
6177 YAHOO.widget.ContextMenuItem.superclass.constructor.call(
6178 this,
6179 p_oObject,
6180 p_oConfig
6181 );
6182
6183};
6184
6185YAHOO.extend(YAHOO.widget.ContextMenuItem, YAHOO.widget.MenuItem, {
6186
6187/**
6188* @method init
6189* @description The ContextMenuItem class's initialization method. This method
6190* is automatically called by the constructor, and sets up all DOM references
6191* for pre-existing markup, and creates required markup if it is not
6192* already present.
6193* @param {String} p_oObject String specifying the text of the context menu item.
6194* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6195* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the
6196* <code>&#60;li&#62;</code> element of the context menu item.
6197* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6198* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
6199* specifying the <code>&#60;optgroup&#62;</code> element of the context
6200* menu item.
6201* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6202* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying
6203* the <code>&#60;option&#62;</code> element of the context menu item.
6204* @param {Object} p_oConfig Optional. Object literal specifying the
6205* configuration for the context menu item. See configuration class
6206* documentation for more details.
6207*/
6208init: function(p_oObject, p_oConfig) {
6209
6210 if(!this.SUBMENU_TYPE) {
6211
6212 this.SUBMENU_TYPE = YAHOO.widget.ContextMenu;
6213
6214 }
6215
6216 /*
6217 Call the init of the superclass (YAHOO.widget.MenuItem)
6218 Note: We don't pass the user config in here yet
6219 because we only want it executed once, at the lowest
6220 subclass level.
6221 */
6222
6223 YAHOO.widget.ContextMenuItem.superclass.init.call(this, p_oObject);
6224
6225 var oConfig = this.cfg;
6226
6227 if(p_oConfig) {
6228
6229 oConfig.applyConfig(p_oConfig, true);
6230
6231 }
6232
6233 oConfig.fireQueue();
6234
6235},
6236
6237// Public methods
6238
6239/**
6240* @method toString
6241* @description Returns a string representing the context menu item.
6242* @return {String}
6243*/
6244toString: function() {
6245
6246 return ("MenuBarItem: " + this.cfg.getProperty("text"));
6247
6248}
6249
6250}); // END YAHOO.extend
6251
6252/**
6253* Horizontal collection of items, each of which can contain a submenu.
6254*
6255* @param {String} p_oElement String specifying the id attribute of the
6256* <code>&#60;div&#62;</code> element of the menu bar.
6257* @param {String} p_oElement String specifying the id attribute of the
6258* <code>&#60;select&#62;</code> element to be used as the data source for the
6259* menu bar.
6260* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6261* one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying
6262* the <code>&#60;div&#62;</code> element of the menu bar.
6263* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6264* one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object
6265* specifying the <code>&#60;select&#62;</code> element to be used as the data
6266* source for the menu bar.
6267* @param {Object} p_oConfig Optional. Object literal specifying the
6268* configuration for the menu bar. See configuration class documentation for
6269* more details.
6270* @class Menubar
6271* @constructor
6272* @extends YAHOO.widget.Menu
6273* @namespace YAHOO.widget
6274*/
6275YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) {
6276
6277 YAHOO.widget.MenuBar.superclass.constructor.call(
6278 this,
6279 p_oElement,
6280 p_oConfig
6281 );
6282
6283};
6284
6285YAHOO.extend(YAHOO.widget.MenuBar, YAHOO.widget.Menu, {
6286
6287/**
6288* @method init
6289* @description The MenuBar class's initialization method. This method is
6290* automatically called by the constructor, and sets up all DOM references for
6291* pre-existing markup, and creates required markup if it is not already present.
6292* @param {String} p_oElement String specifying the id attribute of the
6293* <code>&#60;div&#62;</code> element of the menu bar.
6294* @param {String} p_oElement String specifying the id attribute of the
6295* <code>&#60;select&#62;</code> element to be used as the data source for the
6296* menu bar.
6297* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6298* one-html.html#ID-22445964">HTMLDivElement</a>} p_oElement Object specifying
6299* the <code>&#60;div&#62;</code> element of the menu bar.
6300* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6301* one-html.html#ID-94282980">HTMLSelectElement</a>} p_oElement Object
6302* specifying the <code>&#60;select&#62;</code> element to be used as the data
6303* source for the menu bar.
6304* @param {Object} p_oConfig Optional. Object literal specifying the
6305* configuration for the menu bar. See configuration class documentation for
6306* more details.
6307*/
6308init: function(p_oElement, p_oConfig) {
6309
6310 if(!this.ITEM_TYPE) {
6311
6312 this.ITEM_TYPE = YAHOO.widget.MenuBarItem;
6313
6314 }
6315
6316 // Call the init of the superclass (YAHOO.widget.Menu)
6317
6318 YAHOO.widget.MenuBar.superclass.init.call(this, p_oElement);
6319
6320 this.beforeInitEvent.fire(YAHOO.widget.MenuBar);
6321
6322 if(p_oConfig) {
6323
6324 this.cfg.applyConfig(p_oConfig, true);
6325
6326 }
6327
6328 this.initEvent.fire(YAHOO.widget.MenuBar);
6329
6330},
6331
6332// Constants
6333
6334/**
6335* @property CSS_CLASS_NAME
6336* @description String representing the CSS class(es) to be applied to the menu
6337* bar's <code>&#60;div&#62;</code> element.
6338* @default "yuimenubar"
6339* @final
6340* @type String
6341*/
6342CSS_CLASS_NAME: "yuimenubar",
6343
6344// Protected event handlers
6345
6346/**
6347* @method _onKeyDown
6348* @description "keydown" Custom Event handler for the menu bar.
6349* @private
6350* @param {String} p_sType String representing the name of the event that
6351* was fired.
6352* @param {Array} p_aArgs Array of arguments sent when the event was fired.
6353* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar
6354* that fired the event.
6355*/
6356_onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) {
6357
6358 var Event = YAHOO.util.Event;
6359 var oEvent = p_aArgs[0];
6360 var oItem = p_aArgs[1];
6361 var oItemCfg = oItem.cfg;
6362 var oSubmenu;
6363
6364 switch(oEvent.keyCode) {
6365
6366 case 27: // Esc key
6367
6368 if(this.cfg.getProperty("position") == "dynamic") {
6369
6370 this.hide();
6371
6372 if(this.parent) {
6373
6374 this.parent.focus();
6375
6376 }
6377
6378 }
6379 else if(this.activeItem) {
6380
6381 oSubmenu = this.activeItem.cfg.getProperty("submenu");
6382
6383 if(oSubmenu && oSubmenu.cfg.getProperty("visible")) {
6384
6385 oSubmenu.hide();
6386 this.activeItem.focus();
6387
6388 }
6389 else {
6390
6391 this.activeItem.cfg.setProperty("selected", false);
6392 this.activeItem.blur();
6393
6394 }
6395
6396 }
6397
6398
6399 Event.preventDefault(oEvent);
6400
6401 break;
6402
6403 case 37: // Left arrow
6404 case 39: // Right arrow
6405
6406 if(
6407 oItem == this.activeItem &&
6408 !oItemCfg.getProperty("selected")
6409 ) {
6410
6411 oItemCfg.setProperty("selected", true);
6412
6413 }
6414 else {
6415
6416 var oNextItem = (oEvent.keyCode == 37) ?
6417 oItem.getPreviousEnabledSibling() :
6418 oItem.getNextEnabledSibling();
6419
6420 if(oNextItem) {
6421
6422 this.clearActiveItem();
6423
6424 oNextItem.cfg.setProperty("selected", true);
6425
6426 if(this.cfg.getProperty("autosubmenudisplay")) {
6427
6428 oSubmenu = oNextItem.cfg.getProperty("submenu");
6429
6430 if(oSubmenu) {
6431
6432 oSubmenu.show();
6433 oSubmenu.activeItem.blur();
6434 oSubmenu.activeItem = null;
6435
6436 }
6437
6438 }
6439
6440 oNextItem.focus();
6441
6442 }
6443
6444 }
6445
6446 Event.preventDefault(oEvent);
6447
6448 break;
6449
6450 case 40: // Down arrow
6451
6452 if(this.activeItem != oItem) {
6453
6454 this.clearActiveItem();
6455
6456 oItemCfg.setProperty("selected", true);
6457 oItem.focus();
6458
6459 }
6460
6461 oSubmenu = oItemCfg.getProperty("submenu");
6462
6463 if(oSubmenu) {
6464
6465 if(oSubmenu.cfg.getProperty("visible")) {
6466
6467 oSubmenu.setInitialSelection();
6468 oSubmenu.setInitialFocus();
6469
6470 }
6471 else {
6472
6473 oSubmenu.show();
6474
6475 }
6476
6477 }
6478
6479 Event.preventDefault(oEvent);
6480
6481 break;
6482
6483 }
6484
6485},
6486
6487/**
6488* @method _onClick
6489* @description "click" event handler for the menu bar.
6490* @protected
6491* @param {String} p_sType String representing the name of the event that
6492* was fired.
6493* @param {Array} p_aArgs Array of arguments sent when the event was fired.
6494* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar
6495* that fired the event.
6496*/
6497_onClick: function(p_sType, p_aArgs, p_oMenuBar) {
6498
6499 YAHOO.widget.MenuBar.superclass._onClick.call(
6500 this,
6501 p_sType,
6502 p_aArgs,
6503 p_oMenuBar
6504 );
6505
6506 var oItem = p_aArgs[1];
6507
6508 if(oItem) {
6509
6510 var Event = YAHOO.util.Event;
6511 var Dom = YAHOO.util.Dom;
6512
6513 var oEvent = p_aArgs[0];
6514 var oTarget = Event.getTarget(oEvent);
6515
6516 var oActiveItem = this.activeItem;
6517 var oConfig = this.cfg;
6518
6519 // Hide any other submenus that might be visible
6520
6521 if(oActiveItem && oActiveItem != oItem) {
6522
6523 this.clearActiveItem();
6524
6525 }
6526
6527
6528 // Select and focus the current item
6529
6530 oItem.cfg.setProperty("selected", true);
6531 oItem.focus();
6532
6533
6534 // Show the submenu for the item
6535
6536 var oSubmenu = oItem.cfg.getProperty("submenu");
6537
6538 if(oSubmenu && oTarget != oItem.submenuIndicator) {
6539
6540 if(oSubmenu.cfg.getProperty("visible")) {
6541
6542 oSubmenu.hide();
6543
6544 }
6545 else {
6546
6547 oSubmenu.show();
6548
6549 }
6550
6551 }
6552
6553 }
6554
6555},
6556
6557// Public methods
6558
6559/**
6560* @method toString
6561* @description Returns a string representing the menu bar.
6562* @return {String}
6563*/
6564toString: function() {
6565
6566 return ("MenuBar " + this.id);
6567
6568},
6569
6570/**
6571* @description Initializes the class's configurable properties which can be
6572* changed using the menu bar's Config object ("cfg").
6573* @method initDefaultConfig
6574*/
6575initDefaultConfig: function() {
6576
6577 YAHOO.widget.MenuBar.superclass.initDefaultConfig.call(this);
6578
6579 var oConfig = this.cfg;
6580
6581 // Add configuration properties
6582
6583 /*
6584 Set the default value for the "position" configuration property
6585 to "static" by re-adding the property.
6586 */
6587
6588 /**
6589 * @config position
6590 * @description String indicating how a menu bar should be positioned on the
6591 * screen. Possible values are "static" and "dynamic." Static menu bars
6592 * are visible by default and reside in the normal flow of the document
6593 * (CSS position: static). Dynamic menu bars are hidden by default, reside
6594 * out of the normal flow of the document (CSS position: absolute), and can
6595 * overlay other elements on the screen.
6596 * @default static
6597 * @type String
6598 */
6599 oConfig.addProperty(
6600 "position",
6601 {
6602 value: "static",
6603 handler: this.configPosition,
6604 validator: this._checkPosition,
6605 supercedes: ["visible"]
6606 }
6607 );
6608
6609 /*
6610 Set the default value for the "submenualignment" configuration property
6611 to ["tl","bl"] by re-adding the property.
6612 */
6613
6614 /**
6615 * @config submenualignment
6616 * @description Array defining how submenus should be aligned to their
6617 * parent menu bar item. The format is: [itemCorner, submenuCorner].
6618 * @default ["tl","bl"]
6619 * @type Array
6620 */
6621 oConfig.addProperty("submenualignment", { value: ["tl","bl"] } );
6622
6623 /*
6624 Change the default value for the "autosubmenudisplay" configuration
6625 property to "false" by re-adding the property.
6626 */
6627
6628 /**
6629 * @config autosubmenudisplay
6630 * @description Boolean indicating if submenus are automatically made
6631 * visible when the user mouses over the menu bar's items.
6632 * @default false
6633 * @type Boolean
6634 */
6635 oConfig.addProperty(
6636 "autosubmenudisplay",
6637 { value: false, validator: oConfig.checkBoolean }
6638 );
6639
6640}
6641
6642}); // END YAHOO.extend
6643
6644/**
6645* Creates an item for a menu bar.
6646*
6647* @param {String} p_oObject String specifying the text of the menu bar item.
6648* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6649* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the
6650* <code>&#60;li&#62;</code> element of the menu bar item.
6651* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6652* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
6653* specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
6654* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6655* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying
6656* the <code>&#60;option&#62;</code> element of the menu bar item.
6657* @param {Object} p_oConfig Optional. Object literal specifying the
6658* configuration for the menu bar item. See configuration class documentation
6659* for more details.
6660* @class MenuBarItem
6661* @constructor
6662* @extends YAHOO.widget.MenuItem
6663*/
6664YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) {
6665
6666 YAHOO.widget.MenuBarItem.superclass.constructor.call(
6667 this,
6668 p_oObject,
6669 p_oConfig
6670 );
6671
6672};
6673
6674YAHOO.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, {
6675
6676/**
6677* @method init
6678* @description The MenuBarItem class's initialization method. This method is
6679* automatically called by the constructor, and sets up all DOM references for
6680* pre-existing markup, and creates required markup if it is not already present.
6681* @param {String} p_oObject String specifying the text of the menu bar item.
6682* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6683* one-html.html#ID-74680021">HTMLLIElement</a>} p_oObject Object specifying the
6684* <code>&#60;li&#62;</code> element of the menu bar item.
6685* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6686* one-html.html#ID-38450247">HTMLOptGroupElement</a>} p_oObject Object
6687* specifying the <code>&#60;optgroup&#62;</code> element of the menu bar item.
6688* @param {<a href="http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/level-
6689* one-html.html#ID-70901257">HTMLOptionElement</a>} p_oObject Object specifying
6690* the <code>&#60;option&#62;</code> element of the menu bar item.
6691* @param {Object} p_oConfig Optional. Object literal specifying the
6692* configuration for the menu bar item. See configuration class documentation
6693* for more details.
6694*/
6695init: function(p_oObject, p_oConfig) {
6696
6697 if(!this.SUBMENU_TYPE) {
6698
6699 this.SUBMENU_TYPE = YAHOO.widget.Menu;
6700
6701 }
6702
6703 /*
6704 Call the init of the superclass (YAHOO.widget.MenuItem)
6705 Note: We don't pass the user config in here yet
6706 because we only want it executed once, at the lowest
6707 subclass level.
6708 */
6709
6710 YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject);
6711
6712 var oConfig = this.cfg;
6713
6714 if(p_oConfig) {
6715
6716 oConfig.applyConfig(p_oConfig, true);
6717
6718 }
6719
6720 oConfig.fireQueue();
6721
6722},
6723
6724// Constants
6725
6726/**
6727* @property CSS_CLASS_NAME
6728* @description String representing the CSS class(es) to be applied to the
6729* <code>&#60;li&#62;</code> element of the menu bar item.
6730* @default "yuimenubaritem"
6731* @final
6732* @type String
6733*/
6734CSS_CLASS_NAME: "yuimenubaritem",
6735
6736/**
6737* @property SUBMENU_INDICATOR_IMAGE_PATH
6738* @description String representing the path to the image to be used for the
6739* menu bar item's submenu arrow indicator.
6740* @default "nt/ic/ut/alt1/menuarodwn8_nrm_1.gif"
6741* @final
6742* @type String
6743*/
6744SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_nrm_1.gif",
6745
6746/**
6747* @property SELECTED_SUBMENU_INDICATOR_IMAGE_PATH
6748* @description String representing the path to the image to be used for the
6749* submenu arrow indicator when the menu bar item is selected.
6750* @default "nt/ic/ut/alt1/menuarodwn8_hov_1.gif"
6751* @final
6752* @type String
6753*/
6754SELECTED_SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_hov_1.gif",
6755
6756/**
6757* @property DISABLED_SUBMENU_INDICATOR_IMAGE_PATH
6758* @description String representing the path to the image to be used for the
6759* submenu arrow indicator when the menu bar item is disabled.
6760* @default "nt/ic/ut/alt1/menuarodwn8_dim_1.gif"
6761* @final
6762* @type String
6763*/
6764DISABLED_SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_dim_1.gif",
6765
6766// Public methods
6767
6768/**
6769* @method toString
6770* @description Returns a string representing the menu bar item.
6771* @return {String}
6772*/
6773toString: function() {
6774
6775 return ("MenuBarItem: " + this.cfg.getProperty("text"));
6776
6777}
6778
6779}); // END YAHOO.extend
6780
diff --git a/frontend/beta/js/YUI/slider.js b/frontend/beta/js/YUI/slider.js
new file mode 100644
index 0000000..8d3cd62
--- a/dev/null
+++ b/frontend/beta/js/YUI/slider.js
@@ -0,0 +1,1113 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9 * The Slider component is a UI control that enables the user to adjust
10 * values in a finite range along one or two axes. Typically, the Slider
11 * control is used in a web application as a rich, visual replacement
12 * for an input box that takes a number as input. The Slider control can
13 * also easily accommodate a second dimension, providing x,y output for
14 * a selection point chosen from a rectangular region.
15 *
16 * @module slider
17 * @title Slider Widget
18 * @namespace YAHOO.widget
19 * @requires yahoo,dom,dragdrop,event
20 * @optional animation
21 */
22
23/**
24 * A DragDrop implementation that can be used as a background for a
25 * slider. It takes a reference to the thumb instance
26 * so it can delegate some of the events to it. The goal is to make the
27 * thumb jump to the location on the background when the background is
28 * clicked.
29 *
30 * @class Slider
31 * @extends YAHOO.util.DragDrop
32 * @constructor
33 * @param {String} id The id of the element linked to this instance
34 * @param {String} sGroup The group of related DragDrop items
35 * @param {String} sType The type of slider (horiz, vert, region)
36 */
37YAHOO.widget.Slider = function(sElementId, sGroup, oThumb, sType) {
38 if (sElementId) {
39
40 /**
41 * The type of the slider (horiz, vert, region)
42 * @property type
43 * @type string
44 */
45 this.type = sType;
46
47 this.init(sElementId, sGroup, true);
48
49 //this.removeInvalidHandleType("A");
50
51
52 var self = this;
53
54 /**
55 * Event the fires when the value of the control changes. If
56 * the control is animated the event will fire every point
57 * along the way.
58 * @event change
59 * @param {int} new
60 * @param {int} firstOffset the number of pixels the thumb has moved
61 * from its start position. Normal horizontal and vertical sliders will only
62 * have the firstOffset. Regions will have both, the first is the horizontal
63 * offset, the second the vertical.
64 * @param {int} secondOffset the y offset for region sliders
65 */
66 this.createEvent("change", this);
67
68 /**
69 * Event that fires at the end of a slider thumb move.
70 * @event slideStart
71 */
72 this.createEvent("slideStart", this);
73
74 /**
75 * Event that fires at the end of a slider thumb move
76 * @event slideEnd
77 */
78 this.createEvent("slideEnd", this);
79
80 /**
81 * A YAHOO.widget.SliderThumb instance that we will use to
82 * reposition the thumb when the background is clicked
83 * @property thumb
84 * @type YAHOO.widget.SliderThumb
85 */
86 this.thumb = oThumb;
87
88 // add handler for the handle onchange event
89 oThumb.onChange = function() {
90 self.handleThumbChange();
91 };
92
93 /**
94 * Overrides the isTarget property in YAHOO.util.DragDrop
95 * @property isTarget
96 * @private
97 */
98 this.isTarget = false;
99
100 /**
101 * Flag that determines if the thumb will animate when moved
102 * @property animate
103 * @type boolean
104 */
105 this.animate = YAHOO.widget.Slider.ANIM_AVAIL;
106
107 /**
108 * Set to false to disable a background click thumb move
109 * @property backgroundEnabled
110 * @type boolean
111 */
112 this.backgroundEnabled = true;
113
114 /**
115 * Adjustment factor for tick animation, the more ticks, the
116 * faster the animation (by default)
117 * @property tickPause
118 * @type int
119 */
120 this.tickPause = 40;
121
122 /**
123 * Enables the arrow, home and end keys, defaults to true.
124 * @property enableKeys
125 * @type boolean
126 */
127 this.enableKeys = true;
128
129 /**
130 * Specifies the number of pixels the arrow keys will move the slider.
131 * Default is 25.
132 * @property keyIncrement
133 * @type int
134 */
135 this.keyIncrement = 20;
136
137 /**
138 * moveComplete is set to true when the slider has moved to its final
139 * destination. For animated slider, this value can be checked in
140 * the onChange handler to make it possible to execute logic only
141 * when the move is complete rather than at all points along the way.
142 *
143 * @property moveComplete
144 * @type Boolean
145 */
146 this.moveComplete = true;
147
148 /**
149 * If animation is configured, specifies the length of the animation
150 * in seconds.
151 * @property animationDuration
152 * @type int
153 * @default 0.2
154 */
155 this.animationDuration = 0.2;
156
157 if (oThumb._isHoriz && oThumb.xTicks && oThumb.xTicks.length) {
158 this.tickPause = Math.round(360 / oThumb.xTicks.length);
159 } else if (oThumb.yTicks && oThumb.yTicks.length) {
160 this.tickPause = Math.round(360 / oThumb.yTicks.length);
161 }
162
163
164 // delegate thumb methods
165 oThumb.onMouseDown = function () { return self.focus(); };
166 //oThumb.b4MouseDown = function () { return self.b4MouseDown(); };
167 // oThumb.lock = function() { self.lock(); };
168 // oThumb.unlock = function() { self.unlock(); };
169 oThumb.onMouseUp = function() { self.thumbMouseUp(); };
170 oThumb.onDrag = function() { self.fireEvents(); };
171 oThumb.onAvailable = function() { return self.setStartSliderState(); };
172 }
173};
174
175/**
176 * Factory method for creating a horizontal slider
177 * @method YAHOO.widget.Slider.getHorizSlider
178 * @static
179 * @param {String} sBGElId the id of the slider's background element
180 * @param {String} sHandleElId the id of the thumb element
181 * @param {int} iLeft the number of pixels the element can move left
182 * @param {int} iRight the number of pixels the element can move right
183 * @param {int} iTickSize optional parameter for specifying that the element
184 * should move a certain number pixels at a time.
185 * @return {Slider} a horizontal slider control
186 */
187YAHOO.widget.Slider.getHorizSlider =
188 function (sBGElId, sHandleElId, iLeft, iRight, iTickSize) {
189 return new YAHOO.widget.Slider(sBGElId, sBGElId,
190 new YAHOO.widget.SliderThumb(sHandleElId, sBGElId,
191 iLeft, iRight, 0, 0, iTickSize), "horiz");
192};
193
194/**
195 * Factory method for creating a vertical slider
196 * @method YAHOO.widget.Slider.getVertSlider
197 * @static
198 * @param {String} sBGElId the id of the slider's background element
199 * @param {String} sHandleElId the id of the thumb element
200 * @param {int} iUp the number of pixels the element can move up
201 * @param {int} iDown the number of pixels the element can move down
202 * @param {int} iTickSize optional parameter for specifying that the element
203 * should move a certain number pixels at a time.
204 * @return {Slider} a vertical slider control
205 */
206YAHOO.widget.Slider.getVertSlider =
207 function (sBGElId, sHandleElId, iUp, iDown, iTickSize) {
208 return new YAHOO.widget.Slider(sBGElId, sBGElId,
209 new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, 0, 0,
210 iUp, iDown, iTickSize), "vert");
211};
212
213/**
214 * Factory method for creating a slider region like the one in the color
215 * picker example
216 * @method YAHOO.widget.Slider.getSliderRegion
217 * @static
218 * @param {String} sBGElId the id of the slider's background element
219 * @param {String} sHandleElId the id of the thumb element
220 * @param {int} iLeft the number of pixels the element can move left
221 * @param {int} iRight the number of pixels the element can move right
222 * @param {int} iUp the number of pixels the element can move up
223 * @param {int} iDown the number of pixels the element can move down
224 * @param {int} iTickSize optional parameter for specifying that the element
225 * should move a certain number pixels at a time.
226 * @return {Slider} a slider region control
227 */
228YAHOO.widget.Slider.getSliderRegion =
229 function (sBGElId, sHandleElId, iLeft, iRight, iUp, iDown, iTickSize) {
230 return new YAHOO.widget.Slider(sBGElId, sBGElId,
231 new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, iLeft, iRight,
232 iUp, iDown, iTickSize), "region");
233};
234
235/**
236 * By default, animation is available if the animation library is detected.
237 * @property YAHOO.widget.Slider.ANIM_AVAIL
238 * @static
239 * @type boolean
240 */
241YAHOO.widget.Slider.ANIM_AVAIL = true;
242
243YAHOO.extend(YAHOO.widget.Slider, YAHOO.util.DragDrop, {
244
245 onAvailable: function() {
246 var Event = YAHOO.util.Event;
247 Event.on(this.id, "keydown", this.handleKeyDown, this, true);
248 Event.on(this.id, "keypress", this.handleKeyPress, this, true);
249 },
250
251 handleKeyPress: function(e) {
252 if (this.enableKeys) {
253 var Event = YAHOO.util.Event;
254 var kc = Event.getCharCode(e);
255 switch (kc) {
256 case 0x25: // left
257 case 0x26: // up
258 case 0x27: // right
259 case 0x28: // down
260 case 0x24: // home
261 case 0x23: // end
262 Event.preventDefault(e);
263 break;
264 default:
265 }
266 }
267 },
268
269 handleKeyDown: function(e) {
270 if (this.enableKeys) {
271 var Event = YAHOO.util.Event;
272
273 var kc = Event.getCharCode(e), t=this.thumb;
274 var h=this.getXValue(),v=this.getYValue();
275
276 var horiz = false;
277 var changeValue = true;
278 switch (kc) {
279
280 // left
281 case 0x25: h -= this.keyIncrement; break;
282
283 // up
284 case 0x26: v -= this.keyIncrement; break;
285
286 // right
287 case 0x27: h += this.keyIncrement; break;
288
289 // down
290 case 0x28: v += this.keyIncrement; break;
291
292 // home
293 case 0x24: h = t.leftConstraint;
294 v = t.topConstraint;
295 break;
296
297 // end
298 case 0x23: h = t.rightConstraint;
299 v = t.bottomConstraint;
300 break;
301
302 default: changeValue = false;
303 }
304
305 if (changeValue) {
306 if (t._isRegion) {
307 this.setRegionValue(h, v, true);
308 } else {
309 var newVal = (t._isHoriz) ? h : v;
310 this.setValue(newVal, true);
311 }
312 Event.stopEvent(e);
313 }
314
315 }
316 },
317
318 /**
319 * Initialization that sets up the value offsets once the elements are ready
320 * @method setSliderStartState
321 */
322 setStartSliderState: function() {
323
324
325 this.setThumbCenterPoint();
326
327 /**
328 * The basline position of the background element, used
329 * to determine if the background has moved since the last
330 * operation.
331 * @property baselinePos
332 * @type [int, int]
333 */
334 this.baselinePos = YAHOO.util.Dom.getXY(this.getEl());
335
336 this.thumb.startOffset = this.thumb.getOffsetFromParent(this.baselinePos);
337
338 if (this.thumb._isRegion) {
339 if (this.deferredSetRegionValue) {
340 this.setRegionValue.apply(this, this.deferredSetRegionValue, true);
341 this.deferredSetRegionValue = null;
342 } else {
343 this.setRegionValue(0, 0, true);
344 }
345 } else {
346 if (this.deferredSetValue) {
347 this.setValue.apply(this, this.deferredSetValue, true);
348 this.deferredSetValue = null;
349 } else {
350 this.setValue(0, true, true);
351 }
352 }
353 },
354
355 /**
356 * When the thumb is available, we cache the centerpoint of the element so
357 * we can position the element correctly when the background is clicked
358 * @method setThumbCenterPoint
359 */
360 setThumbCenterPoint: function() {
361
362 var el = this.thumb.getEl();
363
364 if (el) {
365 /**
366 * The center of the slider element is stored so we can position
367 * place it in the correct position when the background is clicked
368 * @property thumbCenterPoint
369 * @type {"x": int, "y": int}
370 */
371 this.thumbCenterPoint = {
372 x: parseInt(el.offsetWidth/2, 10),
373 y: parseInt(el.offsetHeight/2, 10)
374 };
375 }
376
377 },
378
379 /**
380 * Locks the slider, overrides YAHOO.util.DragDrop
381 * @method lock
382 */
383 lock: function() {
384 this.thumb.lock();
385 this.locked = true;
386 },
387
388 /**
389 * Unlocks the slider, overrides YAHOO.util.DragDrop
390 * @method unlock
391 */
392 unlock: function() {
393 this.thumb.unlock();
394 this.locked = false;
395 },
396
397 /**
398 * Handles mouseup event on the slider background
399 * @method thumbMouseUp
400 * @private
401 */
402 thumbMouseUp: function() {
403 if (!this.isLocked() && !this.moveComplete) {
404 this.endMove();
405 }
406
407 },
408
409 /**
410 * Returns a reference to this slider's thumb
411 * @method getThumb
412 * @return {SliderThumb} this slider's thumb
413 */
414 getThumb: function() {
415 return this.thumb;
416 },
417
418 /**
419 * Try to focus the element when clicked so we can add
420 * accessibility features
421 * @method focus
422 * @private
423 */
424 focus: function() {
425
426 // Focus the background element if possible
427 var el = this.getEl();
428
429 if (el.focus) {
430 try {
431 el.focus();
432 } catch(e) {
433 // Prevent permission denied unhandled exception in FF that can
434 // happen when setting focus while another element is handling
435 // the blur. @TODO this is still writing to the error log
436 // (unhandled error) in FF1.5 with strict error checking on.
437 }
438 }
439
440 this.verifyOffset();
441
442 if (this.isLocked()) {
443 return false;
444 } else {
445 this.onSlideStart();
446 return true;
447 }
448 },
449
450 /**
451 * Event that fires when the value of the slider has changed
452 * @method onChange
453 * @param {int} firstOffset the number of pixels the thumb has moved
454 * from its start position. Normal horizontal and vertical sliders will only
455 * have the firstOffset. Regions will have both, the first is the horizontal
456 * offset, the second the vertical.
457 * @param {int} secondOffset the y offset for region sliders
458 * @deprecated use instance.subscribe("change") instead
459 */
460 onChange: function (firstOffset, secondOffset) {
461 /* override me */
462 },
463
464 /**
465 * Event that fires when the at the beginning of the slider thumb move
466 * @method onSlideStart
467 * @deprecated use instance.subscribe("slideStart") instead
468 */
469 onSlideStart: function () {
470 /* override me */
471 },
472
473 /**
474 * Event that fires at the end of a slider thumb move
475 * @method onSliderEnd
476 * @deprecated use instance.subscribe("slideEnd") instead
477 */
478 onSlideEnd: function () {
479 /* override me */
480 },
481
482 /**
483 * Returns the slider's thumb offset from the start position
484 * @method getValue
485 * @return {int} the current value
486 */
487 getValue: function () {
488 return this.thumb.getValue();
489 },
490
491 /**
492 * Returns the slider's thumb X offset from the start position
493 * @method getXValue
494 * @return {int} the current horizontal offset
495 */
496 getXValue: function () {
497 return this.thumb.getXValue();
498 },
499
500 /**
501 * Returns the slider's thumb Y offset from the start position
502 * @method getYValue
503 * @return {int} the current vertical offset
504 */
505 getYValue: function () {
506 return this.thumb.getYValue();
507 },
508
509 /**
510 * Internal handler for the slider thumb's onChange event
511 * @method handleThumbChange
512 * @private
513 */
514 handleThumbChange: function () {
515 var t = this.thumb;
516 if (t._isRegion) {
517 t.onChange(t.getXValue(), t.getYValue());
518 this.fireEvent("change", { x: t.getXValue(), y: t.getYValue() } );
519 } else {
520 t.onChange(t.getValue());
521 this.fireEvent("change", t.getValue());
522 }
523
524 },
525
526 /**
527 * Provides a way to set the value of the slider in code.
528 * @method setValue
529 * @param {int} newOffset the number of pixels the thumb should be
530 * positioned away from the initial start point
531 * @param {boolean} skipAnim set to true to disable the animation
532 * for this move action (but not others).
533 * @param {boolean} force ignore the locked setting and set value anyway
534 * @return {boolean} true if the move was performed, false if it failed
535 */
536 setValue: function(newOffset, skipAnim, force) {
537
538 if (!this.thumb.available) {
539 this.deferredSetValue = arguments;
540 return false;
541 }
542
543 if (this.isLocked() && !force) {
544 return false;
545 }
546
547 if ( isNaN(newOffset) ) {
548 return false;
549 }
550
551 var t = this.thumb;
552 var newX, newY;
553 this.verifyOffset();
554 if (t._isRegion) {
555 return false;
556 } else if (t._isHoriz) {
557 this.onSlideStart();
558 // this.fireEvent("slideStart");
559 newX = t.initPageX + newOffset + this.thumbCenterPoint.x;
560 this.moveThumb(newX, t.initPageY, skipAnim);
561 } else {
562 this.onSlideStart();
563 // this.fireEvent("slideStart");
564 newY = t.initPageY + newOffset + this.thumbCenterPoint.y;
565 this.moveThumb(t.initPageX, newY, skipAnim);
566 }
567
568 return true;
569 },
570
571 /**
572 * Provides a way to set the value of the region slider in code.
573 * @method setRegionValue
574 * @param {int} newOffset the number of pixels the thumb should be
575 * positioned away from the initial start point (x axis for region)
576 * @param {int} newOffset2 the number of pixels the thumb should be
577 * positioned away from the initial start point (y axis for region)
578 * @param {boolean} skipAnim set to true to disable the animation
579 * for this move action (but not others).
580 * @param {boolean} force ignore the locked setting and set value anyway
581 * @return {boolean} true if the move was performed, false if it failed
582 */
583 setRegionValue: function(newOffset, newOffset2, skipAnim) {
584
585 if (!this.thumb.available) {
586 this.deferredSetRegionValue = arguments;
587 return false;
588 }
589
590 if (this.isLocked() && !force) {
591 return false;
592 }
593
594 if ( isNaN(newOffset) ) {
595 return false;
596 }
597
598 var t = this.thumb;
599 if (t._isRegion) {
600 this.onSlideStart();
601 var newX = t.initPageX + newOffset + this.thumbCenterPoint.x;
602 var newY = t.initPageY + newOffset2 + this.thumbCenterPoint.y;
603 this.moveThumb(newX, newY, skipAnim);
604 return true;
605 }
606
607 return false;
608
609 },
610
611 /**
612 * Checks the background position element position. If it has moved from the
613 * baseline position, the constraints for the thumb are reset
614 * @method verifyOffset
615 * @return {boolean} True if the offset is the same as the baseline.
616 */
617 verifyOffset: function() {
618
619 var newPos = YAHOO.util.Dom.getXY(this.getEl());
620
621 if (newPos[0] != this.baselinePos[0] || newPos[1] != this.baselinePos[1]) {
622 this.thumb.resetConstraints();
623 this.baselinePos = newPos;
624 return false;
625 }
626
627 return true;
628 },
629
630 /**
631 * Move the associated slider moved to a timeout to try to get around the
632 * mousedown stealing moz does when I move the slider element between the
633 * cursor and the background during the mouseup event
634 * @method moveThumb
635 * @param {int} x the X coordinate of the click
636 * @param {int} y the Y coordinate of the click
637 * @param {boolean} skipAnim don't animate if the move happend onDrag
638 * @private
639 */
640 moveThumb: function(x, y, skipAnim) {
641
642
643 var t = this.thumb;
644 var self = this;
645
646 if (!t.available) {
647 return;
648 }
649
650
651 // this.verifyOffset();
652
653 t.setDelta(this.thumbCenterPoint.x, this.thumbCenterPoint.y);
654
655 var _p = t.getTargetCoord(x, y);
656 var p = [_p.x, _p.y];
657
658 this.fireEvent("slideStart");
659
660 if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && t._graduated && !skipAnim) {
661 // this.thumb._animating = true;
662 this.lock();
663
664 setTimeout( function() { self.moveOneTick(p); }, this.tickPause );
665
666 } else if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && !skipAnim) {
667
668 // this.thumb._animating = true;
669 this.lock();
670
671 var oAnim = new YAHOO.util.Motion(
672 t.id, { points: { to: p } },
673 this.animationDuration,
674 YAHOO.util.Easing.easeOut );
675
676 oAnim.onComplete.subscribe( function() { self.endMove(); } );
677 oAnim.animate();
678 } else {
679 t.setDragElPos(x, y);
680 // this.fireEvents();
681 this.endMove();
682 }
683 },
684
685 /**
686 * Move the slider one tick mark towards its final coordinate. Used
687 * for the animation when tick marks are defined
688 * @method moveOneTick
689 * @param {int[]} the destination coordinate
690 * @private
691 */
692 moveOneTick: function(finalCoord) {
693
694 var t = this.thumb;
695 var curCoord = YAHOO.util.Dom.getXY(t.getEl());
696 var tmp;
697
698 // var thresh = Math.min(t.tickSize + (Math.floor(t.tickSize/2)), 10);
699 // var thresh = 10;
700 // var thresh = t.tickSize + (Math.floor(t.tickSize/2));
701
702 var nextCoord = null;
703
704 if (t._isRegion) {
705 nextCoord = this._getNextX(curCoord, finalCoord);
706 var tmpX = (nextCoord) ? nextCoord[0] : curCoord[0];
707 nextCoord = this._getNextY([tmpX, curCoord[1]], finalCoord);
708
709 } else if (t._isHoriz) {
710 nextCoord = this._getNextX(curCoord, finalCoord);
711 } else {
712 nextCoord = this._getNextY(curCoord, finalCoord);
713 }
714
715
716 if (nextCoord) {
717
718 // move to the next coord
719 // YAHOO.util.Dom.setXY(t.getEl(), nextCoord);
720
721 // var el = t.getEl();
722 // YAHOO.util.Dom.setStyle(el, "left", (nextCoord[0] + this.thumb.deltaSetXY[0]) + "px");
723 // YAHOO.util.Dom.setStyle(el, "top", (nextCoord[1] + this.thumb.deltaSetXY[1]) + "px");
724
725 this.thumb.alignElWithMouse(t.getEl(), nextCoord[0], nextCoord[1]);
726
727 // check if we are in the final position, if not make a recursive call
728 if (!(nextCoord[0] == finalCoord[0] && nextCoord[1] == finalCoord[1])) {
729 var self = this;
730 setTimeout(function() { self.moveOneTick(finalCoord); },
731 this.tickPause);
732 } else {
733 this.endMove();
734 }
735 } else {
736 this.endMove();
737 }
738
739 //this.tickPause = Math.round(this.tickPause/2);
740 },
741
742 /**
743 * Returns the next X tick value based on the current coord and the target coord.
744 * @method _getNextX
745 * @private
746 */
747 _getNextX: function(curCoord, finalCoord) {
748 var t = this.thumb;
749 var thresh;
750 var tmp = [];
751 var nextCoord = null;
752 if (curCoord[0] > finalCoord[0]) {
753 thresh = t.tickSize - this.thumbCenterPoint.x;
754 tmp = t.getTargetCoord( curCoord[0] - thresh, curCoord[1] );
755 nextCoord = [tmp.x, tmp.y];
756 } else if (curCoord[0] < finalCoord[0]) {
757 thresh = t.tickSize + this.thumbCenterPoint.x;
758 tmp = t.getTargetCoord( curCoord[0] + thresh, curCoord[1] );
759 nextCoord = [tmp.x, tmp.y];
760 } else {
761 // equal, do nothing
762 }
763
764 return nextCoord;
765 },
766
767 /**
768 * Returns the next Y tick value based on the current coord and the target coord.
769 * @method _getNextY
770 * @private
771 */
772 _getNextY: function(curCoord, finalCoord) {
773 var t = this.thumb;
774 var thresh;
775 var tmp = [];
776 var nextCoord = null;
777
778 if (curCoord[1] > finalCoord[1]) {
779 thresh = t.tickSize - this.thumbCenterPoint.y;
780 tmp = t.getTargetCoord( curCoord[0], curCoord[1] - thresh );
781 nextCoord = [tmp.x, tmp.y];
782 } else if (curCoord[1] < finalCoord[1]) {
783 thresh = t.tickSize + this.thumbCenterPoint.y;
784 tmp = t.getTargetCoord( curCoord[0], curCoord[1] + thresh );
785 nextCoord = [tmp.x, tmp.y];
786 } else {
787 // equal, do nothing
788 }
789
790 return nextCoord;
791 },
792
793 /**
794 * Resets the constraints before moving the thumb.
795 * @method b4MouseDown
796 * @private
797 */
798 b4MouseDown: function(e) {
799 this.thumb.autoOffset();
800 this.thumb.resetConstraints();
801 },
802
803 /**
804 * Handles the mousedown event for the slider background
805 * @method onMouseDown
806 * @private
807 */
808 onMouseDown: function(e) {
809 // this.resetConstraints(true);
810 // this.thumb.resetConstraints(true);
811
812 if (! this.isLocked() && this.backgroundEnabled) {
813 var x = YAHOO.util.Event.getPageX(e);
814 var y = YAHOO.util.Event.getPageY(e);
815
816 this.focus();
817 this.moveThumb(x, y);
818 }
819
820 },
821
822 /**
823 * Handles the onDrag event for the slider background
824 * @method onDrag
825 * @private
826 */
827 onDrag: function(e) {
828 if (! this.isLocked()) {
829 var x = YAHOO.util.Event.getPageX(e);
830 var y = YAHOO.util.Event.getPageY(e);
831 this.moveThumb(x, y, true);
832 }
833 },
834
835 /**
836 * Fired when the slider movement ends
837 * @method endMove
838 * @private
839 */
840 endMove: function () {
841 // this._animating = false;
842 this.unlock();
843 this.moveComplete = true;
844 this.fireEvents();
845 },
846
847 /**
848 * Fires the change event if the value has been changed. Ignored if we are in
849 * the middle of an animation as the event will fire when the animation is
850 * complete
851 * @method fireEvents
852 * @private
853 */
854 fireEvents: function () {
855
856 var t = this.thumb;
857
858 t.cachePosition();
859
860 if (! this.isLocked()) {
861 if (t._isRegion) {
862 var newX = t.getXValue();
863 var newY = t.getYValue();
864
865 if (newX != this.previousX || newY != this.previousY) {
866 this.onChange(newX, newY);
867 this.fireEvent("change", { x: newX, y: newY });
868 }
869
870 this.previousX = newX;
871 this.previousY = newY;
872
873 } else {
874 var newVal = t.getValue();
875 if (newVal != this.previousVal) {
876 this.onChange( newVal );
877 this.fireEvent("change", newVal);
878 }
879 this.previousVal = newVal;
880 }
881
882 if (this.moveComplete) {
883 this.onSlideEnd();
884 this.fireEvent("slideEnd");
885 this.moveComplete = false;
886 }
887
888 }
889 },
890
891 /**
892 * Slider toString
893 * @method toString
894 * @return {string} string representation of the instance
895 */
896 toString: function () {
897 return ("Slider (" + this.type +") " + this.id);
898 }
899
900});
901
902YAHOO.augment(YAHOO.widget.Slider, YAHOO.util.EventProvider);
903
904/**
905 * A drag and drop implementation to be used as the thumb of a slider.
906 * @class SliderThumb
907 * @extends YAHOO.util.DD
908 * @constructor
909 * @param {String} id the id of the slider html element
910 * @param {String} sGroup the group of related DragDrop items
911 * @param {int} iLeft the number of pixels the element can move left
912 * @param {int} iRight the number of pixels the element can move right
913 * @param {int} iUp the number of pixels the element can move up
914 * @param {int} iDown the number of pixels the element can move down
915 * @param {int} iTickSize optional parameter for specifying that the element
916 * should move a certain number pixels at a time.
917 */
918YAHOO.widget.SliderThumb = function(id, sGroup, iLeft, iRight, iUp, iDown, iTickSize) {
919
920 if (id) {
921 this.init(id, sGroup);
922
923 /**
924 * The id of the thumbs parent HTML element (the slider background
925 * element).
926 * @property parentElId
927 * @type string
928 */
929 this.parentElId = sGroup;
930 }
931
932 //this.removeInvalidHandleType("A");
933
934
935 /**
936 * Overrides the isTarget property in YAHOO.util.DragDrop
937 * @property isTarget
938 * @private
939 */
940 this.isTarget = false;
941
942 /**
943 * The tick size for this slider
944 * @property tickSize
945 * @type int
946 * @private
947 */
948 this.tickSize = iTickSize;
949
950 /**
951 * Informs the drag and drop util that the offsets should remain when
952 * resetting the constraints. This preserves the slider value when
953 * the constraints are reset
954 * @property maintainOffset
955 * @type boolean
956 * @private
957 */
958 this.maintainOffset = true;
959
960 this.initSlider(iLeft, iRight, iUp, iDown, iTickSize);
961
962 /**
963 * Turns off the autoscroll feature in drag and drop
964 * @property scroll
965 * @private
966 */
967 this.scroll = false;
968
969};
970
971YAHOO.extend(YAHOO.widget.SliderThumb, YAHOO.util.DD, {
972
973 /**
974 * The (X and Y) difference between the thumb location and its parent
975 * (the slider background) when the control is instantiated.
976 * @property startOffset
977 * @type [int, int]
978 */
979 startOffset: null,
980
981 /**
982 * Flag used to figure out if this is a horizontal or vertical slider
983 * @property _isHoriz
984 * @type boolean
985 * @private
986 */
987 _isHoriz: false,
988
989 /**
990 * Cache the last value so we can check for change
991 * @property _prevVal
992 * @type int
993 * @private
994 */
995 _prevVal: 0,
996
997 /**
998 * The slider is _graduated if there is a tick interval defined
999 * @property _graduated
1000 * @type boolean
1001 * @private
1002 */
1003 _graduated: false,
1004
1005 /**
1006 * Returns the difference between the location of the thumb and its parent.
1007 * @method getOffsetFromParent
1008 * @param {[int, int]} parentPos Optionally accepts the position of the parent
1009 * @type [int, int]
1010 */
1011 getOffsetFromParent: function(parentPos) {
1012 var myPos = YAHOO.util.Dom.getXY(this.getEl());
1013 var ppos = parentPos || YAHOO.util.Dom.getXY(this.parentElId);
1014
1015 return [ (myPos[0] - ppos[0]), (myPos[1] - ppos[1]) ];
1016 },
1017
1018 /**
1019 * Set up the slider, must be called in the constructor of all subclasses
1020 * @method initSlider
1021 * @param {int} iLeft the number of pixels the element can move left
1022 * @param {int} iRight the number of pixels the element can move right
1023 * @param {int} iUp the number of pixels the element can move up
1024 * @param {int} iDown the number of pixels the element can move down
1025 * @param {int} iTickSize the width of the tick interval.
1026 */
1027 initSlider: function (iLeft, iRight, iUp, iDown, iTickSize) {
1028
1029 this.setXConstraint(iLeft, iRight, iTickSize);
1030 this.setYConstraint(iUp, iDown, iTickSize);
1031
1032 if (iTickSize && iTickSize > 1) {
1033 this._graduated = true;
1034 }
1035
1036 this._isHoriz = (iLeft || iRight);
1037 this._isVert = (iUp || iDown);
1038 this._isRegion = (this._isHoriz && this._isVert);
1039
1040 },
1041
1042 /**
1043 * Clear's the slider's ticks
1044 * @method clearTicks
1045 */
1046 clearTicks: function () {
1047 YAHOO.widget.SliderThumb.superclass.clearTicks.call(this);
1048 this._graduated = false;
1049 },
1050
1051 /**
1052 * Gets the current offset from the element's start position in
1053 * pixels.
1054 * @method getValue
1055 * @return {int} the number of pixels (positive or negative) the
1056 * slider has moved from the start position.
1057 */
1058 getValue: function () {
1059 if (!this.available) { return 0; }
1060 var val = (this._isHoriz) ? this.getXValue() : this.getYValue();
1061 return val;
1062 },
1063
1064 /**
1065 * Gets the current X offset from the element's start position in
1066 * pixels.
1067 * @method getXValue
1068 * @return {int} the number of pixels (positive or negative) the
1069 * slider has moved horizontally from the start position.
1070 */
1071 getXValue: function () {
1072 if (!this.available) { return 0; }
1073 var newOffset = this.getOffsetFromParent();
1074 return (newOffset[0] - this.startOffset[0]);
1075 },
1076
1077 /**
1078 * Gets the current Y offset from the element's start position in
1079 * pixels.
1080 * @method getYValue
1081 * @return {int} the number of pixels (positive or negative) the
1082 * slider has moved vertically from the start position.
1083 */
1084 getYValue: function () {
1085 if (!this.available) { return 0; }
1086 var newOffset = this.getOffsetFromParent();
1087 return (newOffset[1] - this.startOffset[1]);
1088 },
1089
1090 /**
1091 * Thumb toString
1092 * @method toString
1093 * @return {string} string representation of the instance
1094 */
1095 toString: function () {
1096 return "SliderThumb " + this.id;
1097 },
1098
1099 /**
1100 * The onchange event for the handle/thumb is delegated to the YAHOO.widget.Slider
1101 * instance it belongs to.
1102 * @method onChange
1103 * @private
1104 */
1105 onChange: function (x, y) {
1106 }
1107
1108});
1109
1110if ("undefined" == typeof YAHOO.util.Anim) {
1111 YAHOO.widget.Slider.ANIM_AVAIL = false;
1112}
1113
diff --git a/frontend/beta/js/YUI/tabview.js b/frontend/beta/js/YUI/tabview.js
new file mode 100644
index 0000000..34a09bb
--- a/dev/null
+++ b/frontend/beta/js/YUI/tabview.js
@@ -0,0 +1,1950 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7(function() {
8
9 YAHOO.util.Lang = {
10 isArray: function(val) { // frames lose type, so test constructor string
11 if (val.constructor && val.constructor.toString().indexOf('Array') > -1) {
12 return true;
13 } else {
14 return YAHOO.util.Lang.isObject(val) && val.constructor == Array;
15 }
16 },
17
18 isBoolean: function(val) {
19 return typeof val == 'boolean';
20 },
21
22 isFunction: function(val) {
23 return typeof val == 'function';
24 },
25
26 isNull: function(val) {
27 return val === null;
28 },
29
30 isNumber: function(val) {
31 return !isNaN(val);
32 },
33
34 isObject: function(val) {
35 return typeof val == 'object' || YAHOO.util.Lang.isFunction(val);
36 },
37
38 isString: function(val) {
39 return typeof val == 'string';
40 },
41
42 isUndefined: function(val) {
43 return typeof val == 'undefined';
44 }
45 };
46})();/**
47 * Provides Attribute configurations.
48 * @namespace YAHOO.util
49 * @class Attribute
50 * @constructor
51 * @param hash {Object} The intial Attribute.
52 * @param {YAHOO.util.AttributeProvider} The owner of the Attribute instance.
53 */
54
55YAHOO.util.Attribute = function(hash, owner) {
56 if (owner) {
57 this.owner = owner;
58 this.configure(hash, true);
59 }
60};
61
62YAHOO.util.Attribute.prototype = {
63 /**
64 * The name of the attribute.
65 * @property name
66 * @type String
67 */
68 name: undefined,
69
70 /**
71 * The value of the attribute.
72 * @property value
73 * @type String
74 */
75 value: null,
76
77 /**
78 * The owner of the attribute.
79 * @property owner
80 * @type YAHOO.util.AttributeProvider
81 */
82 owner: null,
83
84 /**
85 * Whether or not the attribute is read only.
86 * @property readOnly
87 * @type Boolean
88 */
89 readOnly: false,
90
91 /**
92 * Whether or not the attribute can only be written once.
93 * @property writeOnce
94 * @type Boolean
95 */
96 writeOnce: false,
97
98 /**
99 * The attribute's initial configuration.
100 * @private
101 * @property _initialConfig
102 * @type Object
103 */
104 _initialConfig: null,
105
106 /**
107 * Whether or not the attribute's value has been set.
108 * @private
109 * @property _written
110 * @type Boolean
111 */
112 _written: false,
113
114 /**
115 * The method to use when setting the attribute's value.
116 * The method recieves the new value as the only argument.
117 * @property method
118 * @type Function
119 */
120 method: null,
121
122 /**
123 * The validator to use when setting the attribute's value.
124 * @property validator
125 * @type Function
126 * @return Boolean
127 */
128 validator: null,
129
130 /**
131 * Retrieves the current value of the attribute.
132 * @method getValue
133 * @return {any} The current value of the attribute.
134 */
135 getValue: function() {
136 return this.value;
137 },
138
139 /**
140 * Sets the value of the attribute and fires beforeChange and change events.
141 * @method setValue
142 * @param {Any} value The value to apply to the attribute.
143 * @param {Boolean} silent If true the change events will not be fired.
144 * @return {Boolean} Whether or not the value was set.
145 */
146 setValue: function(value, silent) {
147 var beforeRetVal;
148 var owner = this.owner;
149 var name = this.name;
150
151 var event = {
152 type: name,
153 prevValue: this.getValue(),
154 newValue: value
155 };
156
157 if (this.readOnly || ( this.writeOnce && this._written) ) {
158 return false; // write not allowed
159 }
160
161 if (this.validator && !this.validator.call(owner, value) ) {
162 return false; // invalid value
163 }
164
165 if (!silent) {
166 beforeRetVal = owner.fireBeforeChangeEvent(event);
167 if (beforeRetVal === false) {
168 YAHOO.log('setValue ' + name +
169 'cancelled by beforeChange event', 'info', 'Attribute');
170 return false;
171 }
172 }
173
174 if (this.method) {
175 this.method.call(owner, value);
176 }
177
178 this.value = value;
179 this._written = true;
180
181 event.type = name;
182
183 if (!silent) {
184 this.owner.fireChangeEvent(event);
185 }
186
187 return true;
188 },
189
190 /**
191 * Allows for configuring the Attribute's properties.
192 * @method configure
193 * @param {Object} map A key-value map of Attribute properties.
194 * @param {Boolean} init Whether or not this should become the initial config.
195 */
196 configure: function(map, init) {
197 map = map || {};
198 this._written = false; // reset writeOnce
199 this._initialConfig = this._initialConfig || {};
200
201 for (var key in map) {
202 if ( key && map.hasOwnProperty(key) ) {
203 this[key] = map[key];
204 if (init) {
205 this._initialConfig[key] = map[key];
206 }
207 }
208 }
209 },
210
211 /**
212 * Resets the value to the initial config value.
213 * @method resetValue
214 * @return {Boolean} Whether or not the value was set.
215 */
216 resetValue: function() {
217 return this.setValue(this._initialConfig.value);
218 },
219
220 /**
221 * Resets the attribute config to the initial config state.
222 * @method resetConfig
223 */
224 resetConfig: function() {
225 this.configure(this._initialConfig);
226 },
227
228 /**
229 * Resets the value to the current value.
230 * Useful when values may have gotten out of sync with actual properties.
231 * @method refresh
232 * @return {Boolean} Whether or not the value was set.
233 */
234 refresh: function(silent) {
235 this.setValue(this.value, silent);
236 }
237};(function() {
238 var Lang = YAHOO.util.Lang;
239
240 /*
241 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
242 Code licensed under the BSD License:
243 http://developer.yahoo.net/yui/license.txt
244 */
245
246 /**
247 * Provides and manages YAHOO.util.Attribute instances
248 * @namespace YAHOO.util
249 * @class AttributeProvider
250 * @uses YAHOO.util.EventProvider
251 */
252 YAHOO.util.AttributeProvider = function() {};
253
254 YAHOO.util.AttributeProvider.prototype = {
255
256 /**
257 * A key-value map of Attribute configurations
258 * @property _configs
259 * @protected (may be used by subclasses and augmentors)
260 * @private
261 * @type {Object}
262 */
263 _configs: null,
264 /**
265 * Returns the current value of the attribute.
266 * @method get
267 * @param {String} key The attribute whose value will be returned.
268 */
269 get: function(key){
270 var configs = this._configs || {};
271 var config = configs[key];
272
273 if (!config) {
274 YAHOO.log(key + ' not found', 'error', 'AttributeProvider');
275 return undefined;
276 }
277
278 return config.value;
279 },
280
281 /**
282 * Sets the value of a config.
283 * @method set
284 * @param {String} key The name of the attribute
285 * @param {Any} value The value to apply to the attribute
286 * @param {Boolean} silent Whether or not to suppress change events
287 * @return {Boolean} Whether or not the value was set.
288 */
289 set: function(key, value, silent){
290 var configs = this._configs || {};
291 var config = configs[key];
292
293 if (!config) {
294 YAHOO.log('set failed: ' + key + ' not found',
295 'error', 'AttributeProvider');
296 return false;
297 }
298
299 return config.setValue(value, silent);
300 },
301
302 /**
303 * Returns an array of attribute names.
304 * @method getAttributeKeys
305 * @return {Array} An array of attribute names.
306 */
307 getAttributeKeys: function(){
308 var configs = this._configs;
309 var keys = [];
310 var config;
311 for (var key in configs) {
312 config = configs[key];
313 if ( configs.hasOwnProperty(key) &&
314 !Lang.isUndefined(config) ) {
315 keys[keys.length] = key;
316 }
317 }
318
319 return keys;
320 },
321
322 /**
323 * Sets multiple attribute values.
324 * @method setAttributes
325 * @param {Object} map A key-value map of attributes
326 * @param {Boolean} silent Whether or not to suppress change events
327 */
328 setAttributes: function(map, silent){
329 for (var key in map) {
330 if ( map.hasOwnProperty(key) ) {
331 this.set(key, map[key], silent);
332 }
333 }
334 },
335
336 /**
337 * Resets the specified attribute's value to its initial value.
338 * @method resetValue
339 * @param {String} key The name of the attribute
340 * @param {Boolean} silent Whether or not to suppress change events
341 * @return {Boolean} Whether or not the value was set
342 */
343 resetValue: function(key, silent){
344 var configs = this._configs || {};
345 if (configs[key]) {
346 this.set(key, configs[key]._initialConfig.value, silent);
347 return true;
348 }
349 return false;
350 },
351
352 /**
353 * Sets the attribute's value to its current value.
354 * @method refresh
355 * @param {String | Array} key The attribute(s) to refresh
356 * @param {Boolean} silent Whether or not to suppress change events
357 */
358 refresh: function(key, silent){
359 var configs = this._configs;
360
361 key = ( ( Lang.isString(key) ) ? [key] : key ) ||
362 this.getAttributeKeys();
363
364 for (var i = 0, len = key.length; i < len; ++i) {
365 if ( // only set if there is a value and not null
366 configs[key[i]] &&
367 ! Lang.isUndefined(configs[key[i]].value) &&
368 ! Lang.isNull(configs[key[i]].value) ) {
369 configs[key[i]].refresh(silent);
370 }
371 }
372 },
373
374 /**
375 * Adds an Attribute to the AttributeProvider instance.
376 * @method register
377 * @param {String} key The attribute's name
378 * @param {Object} map A key-value map containing the
379 * attribute's properties.
380 */
381 register: function(key, map) {
382 this._configs = this._configs || {};
383
384 if (this._configs[key]) { // dont override
385 return false;
386 }
387
388 map.name = key;
389 this._configs[key] = new YAHOO.util.Attribute(map, this);
390 return true;
391 },
392
393 /**
394 * Returns the attribute's properties.
395 * @method getAttributeConfig
396 * @param {String} key The attribute's name
397 * @private
398 * @return {object} A key-value map containing all of the
399 * attribute's properties.
400 */
401 getAttributeConfig: function(key) {
402 var configs = this._configs || {};
403 var config = configs[key] || {};
404 var map = {}; // returning a copy to prevent overrides
405
406 for (key in config) {
407 if ( config.hasOwnProperty(key) ) {
408 map[key] = config[key];
409 }
410 }
411
412 return map;
413 },
414
415 /**
416 * Sets or updates an Attribute instance's properties.
417 * @method configureAttribute
418 * @param {String} key The attribute's name.
419 * @param {Object} map A key-value map of attribute properties
420 * @param {Boolean} init Whether or not this should become the intial config.
421 */
422 configureAttribute: function(key, map, init) {
423 var configs = this._configs || {};
424
425 if (!configs[key]) {
426 YAHOO.log('unable to configure, ' + key + ' not found',
427 'error', 'AttributeProvider');
428 return false;
429 }
430
431 configs[key].configure(map, init);
432 },
433
434 /**
435 * Resets an attribute to its intial configuration.
436 * @method resetAttributeConfig
437 * @param {String} key The attribute's name.
438 * @private
439 */
440 resetAttributeConfig: function(key){
441 var configs = this._configs || {};
442 configs[key].resetConfig();
443 },
444
445 /**
446 * Fires the attribute's beforeChange event.
447 * @method fireBeforeChangeEvent
448 * @param {String} key The attribute's name.
449 * @param {Obj} e The event object to pass to handlers.
450 */
451 fireBeforeChangeEvent: function(e) {
452 var type = 'before';
453 type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change';
454 e.type = type;
455 return this.fireEvent(e.type, e);
456 },
457
458 /**
459 * Fires the attribute's change event.
460 * @method fireChangeEvent
461 * @param {String} key The attribute's name.
462 * @param {Obj} e The event object to pass to the handlers.
463 */
464 fireChangeEvent: function(e) {
465 e.type += 'Change';
466 return this.fireEvent(e.type, e);
467 }
468 };
469
470 YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider);
471})();(function() {
472// internal shorthand
473var Dom = YAHOO.util.Dom,
474 Lang = YAHOO.util.Lang,
475 EventPublisher = YAHOO.util.EventPublisher,
476 AttributeProvider = YAHOO.util.AttributeProvider;
477
478/**
479 * Element provides an interface to an HTMLElement's attributes and common
480 * methods. Other commonly used attributes are added as well.
481 * @namespace YAHOO.util
482 * @class Element
483 * @uses YAHOO.util.AttributeProvider
484 * @constructor
485 * @param el {HTMLElement | String} The html element that
486 * represents the Element.
487 * @param {Object} map A key-value map of initial config names and values
488 */
489YAHOO.util.Element = function(el, map) {
490 if (arguments.length) {
491 this.init(el, map);
492 }
493};
494
495YAHOO.util.Element.prototype = {
496 /**
497 * Dom events supported by the Element instance.
498 * @property DOM_EVENTS
499 * @type Object
500 */
501 DOM_EVENTS: null,
502
503 /**
504 * Wrapper for HTMLElement method.
505 * @method appendChild
506 * @param {Boolean} deep Whether or not to do a deep clone
507 */
508 appendChild: function(child) {
509 child = child.get ? child.get('element') : child;
510 this.get('element').appendChild(child);
511 },
512
513 /**
514 * Wrapper for HTMLElement method.
515 * @method getElementsByTagName
516 * @param {String} tag The tagName to collect
517 */
518 getElementsByTagName: function(tag) {
519 return this.get('element').getElementsByTagName(tag);
520 },
521
522 /**
523 * Wrapper for HTMLElement method.
524 * @method hasChildNodes
525 * @return {Boolean} Whether or not the element has childNodes
526 */
527 hasChildNodes: function() {
528 return this.get('element').hasChildNodes();
529 },
530
531 /**
532 * Wrapper for HTMLElement method.
533 * @method insertBefore
534 * @param {HTMLElement} element The HTMLElement to insert
535 * @param {HTMLElement} before The HTMLElement to insert
536 * the element before.
537 */
538 insertBefore: function(element, before) {
539 element = element.get ? element.get('element') : element;
540 before = (before && before.get) ? before.get('element') : before;
541
542 this.get('element').insertBefore(element, before);
543 },
544
545 /**
546 * Wrapper for HTMLElement method.
547 * @method removeChild
548 * @param {HTMLElement} child The HTMLElement to remove
549 */
550 removeChild: function(child) {
551 child = child.get ? child.get('element') : child;
552 this.get('element').removeChild(child);
553 return true;
554 },
555
556 /**
557 * Wrapper for HTMLElement method.
558 * @method replaceChild
559 * @param {HTMLElement} newNode The HTMLElement to insert
560 * @param {HTMLElement} oldNode The HTMLElement to replace
561 */
562 replaceChild: function(newNode, oldNode) {
563 newNode = newNode.get ? newNode.get('element') : newNode;
564 oldNode = oldNode.get ? oldNode.get('element') : oldNode;
565 return this.get('element').replaceChild(newNode, oldNode);
566 },
567
568
569 /**
570 * Registers Element specific attributes.
571 * @method initAttributes
572 * @param {Object} map A key-value map of initial attribute configs
573 */
574 initAttributes: function(map) {
575 map = map || {};
576 var element = Dom.get(map.element) || null;
577
578 /**
579 * The HTMLElement the Element instance refers to.
580 * @config element
581 * @type HTMLElement
582 */
583 this.register('element', {
584 value: element,
585 readOnly: true
586 });
587 },
588
589 /**
590 * Adds a listener for the given event. These may be DOM or
591 * customEvent listeners. Any event that is fired via fireEvent
592 * can be listened for. All handlers receive an event object.
593 * @method addListener
594 * @param {String} type The name of the event to listen for
595 * @param {Function} fn The handler to call when the event fires
596 * @param {Any} obj A variable to pass to the handler
597 * @param {Object} scope The object to use for the scope of the handler
598 */
599 addListener: function(type, fn, obj, scope) {
600 var el = this.get('element');
601 var scope = scope || this;
602
603 el = this.get('id') || el;
604
605 if (!this._events[type]) { // create on the fly
606 if ( this.DOM_EVENTS[type] ) {
607 YAHOO.util.Event.addListener(el, type, function(e) {
608 if (e.srcElement && !e.target) { // supplement IE with target
609 e.target = e.srcElement;
610 }
611 this.fireEvent(type, e);
612 }, obj, scope);
613 }
614
615 this.createEvent(type, this);
616 this._events[type] = true;
617 }
618
619 this.subscribe.apply(this, arguments); // notify via customEvent
620 },
621
622
623 /**
624 * Alias for addListener
625 * @method on
626 * @param {String} type The name of the event to listen for
627 * @param {Function} fn The function call when the event fires
628 * @param {Any} obj A variable to pass to the handler
629 * @param {Object} scope The object to use for the scope of the handler
630 */
631 on: function() { this.addListener.apply(this, arguments); },
632
633
634 /**
635 * Remove an event listener
636 * @method removeListener
637 * @param {String} type The name of the event to listen for
638 * @param {Function} fn The function call when the event fires
639 */
640 removeListener: function(type, fn) {
641 this.unsubscribe.apply(this, arguments);
642 },
643
644 /**
645 * Wrapper for Dom method.
646 * @method addClass
647 * @param {String} className The className to add
648 */
649 addClass: function(className) {
650 Dom.addClass(this.get('element'), className);
651 },
652
653 /**
654 * Wrapper for Dom method.
655 * @method getElementsByClassName
656 * @param {String} className The className to collect
657 * @param {String} tag (optional) The tag to use in
658 * conjunction with class name
659 * @return {Array} Array of HTMLElements
660 */
661 getElementsByClassName: function(className, tag) {
662 return Dom.getElementsByClassName(className, tag,
663 this.get('element') );
664 },
665
666 /**
667 * Wrapper for Dom method.
668 * @method hasClass
669 * @param {String} className The className to add
670 * @return {Boolean} Whether or not the element has the class name
671 */
672 hasClass: function(className) {
673 return Dom.hasClass(this.get('element'), className);
674 },
675
676 /**
677 * Wrapper for Dom method.
678 * @method removeClass
679 * @param {String} className The className to remove
680 */
681 removeClass: function(className) {
682 return Dom.removeClass(this.get('element'), className);
683 },
684
685 /**
686 * Wrapper for Dom method.
687 * @method replaceClass
688 * @param {String} oldClassName The className to replace
689 * @param {String} newClassName The className to add
690 */
691 replaceClass: function(oldClassName, newClassName) {
692 return Dom.replaceClass(this.get('element'),
693 oldClassName, newClassName);
694 },
695
696 /**
697 * Wrapper for Dom method.
698 * @method setStyle
699 * @param {String} property The style property to set
700 * @param {String} value The value to apply to the style property
701 */
702 setStyle: function(property, value) {
703 return Dom.setStyle(this.get('element'), property, value);
704 },
705
706 /**
707 * Wrapper for Dom method.
708 * @method getStyle
709 * @param {String} property The style property to retrieve
710 * @return {String} The current value of the property
711 */
712 getStyle: function(property) {
713 return Dom.getStyle(this.get('element'), property);
714 },
715
716 /**
717 * Apply any queued set calls.
718 * @method fireQueue
719 */
720 fireQueue: function() {
721 var queue = this._queue;
722 for (var i = 0, len = queue.length; i < len; ++i) {
723 this[queue[i][0]].apply(this, queue[i][1]);
724 }
725 },
726
727 /**
728 * Appends the HTMLElement into either the supplied parentNode.
729 * @method appendTo
730 * @param {HTMLElement | Element} parentNode The node to append to
731 * @param {HTMLElement | Element} before An optional node to insert before
732 */
733 appendTo: function(parent, before) {
734 parent = (parent.get) ? parent.get('element') : Dom.get(parent);
735
736 before = (before && before.get) ?
737 before.get('element') : Dom.get(before);
738 var element = this.get('element');
739
740 var newAddition = !Dom.inDocument(element);
741
742 if (!element) {
743 YAHOO.log('appendTo failed: element not available',
744 'error', 'Element');
745 return false;
746 }
747
748 if (!parent) {
749 YAHOO.log('appendTo failed: parent not available',
750 'error', 'Element');
751 return false;
752 }
753
754 if (element.parent != parent) {
755 if (before) {
756 parent.insertBefore(element, before);
757 } else {
758 parent.appendChild(element);
759 }
760 }
761
762 YAHOO.log(element + 'appended to ' + parent);
763
764 if (!newAddition) {
765 return false; // note return; no refresh if in document
766 }
767
768 // if a new addition, refresh HTMLElement any applied attributes
769 var keys = this.getAttributeKeys();
770
771 for (var key in keys) { // only refresh HTMLElement attributes
772 if ( !Lang.isUndefined(element[key]) ) {
773 this.refresh(key);
774 }
775 }
776 },
777
778 get: function(key) {
779 var configs = this._configs || {};
780 var el = configs.element; // avoid loop due to 'element'
781 if (el && !configs[key] && !Lang.isUndefined(el.value[key]) ) {
782 return el.value[key];
783 }
784
785 return AttributeProvider.prototype.get.call(this, key);
786 },
787
788 set: function(key, value, silent) {
789 var el = this.get('element');
790 if (!el) {
791 this._queue[key] = ['set', arguments];
792 return false;
793 }
794
795 // set it on the element if not a property
796 if ( !this._configs[key] && !Lang.isUndefined(el[key]) ) {
797 _registerHTMLAttr(this, key);
798 }
799
800 return AttributeProvider.prototype.set.apply(this, arguments);
801 },
802
803 register: function(key) { // protect html attributes
804 var configs = this._configs || {};
805 var element = this.get('element') || null;
806
807 if ( element && !Lang.isUndefined(element[key]) ) {
808 YAHOO.log(key + ' is reserved for ' + element,
809 'error', 'Element');
810 return false;
811 }
812
813 return AttributeProvider.prototype.register.apply(this, arguments);
814 },
815
816 configureAttribute: function(property, map, init) { // protect html attributes
817 if (!this._configs[property] && this._configs.element &&
818 !Lang.isUndefined(this._configs.element[property]) ) {
819 _registerHTMLAttr(this, property, map);
820 return false;
821 }
822
823 return AttributeProvider.prototype.configure.apply(this, arguments);
824 },
825
826 getAttributeKeys: function() {
827 var el = this.get('element');
828 var keys = AttributeProvider.prototype.getAttributeKeys.call(this);
829
830 //add any unconfigured element keys
831 for (var key in el) {
832 if (!this._configs[key]) {
833 keys[key] = keys[key] || el[key];
834 }
835 }
836
837 return keys;
838 },
839
840 init: function(el, attr) {
841 this._queue = this._queue || [];
842 this._events = this._events || {};
843 this._configs = this._configs || {};
844 attr = attr || {};
845 attr.element = attr.element || el || null;
846
847 this.DOM_EVENTS = {
848 'click': true,
849 'keydown': true,
850 'keypress': true,
851 'keyup': true,
852 'mousedown': true,
853 'mousemove': true,
854 'mouseout': true,
855 'mouseover': true,
856 'mouseup': true
857 };
858
859 var readyHandler = function() {
860 this.initAttributes(attr);
861
862 this.setAttributes(attr, true);
863 this.fireQueue();
864 this.fireEvent('contentReady', {
865 type: 'contentReady',
866 target: attr.element
867 });
868 };
869
870 if ( Lang.isString(el) ) {
871 _registerHTMLAttr(this, 'id', { value: el });
872 YAHOO.util.Event.onAvailable(el, function() {
873 attr.element = Dom.get(el);
874 this.fireEvent('available', {
875 type: 'available',
876 target: attr.element
877 });
878 }, this, true);
879
880 YAHOO.util.Event.onContentReady(el, function() {
881 readyHandler.call(this);
882 }, this, true);
883 } else {
884 readyHandler.call(this);
885 }
886 }
887};
888
889/**
890 * Sets the value of the property and fires beforeChange and change events.
891 * @private
892 * @method _registerHTMLAttr
893 * @param {YAHOO.util.Element} element The Element instance to
894 * register the config to.
895 * @param {String} key The name of the config to register
896 * @param {Object} map A key-value map of the config's params
897 */
898var _registerHTMLAttr = function(self, key, map) {
899 var el = self.get('element');
900 map = map || {};
901 map.name = key;
902 map.method = map.method || function(value) {
903 el[key] = value;
904 };
905 map.value = map.value || el[key];
906 self._configs[key] = new YAHOO.util.Attribute(map, self);
907};
908
909/**
910 * Fires when the Element's HTMLElement can be retrieved by Id.
911 * <p>See: <a href="#addListener">Element.addListener</a></p>
912 * <p><strong>Event fields:</strong><br>
913 * <code>&lt;String&gt; type</code> available<br>
914 * <code>&lt;HTMLElement&gt;
915 * target</code> the HTMLElement bound to this Element instance<br>
916 * <p><strong>Usage:</strong><br>
917 * <code>var handler = function(e) {var target = e.target};<br>
918 * myTabs.addListener('available', handler);</code></p>
919 * @event available
920 */
921
922/**
923 * Fires when the Element's HTMLElement subtree is rendered.
924 * <p>See: <a href="#addListener">Element.addListener</a></p>
925 * <p><strong>Event fields:</strong><br>
926 * <code>&lt;String&gt; type</code> contentReady<br>
927 * <code>&lt;HTMLElement&gt;
928 * target</code> the HTMLElement bound to this Element instance<br>
929 * <p><strong>Usage:</strong><br>
930 * <code>var handler = function(e) {var target = e.target};<br>
931 * myTabs.addListener('contentReady', handler);</code></p>
932 * @event contentReady
933 */
934
935YAHOO.augment(YAHOO.util.Element, AttributeProvider);
936})();(function() {
937 var Dom = YAHOO.util.Dom,
938 Event = YAHOO.util.Event,
939 Lang = YAHOO.util.Lang;
940
941 /**
942 * A representation of a Tab's label and content.
943 * @namespace YAHOO.widget
944 * @class Tab
945 * @extends YAHOO.util.Element
946 * @constructor
947 * @param element {HTMLElement | String} (optional) The html element that
948 * represents the TabView. An element will be created if none provided.
949 * @param {Object} properties A key map of initial properties
950 */
951 Tab = function(el, attr) {
952 attr = attr || {};
953 if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) {
954 attr = el;
955 el = attr.element;
956 }
957
958 if (!el && !attr.element) {
959 el = _createTabElement.call(this, attr);
960 }
961
962 this.loadHandler = {
963 success: function(o) {
964 this.set('content', o.responseText);
965 },
966 failure: function(o) {
967 YAHOO.log('loading failed: ' + o.statusText,
968 'error', 'Tab');
969 }
970 };
971
972 Tab.superclass.constructor.call(this, el, attr);
973
974 this.DOM_EVENTS = {}; // delegating to tabView
975 };
976
977 YAHOO.extend(Tab, YAHOO.util.Element);
978 var proto = Tab.prototype;
979
980 /**
981 * The default tag name for a Tab's inner element.
982 * @property LABEL_INNER_TAGNAME
983 * @type String
984 * @default "em"
985 */
986 proto.LABEL_TAGNAME = 'em';
987
988 /**
989 * The class name applied to active tabs.
990 * @property ACTIVE_CLASSNAME
991 * @type String
992 * @default "on"
993 */
994 proto.ACTIVE_CLASSNAME = 'selected';
995
996 /**
997 * The class name applied to disabled tabs.
998 * @property DISABLED_CLASSNAME
999 * @type String
1000 * @default "disabled"
1001 */
1002 proto.DISABLED_CLASSNAME = 'disabled';
1003
1004 /**
1005 * The class name applied to dynamic tabs while loading.
1006 * @property LOADING_CLASSNAME
1007 * @type String
1008 * @default "disabled"
1009 */
1010 proto.LOADING_CLASSNAME = 'loading';
1011
1012 /**
1013 * Provides a reference to the connection request object when data is
1014 * loaded dynamically.
1015 * @property dataConnection
1016 * @type Object
1017 */
1018 proto.dataConnection = null;
1019
1020 /**
1021 * Object containing success and failure callbacks for loading data.
1022 * @property loadHandler
1023 * @type object
1024 */
1025 proto.loadHandler = null;
1026
1027 /**
1028 * Provides a readable name for the tab.
1029 * @method toString
1030 * @return String
1031 */
1032 proto.toString = function() {
1033 var el = this.get('element');
1034 var id = el.id || el.tagName;
1035 return "Tab " + id;
1036 };
1037
1038 /**
1039 * Registers TabView specific properties.
1040 * @method initAttributes
1041 * @param {Object} attr Hash of initial attributes
1042 */
1043 proto.initAttributes = function(attr) {
1044 attr = attr || {};
1045 Tab.superclass.initAttributes.call(this, attr);
1046
1047 var el = this.get('element');
1048
1049 /**
1050 * The event that triggers the tab's activation.
1051 * @config activationEvent
1052 * @type String
1053 */
1054 this.register('activationEvent', {
1055 value: attr.activationEvent || 'click'
1056 });
1057
1058 /**
1059 * The element that contains the tab's label.
1060 * @config labelEl
1061 * @type HTMLElement
1062 */
1063 this.register('labelEl', {
1064 value: attr.labelEl || _getlabelEl.call(this),
1065 method: function(value) {
1066 var current = this.get('labelEl');
1067
1068 if (current) {
1069 if (current == value) {
1070 return false; // already set
1071 }
1072
1073 this.replaceChild(value, current);
1074 } else if (el.firstChild) { // ensure label is firstChild by default
1075 this.insertBefore(value, el.firstChild);
1076 } else {
1077 this.appendChild(value);
1078 }
1079 }
1080 });
1081
1082 /**
1083 * The tab's label text (or innerHTML).
1084 * @config label
1085 * @type String
1086 */
1087 this.register('label', {
1088 value: attr.label || _getLabel.call(this),
1089 method: function(value) {
1090 var labelEl = this.get('labelEl');
1091 if (!labelEl) { // create if needed
1092 this.set('labelEl', _createlabelEl.call(this));
1093 }
1094
1095 _setLabel.call(this, value);
1096 }
1097 });
1098
1099 /**
1100 * The HTMLElement that contains the tab's content.
1101 * @config contentEl
1102 * @type HTMLElement
1103 */
1104 this.register('contentEl', { // TODO: apply className?
1105 value: attr.contentEl || document.createElement('div'),
1106 method: function(value) {
1107 var current = this.get('contentEl');
1108
1109 if (current) {
1110 if (current == value) {
1111 return false; // already set
1112 }
1113 this.replaceChild(value, current);
1114 }
1115 }
1116 });
1117
1118 /**
1119 * The tab's content.
1120 * @config content
1121 * @type String
1122 */
1123 this.register('content', {
1124 value: attr.content, // TODO: what about existing?
1125 method: function(value) {
1126 this.get('contentEl').innerHTML = value;
1127 }
1128 });
1129
1130 var _dataLoaded = false;
1131
1132 /**
1133 * The tab's data source, used for loading content dynamically.
1134 * @config dataSrc
1135 * @type String
1136 */
1137 this.register('dataSrc', {
1138 value: attr.dataSrc
1139 });
1140
1141 /**
1142 * Whether or not content should be reloaded for every view.
1143 * @config cacheData
1144 * @type Boolean
1145 * @default false
1146 */
1147 this.register('cacheData', {
1148 value: attr.cacheData || false,
1149 validator: Lang.isBoolean
1150 });
1151
1152 /**
1153 * The method to use for the data request.
1154 * @config loadMethod
1155 * @type String
1156 * @default "GET"
1157 */
1158 this.register('loadMethod', {
1159 value: attr.loadMethod || 'GET',
1160 validator: Lang.isString
1161 });
1162
1163 /**
1164 * Whether or not any data has been loaded from the server.
1165 * @config dataLoaded
1166 * @type Boolean
1167 */
1168 this.register('dataLoaded', {
1169 value: false,
1170 validator: Lang.isBoolean,
1171 writeOnce: true
1172 });
1173
1174 /**
1175 * Number if milliseconds before aborting and calling failure handler.
1176 * @config dataTimeout
1177 * @type Number
1178 * @default null
1179 */
1180 this.register('dataTimeout', {
1181 value: attr.dataTimeout || null,
1182 validator: Lang.isNumber
1183 });
1184
1185 /**
1186 * Whether or not the tab is currently active.
1187 * If a dataSrc is set for the tab, the content will be loaded from
1188 * the given source.
1189 * @config active
1190 * @type Boolean
1191 */
1192 this.register('active', {
1193 value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME),
1194 method: function(value) {
1195 if (value === true) {
1196 this.addClass(this.ACTIVE_CLASSNAME);
1197 this.set('title', 'active');
1198 } else {
1199 this.removeClass(this.ACTIVE_CLASSNAME);
1200 this.set('title', '');
1201 }
1202 },
1203 validator: function(value) {
1204 return Lang.isBoolean(value) && !this.get('disabled') ;
1205 }
1206 });
1207
1208 /**
1209 * Whether or not the tab is disabled.
1210 * @config disabled
1211 * @type Boolean
1212 */
1213 this.register('disabled', {
1214 value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME),
1215 method: function(value) {
1216 if (value === true) {
1217 Dom.addClass(this.get('element'), this.DISABLED_CLASSNAME);
1218 } else {
1219 Dom.removeClass(this.get('element'), this.DISABLED_CLASSNAME);
1220 }
1221 },
1222 validator: Lang.isBoolean
1223 });
1224
1225 /**
1226 * The href of the tab's anchor element.
1227 * @config href
1228 * @type String
1229 * @default '#'
1230 */
1231 this.register('href', {
1232 value: attr.href || '#',
1233 method: function(value) {
1234 this.getElementsByTagName('a')[0].href = value;
1235 },
1236 validator: Lang.isString
1237 });
1238
1239 /**
1240 * The Whether or not the tab's content is visible.
1241 * @config contentVisible
1242 * @type Boolean
1243 * @default false
1244 */
1245 this.register('contentVisible', {
1246 value: attr.contentVisible,
1247 method: function(value) {
1248 if (value == true) {
1249 this.get('contentEl').style.display = 'block';
1250
1251 if ( this.get('dataSrc') ) {
1252 // load dynamic content unless already loaded and caching
1253 if ( !this.get('dataLoaded') || !this.get('cacheData') ) {
1254 _dataConnect.call(this);
1255 }
1256 }
1257 } else {
1258 this.get('contentEl').style.display = 'none';
1259 }
1260 },
1261 validator: Lang.isBoolean
1262 });
1263 };
1264
1265 var _createTabElement = function(attr) {
1266 var el = document.createElement('li');
1267 var a = document.createElement('a');
1268
1269 a.href = attr.href || '#';
1270
1271 el.appendChild(a);
1272
1273 var label = attr.label || null;
1274 var labelEl = attr.labelEl || null;
1275
1276 if (labelEl) { // user supplied labelEl
1277 if (!label) { // user supplied label
1278 label = _getLabel.call(this, labelEl);
1279 }
1280 } else {
1281 labelEl = _createlabelEl.call(this);
1282 }
1283
1284 a.appendChild(labelEl);
1285
1286 return el;
1287 };
1288
1289 var _getlabelEl = function() {
1290 return this.getElementsByTagName(this.LABEL_TAGNAME)[0];
1291 };
1292
1293 var _createlabelEl = function() {
1294 var el = document.createElement(this.LABEL_TAGNAME);
1295 return el;
1296 };
1297
1298 var _setLabel = function(label) {
1299 var el = this.get('labelEl');
1300 el.innerHTML = label;
1301 };
1302
1303 var _getLabel = function() {
1304 var label,
1305 el = this.get('labelEl');
1306
1307 if (!el) {
1308 return undefined;
1309 }
1310
1311 return el.innerHTML;
1312 };
1313
1314 var _dataConnect = function() {
1315 if (!YAHOO.util.Connect) {
1316 YAHOO.log('YAHOO.util.Connect dependency not met',
1317 'error', 'Tab');
1318 return false;
1319 }
1320
1321 Dom.addClass(this.get('contentEl').parentNode, this.LOADING_CLASSNAME);
1322
1323 this.dataConnection = YAHOO.util.Connect.asyncRequest(
1324 this.get('loadMethod'),
1325 this.get('dataSrc'),
1326 {
1327 success: function(o) {
1328 this.loadHandler.success.call(this, o);
1329 this.set('dataLoaded', true);
1330 this.dataConnection = null;
1331 Dom.removeClass(this.get('contentEl').parentNode,
1332 this.LOADING_CLASSNAME);
1333 },
1334 failure: function(o) {
1335 this.loadHandler.failure.call(this, o);
1336 this.dataConnection = null;
1337 Dom.removeClass(this.get('contentEl').parentNode,
1338 this.LOADING_CLASSNAME);
1339 },
1340 scope: this,
1341 timeout: this.get('dataTimeout')
1342 }
1343 );
1344 };
1345
1346 YAHOO.widget.Tab = Tab;
1347
1348 /**
1349 * Fires before the active state is changed.
1350 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1351 * <p>If handler returns false, the change will be cancelled, and the value will not
1352 * be set.</p>
1353 * <p><strong>Event fields:</strong><br>
1354 * <code>&lt;String&gt; type</code> beforeActiveChange<br>
1355 * <code>&lt;Boolean&gt;
1356 * prevValue</code> the current value<br>
1357 * <code>&lt;Boolean&gt;
1358 * newValue</code> the new value</p>
1359 * <p><strong>Usage:</strong><br>
1360 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1361 * myTabs.addListener('beforeActiveChange', handler);</code></p>
1362 * @event beforeActiveChange
1363 */
1364
1365 /**
1366 * Fires after the active state is changed.
1367 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1368 * <p><strong>Event fields:</strong><br>
1369 * <code>&lt;String&gt; type</code> activeChange<br>
1370 * <code>&lt;Boolean&gt;
1371 * prevValue</code> the previous value<br>
1372 * <code>&lt;Boolean&gt;
1373 * newValue</code> the updated value</p>
1374 * <p><strong>Usage:</strong><br>
1375 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1376 * myTabs.addListener('activeChange', handler);</code></p>
1377 * @event activeChange
1378 */
1379
1380 /**
1381 * Fires before the tab label is changed.
1382 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1383 * <p>If handler returns false, the change will be cancelled, and the value will not
1384 * be set.</p>
1385 * <p><strong>Event fields:</strong><br>
1386 * <code>&lt;String&gt; type</code> beforeLabelChange<br>
1387 * <code>&lt;String&gt;
1388 * prevValue</code> the current value<br>
1389 * <code>&lt;String&gt;
1390 * newValue</code> the new value</p>
1391 * <p><strong>Usage:</strong><br>
1392 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1393 * myTabs.addListener('beforeLabelChange', handler);</code></p>
1394 * @event beforeLabelChange
1395 */
1396
1397 /**
1398 * Fires after the tab label is changed.
1399 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1400 * <p><strong>Event fields:</strong><br>
1401 * <code>&lt;String&gt; type</code> labelChange<br>
1402 * <code>&lt;String&gt;
1403 * prevValue</code> the previous value<br>
1404 * <code>&lt;String&gt;
1405 * newValue</code> the updated value</p>
1406 * <p><strong>Usage:</strong><br>
1407 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1408 * myTabs.addListener('labelChange', handler);</code></p>
1409 * @event labelChange
1410 */
1411
1412 /**
1413 * Fires before the tab content is changed.
1414 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1415 * <p>If handler returns false, the change will be cancelled, and the value will not
1416 * be set.</p>
1417 * <p><strong>Event fields:</strong><br>
1418 * <code>&lt;String&gt; type</code> beforeContentChange<br>
1419 * <code>&lt;String&gt;
1420 * prevValue</code> the current value<br>
1421 * <code>&lt;String&gt;
1422 * newValue</code> the new value</p>
1423 * <p><strong>Usage:</strong><br>
1424 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1425 * myTabs.addListener('beforeContentChange', handler);</code></p>
1426 * @event beforeContentChange
1427 */
1428
1429 /**
1430 * Fires after the tab content is changed.
1431 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1432 * <p><strong>Event fields:</strong><br>
1433 * <code>&lt;String&gt; type</code> contentChange<br>
1434 * <code>&lt;String&gt;
1435 * prevValue</code> the previous value<br>
1436 * <code>&lt;Boolean&gt;
1437 * newValue</code> the updated value</p>
1438 * <p><strong>Usage:</strong><br>
1439 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1440 * myTabs.addListener('contentChange', handler);</code></p>
1441 * @event contentChange
1442 */
1443})();(function() {
1444
1445 /**
1446 * The tabview module provides a widget for managing content bound to tabs.
1447 * @module tabview
1448 *
1449 */
1450 /**
1451 * A widget to control tabbed views.
1452 * @namespace YAHOO.widget
1453 * @class TabView
1454 * @extends YAHOO.util.Element
1455 * @constructor
1456 * @param {HTMLElement | String | Object} el(optional) The html
1457 * element that represents the TabView, or the attribute object to use.
1458 * An element will be created if none provided.
1459 * @param {Object} attr (optional) A key map of the tabView's
1460 * initial attributes. Ignored if first arg is attributes object.
1461 */
1462 YAHOO.widget.TabView = function(el, attr) {
1463 attr = attr || {};
1464 if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) {
1465 attr = el; // treat first arg as attr object
1466 el = attr.element || null;
1467 }
1468
1469 if (!el && !attr.element) { // create if we dont have one
1470 el = _createTabViewElement.call(this, attr);
1471 }
1472 YAHOO.widget.TabView.superclass.constructor.call(this, el, attr);
1473 };
1474
1475 YAHOO.extend(YAHOO.widget.TabView, YAHOO.util.Element);
1476
1477 var proto = YAHOO.widget.TabView.prototype;
1478 var Dom = YAHOO.util.Dom;
1479 var Lang = YAHOO.util.Lang;
1480 var Event = YAHOO.util.Event;
1481 var Tab = YAHOO.widget.Tab;
1482
1483
1484 /**
1485 * The className to add when building from scratch.
1486 * @property CLASSNAME
1487 * @default "navset"
1488 */
1489 proto.CLASSNAME = 'yui-navset';
1490
1491 /**
1492 * The className of the HTMLElement containing the TabView's tab elements
1493 * to look for when building from existing markup, or to add when building
1494 * from scratch.
1495 * All childNodes of the tab container are treated as Tabs when building
1496 * from existing markup.
1497 * @property TAB_PARENT_CLASSNAME
1498 * @default "nav"
1499 */
1500 proto.TAB_PARENT_CLASSNAME = 'yui-nav';
1501
1502 /**
1503 * The className of the HTMLElement containing the TabView's label elements
1504 * to look for when building from existing markup, or to add when building
1505 * from scratch.
1506 * All childNodes of the content container are treated as content elements when
1507 * building from existing markup.
1508 * @property CONTENT_PARENT_CLASSNAME
1509 * @default "nav-content"
1510 */
1511 proto.CONTENT_PARENT_CLASSNAME = 'yui-content';
1512
1513 proto._tabParent = null;
1514 proto._contentParent = null;
1515
1516 /**
1517 * Adds a Tab to the TabView instance.
1518 * If no index is specified, the tab is added to the end of the tab list.
1519 * @method addTab
1520 * @param {YAHOO.widget.Tab} tab A Tab instance to add.
1521 * @param {Integer} index The position to add the tab.
1522 * @return void
1523 */
1524 proto.addTab = function(tab, index) {
1525 var tabs = this.get('tabs');
1526 if (!tabs) { // not ready yet
1527 this._queue[this._queue.length] = ['addTab', arguments];
1528 return false;
1529 }
1530
1531 index = (index === undefined) ? tabs.length : index;
1532
1533 var before = this.getTab(index);
1534
1535 var self = this;
1536 var el = this.get('element');
1537 var tabParent = this._tabParent;
1538 var contentParent = this._contentParent;
1539
1540 var tabElement = tab.get('element');
1541 var contentEl = tab.get('contentEl');
1542
1543 if ( before ) {
1544 tabParent.insertBefore(tabElement, before.get('element'));
1545 } else {
1546 tabParent.appendChild(tabElement);
1547 }
1548
1549 if ( contentEl && !Dom.isAncestor(contentParent, contentEl) ) {
1550 contentParent.appendChild(contentEl);
1551 }
1552
1553 if ( !tab.get('active') ) {
1554 tab.set('contentVisible', false, true); /* hide if not active */
1555 } else {
1556 this.set('activeTab', tab, true);
1557
1558 }
1559
1560 var activate = function(e) {
1561 YAHOO.util.Event.preventDefault(e);
1562 self.set('activeTab', this);
1563 };
1564
1565 tab.addListener( tab.get('activationEvent'), activate);
1566
1567 tab.addListener('activationEventChange', function(e) {
1568 if (e.prevValue != e.newValue) {
1569 tab.removeListener(e.prevValue, activate);
1570 tab.addListener(e.newValue, activate);
1571 }
1572 });
1573
1574 tabs.splice(index, 0, tab);
1575 };
1576
1577 /**
1578 * Routes childNode events.
1579 * @method DOMEventHandler
1580 * @param {event} e The Dom event that is being handled.
1581 * @return void
1582 */
1583 proto.DOMEventHandler = function(e) {
1584 var el = this.get('element');
1585 var target = YAHOO.util.Event.getTarget(e);
1586 var tabParent = this._tabParent;
1587
1588 if (Dom.isAncestor(tabParent, target) ) {
1589 var tabEl;
1590 var tab = null;
1591 var contentEl;
1592 var tabs = this.get('tabs');
1593
1594 for (var i = 0, len = tabs.length; i < len; i++) {
1595 tabEl = tabs[i].get('element');
1596 contentEl = tabs[i].get('contentEl');
1597
1598 if ( target == tabEl || Dom.isAncestor(tabEl, target) ) {
1599 tab = tabs[i];
1600 break; // note break
1601 }
1602 }
1603
1604 if (tab) {
1605 tab.fireEvent(e.type, e);
1606 }
1607 }
1608 };
1609
1610 /**
1611 * Returns the Tab instance at the specified index.
1612 * @method getTab
1613 * @param {Integer} index The position of the Tab.
1614 * @return YAHOO.widget.Tab
1615 */
1616 proto.getTab = function(index) {
1617 return this.get('tabs')[index];
1618 };
1619
1620 /**
1621 * Returns the index of given tab.
1622 * @method getTabIndex
1623 * @param {YAHOO.widget.Tab} tab The tab whose index will be returned.
1624 * @return int
1625 */
1626 proto.getTabIndex = function(tab) {
1627 var index = null;
1628 var tabs = this.get('tabs');
1629 for (var i = 0, len = tabs.length; i < len; ++i) {
1630 if (tab == tabs[i]) {
1631 index = i;
1632 break;
1633 }
1634 }
1635
1636 return index;
1637 };
1638
1639 /**
1640 * Removes the specified Tab from the TabView.
1641 * @method removeTab
1642 * @param {YAHOO.widget.Tab} item The Tab instance to be removed.
1643 * @return void
1644 */
1645 proto.removeTab = function(tab) {
1646 var tabCount = this.get('tabs').length;
1647
1648 var index = this.getTabIndex(tab);
1649 var nextIndex = index + 1;
1650 if ( tab == this.get('activeTab') ) { // select next tab
1651 if (tabCount > 1) {
1652 if (index + 1 == tabCount) {
1653 this.set('activeIndex', index - 1);
1654 } else {
1655 this.set('activeIndex', index + 1);
1656 }
1657 }
1658 }
1659
1660 this._tabParent.removeChild( tab.get('element') );
1661 this._contentParent.removeChild( tab.get('contentEl') );
1662 this._configs.tabs.value.splice(index, 1);
1663
1664 };
1665
1666 /**
1667 * Provides a readable name for the TabView instance.
1668 * @method toString
1669 * @return String
1670 */
1671 proto.toString = function() {
1672 var name = this.get('id') || this.get('tagName');
1673 return "TabView " + name;
1674 };
1675
1676 /**
1677 * The transiton to use when switching between tabs.
1678 * @method contentTransition
1679 */
1680 proto.contentTransition = function(newTab, oldTab) {
1681 newTab.set('contentVisible', true);
1682 oldTab.set('contentVisible', false);
1683 };
1684
1685 /**
1686 * Registers TabView specific properties.
1687 * @method initAttributes
1688 * @param {Object} attr Hash of initial attributes
1689 */
1690 proto.initAttributes = function(attr) {
1691 YAHOO.widget.TabView.superclass.initAttributes.call(this, attr);
1692
1693 if (!attr.orientation) {
1694 attr.orientation = 'top';
1695 }
1696
1697 var el = this.get('element');
1698
1699 /**
1700 * The Tabs belonging to the TabView instance.
1701 * @config tabs
1702 * @type Array
1703 */
1704 this.register('tabs', {
1705 value: [],
1706 readOnly: true
1707 });
1708
1709 /**
1710 * The container of the tabView's label elements.
1711 * @property _tabParent
1712 * @private
1713 * @type HTMLElement
1714 */
1715 this._tabParent =
1716 this.getElementsByClassName(this.TAB_PARENT_CLASSNAME,
1717 'ul' )[0] || _createTabParent.call(this);
1718
1719 /**
1720 * The container of the tabView's content elements.
1721 * @property _contentParent
1722 * @type HTMLElement
1723 * @private
1724 */
1725 this._contentParent =
1726 this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME,
1727 'div')[0] || _createContentParent.call(this);
1728
1729 /**
1730 * How the Tabs should be oriented relative to the TabView.
1731 * @config orientation
1732 * @type String
1733 * @default "top"
1734 */
1735 this.register('orientation', {
1736 value: attr.orientation,
1737 method: function(value) {
1738 var current = this.get('orientation');
1739 this.addClass('yui-navset-' + value);
1740
1741 if (current != value) {
1742 this.removeClass('yui-navset-' + current);
1743 }
1744
1745 switch(value) {
1746 case 'bottom':
1747 this.appendChild(this._tabParent);
1748 break;
1749 }
1750 }
1751 });
1752
1753 /**
1754 * The index of the tab currently active.
1755 * @config activeIndex
1756 * @type Int
1757 */
1758 this.register('activeIndex', {
1759 value: attr.activeIndex,
1760 method: function(value) {
1761 this.set('activeTab', this.getTab(value));
1762 },
1763 validator: function(value) {
1764 return !this.getTab(value).get('disabled'); // cannot activate if disabled
1765 }
1766 });
1767
1768 /**
1769 * The tab currently active.
1770 * @config activeTab
1771 * @type YAHOO.widget.Tab
1772 */
1773 this.register('activeTab', {
1774 value: attr.activeTab,
1775 method: function(tab) {
1776 var activeTab = this.get('activeTab');
1777
1778 if (tab) {
1779 tab.set('active', true);
1780 }
1781
1782 if (activeTab && activeTab != tab) {
1783 activeTab.set('active', false);
1784 }
1785
1786 if (activeTab && tab != activeTab) { // no transition if only 1
1787 this.contentTransition(tab, activeTab);
1788 } else if (tab) {
1789 tab.set('contentVisible', true);
1790 }
1791 },
1792 validator: function(value) {
1793 return !value.get('disabled'); // cannot activate if disabled
1794 }
1795 });
1796
1797 if ( this._tabParent ) {
1798 _initTabs.call(this);
1799 }
1800
1801 for (var type in this.DOM_EVENTS) {
1802 if ( this.DOM_EVENTS.hasOwnProperty(type) ) {
1803 this.addListener.call(this, type, this.DOMEventHandler);
1804 }
1805 }
1806 };
1807
1808 /**
1809 * Creates Tab instances from a collection of HTMLElements.
1810 * @method createTabs
1811 * @private
1812 * @param {Array|HTMLCollection} elements The elements to use for Tabs.
1813 * @return void
1814 */
1815 var _initTabs = function() {
1816 var tab,
1817 attr,
1818 contentEl;
1819
1820 var el = this.get('element');
1821 var tabs = _getChildNodes(this._tabParent);
1822 var contentElements = _getChildNodes(this._contentParent);
1823
1824 for (var i = 0, len = tabs.length; i < len; ++i) {
1825 attr = {};
1826
1827 if (contentElements[i]) {
1828 attr.contentEl = contentElements[i];
1829 }
1830
1831 tab = new YAHOO.widget.Tab(tabs[i], attr);
1832 this.addTab(tab);
1833
1834 if (tab.hasClass(tab.ACTIVE_CLASSNAME) ) {
1835 this._configs.activeTab.value = tab; // dont invoke method
1836 }
1837 }
1838 };
1839
1840 var _createTabViewElement = function(attr) {
1841 var el = document.createElement('div');
1842
1843 if ( this.CLASSNAME ) {
1844 el.className = this.CLASSNAME;
1845 }
1846
1847 return el;
1848 };
1849
1850 var _createTabParent = function(attr) {
1851 var el = document.createElement('ul');
1852
1853 if ( this.TAB_PARENT_CLASSNAME ) {
1854 el.className = this.TAB_PARENT_CLASSNAME;
1855 }
1856
1857 this.get('element').appendChild(el);
1858
1859 return el;
1860 };
1861
1862 var _createContentParent = function(attr) {
1863 var el = document.createElement('div');
1864
1865 if ( this.CONTENT_PARENT_CLASSNAME ) {
1866 el.className = this.CONTENT_PARENT_CLASSNAME;
1867 }
1868
1869 this.get('element').appendChild(el);
1870
1871 return el;
1872 };
1873
1874 var _getChildNodes = function(el) {
1875 var nodes = [];
1876 var childNodes = el.childNodes;
1877
1878 for (var i = 0, len = childNodes.length; i < len; ++i) {
1879 if (childNodes[i].nodeType == 1) {
1880 nodes[nodes.length] = childNodes[i];
1881 }
1882 }
1883
1884 return nodes;
1885 };
1886
1887/**
1888 * Fires before the activeTab is changed.
1889 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1890 * <p>If handler returns false, the change will be cancelled, and the value will not
1891 * be set.</p>
1892 * <p><strong>Event fields:</strong><br>
1893 * <code>&lt;String&gt; type</code> beforeActiveTabChange<br>
1894 * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
1895 * prevValue</code> the currently active tab<br>
1896 * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
1897 * newValue</code> the tab to be made active</p>
1898 * <p><strong>Usage:</strong><br>
1899 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1900 * myTabs.addListener('beforeActiveTabChange', handler);</code></p>
1901 * @event beforeActiveTabChange
1902 */
1903
1904/**
1905 * Fires after the activeTab is changed.
1906 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1907 * <p><strong>Event fields:</strong><br>
1908 * <code>&lt;String&gt; type</code> activeTabChange<br>
1909 * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
1910 * prevValue</code> the formerly active tab<br>
1911 * <code>&lt;<a href="YAHOO.widget.Tab.html">YAHOO.widget.Tab</a>&gt;
1912 * newValue</code> the new active tab</p>
1913 * <p><strong>Usage:</strong><br>
1914 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1915 * myTabs.addListener('activeTabChange', handler);</code></p>
1916 * @event activeTabChange
1917 */
1918
1919/**
1920 * Fires before the orientation is changed.
1921 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1922 * <p>If handler returns false, the change will be cancelled, and the value will not
1923 * be set.</p>
1924 * <p><strong>Event fields:</strong><br>
1925 * <code>&lt;String&gt; type</code> beforeOrientationChange<br>
1926 * <code>&lt;String&gt;
1927 * prevValue</code> the current orientation<br>
1928 * <code>&lt;String&gt;
1929 * newValue</code> the new orientation to be applied</p>
1930 * <p><strong>Usage:</strong><br>
1931 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1932 * myTabs.addListener('beforeOrientationChange', handler);</code></p>
1933 * @event beforeOrientationChange
1934 */
1935
1936/**
1937 * Fires after the orientation is changed.
1938 * <p>See: <a href="YAHOO.util.Element.html#addListener">Element.addListener</a></p>
1939 * <p><strong>Event fields:</strong><br>
1940 * <code>&lt;String&gt; type</code> orientationChange<br>
1941 * <code>&lt;String&gt;
1942 * prevValue</code> the former orientation<br>
1943 * <code>&lt;String&gt;
1944 * newValue</code> the new orientation</p>
1945 * <p><strong>Usage:</strong><br>
1946 * <code>var handler = function(e) {var previous = e.prevValue};<br>
1947 * myTabs.addListener('orientationChange', handler);</code></p>
1948 * @event orientationChange
1949 */
1950})(); \ No newline at end of file
diff --git a/frontend/beta/js/YUI/treeview.js b/frontend/beta/js/YUI/treeview.js
new file mode 100644
index 0000000..ea6b6ef
--- a/dev/null
+++ b/frontend/beta/js/YUI/treeview.js
@@ -0,0 +1,2182 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9 * The treeview widget is a generic tree building tool.
10 * @module treeview
11 * @title TreeView Widget
12 * @requires yahoo
13 * @optional animation
14 * @namespace YAHOO.widget
15 */
16
17/**
18 * Contains the tree view state data and the root node.
19 *
20 * @class TreeView
21 * @constructor
22 * @param {string|HTMLElement} id The id of the element, or the element
23 * itself that the tree will be inserted into.
24 */
25YAHOO.widget.TreeView = function(id) {
26 if (id) { this.init(id); }
27};
28
29YAHOO.widget.TreeView.prototype = {
30
31 /**
32 * The id of tree container element
33 * @property id
34 * @type String
35 */
36 id: null,
37
38 /**
39 * The host element for this tree
40 * @property _el
41 * @private
42 */
43 _el: null,
44
45 /**
46 * Flat collection of all nodes in this tree
47 * @property _nodes
48 * @type Node[]
49 * @private
50 */
51 _nodes: null,
52
53 /**
54 * We lock the tree control while waiting for the dynamic loader to return
55 * @property locked
56 * @type boolean
57 */
58 locked: false,
59
60 /**
61 * The animation to use for expanding children, if any
62 * @property _expandAnim
63 * @type string
64 * @private
65 */
66 _expandAnim: null,
67
68 /**
69 * The animation to use for collapsing children, if any
70 * @property _collapseAnim
71 * @type string
72 * @private
73 */
74 _collapseAnim: null,
75
76 /**
77 * The current number of animations that are executing
78 * @property _animCount
79 * @type int
80 * @private
81 */
82 _animCount: 0,
83
84 /**
85 * The maximum number of animations to run at one time.
86 * @property maxAnim
87 * @type int
88 */
89 maxAnim: 2,
90
91 /**
92 * Sets up the animation for expanding children
93 * @method setExpandAnim
94 * @param {string} type the type of animation (acceptable values defined
95 * in YAHOO.widget.TVAnim)
96 */
97 setExpandAnim: function(type) {
98 if (YAHOO.widget.TVAnim.isValid(type)) {
99 this._expandAnim = type;
100 }
101 },
102
103 /**
104 * Sets up the animation for collapsing children
105 * @method setCollapseAnim
106 * @param {string} the type of animation (acceptable values defined in
107 * YAHOO.widget.TVAnim)
108 */
109 setCollapseAnim: function(type) {
110 if (YAHOO.widget.TVAnim.isValid(type)) {
111 this._collapseAnim = type;
112 }
113 },
114
115 /**
116 * Perform the expand animation if configured, or just show the
117 * element if not configured or too many animations are in progress
118 * @method animateExpand
119 * @param el {HTMLElement} the element to animate
120 * @param node {YAHOO.util.Node} the node that was expanded
121 * @return {boolean} true if animation could be invoked, false otherwise
122 */
123 animateExpand: function(el, node) {
124
125 if (this._expandAnim && this._animCount < this.maxAnim) {
126 // this.locked = true;
127 var tree = this;
128 var a = YAHOO.widget.TVAnim.getAnim(this._expandAnim, el,
129 function() { tree.expandComplete(node); });
130 if (a) {
131 ++this._animCount;
132 this.fireEvent("animStart", {
133 "node": node,
134 "type": "expand"
135 });
136 a.animate();
137 }
138
139 return true;
140 }
141
142 return false;
143 },
144
145 /**
146 * Perform the collapse animation if configured, or just show the
147 * element if not configured or too many animations are in progress
148 * @method animateCollapse
149 * @param el {HTMLElement} the element to animate
150 * @param node {YAHOO.util.Node} the node that was expanded
151 * @return {boolean} true if animation could be invoked, false otherwise
152 */
153 animateCollapse: function(el, node) {
154
155 if (this._collapseAnim && this._animCount < this.maxAnim) {
156 // this.locked = true;
157 var tree = this;
158 var a = YAHOO.widget.TVAnim.getAnim(this._collapseAnim, el,
159 function() { tree.collapseComplete(node); });
160 if (a) {
161 ++this._animCount;
162 this.fireEvent("animStart", {
163 "node": node,
164 "type": "collapse"
165 });
166 a.animate();
167 }
168
169 return true;
170 }
171
172 return false;
173 },
174
175 /**
176 * Function executed when the expand animation completes
177 * @method expandComplete
178 */
179 expandComplete: function(node) {
180 --this._animCount;
181 this.fireEvent("animComplete", {
182 "node": node,
183 "type": "expand"
184 });
185 // this.locked = false;
186 },
187
188 /**
189 * Function executed when the collapse animation completes
190 * @method collapseComplete
191 */
192 collapseComplete: function(node) {
193 --this._animCount;
194 this.fireEvent("animComplete", {
195 "node": node,
196 "type": "collapse"
197 });
198 // this.locked = false;
199 },
200
201 /**
202 * Initializes the tree
203 * @method init
204 * @parm {string|HTMLElement} id the id of the element that will hold the tree
205 * @private
206 */
207 init: function(id) {
208
209 this.id = id;
210
211 if ("string" !== typeof id) {
212 this._el = id;
213 this.id = this.generateId(id);
214 }
215
216 /**
217 * When animation is enabled, this event fires when the animation
218 * starts
219 * @event animStart
220 * @type CustomEvent
221 * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
222 * @parm {String} type the type of animation ("expand" or "collapse")
223 */
224 this.createEvent("animStart", this);
225
226 /**
227 * When animation is enabled, this event fires when the animation
228 * completes
229 * @event animComplete
230 * @type CustomEvent
231 * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
232 * @parm {String} type the type of animation ("expand" or "collapse")
233 */
234 this.createEvent("animComplete", this);
235
236 /**
237 * Fires when a node is going to be expanded. Return false to stop
238 * the expand.
239 * @event collapse
240 * @type CustomEvent
241 * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
242 */
243 this.createEvent("collapse", this);
244
245 /**
246 * Fires when a node is going to be collapsed. Return false to stop
247 * the collapse.
248 * @event expand
249 * @type CustomEvent
250 * @param {YAHOO.widget.Node} node the node that is expanding/collapsing
251 */
252 this.createEvent("expand", this);
253
254 this._nodes = [];
255
256 // store a global reference
257 YAHOO.widget.TreeView.trees[this.id] = this;
258
259 // Set up the root node
260 this.root = new YAHOO.widget.RootNode(this);
261
262
263 },
264
265 /**
266 * Renders the tree boilerplate and visible nodes
267 * @method draw
268 */
269 draw: function() {
270 var html = this.root.getHtml();
271 this.getEl().innerHTML = html;
272 this.firstDraw = false;
273 },
274
275 /**
276 * Returns the tree's host element
277 * @method getEl
278 * @return {HTMLElement} the host element
279 */
280 getEl: function() {
281 if (! this._el) {
282 this._el = document.getElementById(this.id);
283 }
284 return this._el;
285 },
286
287 /**
288 * Nodes register themselves with the tree instance when they are created.
289 * @method regNode
290 * @param node {Node} the node to register
291 * @private
292 */
293 regNode: function(node) {
294 this._nodes[node.index] = node;
295 },
296
297 /**
298 * Returns the root node of this tree
299 * @method getRoot
300 * @return {Node} the root node
301 */
302 getRoot: function() {
303 return this.root;
304 },
305
306 /**
307 * Configures this tree to dynamically load all child data
308 * @method setDynamicLoad
309 * @param {function} fnDataLoader the function that will be called to get the data
310 * @param iconMode {int} configures the icon that is displayed when a dynamic
311 * load node is expanded the first time without children. By default, the
312 * "collapse" icon will be used. If set to 1, the leaf node icon will be
313 * displayed.
314 */
315 setDynamicLoad: function(fnDataLoader, iconMode) {
316 this.root.setDynamicLoad(fnDataLoader, iconMode);
317 },
318
319 /**
320 * Expands all child nodes. Note: this conflicts with the "multiExpand"
321 * node property. If expand all is called in a tree with nodes that
322 * do not allow multiple siblings to be displayed, only the last sibling
323 * will be expanded.
324 * @method expandAll
325 */
326 expandAll: function() {
327 if (!this.locked) {
328 this.root.expandAll();
329 }
330 },
331
332 /**
333 * Collapses all expanded child nodes in the entire tree.
334 * @method collapseAll
335 */
336 collapseAll: function() {
337 if (!this.locked) {
338 this.root.collapseAll();
339 }
340 },
341
342 /**
343 * Returns a node in the tree that has the specified index (this index
344 * is created internally, so this function probably will only be used
345 * in html generated for a given node.)
346 * @method getNodeByIndex
347 * @param {int} nodeIndex the index of the node wanted
348 * @return {Node} the node with index=nodeIndex, null if no match
349 */
350 getNodeByIndex: function(nodeIndex) {
351 var n = this._nodes[nodeIndex];
352 return (n) ? n : null;
353 },
354
355 /**
356 * Returns a node that has a matching property and value in the data
357 * object that was passed into its constructor.
358 * @method getNodeByProperty
359 * @param {object} property the property to search (usually a string)
360 * @param {object} value the value we want to find (usuall an int or string)
361 * @return {Node} the matching node, null if no match
362 */
363 getNodeByProperty: function(property, value) {
364 for (var i in this._nodes) {
365 var n = this._nodes[i];
366 if (n.data && value == n.data[property]) {
367 return n;
368 }
369 }
370
371 return null;
372 },
373
374 /**
375 * Returns a collection of nodes that have a matching property
376 * and value in the data object that was passed into its constructor.
377 * @method getNodesByProperty
378 * @param {object} property the property to search (usually a string)
379 * @param {object} value the value we want to find (usuall an int or string)
380 * @return {Array} the matching collection of nodes, null if no match
381 */
382 getNodesByProperty: function(property, value) {
383 var values = [];
384 for (var i in this._nodes) {
385 var n = this._nodes[i];
386 if (n.data && value == n.data[property]) {
387 values.push(n);
388 }
389 }
390
391 return (values.length) ? values : null;
392 },
393
394 /**
395 * Removes the node and its children, and optionally refreshes the
396 * branch of the tree that was affected.
397 * @method removeNode
398 * @param {Node} The node to remove
399 * @param {boolean} autoRefresh automatically refreshes branch if true
400 * @return {boolean} False is there was a problem, true otherwise.
401 */
402 removeNode: function(node, autoRefresh) {
403
404 // Don't delete the root node
405 if (node.isRoot()) {
406 return false;
407 }
408
409 // Get the branch that we may need to refresh
410 var p = node.parent;
411 if (p.parent) {
412 p = p.parent;
413 }
414
415 // Delete the node and its children
416 this._deleteNode(node);
417
418 // Refresh the parent of the parent
419 if (autoRefresh && p && p.childrenRendered) {
420 p.refresh();
421 }
422
423 return true;
424 },
425
426 /**
427 * Deletes this nodes child collection, recursively. Also collapses
428 * the node, and resets the dynamic load flag. The primary use for
429 * this method is to purge a node and allow it to fetch its data
430 * dynamically again.
431 * @method removeChildren
432 * @param {Node} node the node to purge
433 */
434 removeChildren: function(node) {
435 while (node.children.length) {
436 this._deleteNode(node.children[0]);
437 }
438
439 node.childrenRendered = false;
440 node.dynamicLoadComplete = false;
441 if (node.expanded) {
442 node.collapse();
443 } else {
444 node.updateIcon();
445 }
446 },
447
448 /**
449 * Deletes the node and recurses children
450 * @method _deleteNode
451 * @private
452 */
453 _deleteNode: function(node) {
454 // Remove all the child nodes first
455 this.removeChildren(node);
456
457 // Remove the node from the tree
458 this.popNode(node);
459 },
460
461 /**
462 * Removes the node from the tree, preserving the child collection
463 * to make it possible to insert the branch into another part of the
464 * tree, or another tree.
465 * @method popNode
466 * @param {Node} the node to remove
467 */
468 popNode: function(node) {
469 var p = node.parent;
470
471 // Update the parent's collection of children
472 var a = [];
473
474 for (var i=0, len=p.children.length;i<len;++i) {
475 if (p.children[i] != node) {
476 a[a.length] = p.children[i];
477 }
478 }
479
480 p.children = a;
481
482 // reset the childrenRendered flag for the parent
483 p.childrenRendered = false;
484
485 // Update the sibling relationship
486 if (node.previousSibling) {
487 node.previousSibling.nextSibling = node.nextSibling;
488 }
489
490 if (node.nextSibling) {
491 node.nextSibling.previousSibling = node.previousSibling;
492 }
493
494 node.parent = null;
495 node.previousSibling = null;
496 node.nextSibling = null;
497 node.tree = null;
498
499 // Update the tree's node collection
500 delete this._nodes[node.index];
501 },
502
503 /**
504 * TreeView instance toString
505 * @method toString
506 * @return {string} string representation of the tree
507 */
508 toString: function() {
509 return "TreeView " + this.id;
510 },
511
512 /**
513 * Generates an unique id for an element if it doesn't yet have one
514 * @method generateId
515 * @private
516 */
517 generateId: function(el) {
518 var id = el.id;
519
520 if (!id) {
521 id = "yui-tv-auto-id-" + YAHOO.widget.TreeView.counter;
522 ++YAHOO.widget.TreeView.counter;
523 }
524
525 return id;
526 },
527
528 /**
529 * Abstract method that is executed when a node is expanded
530 * @method onExpand
531 * @param node {Node} the node that was expanded
532 * @deprecated use treeobj.subscribe("expand") instead
533 */
534 onExpand: function(node) { },
535
536 /**
537 * Abstract method that is executed when a node is collapsed.
538 * @method onCollapse
539 * @param node {Node} the node that was collapsed.
540 * @deprecated use treeobj.subscribe("collapse") instead
541 */
542 onCollapse: function(node) { }
543
544};
545
546YAHOO.augment(YAHOO.widget.TreeView, YAHOO.util.EventProvider);
547
548/**
549 * Count of all nodes in all trees
550 * @property YAHOO.widget.TreeView.nodeCount
551 * @type int
552 * @static
553 */
554YAHOO.widget.TreeView.nodeCount = 0;
555
556/**
557 * Global cache of tree instances
558 * @property YAHOO.widget.TreeView.trees
559 * @type Array
560 * @static
561 * @private
562 */
563YAHOO.widget.TreeView.trees = [];
564
565/**
566 * Counter for generating a new unique element id
567 * @property YAHOO.widget.TreeView.counter
568 * @static
569 * @private
570 */
571YAHOO.widget.TreeView.counter = 0;
572
573/**
574 * Global method for getting a tree by its id. Used in the generated
575 * tree html.
576 * @method YAHOO.widget.TreeView.getTree
577 * @param treeId {String} the id of the tree instance
578 * @return {TreeView} the tree instance requested, null if not found.
579 * @static
580 */
581YAHOO.widget.TreeView.getTree = function(treeId) {
582 var t = YAHOO.widget.TreeView.trees[treeId];
583 return (t) ? t : null;
584};
585
586/**
587 * Global method for getting a node by its id. Used in the generated
588 * tree html.
589 * @method YAHOO.widget.TreeView.getNode
590 * @param treeId {String} the id of the tree instance
591 * @param nodeIndex {String} the index of the node to return
592 * @return {Node} the node instance requested, null if not found
593 * @static
594 */
595YAHOO.widget.TreeView.getNode = function(treeId, nodeIndex) {
596 var t = YAHOO.widget.TreeView.getTree(treeId);
597 return (t) ? t.getNodeByIndex(nodeIndex) : null;
598};
599
600/**
601 * Add a DOM event
602 * @method YAHOO.widget.TreeView.addHandler
603 * @param el the elment to bind the handler to
604 * @param {string} sType the type of event handler
605 * @param {function} fn the callback to invoke
606 * @static
607 */
608YAHOO.widget.TreeView.addHandler = function (el, sType, fn) {
609 if (el.addEventListener) {
610 el.addEventListener(sType, fn, false);
611 } else if (el.attachEvent) {
612 el.attachEvent("on" + sType, fn);
613 }
614};
615
616/**
617 * Remove a DOM event
618 * @method YAHOO.widget.TreeView.removeHandler
619 * @param el the elment to bind the handler to
620 * @param {string} sType the type of event handler
621 * @param {function} fn the callback to invoke
622 * @static
623 */
624
625YAHOO.widget.TreeView.removeHandler = function (el, sType, fn) {
626 if (el.removeEventListener) {
627 el.removeEventListener(sType, fn, false);
628 } else if (el.detachEvent) {
629 el.detachEvent("on" + sType, fn);
630 }
631};
632
633/**
634 * Attempts to preload the images defined in the styles used to draw the tree by
635 * rendering off-screen elements that use the styles.
636 * @method YAHOO.widget.TreeView.preload
637 * @param {string} prefix the prefix to use to generate the names of the
638 * images to preload, default is ygtv
639 * @static
640 */
641YAHOO.widget.TreeView.preload = function(prefix) {
642 prefix = prefix || "ygtv";
643 var styles = ["tn","tm","tmh","tp","tph","ln","lm","lmh","lp","lph","loading"];
644
645 var sb = [];
646
647 for (var i = 0; i < styles.length; ++i) {
648 sb[sb.length] = '<span class="' + prefix + styles[i] + '">&#160;</span>';
649 }
650
651 var f = document.createElement("div");
652 var s = f.style;
653 s.position = "absolute";
654 s.top = "-1000px";
655 s.left = "-1000px";
656 f.innerHTML = sb.join("");
657
658 document.body.appendChild(f);
659
660 YAHOO.widget.TreeView.removeHandler(window,
661 "load", YAHOO.widget.TreeView.preload);
662
663};
664
665YAHOO.widget.TreeView.addHandler(window,
666 "load", YAHOO.widget.TreeView.preload);
667
668/**
669 * The base class for all tree nodes. The node's presentation and behavior in
670 * response to mouse events is handled in Node subclasses.
671 * @namespace YAHOO.widget
672 * @class Node
673 * @param oData {object} a string or object containing the data that will
674 * be used to render this node
675 * @param oParent {Node} this node's parent node
676 * @param expanded {boolean} the initial expanded/collapsed state
677 * @constructor
678 */
679YAHOO.widget.Node = function(oData, oParent, expanded) {
680 if (oData) { this.init(oData, oParent, expanded); }
681};
682
683YAHOO.widget.Node.prototype = {
684
685 /**
686 * The index for this instance obtained from global counter in YAHOO.widget.TreeView.
687 * @property index
688 * @type int
689 */
690 index: 0,
691
692 /**
693 * This node's child node collection.
694 * @property children
695 * @type Node[]
696 */
697 children: null,
698
699 /**
700 * Tree instance this node is part of
701 * @property tree
702 * @type TreeView
703 */
704 tree: null,
705
706 /**
707 * The data linked to this node. This can be any object or primitive
708 * value, and the data can be used in getNodeHtml().
709 * @property data
710 * @type object
711 */
712 data: null,
713
714 /**
715 * Parent node
716 * @property parent
717 * @type Node
718 */
719 parent: null,
720
721 /**
722 * The depth of this node. We start at -1 for the root node.
723 * @property depth
724 * @type int
725 */
726 depth: -1,
727
728 /**
729 * The href for the node's label. If one is not specified, the href will
730 * be set so that it toggles the node.
731 * @property href
732 * @type string
733 */
734 href: null,
735
736 /**
737 * The label href target, defaults to current window
738 * @property target
739 * @type string
740 */
741 target: "_self",
742
743 /**
744 * The node's expanded/collapsed state
745 * @property expanded
746 * @type boolean
747 */
748 expanded: false,
749
750 /**
751 * Can multiple children be expanded at once?
752 * @property multiExpand
753 * @type boolean
754 */
755 multiExpand: true,
756
757 /**
758 * Should we render children for a collapsed node? It is possible that the
759 * implementer will want to render the hidden data... @todo verify that we
760 * need this, and implement it if we do.
761 * @property renderHidden
762 * @type boolean
763 */
764 renderHidden: false,
765
766 /**
767 * This flag is set to true when the html is generated for this node's
768 * children, and set to false when new children are added.
769 * @property childrenRendered
770 * @type boolean
771 */
772 childrenRendered: false,
773
774 /**
775 * Dynamically loaded nodes only fetch the data the first time they are
776 * expanded. This flag is set to true once the data has been fetched.
777 * @property dynamicLoadComplete
778 * @type boolean
779 */
780 dynamicLoadComplete: false,
781
782 /**
783 * This node's previous sibling
784 * @property previousSibling
785 * @type Node
786 */
787 previousSibling: null,
788
789 /**
790 * This node's next sibling
791 * @property nextSibling
792 * @type Node
793 */
794 nextSibling: null,
795
796 /**
797 * We can set the node up to call an external method to get the child
798 * data dynamically.
799 * @property _dynLoad
800 * @type boolean
801 * @private
802 */
803 _dynLoad: false,
804
805 /**
806 * Function to execute when we need to get this node's child data.
807 * @property dataLoader
808 * @type function
809 */
810 dataLoader: null,
811
812 /**
813 * This is true for dynamically loading nodes while waiting for the
814 * callback to return.
815 * @property isLoading
816 * @type boolean
817 */
818 isLoading: false,
819
820 /**
821 * The toggle/branch icon will not show if this is set to false. This
822 * could be useful if the implementer wants to have the child contain
823 * extra info about the parent, rather than an actual node.
824 * @property hasIcon
825 * @type boolean
826 */
827 hasIcon: true,
828
829 /**
830 * Used to configure what happens when a dynamic load node is expanded
831 * and we discover that it does not have children. By default, it is
832 * treated as if it still could have children (plus/minus icon). Set
833 * iconMode to have it display like a leaf node instead.
834 * @property iconMode
835 * @type int
836 */
837 iconMode: 0,
838
839 /**
840 * The node type
841 * @property _type
842 * @private
843 */
844 _type: "Node",
845
846 /*
847 spacerPath: "http://us.i1.yimg.com/us.yimg.com/i/space.gif",
848 expandedText: "Expanded",
849 collapsedText: "Collapsed",
850 loadingText: "Loading",
851 */
852
853 /**
854 * Initializes this node, gets some of the properties from the parent
855 * @method init
856 * @param oData {object} a string or object containing the data that will
857 * be used to render this node
858 * @param oParent {Node} this node's parent node
859 * @param expanded {boolean} the initial expanded/collapsed state
860 */
861 init: function(oData, oParent, expanded) {
862
863 this.data = oData;
864 this.children = [];
865 this.index = YAHOO.widget.TreeView.nodeCount;
866 ++YAHOO.widget.TreeView.nodeCount;
867 this.expanded = expanded;
868
869 /**
870 * The parentChange event is fired when a parent element is applied
871 * to the node. This is useful if you need to apply tree-level
872 * properties to a tree that need to happen if a node is moved from
873 * one tre to another.
874 *
875 * @event parentChange
876 * @type CustomEvent
877 */
878 this.createEvent("parentChange", this);
879
880 // oParent should never be null except when we create the root node.
881 if (oParent) {
882 oParent.appendChild(this);
883 }
884 },
885
886 /**
887 * Certain properties for the node cannot be set until the parent
888 * is known. This is called after the node is inserted into a tree.
889 * the parent is also applied to this node's children in order to
890 * make it possible to move a branch from one tree to another.
891 * @method applyParent
892 * @param {Node} parentNode this node's parent node
893 * @return {boolean} true if the application was successful
894 */
895 applyParent: function(parentNode) {
896 if (!parentNode) {
897 return false;
898 }
899
900 this.tree = parentNode.tree;
901 this.parent = parentNode;
902 this.depth = parentNode.depth + 1;
903
904 if (!this.href) {
905 this.href = "javascript:" + this.getToggleLink();
906 }
907
908 if (! this.multiExpand) {
909 this.multiExpand = parentNode.multiExpand;
910 }
911
912 this.tree.regNode(this);
913 parentNode.childrenRendered = false;
914
915 // cascade update existing children
916 for (var i=0, len=this.children.length;i<len;++i) {
917 this.children[i].applyParent(this);
918 }
919
920 this.fireEvent("parentChange");
921
922 return true;
923 },
924
925 /**
926 * Appends a node to the child collection.
927 * @method appendChild
928 * @param childNode {Node} the new node
929 * @return {Node} the child node
930 * @private
931 */
932 appendChild: function(childNode) {
933 if (this.hasChildren()) {
934 var sib = this.children[this.children.length - 1];
935 sib.nextSibling = childNode;
936 childNode.previousSibling = sib;
937 }
938 this.children[this.children.length] = childNode;
939 childNode.applyParent(this);
940
941 return childNode;
942 },
943
944 /**
945 * Appends this node to the supplied node's child collection
946 * @method appendTo
947 * @param parentNode {Node} the node to append to.
948 * @return {Node} The appended node
949 */
950 appendTo: function(parentNode) {
951 return parentNode.appendChild(this);
952 },
953
954 /**
955 * Inserts this node before this supplied node
956 * @method insertBefore
957 * @param node {Node} the node to insert this node before
958 * @return {Node} the inserted node
959 */
960 insertBefore: function(node) {
961 var p = node.parent;
962 if (p) {
963
964 if (this.tree) {
965 this.tree.popNode(this);
966 }
967
968 var refIndex = node.isChildOf(p);
969 p.children.splice(refIndex, 0, this);
970 if (node.previousSibling) {
971 node.previousSibling.nextSibling = this;
972 }
973 this.previousSibling = node.previousSibling;
974 this.nextSibling = node;
975 node.previousSibling = this;
976
977 this.applyParent(p);
978 }
979
980 return this;
981 },
982
983 /**
984 * Inserts this node after the supplied node
985 * @method insertAfter
986 * @param node {Node} the node to insert after
987 * @return {Node} the inserted node
988 */
989 insertAfter: function(node) {
990 var p = node.parent;
991 if (p) {
992
993 if (this.tree) {
994 this.tree.popNode(this);
995 }
996
997 var refIndex = node.isChildOf(p);
998
999 if (!node.nextSibling) {
1000 return this.appendTo(p);
1001 }
1002
1003 p.children.splice(refIndex + 1, 0, this);
1004
1005 node.nextSibling.previousSibling = this;
1006 this.previousSibling = node;
1007 this.nextSibling = node.nextSibling;
1008 node.nextSibling = this;
1009
1010 this.applyParent(p);
1011 }
1012
1013 return this;
1014 },
1015
1016 /**
1017 * Returns true if the Node is a child of supplied Node
1018 * @method isChildOf
1019 * @param parentNode {Node} the Node to check
1020 * @return {boolean} The node index if this Node is a child of
1021 * supplied Node, else -1.
1022 * @private
1023 */
1024 isChildOf: function(parentNode) {
1025 if (parentNode && parentNode.children) {
1026 for (var i=0, len=parentNode.children.length; i<len ; ++i) {
1027 if (parentNode.children[i] === this) {
1028 return i;
1029 }
1030 }
1031 }
1032
1033 return -1;
1034 },
1035
1036 /**
1037 * Returns a node array of this node's siblings, null if none.
1038 * @method getSiblings
1039 * @return Node[]
1040 */
1041 getSiblings: function() {
1042 return this.parent.children;
1043 },
1044
1045 /**
1046 * Shows this node's children
1047 * @method showChildren
1048 */
1049 showChildren: function() {
1050 if (!this.tree.animateExpand(this.getChildrenEl(), this)) {
1051 if (this.hasChildren()) {
1052 this.getChildrenEl().style.display = "";
1053 }
1054 }
1055 },
1056
1057 /**
1058 * Hides this node's children
1059 * @method hideChildren
1060 */
1061 hideChildren: function() {
1062
1063 if (!this.tree.animateCollapse(this.getChildrenEl(), this)) {
1064 this.getChildrenEl().style.display = "none";
1065 }
1066 },
1067
1068 /**
1069 * Returns the id for this node's container div
1070 * @method getElId
1071 * @return {string} the element id
1072 */
1073 getElId: function() {
1074 return "ygtv" + this.index;
1075 },
1076
1077 /**
1078 * Returns the id for this node's children div
1079 * @method getChildrenElId
1080 * @return {string} the element id for this node's children div
1081 */
1082 getChildrenElId: function() {
1083 return "ygtvc" + this.index;
1084 },
1085
1086 /**
1087 * Returns the id for this node's toggle element
1088 * @method getToggleElId
1089 * @return {string} the toggel element id
1090 */
1091 getToggleElId: function() {
1092 return "ygtvt" + this.index;
1093 },
1094
1095 /*
1096 * Returns the id for this node's spacer image. The spacer is positioned
1097 * over the toggle and provides feedback for screen readers.
1098 * @method getSpacerId
1099 * @return {string} the id for the spacer image
1100 */
1101 /*
1102 getSpacerId: function() {
1103 return "ygtvspacer" + this.index;
1104 },
1105 */
1106
1107 /**
1108 * Returns this node's container html element
1109 * @method getEl
1110 * @return {HTMLElement} the container html element
1111 */
1112 getEl: function() {
1113 return document.getElementById(this.getElId());
1114 },
1115
1116 /**
1117 * Returns the div that was generated for this node's children
1118 * @method getChildrenEl
1119 * @return {HTMLElement} this node's children div
1120 */
1121 getChildrenEl: function() {
1122 return document.getElementById(this.getChildrenElId());
1123 },
1124
1125 /**
1126 * Returns the element that is being used for this node's toggle.
1127 * @method getToggleEl
1128 * @return {HTMLElement} this node's toggle html element
1129 */
1130 getToggleEl: function() {
1131 return document.getElementById(this.getToggleElId());
1132 },
1133
1134 /*
1135 * Returns the element that is being used for this node's spacer.
1136 * @method getSpacer
1137 * @return {HTMLElement} this node's spacer html element
1138 */
1139 /*
1140 getSpacer: function() {
1141 return document.getElementById( this.getSpacerId() ) || {};
1142 },
1143 */
1144
1145 /*
1146 getStateText: function() {
1147 if (this.isLoading) {
1148 return this.loadingText;
1149 } else if (this.hasChildren(true)) {
1150 if (this.expanded) {
1151 return this.expandedText;
1152 } else {
1153 return this.collapsedText;
1154 }
1155 } else {
1156 return "";
1157 }
1158 },
1159 */
1160
1161 /**
1162 * Generates the link that will invoke this node's toggle method
1163 * @method getToggleLink
1164 * @return {string} the javascript url for toggling this node
1165 */
1166 getToggleLink: function() {
1167 return "YAHOO.widget.TreeView.getNode(\'" + this.tree.id + "\'," +
1168 this.index + ").toggle()";
1169 },
1170
1171 /**
1172 * Hides this nodes children (creating them if necessary), changes the
1173 * @method collapse
1174 * toggle style.
1175 */
1176 collapse: function() {
1177 // Only collapse if currently expanded
1178 if (!this.expanded) { return; }
1179
1180 // fire the collapse event handler
1181 var ret = this.tree.onCollapse(this);
1182
1183 if (false === ret) {
1184 return;
1185 }
1186
1187 ret = this.tree.fireEvent("collapse", this);
1188
1189 if (false === ret) {
1190 return;
1191 }
1192
1193 if (!this.getEl()) {
1194 this.expanded = false;
1195 return;
1196 }
1197
1198 // hide the child div
1199 this.hideChildren();
1200 this.expanded = false;
1201
1202 this.updateIcon();
1203
1204 // this.getSpacer().title = this.getStateText();
1205
1206 },
1207
1208 /**
1209 * Shows this nodes children (creating them if necessary), changes the
1210 * toggle style, and collapses its siblings if multiExpand is not set.
1211 * @method expand
1212 */
1213 expand: function() {
1214 // Only expand if currently collapsed.
1215 if (this.expanded) { return; }
1216
1217 // fire the expand event handler
1218 var ret = this.tree.onExpand(this);
1219
1220 if (false === ret) {
1221 return;
1222 }
1223
1224 ret = this.tree.fireEvent("expand", this);
1225
1226 if (false === ret) {
1227 return;
1228 }
1229
1230 if (!this.getEl()) {
1231 this.expanded = true;
1232 return;
1233 }
1234
1235 if (! this.childrenRendered) {
1236 this.getChildrenEl().innerHTML = this.renderChildren();
1237 } else {
1238 }
1239
1240 this.expanded = true;
1241
1242 this.updateIcon();
1243
1244 // this.getSpacer().title = this.getStateText();
1245
1246 // We do an extra check for children here because the lazy
1247 // load feature can expose nodes that have no children.
1248
1249 // if (!this.hasChildren()) {
1250 if (this.isLoading) {
1251 this.expanded = false;
1252 return;
1253 }
1254
1255 if (! this.multiExpand) {
1256 var sibs = this.getSiblings();
1257 for (var i=0; i<sibs.length; ++i) {
1258 if (sibs[i] != this && sibs[i].expanded) {
1259 sibs[i].collapse();
1260 }
1261 }
1262 }
1263
1264 this.showChildren();
1265 },
1266
1267 updateIcon: function() {
1268 if (this.hasIcon) {
1269 var el = this.getToggleEl();
1270 if (el) {
1271 el.className = this.getStyle();
1272 }
1273 }
1274 },
1275
1276 /**
1277 * Returns the css style name for the toggle
1278 * @method getStyle
1279 * @return {string} the css class for this node's toggle
1280 */
1281 getStyle: function() {
1282 if (this.isLoading) {
1283 return "ygtvloading";
1284 } else {
1285 // location top or bottom, middle nodes also get the top style
1286 var loc = (this.nextSibling) ? "t" : "l";
1287
1288 // type p=plus(expand), m=minus(collapase), n=none(no children)
1289 var type = "n";
1290 if (this.hasChildren(true) || (this.isDynamic() && !this.getIconMode())) {
1291 // if (this.hasChildren(true)) {
1292 type = (this.expanded) ? "m" : "p";
1293 }
1294
1295 return "ygtv" + loc + type;
1296 }
1297 },
1298
1299 /**
1300 * Returns the hover style for the icon
1301 * @return {string} the css class hover state
1302 * @method getHoverStyle
1303 */
1304 getHoverStyle: function() {
1305 var s = this.getStyle();
1306 if (this.hasChildren(true) && !this.isLoading) {
1307 s += "h";
1308 }
1309 return s;
1310 },
1311
1312 /**
1313 * Recursively expands all of this node's children.
1314 * @method expandAll
1315 */
1316 expandAll: function() {
1317 for (var i=0;i<this.children.length;++i) {
1318 var c = this.children[i];
1319 if (c.isDynamic()) {
1320 alert("Not supported (lazy load + expand all)");
1321 break;
1322 } else if (! c.multiExpand) {
1323 alert("Not supported (no multi-expand + expand all)");
1324 break;
1325 } else {
1326 c.expand();
1327 c.expandAll();
1328 }
1329 }
1330 },
1331
1332 /**
1333 * Recursively collapses all of this node's children.
1334 * @method collapseAll
1335 */
1336 collapseAll: function() {
1337 for (var i=0;i<this.children.length;++i) {
1338 this.children[i].collapse();
1339 this.children[i].collapseAll();
1340 }
1341 },
1342
1343 /**
1344 * Configures this node for dynamically obtaining the child data
1345 * when the node is first expanded. Calling it without the callback
1346 * will turn off dynamic load for the node.
1347 * @method setDynamicLoad
1348 * @param fmDataLoader {function} the function that will be used to get the data.
1349 * @param iconMode {int} configures the icon that is displayed when a dynamic
1350 * load node is expanded the first time without children. By default, the
1351 * "collapse" icon will be used. If set to 1, the leaf node icon will be
1352 * displayed.
1353 */
1354 setDynamicLoad: function(fnDataLoader, iconMode) {
1355 if (fnDataLoader) {
1356 this.dataLoader = fnDataLoader;
1357 this._dynLoad = true;
1358 } else {
1359 this.dataLoader = null;
1360 this._dynLoad = false;
1361 }
1362
1363 if (iconMode) {
1364 this.iconMode = iconMode;
1365 }
1366 },
1367
1368 /**
1369 * Evaluates if this node is the root node of the tree
1370 * @method isRoot
1371 * @return {boolean} true if this is the root node
1372 */
1373 isRoot: function() {
1374 return (this == this.tree.root);
1375 },
1376
1377 /**
1378 * Evaluates if this node's children should be loaded dynamically. Looks for
1379 * the property both in this instance and the root node. If the tree is
1380 * defined to load all children dynamically, the data callback function is
1381 * defined in the root node
1382 * @method isDynamic
1383 * @return {boolean} true if this node's children are to be loaded dynamically
1384 */
1385 isDynamic: function() {
1386 var lazy = (!this.isRoot() && (this._dynLoad || this.tree.root._dynLoad));
1387 return lazy;
1388 },
1389
1390 /**
1391 * Returns the current icon mode. This refers to the way childless dynamic
1392 * load nodes appear.
1393 * @method getIconMode
1394 * @return {int} 0 for collapse style, 1 for leaf node style
1395 */
1396 getIconMode: function() {
1397 return (this.iconMode || this.tree.root.iconMode);
1398 },
1399
1400 /**
1401 * Checks if this node has children. If this node is lazy-loading and the
1402 * children have not been rendered, we do not know whether or not there
1403 * are actual children. In most cases, we need to assume that there are
1404 * children (for instance, the toggle needs to show the expandable
1405 * presentation state). In other times we want to know if there are rendered
1406 * children. For the latter, "checkForLazyLoad" should be false.
1407 * @method hasChildren
1408 * @param checkForLazyLoad {boolean} should we check for unloaded children?
1409 * @return {boolean} true if this has children or if it might and we are
1410 * checking for this condition.
1411 */
1412 hasChildren: function(checkForLazyLoad) {
1413 return ( this.children.length > 0 ||
1414 (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete) );
1415 },
1416
1417 /**
1418 * Expands if node is collapsed, collapses otherwise.
1419 * @method toggle
1420 */
1421 toggle: function() {
1422 if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) {
1423 if (this.expanded) { this.collapse(); } else { this.expand(); }
1424 }
1425 },
1426
1427 /**
1428 * Returns the markup for this node and its children.
1429 * @method getHtml
1430 * @return {string} the markup for this node and its expanded children.
1431 */
1432 getHtml: function() {
1433
1434 this.childrenRendered = false;
1435
1436 var sb = [];
1437 sb[sb.length] = '<div class="ygtvitem" id="' + this.getElId() + '">';
1438 sb[sb.length] = this.getNodeHtml();
1439 sb[sb.length] = this.getChildrenHtml();
1440 sb[sb.length] = '</div>';
1441 return sb.join("");
1442 },
1443
1444 /**
1445 * Called when first rendering the tree. We always build the div that will
1446 * contain this nodes children, but we don't render the children themselves
1447 * unless this node is expanded.
1448 * @method getChildrenHtml
1449 * @return {string} the children container div html and any expanded children
1450 * @private
1451 */
1452 getChildrenHtml: function() {
1453
1454 var sb = [];
1455 sb[sb.length] = '<div class="ygtvchildren"';
1456 sb[sb.length] = ' id="' + this.getChildrenElId() + '"';
1457 if (!this.expanded) {
1458 sb[sb.length] = ' style="display:none;"';
1459 }
1460 sb[sb.length] = '>';
1461
1462 // Don't render the actual child node HTML unless this node is expanded.
1463 if ( (this.hasChildren(true) && this.expanded) ||
1464 (this.renderHidden && !this.isDynamic()) ) {
1465 sb[sb.length] = this.renderChildren();
1466 }
1467
1468 sb[sb.length] = '</div>';
1469
1470 return sb.join("");
1471 },
1472
1473 /**
1474 * Generates the markup for the child nodes. This is not done until the node
1475 * is expanded.
1476 * @method renderChildren
1477 * @return {string} the html for this node's children
1478 * @private
1479 */
1480 renderChildren: function() {
1481
1482
1483 var node = this;
1484
1485 if (this.isDynamic() && !this.dynamicLoadComplete) {
1486 this.isLoading = true;
1487 this.tree.locked = true;
1488
1489 if (this.dataLoader) {
1490
1491 setTimeout(
1492 function() {
1493 node.dataLoader(node,
1494 function() {
1495 node.loadComplete();
1496 });
1497 }, 10);
1498
1499 } else if (this.tree.root.dataLoader) {
1500
1501 setTimeout(
1502 function() {
1503 node.tree.root.dataLoader(node,
1504 function() {
1505 node.loadComplete();
1506 });
1507 }, 10);
1508
1509 } else {
1510 return "Error: data loader not found or not specified.";
1511 }
1512
1513 return "";
1514
1515 } else {
1516 return this.completeRender();
1517 }
1518 },
1519
1520 /**
1521 * Called when we know we have all the child data.
1522 * @method completeRender
1523 * @return {string} children html
1524 */
1525 completeRender: function() {
1526 var sb = [];
1527
1528 for (var i=0; i < this.children.length; ++i) {
1529 // this.children[i].childrenRendered = false;
1530 sb[sb.length] = this.children[i].getHtml();
1531 }
1532
1533 this.childrenRendered = true;
1534
1535 return sb.join("");
1536 },
1537
1538 /**
1539 * Load complete is the callback function we pass to the data provider
1540 * in dynamic load situations.
1541 * @method loadComplete
1542 */
1543 loadComplete: function() {
1544 this.getChildrenEl().innerHTML = this.completeRender();
1545 this.dynamicLoadComplete = true;
1546 this.isLoading = false;
1547 this.expand();
1548 this.tree.locked = false;
1549 },
1550
1551 /**
1552 * Returns this node's ancestor at the specified depth.
1553 * @method getAncestor
1554 * @param {int} depth the depth of the ancestor.
1555 * @return {Node} the ancestor
1556 */
1557 getAncestor: function(depth) {
1558 if (depth >= this.depth || depth < 0) {
1559 return null;
1560 }
1561
1562 var p = this.parent;
1563
1564 while (p.depth > depth) {
1565 p = p.parent;
1566 }
1567
1568 return p;
1569 },
1570
1571 /**
1572 * Returns the css class for the spacer at the specified depth for
1573 * this node. If this node's ancestor at the specified depth
1574 * has a next sibling the presentation is different than if it
1575 * does not have a next sibling
1576 * @method getDepthStyle
1577 * @param {int} depth the depth of the ancestor.
1578 * @return {string} the css class for the spacer
1579 */
1580 getDepthStyle: function(depth) {
1581 return (this.getAncestor(depth).nextSibling) ?
1582 "ygtvdepthcell" : "ygtvblankdepthcell";
1583 },
1584
1585 /**
1586 * Get the markup for the node. This is designed to be overrided so that we can
1587 * support different types of nodes.
1588 * @method getNodeHtml
1589 * @return {string} The HTML that will render this node.
1590 */
1591 getNodeHtml: function() {
1592 return "";
1593 },
1594
1595 /**
1596 * Regenerates the html for this node and its children. To be used when the
1597 * node is expanded and new children have been added.
1598 * @method refresh
1599 */
1600 refresh: function() {
1601 // this.loadComplete();
1602 this.getChildrenEl().innerHTML = this.completeRender();
1603
1604 if (this.hasIcon) {
1605 var el = this.getToggleEl();
1606 if (el) {
1607 el.className = this.getStyle();
1608 }
1609 }
1610 },
1611
1612 /**
1613 * Node toString
1614 * @method toString
1615 * @return {string} string representation of the node
1616 */
1617 toString: function() {
1618 return "Node (" + this.index + ")";
1619 }
1620
1621};
1622
1623YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider);
1624
1625/**
1626 * A custom YAHOO.widget.Node that handles the unique nature of
1627 * the virtual, presentationless root node.
1628 * @namespace YAHOO.widget
1629 * @class RootNode
1630 * @extends YAHOO.widget.Node
1631 * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to
1632 * @constructor
1633 */
1634YAHOO.widget.RootNode = function(oTree) {
1635 // Initialize the node with null params. The root node is a
1636 // special case where the node has no presentation. So we have
1637 // to alter the standard properties a bit.
1638 this.init(null, null, true);
1639
1640 /*
1641 * For the root node, we get the tree reference from as a param
1642 * to the constructor instead of from the parent element.
1643 */
1644 this.tree = oTree;
1645};
1646
1647YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, {
1648
1649 // overrides YAHOO.widget.Node
1650 getNodeHtml: function() {
1651 return "";
1652 },
1653
1654 toString: function() {
1655 return "RootNode";
1656 },
1657
1658 loadComplete: function() {
1659 this.tree.draw();
1660 }
1661
1662});
1663/**
1664 * The default node presentation. The first parameter should be
1665 * either a string that will be used as the node's label, or an object
1666 * that has a string propery called label. By default, the clicking the
1667 * label will toggle the expanded/collapsed state of the node. By
1668 * changing the href property of the instance, this behavior can be
1669 * changed so that the label will go to the specified href.
1670 * @namespace YAHOO.widget
1671 * @class TextNode
1672 * @extends YAHOO.widget.Node
1673 * @constructor
1674 * @param oData {object} a string or object containing the data that will
1675 * be used to render this node
1676 * @param oParent {YAHOO.widget.Node} this node's parent node
1677 * @param expanded {boolean} the initial expanded/collapsed state
1678 */
1679YAHOO.widget.TextNode = function(oData, oParent, expanded) {
1680
1681 if (oData) {
1682 this.init(oData, oParent, expanded);
1683 this.setUpLabel(oData);
1684 }
1685
1686};
1687
1688YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, {
1689
1690 /**
1691 * The CSS class for the label href. Defaults to ygtvlabel, but can be
1692 * overridden to provide a custom presentation for a specific node.
1693 * @property labelStyle
1694 * @type string
1695 */
1696 labelStyle: "ygtvlabel",
1697
1698 /**
1699 * The derived element id of the label for this node
1700 * @property labelElId
1701 * @type string
1702 */
1703 labelElId: null,
1704
1705 /**
1706 * The text for the label. It is assumed that the oData parameter will
1707 * either be a string that will be used as the label, or an object that
1708 * has a property called "label" that we will use.
1709 * @property label
1710 * @type string
1711 */
1712 label: null,
1713
1714 textNodeParentChange: function() {
1715
1716 /**
1717 * Custom event that is fired when the text node label is clicked. The
1718 * custom event is defined on the tree instance, so there is a single
1719 * event that handles all nodes in the tree. The node clicked is
1720 * provided as an argument
1721 *
1722 * @event labelClick
1723 * @for YAHOO.widget.TreeView
1724 * @param {YAHOO.widget.Node} node the node clicked
1725 */
1726 if (this.tree && !this.tree.hasEvent("labelClick")) {
1727 this.tree.createEvent("labelClick", this.tree);
1728 }
1729
1730 },
1731
1732 /**
1733 * Sets up the node label
1734 * @method setUpLabel
1735 * @param oData string containing the label, or an object with a label property
1736 */
1737 setUpLabel: function(oData) {
1738
1739 // set up the custom event on the tree
1740 this.textNodeParentChange();
1741 this.subscribe("parentChange", this.textNodeParentChange);
1742
1743 if (typeof oData == "string") {
1744 oData = { label: oData };
1745 }
1746 this.label = oData.label;
1747
1748 // update the link
1749 if (oData.href) {
1750 this.href = oData.href;
1751 }
1752
1753 // set the target
1754 if (oData.target) {
1755 this.target = oData.target;
1756 }
1757
1758 if (oData.style) {
1759 this.labelStyle = oData.style;
1760 }
1761
1762 this.labelElId = "ygtvlabelel" + this.index;
1763 },
1764
1765 /**
1766 * Returns the label element
1767 * @for YAHOO.widget.TextNode
1768 * @method getLabelEl
1769 * @return {object} the element
1770 */
1771 getLabelEl: function() {
1772 return document.getElementById(this.labelElId);
1773 },
1774
1775 // overrides YAHOO.widget.Node
1776 getNodeHtml: function() {
1777 var sb = [];
1778
1779 sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">';
1780 sb[sb.length] = '<tr>';
1781
1782 for (var i=0;i<this.depth;++i) {
1783 // sb[sb.length] = '<td class="ygtvdepthcell">&#160;</td>';
1784 sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '">&#160;</td>';
1785 }
1786
1787 var getNode = 'YAHOO.widget.TreeView.getNode(\'' +
1788 this.tree.id + '\',' + this.index + ')';
1789
1790 sb[sb.length] = '<td';
1791 // sb[sb.length] = ' onselectstart="return false"';
1792 sb[sb.length] = ' id="' + this.getToggleElId() + '"';
1793 sb[sb.length] = ' class="' + this.getStyle() + '"';
1794 if (this.hasChildren(true)) {
1795 sb[sb.length] = ' onmouseover="this.className=';
1796 sb[sb.length] = getNode + '.getHoverStyle()"';
1797 sb[sb.length] = ' onmouseout="this.className=';
1798 sb[sb.length] = getNode + '.getStyle()"';
1799 }
1800 sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '">';
1801
1802 /*
1803 sb[sb.length] = '<img id="' + this.getSpacerId() + '"';
1804 sb[sb.length] = ' alt=""';
1805 sb[sb.length] = ' tabindex=0';
1806 sb[sb.length] = ' src="' + this.spacerPath + '"';
1807 sb[sb.length] = ' title="' + this.getStateText() + '"';
1808 sb[sb.length] = ' class="ygtvspacer"';
1809 // sb[sb.length] = ' onkeypress="return ' + getNode + '".onKeyPress()"';
1810 sb[sb.length] = ' />';
1811 */
1812
1813 sb[sb.length] = '&#160;';
1814
1815 sb[sb.length] = '</td>';
1816 sb[sb.length] = '<td>';
1817 sb[sb.length] = '<a';
1818 sb[sb.length] = ' id="' + this.labelElId + '"';
1819 sb[sb.length] = ' class="' + this.labelStyle + '"';
1820 sb[sb.length] = ' href="' + this.href + '"';
1821 sb[sb.length] = ' target="' + this.target + '"';
1822 sb[sb.length] = ' onclick="return ' + getNode + '.onLabelClick(' + getNode +')"';
1823 if (this.hasChildren(true)) {
1824 sb[sb.length] = ' onmouseover="document.getElementById(\'';
1825 sb[sb.length] = this.getToggleElId() + '\').className=';
1826 sb[sb.length] = getNode + '.getHoverStyle()"';
1827 sb[sb.length] = ' onmouseout="document.getElementById(\'';
1828 sb[sb.length] = this.getToggleElId() + '\').className=';
1829 sb[sb.length] = getNode + '.getStyle()"';
1830 }
1831 sb[sb.length] = ' >';
1832 sb[sb.length] = this.label;
1833 sb[sb.length] = '</a>';
1834 sb[sb.length] = '</td>';
1835 sb[sb.length] = '</tr>';
1836 sb[sb.length] = '</table>';
1837
1838 return sb.join("");
1839 },
1840
1841 /**
1842 * Executed when the label is clicked. Fires the labelClick custom event.
1843 * @method onLabelClick
1844 * @param me {Node} this node
1845 * @scope the anchor tag clicked
1846 * @return false to cancel the anchor click
1847 */
1848 onLabelClick: function(me) {
1849 return me.tree.fireEvent("labelClick", me);
1850 //return true;
1851 },
1852
1853 toString: function() {
1854 return "TextNode (" + this.index + ") " + this.label;
1855 }
1856
1857});
1858/**
1859 * A menu-specific implementation that differs from TextNode in that only
1860 * one sibling can be expanded at a time.
1861 * @namespace YAHOO.widget
1862 * @class MenuNode
1863 * @extends YAHOO.widget.TextNode
1864 * @param oData {object} a string or object containing the data that will
1865 * be used to render this node
1866 * @param oParent {YAHOO.widget.Node} this node's parent node
1867 * @param expanded {boolean} the initial expanded/collapsed state
1868 * @constructor
1869 */
1870YAHOO.widget.MenuNode = function(oData, oParent, expanded) {
1871 if (oData) {
1872 this.init(oData, oParent, expanded);
1873 this.setUpLabel(oData);
1874 }
1875
1876 /*
1877 * Menus usually allow only one branch to be open at a time.
1878 */
1879 this.multiExpand = false;
1880
1881
1882};
1883
1884YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, {
1885
1886 toString: function() {
1887 return "MenuNode (" + this.index + ") " + this.label;
1888 }
1889
1890});
1891/**
1892 * This implementation takes either a string or object for the
1893 * oData argument. If is it a string, we will use it for the display
1894 * of this node (and it can contain any html code). If the parameter
1895 * is an object, we look for a parameter called "html" that will be
1896 * used for this node's display.
1897 * @namespace YAHOO.widget
1898 * @class HTMLNode
1899 * @extends YAHOO.widget.Node
1900 * @constructor
1901 * @param oData {object} a string or object containing the data that will
1902 * be used to render this node
1903 * @param oParent {YAHOO.widget.Node} this node's parent node
1904 * @param expanded {boolean} the initial expanded/collapsed state
1905 * @param hasIcon {boolean} specifies whether or not leaf nodes should
1906 * have an icon
1907 */
1908YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) {
1909 if (oData) {
1910 this.init(oData, oParent, expanded);
1911 this.initContent(oData, hasIcon);
1912 }
1913};
1914
1915YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.Node, {
1916
1917 /**
1918 * The CSS class for the html content container. Defaults to ygtvhtml, but
1919 * can be overridden to provide a custom presentation for a specific node.
1920 * @property contentStyle
1921 * @type string
1922 */
1923 contentStyle: "ygtvhtml",
1924
1925 /**
1926 * The generated id that will contain the data passed in by the implementer.
1927 * @property contentElId
1928 * @type string
1929 */
1930 contentElId: null,
1931
1932 /**
1933 * The HTML content to use for this node's display
1934 * @property content
1935 * @type string
1936 */
1937 content: null,
1938
1939 /**
1940 * Sets up the node label
1941 * @property initContent
1942 * @param {object} An html string or object containing an html property
1943 * @param {boolean} hasIcon determines if the node will be rendered with an
1944 * icon or not
1945 */
1946 initContent: function(oData, hasIcon) {
1947 if (typeof oData == "string") {
1948 oData = { html: oData };
1949 }
1950
1951 this.html = oData.html;
1952 this.contentElId = "ygtvcontentel" + this.index;
1953 this.hasIcon = hasIcon;
1954
1955 },
1956
1957 /**
1958 * Returns the outer html element for this node's content
1959 * @method getContentEl
1960 * @return {HTMLElement} the element
1961 */
1962 getContentEl: function() {
1963 return document.getElementById(this.contentElId);
1964 },
1965
1966 // overrides YAHOO.widget.Node
1967 getNodeHtml: function() {
1968 var sb = [];
1969
1970 sb[sb.length] = '<table border="0" cellpadding="0" cellspacing="0">';
1971 sb[sb.length] = '<tr>';
1972
1973 for (var i=0;i<this.depth;++i) {
1974 sb[sb.length] = '<td class="' + this.getDepthStyle(i) + '">&#160;</td>';
1975 }
1976
1977 if (this.hasIcon) {
1978 sb[sb.length] = '<td';
1979 sb[sb.length] = ' id="' + this.getToggleElId() + '"';
1980 sb[sb.length] = ' class="' + this.getStyle() + '"';
1981 sb[sb.length] = ' onclick="javascript:' + this.getToggleLink() + '"';
1982 if (this.hasChildren(true)) {
1983 sb[sb.length] = ' onmouseover="this.className=';
1984 sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
1985 sb[sb.length] = this.tree.id + '\',' + this.index + ').getHoverStyle()"';
1986 sb[sb.length] = ' onmouseout="this.className=';
1987 sb[sb.length] = 'YAHOO.widget.TreeView.getNode(\'';
1988 sb[sb.length] = this.tree.id + '\',' + this.index + ').getStyle()"';
1989 }
1990 sb[sb.length] = '>&#160;</td>';
1991 }
1992
1993 sb[sb.length] = '<td';
1994 sb[sb.length] = ' id="' + this.contentElId + '"';
1995 sb[sb.length] = ' class="' + this.contentStyle + '"';
1996 sb[sb.length] = ' >';
1997 sb[sb.length] = this.html;
1998 sb[sb.length] = '</td>';
1999 sb[sb.length] = '</tr>';
2000 sb[sb.length] = '</table>';
2001
2002 return sb.join("");
2003 },
2004
2005 toString: function() {
2006 return "HTMLNode (" + this.index + ")";
2007 }
2008
2009});
2010/**
2011 * A static factory class for tree view expand/collapse animations
2012 * @class TVAnim
2013 * @static
2014 */
2015YAHOO.widget.TVAnim = function() {
2016 return {
2017 /**
2018 * Constant for the fade in animation
2019 * @property FADE_IN
2020 * @type string
2021 * @static
2022 */
2023 FADE_IN: "TVFadeIn",
2024
2025 /**
2026 * Constant for the fade out animation
2027 * @property FADE_OUT
2028 * @type string
2029 * @static
2030 */
2031 FADE_OUT: "TVFadeOut",
2032
2033 /**
2034 * Returns a ygAnim instance of the given type
2035 * @method getAnim
2036 * @param type {string} the type of animation
2037 * @param el {HTMLElement} the element to element (probably the children div)
2038 * @param callback {function} function to invoke when the animation is done.
2039 * @return {YAHOO.util.Animation} the animation instance
2040 * @static
2041 */
2042 getAnim: function(type, el, callback) {
2043 if (YAHOO.widget[type]) {
2044 return new YAHOO.widget[type](el, callback);
2045 } else {
2046 return null;
2047 }
2048 },
2049
2050 /**
2051 * Returns true if the specified animation class is available
2052 * @method isValid
2053 * @param type {string} the type of animation
2054 * @return {boolean} true if valid, false if not
2055 * @static
2056 */
2057 isValid: function(type) {
2058 return (YAHOO.widget[type]);
2059 }
2060 };
2061} ();
2062
2063/**
2064 * A 1/2 second fade-in animation.
2065 * @class TVFadeIn
2066 * @constructor
2067 * @param el {HTMLElement} the element to animate
2068 * @param callback {function} function to invoke when the animation is finished
2069 */
2070YAHOO.widget.TVFadeIn = function(el, callback) {
2071 /**
2072 * The element to animate
2073 * @property el
2074 * @type HTMLElement
2075 */
2076 this.el = el;
2077
2078 /**
2079 * the callback to invoke when the animation is complete
2080 * @property callback
2081 * @type function
2082 */
2083 this.callback = callback;
2084
2085};
2086
2087YAHOO.widget.TVFadeIn.prototype = {
2088 /**
2089 * Performs the animation
2090 * @method animate
2091 */
2092 animate: function() {
2093 var tvanim = this;
2094
2095 var s = this.el.style;
2096 s.opacity = 0.1;
2097 s.filter = "alpha(opacity=10)";
2098 s.display = "";
2099
2100 var dur = 0.4;
2101 var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}, dur);
2102 a.onComplete.subscribe( function() { tvanim.onComplete(); } );
2103 a.animate();
2104 },
2105
2106 /**
2107 * Clean up and invoke callback
2108 * @method onComplete
2109 */
2110 onComplete: function() {
2111 this.callback();
2112 },
2113
2114 /**
2115 * toString
2116 * @method toString
2117 * @return {string} the string representation of the instance
2118 */
2119 toString: function() {
2120 return "TVFadeIn";
2121 }
2122};
2123
2124/**
2125 * A 1/2 second fade out animation.
2126 * @class TVFadeOut
2127 * @constructor
2128 * @param el {HTMLElement} the element to animate
2129 * @param callback {Function} function to invoke when the animation is finished
2130 */
2131YAHOO.widget.TVFadeOut = function(el, callback) {
2132 /**
2133 * The element to animate
2134 * @property el
2135 * @type HTMLElement
2136 */
2137 this.el = el;
2138
2139 /**
2140 * the callback to invoke when the animation is complete
2141 * @property callback
2142 * @type function
2143 */
2144 this.callback = callback;
2145
2146};
2147
2148YAHOO.widget.TVFadeOut.prototype = {
2149 /**
2150 * Performs the animation
2151 * @method animate
2152 */
2153 animate: function() {
2154 var tvanim = this;
2155 var dur = 0.4;
2156 var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur);
2157 a.onComplete.subscribe( function() { tvanim.onComplete(); } );
2158 a.animate();
2159 },
2160
2161 /**
2162 * Clean up and invoke callback
2163 * @method onComplete
2164 */
2165 onComplete: function() {
2166 var s = this.el.style;
2167 s.display = "none";
2168 // s.opacity = 1;
2169 s.filter = "alpha(opacity=100)";
2170 this.callback();
2171 },
2172
2173 /**
2174 * toString
2175 * @method toString
2176 * @return {string} the string representation of the instance
2177 */
2178 toString: function() {
2179 return "TVFadeOut";
2180 }
2181};
2182
diff --git a/frontend/beta/js/YUI/yahoo.js b/frontend/beta/js/YUI/yahoo.js
new file mode 100644
index 0000000..8a44a91
--- a/dev/null
+++ b/frontend/beta/js/YUI/yahoo.js
@@ -0,0 +1,145 @@
1/*
2Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3Code licensed under the BSD License:
4http://developer.yahoo.net/yui/license.txt
5version: 0.12.0
6*/
7
8/**
9 * The YAHOO object is the single global object used by YUI Library. It
10 * contains utility function for setting up namespaces, inheritance, and
11 * logging. YAHOO.util, YAHOO.widget, and YAHOO.example are namespaces
12 * created automatically for and used by the library.
13 * @module yahoo
14 * @title YAHOO Global
15 */
16
17/**
18 * The YAHOO global namespace object
19 * @class YAHOO
20 * @static
21 */
22if (typeof YAHOO == "undefined") {
23 var YAHOO = {};
24}
25
26/**
27 * Returns the namespace specified and creates it if it doesn't exist
28 * <pre>
29 * YAHOO.namespace("property.package");
30 * YAHOO.namespace("YAHOO.property.package");
31 * </pre>
32 * Either of the above would create YAHOO.property, then
33 * YAHOO.property.package
34 *
35 * Be careful when naming packages. Reserved words may work in some browsers
36 * and not others. For instance, the following will fail in Safari:
37 * <pre>
38 * YAHOO.namespace("really.long.nested.namespace");
39 * </pre>
40 * This fails because "long" is a future reserved word in ECMAScript
41 *
42 * @method namespace
43 * @static
44 * @param {String*} arguments 1-n namespaces to create
45 * @return {Object} A reference to the last namespace object created
46 */
47YAHOO.namespace = function() {
48 var a=arguments, o=null, i, j, d;
49 for (i=0; i<a.length; ++i) {
50 d=a[i].split(".");
51 o=YAHOO;
52
53 // YAHOO is implied, so it is ignored if it is included
54 for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; ++j) {
55 o[d[j]]=o[d[j]] || {};
56 o=o[d[j]];
57 }
58 }
59
60 return o;
61};
62
63/**
64 * Uses YAHOO.widget.Logger to output a log message, if the widget is available.
65 *
66 * @method log
67 * @static
68 * @param {String} msg The message to log.
69 * @param {String} cat The log category for the message. Default
70 * categories are "info", "warn", "error", time".
71 * Custom categories can be used as well. (opt)
72 * @param {String} src The source of the the message (opt)
73 * @return {Boolean} True if the log operation was successful.
74 */
75YAHOO.log = function(msg, cat, src) {
76 var l=YAHOO.widget.Logger;
77 if(l && l.log) {
78 return l.log(msg, cat, src);
79 } else {
80 return false;
81 }
82};
83
84/**
85 * Utility to set up the prototype, constructor and superclass properties to
86 * support an inheritance strategy that can chain constructors and methods.
87 *
88 * @method extend
89 * @static
90 * @param {Function} subc the object to modify
91 * @param {Function} superc the object to inherit
92 * @param {String[]} overrides additional properties/methods to add to the
93 * subclass prototype. These will override the
94 * matching items obtained from the superclass
95 * if present.
96 */
97YAHOO.extend = function(subc, superc, overrides) {
98 var F = function() {};
99 F.prototype=superc.prototype;
100 subc.prototype=new F();
101 subc.prototype.constructor=subc;
102 subc.superclass=superc.prototype;
103 if (superc.prototype.constructor == Object.prototype.constructor) {
104 superc.prototype.constructor=superc;
105 }
106
107 if (overrides) {
108 for (var i in overrides) {
109 subc.prototype[i]=overrides[i];
110 }
111 }
112};
113
114/**
115 * Applies all prototype properties in the supplier to the receiver if the
116 * receiver does not have these properties yet. Optionally, one or more
117 * methods/properties can be specified (as additional parameters). This
118 * option will overwrite the property if receiver has it already.
119 *
120 * @method augment
121 * @static
122 * @param {Function} r the object to receive the augmentation
123 * @param {Function} s the object that supplies the properties to augment
124 * @param {String*} arguments zero or more properties methods to augment the
125 * receiver with. If none specified, everything
126 * in the supplier will be used unless it would
127 * overwrite an existing property in the receiver
128 */
129YAHOO.augment = function(r, s) {
130 var rp=r.prototype, sp=s.prototype, a=arguments, i, p;
131 if (a[2]) {
132 for (i=2; i<a.length; ++i) {
133 rp[a[i]] = sp[a[i]];
134 }
135 } else {
136 for (p in sp) {
137 if (!rp[p]) {
138 rp[p] = sp[p];
139 }
140 }
141 }
142};
143
144YAHOO.namespace("util", "widget", "example");
145