diff --git a/source/web/scripts/ajax/yahoo/animation/README b/source/web/scripts/ajax/yahoo/animation/README new file mode 100644 index 0000000000..e0f99516e1 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/animation/README @@ -0,0 +1,34 @@ +Animation Release Notes + +*** version 0.12.0 *** + +* added boolean finish argument to Anim.stop() + +*** version 0.11.3 *** + +* no changes + +*** version 0.11.1 *** + +* changed "prototype" shorthand to "proto" (workaround firefox < 1.5 scoping +bug) + +*** version 0.11.0 *** + +* ColorAnim subclass added +* Motion and Scroll now inherit from ColorAnim +* getDefaultUnit method added +* defaultUnit and defaultUnits deprecated +* getDefault and setDefault methods deprecated + +*** version 0.10.0 *** + +* Scroll now handles relative ("by") animation correctly + +* Now converts "auto" values of "from" to appropriate initial values + +*** version 0.9.0 *** + +* Initial release + + diff --git a/source/web/scripts/ajax/yahoo/animation/animation-debug.js b/source/web/scripts/ajax/yahoo/animation/animation-debug.js new file mode 100644 index 0000000000..1a4c89c33d --- /dev/null +++ b/source/web/scripts/ajax/yahoo/animation/animation-debug.js @@ -0,0 +1,1276 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ + +/** + * The animation module provides allows effects to be added to HTMLElements. + * @module animation + */ + +/** + * + * Base animation class that provides the interface for building animated effects. + *
Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);
+ * @class Anim + * @namespace YAHOO.util + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String | HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + +YAHOO.util.Anim = function(el, attributes, duration, method) { + if (el) { + this.init(el, attributes, duration, method); + } +}; + +YAHOO.util.Anim.prototype = { + /** + * Provides a readable name for the Anim instance. + * @method toString + * @return {String} + */ + toString: function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Anim " + id); + }, + + patterns: { // cached for performance + noNegatives: /width|height|opacity|padding/i, // keep at zero or above + offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default + defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default + offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset + }, + + /** + * Returns the value computed by the animation's "method". + * @method doMethod + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + doMethod: function(attr, start, end) { + return this.method(this.currentFrame, start, end - start, this.totalFrames); + }, + + /** + * Applies a value to an attribute. + * @method setAttribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + setAttribute: function(attr, val, unit) { + if ( this.patterns.noNegatives.test(attr) ) { + val = (val > 0) ? val : 0; + } + + YAHOO.util.Dom.setStyle(this.getEl(), attr, val + unit); + }, + + /** + * Returns current value of the attribute. + * @method getAttribute + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + getAttribute: function(attr) { + var el = this.getEl(); + var val = YAHOO.util.Dom.getStyle(el, attr); + + if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) { + return parseFloat(val); + } + + var a = this.patterns.offsetAttribute.exec(attr) || []; + var pos = !!( a[3] ); // top or left + var box = !!( a[2] ); // width or height + + // use offsets for width/height and abs pos top/left + if ( box || (YAHOO.util.Dom.getStyle(el, 'position') == 'absolute' && pos) ) { + val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)]; + } else { // default to zero for other 'auto' + val = 0; + } + + return val; + }, + + /** + * Returns the unit to use when none is supplied. + * @method getDefaultUnit + * @param {attr} attr The name of the attribute. + * @return {String} The default unit to be used. + */ + getDefaultUnit: function(attr) { + if ( this.patterns.defaultUnit.test(attr) ) { + return 'px'; + } + + return ''; + }, + + /** + * Sets the actual values to be used during the animation. + * @method setRuntimeAttribute + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + setRuntimeAttribute: function(attr) { + var start; + var end; + var attributes = this.attributes; + + this.runtimeAttributes[attr] = {}; + + var isset = function(prop) { + return (typeof prop !== 'undefined'); + }; + + if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) { + return false; // note return; nothing to animate to + } + + start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr); + + // To beats by, per SMIL 2.1 spec + if ( isset(attributes[attr]['to']) ) { + end = attributes[attr]['to']; + } else if ( isset(attributes[attr]['by']) ) { + if (start.constructor == Array) { + end = []; + for (var i = 0, len = start.length; i < len; ++i) { + end[i] = start[i] + attributes[attr]['by'][i]; + } + } else { + end = start + attributes[attr]['by']; + } + } + + this.runtimeAttributes[attr].start = start; + this.runtimeAttributes[attr].end = end; + + // set units if needed + this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr); + }, + + /** + * Constructor for Anim instance. + * @method init + * @param {String | HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + init: function(el, attributes, duration, method) { + /** + * Whether or not the animation is running. + * @property isAnimated + * @private + * @type Boolean + */ + var isAnimated = false; + + /** + * A Date object that is created when the animation begins. + * @property startTime + * @private + * @type Date + */ + var startTime = null; + + /** + * The number of frames this animation was able to execute. + * @property actualFrames + * @private + * @type Int + */ + var actualFrames = 0; + + /** + * The element to be animated. + * @property el + * @private + * @type HTMLElement + */ + el = YAHOO.util.Dom.get(el); + + /** + * The collection of attributes to be animated. + * Each attribute must have at least a "to" or "by" defined in order to animate. + * If "to" is supplied, the animation will end with the attribute at that value. + * If "by" is supplied, the animation will end at that value plus its starting value. + * If both are supplied, "to" is used, and "by" is ignored. + * 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). + * @property attributes + * @type Object + */ + this.attributes = attributes || {}; + + /** + * The length of the animation. Defaults to "1" (second). + * @property duration + * @type Number + */ + this.duration = duration || 1; + + /** + * The method that will provide values to the attribute(s) during the animation. + * Defaults to "YAHOO.util.Easing.easeNone". + * @property method + * @type Function + */ + this.method = method || YAHOO.util.Easing.easeNone; + + /** + * Whether or not the duration should be treated as seconds. + * Defaults to true. + * @property useSeconds + * @type Boolean + */ + this.useSeconds = true; // default to seconds + + /** + * The location of the current animation on the timeline. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @property currentFrame + * @type Int + */ + this.currentFrame = 0; + + /** + * The total number of frames to be executed. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @property totalFrames + * @type Int + */ + this.totalFrames = YAHOO.util.AnimMgr.fps; + + + /** + * Returns a reference to the animated element. + * @method getEl + * @return {HTMLElement} + */ + this.getEl = function() { return el; }; + + /** + * Checks whether the element is currently animated. + * @method isAnimated + * @return {Boolean} current value of isAnimated. + */ + this.isAnimated = function() { + return isAnimated; + }; + + /** + * Returns the animation start time. + * @method getStartTime + * @return {Date} current value of startTime. + */ + this.getStartTime = function() { + return startTime; + }; + + this.runtimeAttributes = {}; + + var logger = {}; + logger.log = function() {YAHOO.log.apply(window, arguments)}; + + logger.log('creating new instance of ' + this); + + /** + * Starts the animation by registering it with the animation manager. + * @method animate + */ + this.animate = function() { + if ( this.isAnimated() ) { return false; } + + this.currentFrame = 0; + + this.totalFrames = ( this.useSeconds ) ? Math.ceil(YAHOO.util.AnimMgr.fps * this.duration) : this.duration; + + YAHOO.util.AnimMgr.registerElement(this); + }; + + /** + * Stops the animation. Normally called by AnimMgr when animation completes. + * @method stop + * @param {Boolean} finish (optional) If true, animation will jump to final frame. + */ + this.stop = function(finish) { + if (finish) { + this.currentFrame = this.totalFrames; + this._onTween.fire(); + } + YAHOO.util.AnimMgr.stop(this); + }; + + var onStart = function() { + this.onStart.fire(); + + this.runtimeAttributes = {}; + for (var attr in this.attributes) { + this.setRuntimeAttribute(attr); + } + + isAnimated = true; + actualFrames = 0; + startTime = new Date(); + }; + + /** + * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s). + * @private + */ + + var onTween = function() { + var data = { + duration: new Date() - this.getStartTime(), + currentFrame: this.currentFrame + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', currentFrame: ' + data.currentFrame + ); + }; + + this.onTween.fire(data); + + var runtimeAttributes = this.runtimeAttributes; + + for (var attr in runtimeAttributes) { + this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit); + } + + actualFrames += 1; + }; + + var onComplete = function() { + var actual_duration = (new Date() - startTime) / 1000 ; + + var data = { + duration: actual_duration, + frames: actualFrames, + fps: actualFrames / actual_duration + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', frames: ' + data.frames + + ', fps: ' + data.fps + ); + }; + + isAnimated = false; + actualFrames = 0; + this.onComplete.fire(data); + }; + + /** + * Custom event that fires after onStart, useful in subclassing + * @private + */ + this._onStart = new YAHOO.util.CustomEvent('_start', this, true); + + /** + * Custom event that fires when animation begins + * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction) + * @event onStart + */ + this.onStart = new YAHOO.util.CustomEvent('start', this); + + /** + * Custom event that fires between each frame + * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction) + * @event onTween + */ + this.onTween = new YAHOO.util.CustomEvent('tween', this); + + /** + * Custom event that fires after onTween + * @private + */ + this._onTween = new YAHOO.util.CustomEvent('_tween', this, true); + + /** + * Custom event that fires when animation ends + * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction) + * @event onComplete + */ + this.onComplete = new YAHOO.util.CustomEvent('complete', this); + /** + * Custom event that fires after onComplete + * @private + */ + this._onComplete = new YAHOO.util.CustomEvent('_complete', this, true); + + this._onStart.subscribe(onStart); + this._onTween.subscribe(onTween); + this._onComplete.subscribe(onComplete); + } +}; + +/** + * Handles animation queueing and threading. + * Used by Anim and subclasses. + * @class AnimMgr + * @namespace YAHOO.util + */ +YAHOO.util.AnimMgr = new function() { + /** + * Reference to the animation Interval. + * @property thread + * @private + * @type Int + */ + var thread = null; + + /** + * The current queue of registered animation objects. + * @property queue + * @private + * @type Array + */ + var queue = []; + + /** + * The number of active animations. + * @property tweenCount + * @private + * @type Int + */ + var tweenCount = 0; + + /** + * Base frame rate (frames per second). + * Arbitrarily high for better x-browser calibration (slower browsers drop more frames). + * @property fps + * @type Int + * + */ + this.fps = 200; + + /** + * Interval delay in milliseconds, defaults to fastest possible. + * @property delay + * @type Int + * + */ + this.delay = 1; + + /** + * Adds an animation instance to the animation queue. + * All animation instances must be registered in order to animate. + * @method registerElement + * @param {object} tween The Anim instance to be be registered + */ + this.registerElement = function(tween) { + queue[queue.length] = tween; + tweenCount += 1; + tween._onStart.fire(); + this.start(); + }; + + /** + * removes an animation instance from the animation queue. + * All animation instances must be registered in order to animate. + * @method unRegister + * @param {object} tween The Anim instance to be be registered + * @param {Int} index The index of the Anim instance + * @private + */ + this.unRegister = function(tween, index) { + tween._onComplete.fire(); + index = index || getIndex(tween); + if (index != -1) { queue.splice(index, 1); } + + tweenCount -= 1; + if (tweenCount <= 0) { this.stop(); } + }; + + /** + * Starts the animation thread. + * Only one thread can run at a time. + * @method start + */ + this.start = function() { + if (thread === null) { thread = setInterval(this.run, this.delay); } + }; + + /** + * Stops the animation thread or a specific animation instance. + * @method stop + * @param {object} tween A specific Anim instance to stop (optional) + * If no instance given, Manager stops thread and all animations. + */ + this.stop = function(tween) { + if (!tween) { + clearInterval(thread); + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i].isAnimated()) { + this.unRegister(tween, i); + } + } + queue = []; + thread = null; + tweenCount = 0; + } + else { + this.unRegister(tween); + } + }; + + /** + * Called per Interval to handle each animation frame. + * @method run + */ + this.run = function() { + for (var i = 0, len = queue.length; i < len; ++i) { + var tween = queue[i]; + if ( !tween || !tween.isAnimated() ) { continue; } + + if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null) + { + tween.currentFrame += 1; + + if (tween.useSeconds) { + correctFrame(tween); + } + tween._onTween.fire(); + } + else { YAHOO.util.AnimMgr.stop(tween, i); } + } + }; + + var getIndex = function(anim) { + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i] == anim) { + return i; // note return; + } + } + return -1; + }; + + /** + * On the fly frame correction to keep animation on time. + * @method correctFrame + * @private + * @param {Object} tween The Anim instance being corrected. + */ + var correctFrame = function(tween) { + var frames = tween.totalFrames; + var frame = tween.currentFrame; + var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames); + var elapsed = (new Date() - tween.getStartTime()); + var tweak = 0; + + if (elapsed < tween.duration * 1000) { // check if falling behind + tweak = Math.round((elapsed / expected - 1) * tween.currentFrame); + } else { // went over duration, so jump to end + tweak = frames - (frame + 1); + } + if (tweak > 0 && isFinite(tweak)) { // adjust if needed + if (tween.currentFrame + tweak >= frames) {// dont go past last frame + tweak = frames - (frame + 1); + } + + tween.currentFrame += tweak; + } + }; +}; +/** + * Used to calculate Bezier splines for any number of control points. + * @class Bezier + * @namespace YAHOO.util + * + */ +YAHOO.util.Bezier = new function() +{ + /** + * Get the current position of the animated element based on t. + * Each point is an array of "x" and "y" values (0 = x, 1 = y) + * At least 2 points are required (start and end). + * First point is start. Last point is end. + * Additional control points are optional. + * @method getPosition + * @param {Array} points An array containing Bezier points + * @param {Number} t A number between 0 and 1 which is the basis for determining current position + * @return {Array} An array containing int x and y member data + */ + this.getPosition = function(points, t) + { + var n = points.length; + var tmp = []; + + for (var i = 0; i < n; ++i){ + tmp[i] = [points[i][0], points[i][1]]; // save input + } + + for (var j = 1; j < n; ++j) { + for (i = 0; i < n - j; ++i) { + tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0]; + tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1]; + } + } + + return [ tmp[0][0], tmp[0][1] ]; + + }; +}; +/** + * Anim subclass for color transitions. + *Usage: var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);
Color values can be specified with either 112233, #112233,
+ * [255,255,255], or rgb(255,255,255)
Usage: var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);
Usage: var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);
Usage: var myAnim = new YAHOO.util.Anim(el, { width: { from: 10, to: 100 } }, 1, YAHOO.util.Easing.easeOut);
+ * @class Anim + * @namespace YAHOO.util + * @requires YAHOO.util.AnimMgr + * @requires YAHOO.util.Easing + * @requires YAHOO.util.Dom + * @requires YAHOO.util.Event + * @requires YAHOO.util.CustomEvent + * @constructor + * @param {String | HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + +YAHOO.util.Anim = function(el, attributes, duration, method) { + if (el) { + this.init(el, attributes, duration, method); + } +}; + +YAHOO.util.Anim.prototype = { + /** + * Provides a readable name for the Anim instance. + * @method toString + * @return {String} + */ + toString: function() { + var el = this.getEl(); + var id = el.id || el.tagName; + return ("Anim " + id); + }, + + patterns: { // cached for performance + noNegatives: /width|height|opacity|padding/i, // keep at zero or above + offsetAttribute: /^((width|height)|(top|left))$/, // use offsetValue as default + defaultUnit: /width|height|top$|bottom$|left$|right$/i, // use 'px' by default + offsetUnit: /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i // IE may return these, so convert these to offset + }, + + /** + * Returns the value computed by the animation's "method". + * @method doMethod + * @param {String} attr The name of the attribute. + * @param {Number} start The value this attribute should start from for this animation. + * @param {Number} end The value this attribute should end at for this animation. + * @return {Number} The Value to be applied to the attribute. + */ + doMethod: function(attr, start, end) { + return this.method(this.currentFrame, start, end - start, this.totalFrames); + }, + + /** + * Applies a value to an attribute. + * @method setAttribute + * @param {String} attr The name of the attribute. + * @param {Number} val The value to be applied to the attribute. + * @param {String} unit The unit ('px', '%', etc.) of the value. + */ + setAttribute: function(attr, val, unit) { + if ( this.patterns.noNegatives.test(attr) ) { + val = (val > 0) ? val : 0; + } + + YAHOO.util.Dom.setStyle(this.getEl(), attr, val + unit); + }, + + /** + * Returns current value of the attribute. + * @method getAttribute + * @param {String} attr The name of the attribute. + * @return {Number} val The current value of the attribute. + */ + getAttribute: function(attr) { + var el = this.getEl(); + var val = YAHOO.util.Dom.getStyle(el, attr); + + if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) { + return parseFloat(val); + } + + var a = this.patterns.offsetAttribute.exec(attr) || []; + var pos = !!( a[3] ); // top or left + var box = !!( a[2] ); // width or height + + // use offsets for width/height and abs pos top/left + if ( box || (YAHOO.util.Dom.getStyle(el, 'position') == 'absolute' && pos) ) { + val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)]; + } else { // default to zero for other 'auto' + val = 0; + } + + return val; + }, + + /** + * Returns the unit to use when none is supplied. + * @method getDefaultUnit + * @param {attr} attr The name of the attribute. + * @return {String} The default unit to be used. + */ + getDefaultUnit: function(attr) { + if ( this.patterns.defaultUnit.test(attr) ) { + return 'px'; + } + + return ''; + }, + + /** + * Sets the actual values to be used during the animation. + * @method setRuntimeAttribute + * Should only be needed for subclass use. + * @param {Object} attr The attribute object + * @private + */ + setRuntimeAttribute: function(attr) { + var start; + var end; + var attributes = this.attributes; + + this.runtimeAttributes[attr] = {}; + + var isset = function(prop) { + return (typeof prop !== 'undefined'); + }; + + if ( !isset(attributes[attr]['to']) && !isset(attributes[attr]['by']) ) { + return false; // note return; nothing to animate to + } + + start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr); + + // To beats by, per SMIL 2.1 spec + if ( isset(attributes[attr]['to']) ) { + end = attributes[attr]['to']; + } else if ( isset(attributes[attr]['by']) ) { + if (start.constructor == Array) { + end = []; + for (var i = 0, len = start.length; i < len; ++i) { + end[i] = start[i] + attributes[attr]['by'][i]; + } + } else { + end = start + attributes[attr]['by']; + } + } + + this.runtimeAttributes[attr].start = start; + this.runtimeAttributes[attr].end = end; + + // set units if needed + this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr); + }, + + /** + * Constructor for Anim instance. + * @method init + * @param {String | HTMLElement} el Reference to the element that will be animated + * @param {Object} attributes The attribute(s) to be animated. + * Each attribute is an object with at minimum a "to" or "by" member defined. + * Additional optional members are "from" (defaults to current value), "units" (defaults to "px"). + * All attribute names use camelCase. + * @param {Number} duration (optional, defaults to 1 second) Length of animation (frames or seconds), defaults to time-based + * @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) + */ + init: function(el, attributes, duration, method) { + /** + * Whether or not the animation is running. + * @property isAnimated + * @private + * @type Boolean + */ + var isAnimated = false; + + /** + * A Date object that is created when the animation begins. + * @property startTime + * @private + * @type Date + */ + var startTime = null; + + /** + * The number of frames this animation was able to execute. + * @property actualFrames + * @private + * @type Int + */ + var actualFrames = 0; + + /** + * The element to be animated. + * @property el + * @private + * @type HTMLElement + */ + el = YAHOO.util.Dom.get(el); + + /** + * The collection of attributes to be animated. + * Each attribute must have at least a "to" or "by" defined in order to animate. + * If "to" is supplied, the animation will end with the attribute at that value. + * If "by" is supplied, the animation will end at that value plus its starting value. + * If both are supplied, "to" is used, and "by" is ignored. + * 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). + * @property attributes + * @type Object + */ + this.attributes = attributes || {}; + + /** + * The length of the animation. Defaults to "1" (second). + * @property duration + * @type Number + */ + this.duration = duration || 1; + + /** + * The method that will provide values to the attribute(s) during the animation. + * Defaults to "YAHOO.util.Easing.easeNone". + * @property method + * @type Function + */ + this.method = method || YAHOO.util.Easing.easeNone; + + /** + * Whether or not the duration should be treated as seconds. + * Defaults to true. + * @property useSeconds + * @type Boolean + */ + this.useSeconds = true; // default to seconds + + /** + * The location of the current animation on the timeline. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @property currentFrame + * @type Int + */ + this.currentFrame = 0; + + /** + * The total number of frames to be executed. + * In time-based animations, this is used by AnimMgr to ensure the animation finishes on time. + * @property totalFrames + * @type Int + */ + this.totalFrames = YAHOO.util.AnimMgr.fps; + + + /** + * Returns a reference to the animated element. + * @method getEl + * @return {HTMLElement} + */ + this.getEl = function() { return el; }; + + /** + * Checks whether the element is currently animated. + * @method isAnimated + * @return {Boolean} current value of isAnimated. + */ + this.isAnimated = function() { + return isAnimated; + }; + + /** + * Returns the animation start time. + * @method getStartTime + * @return {Date} current value of startTime. + */ + this.getStartTime = function() { + return startTime; + }; + + this.runtimeAttributes = {}; + + + + /** + * Starts the animation by registering it with the animation manager. + * @method animate + */ + this.animate = function() { + if ( this.isAnimated() ) { return false; } + + this.currentFrame = 0; + + this.totalFrames = ( this.useSeconds ) ? Math.ceil(YAHOO.util.AnimMgr.fps * this.duration) : this.duration; + + YAHOO.util.AnimMgr.registerElement(this); + }; + + /** + * Stops the animation. Normally called by AnimMgr when animation completes. + * @method stop + * @param {Boolean} finish (optional) If true, animation will jump to final frame. + */ + this.stop = function(finish) { + if (finish) { + this.currentFrame = this.totalFrames; + this._onTween.fire(); + } + YAHOO.util.AnimMgr.stop(this); + }; + + var onStart = function() { + this.onStart.fire(); + + this.runtimeAttributes = {}; + for (var attr in this.attributes) { + this.setRuntimeAttribute(attr); + } + + isAnimated = true; + actualFrames = 0; + startTime = new Date(); + }; + + /** + * Feeds the starting and ending values for each animated attribute to doMethod once per frame, then applies the resulting value to the attribute(s). + * @private + */ + + var onTween = function() { + var data = { + duration: new Date() - this.getStartTime(), + currentFrame: this.currentFrame + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', currentFrame: ' + data.currentFrame + ); + }; + + this.onTween.fire(data); + + var runtimeAttributes = this.runtimeAttributes; + + for (var attr in runtimeAttributes) { + this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit); + } + + actualFrames += 1; + }; + + var onComplete = function() { + var actual_duration = (new Date() - startTime) / 1000 ; + + var data = { + duration: actual_duration, + frames: actualFrames, + fps: actualFrames / actual_duration + }; + + data.toString = function() { + return ( + 'duration: ' + data.duration + + ', frames: ' + data.frames + + ', fps: ' + data.fps + ); + }; + + isAnimated = false; + actualFrames = 0; + this.onComplete.fire(data); + }; + + /** + * Custom event that fires after onStart, useful in subclassing + * @private + */ + this._onStart = new YAHOO.util.CustomEvent('_start', this, true); + + /** + * Custom event that fires when animation begins + * Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction) + * @event onStart + */ + this.onStart = new YAHOO.util.CustomEvent('start', this); + + /** + * Custom event that fires between each frame + * Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction) + * @event onTween + */ + this.onTween = new YAHOO.util.CustomEvent('tween', this); + + /** + * Custom event that fires after onTween + * @private + */ + this._onTween = new YAHOO.util.CustomEvent('_tween', this, true); + + /** + * Custom event that fires when animation ends + * Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction) + * @event onComplete + */ + this.onComplete = new YAHOO.util.CustomEvent('complete', this); + /** + * Custom event that fires after onComplete + * @private + */ + this._onComplete = new YAHOO.util.CustomEvent('_complete', this, true); + + this._onStart.subscribe(onStart); + this._onTween.subscribe(onTween); + this._onComplete.subscribe(onComplete); + } +}; + +/** + * Handles animation queueing and threading. + * Used by Anim and subclasses. + * @class AnimMgr + * @namespace YAHOO.util + */ +YAHOO.util.AnimMgr = new function() { + /** + * Reference to the animation Interval. + * @property thread + * @private + * @type Int + */ + var thread = null; + + /** + * The current queue of registered animation objects. + * @property queue + * @private + * @type Array + */ + var queue = []; + + /** + * The number of active animations. + * @property tweenCount + * @private + * @type Int + */ + var tweenCount = 0; + + /** + * Base frame rate (frames per second). + * Arbitrarily high for better x-browser calibration (slower browsers drop more frames). + * @property fps + * @type Int + * + */ + this.fps = 200; + + /** + * Interval delay in milliseconds, defaults to fastest possible. + * @property delay + * @type Int + * + */ + this.delay = 1; + + /** + * Adds an animation instance to the animation queue. + * All animation instances must be registered in order to animate. + * @method registerElement + * @param {object} tween The Anim instance to be be registered + */ + this.registerElement = function(tween) { + queue[queue.length] = tween; + tweenCount += 1; + tween._onStart.fire(); + this.start(); + }; + + /** + * removes an animation instance from the animation queue. + * All animation instances must be registered in order to animate. + * @method unRegister + * @param {object} tween The Anim instance to be be registered + * @param {Int} index The index of the Anim instance + * @private + */ + this.unRegister = function(tween, index) { + tween._onComplete.fire(); + index = index || getIndex(tween); + if (index != -1) { queue.splice(index, 1); } + + tweenCount -= 1; + if (tweenCount <= 0) { this.stop(); } + }; + + /** + * Starts the animation thread. + * Only one thread can run at a time. + * @method start + */ + this.start = function() { + if (thread === null) { thread = setInterval(this.run, this.delay); } + }; + + /** + * Stops the animation thread or a specific animation instance. + * @method stop + * @param {object} tween A specific Anim instance to stop (optional) + * If no instance given, Manager stops thread and all animations. + */ + this.stop = function(tween) { + if (!tween) { + clearInterval(thread); + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i].isAnimated()) { + this.unRegister(tween, i); + } + } + queue = []; + thread = null; + tweenCount = 0; + } + else { + this.unRegister(tween); + } + }; + + /** + * Called per Interval to handle each animation frame. + * @method run + */ + this.run = function() { + for (var i = 0, len = queue.length; i < len; ++i) { + var tween = queue[i]; + if ( !tween || !tween.isAnimated() ) { continue; } + + if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null) + { + tween.currentFrame += 1; + + if (tween.useSeconds) { + correctFrame(tween); + } + tween._onTween.fire(); + } + else { YAHOO.util.AnimMgr.stop(tween, i); } + } + }; + + var getIndex = function(anim) { + for (var i = 0, len = queue.length; i < len; ++i) { + if (queue[i] == anim) { + return i; // note return; + } + } + return -1; + }; + + /** + * On the fly frame correction to keep animation on time. + * @method correctFrame + * @private + * @param {Object} tween The Anim instance being corrected. + */ + var correctFrame = function(tween) { + var frames = tween.totalFrames; + var frame = tween.currentFrame; + var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames); + var elapsed = (new Date() - tween.getStartTime()); + var tweak = 0; + + if (elapsed < tween.duration * 1000) { // check if falling behind + tweak = Math.round((elapsed / expected - 1) * tween.currentFrame); + } else { // went over duration, so jump to end + tweak = frames - (frame + 1); + } + if (tweak > 0 && isFinite(tweak)) { // adjust if needed + if (tween.currentFrame + tweak >= frames) {// dont go past last frame + tweak = frames - (frame + 1); + } + + tween.currentFrame += tweak; + } + }; +}; +/** + * Used to calculate Bezier splines for any number of control points. + * @class Bezier + * @namespace YAHOO.util + * + */ +YAHOO.util.Bezier = new function() +{ + /** + * Get the current position of the animated element based on t. + * Each point is an array of "x" and "y" values (0 = x, 1 = y) + * At least 2 points are required (start and end). + * First point is start. Last point is end. + * Additional control points are optional. + * @method getPosition + * @param {Array} points An array containing Bezier points + * @param {Number} t A number between 0 and 1 which is the basis for determining current position + * @return {Array} An array containing int x and y member data + */ + this.getPosition = function(points, t) + { + var n = points.length; + var tmp = []; + + for (var i = 0; i < n; ++i){ + tmp[i] = [points[i][0], points[i][1]]; // save input + } + + for (var j = 1; j < n; ++j) { + for (i = 0; i < n - j; ++i) { + tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0]; + tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1]; + } + } + + return [ tmp[0][0], tmp[0][1] ]; + + }; +}; +/** + * Anim subclass for color transitions. + *Usage: var myAnim = new Y.ColorAnim(el, { backgroundColor: { from: '#FF0000', to: '#FFFFFF' } }, 1, Y.Easing.easeOut);
Color values can be specified with either 112233, #112233,
+ * [255,255,255], or rgb(255,255,255)
Usage: var myAnim = new YAHOO.util.Motion(el, { points: { to: [800, 800] } }, 1, YAHOO.util.Easing.easeOut);
Usage: var myAnim = new YAHOO.util.Scroll(el, { scroll: { to: [0, 800] } }, 1, YAHOO.util.Easing.easeOut);
To construct the placeholder for the calendar widget, the code is as +* follows: +*
To construct the placeholder for the calendar widget, the code is as +* follows: +*
+ * dd = new YAHOO.util.DragDrop("div1", "group1"); + *+ * Since none of the event handlers have been implemented, nothing would + * actually happen if you were to run the code above. Normally you would + * override this class or one of the default implementations, but you can + * also override the methods you want on an instance of the class... + *
+ * dd.onDragDrop = function(e, id) { + * alert("dd was dropped on " + id); + * } + *+ * @namespace YAHOO.util + * @class DragDrop + * @constructor + * @param {String} id of the element that is linked to this instance + * @param {String} sGroup the group of related DragDrop objects + * @param {object} config an object containing configurable attributes + * Valid properties for DragDrop: + * padding, isTarget, maintainOffset, primaryButtonOnly + */ +YAHOO.util.DragDrop = function(id, sGroup, config) { + if (id) { + this.init(id, sGroup, config); + } +}; + +YAHOO.util.DragDrop.prototype = { + + /** + * The id of the element associated with this object. This is what we + * refer to as the "linked element" because the size and position of + * this element is used to determine when the drag and drop objects have + * interacted. + * @property id + * @type String + */ + id: null, + + /** + * Configuration attributes passed into the constructor + * @property config + * @type object + */ + config: null, + + /** + * The id of the element that will be dragged. By default this is same + * as the linked element , but could be changed to another element. Ex: + * YAHOO.util.DDProxy + * @property dragElId + * @type String + * @private + */ + dragElId: null, + + /** + * the id of the element that initiates the drag operation. By default + * this is the linked element, but could be changed to be a child of this + * element. This lets us do things like only starting the drag when the + * header element within the linked html element is clicked. + * @property handleElId + * @type String + * @private + */ + handleElId: null, + + /** + * An associative array of HTML tags that will be ignored if clicked. + * @property invalidHandleTypes + * @type {string: string} + */ + invalidHandleTypes: null, + + /** + * An associative array of ids for elements that will be ignored if clicked + * @property invalidHandleIds + * @type {string: string} + */ + invalidHandleIds: null, + + /** + * An indexted array of css class names for elements that will be ignored + * if clicked. + * @property invalidHandleClasses + * @type string[] + */ + invalidHandleClasses: null, + + /** + * The linked element's absolute X position at the time the drag was + * started + * @property startPageX + * @type int + * @private + */ + startPageX: 0, + + /** + * The linked element's absolute X position at the time the drag was + * started + * @property startPageY + * @type int + * @private + */ + startPageY: 0, + + /** + * The group defines a logical collection of DragDrop objects that are + * related. Instances only get events when interacting with other + * DragDrop object in the same group. This lets us define multiple + * groups using a single DragDrop subclass if we want. + * @property groups + * @type {string: string} + */ + groups: null, + + /** + * Individual drag/drop instances can be locked. This will prevent + * onmousedown start drag. + * @property locked + * @type boolean + * @private + */ + locked: false, + + /** + * Lock this instance + * @method lock + */ + lock: function() { this.locked = true; }, + + /** + * Unlock this instace + * @method unlock + */ + unlock: function() { this.locked = false; }, + + /** + * By default, all insances can be a drop target. This can be disabled by + * setting isTarget to false. + * @method isTarget + * @type boolean + */ + isTarget: true, + + /** + * The padding configured for this drag and drop object for calculating + * the drop zone intersection with this object. + * @method padding + * @type int[] + */ + padding: null, + + /** + * Cached reference to the linked element + * @property _domRef + * @private + */ + _domRef: null, + + /** + * Internal typeof flag + * @property __ygDragDrop + * @private + */ + __ygDragDrop: true, + + /** + * Set to true when horizontal contraints are applied + * @property constrainX + * @type boolean + * @private + */ + constrainX: false, + + /** + * Set to true when vertical contraints are applied + * @property constrainY + * @type boolean + * @private + */ + constrainY: false, + + /** + * The left constraint + * @property minX + * @type int + * @private + */ + minX: 0, + + /** + * The right constraint + * @property maxX + * @type int + * @private + */ + maxX: 0, + + /** + * The up constraint + * @property minY + * @type int + * @type int + * @private + */ + minY: 0, + + /** + * The down constraint + * @property maxY + * @type int + * @private + */ + maxY: 0, + + /** + * Maintain offsets when we resetconstraints. Set to true when you want + * the position of the element relative to its parent to stay the same + * when the page changes + * + * @property maintainOffset + * @type boolean + */ + maintainOffset: false, + + /** + * Array of pixel locations the element will snap to if we specified a + * horizontal graduation/interval. This array is generated automatically + * when you define a tick interval. + * @property xTicks + * @type int[] + */ + xTicks: null, + + /** + * Array of pixel locations the element will snap to if we specified a + * vertical graduation/interval. This array is generated automatically + * when you define a tick interval. + * @property yTicks + * @type int[] + */ + yTicks: null, + + /** + * By default the drag and drop instance will only respond to the primary + * button click (left button for a right-handed mouse). Set to true to + * allow drag and drop to start with any mouse click that is propogated + * by the browser + * @property primaryButtonOnly + * @type boolean + */ + primaryButtonOnly: true, + + /** + * The availabe property is false until the linked dom element is accessible. + * @property available + * @type boolean + */ + available: false, + + /** + * By default, drags can only be initiated if the mousedown occurs in the + * region the linked element is. This is done in part to work around a + * bug in some browsers that mis-report the mousedown if the previous + * mouseup happened outside of the window. This property is set to true + * if outer handles are defined. + * + * @property hasOuterHandles + * @type boolean + * @default false + */ + hasOuterHandles: false, + + /** + * Code that executes immediately before the startDrag event + * @method b4StartDrag + * @private + */ + b4StartDrag: function(x, y) { }, + + /** + * Abstract method called after a drag/drop object is clicked + * and the drag or mousedown time thresholds have beeen met. + * @method startDrag + * @param {int} X click location + * @param {int} Y click location + */ + startDrag: function(x, y) { /* override this */ }, + + /** + * Code that executes immediately before the onDrag event + * @method b4Drag + * @private + */ + b4Drag: function(e) { }, + + /** + * Abstract method called during the onMouseMove event while dragging an + * object. + * @method onDrag + * @param {Event} e the mousemove event + */ + onDrag: function(e) { /* override this */ }, + + /** + * Abstract method called when this element fist begins hovering over + * another DragDrop obj + * @method onDragEnter + * @param {Event} e the mousemove event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this is hovering over. In INTERSECT mode, an array of one or more + * dragdrop items being hovered over. + */ + onDragEnter: function(e, id) { /* override this */ }, + + /** + * Code that executes immediately before the onDragOver event + * @method b4DragOver + * @private + */ + b4DragOver: function(e) { }, + + /** + * Abstract method called when this element is hovering over another + * DragDrop obj + * @method onDragOver + * @param {Event} e the mousemove event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this is hovering over. In INTERSECT mode, an array of dd items + * being hovered over. + */ + onDragOver: function(e, id) { /* override this */ }, + + /** + * Code that executes immediately before the onDragOut event + * @method b4DragOut + * @private + */ + b4DragOut: function(e) { }, + + /** + * Abstract method called when we are no longer hovering over an element + * @method onDragOut + * @param {Event} e the mousemove event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this was hovering over. In INTERSECT mode, an array of dd items + * that the mouse is no longer over. + */ + onDragOut: function(e, id) { /* override this */ }, + + /** + * Code that executes immediately before the onDragDrop event + * @method b4DragDrop + * @private + */ + b4DragDrop: function(e) { }, + + /** + * Abstract method called when this item is dropped on another DragDrop + * obj + * @method onDragDrop + * @param {Event} e the mouseup event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this was dropped on. In INTERSECT mode, an array of dd items this + * was dropped on. + */ + onDragDrop: function(e, id) { /* override this */ }, + + /** + * Abstract method called when this item is dropped on an area with no + * drop target + * @method onInvalidDrop + * @param {Event} e the mouseup event + */ + onInvalidDrop: function(e) { /* override this */ }, + + /** + * Code that executes immediately before the endDrag event + * @method b4EndDrag + * @private + */ + b4EndDrag: function(e) { }, + + /** + * Fired when we are done dragging the object + * @method endDrag + * @param {Event} e the mouseup event + */ + endDrag: function(e) { /* override this */ }, + + /** + * Code executed immediately before the onMouseDown event + * @method b4MouseDown + * @param {Event} e the mousedown event + * @private + */ + b4MouseDown: function(e) { }, + + /** + * Event handler that fires when a drag/drop obj gets a mousedown + * @method onMouseDown + * @param {Event} e the mousedown event + */ + onMouseDown: function(e) { /* override this */ }, + + /** + * Event handler that fires when a drag/drop obj gets a mouseup + * @method onMouseUp + * @param {Event} e the mouseup event + */ + onMouseUp: function(e) { /* override this */ }, + + /** + * Override the onAvailable method to do what is needed after the initial + * position was determined. + * @method onAvailable + */ + onAvailable: function () { + this.logger.log("onAvailable (base)"); + }, + + /** + * Returns a reference to the linked element + * @method getEl + * @return {HTMLElement} the html element + */ + getEl: function() { + if (!this._domRef) { + this._domRef = Dom.get(this.id); + } + + return this._domRef; + }, + + /** + * Returns a reference to the actual element to drag. By default this is + * the same as the html element, but it can be assigned to another + * element. An example of this can be found in YAHOO.util.DDProxy + * @method getDragEl + * @return {HTMLElement} the html element + */ + getDragEl: function() { + return Dom.get(this.dragElId); + }, + + /** + * Sets up the DragDrop object. Must be called in the constructor of any + * YAHOO.util.DragDrop subclass + * @method init + * @param id the id of the linked element + * @param {String} sGroup the group of related items + * @param {object} config configuration attributes + */ + init: function(id, sGroup, config) { + this.initTarget(id, sGroup, config); + Event.on(this.id, "mousedown", this.handleMouseDown, this, true); + // Event.on(this.id, "selectstart", Event.preventDefault); + }, + + /** + * Initializes Targeting functionality only... the object does not + * get a mousedown handler. + * @method initTarget + * @param id the id of the linked element + * @param {String} sGroup the group of related items + * @param {object} config configuration attributes + */ + initTarget: function(id, sGroup, config) { + + // configuration attributes + this.config = config || {}; + + // create a local reference to the drag and drop manager + this.DDM = YAHOO.util.DDM; + // initialize the groups array + this.groups = {}; + + // assume that we have an element reference instead of an id if the + // parameter is not a string + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + + // set the id + this.id = id; + + // add to an interaction group + this.addToGroup((sGroup) ? sGroup : "default"); + + // We don't want to register this as the handle with the manager + // so we just set the id rather than calling the setter. + this.handleElId = id; + + Event.onAvailable(id, this.handleOnAvailable, this, true); + + // create a logger instance + this.logger = (YAHOO.widget.LogWriter) ? + new YAHOO.widget.LogWriter(this.toString()) : YAHOO; + + // the linked element is the element that gets dragged by default + this.setDragElId(id); + + // by default, clicked anchors will not start drag operations. + // @TODO what else should be here? Probably form fields. + this.invalidHandleTypes = { A: "A" }; + this.invalidHandleIds = {}; + this.invalidHandleClasses = []; + + this.applyConfig(); + }, + + /** + * Applies the configuration parameters that were passed into the constructor. + * This is supposed to happen at each level through the inheritance chain. So + * a DDProxy implentation will execute apply config on DDProxy, DD, and + * DragDrop in order to get all of the parameters that are available in + * each object. + * @method applyConfig + */ + applyConfig: function() { + + // configurable properties: + // padding, isTarget, maintainOffset, primaryButtonOnly + this.padding = this.config.padding || [0, 0, 0, 0]; + this.isTarget = (this.config.isTarget !== false); + this.maintainOffset = (this.config.maintainOffset); + this.primaryButtonOnly = (this.config.primaryButtonOnly !== false); + + }, + + /** + * Executed when the linked element is available + * @method handleOnAvailable + * @private + */ + handleOnAvailable: function() { + this.logger.log("handleOnAvailable"); + this.available = true; + this.resetConstraints(); + this.onAvailable(); + }, + + /** + * Configures the padding for the target zone in px. Effectively expands + * (or reduces) the virtual object size for targeting calculations. + * Supports css-style shorthand; if only one parameter is passed, all sides + * will have that padding, and if only two are passed, the top and bottom + * will have the first param, the left and right the second. + * @method setPadding + * @param {int} iTop Top pad + * @param {int} iRight Right pad + * @param {int} iBot Bot pad + * @param {int} iLeft Left pad + */ + setPadding: function(iTop, iRight, iBot, iLeft) { + // this.padding = [iLeft, iRight, iTop, iBot]; + if (!iRight && 0 !== iRight) { + this.padding = [iTop, iTop, iTop, iTop]; + } else if (!iBot && 0 !== iBot) { + this.padding = [iTop, iRight, iTop, iRight]; + } else { + this.padding = [iTop, iRight, iBot, iLeft]; + } + }, + + /** + * Stores the initial placement of the linked element. + * @method setInitialPosition + * @param {int} diffX the X offset, default 0 + * @param {int} diffY the Y offset, default 0 + */ + setInitPosition: function(diffX, diffY) { + var el = this.getEl(); + + if (!this.DDM.verifyEl(el)) { + this.logger.log(this.id + " element is broken"); + return; + } + + var dx = diffX || 0; + var dy = diffY || 0; + + var p = Dom.getXY( el ); + + this.initPageX = p[0] - dx; + this.initPageY = p[1] - dy; + + this.lastPageX = p[0]; + this.lastPageY = p[1]; + + this.logger.log(this.id + " inital position: " + this.initPageX + + ", " + this.initPageY); + + + this.setStartPosition(p); + }, + + /** + * Sets the start position of the element. This is set when the obj + * is initialized, the reset when a drag is started. + * @method setStartPosition + * @param pos current position (from previous lookup) + * @private + */ + setStartPosition: function(pos) { + var p = pos || Dom.getXY( this.getEl() ); + this.deltaSetXY = null; + + this.startPageX = p[0]; + this.startPageY = p[1]; + }, + + /** + * Add this instance to a group of related drag/drop objects. All + * instances belong to at least one group, and can belong to as many + * groups as needed. + * @method addToGroup + * @param sGroup {string} the name of the group + */ + addToGroup: function(sGroup) { + this.groups[sGroup] = true; + this.DDM.regDragDrop(this, sGroup); + }, + + /** + * Remove's this instance from the supplied interaction group + * @method removeFromGroup + * @param {string} sGroup The group to drop + */ + removeFromGroup: function(sGroup) { + this.logger.log("Removing from group: " + sGroup); + if (this.groups[sGroup]) { + delete this.groups[sGroup]; + } + + this.DDM.removeDDFromGroup(this, sGroup); + }, + + /** + * Allows you to specify that an element other than the linked element + * will be moved with the cursor during a drag + * @method setDragElId + * @param id {string} the id of the element that will be used to initiate the drag + */ + setDragElId: function(id) { + this.dragElId = id; + }, + + /** + * Allows you to specify a child of the linked element that should be + * used to initiate the drag operation. An example of this would be if + * you have a content div with text and links. Clicking anywhere in the + * content area would normally start the drag operation. Use this method + * to specify that an element inside of the content div is the element + * that starts the drag operation. + * @method setHandleElId + * @param id {string} the id of the element that will be used to + * initiate the drag. + */ + setHandleElId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + this.handleElId = id; + this.DDM.regHandle(this.id, id); + }, + + /** + * Allows you to set an element outside of the linked element as a drag + * handle + * @method setOuterHandleElId + * @param id the id of the element that will be used to initiate the drag + */ + setOuterHandleElId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + this.logger.log("Adding outer handle event: " + id); + Event.on(id, "mousedown", + this.handleMouseDown, this, true); + this.setHandleElId(id); + + this.hasOuterHandles = true; + }, + + /** + * Remove all drag and drop hooks for this element + * @method unreg + */ + unreg: function() { + this.logger.log("DragDrop obj cleanup " + this.id); + Event.removeListener(this.id, "mousedown", + this.handleMouseDown); + this._domRef = null; + this.DDM._remove(this); + }, + + /** + * Returns true if this instance is locked, or the drag drop mgr is locked + * (meaning that all drag/drop is disabled on the page.) + * @method isLocked + * @return {boolean} true if this obj or all drag/drop is locked, else + * false + */ + isLocked: function() { + return (this.DDM.isLocked() || this.locked); + }, + + /** + * Fired when this object is clicked + * @method handleMouseDown + * @param {Event} e + * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj) + * @private + */ + handleMouseDown: function(e, oDD) { + + var button = e.which || e.button; + this.logger.log("button: " + button); + + if (this.primaryButtonOnly && button > 1) { + this.logger.log("Mousedown was not produced by the primary button"); + return; + } + + if (this.isLocked()) { + this.logger.log("Drag and drop is disabled, aborting"); + return; + } + + this.logger.log("mousedown " + this.id); + this.DDM.refreshCache(this.groups); + // var self = this; + // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0); + + // Only process the event if we really clicked within the linked + // element. The reason we make this check is that in the case that + // another element was moved between the clicked element and the + // cursor in the time between the mousedown and mouseup events. When + // this happens, the element gets the next mousedown event + // regardless of where on the screen it happened. + var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e)); + if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) { + this.logger.log("Click was not over the element: " + this.id); + } else { + if (this.clickValidator(e)) { + + this.logger.log("click was a valid handle"); + + // set the initial element position + this.setStartPosition(); + + this.logger.log("firing onMouseDown events"); + + this.b4MouseDown(e); + this.onMouseDown(e); + this.DDM.handleMouseDown(e, this); + + this.DDM.stopEvent(e); + } else { + +this.logger.log("clickValidator returned false, drag not initiated"); + + } + } + }, + + clickValidator: function(e) { + var target = Event.getTarget(e); + return ( this.isValidHandleChild(target) && + (this.id == this.handleElId || + this.DDM.handleWasClicked(target, this.id)) ); + }, + + /** + * Allows you to specify a tag name that should not start a drag operation + * when clicked. This is designed to facilitate embedding links within a + * drag handle that do something other than start the drag. + * @method addInvalidHandleType + * @param {string} tagName the type of element to exclude + */ + addInvalidHandleType: function(tagName) { + var type = tagName.toUpperCase(); + this.invalidHandleTypes[type] = type; + }, + + /** + * Lets you to specify an element id for a child of a drag handle + * that should not initiate a drag + * @method addInvalidHandleId + * @param {string} id the element id of the element you wish to ignore + */ + addInvalidHandleId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + this.invalidHandleIds[id] = id; + }, + + + /** + * Lets you specify a css class of elements that will not initiate a drag + * @method addInvalidHandleClass + * @param {string} cssClass the class of the elements you wish to ignore + */ + addInvalidHandleClass: function(cssClass) { + this.invalidHandleClasses.push(cssClass); + }, + + /** + * Unsets an excluded tag name set by addInvalidHandleType + * @method removeInvalidHandleType + * @param {string} tagName the type of element to unexclude + */ + removeInvalidHandleType: function(tagName) { + var type = tagName.toUpperCase(); + // this.invalidHandleTypes[type] = null; + delete this.invalidHandleTypes[type]; + }, + + /** + * Unsets an invalid handle id + * @method removeInvalidHandleId + * @param {string} id the id of the element to re-enable + */ + removeInvalidHandleId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + delete this.invalidHandleIds[id]; + }, + + /** + * Unsets an invalid css class + * @method removeInvalidHandleClass + * @param {string} cssClass the class of the element(s) you wish to + * re-enable + */ + removeInvalidHandleClass: function(cssClass) { + for (var i=0, len=this.invalidHandleClasses.length; i
+ * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
+ *
+ * @TODO this really should be an indexed array. Alternatively this
+ * method could accept both.
+ * @method refreshCache
+ * @param {Object} groups an associative array of groups to refresh
+ * @static
+ */
+ refreshCache: function(groups) {
+ this.logger.log("refreshing element location cache");
+ for (var sGroup in groups) {
+ if ("string" != typeof sGroup) {
+ continue;
+ }
+ for (var i in this.ids[sGroup]) {
+ var oDD = this.ids[sGroup][i];
+
+ if (this.isTypeOfDD(oDD)) {
+ // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
+ var loc = this.getLocation(oDD);
+ if (loc) {
+ this.locationCache[oDD.id] = loc;
+ } else {
+ delete this.locationCache[oDD.id];
+ this.logger.log("Could not get the loc for " + oDD.id,
+ "warn");
+ // this will unregister the drag and drop object if
+ // the element is not in a usable state
+ // oDD.unreg();
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * This checks to make sure an element exists and is in the DOM. The
+ * main purpose is to handle cases where innerHTML is used to remove
+ * drag and drop objects from the DOM. IE provides an 'unspecified
+ * error' when trying to access the offsetParent of such an element
+ * @method verifyEl
+ * @param {HTMLElement} el the element to check
+ * @return {boolean} true if the element looks usable
+ * @static
+ */
+ verifyEl: function(el) {
+ try {
+ if (el) {
+ var parent = el.offsetParent;
+ if (parent) {
+ return true;
+ }
+ }
+ } catch(e) {
+ this.logger.log("detected problem with an element");
+ }
+
+ return false;
+ },
+
+ /**
+ * Returns a Region object containing the drag and drop element's position
+ * and size, including the padding configured for it
+ * @method getLocation
+ * @param {DragDrop} oDD the drag and drop object to get the
+ * location for
+ * @return {YAHOO.util.Region} a Region object representing the total area
+ * the element occupies, including any padding
+ * the instance is configured for.
+ * @static
+ */
+ getLocation: function(oDD) {
+ if (! this.isTypeOfDD(oDD)) {
+ this.logger.log(oDD + " is not a DD obj");
+ return null;
+ }
+
+ var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
+
+ try {
+ pos= YAHOO.util.Dom.getXY(el);
+ } catch (e) { }
+
+ if (!pos) {
+ this.logger.log("getXY failed");
+ return null;
+ }
+
+ x1 = pos[0];
+ x2 = x1 + el.offsetWidth;
+ y1 = pos[1];
+ y2 = y1 + el.offsetHeight;
+
+ t = y1 - oDD.padding[0];
+ r = x2 + oDD.padding[1];
+ b = y2 + oDD.padding[2];
+ l = x1 - oDD.padding[3];
+
+ return new YAHOO.util.Region( t, r, b, l );
+ },
+
+ /**
+ * Checks the cursor location to see if it over the target
+ * @method isOverTarget
+ * @param {YAHOO.util.Point} pt The point to evaluate
+ * @param {DragDrop} oTarget the DragDrop object we are inspecting
+ * @return {boolean} true if the mouse is over the target
+ * @private
+ * @static
+ */
+ isOverTarget: function(pt, oTarget, intersect) {
+ // use cache if available
+ var loc = this.locationCache[oTarget.id];
+ if (!loc || !this.useCache) {
+ this.logger.log("cache not populated");
+ loc = this.getLocation(oTarget);
+ this.locationCache[oTarget.id] = loc;
+
+ this.logger.log("cache: " + loc);
+ }
+
+ if (!loc) {
+ this.logger.log("could not get the location of the element");
+ return false;
+ }
+
+ //this.logger.log("loc: " + loc + ", pt: " + pt);
+ oTarget.cursorIsOver = loc.contains( pt );
+
+ // DragDrop is using this as a sanity check for the initial mousedown
+ // in this case we are done. In POINT mode, if the drag obj has no
+ // contraints, we are also done. Otherwise we need to evaluate the
+ // location of the target as related to the actual location of the
+ // dragged element.
+ var dc = this.dragCurrent;
+ if (!dc || !dc.getTargetCoord ||
+ (!intersect && !dc.constrainX && !dc.constrainY)) {
+ return oTarget.cursorIsOver;
+ }
+
+ oTarget.overlap = null;
+
+ // Get the current location of the drag element, this is the
+ // location of the mouse event less the delta that represents
+ // where the original mousedown happened on the element. We
+ // need to consider constraints and ticks as well.
+ var pos = dc.getTargetCoord(pt.x, pt.y);
+
+ var el = dc.getDragEl();
+ var curRegion = new YAHOO.util.Region( pos.y,
+ pos.x + el.offsetWidth,
+ pos.y + el.offsetHeight,
+ pos.x );
+
+ var overlap = curRegion.intersect(loc);
+
+ if (overlap) {
+ oTarget.overlap = overlap;
+ return (intersect) ? true : oTarget.cursorIsOver;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * unload event handler
+ * @method _onUnload
+ * @private
+ * @static
+ */
+ _onUnload: function(e, me) {
+ this.unregAll();
+ },
+
+ /**
+ * Cleans up the drag and drop events and objects.
+ * @method unregAll
+ * @private
+ * @static
+ */
+ unregAll: function() {
+ this.logger.log("unregister all");
+
+ if (this.dragCurrent) {
+ this.stopDrag();
+ this.dragCurrent = null;
+ }
+
+ this._execOnAll("unreg", []);
+
+ for (i in this.elementCache) {
+ delete this.elementCache[i];
+ }
+
+ this.elementCache = {};
+ this.ids = {};
+ },
+
+ /**
+ * A cache of DOM elements
+ * @property elementCache
+ * @private
+ * @static
+ */
+ elementCache: {},
+
+ /**
+ * Get the wrapper for the DOM element specified
+ * @method getElWrapper
+ * @param {String} id the id of the element to get
+ * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
+ * @private
+ * @deprecated This wrapper isn't that useful
+ * @static
+ */
+ getElWrapper: function(id) {
+ var oWrapper = this.elementCache[id];
+ if (!oWrapper || !oWrapper.el) {
+ oWrapper = this.elementCache[id] =
+ new this.ElementWrapper(YAHOO.util.Dom.get(id));
+ }
+ return oWrapper;
+ },
+
+ /**
+ * Returns the actual DOM element
+ * @method getElement
+ * @param {String} id the id of the elment to get
+ * @return {Object} The element
+ * @deprecated use YAHOO.util.Dom.get instead
+ * @static
+ */
+ getElement: function(id) {
+ return YAHOO.util.Dom.get(id);
+ },
+
+ /**
+ * Returns the style property for the DOM element (i.e.,
+ * document.getElById(id).style)
+ * @method getCss
+ * @param {String} id the id of the elment to get
+ * @return {Object} The style property of the element
+ * @deprecated use YAHOO.util.Dom instead
+ * @static
+ */
+ getCss: function(id) {
+ var el = YAHOO.util.Dom.get(id);
+ return (el) ? el.style : null;
+ },
+
+ /**
+ * Inner class for cached elements
+ * @class DragDropMgr.ElementWrapper
+ * @for DragDropMgr
+ * @private
+ * @deprecated
+ */
+ ElementWrapper: function(el) {
+ /**
+ * The element
+ * @property el
+ */
+ this.el = el || null;
+ /**
+ * The element id
+ * @property id
+ */
+ this.id = this.el && el.id;
+ /**
+ * A reference to the style property
+ * @property css
+ */
+ this.css = this.el && el.style;
+ },
+
+ /**
+ * Returns the X position of an html element
+ * @method getPosX
+ * @param el the element for which to get the position
+ * @return {int} the X coordinate
+ * @for DragDropMgr
+ * @deprecated use YAHOO.util.Dom.getX instead
+ * @static
+ */
+ getPosX: function(el) {
+ return YAHOO.util.Dom.getX(el);
+ },
+
+ /**
+ * Returns the Y position of an html element
+ * @method getPosY
+ * @param el the element for which to get the position
+ * @return {int} the Y coordinate
+ * @deprecated use YAHOO.util.Dom.getY instead
+ * @static
+ */
+ getPosY: function(el) {
+ return YAHOO.util.Dom.getY(el);
+ },
+
+ /**
+ * Swap two nodes. In IE, we use the native method, for others we
+ * emulate the IE behavior
+ * @method swapNode
+ * @param n1 the first node to swap
+ * @param n2 the other node to swap
+ * @static
+ */
+ swapNode: function(n1, n2) {
+ if (n1.swapNode) {
+ n1.swapNode(n2);
+ } else {
+ var p = n2.parentNode;
+ var s = n2.nextSibling;
+
+ if (s == n1) {
+ p.insertBefore(n1, n2);
+ } else if (n2 == n1.nextSibling) {
+ p.insertBefore(n2, n1);
+ } else {
+ n1.parentNode.replaceChild(n2, n1);
+ p.insertBefore(n1, s);
+ }
+ }
+ },
+
+ /**
+ * Returns the current scroll position
+ * @method getScroll
+ * @private
+ * @static
+ */
+ getScroll: function () {
+ var t, l, dde=document.documentElement, db=document.body;
+ if (dde && (dde.scrollTop || dde.scrollLeft)) {
+ t = dde.scrollTop;
+ l = dde.scrollLeft;
+ } else if (db) {
+ t = db.scrollTop;
+ l = db.scrollLeft;
+ } else {
+ YAHOO.log("could not get scroll property");
+ }
+ return { top: t, left: l };
+ },
+
+ /**
+ * Returns the specified element style property
+ * @method getStyle
+ * @param {HTMLElement} el the element
+ * @param {string} styleProp the style property
+ * @return {string} The value of the style property
+ * @deprecated use YAHOO.util.Dom.getStyle
+ * @static
+ */
+ getStyle: function(el, styleProp) {
+ return YAHOO.util.Dom.getStyle(el, styleProp);
+ },
+
+ /**
+ * Gets the scrollTop
+ * @method getScrollTop
+ * @return {int} the document's scrollTop
+ * @static
+ */
+ getScrollTop: function () { return this.getScroll().top; },
+
+ /**
+ * Gets the scrollLeft
+ * @method getScrollLeft
+ * @return {int} the document's scrollTop
+ * @static
+ */
+ getScrollLeft: function () { return this.getScroll().left; },
+
+ /**
+ * Sets the x/y position of an element to the location of the
+ * target element.
+ * @method moveToEl
+ * @param {HTMLElement} moveEl The element to move
+ * @param {HTMLElement} targetEl The position reference element
+ * @static
+ */
+ moveToEl: function (moveEl, targetEl) {
+ var aCoord = YAHOO.util.Dom.getXY(targetEl);
+ this.logger.log("moveToEl: " + aCoord);
+ YAHOO.util.Dom.setXY(moveEl, aCoord);
+ },
+
+ /**
+ * Gets the client height
+ * @method getClientHeight
+ * @return {int} client height in px
+ * @deprecated use YAHOO.util.Dom.getViewportHeight instead
+ * @static
+ */
+ getClientHeight: function() {
+ return YAHOO.util.Dom.getViewportHeight();
+ },
+
+ /**
+ * Gets the client width
+ * @method getClientWidth
+ * @return {int} client width in px
+ * @deprecated use YAHOO.util.Dom.getViewportWidth instead
+ * @static
+ */
+ getClientWidth: function() {
+ return YAHOO.util.Dom.getViewportWidth();
+ },
+
+ /**
+ * Numeric array sort function
+ * @method numericSort
+ * @static
+ */
+ numericSort: function(a, b) { return (a - b); },
+
+ /**
+ * Internal counter
+ * @property _timeoutCount
+ * @private
+ * @static
+ */
+ _timeoutCount: 0,
+
+ /**
+ * Trying to make the load order less important. Without this we get
+ * an error if this file is loaded before the Event Utility.
+ * @method _addListeners
+ * @private
+ * @static
+ */
+ _addListeners: function() {
+ var DDM = YAHOO.util.DDM;
+ if ( YAHOO.util.Event && document ) {
+ DDM._onLoad();
+ } else {
+ if (DDM._timeoutCount > 2000) {
+ DDM.logger.log("DragDrop requires the Event Utility");
+ } else {
+ setTimeout(DDM._addListeners, 10);
+ if (document && document.body) {
+ DDM._timeoutCount += 1;
+ }
+ }
+ }
+ },
+
+ /**
+ * Recursively searches the immediate parent and all child nodes for
+ * the handle element in order to determine wheter or not it was
+ * clicked.
+ * @method handleWasClicked
+ * @param node the html element to inspect
+ * @static
+ */
+ handleWasClicked: function(node, id) {
+ if (this.isHandle(id, node.id)) {
+ this.logger.log("clicked node is a handle");
+ return true;
+ } else {
+ // check to see if this is a text node child of the one we want
+ var p = node.parentNode;
+ // this.logger.log("p: " + p);
+
+ while (p) {
+ if (this.isHandle(id, p.id)) {
+ return true;
+ } else {
+ this.logger.log(p.id + " is not a handle");
+ p = p.parentNode;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ };
+
+}();
+
+// shorter alias, save a few bytes
+YAHOO.util.DDM = YAHOO.util.DragDropMgr;
+YAHOO.util.DDM._addListeners();
+
+}
+
+/**
+ * A DragDrop implementation where the linked element follows the
+ * mouse cursor during a drag.
+ * @class DD
+ * @extends YAHOO.util.DragDrop
+ * @constructor
+ * @param {String} id the id of the linked element
+ * @param {String} sGroup the group of related DragDrop items
+ * @param {object} config an object containing configurable attributes
+ * Valid properties for DD:
+ * scroll
+ */
+YAHOO.util.DD = function(id, sGroup, config) {
+ if (id) {
+ this.init(id, sGroup, config);
+ }
+};
+
+YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
+
+ /**
+ * When set to true, the utility automatically tries to scroll the browser
+ * window wehn a drag and drop element is dragged near the viewport boundary.
+ * Defaults to true.
+ * @property scroll
+ * @type boolean
+ */
+ scroll: true,
+
+ /**
+ * Sets the pointer offset to the distance between the linked element's top
+ * left corner and the location the element was clicked
+ * @method autoOffset
+ * @param {int} iPageX the X coordinate of the click
+ * @param {int} iPageY the Y coordinate of the click
+ */
+ autoOffset: function(iPageX, iPageY) {
+ var x = iPageX - this.startPageX;
+ var y = iPageY - this.startPageY;
+ this.setDelta(x, y);
+ // this.logger.log("autoOffset el pos: " + aCoord + ", delta: " + x + "," + y);
+ },
+
+ /**
+ * Sets the pointer offset. You can call this directly to force the
+ * offset to be in a particular location (e.g., pass in 0,0 to set it
+ * to the center of the object, as done in YAHOO.widget.Slider)
+ * @method setDelta
+ * @param {int} iDeltaX the distance from the left
+ * @param {int} iDeltaY the distance from the top
+ */
+ setDelta: function(iDeltaX, iDeltaY) {
+ this.deltaX = iDeltaX;
+ this.deltaY = iDeltaY;
+ this.logger.log("deltaX:" + this.deltaX + ", deltaY:" + this.deltaY);
+ },
+
+ /**
+ * Sets the drag element to the location of the mousedown or click event,
+ * maintaining the cursor location relative to the location on the element
+ * that was clicked. Override this if you want to place the element in a
+ * location other than where the cursor is.
+ * @method setDragElPos
+ * @param {int} iPageX the X coordinate of the mousedown or drag event
+ * @param {int} iPageY the Y coordinate of the mousedown or drag event
+ */
+ setDragElPos: function(iPageX, iPageY) {
+ // the first time we do this, we are going to check to make sure
+ // the element has css positioning
+
+ var el = this.getDragEl();
+ this.alignElWithMouse(el, iPageX, iPageY);
+ },
+
+ /**
+ * Sets the element to the location of the mousedown or click event,
+ * maintaining the cursor location relative to the location on the element
+ * that was clicked. Override this if you want to place the element in a
+ * location other than where the cursor is.
+ * @method alignElWithMouse
+ * @param {HTMLElement} el the element to move
+ * @param {int} iPageX the X coordinate of the mousedown or drag event
+ * @param {int} iPageY the Y coordinate of the mousedown or drag event
+ */
+ alignElWithMouse: function(el, iPageX, iPageY) {
+ var oCoord = this.getTargetCoord(iPageX, iPageY);
+ // this.logger.log("****alignElWithMouse : " + el.id + ", " + aCoord + ", " + el.style.display);
+
+ if (!this.deltaSetXY) {
+ var aCoord = [oCoord.x, oCoord.y];
+ YAHOO.util.Dom.setXY(el, aCoord);
+ var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
+ var newTop = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
+
+ this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
+ } else {
+ YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
+ YAHOO.util.Dom.setStyle(el, "top", (oCoord.y + this.deltaSetXY[1]) + "px");
+ }
+
+ this.cachePosition(oCoord.x, oCoord.y);
+ this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
+ },
+
+ /**
+ * Saves the most recent position so that we can reset the constraints and
+ * tick marks on-demand. We need to know this so that we can calculate the
+ * number of pixels the element is offset from its original position.
+ * @method cachePosition
+ * @param iPageX the current x position (optional, this just makes it so we
+ * don't have to look it up again)
+ * @param iPageY the current y position (optional, this just makes it so we
+ * don't have to look it up again)
+ */
+ cachePosition: function(iPageX, iPageY) {
+ if (iPageX) {
+ this.lastPageX = iPageX;
+ this.lastPageY = iPageY;
+ } else {
+ var aCoord = YAHOO.util.Dom.getXY(this.getEl());
+ this.lastPageX = aCoord[0];
+ this.lastPageY = aCoord[1];
+ }
+ },
+
+ /**
+ * Auto-scroll the window if the dragged object has been moved beyond the
+ * visible window boundary.
+ * @method autoScroll
+ * @param {int} x the drag element's x position
+ * @param {int} y the drag element's y position
+ * @param {int} h the height of the drag element
+ * @param {int} w the width of the drag element
+ * @private
+ */
+ autoScroll: function(x, y, h, w) {
+
+ if (this.scroll) {
+ // The client height
+ var clientH = this.DDM.getClientHeight();
+
+ // The client width
+ var clientW = this.DDM.getClientWidth();
+
+ // The amt scrolled down
+ var st = this.DDM.getScrollTop();
+
+ // The amt scrolled right
+ var sl = this.DDM.getScrollLeft();
+
+ // Location of the bottom of the element
+ var bot = h + y;
+
+ // Location of the right of the element
+ var right = w + x;
+
+ // The distance from the cursor to the bottom of the visible area,
+ // adjusted so that we don't scroll if the cursor is beyond the
+ // element drag constraints
+ var toBot = (clientH + st - y - this.deltaY);
+
+ // The distance from the cursor to the right of the visible area
+ var toRight = (clientW + sl - x - this.deltaX);
+
+ // this.logger.log( " x: " + x + " y: " + y + " h: " + h +
+ // " clientH: " + clientH + " clientW: " + clientW +
+ // " st: " + st + " sl: " + sl + " bot: " + bot +
+ // " right: " + right + " toBot: " + toBot + " toRight: " + toRight);
+
+ // How close to the edge the cursor must be before we scroll
+ // var thresh = (document.all) ? 100 : 40;
+ var thresh = 40;
+
+ // How many pixels to scroll per autoscroll op. This helps to reduce
+ // clunky scrolling. IE is more sensitive about this ... it needs this
+ // value to be higher.
+ var scrAmt = (document.all) ? 80 : 30;
+
+ // Scroll down if we are near the bottom of the visible page and the
+ // obj extends below the crease
+ if ( bot > clientH && toBot < thresh ) {
+ window.scrollTo(sl, st + scrAmt);
+ }
+
+ // Scroll up if the window is scrolled down and the top of the object
+ // goes above the top border
+ if ( y < st && st > 0 && y - st < thresh ) {
+ window.scrollTo(sl, st - scrAmt);
+ }
+
+ // Scroll right if the obj is beyond the right border and the cursor is
+ // near the border.
+ if ( right > clientW && toRight < thresh ) {
+ window.scrollTo(sl + scrAmt, st);
+ }
+
+ // Scroll left if the window has been scrolled to the right and the obj
+ // extends past the left border
+ if ( x < sl && sl > 0 && x - sl < thresh ) {
+ window.scrollTo(sl - scrAmt, st);
+ }
+ }
+ },
+
+ /**
+ * Finds the location the element should be placed if we want to move
+ * it to where the mouse location less the click offset would place us.
+ * @method getTargetCoord
+ * @param {int} iPageX the X coordinate of the click
+ * @param {int} iPageY the Y coordinate of the click
+ * @return an object that contains the coordinates (Object.x and Object.y)
+ * @private
+ */
+ getTargetCoord: function(iPageX, iPageY) {
+
+ // this.logger.log("getTargetCoord: " + iPageX + ", " + iPageY);
+
+ var x = iPageX - this.deltaX;
+ var y = iPageY - this.deltaY;
+
+ if (this.constrainX) {
+ if (x < this.minX) { x = this.minX; }
+ if (x > this.maxX) { x = this.maxX; }
+ }
+
+ if (this.constrainY) {
+ if (y < this.minY) { y = this.minY; }
+ if (y > this.maxY) { y = this.maxY; }
+ }
+
+ x = this.getTick(x, this.xTicks);
+ y = this.getTick(y, this.yTicks);
+
+ // this.logger.log("getTargetCoord " +
+ // " iPageX: " + iPageX +
+ // " iPageY: " + iPageY +
+ // " x: " + x + ", y: " + y);
+
+ return {x:x, y:y};
+ },
+
+ /*
+ * Sets up config options specific to this class. Overrides
+ * YAHOO.util.DragDrop, but all versions of this method through the
+ * inheritance chain are called
+ */
+ applyConfig: function() {
+ YAHOO.util.DD.superclass.applyConfig.call(this);
+ this.scroll = (this.config.scroll !== false);
+ },
+
+ /*
+ * Event that fires prior to the onMouseDown event. Overrides
+ * YAHOO.util.DragDrop.
+ */
+ b4MouseDown: function(e) {
+ // this.resetConstraints();
+ this.autoOffset(YAHOO.util.Event.getPageX(e),
+ YAHOO.util.Event.getPageY(e));
+ },
+
+ /*
+ * Event that fires prior to the onDrag event. Overrides
+ * YAHOO.util.DragDrop.
+ */
+ b4Drag: function(e) {
+ this.setDragElPos(YAHOO.util.Event.getPageX(e),
+ YAHOO.util.Event.getPageY(e));
+ },
+
+ toString: function() {
+ return ("DD " + this.id);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Debugging ygDragDrop events that can be overridden
+ //////////////////////////////////////////////////////////////////////////
+ /*
+ startDrag: function(x, y) {
+ this.logger.log(this.id.toString() + " startDrag");
+ },
+
+ onDrag: function(e) {
+ this.logger.log(this.id.toString() + " onDrag");
+ },
+
+ onDragEnter: function(e, id) {
+ this.logger.log(this.id.toString() + " onDragEnter: " + id);
+ },
+
+ onDragOver: function(e, id) {
+ this.logger.log(this.id.toString() + " onDragOver: " + id);
+ },
+
+ onDragOut: function(e, id) {
+ this.logger.log(this.id.toString() + " onDragOut: " + id);
+ },
+
+ onDragDrop: function(e, id) {
+ this.logger.log(this.id.toString() + " onDragDrop: " + id);
+ },
+
+ endDrag: function(e) {
+ this.logger.log(this.id.toString() + " endDrag");
+ }
+
+ */
+
+});
+/**
+ * A DragDrop implementation that inserts an empty, bordered div into
+ * the document that follows the cursor during drag operations. At the time of
+ * the click, the frame div is resized to the dimensions of the linked html
+ * element, and moved to the exact location of the linked element.
+ *
+ * References to the "frame" element refer to the single proxy element that
+ * was created to be dragged in place of all DDProxy elements on the
+ * page.
+ *
+ * @class DDProxy
+ * @extends YAHOO.util.DD
+ * @constructor
+ * @param {String} id the id of the linked html element
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ * Valid properties for DDProxy in addition to those in DragDrop:
+ * resizeFrame, centerFrame, dragElId
+ */
+YAHOO.util.DDProxy = function(id, sGroup, config) {
+ if (id) {
+ this.init(id, sGroup, config);
+ this.initFrame();
+ }
+};
+
+/**
+ * The default drag frame div id
+ * @property YAHOO.util.DDProxy.dragElId
+ * @type String
+ * @static
+ */
+YAHOO.util.DDProxy.dragElId = "ygddfdiv";
+
+YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
+
+ /**
+ * By default we resize the drag frame to be the same size as the element
+ * we want to drag (this is to get the frame effect). We can turn it off
+ * if we want a different behavior.
+ * @property resizeFrame
+ * @type boolean
+ */
+ resizeFrame: true,
+
+ /**
+ * By default the frame is positioned exactly where the drag element is, so
+ * we use the cursor offset provided by YAHOO.util.DD. Another option that works only if
+ * you do not have constraints on the obj is to have the drag frame centered
+ * around the cursor. Set centerFrame to true for this effect.
+ * @property centerFrame
+ * @type boolean
+ */
+ centerFrame: false,
+
+ /**
+ * Creates the proxy element if it does not yet exist
+ * @method createFrame
+ */
+ createFrame: function() {
+ var self = this;
+ var body = document.body;
+
+ if (!body || !body.firstChild) {
+ setTimeout( function() { self.createFrame(); }, 50 );
+ return;
+ }
+
+ var div = this.getDragEl();
+
+ if (!div) {
+ div = document.createElement("div");
+ div.id = this.dragElId;
+ var s = div.style;
+
+ s.position = "absolute";
+ s.visibility = "hidden";
+ s.cursor = "move";
+ s.border = "2px solid #aaa";
+ s.zIndex = 999;
+
+ // appendChild can blow up IE if invoked prior to the window load event
+ // while rendering a table. It is possible there are other scenarios
+ // that would cause this to happen as well.
+ body.insertBefore(div, body.firstChild);
+ }
+ },
+
+ /**
+ * Initialization for the drag frame element. Must be called in the
+ * constructor of all subclasses
+ * @method initFrame
+ */
+ initFrame: function() {
+ this.createFrame();
+ },
+
+ applyConfig: function() {
+ this.logger.log("DDProxy applyConfig");
+ YAHOO.util.DDProxy.superclass.applyConfig.call(this);
+
+ this.resizeFrame = (this.config.resizeFrame !== false);
+ this.centerFrame = (this.config.centerFrame);
+ this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
+ },
+
+ /**
+ * Resizes the drag frame to the dimensions of the clicked object, positions
+ * it over the object, and finally displays it
+ * @method showFrame
+ * @param {int} iPageX X click position
+ * @param {int} iPageY Y click position
+ * @private
+ */
+ showFrame: function(iPageX, iPageY) {
+ var el = this.getEl();
+ var dragEl = this.getDragEl();
+ var s = dragEl.style;
+
+ this._resizeProxy();
+
+ if (this.centerFrame) {
+ this.setDelta( Math.round(parseInt(s.width, 10)/2),
+ Math.round(parseInt(s.height, 10)/2) );
+ }
+
+ this.setDragElPos(iPageX, iPageY);
+
+ YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible");
+ },
+
+ /**
+ * The proxy is automatically resized to the dimensions of the linked
+ * element when a drag is initiated, unless resizeFrame is set to false
+ * @method _resizeProxy
+ * @private
+ */
+ _resizeProxy: function() {
+ if (this.resizeFrame) {
+ var DOM = YAHOO.util.Dom;
+ var el = this.getEl();
+ var dragEl = this.getDragEl();
+
+ var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth" ), 10);
+ var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth" ), 10);
+ var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
+ var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth" ), 10);
+
+ if (isNaN(bt)) { bt = 0; }
+ if (isNaN(br)) { br = 0; }
+ if (isNaN(bb)) { bb = 0; }
+ if (isNaN(bl)) { bl = 0; }
+
+ this.logger.log("proxy size: " + bt + " " + br + " " + bb + " " + bl);
+
+ var newWidth = Math.max(0, el.offsetWidth - br - bl);
+ var newHeight = Math.max(0, el.offsetHeight - bt - bb);
+
+ this.logger.log("Resizing proxy element");
+
+ DOM.setStyle( dragEl, "width", newWidth + "px" );
+ DOM.setStyle( dragEl, "height", newHeight + "px" );
+ }
+ },
+
+ // overrides YAHOO.util.DragDrop
+ b4MouseDown: function(e) {
+ var x = YAHOO.util.Event.getPageX(e);
+ var y = YAHOO.util.Event.getPageY(e);
+ this.autoOffset(x, y);
+ this.setDragElPos(x, y);
+ },
+
+ // overrides YAHOO.util.DragDrop
+ b4StartDrag: function(x, y) {
+ // show the drag frame
+ this.logger.log("start drag show frame, x: " + x + ", y: " + y);
+ this.showFrame(x, y);
+ },
+
+ // overrides YAHOO.util.DragDrop
+ b4EndDrag: function(e) {
+ this.logger.log(this.id + " b4EndDrag");
+ YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden");
+ },
+
+ // overrides YAHOO.util.DragDrop
+ // By default we try to move the element to the last location of the frame.
+ // This is so that the default behavior mirrors that of YAHOO.util.DD.
+ endDrag: function(e) {
+ var DOM = YAHOO.util.Dom;
+ this.logger.log(this.id + " endDrag");
+ var lel = this.getEl();
+ var del = this.getDragEl();
+
+ // Show the drag frame briefly so we can get its position
+ // del.style.visibility = "";
+ DOM.setStyle(del, "visibility", "");
+
+ // Hide the linked element before the move to get around a Safari
+ // rendering bug.
+ //lel.style.visibility = "hidden";
+ DOM.setStyle(lel, "visibility", "hidden");
+ YAHOO.util.DDM.moveToEl(lel, del);
+ //del.style.visibility = "hidden";
+ DOM.setStyle(del, "visibility", "hidden");
+ //lel.style.visibility = "";
+ DOM.setStyle(lel, "visibility", "");
+ },
+
+ toString: function() {
+ return ("DDProxy " + this.id);
+ }
+
+});
+/**
+ * A DragDrop implementation that does not move, but can be a drop
+ * target. You would get the same result by simply omitting implementation
+ * for the event callbacks, but this way we reduce the processing cost of the
+ * event listener and the callbacks.
+ * @class DDTarget
+ * @extends YAHOO.util.DragDrop
+ * @constructor
+ * @param {String} id the id of the element that is a drop target
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ * Valid properties for DDTarget in addition to those in
+ * DragDrop:
+ * none
+ */
+YAHOO.util.DDTarget = function(id, sGroup, config) {
+ if (id) {
+ this.initTarget(id, sGroup, config);
+ }
+};
+
+// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
+YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
+ toString: function() {
+ return ("DDTarget " + this.id);
+ }
+});
diff --git a/source/web/scripts/ajax/yahoo/dragdrop/dragdrop-min.js b/source/web/scripts/ajax/yahoo/dragdrop/dragdrop-min.js
new file mode 100644
index 0000000000..56293cf8fa
--- /dev/null
+++ b/source/web/scripts/ajax/yahoo/dragdrop/dragdrop-min.js
@@ -0,0 +1 @@
+/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ (function(){var _1=YAHOO.util.Event;var _2=YAHOO.util.Dom;YAHOO.util.DragDrop=function(id,_4,_5){if(id){this.init(id,_4,_5);}};YAHOO.util.DragDrop.prototype={id:null,config:null,dragElId:null,handleElId:null,invalidHandleTypes:null,invalidHandleIds:null,invalidHandleClasses:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isTarget:true,padding:null,_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,available:false,hasOuterHandles:false,b4StartDrag:function(x,y){},startDrag:function(x,y){},b4Drag:function(e){},onDrag:function(e){},onDragEnter:function(e,id){},b4DragOver:function(e){},onDragOver:function(e,id){},b4DragOut:function(e){},onDragOut:function(e,id){},b4DragDrop:function(e){},onDragDrop:function(e,id){},onInvalidDrop:function(e){},b4EndDrag:function(e){},endDrag:function(e){},b4MouseDown:function(e){},onMouseDown:function(e){},onMouseUp:function(e){},onAvailable:function(){},getEl:function(){if(!this._domRef){this._domRef=_2.get(this.id);}return this._domRef;},getDragEl:function(){return _2.get(this.dragElId);},init:function(id,_9,_10){this.initTarget(id,_9,_10);_1.on(this.id,"mousedown",this.handleMouseDown,this,true);},initTarget:function(id,_11,_12){this.config=_12||{};this.DDM=YAHOO.util.DDM;this.groups={};if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.id=id;this.addToGroup((_11)?_11:"default");this.handleElId=id;_1.onAvailable(id,this.handleOnAvailable,this,true);this.setDragElId(id);this.invalidHandleTypes={A:"A"};this.invalidHandleIds={};this.invalidHandleClasses=[];this.applyConfig();},applyConfig:function(){this.padding=this.config.padding||[0,0,0,0];this.isTarget=(this.config.isTarget!==false);this.maintainOffset=(this.config.maintainOffset);this.primaryButtonOnly=(this.config.primaryButtonOnly!==false);},handleOnAvailable:function(){this.available=true;this.resetConstraints();this.onAvailable();},setPadding:function(_13,_14,_15,_16){if(!_14&&0!==_14){this.padding=[_13,_13,_13,_13];}else{if(!_15&&0!==_15){this.padding=[_13,_14,_13,_14];}else{this.padding=[_13,_14,_15,_16];}}},setInitPosition:function(_17,_18){var el=this.getEl();if(!this.DDM.verifyEl(el)){return;}var dx=_17||0;var dy=_18||0;var p=_2.getXY(el);this.initPageX=p[0]-dx;this.initPageY=p[1]-dy;this.lastPageX=p[0];this.lastPageY=p[1];this.setStartPosition(p);},setStartPosition:function(pos){var p=pos||_2.getXY(this.getEl());this.deltaSetXY=null;this.startPageX=p[0];this.startPageY=p[1];},addToGroup:function(_24){this.groups[_24]=true;this.DDM.regDragDrop(this,_24);},removeFromGroup:function(_25){if(this.groups[_25]){delete this.groups[_25];}this.DDM.removeDDFromGroup(this,_25);},setDragElId:function(id){this.dragElId=id;},setHandleElId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.handleElId=id;this.DDM.regHandle(this.id,id);},setOuterHandleElId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}_1.on(id,"mousedown",this.handleMouseDown,this,true);this.setHandleElId(id);this.hasOuterHandles=true;},unreg:function(){_1.removeListener(this.id,"mousedown",this.handleMouseDown);this._domRef=null;this.DDM._remove(this);},isLocked:function(){return (this.DDM.isLocked()||this.locked);},handleMouseDown:function(e,oDD){var _27=e.which||e.button;if(this.primaryButtonOnly&&_27>1){return;}if(this.isLocked()){return;}this.DDM.refreshCache(this.groups);var pt=new YAHOO.util.Point(_1.getPageX(e),_1.getPageY(e));if(!this.hasOuterHandles&&!this.DDM.isOverTarget(pt,this)){}else{if(this.clickValidator(e)){this.setStartPosition();this.b4MouseDown(e);this.onMouseDown(e);this.DDM.handleMouseDown(e,this);this.DDM.stopEvent(e);}else{}}},clickValidator:function(e){var _29=_1.getTarget(e);return (this.isValidHandleChild(_29)&&(this.id==this.handleElId||this.DDM.handleWasClicked(_29,this.id)));},addInvalidHandleType:function(_30){var _31=_30.toUpperCase();this.invalidHandleTypes[_31]=_31;},addInvalidHandleId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.invalidHandleIds[id]=id;},addInvalidHandleClass:function(_32){this.invalidHandleClasses.push(_32);},removeInvalidHandleType:function(_33){var _34=_33.toUpperCase();delete this.invalidHandleTypes[_34];},removeInvalidHandleId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}delete this.invalidHandleIds[id];},removeInvalidHandleClass:function(_35){for(var i=0,len=this.invalidHandleClasses.length;i+ * dd = new YAHOO.util.DragDrop("div1", "group1"); + *+ * Since none of the event handlers have been implemented, nothing would + * actually happen if you were to run the code above. Normally you would + * override this class or one of the default implementations, but you can + * also override the methods you want on an instance of the class... + *
+ * dd.onDragDrop = function(e, id) { + * alert("dd was dropped on " + id); + * } + *+ * @namespace YAHOO.util + * @class DragDrop + * @constructor + * @param {String} id of the element that is linked to this instance + * @param {String} sGroup the group of related DragDrop objects + * @param {object} config an object containing configurable attributes + * Valid properties for DragDrop: + * padding, isTarget, maintainOffset, primaryButtonOnly + */ +YAHOO.util.DragDrop = function(id, sGroup, config) { + if (id) { + this.init(id, sGroup, config); + } +}; + +YAHOO.util.DragDrop.prototype = { + + /** + * The id of the element associated with this object. This is what we + * refer to as the "linked element" because the size and position of + * this element is used to determine when the drag and drop objects have + * interacted. + * @property id + * @type String + */ + id: null, + + /** + * Configuration attributes passed into the constructor + * @property config + * @type object + */ + config: null, + + /** + * The id of the element that will be dragged. By default this is same + * as the linked element , but could be changed to another element. Ex: + * YAHOO.util.DDProxy + * @property dragElId + * @type String + * @private + */ + dragElId: null, + + /** + * the id of the element that initiates the drag operation. By default + * this is the linked element, but could be changed to be a child of this + * element. This lets us do things like only starting the drag when the + * header element within the linked html element is clicked. + * @property handleElId + * @type String + * @private + */ + handleElId: null, + + /** + * An associative array of HTML tags that will be ignored if clicked. + * @property invalidHandleTypes + * @type {string: string} + */ + invalidHandleTypes: null, + + /** + * An associative array of ids for elements that will be ignored if clicked + * @property invalidHandleIds + * @type {string: string} + */ + invalidHandleIds: null, + + /** + * An indexted array of css class names for elements that will be ignored + * if clicked. + * @property invalidHandleClasses + * @type string[] + */ + invalidHandleClasses: null, + + /** + * The linked element's absolute X position at the time the drag was + * started + * @property startPageX + * @type int + * @private + */ + startPageX: 0, + + /** + * The linked element's absolute X position at the time the drag was + * started + * @property startPageY + * @type int + * @private + */ + startPageY: 0, + + /** + * The group defines a logical collection of DragDrop objects that are + * related. Instances only get events when interacting with other + * DragDrop object in the same group. This lets us define multiple + * groups using a single DragDrop subclass if we want. + * @property groups + * @type {string: string} + */ + groups: null, + + /** + * Individual drag/drop instances can be locked. This will prevent + * onmousedown start drag. + * @property locked + * @type boolean + * @private + */ + locked: false, + + /** + * Lock this instance + * @method lock + */ + lock: function() { this.locked = true; }, + + /** + * Unlock this instace + * @method unlock + */ + unlock: function() { this.locked = false; }, + + /** + * By default, all insances can be a drop target. This can be disabled by + * setting isTarget to false. + * @method isTarget + * @type boolean + */ + isTarget: true, + + /** + * The padding configured for this drag and drop object for calculating + * the drop zone intersection with this object. + * @method padding + * @type int[] + */ + padding: null, + + /** + * Cached reference to the linked element + * @property _domRef + * @private + */ + _domRef: null, + + /** + * Internal typeof flag + * @property __ygDragDrop + * @private + */ + __ygDragDrop: true, + + /** + * Set to true when horizontal contraints are applied + * @property constrainX + * @type boolean + * @private + */ + constrainX: false, + + /** + * Set to true when vertical contraints are applied + * @property constrainY + * @type boolean + * @private + */ + constrainY: false, + + /** + * The left constraint + * @property minX + * @type int + * @private + */ + minX: 0, + + /** + * The right constraint + * @property maxX + * @type int + * @private + */ + maxX: 0, + + /** + * The up constraint + * @property minY + * @type int + * @type int + * @private + */ + minY: 0, + + /** + * The down constraint + * @property maxY + * @type int + * @private + */ + maxY: 0, + + /** + * Maintain offsets when we resetconstraints. Set to true when you want + * the position of the element relative to its parent to stay the same + * when the page changes + * + * @property maintainOffset + * @type boolean + */ + maintainOffset: false, + + /** + * Array of pixel locations the element will snap to if we specified a + * horizontal graduation/interval. This array is generated automatically + * when you define a tick interval. + * @property xTicks + * @type int[] + */ + xTicks: null, + + /** + * Array of pixel locations the element will snap to if we specified a + * vertical graduation/interval. This array is generated automatically + * when you define a tick interval. + * @property yTicks + * @type int[] + */ + yTicks: null, + + /** + * By default the drag and drop instance will only respond to the primary + * button click (left button for a right-handed mouse). Set to true to + * allow drag and drop to start with any mouse click that is propogated + * by the browser + * @property primaryButtonOnly + * @type boolean + */ + primaryButtonOnly: true, + + /** + * The availabe property is false until the linked dom element is accessible. + * @property available + * @type boolean + */ + available: false, + + /** + * By default, drags can only be initiated if the mousedown occurs in the + * region the linked element is. This is done in part to work around a + * bug in some browsers that mis-report the mousedown if the previous + * mouseup happened outside of the window. This property is set to true + * if outer handles are defined. + * + * @property hasOuterHandles + * @type boolean + * @default false + */ + hasOuterHandles: false, + + /** + * Code that executes immediately before the startDrag event + * @method b4StartDrag + * @private + */ + b4StartDrag: function(x, y) { }, + + /** + * Abstract method called after a drag/drop object is clicked + * and the drag or mousedown time thresholds have beeen met. + * @method startDrag + * @param {int} X click location + * @param {int} Y click location + */ + startDrag: function(x, y) { /* override this */ }, + + /** + * Code that executes immediately before the onDrag event + * @method b4Drag + * @private + */ + b4Drag: function(e) { }, + + /** + * Abstract method called during the onMouseMove event while dragging an + * object. + * @method onDrag + * @param {Event} e the mousemove event + */ + onDrag: function(e) { /* override this */ }, + + /** + * Abstract method called when this element fist begins hovering over + * another DragDrop obj + * @method onDragEnter + * @param {Event} e the mousemove event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this is hovering over. In INTERSECT mode, an array of one or more + * dragdrop items being hovered over. + */ + onDragEnter: function(e, id) { /* override this */ }, + + /** + * Code that executes immediately before the onDragOver event + * @method b4DragOver + * @private + */ + b4DragOver: function(e) { }, + + /** + * Abstract method called when this element is hovering over another + * DragDrop obj + * @method onDragOver + * @param {Event} e the mousemove event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this is hovering over. In INTERSECT mode, an array of dd items + * being hovered over. + */ + onDragOver: function(e, id) { /* override this */ }, + + /** + * Code that executes immediately before the onDragOut event + * @method b4DragOut + * @private + */ + b4DragOut: function(e) { }, + + /** + * Abstract method called when we are no longer hovering over an element + * @method onDragOut + * @param {Event} e the mousemove event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this was hovering over. In INTERSECT mode, an array of dd items + * that the mouse is no longer over. + */ + onDragOut: function(e, id) { /* override this */ }, + + /** + * Code that executes immediately before the onDragDrop event + * @method b4DragDrop + * @private + */ + b4DragDrop: function(e) { }, + + /** + * Abstract method called when this item is dropped on another DragDrop + * obj + * @method onDragDrop + * @param {Event} e the mouseup event + * @param {String|DragDrop[]} id In POINT mode, the element + * id this was dropped on. In INTERSECT mode, an array of dd items this + * was dropped on. + */ + onDragDrop: function(e, id) { /* override this */ }, + + /** + * Abstract method called when this item is dropped on an area with no + * drop target + * @method onInvalidDrop + * @param {Event} e the mouseup event + */ + onInvalidDrop: function(e) { /* override this */ }, + + /** + * Code that executes immediately before the endDrag event + * @method b4EndDrag + * @private + */ + b4EndDrag: function(e) { }, + + /** + * Fired when we are done dragging the object + * @method endDrag + * @param {Event} e the mouseup event + */ + endDrag: function(e) { /* override this */ }, + + /** + * Code executed immediately before the onMouseDown event + * @method b4MouseDown + * @param {Event} e the mousedown event + * @private + */ + b4MouseDown: function(e) { }, + + /** + * Event handler that fires when a drag/drop obj gets a mousedown + * @method onMouseDown + * @param {Event} e the mousedown event + */ + onMouseDown: function(e) { /* override this */ }, + + /** + * Event handler that fires when a drag/drop obj gets a mouseup + * @method onMouseUp + * @param {Event} e the mouseup event + */ + onMouseUp: function(e) { /* override this */ }, + + /** + * Override the onAvailable method to do what is needed after the initial + * position was determined. + * @method onAvailable + */ + onAvailable: function () { + }, + + /** + * Returns a reference to the linked element + * @method getEl + * @return {HTMLElement} the html element + */ + getEl: function() { + if (!this._domRef) { + this._domRef = Dom.get(this.id); + } + + return this._domRef; + }, + + /** + * Returns a reference to the actual element to drag. By default this is + * the same as the html element, but it can be assigned to another + * element. An example of this can be found in YAHOO.util.DDProxy + * @method getDragEl + * @return {HTMLElement} the html element + */ + getDragEl: function() { + return Dom.get(this.dragElId); + }, + + /** + * Sets up the DragDrop object. Must be called in the constructor of any + * YAHOO.util.DragDrop subclass + * @method init + * @param id the id of the linked element + * @param {String} sGroup the group of related items + * @param {object} config configuration attributes + */ + init: function(id, sGroup, config) { + this.initTarget(id, sGroup, config); + Event.on(this.id, "mousedown", this.handleMouseDown, this, true); + // Event.on(this.id, "selectstart", Event.preventDefault); + }, + + /** + * Initializes Targeting functionality only... the object does not + * get a mousedown handler. + * @method initTarget + * @param id the id of the linked element + * @param {String} sGroup the group of related items + * @param {object} config configuration attributes + */ + initTarget: function(id, sGroup, config) { + + // configuration attributes + this.config = config || {}; + + // create a local reference to the drag and drop manager + this.DDM = YAHOO.util.DDM; + // initialize the groups array + this.groups = {}; + + // assume that we have an element reference instead of an id if the + // parameter is not a string + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + + // set the id + this.id = id; + + // add to an interaction group + this.addToGroup((sGroup) ? sGroup : "default"); + + // We don't want to register this as the handle with the manager + // so we just set the id rather than calling the setter. + this.handleElId = id; + + Event.onAvailable(id, this.handleOnAvailable, this, true); + + + // the linked element is the element that gets dragged by default + this.setDragElId(id); + + // by default, clicked anchors will not start drag operations. + // @TODO what else should be here? Probably form fields. + this.invalidHandleTypes = { A: "A" }; + this.invalidHandleIds = {}; + this.invalidHandleClasses = []; + + this.applyConfig(); + }, + + /** + * Applies the configuration parameters that were passed into the constructor. + * This is supposed to happen at each level through the inheritance chain. So + * a DDProxy implentation will execute apply config on DDProxy, DD, and + * DragDrop in order to get all of the parameters that are available in + * each object. + * @method applyConfig + */ + applyConfig: function() { + + // configurable properties: + // padding, isTarget, maintainOffset, primaryButtonOnly + this.padding = this.config.padding || [0, 0, 0, 0]; + this.isTarget = (this.config.isTarget !== false); + this.maintainOffset = (this.config.maintainOffset); + this.primaryButtonOnly = (this.config.primaryButtonOnly !== false); + + }, + + /** + * Executed when the linked element is available + * @method handleOnAvailable + * @private + */ + handleOnAvailable: function() { + this.available = true; + this.resetConstraints(); + this.onAvailable(); + }, + + /** + * Configures the padding for the target zone in px. Effectively expands + * (or reduces) the virtual object size for targeting calculations. + * Supports css-style shorthand; if only one parameter is passed, all sides + * will have that padding, and if only two are passed, the top and bottom + * will have the first param, the left and right the second. + * @method setPadding + * @param {int} iTop Top pad + * @param {int} iRight Right pad + * @param {int} iBot Bot pad + * @param {int} iLeft Left pad + */ + setPadding: function(iTop, iRight, iBot, iLeft) { + // this.padding = [iLeft, iRight, iTop, iBot]; + if (!iRight && 0 !== iRight) { + this.padding = [iTop, iTop, iTop, iTop]; + } else if (!iBot && 0 !== iBot) { + this.padding = [iTop, iRight, iTop, iRight]; + } else { + this.padding = [iTop, iRight, iBot, iLeft]; + } + }, + + /** + * Stores the initial placement of the linked element. + * @method setInitialPosition + * @param {int} diffX the X offset, default 0 + * @param {int} diffY the Y offset, default 0 + */ + setInitPosition: function(diffX, diffY) { + var el = this.getEl(); + + if (!this.DDM.verifyEl(el)) { + return; + } + + var dx = diffX || 0; + var dy = diffY || 0; + + var p = Dom.getXY( el ); + + this.initPageX = p[0] - dx; + this.initPageY = p[1] - dy; + + this.lastPageX = p[0]; + this.lastPageY = p[1]; + + + this.setStartPosition(p); + }, + + /** + * Sets the start position of the element. This is set when the obj + * is initialized, the reset when a drag is started. + * @method setStartPosition + * @param pos current position (from previous lookup) + * @private + */ + setStartPosition: function(pos) { + var p = pos || Dom.getXY( this.getEl() ); + this.deltaSetXY = null; + + this.startPageX = p[0]; + this.startPageY = p[1]; + }, + + /** + * Add this instance to a group of related drag/drop objects. All + * instances belong to at least one group, and can belong to as many + * groups as needed. + * @method addToGroup + * @param sGroup {string} the name of the group + */ + addToGroup: function(sGroup) { + this.groups[sGroup] = true; + this.DDM.regDragDrop(this, sGroup); + }, + + /** + * Remove's this instance from the supplied interaction group + * @method removeFromGroup + * @param {string} sGroup The group to drop + */ + removeFromGroup: function(sGroup) { + if (this.groups[sGroup]) { + delete this.groups[sGroup]; + } + + this.DDM.removeDDFromGroup(this, sGroup); + }, + + /** + * Allows you to specify that an element other than the linked element + * will be moved with the cursor during a drag + * @method setDragElId + * @param id {string} the id of the element that will be used to initiate the drag + */ + setDragElId: function(id) { + this.dragElId = id; + }, + + /** + * Allows you to specify a child of the linked element that should be + * used to initiate the drag operation. An example of this would be if + * you have a content div with text and links. Clicking anywhere in the + * content area would normally start the drag operation. Use this method + * to specify that an element inside of the content div is the element + * that starts the drag operation. + * @method setHandleElId + * @param id {string} the id of the element that will be used to + * initiate the drag. + */ + setHandleElId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + this.handleElId = id; + this.DDM.regHandle(this.id, id); + }, + + /** + * Allows you to set an element outside of the linked element as a drag + * handle + * @method setOuterHandleElId + * @param id the id of the element that will be used to initiate the drag + */ + setOuterHandleElId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + Event.on(id, "mousedown", + this.handleMouseDown, this, true); + this.setHandleElId(id); + + this.hasOuterHandles = true; + }, + + /** + * Remove all drag and drop hooks for this element + * @method unreg + */ + unreg: function() { + Event.removeListener(this.id, "mousedown", + this.handleMouseDown); + this._domRef = null; + this.DDM._remove(this); + }, + + /** + * Returns true if this instance is locked, or the drag drop mgr is locked + * (meaning that all drag/drop is disabled on the page.) + * @method isLocked + * @return {boolean} true if this obj or all drag/drop is locked, else + * false + */ + isLocked: function() { + return (this.DDM.isLocked() || this.locked); + }, + + /** + * Fired when this object is clicked + * @method handleMouseDown + * @param {Event} e + * @param {YAHOO.util.DragDrop} oDD the clicked dd object (this dd obj) + * @private + */ + handleMouseDown: function(e, oDD) { + + var button = e.which || e.button; + + if (this.primaryButtonOnly && button > 1) { + return; + } + + if (this.isLocked()) { + return; + } + + this.DDM.refreshCache(this.groups); + // var self = this; + // setTimeout( function() { self.DDM.refreshCache(self.groups); }, 0); + + // Only process the event if we really clicked within the linked + // element. The reason we make this check is that in the case that + // another element was moved between the clicked element and the + // cursor in the time between the mousedown and mouseup events. When + // this happens, the element gets the next mousedown event + // regardless of where on the screen it happened. + var pt = new YAHOO.util.Point(Event.getPageX(e), Event.getPageY(e)); + if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) ) { + } else { + if (this.clickValidator(e)) { + + + // set the initial element position + this.setStartPosition(); + + + this.b4MouseDown(e); + this.onMouseDown(e); + this.DDM.handleMouseDown(e, this); + + this.DDM.stopEvent(e); + } else { + + + } + } + }, + + clickValidator: function(e) { + var target = Event.getTarget(e); + return ( this.isValidHandleChild(target) && + (this.id == this.handleElId || + this.DDM.handleWasClicked(target, this.id)) ); + }, + + /** + * Allows you to specify a tag name that should not start a drag operation + * when clicked. This is designed to facilitate embedding links within a + * drag handle that do something other than start the drag. + * @method addInvalidHandleType + * @param {string} tagName the type of element to exclude + */ + addInvalidHandleType: function(tagName) { + var type = tagName.toUpperCase(); + this.invalidHandleTypes[type] = type; + }, + + /** + * Lets you to specify an element id for a child of a drag handle + * that should not initiate a drag + * @method addInvalidHandleId + * @param {string} id the element id of the element you wish to ignore + */ + addInvalidHandleId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + this.invalidHandleIds[id] = id; + }, + + /** + * Lets you specify a css class of elements that will not initiate a drag + * @method addInvalidHandleClass + * @param {string} cssClass the class of the elements you wish to ignore + */ + addInvalidHandleClass: function(cssClass) { + this.invalidHandleClasses.push(cssClass); + }, + + /** + * Unsets an excluded tag name set by addInvalidHandleType + * @method removeInvalidHandleType + * @param {string} tagName the type of element to unexclude + */ + removeInvalidHandleType: function(tagName) { + var type = tagName.toUpperCase(); + // this.invalidHandleTypes[type] = null; + delete this.invalidHandleTypes[type]; + }, + + /** + * Unsets an invalid handle id + * @method removeInvalidHandleId + * @param {string} id the id of the element to re-enable + */ + removeInvalidHandleId: function(id) { + if (typeof id !== "string") { + YAHOO.log("id is not a string, assuming it is an HTMLElement"); + id = Dom.generateId(id); + } + delete this.invalidHandleIds[id]; + }, + + /** + * Unsets an invalid css class + * @method removeInvalidHandleClass + * @param {string} cssClass the class of the element(s) you wish to + * re-enable + */ + removeInvalidHandleClass: function(cssClass) { + for (var i=0, len=this.invalidHandleClasses.length; i
+ * YAHOO.util.DragDropMgr.refreshCache({group1:true, group2:true});
+ *
+ * @TODO this really should be an indexed array. Alternatively this
+ * method could accept both.
+ * @method refreshCache
+ * @param {Object} groups an associative array of groups to refresh
+ * @static
+ */
+ refreshCache: function(groups) {
+ for (var sGroup in groups) {
+ if ("string" != typeof sGroup) {
+ continue;
+ }
+ for (var i in this.ids[sGroup]) {
+ var oDD = this.ids[sGroup][i];
+
+ if (this.isTypeOfDD(oDD)) {
+ // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
+ var loc = this.getLocation(oDD);
+ if (loc) {
+ this.locationCache[oDD.id] = loc;
+ } else {
+ delete this.locationCache[oDD.id];
+ // this will unregister the drag and drop object if
+ // the element is not in a usable state
+ // oDD.unreg();
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * This checks to make sure an element exists and is in the DOM. The
+ * main purpose is to handle cases where innerHTML is used to remove
+ * drag and drop objects from the DOM. IE provides an 'unspecified
+ * error' when trying to access the offsetParent of such an element
+ * @method verifyEl
+ * @param {HTMLElement} el the element to check
+ * @return {boolean} true if the element looks usable
+ * @static
+ */
+ verifyEl: function(el) {
+ try {
+ if (el) {
+ var parent = el.offsetParent;
+ if (parent) {
+ return true;
+ }
+ }
+ } catch(e) {
+ }
+
+ return false;
+ },
+
+ /**
+ * Returns a Region object containing the drag and drop element's position
+ * and size, including the padding configured for it
+ * @method getLocation
+ * @param {DragDrop} oDD the drag and drop object to get the
+ * location for
+ * @return {YAHOO.util.Region} a Region object representing the total area
+ * the element occupies, including any padding
+ * the instance is configured for.
+ * @static
+ */
+ getLocation: function(oDD) {
+ if (! this.isTypeOfDD(oDD)) {
+ return null;
+ }
+
+ var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
+
+ try {
+ pos= YAHOO.util.Dom.getXY(el);
+ } catch (e) { }
+
+ if (!pos) {
+ return null;
+ }
+
+ x1 = pos[0];
+ x2 = x1 + el.offsetWidth;
+ y1 = pos[1];
+ y2 = y1 + el.offsetHeight;
+
+ t = y1 - oDD.padding[0];
+ r = x2 + oDD.padding[1];
+ b = y2 + oDD.padding[2];
+ l = x1 - oDD.padding[3];
+
+ return new YAHOO.util.Region( t, r, b, l );
+ },
+
+ /**
+ * Checks the cursor location to see if it over the target
+ * @method isOverTarget
+ * @param {YAHOO.util.Point} pt The point to evaluate
+ * @param {DragDrop} oTarget the DragDrop object we are inspecting
+ * @return {boolean} true if the mouse is over the target
+ * @private
+ * @static
+ */
+ isOverTarget: function(pt, oTarget, intersect) {
+ // use cache if available
+ var loc = this.locationCache[oTarget.id];
+ if (!loc || !this.useCache) {
+ loc = this.getLocation(oTarget);
+ this.locationCache[oTarget.id] = loc;
+
+ }
+
+ if (!loc) {
+ return false;
+ }
+
+ oTarget.cursorIsOver = loc.contains( pt );
+
+ // DragDrop is using this as a sanity check for the initial mousedown
+ // in this case we are done. In POINT mode, if the drag obj has no
+ // contraints, we are also done. Otherwise we need to evaluate the
+ // location of the target as related to the actual location of the
+ // dragged element.
+ var dc = this.dragCurrent;
+ if (!dc || !dc.getTargetCoord ||
+ (!intersect && !dc.constrainX && !dc.constrainY)) {
+ return oTarget.cursorIsOver;
+ }
+
+ oTarget.overlap = null;
+
+ // Get the current location of the drag element, this is the
+ // location of the mouse event less the delta that represents
+ // where the original mousedown happened on the element. We
+ // need to consider constraints and ticks as well.
+ var pos = dc.getTargetCoord(pt.x, pt.y);
+
+ var el = dc.getDragEl();
+ var curRegion = new YAHOO.util.Region( pos.y,
+ pos.x + el.offsetWidth,
+ pos.y + el.offsetHeight,
+ pos.x );
+
+ var overlap = curRegion.intersect(loc);
+
+ if (overlap) {
+ oTarget.overlap = overlap;
+ return (intersect) ? true : oTarget.cursorIsOver;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * unload event handler
+ * @method _onUnload
+ * @private
+ * @static
+ */
+ _onUnload: function(e, me) {
+ this.unregAll();
+ },
+
+ /**
+ * Cleans up the drag and drop events and objects.
+ * @method unregAll
+ * @private
+ * @static
+ */
+ unregAll: function() {
+
+ if (this.dragCurrent) {
+ this.stopDrag();
+ this.dragCurrent = null;
+ }
+
+ this._execOnAll("unreg", []);
+
+ for (i in this.elementCache) {
+ delete this.elementCache[i];
+ }
+
+ this.elementCache = {};
+ this.ids = {};
+ },
+
+ /**
+ * A cache of DOM elements
+ * @property elementCache
+ * @private
+ * @static
+ */
+ elementCache: {},
+
+ /**
+ * Get the wrapper for the DOM element specified
+ * @method getElWrapper
+ * @param {String} id the id of the element to get
+ * @return {YAHOO.util.DDM.ElementWrapper} the wrapped element
+ * @private
+ * @deprecated This wrapper isn't that useful
+ * @static
+ */
+ getElWrapper: function(id) {
+ var oWrapper = this.elementCache[id];
+ if (!oWrapper || !oWrapper.el) {
+ oWrapper = this.elementCache[id] =
+ new this.ElementWrapper(YAHOO.util.Dom.get(id));
+ }
+ return oWrapper;
+ },
+
+ /**
+ * Returns the actual DOM element
+ * @method getElement
+ * @param {String} id the id of the elment to get
+ * @return {Object} The element
+ * @deprecated use YAHOO.util.Dom.get instead
+ * @static
+ */
+ getElement: function(id) {
+ return YAHOO.util.Dom.get(id);
+ },
+
+ /**
+ * Returns the style property for the DOM element (i.e.,
+ * document.getElById(id).style)
+ * @method getCss
+ * @param {String} id the id of the elment to get
+ * @return {Object} The style property of the element
+ * @deprecated use YAHOO.util.Dom instead
+ * @static
+ */
+ getCss: function(id) {
+ var el = YAHOO.util.Dom.get(id);
+ return (el) ? el.style : null;
+ },
+
+ /**
+ * Inner class for cached elements
+ * @class DragDropMgr.ElementWrapper
+ * @for DragDropMgr
+ * @private
+ * @deprecated
+ */
+ ElementWrapper: function(el) {
+ /**
+ * The element
+ * @property el
+ */
+ this.el = el || null;
+ /**
+ * The element id
+ * @property id
+ */
+ this.id = this.el && el.id;
+ /**
+ * A reference to the style property
+ * @property css
+ */
+ this.css = this.el && el.style;
+ },
+
+ /**
+ * Returns the X position of an html element
+ * @method getPosX
+ * @param el the element for which to get the position
+ * @return {int} the X coordinate
+ * @for DragDropMgr
+ * @deprecated use YAHOO.util.Dom.getX instead
+ * @static
+ */
+ getPosX: function(el) {
+ return YAHOO.util.Dom.getX(el);
+ },
+
+ /**
+ * Returns the Y position of an html element
+ * @method getPosY
+ * @param el the element for which to get the position
+ * @return {int} the Y coordinate
+ * @deprecated use YAHOO.util.Dom.getY instead
+ * @static
+ */
+ getPosY: function(el) {
+ return YAHOO.util.Dom.getY(el);
+ },
+
+ /**
+ * Swap two nodes. In IE, we use the native method, for others we
+ * emulate the IE behavior
+ * @method swapNode
+ * @param n1 the first node to swap
+ * @param n2 the other node to swap
+ * @static
+ */
+ swapNode: function(n1, n2) {
+ if (n1.swapNode) {
+ n1.swapNode(n2);
+ } else {
+ var p = n2.parentNode;
+ var s = n2.nextSibling;
+
+ if (s == n1) {
+ p.insertBefore(n1, n2);
+ } else if (n2 == n1.nextSibling) {
+ p.insertBefore(n2, n1);
+ } else {
+ n1.parentNode.replaceChild(n2, n1);
+ p.insertBefore(n1, s);
+ }
+ }
+ },
+
+ /**
+ * Returns the current scroll position
+ * @method getScroll
+ * @private
+ * @static
+ */
+ getScroll: function () {
+ var t, l, dde=document.documentElement, db=document.body;
+ if (dde && (dde.scrollTop || dde.scrollLeft)) {
+ t = dde.scrollTop;
+ l = dde.scrollLeft;
+ } else if (db) {
+ t = db.scrollTop;
+ l = db.scrollLeft;
+ } else {
+ YAHOO.log("could not get scroll property");
+ }
+ return { top: t, left: l };
+ },
+
+ /**
+ * Returns the specified element style property
+ * @method getStyle
+ * @param {HTMLElement} el the element
+ * @param {string} styleProp the style property
+ * @return {string} The value of the style property
+ * @deprecated use YAHOO.util.Dom.getStyle
+ * @static
+ */
+ getStyle: function(el, styleProp) {
+ return YAHOO.util.Dom.getStyle(el, styleProp);
+ },
+
+ /**
+ * Gets the scrollTop
+ * @method getScrollTop
+ * @return {int} the document's scrollTop
+ * @static
+ */
+ getScrollTop: function () { return this.getScroll().top; },
+
+ /**
+ * Gets the scrollLeft
+ * @method getScrollLeft
+ * @return {int} the document's scrollTop
+ * @static
+ */
+ getScrollLeft: function () { return this.getScroll().left; },
+
+ /**
+ * Sets the x/y position of an element to the location of the
+ * target element.
+ * @method moveToEl
+ * @param {HTMLElement} moveEl The element to move
+ * @param {HTMLElement} targetEl The position reference element
+ * @static
+ */
+ moveToEl: function (moveEl, targetEl) {
+ var aCoord = YAHOO.util.Dom.getXY(targetEl);
+ YAHOO.util.Dom.setXY(moveEl, aCoord);
+ },
+
+ /**
+ * Gets the client height
+ * @method getClientHeight
+ * @return {int} client height in px
+ * @deprecated use YAHOO.util.Dom.getViewportHeight instead
+ * @static
+ */
+ getClientHeight: function() {
+ return YAHOO.util.Dom.getViewportHeight();
+ },
+
+ /**
+ * Gets the client width
+ * @method getClientWidth
+ * @return {int} client width in px
+ * @deprecated use YAHOO.util.Dom.getViewportWidth instead
+ * @static
+ */
+ getClientWidth: function() {
+ return YAHOO.util.Dom.getViewportWidth();
+ },
+
+ /**
+ * Numeric array sort function
+ * @method numericSort
+ * @static
+ */
+ numericSort: function(a, b) { return (a - b); },
+
+ /**
+ * Internal counter
+ * @property _timeoutCount
+ * @private
+ * @static
+ */
+ _timeoutCount: 0,
+
+ /**
+ * Trying to make the load order less important. Without this we get
+ * an error if this file is loaded before the Event Utility.
+ * @method _addListeners
+ * @private
+ * @static
+ */
+ _addListeners: function() {
+ var DDM = YAHOO.util.DDM;
+ if ( YAHOO.util.Event && document ) {
+ DDM._onLoad();
+ } else {
+ if (DDM._timeoutCount > 2000) {
+ } else {
+ setTimeout(DDM._addListeners, 10);
+ if (document && document.body) {
+ DDM._timeoutCount += 1;
+ }
+ }
+ }
+ },
+
+ /**
+ * Recursively searches the immediate parent and all child nodes for
+ * the handle element in order to determine wheter or not it was
+ * clicked.
+ * @method handleWasClicked
+ * @param node the html element to inspect
+ * @static
+ */
+ handleWasClicked: function(node, id) {
+ if (this.isHandle(id, node.id)) {
+ return true;
+ } else {
+ // check to see if this is a text node child of the one we want
+ var p = node.parentNode;
+
+ while (p) {
+ if (this.isHandle(id, p.id)) {
+ return true;
+ } else {
+ p = p.parentNode;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ };
+
+}();
+
+// shorter alias, save a few bytes
+YAHOO.util.DDM = YAHOO.util.DragDropMgr;
+YAHOO.util.DDM._addListeners();
+
+}
+
+/**
+ * A DragDrop implementation where the linked element follows the
+ * mouse cursor during a drag.
+ * @class DD
+ * @extends YAHOO.util.DragDrop
+ * @constructor
+ * @param {String} id the id of the linked element
+ * @param {String} sGroup the group of related DragDrop items
+ * @param {object} config an object containing configurable attributes
+ * Valid properties for DD:
+ * scroll
+ */
+YAHOO.util.DD = function(id, sGroup, config) {
+ if (id) {
+ this.init(id, sGroup, config);
+ }
+};
+
+YAHOO.extend(YAHOO.util.DD, YAHOO.util.DragDrop, {
+
+ /**
+ * When set to true, the utility automatically tries to scroll the browser
+ * window wehn a drag and drop element is dragged near the viewport boundary.
+ * Defaults to true.
+ * @property scroll
+ * @type boolean
+ */
+ scroll: true,
+
+ /**
+ * Sets the pointer offset to the distance between the linked element's top
+ * left corner and the location the element was clicked
+ * @method autoOffset
+ * @param {int} iPageX the X coordinate of the click
+ * @param {int} iPageY the Y coordinate of the click
+ */
+ autoOffset: function(iPageX, iPageY) {
+ var x = iPageX - this.startPageX;
+ var y = iPageY - this.startPageY;
+ this.setDelta(x, y);
+ },
+
+ /**
+ * Sets the pointer offset. You can call this directly to force the
+ * offset to be in a particular location (e.g., pass in 0,0 to set it
+ * to the center of the object, as done in YAHOO.widget.Slider)
+ * @method setDelta
+ * @param {int} iDeltaX the distance from the left
+ * @param {int} iDeltaY the distance from the top
+ */
+ setDelta: function(iDeltaX, iDeltaY) {
+ this.deltaX = iDeltaX;
+ this.deltaY = iDeltaY;
+ },
+
+ /**
+ * Sets the drag element to the location of the mousedown or click event,
+ * maintaining the cursor location relative to the location on the element
+ * that was clicked. Override this if you want to place the element in a
+ * location other than where the cursor is.
+ * @method setDragElPos
+ * @param {int} iPageX the X coordinate of the mousedown or drag event
+ * @param {int} iPageY the Y coordinate of the mousedown or drag event
+ */
+ setDragElPos: function(iPageX, iPageY) {
+ // the first time we do this, we are going to check to make sure
+ // the element has css positioning
+
+ var el = this.getDragEl();
+ this.alignElWithMouse(el, iPageX, iPageY);
+ },
+
+ /**
+ * Sets the element to the location of the mousedown or click event,
+ * maintaining the cursor location relative to the location on the element
+ * that was clicked. Override this if you want to place the element in a
+ * location other than where the cursor is.
+ * @method alignElWithMouse
+ * @param {HTMLElement} el the element to move
+ * @param {int} iPageX the X coordinate of the mousedown or drag event
+ * @param {int} iPageY the Y coordinate of the mousedown or drag event
+ */
+ alignElWithMouse: function(el, iPageX, iPageY) {
+ var oCoord = this.getTargetCoord(iPageX, iPageY);
+
+ if (!this.deltaSetXY) {
+ var aCoord = [oCoord.x, oCoord.y];
+ YAHOO.util.Dom.setXY(el, aCoord);
+ var newLeft = parseInt( YAHOO.util.Dom.getStyle(el, "left"), 10 );
+ var newTop = parseInt( YAHOO.util.Dom.getStyle(el, "top" ), 10 );
+
+ this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
+ } else {
+ YAHOO.util.Dom.setStyle(el, "left", (oCoord.x + this.deltaSetXY[0]) + "px");
+ YAHOO.util.Dom.setStyle(el, "top", (oCoord.y + this.deltaSetXY[1]) + "px");
+ }
+
+ this.cachePosition(oCoord.x, oCoord.y);
+ this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
+ },
+
+ /**
+ * Saves the most recent position so that we can reset the constraints and
+ * tick marks on-demand. We need to know this so that we can calculate the
+ * number of pixels the element is offset from its original position.
+ * @method cachePosition
+ * @param iPageX the current x position (optional, this just makes it so we
+ * don't have to look it up again)
+ * @param iPageY the current y position (optional, this just makes it so we
+ * don't have to look it up again)
+ */
+ cachePosition: function(iPageX, iPageY) {
+ if (iPageX) {
+ this.lastPageX = iPageX;
+ this.lastPageY = iPageY;
+ } else {
+ var aCoord = YAHOO.util.Dom.getXY(this.getEl());
+ this.lastPageX = aCoord[0];
+ this.lastPageY = aCoord[1];
+ }
+ },
+
+ /**
+ * Auto-scroll the window if the dragged object has been moved beyond the
+ * visible window boundary.
+ * @method autoScroll
+ * @param {int} x the drag element's x position
+ * @param {int} y the drag element's y position
+ * @param {int} h the height of the drag element
+ * @param {int} w the width of the drag element
+ * @private
+ */
+ autoScroll: function(x, y, h, w) {
+
+ if (this.scroll) {
+ // The client height
+ var clientH = this.DDM.getClientHeight();
+
+ // The client width
+ var clientW = this.DDM.getClientWidth();
+
+ // The amt scrolled down
+ var st = this.DDM.getScrollTop();
+
+ // The amt scrolled right
+ var sl = this.DDM.getScrollLeft();
+
+ // Location of the bottom of the element
+ var bot = h + y;
+
+ // Location of the right of the element
+ var right = w + x;
+
+ // The distance from the cursor to the bottom of the visible area,
+ // adjusted so that we don't scroll if the cursor is beyond the
+ // element drag constraints
+ var toBot = (clientH + st - y - this.deltaY);
+
+ // The distance from the cursor to the right of the visible area
+ var toRight = (clientW + sl - x - this.deltaX);
+
+
+ // How close to the edge the cursor must be before we scroll
+ // var thresh = (document.all) ? 100 : 40;
+ var thresh = 40;
+
+ // How many pixels to scroll per autoscroll op. This helps to reduce
+ // clunky scrolling. IE is more sensitive about this ... it needs this
+ // value to be higher.
+ var scrAmt = (document.all) ? 80 : 30;
+
+ // Scroll down if we are near the bottom of the visible page and the
+ // obj extends below the crease
+ if ( bot > clientH && toBot < thresh ) {
+ window.scrollTo(sl, st + scrAmt);
+ }
+
+ // Scroll up if the window is scrolled down and the top of the object
+ // goes above the top border
+ if ( y < st && st > 0 && y - st < thresh ) {
+ window.scrollTo(sl, st - scrAmt);
+ }
+
+ // Scroll right if the obj is beyond the right border and the cursor is
+ // near the border.
+ if ( right > clientW && toRight < thresh ) {
+ window.scrollTo(sl + scrAmt, st);
+ }
+
+ // Scroll left if the window has been scrolled to the right and the obj
+ // extends past the left border
+ if ( x < sl && sl > 0 && x - sl < thresh ) {
+ window.scrollTo(sl - scrAmt, st);
+ }
+ }
+ },
+
+ /**
+ * Finds the location the element should be placed if we want to move
+ * it to where the mouse location less the click offset would place us.
+ * @method getTargetCoord
+ * @param {int} iPageX the X coordinate of the click
+ * @param {int} iPageY the Y coordinate of the click
+ * @return an object that contains the coordinates (Object.x and Object.y)
+ * @private
+ */
+ getTargetCoord: function(iPageX, iPageY) {
+
+
+ var x = iPageX - this.deltaX;
+ var y = iPageY - this.deltaY;
+
+ if (this.constrainX) {
+ if (x < this.minX) { x = this.minX; }
+ if (x > this.maxX) { x = this.maxX; }
+ }
+
+ if (this.constrainY) {
+ if (y < this.minY) { y = this.minY; }
+ if (y > this.maxY) { y = this.maxY; }
+ }
+
+ x = this.getTick(x, this.xTicks);
+ y = this.getTick(y, this.yTicks);
+
+
+ return {x:x, y:y};
+ },
+
+ /*
+ * Sets up config options specific to this class. Overrides
+ * YAHOO.util.DragDrop, but all versions of this method through the
+ * inheritance chain are called
+ */
+ applyConfig: function() {
+ YAHOO.util.DD.superclass.applyConfig.call(this);
+ this.scroll = (this.config.scroll !== false);
+ },
+
+ /*
+ * Event that fires prior to the onMouseDown event. Overrides
+ * YAHOO.util.DragDrop.
+ */
+ b4MouseDown: function(e) {
+ // this.resetConstraints();
+ this.autoOffset(YAHOO.util.Event.getPageX(e),
+ YAHOO.util.Event.getPageY(e));
+ },
+
+ /*
+ * Event that fires prior to the onDrag event. Overrides
+ * YAHOO.util.DragDrop.
+ */
+ b4Drag: function(e) {
+ this.setDragElPos(YAHOO.util.Event.getPageX(e),
+ YAHOO.util.Event.getPageY(e));
+ },
+
+ toString: function() {
+ return ("DD " + this.id);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Debugging ygDragDrop events that can be overridden
+ //////////////////////////////////////////////////////////////////////////
+ /*
+ startDrag: function(x, y) {
+ },
+
+ onDrag: function(e) {
+ },
+
+ onDragEnter: function(e, id) {
+ },
+
+ onDragOver: function(e, id) {
+ },
+
+ onDragOut: function(e, id) {
+ },
+
+ onDragDrop: function(e, id) {
+ },
+
+ endDrag: function(e) {
+ }
+
+ */
+
+});
+/**
+ * A DragDrop implementation that inserts an empty, bordered div into
+ * the document that follows the cursor during drag operations. At the time of
+ * the click, the frame div is resized to the dimensions of the linked html
+ * element, and moved to the exact location of the linked element.
+ *
+ * References to the "frame" element refer to the single proxy element that
+ * was created to be dragged in place of all DDProxy elements on the
+ * page.
+ *
+ * @class DDProxy
+ * @extends YAHOO.util.DD
+ * @constructor
+ * @param {String} id the id of the linked html element
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ * Valid properties for DDProxy in addition to those in DragDrop:
+ * resizeFrame, centerFrame, dragElId
+ */
+YAHOO.util.DDProxy = function(id, sGroup, config) {
+ if (id) {
+ this.init(id, sGroup, config);
+ this.initFrame();
+ }
+};
+
+/**
+ * The default drag frame div id
+ * @property YAHOO.util.DDProxy.dragElId
+ * @type String
+ * @static
+ */
+YAHOO.util.DDProxy.dragElId = "ygddfdiv";
+
+YAHOO.extend(YAHOO.util.DDProxy, YAHOO.util.DD, {
+
+ /**
+ * By default we resize the drag frame to be the same size as the element
+ * we want to drag (this is to get the frame effect). We can turn it off
+ * if we want a different behavior.
+ * @property resizeFrame
+ * @type boolean
+ */
+ resizeFrame: true,
+
+ /**
+ * By default the frame is positioned exactly where the drag element is, so
+ * we use the cursor offset provided by YAHOO.util.DD. Another option that works only if
+ * you do not have constraints on the obj is to have the drag frame centered
+ * around the cursor. Set centerFrame to true for this effect.
+ * @property centerFrame
+ * @type boolean
+ */
+ centerFrame: false,
+
+ /**
+ * Creates the proxy element if it does not yet exist
+ * @method createFrame
+ */
+ createFrame: function() {
+ var self = this;
+ var body = document.body;
+
+ if (!body || !body.firstChild) {
+ setTimeout( function() { self.createFrame(); }, 50 );
+ return;
+ }
+
+ var div = this.getDragEl();
+
+ if (!div) {
+ div = document.createElement("div");
+ div.id = this.dragElId;
+ var s = div.style;
+
+ s.position = "absolute";
+ s.visibility = "hidden";
+ s.cursor = "move";
+ s.border = "2px solid #aaa";
+ s.zIndex = 999;
+
+ // appendChild can blow up IE if invoked prior to the window load event
+ // while rendering a table. It is possible there are other scenarios
+ // that would cause this to happen as well.
+ body.insertBefore(div, body.firstChild);
+ }
+ },
+
+ /**
+ * Initialization for the drag frame element. Must be called in the
+ * constructor of all subclasses
+ * @method initFrame
+ */
+ initFrame: function() {
+ this.createFrame();
+ },
+
+ applyConfig: function() {
+ YAHOO.util.DDProxy.superclass.applyConfig.call(this);
+
+ this.resizeFrame = (this.config.resizeFrame !== false);
+ this.centerFrame = (this.config.centerFrame);
+ this.setDragElId(this.config.dragElId || YAHOO.util.DDProxy.dragElId);
+ },
+
+ /**
+ * Resizes the drag frame to the dimensions of the clicked object, positions
+ * it over the object, and finally displays it
+ * @method showFrame
+ * @param {int} iPageX X click position
+ * @param {int} iPageY Y click position
+ * @private
+ */
+ showFrame: function(iPageX, iPageY) {
+ var el = this.getEl();
+ var dragEl = this.getDragEl();
+ var s = dragEl.style;
+
+ this._resizeProxy();
+
+ if (this.centerFrame) {
+ this.setDelta( Math.round(parseInt(s.width, 10)/2),
+ Math.round(parseInt(s.height, 10)/2) );
+ }
+
+ this.setDragElPos(iPageX, iPageY);
+
+ YAHOO.util.Dom.setStyle(dragEl, "visibility", "visible");
+ },
+
+ /**
+ * The proxy is automatically resized to the dimensions of the linked
+ * element when a drag is initiated, unless resizeFrame is set to false
+ * @method _resizeProxy
+ * @private
+ */
+ _resizeProxy: function() {
+ if (this.resizeFrame) {
+ var DOM = YAHOO.util.Dom;
+ var el = this.getEl();
+ var dragEl = this.getDragEl();
+
+ var bt = parseInt( DOM.getStyle(dragEl, "borderTopWidth" ), 10);
+ var br = parseInt( DOM.getStyle(dragEl, "borderRightWidth" ), 10);
+ var bb = parseInt( DOM.getStyle(dragEl, "borderBottomWidth" ), 10);
+ var bl = parseInt( DOM.getStyle(dragEl, "borderLeftWidth" ), 10);
+
+ if (isNaN(bt)) { bt = 0; }
+ if (isNaN(br)) { br = 0; }
+ if (isNaN(bb)) { bb = 0; }
+ if (isNaN(bl)) { bl = 0; }
+
+
+ var newWidth = Math.max(0, el.offsetWidth - br - bl);
+ var newHeight = Math.max(0, el.offsetHeight - bt - bb);
+
+
+ DOM.setStyle( dragEl, "width", newWidth + "px" );
+ DOM.setStyle( dragEl, "height", newHeight + "px" );
+ }
+ },
+
+ // overrides YAHOO.util.DragDrop
+ b4MouseDown: function(e) {
+ var x = YAHOO.util.Event.getPageX(e);
+ var y = YAHOO.util.Event.getPageY(e);
+ this.autoOffset(x, y);
+ this.setDragElPos(x, y);
+ },
+
+ // overrides YAHOO.util.DragDrop
+ b4StartDrag: function(x, y) {
+ // show the drag frame
+ this.showFrame(x, y);
+ },
+
+ // overrides YAHOO.util.DragDrop
+ b4EndDrag: function(e) {
+ YAHOO.util.Dom.setStyle(this.getDragEl(), "visibility", "hidden");
+ },
+
+ // overrides YAHOO.util.DragDrop
+ // By default we try to move the element to the last location of the frame.
+ // This is so that the default behavior mirrors that of YAHOO.util.DD.
+ endDrag: function(e) {
+ var DOM = YAHOO.util.Dom;
+ var lel = this.getEl();
+ var del = this.getDragEl();
+
+ // Show the drag frame briefly so we can get its position
+ // del.style.visibility = "";
+ DOM.setStyle(del, "visibility", "");
+
+ // Hide the linked element before the move to get around a Safari
+ // rendering bug.
+ //lel.style.visibility = "hidden";
+ DOM.setStyle(lel, "visibility", "hidden");
+ YAHOO.util.DDM.moveToEl(lel, del);
+ //del.style.visibility = "hidden";
+ DOM.setStyle(del, "visibility", "hidden");
+ //lel.style.visibility = "";
+ DOM.setStyle(lel, "visibility", "");
+ },
+
+ toString: function() {
+ return ("DDProxy " + this.id);
+ }
+
+});
+/**
+ * A DragDrop implementation that does not move, but can be a drop
+ * target. You would get the same result by simply omitting implementation
+ * for the event callbacks, but this way we reduce the processing cost of the
+ * event listener and the callbacks.
+ * @class DDTarget
+ * @extends YAHOO.util.DragDrop
+ * @constructor
+ * @param {String} id the id of the element that is a drop target
+ * @param {String} sGroup the group of related DragDrop objects
+ * @param {object} config an object containing configurable attributes
+ * Valid properties for DDTarget in addition to those in
+ * DragDrop:
+ * none
+ */
+YAHOO.util.DDTarget = function(id, sGroup, config) {
+ if (id) {
+ this.initTarget(id, sGroup, config);
+ }
+};
+
+// YAHOO.util.DDTarget.prototype = new YAHOO.util.DragDrop();
+YAHOO.extend(YAHOO.util.DDTarget, YAHOO.util.DragDrop, {
+ toString: function() {
+ return ("DDTarget " + this.id);
+ }
+});
diff --git a/source/web/scripts/ajax/yahoo/event/README b/source/web/scripts/ajax/yahoo/event/README
new file mode 100644
index 0000000000..e6b1f93497
--- /dev/null
+++ b/source/web/scripts/ajax/yahoo/event/README
@@ -0,0 +1,122 @@
+
+YUI Library - Event - Release Notes
+
+0.12.0
+
+ * If the function argument is not provided to Event.removeListener, all
+ all listeners for the specified event type on the element will be removed.
+
+ * CustomEvent now has an optional parameter that defines the signature of
+ the listeners for this event. Two signatures are supported:
+
+ YAHOO.util.CustomEvent.LIST:
+ param1: event name
+ param2: array of arguments provided to fire()
+ param3: ", + sourceAndDetail, + ":
", + msg, + "
"] : + + ["", label, " ", + totalTime, "ms (+", elapsedTime, ") ", + localTime, ": ", + sourceAndDetail, ": ", + msg,"
"]; + + return output.join(""); +}; + +/** + * Converts input chars "<", ">", and "&" to HTML entities. + * + * @method html2Text + * @param sHtml {String} String to convert. + * @private + */ +YAHOO.widget.LogReader.prototype.html2Text = function(sHtml) { + if(sHtml) { + sHtml += ""; + return sHtml.replace(/&/g, "&").replace(//g, ">"); + } + return ""; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Private member variables +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Internal class member to index multiple log reader instances. + * + * @property _memberName + * @static + * @type Number + * @default 0 + * @private + */ +YAHOO.widget.LogReader._index = 0; + +/** + * Name of LogReader instance. + * + * @property _sName + * @type String + * @private + */ +YAHOO.widget.LogReader.prototype._sName = null; + +/** + * A class member shared by all log readers if a container needs to be + * created during instantiation. Will be null if a container element never needs to + * be created on the fly, such as when the implementer passes in their own element. + * + * @property _elDefaultContainer + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader._elDefaultContainer = null; + +/** + * Buffer of log message objects for batch output. + * + * @property _buffer + * @type Object[] + * @private + */ +YAHOO.widget.LogReader.prototype._buffer = null; + +/** + * Number of log messages output to console. + * + * @property _consoleMsgCount + * @type Number + * @default 0 + * @private + */ +YAHOO.widget.LogReader.prototype._consoleMsgCount = 0; + +/** + * Date of last output log message. + * + * @property _lastTime + * @type Date + * @private + */ +YAHOO.widget.LogReader.prototype._lastTime = null; + +/** + * Batched output timeout ID. + * + * @property _timeout + * @type Number + * @private + */ +YAHOO.widget.LogReader.prototype._timeout = null; + +/** + * Array of filters for log message categories. + * + * @property _categoryFilters + * @type String[] + * @private + */ +YAHOO.widget.LogReader.prototype._categoryFilters = null; + +/** + * Array of filters for log message sources. + * + * @property _sourceFilters + * @type String[] + * @private + */ +YAHOO.widget.LogReader.prototype._sourceFilters = null; + +/** + * Log reader container element. + * + * @property _elContainer + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elContainer = null; + +/** + * Log reader header element. + * + * @property _elHd + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elHd = null; + +/** + * Log reader collapse element. + * + * @property _elCollapse + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elCollapse = null; + +/** + * Log reader collapse button element. + * + * @property _btnCollapse + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._btnCollapse = null; + +/** + * Log reader title header element. + * + * @property _title + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._title = null; + +/** + * Log reader console element. + * + * @property _elConsole + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elConsole = null; + +/** + * Log reader footer element. + * + * @property _elFt + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elFt = null; + +/** + * Log reader buttons container element. + * + * @property _elBtns + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elBtns = null; + +/** + * Container element for log reader category filter checkboxes. + * + * @property _elCategoryFilters + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elCategoryFilters = null; + +/** + * Container element for log reader source filter checkboxes. + * + * @property _elSourceFilters + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elSourceFilters = null; + +/** + * Log reader pause button element. + * + * @property _btnPause + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._btnPause = null; + +/** + * Clear button element. + * + * @property _btnClear + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._btnClear = null; + +///////////////////////////////////////////////////////////////////////////// +// +// Private methods +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Creates the UI for a category filter in the log reader footer element. + * + * @method _createCategoryCheckbox + * @param sCategory {String} Category name. + * @private + */ +YAHOO.widget.LogReader.prototype._createCategoryCheckbox = function(sCategory) { + var oSelf = this; + + if(this._elFt) { + var elParent = this._elCategoryFilters; + var filters = this._categoryFilters; + + var elFilter = elParent.appendChild(document.createElement("span")); + elFilter.className = "yui-log-filtergrp"; + // Append el at the end so IE 5.5 can set "type" attribute + // and THEN set checked property + var chkCategory = document.createElement("input"); + chkCategory.id = "yui-log-filter-" + sCategory + this._sName; + chkCategory.className = "yui-log-filter-" + sCategory; + chkCategory.type = "checkbox"; + chkCategory.category = sCategory; + chkCategory = elFilter.appendChild(chkCategory); + chkCategory.checked = true; + + // Add this checked filter to the internal array of filters + filters.push(sCategory); + // Subscribe to the click event + YAHOO.util.Event.addListener(chkCategory,'click',oSelf._onCheckCategory,oSelf); + + // Create and class the text label + var lblCategory = elFilter.appendChild(document.createElement("label")); + lblCategory.htmlFor = chkCategory.id; + lblCategory.className = sCategory; + lblCategory.innerHTML = sCategory; + } +}; + +/** + * Creates a checkbox in the log reader footer element to filter by source. + * + * @method _createSourceCheckbox + * @param sSource {String} Source name. + * @private + */ +YAHOO.widget.LogReader.prototype._createSourceCheckbox = function(sSource) { + var oSelf = this; + + if(this._elFt) { + var elParent = this._elSourceFilters; + var filters = this._sourceFilters; + + var elFilter = elParent.appendChild(document.createElement("span")); + elFilter.className = "yui-log-filtergrp"; + + // Append el at the end so IE 5.5 can set "type" attribute + // and THEN set checked property + var chkSource = document.createElement("input"); + chkSource.id = "yui-log-filter" + sSource + this._sName; + chkSource.className = "yui-log-filter" + sSource; + chkSource.type = "checkbox"; + chkSource.source = sSource; + chkSource = elFilter.appendChild(chkSource); + chkSource.checked = true; + + // Add this checked filter to the internal array of filters + filters.push(sSource); + // Subscribe to the click event + YAHOO.util.Event.addListener(chkSource,'click',oSelf._onCheckSource,oSelf); + + // Create and class the text label + var lblSource = elFilter.appendChild(document.createElement("label")); + lblSource.htmlFor = chkSource.id; + lblSource.className = sSource; + lblSource.innerHTML = sSource; + } +}; + +/** + * Reprints all log messages in the stack through filters. + * + * @method _filterLogs + * @private + */ +YAHOO.widget.LogReader.prototype._filterLogs = function() { + // Reprint stack with new filters + if (this._elConsole !== null) { + this._clearConsole(); + this._printToConsole(YAHOO.widget.Logger.getStack()); + } +}; + +/** + * Clears all outputted log messages from the console and resets the time of the + * last output log message. + * + * @method _clearConsole + * @private + */ +YAHOO.widget.LogReader.prototype._clearConsole = function() { + // Clear the buffer of any pending messages + this._timeout = null; + this._buffer = []; + this._consoleMsgCount = 0; + + // Reset the rolling timer + this._lastTime = YAHOO.widget.Logger.getStartTime(); + + var elConsole = this._elConsole; + while(elConsole.hasChildNodes()) { + elConsole.removeChild(elConsole.firstChild); + } +}; + +/** + * Sends buffer of log messages to output and clears buffer. + * + * @method _printBuffer + * @private + */ +YAHOO.widget.LogReader.prototype._printBuffer = function() { + this._timeout = null; + + if(this._elConsole !== null) { + var thresholdMax = this.thresholdMax; + thresholdMax = (thresholdMax && !isNaN(thresholdMax)) ? thresholdMax : 500; + if(this._consoleMsgCount < thresholdMax) { + var entries = []; + for (var i=0; itag (for wrapping) + var container = (this.verboseOutput) ? "CODE" : "PRE"; + var oNewElement = (this.newestOnTop) ? + this._elConsole.insertBefore( + document.createElement(container),this._elConsole.firstChild): + this._elConsole.appendChild(document.createElement(container)); + + oNewElement.innerHTML = output; + this._consoleMsgCount++; + this._lastTime = entry.time.getTime(); + } + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Private event handlers +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Handles Logger's categoryCreateEvent. + * + * @method _onCategoryCreate + * @param sType {String} The event. + * @param aArgs {Object[]} Data passed from event firer. + * @param oSelf {Object} The LogReader instance. + * @private + */ +YAHOO.widget.LogReader.prototype._onCategoryCreate = function(sType, aArgs, oSelf) { + var category = aArgs[0]; + if(oSelf._elFt) { + oSelf._createCategoryCheckbox(category); + } +}; + +/** + * Handles Logger's sourceCreateEvent. + * + * @method _onSourceCreate + * @param sType {String} The event. + * @param aArgs {Object[]} Data passed from event firer. + * @param oSelf {Object} The LogReader instance. + * @private + */ +YAHOO.widget.LogReader.prototype._onSourceCreate = function(sType, aArgs, oSelf) { + var source = aArgs[0]; + if(oSelf._elFt) { + oSelf._createSourceCheckbox(source); + } +}; + +/** + * Handles check events on the category filter checkboxes. + * + * @method _onCheckCategory + * @param v {HTMLEvent} The click event. + * @param oSelf {Object} The LogReader instance. + * @private + */ +YAHOO.widget.LogReader.prototype._onCheckCategory = function(v, oSelf) { + var newFilter = this.category; + var filtersArray = oSelf._categoryFilters; + + if(!this.checked) { // Remove category from filters + for(var i=0; i+ * Either of the above would create YAHOO.property, then + * YAHOO.property.package + * + * Be careful when naming packages. Reserved words may work in some browsers + * and not others. For instance, the following will fail in Safari: + *0) { + // Substring until first space + sClass = sSource.substring(0,spaceIndex); + // The rest of the source + sDetail = sSource.substring(spaceIndex,sSource.length); + } + else { + sClass = sSource; + } + if(this._isNewSource(sClass)) { + this._createNewSource(sClass); + } + } + + var timestamp = new Date(); + var logEntry = new YAHOO.widget.LogMsg({ + msg: sMsg, + time: timestamp, + category: sCategory, + source: sClass, + sourceDetail: sDetail + }); + + var stack = this._stack; + var maxStackEntries = this.maxStackEntries; + if(maxStackEntries && !isNaN(maxStackEntries) && + (stack.length >= maxStackEntries)) { + stack.shift(); + } + stack.push(logEntry); + this.newLogEvent.fire(logEntry); + + if(this._browserConsoleEnabled) { + this._printToBrowserConsole(logEntry); + } + return true; + } + else { + return false; + } +}; + +/** + * Resets internal stack and startTime, enables Logger, and fires logResetEvent. + * + * @method reset + */ +YAHOO.widget.Logger.reset = function() { + this._stack = []; + this._startTime = new Date().getTime(); + this.loggerEnabled = true; + this.log("Logger reset"); + this.logResetEvent.fire(); +}; + +/** + * Public accessor to internal stack of log message objects. + * + * @method getStack + * @return {Object[]} Array of log message objects. + */ +YAHOO.widget.Logger.getStack = function() { + return this._stack; +}; + +/** + * Public accessor to internal start time. + * + * @method getStartTime + * @return {Date} Internal date of when Logger singleton was initialized. + */ +YAHOO.widget.Logger.getStartTime = function() { + return this._startTime; +}; + +/** + * Disables output to the browser's global console.log() function, which is used + * by the Firebug extension to Firefox as well as Safari. + * + * @method disableBrowserConsole + */ +YAHOO.widget.Logger.disableBrowserConsole = function() { + YAHOO.log("Logger output to the function console.log() has been disabled."); + this._browserConsoleEnabled = false; +}; + +/** + * Enables output to the browser's global console.log() function, which is used + * by the Firebug extension to Firefox as well as Safari. + * + * @method enableBrowserConsole + */ +YAHOO.widget.Logger.enableBrowserConsole = function() { + this._browserConsoleEnabled = true; + YAHOO.log("Logger output to the function console.log() has been enabled."); +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Public events +// +///////////////////////////////////////////////////////////////////////////// + + /** + * Fired when a new category has been created. + * + * @event categoryCreateEvent + * @param sCategory {String} Category name. + */ +YAHOO.widget.Logger.categoryCreateEvent = + new YAHOO.util.CustomEvent("categoryCreate", this, true); + + /** + * Fired when a new source has been named. + * + * @event sourceCreateEvent + * @param sSource {String} Source name. + */ +YAHOO.widget.Logger.sourceCreateEvent = + new YAHOO.util.CustomEvent("sourceCreate", this, true); + + /** + * Fired when a new log message has been created. + * + * @event newLogEvent + * @param sMsg {String} Log message. + */ +YAHOO.widget.Logger.newLogEvent = new YAHOO.util.CustomEvent("newLog", this, true); + +/** + * Fired when the Logger has been reset has been created. + * + * @event logResetEvent + */ +YAHOO.widget.Logger.logResetEvent = new YAHOO.util.CustomEvent("logReset", this, true); + +///////////////////////////////////////////////////////////////////////////// +// +// Private methods +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Creates a new category of log messages and fires categoryCreateEvent. + * + * @method _createNewCategory + * @param sCategory {String} Category name. + * @private + */ +YAHOO.widget.Logger._createNewCategory = function(sCategory) { + this.categories.push(sCategory); + this.categoryCreateEvent.fire(sCategory); +}; + +/** + * Checks to see if a category has already been created. + * + * @method _isNewCategory + * @param sCategory {String} Category name. + * @return {Boolean} Returns true if category is unknown, else returns false. + * @private + */ +YAHOO.widget.Logger._isNewCategory = function(sCategory) { + for(var i=0; i < this.categories.length; i++) { + if(sCategory == this.categories[i]) { + return false; + } + } + return true; +}; + +/** + * Creates a new source of log messages and fires sourceCreateEvent. + * + * @method _createNewSource + * @param sSource {String} Source name. + * @private + */ +YAHOO.widget.Logger._createNewSource = function(sSource) { + this.sources.push(sSource); + this.sourceCreateEvent.fire(sSource); +}; + +/** + * Checks to see if a source already exists. + * + * @method _isNewSource + * @param sSource {String} Source name. + * @return {Boolean} Returns true if source is unknown, else returns false. + * @private + */ +YAHOO.widget.Logger._isNewSource = function(sSource) { + if(sSource) { + for(var i=0; i < this.sources.length; i++) { + if(sSource == this.sources[i]) { + return false; + } + } + return true; + } +}; + +/** + * Outputs a log message to global console.log() function. + * + * @method _printToBrowserConsole + * @param oEntry {Object} Log entry object. + * @private + */ +YAHOO.widget.Logger._printToBrowserConsole = function(oEntry) { + if(window.console && console.log) { + var category = oEntry.category; + var label = oEntry.category.substring(0,4).toUpperCase(); + + var time = oEntry.time; + if (time.toLocaleTimeString) { + var localTime = time.toLocaleTimeString(); + } + else { + localTime = time.toString(); + } + + var msecs = time.getTime(); + var elapsedTime = (YAHOO.widget.Logger._lastTime) ? + (msecs - YAHOO.widget.Logger._lastTime) : 0; + YAHOO.widget.Logger._lastTime = msecs; + + var output = + localTime + " (" + + elapsedTime + "ms): " + + oEntry.source + ": " + + oEntry.msg; + + console.log(output); + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Private event handlers +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Handles logging of messages due to window error events. + * + * @method _onWindowError + * @param sMsg {String} The error message. + * @param sUrl {String} URL of the error. + * @param sLine {String} Line number of the error. + * @private + */ +YAHOO.widget.Logger._onWindowError = function(sMsg,sUrl,sLine) { + // Logger is not in scope of this event handler + try { + YAHOO.widget.Logger.log(sMsg+' ('+sUrl+', line '+sLine+')', "window"); + if(YAHOO.widget.Logger._origOnWindowError) { + YAHOO.widget.Logger._origOnWindowError(); + } + } + catch(e) { + return false; + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Enable handling of native JavaScript errors +// NB: Not all browsers support the window.onerror event +// +///////////////////////////////////////////////////////////////////////////// + +if(window.onerror) { + // Save any previously defined handler to call + YAHOO.widget.Logger._origOnWindowError = window.onerror; +} +window.onerror = YAHOO.widget.Logger._onWindowError; + +///////////////////////////////////////////////////////////////////////////// +// +// First log +// +///////////////////////////////////////////////////////////////////////////// + +YAHOO.widget.Logger.log("Logger initialized"); + diff --git a/source/web/scripts/ajax/yahoo/logger/logger-min.js b/source/web/scripts/ajax/yahoo/logger/logger-min.js new file mode 100644 index 0000000000..f7c0e637cc --- /dev/null +++ b/source/web/scripts/ajax/yahoo/logger/logger-min.js @@ -0,0 +1,56 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ +YAHOO.widget.LogMsg=function(oConfigs){if(typeof oConfigs=="object"){for(var param in oConfigs){this[param]=oConfigs[param];}}};YAHOO.widget.LogMsg.prototype.msg=null;YAHOO.widget.LogMsg.prototype.time=null;YAHOO.widget.LogMsg.prototype.category=null;YAHOO.widget.LogMsg.prototype.source=null;YAHOO.widget.LogMsg.prototype.sourceDetail=null;YAHOO.widget.LogWriter=function(sSource){if(!sSource){YAHOO.log("Could not instantiate LogWriter due to invalid source.","error","LogWriter");return;} +this._source=sSource;};YAHOO.widget.LogWriter.prototype.toString=function(){return"LogWriter "+this._sSource;};YAHOO.widget.LogWriter.prototype.log=function(sMsg,sCategory){YAHOO.widget.Logger.log(sMsg,sCategory,this._source);};YAHOO.widget.LogWriter.prototype.getSource=function(){return this._sSource;};YAHOO.widget.LogWriter.prototype.setSource=function(sSource){if(!sSource){YAHOO.log("Could not set source due to invalid source.","error",this.toString());return;} +else{this._sSource=sSource;}};YAHOO.widget.LogWriter.prototype._source=null;YAHOO.widget.LogReader=function(elContainer,oConfigs){var oSelf=this;this._sName=YAHOO.widget.LogReader._index;YAHOO.widget.LogReader._index++;if(typeof oConfigs=="object"){for(var param in oConfigs){this[param]=oConfigs[param];}} +if(elContainer){if(typeof elContainer=="string"){this._elContainer=document.getElementById(elContainer);} +else if(elContainer.tagName){this._elContainer=elContainer;} +this._elContainer.className="yui-log";} +if(!this._elContainer){if(YAHOO.widget.LogReader._elDefaultContainer){this._elContainer=YAHOO.widget.LogReader._elDefaultContainer;} +else{this._elContainer=document.body.appendChild(document.createElement("div"));this._elContainer.id="yui-log";this._elContainer.className="yui-log";YAHOO.widget.LogReader._elDefaultContainer=this._elContainer;} +var containerStyle=this._elContainer.style;if(this.width){containerStyle.width=this.width;} +if(this.left){containerStyle.left=this.left;} +if(this.right){containerStyle.right=this.right;} +if(this.bottom){containerStyle.bottom=this.bottom;} +if(this.top){containerStyle.top=this.top;} +if(this.fontSize){containerStyle.fontSize=this.fontSize;}} +if(this._elContainer){if(!this._elHd){this._elHd=this._elContainer.appendChild(document.createElement("div"));this._elHd.id="yui-log-hd"+this._sName;this._elHd.className="yui-log-hd";this._elCollapse=this._elHd.appendChild(document.createElement("div"));this._elCollapse.className="yui-log-btns";this._btnCollapse=document.createElement("input");this._btnCollapse.type="button";this._btnCollapse.style.fontSize=YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");this._btnCollapse.className="yui-log-button";this._btnCollapse.value="Collapse";this._btnCollapse=this._elCollapse.appendChild(this._btnCollapse);YAHOO.util.Event.addListener(oSelf._btnCollapse,'click',oSelf._onClickCollapseBtn,oSelf);this._title=this._elHd.appendChild(document.createElement("h4"));this._title.innerHTML="Logger Console";if(YAHOO.util.DD&&(YAHOO.widget.LogReader._elDefaultContainer==this._elContainer)){var ylog_dd=new YAHOO.util.DD(this._elContainer.id);ylog_dd.setHandleElId(this._elHd.id);this._elHd.style.cursor="move";}} +if(!this._elConsole){this._elConsole=this._elContainer.appendChild(document.createElement("div"));this._elConsole.className="yui-log-bd";if(this.height){this._elConsole.style.height=this.height;}} +if(!this._elFt&&this.footerEnabled){this._elFt=this._elContainer.appendChild(document.createElement("div"));this._elFt.className="yui-log-ft";this._elBtns=this._elFt.appendChild(document.createElement("div"));this._elBtns.className="yui-log-btns";this._btnPause=document.createElement("input");this._btnPause.type="button";this._btnPause.style.fontSize=YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");this._btnPause.className="yui-log-button";this._btnPause.value="Pause";this._btnPause=this._elBtns.appendChild(this._btnPause);YAHOO.util.Event.addListener(oSelf._btnPause,'click',oSelf._onClickPauseBtn,oSelf);this._btnClear=document.createElement("input");this._btnClear.type="button";this._btnClear.style.fontSize=YAHOO.util.Dom.getStyle(this._elContainer,"fontSize");this._btnClear.className="yui-log-button";this._btnClear.value="Clear";this._btnClear=this._elBtns.appendChild(this._btnClear);YAHOO.util.Event.addListener(oSelf._btnClear,'click',oSelf._onClickClearBtn,oSelf);this._elCategoryFilters=this._elFt.appendChild(document.createElement("div"));this._elCategoryFilters.className="yui-log-categoryfilters";this._elSourceFilters=this._elFt.appendChild(document.createElement("div"));this._elSourceFilters.className="yui-log-sourcefilters";}} +if(!this._buffer){this._buffer=[];} +this._lastTime=YAHOO.widget.Logger.getStartTime();YAHOO.widget.Logger.newLogEvent.subscribe(this._onNewLog,this);YAHOO.widget.Logger.logResetEvent.subscribe(this._onReset,this);this._categoryFilters=[];var catsLen=YAHOO.widget.Logger.categories.length;if(this._elCategoryFilters){for(var i=0;i ",label," ",totalTime,"ms (+",elapsedTime,") ",localTime,": "," ",sourceAndDetail,":
",msg,"
"]:["",label," ",totalTime,"ms (+",elapsedTime,") ",localTime,": ",sourceAndDetail,": ",msg,"
"];return output.join("");};YAHOO.widget.LogReader.prototype.html2Text=function(sHtml){if(sHtml){sHtml+="";return sHtml.replace(/&/g,"&").replace(//g,">");} +return"";};YAHOO.widget.LogReader._index=0;YAHOO.widget.LogReader.prototype._sName=null;YAHOO.widget.LogReader._elDefaultContainer=null;YAHOO.widget.LogReader.prototype._buffer=null;YAHOO.widget.LogReader.prototype._consoleMsgCount=0;YAHOO.widget.LogReader.prototype._lastTime=null;YAHOO.widget.LogReader.prototype._timeout=null;YAHOO.widget.LogReader.prototype._categoryFilters=null;YAHOO.widget.LogReader.prototype._sourceFilters=null;YAHOO.widget.LogReader.prototype._elContainer=null;YAHOO.widget.LogReader.prototype._elHd=null;YAHOO.widget.LogReader.prototype._elCollapse=null;YAHOO.widget.LogReader.prototype._btnCollapse=null;YAHOO.widget.LogReader.prototype._title=null;YAHOO.widget.LogReader.prototype._elConsole=null;YAHOO.widget.LogReader.prototype._elFt=null;YAHOO.widget.LogReader.prototype._elBtns=null;YAHOO.widget.LogReader.prototype._elCategoryFilters=null;YAHOO.widget.LogReader.prototype._elSourceFilters=null;YAHOO.widget.LogReader.prototype._btnPause=null;YAHOO.widget.LogReader.prototype._btnClear=null;YAHOO.widget.LogReader.prototype._createCategoryCheckbox=function(sCategory){var oSelf=this;if(this._elFt){var elParent=this._elCategoryFilters;var filters=this._categoryFilters;var elFilter=elParent.appendChild(document.createElement("span"));elFilter.className="yui-log-filtergrp";var chkCategory=document.createElement("input");chkCategory.id="yui-log-filter-"+sCategory+this._sName;chkCategory.className="yui-log-filter-"+sCategory;chkCategory.type="checkbox";chkCategory.category=sCategory;chkCategory=elFilter.appendChild(chkCategory);chkCategory.checked=true;filters.push(sCategory);YAHOO.util.Event.addListener(chkCategory,'click',oSelf._onCheckCategory,oSelf);var lblCategory=elFilter.appendChild(document.createElement("label"));lblCategory.htmlFor=chkCategory.id;lblCategory.className=sCategory;lblCategory.innerHTML=sCategory;}};YAHOO.widget.LogReader.prototype._createSourceCheckbox=function(sSource){var oSelf=this;if(this._elFt){var elParent=this._elSourceFilters;var filters=this._sourceFilters;var elFilter=elParent.appendChild(document.createElement("span"));elFilter.className="yui-log-filtergrp";var chkSource=document.createElement("input");chkSource.id="yui-log-filter"+sSource+this._sName;chkSource.className="yui-log-filter"+sSource;chkSource.type="checkbox";chkSource.source=sSource;chkSource=elFilter.appendChild(chkSource);chkSource.checked=true;filters.push(sSource);YAHOO.util.Event.addListener(chkSource,'click',oSelf._onCheckSource,oSelf);var lblSource=elFilter.appendChild(document.createElement("label"));lblSource.htmlFor=chkSource.id;lblSource.className=sSource;lblSource.innerHTML=sSource;}};YAHOO.widget.LogReader.prototype._filterLogs=function(){if(this._elConsole!==null){this._clearConsole();this._printToConsole(YAHOO.widget.Logger.getStack());}};YAHOO.widget.LogReader.prototype._clearConsole=function(){this._timeout=null;this._buffer=[];this._consoleMsgCount=0;this._lastTime=YAHOO.widget.Logger.getStartTime();var elConsole=this._elConsole;while(elConsole.hasChildNodes()){elConsole.removeChild(elConsole.firstChild);}};YAHOO.widget.LogReader.prototype._printBuffer=function(){this._timeout=null;if(this._elConsole!==null){var thresholdMax=this.thresholdMax;thresholdMax=(thresholdMax&&!isNaN(thresholdMax))?thresholdMax:500;if(this._consoleMsgCountthis.thresholdMax)){thresholdMin=0;} +var entriesStartIndex=(entriesLen>thresholdMin)?(entriesLen-thresholdMin):0;var sourceFiltersLen=this._sourceFilters.length;var categoryFiltersLen=this._categoryFilters.length;for(var i=entriesStartIndex;i 0){sClass=sSource.substring(0,spaceIndex);sDetail=sSource.substring(spaceIndex,sSource.length);} +else{sClass=sSource;} +if(this._isNewSource(sClass)){this._createNewSource(sClass);}} +var timestamp=new Date();var logEntry=new YAHOO.widget.LogMsg({msg:sMsg,time:timestamp,category:sCategory,source:sClass,sourceDetail:sDetail});var stack=this._stack;var maxStackEntries=this.maxStackEntries;if(maxStackEntries&&!isNaN(maxStackEntries)&&(stack.length>=maxStackEntries)){stack.shift();} +stack.push(logEntry);this.newLogEvent.fire(logEntry);if(this._browserConsoleEnabled){this._printToBrowserConsole(logEntry);} +return true;} +else{return false;}};YAHOO.widget.Logger.reset=function(){this._stack=[];this._startTime=new Date().getTime();this.loggerEnabled=true;this.log("Logger reset");this.logResetEvent.fire();};YAHOO.widget.Logger.getStack=function(){return this._stack;};YAHOO.widget.Logger.getStartTime=function(){return this._startTime;};YAHOO.widget.Logger.disableBrowserConsole=function(){YAHOO.log("Logger output to the function console.log() has been disabled.");this._browserConsoleEnabled=false;};YAHOO.widget.Logger.enableBrowserConsole=function(){this._browserConsoleEnabled=true;YAHOO.log("Logger output to the function console.log() has been enabled.");};YAHOO.widget.Logger.categoryCreateEvent=new YAHOO.util.CustomEvent("categoryCreate",this,true);YAHOO.widget.Logger.sourceCreateEvent=new YAHOO.util.CustomEvent("sourceCreate",this,true);YAHOO.widget.Logger.newLogEvent=new YAHOO.util.CustomEvent("newLog",this,true);YAHOO.widget.Logger.logResetEvent=new YAHOO.util.CustomEvent("logReset",this,true);YAHOO.widget.Logger._createNewCategory=function(sCategory){this.categories.push(sCategory);this.categoryCreateEvent.fire(sCategory);};YAHOO.widget.Logger._isNewCategory=function(sCategory){for(var i=0;i ", label, " ", + totalTime, "ms (+", elapsedTime, ") ", + localTime, ": ", + " ", + sourceAndDetail, + ":
", + msg, + "
"] : + + ["", label, " ", + totalTime, "ms (+", elapsedTime, ") ", + localTime, ": ", + sourceAndDetail, ": ", + msg,"
"]; + + return output.join(""); +}; + +/** + * Converts input chars "<", ">", and "&" to HTML entities. + * + * @method html2Text + * @param sHtml {String} String to convert. + * @private + */ +YAHOO.widget.LogReader.prototype.html2Text = function(sHtml) { + if(sHtml) { + sHtml += ""; + return sHtml.replace(/&/g, "&").replace(//g, ">"); + } + return ""; +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Private member variables +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Internal class member to index multiple log reader instances. + * + * @property _memberName + * @static + * @type Number + * @default 0 + * @private + */ +YAHOO.widget.LogReader._index = 0; + +/** + * Name of LogReader instance. + * + * @property _sName + * @type String + * @private + */ +YAHOO.widget.LogReader.prototype._sName = null; + +/** + * A class member shared by all log readers if a container needs to be + * created during instantiation. Will be null if a container element never needs to + * be created on the fly, such as when the implementer passes in their own element. + * + * @property _elDefaultContainer + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader._elDefaultContainer = null; + +/** + * Buffer of log message objects for batch output. + * + * @property _buffer + * @type Object[] + * @private + */ +YAHOO.widget.LogReader.prototype._buffer = null; + +/** + * Number of log messages output to console. + * + * @property _consoleMsgCount + * @type Number + * @default 0 + * @private + */ +YAHOO.widget.LogReader.prototype._consoleMsgCount = 0; + +/** + * Date of last output log message. + * + * @property _lastTime + * @type Date + * @private + */ +YAHOO.widget.LogReader.prototype._lastTime = null; + +/** + * Batched output timeout ID. + * + * @property _timeout + * @type Number + * @private + */ +YAHOO.widget.LogReader.prototype._timeout = null; + +/** + * Array of filters for log message categories. + * + * @property _categoryFilters + * @type String[] + * @private + */ +YAHOO.widget.LogReader.prototype._categoryFilters = null; + +/** + * Array of filters for log message sources. + * + * @property _sourceFilters + * @type String[] + * @private + */ +YAHOO.widget.LogReader.prototype._sourceFilters = null; + +/** + * Log reader container element. + * + * @property _elContainer + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elContainer = null; + +/** + * Log reader header element. + * + * @property _elHd + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elHd = null; + +/** + * Log reader collapse element. + * + * @property _elCollapse + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elCollapse = null; + +/** + * Log reader collapse button element. + * + * @property _btnCollapse + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._btnCollapse = null; + +/** + * Log reader title header element. + * + * @property _title + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._title = null; + +/** + * Log reader console element. + * + * @property _elConsole + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elConsole = null; + +/** + * Log reader footer element. + * + * @property _elFt + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elFt = null; + +/** + * Log reader buttons container element. + * + * @property _elBtns + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elBtns = null; + +/** + * Container element for log reader category filter checkboxes. + * + * @property _elCategoryFilters + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elCategoryFilters = null; + +/** + * Container element for log reader source filter checkboxes. + * + * @property _elSourceFilters + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._elSourceFilters = null; + +/** + * Log reader pause button element. + * + * @property _btnPause + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._btnPause = null; + +/** + * Clear button element. + * + * @property _btnClear + * @type HTMLElement + * @private + */ +YAHOO.widget.LogReader.prototype._btnClear = null; + +///////////////////////////////////////////////////////////////////////////// +// +// Private methods +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Creates the UI for a category filter in the log reader footer element. + * + * @method _createCategoryCheckbox + * @param sCategory {String} Category name. + * @private + */ +YAHOO.widget.LogReader.prototype._createCategoryCheckbox = function(sCategory) { + var oSelf = this; + + if(this._elFt) { + var elParent = this._elCategoryFilters; + var filters = this._categoryFilters; + + var elFilter = elParent.appendChild(document.createElement("span")); + elFilter.className = "yui-log-filtergrp"; + // Append el at the end so IE 5.5 can set "type" attribute + // and THEN set checked property + var chkCategory = document.createElement("input"); + chkCategory.id = "yui-log-filter-" + sCategory + this._sName; + chkCategory.className = "yui-log-filter-" + sCategory; + chkCategory.type = "checkbox"; + chkCategory.category = sCategory; + chkCategory = elFilter.appendChild(chkCategory); + chkCategory.checked = true; + + // Add this checked filter to the internal array of filters + filters.push(sCategory); + // Subscribe to the click event + YAHOO.util.Event.addListener(chkCategory,'click',oSelf._onCheckCategory,oSelf); + + // Create and class the text label + var lblCategory = elFilter.appendChild(document.createElement("label")); + lblCategory.htmlFor = chkCategory.id; + lblCategory.className = sCategory; + lblCategory.innerHTML = sCategory; + } +}; + +/** + * Creates a checkbox in the log reader footer element to filter by source. + * + * @method _createSourceCheckbox + * @param sSource {String} Source name. + * @private + */ +YAHOO.widget.LogReader.prototype._createSourceCheckbox = function(sSource) { + var oSelf = this; + + if(this._elFt) { + var elParent = this._elSourceFilters; + var filters = this._sourceFilters; + + var elFilter = elParent.appendChild(document.createElement("span")); + elFilter.className = "yui-log-filtergrp"; + + // Append el at the end so IE 5.5 can set "type" attribute + // and THEN set checked property + var chkSource = document.createElement("input"); + chkSource.id = "yui-log-filter" + sSource + this._sName; + chkSource.className = "yui-log-filter" + sSource; + chkSource.type = "checkbox"; + chkSource.source = sSource; + chkSource = elFilter.appendChild(chkSource); + chkSource.checked = true; + + // Add this checked filter to the internal array of filters + filters.push(sSource); + // Subscribe to the click event + YAHOO.util.Event.addListener(chkSource,'click',oSelf._onCheckSource,oSelf); + + // Create and class the text label + var lblSource = elFilter.appendChild(document.createElement("label")); + lblSource.htmlFor = chkSource.id; + lblSource.className = sSource; + lblSource.innerHTML = sSource; + } +}; + +/** + * Reprints all log messages in the stack through filters. + * + * @method _filterLogs + * @private + */ +YAHOO.widget.LogReader.prototype._filterLogs = function() { + // Reprint stack with new filters + if (this._elConsole !== null) { + this._clearConsole(); + this._printToConsole(YAHOO.widget.Logger.getStack()); + } +}; + +/** + * Clears all outputted log messages from the console and resets the time of the + * last output log message. + * + * @method _clearConsole + * @private + */ +YAHOO.widget.LogReader.prototype._clearConsole = function() { + // Clear the buffer of any pending messages + this._timeout = null; + this._buffer = []; + this._consoleMsgCount = 0; + + // Reset the rolling timer + this._lastTime = YAHOO.widget.Logger.getStartTime(); + + var elConsole = this._elConsole; + while(elConsole.hasChildNodes()) { + elConsole.removeChild(elConsole.firstChild); + } +}; + +/** + * Sends buffer of log messages to output and clears buffer. + * + * @method _printBuffer + * @private + */ +YAHOO.widget.LogReader.prototype._printBuffer = function() { + this._timeout = null; + + if(this._elConsole !== null) { + var thresholdMax = this.thresholdMax; + thresholdMax = (thresholdMax && !isNaN(thresholdMax)) ? thresholdMax : 500; + if(this._consoleMsgCount < thresholdMax) { + var entries = []; + for (var i=0; ithis.thresholdMax)) { + thresholdMin = 0; + } + var entriesStartIndex = (entriesLen > thresholdMin) ? (entriesLen - thresholdMin) : 0; + + // Iterate through all log entries + var sourceFiltersLen = this._sourceFilters.length; + var categoryFiltersLen = this._categoryFilters.length; + for(var i=entriesStartIndex; i tag instead of tag (for wrapping) + var container = (this.verboseOutput) ? "CODE" : "PRE"; + var oNewElement = (this.newestOnTop) ? + this._elConsole.insertBefore( + document.createElement(container),this._elConsole.firstChild): + this._elConsole.appendChild(document.createElement(container)); + + oNewElement.innerHTML = output; + this._consoleMsgCount++; + this._lastTime = entry.time.getTime(); + } + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Private event handlers +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Handles Logger's categoryCreateEvent. + * + * @method _onCategoryCreate + * @param sType {String} The event. + * @param aArgs {Object[]} Data passed from event firer. + * @param oSelf {Object} The LogReader instance. + * @private + */ +YAHOO.widget.LogReader.prototype._onCategoryCreate = function(sType, aArgs, oSelf) { + var category = aArgs[0]; + if(oSelf._elFt) { + oSelf._createCategoryCheckbox(category); + } +}; + +/** + * Handles Logger's sourceCreateEvent. + * + * @method _onSourceCreate + * @param sType {String} The event. + * @param aArgs {Object[]} Data passed from event firer. + * @param oSelf {Object} The LogReader instance. + * @private + */ +YAHOO.widget.LogReader.prototype._onSourceCreate = function(sType, aArgs, oSelf) { + var source = aArgs[0]; + if(oSelf._elFt) { + oSelf._createSourceCheckbox(source); + } +}; + +/** + * Handles check events on the category filter checkboxes. + * + * @method _onCheckCategory + * @param v {HTMLEvent} The click event. + * @param oSelf {Object} The LogReader instance. + * @private + */ +YAHOO.widget.LogReader.prototype._onCheckCategory = function(v, oSelf) { + var newFilter = this.category; + var filtersArray = oSelf._categoryFilters; + + if(!this.checked) { // Remove category from filters + for(var i=0; i+ * Either of the above would create YAHOO.property, then + * YAHOO.property.package + * + * Be careful when naming packages. Reserved words may work in some browsers + * and not others. For instance, the following will fail in Safari: + *0) { + // Substring until first space + sClass = sSource.substring(0,spaceIndex); + // The rest of the source + sDetail = sSource.substring(spaceIndex,sSource.length); + } + else { + sClass = sSource; + } + if(this._isNewSource(sClass)) { + this._createNewSource(sClass); + } + } + + var timestamp = new Date(); + var logEntry = new YAHOO.widget.LogMsg({ + msg: sMsg, + time: timestamp, + category: sCategory, + source: sClass, + sourceDetail: sDetail + }); + + var stack = this._stack; + var maxStackEntries = this.maxStackEntries; + if(maxStackEntries && !isNaN(maxStackEntries) && + (stack.length >= maxStackEntries)) { + stack.shift(); + } + stack.push(logEntry); + this.newLogEvent.fire(logEntry); + + if(this._browserConsoleEnabled) { + this._printToBrowserConsole(logEntry); + } + return true; + } + else { + return false; + } +}; + +/** + * Resets internal stack and startTime, enables Logger, and fires logResetEvent. + * + * @method reset + */ +YAHOO.widget.Logger.reset = function() { + this._stack = []; + this._startTime = new Date().getTime(); + this.loggerEnabled = true; + this.log("Logger reset"); + this.logResetEvent.fire(); +}; + +/** + * Public accessor to internal stack of log message objects. + * + * @method getStack + * @return {Object[]} Array of log message objects. + */ +YAHOO.widget.Logger.getStack = function() { + return this._stack; +}; + +/** + * Public accessor to internal start time. + * + * @method getStartTime + * @return {Date} Internal date of when Logger singleton was initialized. + */ +YAHOO.widget.Logger.getStartTime = function() { + return this._startTime; +}; + +/** + * Disables output to the browser's global console.log() function, which is used + * by the Firebug extension to Firefox as well as Safari. + * + * @method disableBrowserConsole + */ +YAHOO.widget.Logger.disableBrowserConsole = function() { + YAHOO.log("Logger output to the function console.log() has been disabled."); + this._browserConsoleEnabled = false; +}; + +/** + * Enables output to the browser's global console.log() function, which is used + * by the Firebug extension to Firefox as well as Safari. + * + * @method enableBrowserConsole + */ +YAHOO.widget.Logger.enableBrowserConsole = function() { + this._browserConsoleEnabled = true; + YAHOO.log("Logger output to the function console.log() has been enabled."); +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Public events +// +///////////////////////////////////////////////////////////////////////////// + + /** + * Fired when a new category has been created. + * + * @event categoryCreateEvent + * @param sCategory {String} Category name. + */ +YAHOO.widget.Logger.categoryCreateEvent = + new YAHOO.util.CustomEvent("categoryCreate", this, true); + + /** + * Fired when a new source has been named. + * + * @event sourceCreateEvent + * @param sSource {String} Source name. + */ +YAHOO.widget.Logger.sourceCreateEvent = + new YAHOO.util.CustomEvent("sourceCreate", this, true); + + /** + * Fired when a new log message has been created. + * + * @event newLogEvent + * @param sMsg {String} Log message. + */ +YAHOO.widget.Logger.newLogEvent = new YAHOO.util.CustomEvent("newLog", this, true); + +/** + * Fired when the Logger has been reset has been created. + * + * @event logResetEvent + */ +YAHOO.widget.Logger.logResetEvent = new YAHOO.util.CustomEvent("logReset", this, true); + +///////////////////////////////////////////////////////////////////////////// +// +// Private methods +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Creates a new category of log messages and fires categoryCreateEvent. + * + * @method _createNewCategory + * @param sCategory {String} Category name. + * @private + */ +YAHOO.widget.Logger._createNewCategory = function(sCategory) { + this.categories.push(sCategory); + this.categoryCreateEvent.fire(sCategory); +}; + +/** + * Checks to see if a category has already been created. + * + * @method _isNewCategory + * @param sCategory {String} Category name. + * @return {Boolean} Returns true if category is unknown, else returns false. + * @private + */ +YAHOO.widget.Logger._isNewCategory = function(sCategory) { + for(var i=0; i < this.categories.length; i++) { + if(sCategory == this.categories[i]) { + return false; + } + } + return true; +}; + +/** + * Creates a new source of log messages and fires sourceCreateEvent. + * + * @method _createNewSource + * @param sSource {String} Source name. + * @private + */ +YAHOO.widget.Logger._createNewSource = function(sSource) { + this.sources.push(sSource); + this.sourceCreateEvent.fire(sSource); +}; + +/** + * Checks to see if a source already exists. + * + * @method _isNewSource + * @param sSource {String} Source name. + * @return {Boolean} Returns true if source is unknown, else returns false. + * @private + */ +YAHOO.widget.Logger._isNewSource = function(sSource) { + if(sSource) { + for(var i=0; i < this.sources.length; i++) { + if(sSource == this.sources[i]) { + return false; + } + } + return true; + } +}; + +/** + * Outputs a log message to global console.log() function. + * + * @method _printToBrowserConsole + * @param oEntry {Object} Log entry object. + * @private + */ +YAHOO.widget.Logger._printToBrowserConsole = function(oEntry) { + if(window.console && console.log) { + var category = oEntry.category; + var label = oEntry.category.substring(0,4).toUpperCase(); + + var time = oEntry.time; + if (time.toLocaleTimeString) { + var localTime = time.toLocaleTimeString(); + } + else { + localTime = time.toString(); + } + + var msecs = time.getTime(); + var elapsedTime = (YAHOO.widget.Logger._lastTime) ? + (msecs - YAHOO.widget.Logger._lastTime) : 0; + YAHOO.widget.Logger._lastTime = msecs; + + var output = + localTime + " (" + + elapsedTime + "ms): " + + oEntry.source + ": " + + oEntry.msg; + + console.log(output); + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Private event handlers +// +///////////////////////////////////////////////////////////////////////////// + +/** + * Handles logging of messages due to window error events. + * + * @method _onWindowError + * @param sMsg {String} The error message. + * @param sUrl {String} URL of the error. + * @param sLine {String} Line number of the error. + * @private + */ +YAHOO.widget.Logger._onWindowError = function(sMsg,sUrl,sLine) { + // Logger is not in scope of this event handler + try { + YAHOO.widget.Logger.log(sMsg+' ('+sUrl+', line '+sLine+')', "window"); + if(YAHOO.widget.Logger._origOnWindowError) { + YAHOO.widget.Logger._origOnWindowError(); + } + } + catch(e) { + return false; + } +}; + +///////////////////////////////////////////////////////////////////////////// +// +// Enable handling of native JavaScript errors +// NB: Not all browsers support the window.onerror event +// +///////////////////////////////////////////////////////////////////////////// + +if(window.onerror) { + // Save any previously defined handler to call + YAHOO.widget.Logger._origOnWindowError = window.onerror; +} +window.onerror = YAHOO.widget.Logger._onWindowError; + +///////////////////////////////////////////////////////////////////////////// +// +// First log +// +///////////////////////////////////////////////////////////////////////////// + +YAHOO.widget.Logger.log("Logger initialized"); + diff --git a/source/web/scripts/ajax/yahoo/menu/README b/source/web/scripts/ajax/yahoo/menu/README new file mode 100644 index 0000000000..8b82ff5569 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/menu/README @@ -0,0 +1,224 @@ +Menu Release Notes + +*** version 0.10.0 *** + +* Initial release + +* Known issues: + + * Some Firefox extensions disable the ability for JavaScript to prevent + the display of the browser's default context menu. These extensions + can cause the YUI ContextMenu to stop working. If you encounter this + problem, you can reset the context menu preference in Firefox back to + the default by making sure the "Disable or replace context menus" + preference is checked: + + Mac Firefox 1.0: + ------------------- + Preferences > Web Features > + Advanced... > Disable or replace context menus + + Mac Firefox 1.5 + ------------------- + Preferences > Context > + Advanced... > Disable or replace context menus + + Windows Firefox 1.0 + ------------------- + Tools > Options > Web Features > + Advanced... > Disable or replace context menus + + Windows Firefox 1.5 + ------------------- + Tools > Options > Context > + Advanced... > Disable or replace context menus + + +*** version 0.11.0 *** + +Added the following features: +----------------------------- +* Overloaded the "addItem" and "insertItem" methods of MenuModule to accept a + string or a MenuModuleItem instance + +* Added the ability to define a MenuItem instance as being "checked" + + +Fixed the following bugs: +------------------------- +* Changing the path for the submenu indicator image of one MenuModuleItem + subclass will no longer affect other subclasses + +* MenuItem instances built from existing markup without anchor tags will no + longer trigger a JavaScript error when clicked + +* Modified the implementation of the "imageRoot" property for the + MenuModuleItem class so that it is set to a secure/non-secure path when the + object is instantiated + +* Menu instances now resize in response to changes to the browser's font size + +* Modified the propagation of the MenuModule class's "submenualignment" + configuration property so that it only applies to instances of the same type + +* Adjusted the specificity of the style rule that controls the position of a + MenuItem instance's submenu indicator image to prevent it from wrapping in IE + +* Specified a width and height for submenu indicator images in the Menu + stylesheet to ensure that Menu instances are always rendered at the correct + width + +* Clicking a MenuItem instance will no longer trigger two HTTP GET requests + +* Users can now control or shift-click on MenuItem links + + +Changes: +-------- +* In the Menu stylesheet (menu.css), switched from using "first" class to + "first-of-type" class + +* Changed case of MenuModuleItem class's "subMenuIndicator" property + to "submenuIndicator" + + +*** version 0.11.3 *** + +Added the following features: +----------------------------- + +* Added a "target" configuration property to the MenuModuleItem object that + allows the user to specify the target of an item's anchor element. Items + that make use of the "target" configuration property will require the user + to click exactly on the item's anchor element to navigate to the specified + URL. + +* Items without a "url" property set will automatically hide their parent + menu instance(s) when clicked. + + +Fixed the following bugs: +------------------------- + +* Items in a submenu should now navigate to their specified URL when clicked. + +* Removed MenuBar's use of "overflow:hidden." This fixes an issue in Firefox + 1.5 in which submenus of a Menubar instance cannot overlay other absolutely + positioned elements on the page. + +* Submenus of a Menubar instance will now automatically have their iframe shim + enabled in IE<7. + +* Statically positioned Menubar and Menu instances will now render with the + correct position and dimensions in Safari. + +* MenuModuleItem's "focus" method now checks to make sure that an item's + "display" style property is not "none" before trying to set focus to its + anchor element. + +* A ContextMenu instance will now hide all other ContextMenu instances before + displaying itself. + +* Removed the dead space in front of an item's submenu indicator image in IE. + This space was causing an item's submenu to flicker when the user hovered + over it. + + +Changes: +-------- + +* Moved the DOM event handlers for every menu from the root DIV node of each + instance to the document object. This change reduces the number of DOM event + handlers used by Menu to eight, improving the cleanup time required by the + Event utility. + + +*** version 0.12 *** + +Added the following features: +----------------------------- + +* Added the YAHOO.widget.MenuManager singleton class. + +* Added two new methods to YAHOO.widget.Menu: + + * "addItems" - Adds an array of items to a menu. + + * "getRoot" - Returns the root menu in a menu hierarchy. + +* Added two new events to YAHOO.widget.Menu: + + * "itemAddedEvent" - Fires when an item is added to a menu. + + * "itemRemovedEvent" - Fires when an item is removed from a menu. + +* Added two new properties to YAHOO.widget.Menu: + + * "itemData" - Array of items to be added to the menu. + + * "lazyLoad" - Boolean indicating if the menu's "lazy load" feature + is enabled. + +* Added new configuration properties to YAHOO.widget.Menu: + + * "hidedelay" - Hides the menu after the specified number of milliseconds. + + * "showdelay" - Shows the menu after the specified number of milliseconds. + + * "container" - The containing element the menu should be rendered into. + + * "clicktohide" - Boolean indicating if the menu will automatically be + hidden if the user clicks outside of it. + + * "autosubmenudisplay" - Boolean indicating if submenus are automatically + made visible when the user mouses over the menu's items. + +* Added a "toString" method to YAHOO.widget.MenuItem, YAHOO.widget.MenuBarItem + and YAHOO.widget.ContextMenuItem that returns the class name followed by the + value of the item's "text" configuration property. + + +Fixed the following bugs: +------------------------- + +* Setting a YAHOO.widget.ContextMenu instance's "trigger" configuration + property will remove all previous triggers before setting up the new ones. + +* "destroy" method of YAHOO.widget.ContextMenu cleans up all DOM event handlers. + +* Clicking on a menu item with a submenu no longer hides/collapses the + entire menu. + +* Clicking an item's submenu indicator image no longer collapses the + entire menu. + + +Changes: +-------- + +* Deprecated the YAHOO.widget.MenuModule and YAHOO.widget.MenuModuleItem + classes. The Base classes are now YAHOO.widget.Menu and + YAHOO.widget.MenuItem. + +* "addItem" and "insertItem" methods of YAHOO.widget.Menu now accept an + object literal representing YAHOO.widget.MenuItem configuration properties. + +* "clearActiveItem" now takes an argument: flag indicating if the Menu + instance's active item should be blurred. + +* Switched the default value of the "visible" configuration property for + YAHOO.widget.Menu to "false." + +* Switched the default value of the "constraintoviewport" configuration + property for YAHOO.widget.Menu to "true." + +* Overloaded the "submenu" configuration property for YAHOO.widget.MenuItem + so that it now can accept any of the following: + + * YAHOO.widget.Menu instance + * Object literal representation of a menu + * Element id + * Element reference + +* "hide" and "show" methods of statically positioned menus now toggle the their + element's "display" style property between "block" and "none." \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menu.css b/source/web/scripts/ajax/yahoo/menu/assets/menu.css new file mode 100644 index 0000000000..66490a347e --- /dev/null +++ b/source/web/scripts/ajax/yahoo/menu/assets/menu.css @@ -0,0 +1,297 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.txt +Version: 0.12 +*/ + + + +/* Menu styles */ + +div.yuimenu { + + background-color:#f6f7ee; + border:solid 1px #c4c4be; + padding:1px; + +} + +/* Submenus are positioned absolute and hidden by default */ + +div.yuimenu div.yuimenu, +div.yuimenubar div.yuimenu { + + position:absolute; + visibility:hidden; + +} + +/* MenuBar Styles */ + +div.yuimenubar { + + background-color:#f6f7ee; + +} + +/* + Applying a width triggers "haslayout" in IE so that the module's + body clears its floated elements +*/ +div.yuimenubar div.bd { + + width:100%; + +} + +/* + Clear the module body for other browsers +*/ +div.yuimenubar div.bd:after { + + content:'.'; + display:block; + clear:both; + visibility:hidden; + height:0; + +} + + +/* Matches the group title (H6) inside a Menu or MenuBar instance */ + +div.yuimenu h6, +div.yuimenubar h6 { + + font-size:100%; + font-weight:normal; + margin:0; + border:solid 1px #c4c4be; + color:#b9b9b9; + +} + +div.yuimenubar h6 { + + float:left; + display:inline; /* Prevent margin doubling in IE */ + padding:4px 12px; + border-width:0 1px 0 0; + +} + +div.yuimenu h6 { + + float:none; + display:block; + border-width:1px 0 0 0; + padding:5px 10px 0 10px; + +} + + +/* Matches the UL inside a Menu or MenuBar instance */ + +div.yuimenubar ul { + + list-style-type:none; + margin:0; + padding:0; + +} + +div.yuimenu ul { + + list-style-type:none; + border:solid 1px #c4c4be; + border-width:1px 0 0 0; + margin:0; + padding:10px 0; + +} + + +div.yuimenu ul.first-of-type, +div.yuimenu ul.hastitle, +div.yuimenu h6.first-of-type { + + border-width:0; + +} + + +/* MenuItem and MenuBarItem styles */ + +div.yuimenu li, +div.yuimenubar li { + + font-size:85%; + cursor:pointer; + cursor:hand; + white-space:nowrap; + text-align:left; + +} + +div.yuimenu li.yuimenuitem { + + padding:2px 24px; + +} + +div.yuimenu li li, +div.yuimenubar li li { + + font-size:100%; + +} + + +/* Matches the help text for a menu item */ + +div.yuimenu li em { + + font-style:normal; + margin:0 0 0 40px; + +} + +div.yuimenu li a em { + + margin:0; + +} + +div.yuimenu li a, +div.yuimenubar li a { + + /* + "zoom:1" triggers "haslayout" in IE to ensure that the mouseover and + mouseout events bubble to the parent LI in IE. + */ + zoom:1; + color:#000; + text-decoration:none; + +} + +div.yuimenu li.hassubmenu, +div.yuimenu li.hashelptext { + + text-align:right; + +} + +div.yuimenu li.hassubmenu a.hassubmenu, +div.yuimenu li.hashelptext a.hashelptext { + + float:left; + display:inline; /* Prevent margin doubling in IE */ + text-align:left; + +} + + +/* Matches focused and selected menu items */ + +div.yuimenu li.selected, +div.yuimenubar li.selected { + + background-color:#8c8ad0; + +} + +div.yuimenu li.selected a.selected, +div.yuimenubar li.selected a.selected { + + text-decoration:underline; + +} + +div.yuimenu li.selected a.selected, +div.yuimenu li.selected em.selected, +div.yuimenubar li.selected a.selected { + + color:#fff; + +} + + +/* Matches disabled menu items */ + +div.yuimenu li.disabled, +div.yuimenubar li.disabled { + + cursor:default; + +} + +div.yuimenu li.disabled a.disabled, +div.yuimenu li.disabled em.disabled, +div.yuimenubar li.disabled a.disabled { + + color:#b9b9b9; + cursor:default; + +} + +div.yuimenubar li.yuimenubaritem { + + float:left; + display:inline; /* Prevent margin doubling in IE */ + border-width:0 0 0 1px; + border-style:solid; + border-color:#c4c4be; + padding:4px 24px; + margin:0; + +} + +div.yuimenubar li.yuimenubaritem.first-of-type { + + border-width:0; + +} + + +/* Matches the submenu indicator for menu items */ + +div.yuimenubar li.yuimenubaritem img { + + height:8px; + width:8px; + margin:0 0 0 10px; + vertical-align:middle; + +} + +div.yuimenu li.yuimenuitem img { + + height:8px; + width:8px; + margin:0 -16px 0 0; + padding-left:10px; + border:0; + +} + +div.yuimenu li.checked { + + position:relative; + +} + +div.yuimenu li.checked img.checked { + + height:8px; + width:8px; + margin:0; + padding:0; + border:0; + position:absolute; + left:6px; + _left:-16px; /* Underscore hack b/c this is for IE 6 only */ + top:.5em; + +} \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_dim_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_dim_1.gif new file mode 100644 index 0000000000..60b38365a2 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_dim_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_hov_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_hov_1.gif new file mode 100644 index 0000000000..66632e83e2 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_hov_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_nrm_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_nrm_1.gif new file mode 100644 index 0000000000..bf5d3f1d2d Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuarodwn8_nrm_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_dim_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_dim_1.gif new file mode 100644 index 0000000000..0780b66211 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_dim_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_hov_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_hov_1.gif new file mode 100644 index 0000000000..0d87f1a355 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_hov_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_nrm_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_nrm_1.gif new file mode 100644 index 0000000000..3cdc2a9d18 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuarorght8_nrm_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_dim_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_dim_1.gif new file mode 100644 index 0000000000..d282a6c06a Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_dim_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_hov_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_hov_1.gif new file mode 100644 index 0000000000..2fabaef59b Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_hov_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_nrm_1.gif b/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_nrm_1.gif new file mode 100644 index 0000000000..9732837a77 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/menu/assets/menuchk8_nrm_1.gif differ diff --git a/source/web/scripts/ajax/yahoo/menu/menu-debug.js b/source/web/scripts/ajax/yahoo/menu/menu-debug.js new file mode 100644 index 0000000000..86776ac761 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/menu/menu-debug.js @@ -0,0 +1,7224 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.txt +version: 0.12.0 +*/ + +/** +* @module menu +* @description The Menu Library features a collection of widgets that make +* it easy to add menus to your website or web application. With the Menu +* Library you can create website fly-out menus, customized context menus, or +* application-style menu bars with just a small amount of scripting.
+*+*
+* @title Menu Library +* @namespace YAHOO.widget +* @requires Event, Dom, Container +*/ +(function() { + +var Dom = YAHOO.util.Dom; +var Event = YAHOO.util.Event; + +/** +* Singleton that manages a collection of all menus and menu items. Listens for +* DOM events at the document level and dispatches the events to the +* corresponding menu or menu item. +* +* @namespace YAHOO.widget +* @class MenuManager +* @static +*/ +YAHOO.widget.MenuManager = new function() { + + // Private member variables + + + // Flag indicating if the DOM event handlers have been attached + + var m_bInitializedEventHandlers = false; + + + // Collection of menus + + var m_oMenus = {}; + + + // Collection of menu items + + var m_oItems = {}; + + + // Collection of visible menus + + var m_oVisibleMenus = {}; + + + // Logger + + var m_oLogger = new YAHOO.widget.LogWriter(this.toString()); + + + + // Private methods + + + /** + * Adds an item to the collection of known menu items. + * @private + * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem + * instance to be added. + */ + var addItem = function(p_oItem) { + + var sYUIId = Dom.generateId(); + + if(p_oItem && m_oItems[sYUIId] != p_oItem) { + + p_oItem.element.setAttribute("yuiid", sYUIId); + + m_oItems[sYUIId] = p_oItem; + + p_oItem.destroyEvent.subscribe(onItemDestroy, p_oItem); + + m_oLogger.log("Item: " + + p_oItem.toString() + " successfully registered."); + + } + + }; + + + /** + * Removes an item from the collection of known menu items. + * @private + * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem + * instance to be removed. + */ + var removeItem = function(p_oItem) { + + var sYUIId = p_oItem.element.getAttribute("yuiid"); + + if(sYUIId && m_oItems[sYUIId]) { + + delete m_oItems[sYUIId]; + + m_oLogger.log("Item: " + + p_oItem.toString() + " successfully unregistered."); + + } + + }; + + + /** + * Finds the root DIV node of a menu or the root LI node of a menu item. + * @private + * @param {HTMLElement} p_oElement Object specifying + * an HTML element. + */ + var getMenuRootElement = function(p_oElement) { + + var oParentNode; + + if(p_oElement && p_oElement.tagName) { + + switch(p_oElement.tagName.toUpperCase()) { + + case "DIV": + + oParentNode = p_oElement.parentNode; + + // Check if the DIV is the inner "body" node of a menu + + if( + Dom.hasClass(p_oElement, "bd") && + oParentNode && + oParentNode.tagName && + oParentNode.tagName.toUpperCase() == "DIV" + ) { + + return oParentNode; + + } + else { + + return p_oElement; + + } + + break; + + case "LI": + + return p_oElement; + + default: + + oParentNode = p_oElement.parentNode; + + if(oParentNode) { + + return getMenuRootElement(oParentNode); + + } + + break; + + } + + } + + }; + + + + // Private event handlers + + + /** + * Generic, global event handler for all of a menu's DOM-based + * events. This listens for events against the document object. If the + * target of a given event is a member of a menu or menu item's DOM, the + * instance's corresponding Custom Event is fired. + * @private + * @param {Event} p_oEvent Object representing the DOM event object passed + * back by the event utility (YAHOO.util.Event). + */ + var onDOMEvent = function(p_oEvent) { + + // Get the target node of the DOM event + + var oTarget = Event.getTarget(p_oEvent); + + + // See if the target of the event was a menu, or a menu item + + var oElement = getMenuRootElement(oTarget); + + var oMenuItem; + var oMenu; + + + if(oElement) { + + var sTagName = oElement.tagName.toUpperCase(); + + if(sTagName == "LI") { + + var sYUIId = oElement.getAttribute("yuiid"); + + if(sYUIId) { + + oMenuItem = m_oItems[sYUIId]; + oMenu = oMenuItem.parent; + + } + + } + else if(sTagName == "DIV") { + + if(oElement.id) { + + oMenu = m_oMenus[oElement.id]; + + } + + } + + } + + if(oMenu) { + + // Map of DOM event names to CustomEvent names + + var oEventTypes = { + "click": "clickEvent", + "mousedown": "mouseDownEvent", + "mouseup": "mouseUpEvent", + "mouseover": "mouseOverEvent", + "mouseout": "mouseOutEvent", + "keydown": "keyDownEvent", + "keyup": "keyUpEvent", + "keypress": "keyPressEvent" + }; + + var sCustomEventType = oEventTypes[p_oEvent.type]; + + + // Fire the Custom Even that corresponds the current DOM event + + if(oMenuItem && !oMenuItem.cfg.getProperty("disabled")) { + + oMenuItem[sCustomEventType].fire(p_oEvent); + + } + + oMenu[sCustomEventType].fire(p_oEvent, oMenuItem); + + } + else if(p_oEvent.type == "mousedown") { + + + /* + If the target of the event wasn't a menu, hide all + dynamically positioned menus + */ + + var oActiveItem; + + for(var i in m_oMenus) { + + if(m_oMenus.hasOwnProperty(i)) { + + oMenu = m_oMenus[i]; + + if( + oMenu.cfg.getProperty("clicktohide") && + oMenu.cfg.getProperty("position") == "dynamic" + ) { + + oMenu.hide(); + + } + else { + + oMenu.clearActiveItem(true); + + } + + } + + } + + } + + }; + + + /** + * "destroy" event handler for a menu. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that + * fired the event. + */ + var onMenuDestroy = function(p_sType, p_aArgs, p_oMenu) { + + this.removeMenu(p_oMenu); + + }; + + + /** + * "destroy" event handler for a MenuItem instance. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + var onItemDestroy = function(p_sType, p_aArgs, p_oItem) { + + var sYUIId = p_oItem.element.getAttribute("yuiid"); + + if(sYUIId) { + + delete m_oItems[sYUIId]; + + } + + }; + + + /** + * Event handler for when the "visible" configuration property + * of a Menu instance changes. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that + * fired the event. + */ + var onMenuVisibleConfigChange = function(p_sType, p_aArgs, p_oMenu) { + + var bVisible = p_aArgs[0]; + + if(bVisible) { + + m_oVisibleMenus[p_oMenu.id] = p_oMenu; + + m_oLogger.log("Menu: " + + p_oMenu.toString() + + " registered with the collection of visible menus."); + + } + else if(m_oVisibleMenus[p_oMenu.id]) { + + delete m_oVisibleMenus[p_oMenu.id]; + + m_oLogger.log("Menu: " + + p_oMenu.toString() + + " unregistered from the collection of visible menus."); + + } + + }; + + + /** + * "itemadded" event handler for a Menu instance. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + */ + var onItemAdded = function(p_sType, p_aArgs) { + + addItem(p_aArgs[0]); + + }; + + + /** + * "itemremoved" event handler for a Menu instance. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + */ + var onItemRemoved = function(p_sType, p_aArgs) { + + removeItem(p_aArgs[0]); + + }; + + + + // Privileged methods + + + /** + * @method addMenu + * @description Adds a menu to the collection of known menus. + * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu instance + * to be added. + */ + this.addMenu = function(p_oMenu) { + + if(p_oMenu && p_oMenu.id && !m_oMenus[p_oMenu.id]) { + + m_oMenus[p_oMenu.id] = p_oMenu; + + + if(!m_bInitializedEventHandlers) { + + var oDoc = document; + + Event.addListener(oDoc, "mouseover", onDOMEvent, this, true); + Event.addListener(oDoc, "mouseout", onDOMEvent, this, true); + Event.addListener(oDoc, "mousedown", onDOMEvent, this, true); + Event.addListener(oDoc, "mouseup", onDOMEvent, this, true); + Event.addListener(oDoc, "click", onDOMEvent, this, true); + Event.addListener(oDoc, "keydown", onDOMEvent, this, true); + Event.addListener(oDoc, "keyup", onDOMEvent, this, true); + Event.addListener(oDoc, "keypress", onDOMEvent, this, true); + + m_bInitializedEventHandlers = true; + + m_oLogger.log("DOM event handlers initialized."); + + } + + p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this); + + p_oMenu.cfg.subscribeToConfigEvent( + "visible", + onMenuVisibleConfigChange, + p_oMenu + ); + + p_oMenu.itemAddedEvent.subscribe(onItemAdded); + p_oMenu.itemRemovedEvent.subscribe(onItemRemoved); + + m_oLogger.log("Menu: " + + p_oMenu.toString() + " successfully registered."); + + } + + }; + + + /** + * @method removeMenu + * @description Removes a menu from the collection of known menus. + * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu instance + * to be removed. + */ + this.removeMenu = function(p_oMenu) { + + if(p_oMenu && m_oMenus[p_oMenu.id]) { + + delete m_oMenus[p_oMenu.id]; + + m_oLogger.log("Menu: " + + p_oMenu.toString() + " successfully unregistered."); + + } + + }; + + + /** + * @method hideVisible + * @description Hides all visible, dynamically positioned menus. + */ + this.hideVisible = function() { + + var oMenu; + + for(var i in m_oVisibleMenus) { + + if(m_oVisibleMenus.hasOwnProperty(i)) { + + oMenu = m_oVisibleMenus[i]; + + if(oMenu.cfg.getProperty("position") == "dynamic") { + + oMenu.hide(); + + } + + } + + } + + }; + + + /** + * @method getMenus + * @description Returns an array of all menus registered with the + * menu manger. + * @return {Array} + */ + this.getMenus = function() { + + return m_oMenus; + + }; + + + /** + * @method getMenu + * @description Returns a menu with the specified id. + * @param {String} p_sId String specifying the id of the menu to + * be retrieved. + * @return {YAHOO.widget.Menu} + */ + this.getMenu = function(p_sId) { + + if(m_oMenus[p_sId]) { + + return m_oMenus[p_sId]; + + } + + }; + + + /** + * @method toString + * @description Returns a string representing the menu manager. + * @return {String} + */ + this.toString = function() { + + return ("MenuManager"); + + }; + +}; + +})(); + + +(function() { + +var Dom = YAHOO.util.Dom; +var Event = YAHOO.util.Event; + + +/** +* The Menu class creates a container that holds a vertical list representing +* a set of options or commands. Menu is the base class for all +* menu containers. +* @param {String} p_oElement String specifying the id attribute of the +*- Screen-reader accessibility.
+*- Keyboard and mouse navigation.
+*- A rich event model that provides access to all of a menu's +* interesting moments.
+*- Support for +* Progressive +* Enhancement; Menus can be created from simple, +* semantic markup on the page or purely through JavaScript.
+*<div>
element of the menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source +* for the menu. +* @param {HTMLDivElement} p_oElement Object +* specifying the<div>
element of the menu. +* @param {HTMLSelectElement} p_oElement +* Object specifying the<select>
element to be used as +* the data source for the menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu. See configuration class documentation for +* more details. +* @namespace YAHOO.widget +* @class Menu +* @constructor +* @extends YAHOO.widget.Overlay +*/ +YAHOO.widget.Menu = function(p_oElement, p_oConfig) { + + if(p_oConfig) { + + this.parent = p_oConfig.parent; + + this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload; + + this.itemData = p_oConfig.itemData || p_oConfig.itemdata; + + } + + + YAHOO.widget.Menu.superclass.constructor.call( + this, + p_oElement, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.Menu, YAHOO.widget.Overlay, { + + + +// Constants + + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the +* menu's<div>
element. +* @default "yuimenu" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenu", + + +/** +* @property ITEM_TYPE +* @description Object representing the type of menu item to instantiate and +* add when parsing the child nodes (either<li>
element, +*<optgroup>
element or<option>
) +* of the menu's source HTML element. +* @default YAHOO.widget.MenuItem +* @final +* @type YAHOO.widget.MenuItem +*/ +ITEM_TYPE: null, + + +/** +* @property GROUP_TITLE_TAG_NAME +* @description String representing the tagname of the HTML element used to +* title the menu's item groups. +* @default H6 +* @final +* @type String +*/ +GROUP_TITLE_TAG_NAME: "h6", + + + +// Private properties + + +/** +* @property _nHideDelayId +* @description Number representing the time-out setting used to cancel the +* hiding of a menu. +* @default null +* @private +* @type Number +*/ +_nHideDelayId: null, + + +/** +* @property _nShowDelayId +* @description Number representing the time-out setting used to cancel the +* showing of a menu. +* @default null +* @private +* @type Number +*/ +_nShowDelayId: null, + + +/** +* @property _hideDelayEventHandlersAssigned +* @description Boolean indicating if the "mouseover" and "mouseout" event +* handlers used for hiding the menu via a call to "window.setTimeout" have +* already been assigned. +* @default false +* @private +* @type Boolean +*/ +_hideDelayEventHandlersAssigned: false, + + +/** +* @property _bHandledMouseOverEvent +* @description Boolean indicating the current state of the menu's +* "mouseover" event. +* @default false +* @private +* @type Boolean +*/ +_bHandledMouseOverEvent: false, + + +/** +* @property _bHandledMouseOutEvent +* @description Boolean indicating the current state of the menu's +* "mouseout" event. +* @default false +* @private +* @type Boolean +*/ +_bHandledMouseOutEvent: false, + + +/** +* @property _aGroupTitleElements +* @description Array of HTML element used to title groups of menu items. +* @default [] +* @private +* @type Array +*/ +_aGroupTitleElements: null, + + +/** +* @property _aItemGroups +* @description Array of menu items. +* @default [] +* @private +* @type Array +*/ +_aItemGroups: null, + + +/** +* @property _aListElements +* @description Array of<ul>
elements, each of which is +* the parent node for each item's<li>
element. +* @default [] +* @private +* @type Array +*/ +_aListElements: null, + + + +// Public properties + + +/** +* @property lazyLoad +* @description Boolean indicating if the menu's "lazy load" feature is +* enabled. If set to "true," initialization and rendering of the menu's +* items will be deferred until the first time it is made visible. This +* property should be set via the constructor using the configuration +* object literal. +* @default false +* @type Boolean +*/ +lazyLoad: false, + + +/** +* @property itemData +* @description Array of items to be added to the menu. The array can contain +* strings representing the text for each item to be created, object literals +* representing the menu item configuration properties, or MenuItem instances. +* This property should be set via the constructor using the configuration +* object literal. +* @default null +* @type Array +*/ +itemData: null, + + +/** +* @property activeItem +* @description Object reference to the item in the menu that has focus. +* @default null +* @type YAHOO.widget.MenuItem +*/ +activeItem: null, + + +/** +* @property parent +* @description Object reference to the menu's parent menu or menu item. +* This property can be set via the constructor using the configuration +* object literal. +* @default null +* @type YAHOO.widget.MenuItem +*/ +parent: null, + + +/** +* @property srcElement +* @description Object reference to the HTML element (either +*<select>
or<div>
) used to +* create the menu. +* @default null +* @type HTMLSelectElement|HTMLDivElement +*/ +srcElement: null, + + + +// Events + + +/** +* @event mouseOverEvent +* @description Fires when the mouse has entered the menu. Passes back +* the DOM Event object as an argument. +*/ +mouseOverEvent: null, + + +/** +* @event mouseOutEvent +* @description Fires when the mouse has left the menu. Passes back the DOM +* Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +mouseOutEvent: null, + + +/** +* @event mouseDownEvent +* @description Fires when the user mouses down on the menu. Passes back the +* DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +mouseDownEvent: null, + + +/** +* @event mouseUpEvent +* @description Fires when the user releases a mouse button while the mouse is +* over the menu. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +mouseUpEvent: null, + + +/** +* @event clickEvent +* @description Fires when the user clicks the on the menu. Passes back the +* DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +clickEvent: null, + + +/** +* @event keyPressEvent +* @description Fires when the user presses an alphanumeric key when one of the +* menu's items has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +keyPressEvent: null, + + +/** +* @event keyDownEvent +* @description Fires when the user presses a key when one of the menu's items +* has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +keyDownEvent: null, + + +/** +* @event keyUpEvent +* @description Fires when the user releases a key when one of the menu's items +* has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +keyUpEvent: null, + + +/** +* @event itemAddedEvent +* @description Fires when an item is added to the menu. +* @type YAHOO.util.CustomEvent +*/ +itemAddedEvent: null, + + +/** +* @event itemRemovedEvent +* @description Fires when an item is removed to the menu. +* @type YAHOO.util.CustomEvent +*/ +itemRemovedEvent: null, + + +/** +* @method init +* @description The Menu class's initialization method. 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. +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source +* for the menu. +* @param {HTMLDivElement} p_oElement Object +* specifying the<div>
element of the menu. +* @param {HTMLSelectElement} p_oElement +* Object specifying the<select>
element to be used as +* the data source for the menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu. See configuration class documentation for +* more details. +*/ +init: function(p_oElement, p_oConfig) { + + this._aItemGroups = []; + this._aListElements = []; + this._aGroupTitleElements = []; + + + if(!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.MenuItem; + + } + + + var oElement; + + if(typeof p_oElement == "string") { + + oElement = document.getElementById(p_oElement); + + } + else if(p_oElement.tagName) { + + oElement = p_oElement; + + } + + + if(oElement && oElement.tagName) { + + switch(oElement.tagName.toUpperCase()) { + + case "DIV": + + this.srcElement = oElement; + + if(!oElement.id) { + + oElement.setAttribute("id", Dom.generateId()); + + } + + + /* + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.Menu.superclass.init.call(this, oElement); + + this.beforeInitEvent.fire(YAHOO.widget.Menu); + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + + this.logger.log("Source element: " + this.srcElement.tagName); + + break; + + case "SELECT": + + this.srcElement = oElement; + + + /* + The source element is not something that we can use + outright, so we need to create a new Overlay + + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.Menu.superclass.init.call(this, Dom.generateId()); + + this.beforeInitEvent.fire(YAHOO.widget.Menu); + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + + this.logger.log("Source element: " + this.srcElement.tagName); + + break; + + } + + } + else { + + /* + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.Menu.superclass.init.call(this, p_oElement); + + this.beforeInitEvent.fire(YAHOO.widget.Menu); + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + + this.logger.log("No source element found. " + + "Created element with id: " + this.id); + + } + + + if(this.element) { + + var oEl = this.element; + + Dom.addClass(oEl, this.CSS_CLASS_NAME); + + + // Subscribe to Custom Events + + this.initEvent.subscribe(this._onInit, this, true); + this.beforeRenderEvent.subscribe(this._onBeforeRender, this, true); + this.renderEvent.subscribe(this._onRender, this, true); + this.beforeShowEvent.subscribe(this._onBeforeShow, this, true); + this.showEvent.subscribe(this._onShow, this, true); + this.beforeHideEvent.subscribe(this._onBeforeHide, this, true); + this.mouseOverEvent.subscribe(this._onMouseOver, this, true); + this.mouseOutEvent.subscribe(this._onMouseOut, this, true); + this.clickEvent.subscribe(this._onClick, this, true); + this.keyDownEvent.subscribe(this._onKeyDown, this, true); + + + if(p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + + // Register the Menu instance with the MenuManager + + YAHOO.widget.MenuManager.addMenu(this); + + + this.initEvent.fire(YAHOO.widget.Menu); + + } + +}, + + + +// Private methods + + +/** +* @method _initSubTree +* @description Iterates the childNodes of the source element to find nodes +* used to instantiate menu and menu items. +* @private +*/ +_initSubTree: function() { + + var oNode; + + if(this.srcElement.tagName == "DIV") { + + /* + Populate the collection of item groups and item + group titles + */ + + oNode = this.body.firstChild; + + var nGroup = 0; + var sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase(); + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case sGroupTitleTagName: + + this._aGroupTitleElements[nGroup] = oNode; + + break; + + case "UL": + + this._aListElements[nGroup] = oNode; + this._aItemGroups[nGroup] = []; + nGroup++; + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + + /* + Apply the "first-of-type" class to the first UL to mimic + the "first-of-type" CSS3 psuedo class. + */ + + if(this._aListElements[0]) { + + Dom.addClass(this._aListElements[0], "first-of-type"); + + } + + } + + + oNode = null; + + this.logger.log("Searching DOM for items to initialize."); + + if(this.srcElement.tagName) { + + switch(this.srcElement.tagName.toUpperCase()) { + + case "DIV": + + if(this._aListElements.length > 0) { + + this.logger.log("Found " + + this._aListElements.length + + " item groups to initialize."); + + var i = this._aListElements.length - 1; + + do { + + oNode = this._aListElements[i].firstChild; + + this.logger.log("Scanning " + + this._aListElements[i].childNodes.length + + " child nodes for items to initialize."); + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case "LI": + + this.logger.log("Initializing " + + oNode.tagName + " node."); + + this.addItem( + new this.ITEM_TYPE( + oNode, + { parent: this } + ), + i + ); + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + } + while(i--); + + } + + break; + + case "SELECT": + + this.logger.log("Scanning " + + this.srcElement.childNodes.length + + " child nodes for items to initialize."); + + oNode = this.srcElement.firstChild; + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case "OPTGROUP": + case "OPTION": + + this.logger.log("Initializing " + + oNode.tagName + " node."); + + this.addItem( + new this.ITEM_TYPE( + oNode, + { parent: this } + ) + ); + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + break; + + } + + } + +}, + + +/** +* @method _getFirstEnabledItem +* @description Returns the first enabled item in the menu. +* @return {YAHOO.widget.MenuItem} +* @private +*/ +_getFirstEnabledItem: function() { + + var nGroups = this._aItemGroups.length; + var oItem; + var aItemGroup; + + for(var i=0; i= aGroup.length); + + + if(aGroup[p_nItemIndex]) { + + aGroup.splice(p_nItemIndex, 0, oItem); + + } + else { + + aGroup[p_nItemIndex] = oItem; + + } + + + oGroupItem = aGroup[p_nItemIndex]; + + if(oGroupItem) { + + if( + bAppend && + ( + !oGroupItem.element.parentNode || + oGroupItem.element.parentNode.nodeType == 11 + ) + ) { + + this._aListElements[nGroupIndex].appendChild( + oGroupItem.element + ); + + } + else { + + + /** + * Returns the next sibling of an item in an array. + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getNextItemSibling = + + function(p_aArray, p_nStartIndex) { + + return ( + p_aArray[p_nStartIndex] || + getNextItemSibling( + p_aArray, + (p_nStartIndex+1) + ) + ); + + }; + + + var oNextItemSibling = + getNextItemSibling(aGroup, (p_nItemIndex+1)); + + if( + oNextItemSibling && + ( + !oGroupItem.element.parentNode || + oGroupItem.element.parentNode.nodeType == 11 + ) + ) { + + this._aListElements[nGroupIndex].insertBefore( + oGroupItem.element, + oNextItemSibling.element + ); + + } + + } + + + oGroupItem.parent = this; + + this._subscribeToItemEvents(oGroupItem); + + this._configureSubmenu(oGroupItem); + + this._updateItemProperties(nGroupIndex); + + this.logger.log("Item inserted." + + " Text: " + oGroupItem.cfg.getProperty("text") + ", " + + " Index: " + oGroupItem.index + ", " + + " Group Index: " + oGroupItem.groupIndex); + + this.itemAddedEvent.fire(oGroupItem); + + return oGroupItem; + + } + + } + else { + + var nItemIndex = aGroup.length; + + aGroup[nItemIndex] = oItem; + + oGroupItem = aGroup[nItemIndex]; + + + if(oGroupItem) { + + if( + !Dom.isAncestor( + this._aListElements[nGroupIndex], + oGroupItem.element + ) + ) { + + this._aListElements[nGroupIndex].appendChild( + oGroupItem.element + ); + + } + + oGroupItem.element.setAttribute("groupindex", nGroupIndex); + oGroupItem.element.setAttribute("index", nItemIndex); + + oGroupItem.parent = this; + + oGroupItem.index = nItemIndex; + oGroupItem.groupIndex = nGroupIndex; + + this._subscribeToItemEvents(oGroupItem); + + this._configureSubmenu(oGroupItem); + + if(nItemIndex === 0) { + + Dom.addClass(oGroupItem.element, "first-of-type"); + + } + + this.logger.log("Item added." + + " Text: " + oGroupItem.cfg.getProperty("text") + ", " + + " Index: " + oGroupItem.index + ", " + + " Group Index: " + oGroupItem.groupIndex); + + + this.itemAddedEvent.fire(oGroupItem); + + return oGroupItem; + + } + + } + + } + +}, + + +/** +* @method _removeItemFromGroupByIndex +* @description Removes a menu item from a group by index. Returns the menu +* item that was removed. +* @private +* @param {Number} p_nGroupIndex Number indicating the group to which the menu +* item belongs. +* @param {Number} p_nItemIndex Number indicating the index of the menu item +* to be removed. +* @return {YAHOO.widget.MenuItem} +*/ +_removeItemFromGroupByIndex: function(p_nGroupIndex, p_nItemIndex) { + + var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0; + var aGroup = this._getItemGroup(nGroupIndex); + + if(aGroup) { + + var aArray = aGroup.splice(p_nItemIndex, 1); + var oItem = aArray[0]; + + if(oItem) { + + // Update the index and className properties of each member + + this._updateItemProperties(nGroupIndex); + + if(aGroup.length === 0) { + + // Remove the UL + + var oUL = this._aListElements[nGroupIndex]; + + if(this.body && oUL) { + + this.body.removeChild(oUL); + + } + + // Remove the group from the array of items + + this._aItemGroups.splice(nGroupIndex, 1); + + + // Remove the UL from the array of ULs + + this._aListElements.splice(nGroupIndex, 1); + + + /* + Assign the "first-of-type" class to the new first UL + in the collection + */ + + oUL = this._aListElements[0]; + + if(oUL) { + + Dom.addClass(oUL, "first-of-type"); + + } + + } + + + this.itemRemovedEvent.fire(oItem); + + + // Return a reference to the item that was removed + + return oItem; + + } + + } + +}, + + +/** +* @method _removeItemFromGroupByValue +* @description Removes a menu item from a group by reference. Returns the +* menu item that was removed. +* @private +* @param {Number} p_nGroupIndex Number indicating the group to which the +* menu item belongs. +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance to be removed. +* @return {YAHOO.widget.MenuItem} +*/ +_removeItemFromGroupByValue: function(p_nGroupIndex, p_oItem) { + + var aGroup = this._getItemGroup(p_nGroupIndex); + + if(aGroup) { + + var nItems = aGroup.length; + var nItemIndex = -1; + + if(nItems > 0) { + + var i = nItems-1; + + do { + + if(aGroup[i] == p_oItem) { + + nItemIndex = i; + break; + + } + + } + while(i--); + + if(nItemIndex > -1) { + + return this._removeItemFromGroupByIndex( + p_nGroupIndex, + nItemIndex + ); + + } + + } + + } + +}, + + +/** +* @method _updateItemProperties +* @description Updates the "index," "groupindex," and "className" properties +* of the menu items in the specified group. +* @private +* @param {Number} p_nGroupIndex Number indicating the group of items to update. +*/ +_updateItemProperties: function(p_nGroupIndex) { + + var aGroup = this._getItemGroup(p_nGroupIndex); + var nItems = aGroup.length; + + if(nItems > 0) { + + var i = nItems - 1; + var oItem; + var oLI; + + // Update the index and className properties of each member + + do { + + oItem = aGroup[i]; + + if(oItem) { + + oLI = oItem.element; + + oItem.index = i; + oItem.groupIndex = p_nGroupIndex; + + oLI.setAttribute("groupindex", p_nGroupIndex); + oLI.setAttribute("index", i); + + Dom.removeClass(oLI, "first-of-type"); + + } + + } + while(i--); + + + if(oLI) { + + Dom.addClass(oLI, "first-of-type"); + + } + + } + +}, + + +/** +* @method _createItemGroup +* @description Creates a new menu item group (array) and its associated +* <ul>
element. Returns an aray of menu item groups. +* @private +* @param {Number} p_nIndex Number indicating the group to create. +* @return {Array} +*/ +_createItemGroup: function(p_nIndex) { + + if(!this._aItemGroups[p_nIndex]) { + + this._aItemGroups[p_nIndex] = []; + + var oUL = document.createElement("ul"); + + this._aListElements[p_nIndex] = oUL; + + return this._aItemGroups[p_nIndex]; + + } + +}, + + +/** +* @method _getItemGroup +* @description Returns the menu item group at the specified index. +* @private +* @param {Number} p_nIndex Number indicating the index of the menu item group +* to be retrieved. +* @return {Array} +*/ +_getItemGroup: function(p_nIndex) { + + var nIndex = ((typeof p_nIndex == "number") ? p_nIndex : 0); + + return this._aItemGroups[nIndex]; + +}, + + +/** +* @method _configureSubmenu +* @description Subscribes the menu item's submenu to its parent menu's events. +* @private +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance with the submenu to be configured. +*/ +_configureSubmenu: function(p_oItem) { + + var oSubmenu = p_oItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + /* + Listen for configuration changes to the parent menu + so they they can be applied to the submenu. + */ + + this.cfg.configChangedEvent.subscribe( + this._onParentMenuConfigChange, + oSubmenu, + true + ); + + this.renderEvent.subscribe( + this._onParentMenuRender, + oSubmenu, + true + ); + + oSubmenu.beforeShowEvent.subscribe( + this._onSubmenuBeforeShow, + oSubmenu, + true + ); + + oSubmenu.showEvent.subscribe( + this._onSubmenuShow, + oSubmenu, + true + ); + + oSubmenu.hideEvent.subscribe( + this._onSubmenuHide, + oSubmenu, + true + ); + + } + +}, + + +/** +* @method _subscribeToItemEvents +* @description Subscribes a menu to a menu item's event. +* @private +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance whose events should be subscribed to. +*/ +_subscribeToItemEvents: function(p_oItem) { + + p_oItem.focusEvent.subscribe(this._onMenuItemFocus, p_oItem, this); + + p_oItem.blurEvent.subscribe(this._onMenuItemBlur, this, true); + + p_oItem.cfg.configChangedEvent.subscribe( + this._onMenuItemConfigChange, + p_oItem, + this + ); + +}, + + +/** +* @method _getOffsetWidth +* @description Returns the offset width of the menu's +*<div>
element. +* @private +*/ +_getOffsetWidth: function() { + + var oClone = this.element.cloneNode(true); + + Dom.setStyle(oClone, "width", ""); + + document.body.appendChild(oClone); + + var sWidth = oClone.offsetWidth; + + document.body.removeChild(oClone); + + return sWidth; + +}, + + +/** +* @method _cancelHideDelay +* @description Cancels the call to "hideMenu." +* @private +*/ +_cancelHideDelay: function() { + + var oRoot = this.getRoot(); + + if(oRoot._nHideDelayId) { + + window.clearTimeout(oRoot._nHideDelayId); + + } + +}, + + +/** +* @method _execHideDelay +* @description Hides the menu after the number of milliseconds specified by +* the "hidedelay" configuration property. +* @private +*/ +_execHideDelay: function() { + + this._cancelHideDelay(); + + var oRoot = this.getRoot(); + var me = this; + + var hideMenu = function() { + + if(oRoot.activeItem) { + + oRoot.clearActiveItem(); + + } + + if(oRoot == me && me.cfg.getProperty("position") == "dynamic") { + + me.hide(); + + } + + }; + + + oRoot._nHideDelayId = + window.setTimeout(hideMenu, oRoot.cfg.getProperty("hidedelay")); + +}, + + +/** +* @method _cancelShowDelay +* @description Cancels the call to the "showMenu." +* @private +*/ +_cancelShowDelay: function() { + + var oRoot = this.getRoot(); + + if(oRoot._nShowDelayId) { + + window.clearTimeout(oRoot._nShowDelayId); + + } + +}, + + +/** +* @method _execShowDelay +* @description Shows the menu after the number of milliseconds specified by +* the "showdelay" configuration property have ellapsed. +* @private +* @param {YAHOO.widget.Menu} p_oMenu Object specifying the menu that should +* be made visible. +*/ +_execShowDelay: function(p_oMenu) { + + this._cancelShowDelay(); + + var oRoot = this.getRoot(); + + var showMenu = function() { + + p_oMenu.show(); + + }; + + + oRoot._nShowDelayId = + window.setTimeout(showMenu, oRoot.cfg.getProperty("showdelay")); + +}, + + + +// Protected methods + + +/** +* @method _onMouseOver +* @description "mouseover" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onMouseOver: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oTarget = Event.getTarget(oEvent); + + if( + !this._bHandledMouseOverEvent && + (oTarget == this.element || Dom.isAncestor(this.element, oTarget)) + ) { + + // MENU MOUSEOVER LOGIC HERE + + this.clearActiveItem(); + + this._bHandledMouseOverEvent = true; + this._bHandledMouseOutEvent = false; + + } + + + if( + oItem && !oItem.handledMouseOverEvent && + (oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget)) + ) { + + var oItemCfg = oItem.cfg; + + // Select and focus the current menu item + + oItemCfg.setProperty("selected", true); + oItem.focus(); + + + if(this.cfg.getProperty("autosubmenudisplay")) { + + // Show the submenu this menu item + + var oSubmenu = oItemCfg.getProperty("submenu"); + + if(oSubmenu) { + + if(this.cfg.getProperty("showdelay") > 0) { + + this._execShowDelay(oSubmenu); + + } + else { + + oSubmenu.show(); + + } + + } + + } + + oItem.handledMouseOverEvent = true; + oItem.handledMouseOutEvent = false; + + } + +}, + + +/** +* @method _onMouseOut +* @description "mouseout" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onMouseOut: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oRelatedTarget = Event.getRelatedTarget(oEvent); + var bMovingToSubmenu = false; + + if(oItem) { + + var oItemCfg = oItem.cfg; + var oSubmenu = oItemCfg.getProperty("submenu"); + + + if( + oSubmenu && + ( + oRelatedTarget == oSubmenu.element || + Dom.isAncestor(oSubmenu.element, oRelatedTarget) + ) + ) { + + bMovingToSubmenu = true; + + } + + + if( + !oItem.handledMouseOutEvent && + ( + ( + oRelatedTarget != oItem.element && + !Dom.isAncestor(oItem.element, oRelatedTarget) + ) || bMovingToSubmenu + ) + ) { + + + if(this.cfg.getProperty("showdelay") > 0) { + + this._cancelShowDelay(); + + } + + + if(!bMovingToSubmenu) { + + oItemCfg.setProperty("selected", false); + + } + + + if(this.cfg.getProperty("autosubmenudisplay")) { + + if(oSubmenu) { + + if( + !( + oRelatedTarget == oSubmenu.element || + YAHOO.util.Dom.isAncestor( + oSubmenu.element, + oRelatedTarget + ) + ) + ) { + + oSubmenu.hide(); + + } + + } + + } + + oItem.handledMouseOutEvent = true; + oItem.handledMouseOverEvent = false; + + } + + } + + + if( + !this._bHandledMouseOutEvent && + ( + ( + oRelatedTarget != this.element && + !Dom.isAncestor(this.element, oRelatedTarget) + ) + || bMovingToSubmenu + ) + ) { + + this._bHandledMouseOutEvent = true; + this._bHandledMouseOverEvent = false; + + } + +}, + + +/** +* @method _onClick +* @description "click" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onClick: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oTarget = Event.getTarget(oEvent); + + if(oItem) { + + var oItemCfg = oItem.cfg; + var oSubmenu = oItemCfg.getProperty("submenu"); + + + /* + ACCESSIBILITY FEATURE FOR SCREEN READERS: + Expand/collapse the submenu when the user clicks + on the submenu indicator image. + */ + + if(oTarget == oItem.submenuIndicator && oSubmenu) { + + if(oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + + } + else { + + this.clearActiveItem(); + + this.activeItem = oItem; + + oItem.cfg.setProperty("selected", true); + + oSubmenu.show(); + + } + + } + else { + + var sURL = oItemCfg.getProperty("url"); + var bCurrentPageURL = (sURL.substr((sURL.length-1),1) == "#"); + var sTarget = oItemCfg.getProperty("target"); + var bHasTarget = (sTarget && sTarget.length > 0); + + /* + Prevent the browser from following links + equal to "#" + */ + + if( + oTarget.tagName.toUpperCase() == "A" && + bCurrentPageURL && !bHasTarget + ) { + + Event.preventDefault(oEvent); + + } + + if( + oTarget.tagName.toUpperCase() != "A" && + !bCurrentPageURL && !bHasTarget + ) { + + /* + Follow the URL of the item regardless of + whether or not the user clicked specifically + on the anchor element. + */ + + document.location = sURL; + + } + + + /* + If the item doesn't navigate to a URL and it doesn't have + a submenu, then collapse the menu tree. + */ + + if(bCurrentPageURL && !oSubmenu) { + + var oRoot = this.getRoot(); + + if(oRoot.cfg.getProperty("position") == "static") { + + oRoot.clearActiveItem(); + + } + else { + + oRoot.hide(); + + } + + } + + } + + } + +}, + + +/** +* @method _onKeyDown +* @description "keydown" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onKeyDown: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oSubmenu; + + if(oItem) { + + var oItemCfg = oItem.cfg; + var oParentItem = this.parent; + var oRoot; + var oNextItem; + + + switch(oEvent.keyCode) { + + case 38: // Up arrow + case 40: // Down arrow + + if( + oItem == this.activeItem && + !oItemCfg.getProperty("selected") + ) { + + oItemCfg.setProperty("selected", true); + + } + else { + + oNextItem = (oEvent.keyCode == 38) ? + oItem.getPreviousEnabledSibling() : + oItem.getNextEnabledSibling(); + + if(oNextItem) { + + this.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + oNextItem.focus(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + + case 39: // Right arrow + + oSubmenu = oItemCfg.getProperty("submenu"); + + if(oSubmenu) { + + if(!oItemCfg.getProperty("selected")) { + + oItemCfg.setProperty("selected", true); + + } + + oSubmenu.show(); + + oSubmenu.setInitialSelection(); + + } + else { + + oRoot = this.getRoot(); + + if(oRoot instanceof YAHOO.widget.MenuBar) { + + oNextItem = oRoot.activeItem.getNextEnabledSibling(); + + if(oNextItem) { + + oRoot.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + + oSubmenu = oNextItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.show(); + + } + + oNextItem.focus(); + + } + + } + + } + + + Event.preventDefault(oEvent); + + break; + + + case 37: // Left arrow + + if(oParentItem) { + + var oParentMenu = oParentItem.parent; + + if(oParentMenu instanceof YAHOO.widget.MenuBar) { + + oNextItem = + oParentMenu.activeItem.getPreviousEnabledSibling(); + + if(oNextItem) { + + oParentMenu.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + + oSubmenu = oNextItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.show(); + + } + + oNextItem.focus(); + + } + + } + else { + + this.hide(); + + oParentItem.focus(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + } + + + } + + + if(oEvent.keyCode == 27) { // Esc key + + if(this.cfg.getProperty("position") == "dynamic") { + + this.hide(); + + if(this.parent) { + + this.parent.focus(); + + } + + } + else if(this.activeItem) { + + oSubmenu = this.activeItem.cfg.getProperty("submenu"); + + if(oSubmenu && oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + this.activeItem.focus(); + + } + else { + + this.activeItem.cfg.setProperty("selected", false); + this.activeItem.blur(); + + } + + } + + + Event.preventDefault(oEvent); + + } + +}, + + + +// Private methods + + +/** +* @method _onInit +* @description "init" event handler for the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onInit: function(p_sType, p_aArgs, p_oMenu) { + + if( + ( + (this.parent && !this.lazyLoad) || + (!this.parent && this.cfg.getProperty("position") == "static") || + ( + !this.parent && + !this.lazyLoad && + this.cfg.getProperty("position") == "dynamic" + ) + ) && + this.getItemGroups().length === 0 + ) { + + if(this.srcElement) { + + this._initSubTree(); + + } + + + if(this.itemData) { + + this.addItems(this.itemData); + + } + + } + else if(this.lazyLoad) { + + this.cfg.fireQueue(); + + } + +}, + + +/** +* @method _onBeforeRender +* @description "beforerender" event handler for the menu. Appends all of the +*<ul>
,<li>
and their accompanying +* title elements to the body element of the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onBeforeRender: function(p_sType, p_aArgs, p_oMenu) { + + var oConfig = this.cfg; + var oEl = this.element; + var nListElements = this._aListElements.length; + + + if(nListElements > 0) { + + var i = 0; + var bFirstList = true; + var oUL; + var oGroupTitle; + + + do { + + oUL = this._aListElements[i]; + + if(oUL) { + + if(bFirstList) { + + Dom.addClass(oUL, "first-of-type"); + bFirstList = false; + + } + + + if(!Dom.isAncestor(oEl, oUL)) { + + this.appendToBody(oUL); + + } + + + oGroupTitle = this._aGroupTitleElements[i]; + + if(oGroupTitle) { + + if(!Dom.isAncestor(oEl, oGroupTitle)) { + + oUL.parentNode.insertBefore(oGroupTitle, oUL); + + } + + + Dom.addClass(oUL, "hastitle"); + + } + + } + + i++; + + } + while(i < nListElements); + + } + +}, + + +/** +* @method _onRender +* @description "render" event handler for the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onRender: function(p_sType, p_aArgs, p_oMenu) { + + if(this.cfg.getProperty("position") == "dynamic") { + + var sWidth = + this.element.parentNode.tagName.toUpperCase() == "BODY" ? + this.element.offsetWidth : this._getOffsetWidth(); + + this.cfg.setProperty("width", (sWidth + "px")); + + } + +}, + + +/** +* @method _onBeforeShow +* @description "beforeshow" event handler for the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onBeforeShow: function(p_sType, p_aArgs, p_oMenu) { + + if(this.lazyLoad && this.getItemGroups().length === 0) { + + if(this.srcElement) { + + this._initSubTree(); + + } + + + if(this.itemData) { + + if( + this.parent && this.parent.parent && + this.parent.parent.srcElement && + this.parent.parent.srcElement.tagName.toUpperCase() == "SELECT" + ) { + + var nOptions = this.itemData.length; + + for(var n=0; n0) { + + oConfig.showdelay = nShowDelay; + + } + + + var nHideDelay = oParentMenu.cfg.getProperty("hidedelay"); + + if(nHideDelay > 0) { + + oConfig.hidedelay = nHideDelay; + + } + + + /* + Only sync the "iframe" configuration property if the parent + menu's "position" configuration is the same. + */ + + if( + this.cfg.getProperty("position") == + oParentMenu.cfg.getProperty("position") + ) { + + oConfig.iframe = oParentMenu.cfg.getProperty("iframe"); + + } + + + p_oSubmenu.cfg.applyConfig(oConfig); + + + if(!this.lazyLoad) { + + if(Dom.inDocument(this.element)) { + + this.render(); + + } + else { + + this.render(this.parent.element); + + } + + } + +}, + + +/** +* @method _onSubmenuBeforeShow +* @description "beforeshow" event handler for a submenu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onSubmenuBeforeShow: function(p_sType, p_aArgs, p_oSubmenu) { + + var oParent = this.parent; + var aAlignment = oParent.parent.cfg.getProperty("submenualignment"); + + this.cfg.setProperty( + "context", + [oParent.element, aAlignment[0], aAlignment[1]] + ); + + oParent.submenuIndicator.alt = oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT; + +}, + + +/** +* @method _onSubmenuShow +* @description "show" event handler for a submenu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onSubmenuShow: function(p_sType, p_aArgs, p_oSubmenu) { + + var oParent = this.parent; + + oParent.submenuIndicator.alt = oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT; + +}, + + +/** +* @method _onSubmenuHide +* @description "hide" Custom Event handler for a submenu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onSubmenuHide: function(p_sType, p_aArgs, p_oSubmenu) { + + var oParent = this.parent; + + oParent.submenuIndicator.alt = oParent.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT; + +}, + + +/** +* @method _onMenuItemFocus +* @description "focus" event handler for the menu's items. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item +* that fired the event. +*/ +_onMenuItemFocus: function(p_sType, p_aArgs, p_oItem) { + + this.activeItem = p_oItem; + +}, + + +/** +* @method _onMenuItemBlur +* @description "blur" event handler for the menu's items. +* @private +* @param {String} p_sType String representing the name of the event +* that was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +*/ +_onMenuItemBlur: function(p_sType, p_aArgs) { + + this.activeItem = null; + +}, + + +/** +* @method _onMenuItemConfigChange +* @description "configchange" event handler for the menu's items. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item +* that fired the event. +*/ +_onMenuItemConfigChange: function(p_sType, p_aArgs, p_oItem) { + + var sProperty = p_aArgs[0][0]; + + switch(sProperty) { + + case "submenu": + + var oSubmenu = p_aArgs[0][1]; + + if(oSubmenu) { + + this._configureSubmenu(p_oItem); + + } + + break; + + case "text": + case "helptext": + + /* + A change to an item's "text" or "helptext" + configuration properties requires the width of the parent + menu to be recalculated. + */ + + if(this.element.style.width) { + + var sWidth = this._getOffsetWidth() + "px"; + + Dom.setStyle(this.element, "width", sWidth); + + } + + break; + + } + +}, + + + +// Public event handlers for configuration properties + + +/** +* @method enforceConstraints +* @description The default event handler executed when the moveEvent is fired, +* if the "constraintoviewport" configuration property is set to true. +* @param {String} type The name of the event that was fired. +* @param {Array} args Collection of arguments sent when the +* event was fired. +* @param {Array} obj Array containing the current Menu instance +* and the item that fired the event. +*/ +enforceConstraints: function(type, args, obj) { + + var oConfig = this.cfg; + + var pos = args[0]; + + var x = pos[0]; + var y = pos[1]; + + var bod = document.getElementsByTagName('body')[0]; + var htm = document.getElementsByTagName('html')[0]; + + var bodyOverflow = Dom.getStyle(bod, "overflow"); + var htmOverflow = Dom.getStyle(htm, "overflow"); + + var offsetHeight = this.element.offsetHeight; + var offsetWidth = this.element.offsetWidth; + + var viewPortWidth = Dom.getClientWidth(); + var viewPortHeight = Dom.getClientHeight(); + + var scrollX = window.scrollX || document.body.scrollLeft; + var scrollY = window.scrollY || document.body.scrollTop; + + var topConstraint = scrollY + 10; + var leftConstraint = scrollX + 10; + var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10; + var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10; + + var aContext = oConfig.getProperty("context"); + var oContextElement = aContext ? aContext[0] : null; + + + if (x < 10) { + + x = leftConstraint; + + } else if ((x + offsetWidth) > viewPortWidth) { + + if( + oContextElement && + ((x - oContextElement.offsetWidth) > offsetWidth) + ) { + + x = (x - (oContextElement.offsetWidth + offsetWidth)); + + } + else { + + x = rightConstraint; + + } + + } + + if (y < 10) { + + y = topConstraint; + + } else if (y > bottomConstraint) { + + if(oContextElement && (y > offsetHeight)) { + + y = ((y + oContextElement.offsetHeight) - offsetHeight); + + } + else { + + y = bottomConstraint; + + } + + } + + oConfig.setProperty("x", x, true); + oConfig.setProperty("y", y, true); + +}, + + +/** +* @method configVisible +* @description Event handler for when the "visible" configuration property +* the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configVisible: function(p_sType, p_aArgs, p_oMenu) { + + if(this.cfg.getProperty("position") == "dynamic") { + + YAHOO.widget.Menu.superclass.configVisible.call( + this, + p_sType, + p_aArgs, + p_oMenu + ); + + } + else { + + var bVisible = p_aArgs[0]; + var sDisplay = Dom.getStyle(this.element, "display"); + + if(bVisible) { + + if(sDisplay != "block") { + this.beforeShowEvent.fire(); + Dom.setStyle(this.element, "display", "block"); + this.showEvent.fire(); + } + + } + else { + + if(sDisplay == "block") { + this.beforeHideEvent.fire(); + Dom.setStyle(this.element, "display", "none"); + this.hideEvent.fire(); + } + + } + + } + +}, + + +/** +* @method configPosition +* @description Event handler for when the "position" configuration property +* of the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configPosition: function(p_sType, p_aArgs, p_oMenu) { + + var sCSSPosition = p_aArgs[0] == "static" ? "static" : "absolute"; + var oCfg = this.cfg; + + Dom.setStyle(this.element, "position", sCSSPosition); + + + if(sCSSPosition == "static") { + + /* + Remove the iframe for statically positioned menus since it will + intercept mouse events. + */ + + oCfg.setProperty("iframe", false); + + + // Statically positioned menus are visible by default + + Dom.setStyle(this.element, "display", "block"); + + oCfg.setProperty("visible", true); + + } + else { + + /* + Even though the "visible" property is queued to + "false" by default, we need to set the "visibility" property to + "hidden" since Overlay's "configVisible" implementation checks the + element's "visibility" style property before deciding whether + or not to show an Overlay instance. + */ + + Dom.setStyle(this.element, "visibility", "hidden"); + + } + + + if(sCSSPosition == "absolute") { + + var nZIndex = oCfg.getProperty("zindex"); + + if(!nZIndex || nZIndex === 0) { + + nZIndex = this.parent ? + (this.parent.parent.cfg.getProperty("zindex") + 1) : 1; + + oCfg.setProperty("zindex", nZIndex); + + } + + } + +}, + + +/** +* @method configIframe +* @description Event handler for when the "iframe" configuration property of +* the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configIframe: function(p_sType, p_aArgs, p_oMenu) { + + if(this.cfg.getProperty("position") == "dynamic") { + + YAHOO.widget.Menu.superclass.configIframe.call( + this, + p_sType, + p_aArgs, + p_oMenu + ); + + } + +}, + + +/** +* @method configHideDelay +* @description Event handler for when the "hidedelay" configuration property +* of the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configHideDelay: function(p_sType, p_aArgs, p_oMenu) { + + var nHideDelay = p_aArgs[0]; + var oMouseOutEvent = this.mouseOutEvent; + var oMouseOverEvent = this.mouseOverEvent; + var oKeyDownEvent = this.keyDownEvent; + + if(nHideDelay > 0) { + + /* + Only assign event handlers once. This way the user change + the value for the hidedelay as many times as they want. + */ + + if(!this._hideDelayEventHandlersAssigned) { + + oMouseOutEvent.subscribe(this._execHideDelay, true); + oMouseOverEvent.subscribe(this._cancelHideDelay, this, true); + oKeyDownEvent.subscribe(this._cancelHideDelay, this, true); + + this._hideDelayEventHandlersAssigned = true; + + } + + } + else { + + oMouseOutEvent.unsubscribe(this._execHideDelay, this); + oMouseOverEvent.unsubscribe(this._cancelHideDelay, this); + oKeyDownEvent.unsubscribe(this._cancelHideDelay, this); + + this._hideDelayEventHandlersAssigned = false; + + } + +}, + + +/** +* @method configContainer +* @description Event handler for when the "container" configuration property +of the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configContainer: function(p_sType, p_aArgs, p_oMenu) { + + var oElement = p_aArgs[0]; + + if(typeof oElement == 'string') { + + this.cfg.setProperty( + "container", + document.getElementById(oElement), + true + ); + + } + +}, + + + +// Public methods + + +/** +* Event handler called when the resize monitor element's "resize" evet is fired. +*/ +onDomResize: function(e, obj) { + + if(!this._handleResize) { + + this._handleResize = true; + return; + + } + + var oConfig = this.cfg; + + if(oConfig.getProperty("position") == "dynamic") { + + oConfig.setProperty("width", (this._getOffsetWidth() + "px")); + + } + + YAHOO.widget.Menu.superclass.onDomResize.call(this, e, obj); + +}, + + +/** +* @method initEvents +* @description Initializes the custom events for the menu. +*/ +initEvents: function() { + + YAHOO.widget.Menu.superclass.initEvents.call(this); + + // Create custom events + + var CustomEvent = YAHOO.util.CustomEvent; + + this.mouseOverEvent = new CustomEvent("mouseOverEvent", this); + this.mouseOutEvent = new CustomEvent("mouseOutEvent", this); + this.mouseDownEvent = new CustomEvent("mouseDownEvent", this); + this.mouseUpEvent = new CustomEvent("mouseUpEvent", this); + this.clickEvent = new CustomEvent("clickEvent", this); + this.keyPressEvent = new CustomEvent("keyPressEvent", this); + this.keyDownEvent = new CustomEvent("keyDownEvent", this); + this.keyUpEvent = new CustomEvent("keyUpEvent", this); + this.itemAddedEvent = new CustomEvent("itemAddedEvent", this); + this.itemRemovedEvent = new CustomEvent("itemRemovedEvent", this); + +}, + + +/** +* @method getRoot +* @description Finds the menu's root menu. +*/ +getRoot: function() { + + var oItem = this.parent; + + if(oItem) { + + var oParentMenu = oItem.parent; + + return oParentMenu ? oParentMenu.getRoot() : this; + + } + else { + + return this; + + } + +}, + + +/** +* @method toString +* @description Returns a string representing the menu. +* @return {String} +*/ +toString: function() { + + return ("Menu " + this.id); + +}, + + +/** +* @method setItemGroupTitle +* @description Sets the title of a group of menu items. +* @param {String} p_sGroupTitle String specifying the title of the group. +* @param {Number} p_nGroupIndex Optional. Number specifying the group to which +* the title belongs. +*/ +setItemGroupTitle: function(p_sGroupTitle, p_nGroupIndex) { + + if(typeof p_sGroupTitle == "string" && p_sGroupTitle.length > 0) { + + var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0; + var oTitle = this._aGroupTitleElements[nGroupIndex]; + + + if(oTitle) { + + oTitle.innerHTML = p_sGroupTitle; + + } + else { + + oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME); + + oTitle.innerHTML = p_sGroupTitle; + + this._aGroupTitleElements[nGroupIndex] = oTitle; + + } + + + var i = this._aGroupTitleElements.length - 1; + var nFirstIndex; + + do { + + if(this._aGroupTitleElements[i]) { + + Dom.removeClass(this._aGroupTitleElements[i], "first-of-type"); + + nFirstIndex = i; + + } + + } + while(i--); + + + if(nFirstIndex !== null) { + + Dom.addClass( + this._aGroupTitleElements[nFirstIndex], + "first-of-type" + ); + + } + + } + +}, + + + +/** +* @method addItem +* @description Appends an item to the menu. +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance to be added to the menu. +* @param {String} p_oItem String specifying the text of the item to be added +* to the menu. +* @param {Object} p_oItem Object literal containing a set of menu item +* configuration properties. +* @param {Number} p_nGroupIndex Optional. Number indicating the group to +* which the item belongs. +* @return {YAHOO.widget.MenuItem} +*/ +addItem: function(p_oItem, p_nGroupIndex) { + + if(p_oItem) { + + return this._addItemToGroup(p_nGroupIndex, p_oItem); + + } + +}, + + +/** +* @method addItems +* @description Adds an array of items to the menu. +* @param {Array} p_aItems Array of items to be added to the menu. The array +* can contain strings specifying the text for each item to be created, object +* literals specifying each of the menu item configuration properties, +* or MenuItem instances. +* @param {Number} p_nGroupIndex Optional. Number specifying the group to +* which the items belongs. +* @return {Array} +*/ +addItems: function(p_aItems, p_nGroupIndex) { + + function isArray(p_oValue) { + + return (typeof p_oValue == "object" && p_oValue.constructor == Array); + + } + + + if(isArray(p_aItems)) { + + var nItems = p_aItems.length; + var aItems = []; + var oItem; + + + for(var i=0; i <div> element +* (and accompanying child nodes) from the document. +*/ +destroy: function() { + + // Remove Custom Event listeners + + this.mouseOverEvent.unsubscribeAll(); + this.mouseOutEvent.unsubscribeAll(); + this.mouseDownEvent.unsubscribeAll(); + this.mouseUpEvent.unsubscribeAll(); + this.clickEvent.unsubscribeAll(); + this.keyPressEvent.unsubscribeAll(); + this.keyDownEvent.unsubscribeAll(); + this.keyUpEvent.unsubscribeAll(); + + + var nItemGroups = this._aItemGroups.length; + var nItems; + var oItemGroup; + var oItem; + var i; + var n; + + + // Remove all items + + if(nItemGroups > 0) { + + i = nItemGroups - 1; + + do { + + oItemGroup = this._aItemGroups[i]; + + if(oItemGroup) { + + nItems = oItemGroup.length; + + if(nItems > 0) { + + n = nItems - 1; + + do { + + oItem = this._aItemGroups[i][n]; + + if(oItem) { + + oItem.destroy(); + } + + } + while(n--); + + } + + } + + } + while(i--); + + } + + + // Continue with the superclass implementation of this method + + YAHOO.widget.Menu.superclass.destroy.call(this); + + this.logger.log("Destroyed."); + +}, + + +/** +* @method setInitialFocus +* @description Sets focus to the menu's first enabled item. +*/ +setInitialFocus: function() { + + var oItem = this._getFirstEnabledItem(); + + if(oItem) { + + oItem.focus(); + } + +}, + + +/** +* @method setInitialSelection +* @description Sets the "selected" configuration property of the menu's first +* enabled item to "true." +*/ +setInitialSelection: function() { + + var oItem = this._getFirstEnabledItem(); + + if(oItem) { + + oItem.cfg.setProperty("selected", true); + } + +}, + + +/** +* @method clearActiveItem +* @description Sets the "selected" configuration property of the menu's active +* item to "false" and hides the item's submenu. +* @param {Boolean} p_bBlur Boolean indicating if the menu's active item +* should be blurred. +*/ +clearActiveItem: function(p_bBlur) { + + if(this.cfg.getProperty("showdelay") > 0) { + + this._cancelShowDelay(); + + } + + + var oActiveItem = this.activeItem; + + if(oActiveItem) { + + var oConfig = oActiveItem.cfg; + + oConfig.setProperty("selected", false); + + var oSubmenu = oConfig.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.hide(); + + } + + if(p_bBlur) { + + oActiveItem.blur(); + + } + + } + +}, + + +/** +* @description Initializes the class's configurable properties which can be +* changed using the menu's Config object ("cfg"). +* @method initDefaultConfig +*/ +initDefaultConfig: function() { + + YAHOO.widget.Menu.superclass.initDefaultConfig.call(this); + + var oConfig = this.cfg; + + // Add configuration properties + + /* + Change the default value for the "visible" configuration + property to "false" by re-adding the property. + */ + + /** + * @config visible + * @description Boolean indicating whether or not the menu is visible. If + * the menu's "position" configuration property is set to "dynamic" (the + * default), this property toggles the menu's <div>
+ * element's "visibility" style property between "visible" (true) or + * "hidden" (false). If the menu's "position" configuration property is + * set to "static" this property toggles the menu's + *<div>
element's "display" style property + * between "block" (true) or "none" (false). + * @default false + * @type Boolean + */ + oConfig.addProperty( + "visible", + { + value:false, + handler:this.configVisible, + validator:this.cfg.checkBoolean + } + ); + + + /* + Change the default value for the "constraintoviewport" configuration + property to "true" by re-adding the property. + */ + + /** + * @config constraintoviewport + * @description Boolean indicating if the menu will try to remain inside + * the boundaries of the size of viewport. + * @default true + * @type Boolean + */ + oConfig.addProperty( + "constraintoviewport", + { + value:true, + handler:this.configConstrainToViewport, + validator:this.cfg.checkBoolean, + supercedes:["iframe","x","y","xy"] + } + ); + + + /** + * @config position + * @description String indicating how a menu should be positioned on the + * screen. Possible values are "static" and "dynamic." Static menus are + * visible by default and reside in the normal flow of the document + * (CSS position: static). Dynamic menus are hidden by default, reside + * out of the normal flow of the document (CSS position: absolute), and + * can overlay other elements on the screen. + * @default dynamic + * @type String + */ + oConfig.addProperty( + "position", + { + value: "dynamic", + handler: this.configPosition, + validator: this._checkPosition, + supercedes: ["visible"] + } + ); + + + /** + * @config submenualignment + * @description Array defining how submenus should be aligned to their + * parent menu item. The format is: [itemCorner, submenuCorner]. By default + * a submenu's top left corner is aligned to its parent menu item's top + * right corner. + * @default ["tl","tr"] + * @type Array + */ + oConfig.addProperty("submenualignment", { value: ["tl","tr"] } ); + + + /** + * @config autosubmenudisplay + * @description Boolean indicating if submenus are automatically made + * visible when the user mouses over the menu's items. + * @default true + * @type Boolean + */ + oConfig.addProperty( + "autosubmenudisplay", + { + value: true, + validator: oConfig.checkBoolean + } + ); + + + /** + * @config showdelay + * @description Number indicating the time (in milliseconds) that should + * expire before a submenu is made visible when the user mouses over + * the menu's items. + * @default 0 + * @type Number + */ + oConfig.addProperty( + "showdelay", + { + value: 0, + validator: oConfig.checkNumber + } + ); + + + /** + * @config hidedelay + * @description Number indicating the time (in milliseconds) that should + * expire before the menu is hidden. + * @default 0 + * @type Number + */ + oConfig.addProperty( + "hidedelay", + { + value: 0, + validator: oConfig.checkNumber, + handler: this.configHideDelay, + suppressEvent: true + } + ); + + + /** + * @config clicktohide + * @description Boolean indicating if the menu will automatically be + * hidden if the user clicks outside of it. + * @default true + * @type Boolean + */ + oConfig.addProperty( + "clicktohide", + { + value: true, + validator: oConfig.checkBoolean + } + ); + + + /** + * @config container + * @description HTML element reference or string specifying the id + * attribute of the HTML element that the menu's markup should be rendered into. + * @type HTMLElement|String + * @default document.body + */ + this.cfg.addProperty( + "container", + { value:document.body, handler:this.configContainer } + ); + +} + +}); // END YAHOO.extend + +})(); + + +/** +* The base class for all menuing containers. +* +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu module. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* menu module. +* @param {HTMLDivElement} p_oElement Object +* specifying the<div>
element of the menu module. +* @param {HTMLSelectElement} p_oElement Object +* specifying the<select>
element to be used as the data +* source for the menu module. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu module. See configuration class documentation for +* more details. +* @class MenuModule +* @constructor +* @extends YAHOO.widget.Overlay +* @deprecated As of version 0.12, all MenuModule functionality has been +* implemented directly in YAHOO.widget.Menu, making YAHOO.widget.Menu the base +* class for all menuing containers. +*/ +YAHOO.widget.MenuModule = YAHOO.widget.Menu; + + +(function() { + +var Dom = YAHOO.util.Dom; +var Module = YAHOO.widget.Module; +var Menu = YAHOO.widget.Menu; + + +/** +* Creates an item for a menu. +* +* @param {String} p_oObject String specifying the text of the menu item. +* @param {HTMLLIElement} p_oObject Object specifying +* the<li>
element of the menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the menu item. +* @param {HTMLOptionElement} p_oObject Object +* specifying the<option>
element of the menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu item. See configuration class documentation +* for more details. +* @class MenuItem +* @constructor +*/ +YAHOO.widget.MenuItem = function(p_oObject, p_oConfig) { + + if(p_oObject) { + + if(p_oConfig) { + + this.parent = p_oConfig.parent; + this.value = p_oConfig.value; + + } + + this.init(p_oObject, p_oConfig); + + } + +}; + +YAHOO.widget.MenuItem.prototype = { + + // Constants + + /** + * @property SUBMENU_INDICATOR_IMAGE_PATH + * @description String representing the path to the image to be used for the + * menu item's submenu arrow indicator. + * @default "nt/ic/ut/alt1/menuarorght8_nrm_1.gif" + * @final + * @type String + */ + SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarorght8_nrm_1.gif", + + + /** + * @property SELECTED_SUBMENU_INDICATOR_IMAGE_PATH + * @description String representing the path to the image to be used for the + * submenu arrow indicator when the menu item is selected. + * @default "nt/ic/ut/alt1/menuarorght8_hov_1.gif" + * @final + * @type String + */ + SELECTED_SUBMENU_INDICATOR_IMAGE_PATH: + "nt/ic/ut/alt1/menuarorght8_hov_1.gif", + + + /** + * @property DISABLED_SUBMENU_INDICATOR_IMAGE_PATH + * @description String representing the path to the image to be used for the + * submenu arrow indicator when the menu item is disabled. + * @default "nt/ic/ut/alt1/menuarorght8_dim_1.gif" + * @final + * @type String + */ + DISABLED_SUBMENU_INDICATOR_IMAGE_PATH: + "nt/ic/ut/alt1/menuarorght8_dim_1.gif", + + + /** + * @property COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the submenu arrow indicator. + * @default "Collapsed. Click to expand." + * @final + * @type String + */ + COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT: "Collapsed. Click to expand.", + + + /** + * @property EXPANDED_SUBMENU_INDICATOR_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the submenu arrow indicator when the submenu is visible. + * @default "Expanded. Click to collapse." + * @final + * @type String + */ + EXPANDED_SUBMENU_INDICATOR_ALT_TEXT: "Expanded. Click to collapse.", + + + /** + * @property DISABLED_SUBMENU_INDICATOR_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the submenu arrow indicator when the menu item is disabled. + * @default "Disabled." + * @final + * @type String + */ + DISABLED_SUBMENU_INDICATOR_ALT_TEXT: "Disabled.", + + + /** + * @property CHECKED_IMAGE_PATH + * @description String representing the path to the image to be used for + * the checked state. + * @default "nt/ic/ut/bsc/menuchk8_nrm_1.gif" + * @final + * @type String + */ + CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_nrm_1.gif", + + + /** + * @property SELECTED_CHECKED_IMAGE_PATH + * @description String representing the path to the image to be used for + * the selected checked state. + * @default "nt/ic/ut/bsc/menuchk8_hov_1.gif" + * @final + * @type String + */ + SELECTED_CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_hov_1.gif", + + + /** + * @property DISABLED_CHECKED_IMAGE_PATH + * @description String representing the path to the image to be used for + * the disabled checked state. + * @default "nt/ic/ut/bsc/menuchk8_dim_1.gif" + * @final + * @type String + */ + DISABLED_CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_dim_1.gif", + + + /** + * @property CHECKED_IMAGE_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the checked image. + * @default "Checked." + * @final + * @type String + */ + CHECKED_IMAGE_ALT_TEXT: "Checked.", + + + /** + * @property DISABLED_CHECKED_IMAGE_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the checked image when the item is disabled. + * @default "Checked. (Item disabled.)" + * @final + * @type String + */ + DISABLED_CHECKED_IMAGE_ALT_TEXT: "Checked. (Item disabled.)", + + + /** + * @property CSS_CLASS_NAME + * @description String representing the CSS class(es) to be applied to the + *<li>
element of the menu item. + * @default "yuimenuitem" + * @final + * @type String + */ + CSS_CLASS_NAME: "yuimenuitem", + + + /** + * @property SUBMENU_TYPE + * @description Object representing the type of menu to instantiate and + * add when parsing the child nodes of the menu item's source HTML element. + * @final + * @type YAHOO.widget.Menu + */ + SUBMENU_TYPE: null, + + + /** + * @property IMG_ROOT + * @description String representing the prefix path to use for + * non-secure images. + * @default "http://us.i1.yimg.com/us.yimg.com/i/" + * @type String + */ + IMG_ROOT: "http://us.i1.yimg.com/us.yimg.com/i/", + + + /** + * @property IMG_ROOT_SSL + * @description String representing the prefix path to use for securely + * served images. + * @default "https://a248.e.akamai.net/sec.yimg.com/i/" + * @type String + */ + IMG_ROOT_SSL: "https://a248.e.akamai.net/sec.yimg.com/i/", + + + + // Private member variables + + /** + * @property _oAnchor + * @description Object reference to the menu item's + *<a>
element. + * @default null + * @private + * @type HTMLAnchorElement + */ + _oAnchor: null, + + + /** + * @property _oText + * @description Object reference to the menu item's text node. + * @default null + * @private + * @type TextNode + */ + _oText: null, + + + /** + * @property _oHelpTextEM + * @description Object reference to the menu item's help text + *<em>
element. + * @default null + * @private + * @type HTMLElement + */ + _oHelpTextEM: null, + + + /** + * @property _oSubmenu + * @description Object reference to the menu item's submenu. + * @default null + * @private + * @type YAHOO.widget.Menu + */ + _oSubmenu: null, + + + /** + * @property _checkImage + * @description Object reference to the menu item's checkmark image. + * @default null + * @private + * @type HTMLImageElement + */ + _checkImage: null, + + + + // Public properties + + /** + * @property constructor + * @description Object reference to the menu item's constructor function. + * @default YAHOO.widget.MenuItem + * @type YAHOO.widget.MenuItem + */ + constructor: YAHOO.widget.MenuItem, + + + /** + * @property imageRoot + * @description String representing the root path for all of the menu + * item's images. + * @type String + */ + imageRoot: null, + + + /** + * @property isSecure + * @description Boolean representing whether or not the current browsing + * context is secure (HTTPS). + * @type Boolean + */ + isSecure: Module.prototype.isSecure, + + + /** + * @property index + * @description Number indicating the ordinal position of the menu item in + * its group. + * @default null + * @type Number + */ + index: null, + + + /** + * @property groupIndex + * @description Number indicating the index of the group to which the menu + * item belongs. + * @default null + * @type Number + */ + groupIndex: null, + + + /** + * @property parent + * @description Object reference to the menu item's parent menu. + * @default null + * @type YAHOO.widget.Menu + */ + parent: null, + + + /** + * @property element + * @description Object reference to the menu item's + *<li>
element. + * @default HTMLLIElement + * @type HTMLLIElement + */ + element: null, + + + /** + * @property srcElement + * @description Object reference to the HTML element (either + *<li>
,<optgroup>
or + *<option>
) used create the menu item. + * @default HTMLLIElement|HTMLOptGroupElement|HTMLOptionElement + * @type HTMLLIElement| + * HTMLOptGroupElement|HTMLOptionElement + */ + srcElement: null, + + + /** + * @property value + * @description Object reference to the menu item's value. + * @default null + * @type Object + */ + value: null, + + + /** + * @property submenuIndicator + * @description Object reference to the<img>
element + * used to create the submenu indicator for the menu item. + * @default HTMLImageElement + * @type HTMLImageElement + */ + submenuIndicator: null, + + + /** + * @property browser + * @description String representing the browser. + * @type String + */ + browser: Module.prototype.browser, + + + + // Events + + + /** + * @event destroyEvent + * @description Fires when the menu item's<li>
+ * element is removed from its parent<ul>
element. + * @type YAHOO.util.CustomEvent + */ + destroyEvent: null, + + + /** + * @event mouseOverEvent + * @description Fires when the mouse has entered the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseOverEvent: null, + + + /** + * @event mouseOutEvent + * @description Fires when the mouse has left the menu item. Passes back + * the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseOutEvent: null, + + + /** + * @event mouseDownEvent + * @description Fires when the user mouses down on the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseDownEvent: null, + + + /** + * @event mouseUpEvent + * @description Fires when the user releases a mouse button while the mouse + * is over the menu item. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseUpEvent: null, + + + /** + * @event clickEvent + * @description Fires when the user clicks the on the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + clickEvent: null, + + + /** + * @event keyPressEvent + * @description Fires when the user presses an alphanumeric key when the + * menu item has focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + keyPressEvent: null, + + + /** + * @event keyDownEvent + * @description Fires when the user presses a key when the menu item has + * focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + keyDownEvent: null, + + + /** + * @event keyUpEvent + * @description Fires when the user releases a key when the menu item has + * focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + keyUpEvent: null, + + + /** + * @event focusEvent + * @description Fires when the menu item receives focus. + * @type YAHOO.util.CustomEvent + */ + focusEvent: null, + + + /** + * @event blurEvent + * @description Fires when the menu item loses the input focus. + * @type YAHOO.util.CustomEvent + */ + blurEvent: null, + + + /** + * @method init + * @description The MenuItem class's initialization method. 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. + * @param {String} p_oObject String specifying the text of the menu item. + * @param {HTMLLIElement} p_oObject Object specifying + * the<li>
element of the menu item. + * @param {HTMLOptGroupElement} p_oObject Object + * specifying the<optgroup>
element of the menu item. + * @param {HTMLOptionElement} p_oObject Object + * specifying the<option>
element of the menu item. + * @param {Object} p_oConfig Optional. Object literal specifying the + * configuration for the menu item. See configuration class documentation + * for more details. + */ + init: function(p_oObject, p_oConfig) { + + this.imageRoot = (this.isSecure) ? this.IMG_ROOT_SSL : this.IMG_ROOT; + + + if(!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = Menu; + + } + + + // Create the config object + + this.cfg = new YAHOO.util.Config(this); + + this.initDefaultConfig(); + + var oConfig = this.cfg; + + + if(this._checkString(p_oObject)) { + + this._createRootNodeStructure(); + + oConfig.setProperty("text", p_oObject); + + } + else if(this._checkDOMNode(p_oObject)) { + + switch(p_oObject.tagName.toUpperCase()) { + + case "OPTION": + + this._createRootNodeStructure(); + + oConfig.setProperty("text", p_oObject.text); + + this.srcElement = p_oObject; + + break; + + case "OPTGROUP": + + this._createRootNodeStructure(); + + oConfig.setProperty("text", p_oObject.label); + + this.srcElement = p_oObject; + + this._initSubTree(); + + break; + + case "LI": + + // Get the anchor node (if it exists) + + var oAnchor = this._getFirstElement(p_oObject, "A"); + var sURL = "#"; + var sTarget = null; + var sText = null; + + + // Capture the "text" and/or the "URL" + + if(oAnchor) { + + sURL = oAnchor.getAttribute("href"); + sTarget = oAnchor.getAttribute("target"); + + if(oAnchor.innerText) { + + sText = oAnchor.innerText; + + } + else { + + var oRange = oAnchor.ownerDocument.createRange(); + + oRange.selectNodeContents(oAnchor); + + sText = oRange.toString(); + + } + + } + else { + + var oText = p_oObject.firstChild; + + sText = oText.nodeValue; + + oAnchor = document.createElement("a"); + + oAnchor.setAttribute("href", sURL); + + p_oObject.replaceChild(oAnchor, oText); + + oAnchor.appendChild(oText); + + } + + + this.srcElement = p_oObject; + this.element = p_oObject; + this._oAnchor = oAnchor; + + + // Check if emphasis has been applied to the MenuItem + + var oEmphasisNode = this._getFirstElement(oAnchor); + var bEmphasis = false; + var bStrongEmphasis = false; + + if(oEmphasisNode) { + + // Set a reference to the text node + + this._oText = oEmphasisNode.firstChild; + + switch(oEmphasisNode.tagName.toUpperCase()) { + + case "EM": + + bEmphasis = true; + + break; + + case "STRONG": + + bStrongEmphasis = true; + + break; + + } + + } + else { + + // Set a reference to the text node + + this._oText = oAnchor.firstChild; + + } + + + /* + Set these properties silently to sync up the + configuration object without making changes to the + element's DOM + */ + + oConfig.setProperty("text", sText, true); + oConfig.setProperty("url", sURL, true); + oConfig.setProperty("target", sTarget, true); + oConfig.setProperty("emphasis", bEmphasis, true); + oConfig.setProperty( + "strongemphasis", + bStrongEmphasis, + true + ); + + this._initSubTree(); + + break; + + } + + } + + + if(this.element) { + + + Dom.addClass(this.element, this.CSS_CLASS_NAME); + + + // Create custom events + + var CustomEvent = YAHOO.util.CustomEvent; + + this.destroyEvent = new CustomEvent("destroyEvent", this); + this.mouseOverEvent = new CustomEvent("mouseOverEvent", this); + this.mouseOutEvent = new CustomEvent("mouseOutEvent", this); + this.mouseDownEvent = new CustomEvent("mouseDownEvent", this); + this.mouseUpEvent = new CustomEvent("mouseUpEvent", this); + this.clickEvent = new CustomEvent("clickEvent", this); + this.keyPressEvent = new CustomEvent("keyPressEvent", this); + this.keyDownEvent = new CustomEvent("keyDownEvent", this); + this.keyUpEvent = new CustomEvent("keyUpEvent", this); + this.focusEvent = new CustomEvent("focusEvent", this); + this.blurEvent = new CustomEvent("blurEvent", this); + + + if(p_oConfig) { + + oConfig.applyConfig(p_oConfig); + + } + + oConfig.fireQueue(); + + } + + }, + + + + // Private methods + + /** + * @method _getFirstElement + * @description Returns an HTML element's first HTML element node. + * @private + * @param {HTMLElement} p_oElement Object + * reference specifying the element to be evaluated. + * @param {String} p_sTagName Optional. String specifying the tagname of + * the element to be retrieved. + * @return {HTMLElement} + */ + _getFirstElement: function(p_oElement, p_sTagName) { + + var oElement; + + if(p_oElement.firstChild && p_oElement.firstChild.nodeType == 1) { + + oElement = p_oElement.firstChild; + + } + else if( + p_oElement.firstChild && + p_oElement.firstChild.nextSibling && + p_oElement.firstChild.nextSibling.nodeType == 1 + ) { + + oElement = p_oElement.firstChild.nextSibling; + + } + + + if(p_sTagName) { + + return (oElement && oElement.tagName.toUpperCase() == p_sTagName) ? + oElement : false; + + } + + return oElement; + + }, + + + /** + * @method _checkString + * @description Determines if an object is a string. + * @private + * @param {Object} p_oObject Object to be evaluated. + * @return {Boolean} + */ + _checkString: function(p_oObject) { + + return (typeof p_oObject == "string"); + + }, + + + /** + * @method _checkDOMNode + * @description Determines if an object is an HTML element. + * @private + * @param {Object} p_oObject Object to be evaluated. + * @return {Boolean} + */ + _checkDOMNode: function(p_oObject) { + + return (p_oObject && p_oObject.tagName); + + }, + + + /** + * @method _createRootNodeStructure + * @description Creates the core DOM structure for the menu item. + * @private + */ + _createRootNodeStructure: function () { + + this.element = document.createElement("li"); + + this._oText = document.createTextNode(""); + + this._oAnchor = document.createElement("a"); + this._oAnchor.appendChild(this._oText); + + this.cfg.refireEvent("url"); + + this.element.appendChild(this._oAnchor); + + }, + + + /** + * @method _initSubTree + * @description Iterates the source element's childNodes collection and uses + * the child nodes to instantiate other menus. + * @private + */ + _initSubTree: function() { + + var oSrcEl = this.srcElement; + var oConfig = this.cfg; + + + if(oSrcEl.childNodes.length > 0) { + + if( + this.parent.lazyLoad && + this.parent.srcElement && + this.parent.srcElement.tagName.toUpperCase() == "SELECT" + ) { + + oConfig.setProperty( + "submenu", + { id: Dom.generateId(), itemdata: oSrcEl.childNodes } + ); + + } + else { + + var oNode = oSrcEl.firstChild; + var aOptions = []; + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case "DIV": + + oConfig.setProperty("submenu", oNode); + + break; + + case "OPTION": + + aOptions[aOptions.length] = oNode; + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + + var nOptions = aOptions.length; + + if(nOptions > 0) { + + var oMenu = new this.SUBMENU_TYPE(Dom.generateId()); + + oConfig.setProperty("submenu", oMenu); + + for(var n=0; n0) { + + oAnchor.setAttribute("target", sTarget); + + } + else { + + oAnchor.removeAttribute("target"); + + } + + }, + + + /** + * @method configEmphasis + * @description Event handler for when the "emphasis" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configEmphasis: function(p_sType, p_aArgs, p_oItem) { + + var bEmphasis = p_aArgs[0]; + var oAnchor = this._oAnchor; + var oText = this._oText; + var oConfig = this.cfg; + var oEM; + + + if(bEmphasis && oConfig.getProperty("strongemphasis")) { + + oConfig.setProperty("strongemphasis", false); + + } + + + if(oAnchor) { + + if(bEmphasis) { + + oEM = document.createElement("em"); + oEM.appendChild(oText); + + oAnchor.appendChild(oEM); + + } + else { + + oEM = this._getFirstElement(oAnchor, "EM"); + + oAnchor.removeChild(oEM); + oAnchor.appendChild(oText); + + } + + } + + }, + + + /** + * @method configStrongEmphasis + * @description Event handler for when the "strongemphasis" configuration + * property of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configStrongEmphasis: function(p_sType, p_aArgs, p_oItem) { + + var bStrongEmphasis = p_aArgs[0]; + var oAnchor = this._oAnchor; + var oText = this._oText; + var oConfig = this.cfg; + var oStrong; + + if(bStrongEmphasis && oConfig.getProperty("emphasis")) { + + oConfig.setProperty("emphasis", false); + + } + + if(oAnchor) { + + if(bStrongEmphasis) { + + oStrong = document.createElement("strong"); + oStrong.appendChild(oText); + + oAnchor.appendChild(oStrong); + + } + else { + + oStrong = this._getFirstElement(oAnchor, "STRONG"); + + oAnchor.removeChild(oStrong); + oAnchor.appendChild(oText); + + } + + } + + }, + + + /** + * @method configChecked + * @description Event handler for when the "checked" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configChecked: function(p_sType, p_aArgs, p_oItem) { + + var bChecked = p_aArgs[0]; + var oEl = this.element; + var oConfig = this.cfg; + var oImg; + + + if(bChecked) { + + this._preloadImage(this.CHECKED_IMAGE_PATH); + this._preloadImage(this.SELECTED_CHECKED_IMAGE_PATH); + this._preloadImage(this.DISABLED_CHECKED_IMAGE_PATH); + + + oImg = document.createElement("img"); + oImg.src = (this.imageRoot + this.CHECKED_IMAGE_PATH); + oImg.alt = this.CHECKED_IMAGE_ALT_TEXT; + + var oSubmenu = this.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oEl.insertBefore(oImg, oSubmenu.element); + + } + else { + + oEl.appendChild(oImg); + + } + + + Dom.addClass([oEl, oImg], "checked"); + + this._checkImage = oImg; + + if(oConfig.getProperty("disabled")) { + + oConfig.refireEvent("disabled"); + + } + + if(oConfig.getProperty("selected")) { + + oConfig.refireEvent("selected"); + + } + + } + else { + + oImg = this._checkImage; + + Dom.removeClass([oEl, oImg], "checked"); + + if(oImg) { + + oEl.removeChild(oImg); + + } + + this._checkImage = null; + + } + + }, + + + + /** + * @method configDisabled + * @description Event handler for when the "disabled" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configDisabled: function(p_sType, p_aArgs, p_oItem) { + + var bDisabled = p_aArgs[0]; + var oAnchor = this._oAnchor; + var aNodes = [this.element, oAnchor]; + var oEM = this._oHelpTextEM; + var oConfig = this.cfg; + var oImg; + var sImgSrc; + var sImgAlt; + + + if(oEM) { + + aNodes[2] = oEM; + + } + + + if(this.cfg.getProperty("checked")) { + + sImgAlt = this.CHECKED_IMAGE_ALT_TEXT; + sImgSrc = this.CHECKED_IMAGE_PATH; + oImg = this._checkImage; + + if(bDisabled) { + + sImgAlt = this.DISABLED_CHECKED_IMAGE_ALT_TEXT; + sImgSrc = this.DISABLED_CHECKED_IMAGE_PATH; + + } + + oImg.src = document.images[(this.imageRoot + sImgSrc)].src; + oImg.alt = sImgAlt; + + } + + + oImg = this.submenuIndicator; + + if(bDisabled) { + + if(oConfig.getProperty("selected")) { + + oConfig.setProperty("selected", false); + + } + + oAnchor.removeAttribute("href"); + + Dom.addClass(aNodes, "disabled"); + + sImgSrc = this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH; + sImgAlt = this.DISABLED_SUBMENU_INDICATOR_ALT_TEXT; + + } + else { + + oAnchor.setAttribute("href", oConfig.getProperty("url")); + + Dom.removeClass(aNodes, "disabled"); + + sImgSrc = this.SUBMENU_INDICATOR_IMAGE_PATH; + sImgAlt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT; + + } + + + if(oImg) { + + oImg.src = this.imageRoot + sImgSrc; + oImg.alt = sImgAlt; + + } + + }, + + + /** + * @method configSelected + * @description Event handler for when the "selected" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configSelected: function(p_sType, p_aArgs, p_oItem) { + + if(!this.cfg.getProperty("disabled")) { + + var bSelected = p_aArgs[0]; + var oEM = this._oHelpTextEM; + var aNodes = [this.element, this._oAnchor]; + var oImg = this.submenuIndicator; + var sImgSrc; + + + if(oEM) { + + aNodes[aNodes.length] = oEM; + + } + + if(oImg) { + + aNodes[aNodes.length] = oImg; + + } + + + if(this.cfg.getProperty("checked")) { + + sImgSrc = this.imageRoot + (bSelected ? + this.SELECTED_CHECKED_IMAGE_PATH : this.CHECKED_IMAGE_PATH); + + this._checkImage.src = document.images[sImgSrc].src; + + } + + + if(bSelected) { + + Dom.addClass(aNodes, "selected"); + sImgSrc = this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH; + + } + else { + + Dom.removeClass(aNodes, "selected"); + sImgSrc = this.SUBMENU_INDICATOR_IMAGE_PATH; + + } + + if(oImg) { + + oImg.src = document.images[(this.imageRoot + sImgSrc)].src; + + } + + } + + }, + + + /** + * @method configSubmenu + * @description Event handler for when the "submenu" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configSubmenu: function(p_sType, p_aArgs, p_oItem) { + + var oEl = this.element; + var oSubmenu = p_aArgs[0]; + var oImg = this.submenuIndicator; + var oConfig = this.cfg; + var aNodes = [this.element, this._oAnchor]; + var oMenu; + var bLazyLoad = this.parent && this.parent.lazyLoad; + + + if(oSubmenu) { + + if(oSubmenu instanceof Menu) { + + oMenu = oSubmenu; + oMenu.parent = this; + oMenu.lazyLoad = bLazyLoad; + + } + else if( + typeof oSubmenu == "object" && + oSubmenu.id && + !oSubmenu.nodeType + ) { + + var sSubmenuId = oSubmenu.id; + var oSubmenuConfig = oSubmenu; + + delete oSubmenu["id"]; + + oSubmenuConfig.lazyload = bLazyLoad; + oSubmenuConfig.parent = this; + + oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig); + + + // Set the value of the property to the Menu instance + + this.cfg.setProperty("submenu", oMenu, true); + + } + else { + + oMenu = new this.SUBMENU_TYPE( + oSubmenu, + { lazyload: bLazyLoad, parent: this } + ); + + + // Set the value of the property to the Menu instance + + this.cfg.setProperty("submenu", oMenu, true); + + } + + + if(oMenu) { + + this._oSubmenu = oMenu; + + + if(!oImg) { + + this._preloadImage(this.SUBMENU_INDICATOR_IMAGE_PATH); + this._preloadImage( + this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH + ); + + this._preloadImage( + this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH + ); + + oImg = document.createElement("img"); + + oImg.src = + (this.imageRoot + this.SUBMENU_INDICATOR_IMAGE_PATH); + + oImg.alt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT; + + oEl.appendChild(oImg); + + this.submenuIndicator = oImg; + + Dom.addClass(aNodes, "hassubmenu"); + + + if(oConfig.getProperty("disabled")) { + + oConfig.refireEvent("disabled"); + + } + + if(oConfig.getProperty("selected")) { + + oConfig.refireEvent("selected"); + + } + + } + + } + + } + else { + + Dom.removeClass(aNodes, "hassubmenu"); + + if(oImg) { + + oEl.removeChild(oImg); + + } + + if(this._oSubmenu) { + + this._oSubmenu.destroy(); + + } + + } + + }, + + + // Public methods + + /** + * @method initDefaultConfig + * @description Initializes an item's configurable properties. + */ + initDefaultConfig : function() { + + var oConfig = this.cfg; + var CheckBoolean = oConfig.checkBoolean; + + + // Define the config properties + + /** + * @config text + * @description String specifying the text label for the menu item. + * When building a menu from existing HTML the value of this property + * will be interpreted from the menu's markup. + * @default "" + * @type String + */ + oConfig.addProperty( + "text", + { + value: "", + handler: this.configText, + validator: this._checkString, + suppressEvent: true + } + ); + + + /** + * @config helptext + * @description String specifying additional instructional text to + * accompany the text for the nenu item. + * @default null + * @type String| + * HTMLElement + */ + oConfig.addProperty("helptext", { handler: this.configHelpText }); + + + /** + * @config url + * @description String specifying the URL for the menu item's anchor's + * "href" attribute. When building a menu from existing HTML the value + * of this property will be interpreted from the menu's markup. + * @default "#" + * @type String + */ + oConfig.addProperty( + "url", + { value: "#", handler: this.configURL, suppressEvent: true } + ); + + + /** + * @config target + * @description String specifying the value for the "target" attribute + * of the menu item's anchor element. Specifying a target will + * require the user to click directly on the menu item's anchor node in + * order to cause the browser to navigate to the specified URL. + * When building a menu from existing HTML the value of this property + * will be interpreted from the menu's markup. + * @default null + * @type String + */ + oConfig.addProperty( + "target", + { handler: this.configTarget, suppressEvent: true } + ); + + + /** + * @config emphasis + * @description Boolean indicating if the text of the menu item will be + * rendered with emphasis. When building a menu from existing HTML the + * value of this property will be interpreted from the menu's markup. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "emphasis", + { + value: false, + handler: this.configEmphasis, + validator: CheckBoolean, + suppressEvent: true + } + ); + + + /** + * @config strongemphasis + * @description Boolean indicating if the text of the menu item will be + * rendered with strong emphasis. When building a menu from existing + * HTML the value of this property will be interpreted from the + * menu's markup. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "strongemphasis", + { + value: false, + handler: this.configStrongEmphasis, + validator: CheckBoolean, + suppressEvent: true + } + ); + + + /** + * @config checked + * @description Boolean indicating if the menu item should be rendered + * with a checkmark. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "checked", + { + value: false, + handler: this.configChecked, + validator: this.cfg.checkBoolean, + suppressEvent: true, + supercedes:["disabled"] + } + ); + + + /** + * @config disabled + * @description Boolean indicating if the menu item should be disabled. + * (Disabled menu items are dimmed and will not respond to user input + * or fire events.) + * @default false + * @type Boolean + */ + oConfig.addProperty( + "disabled", + { + value: false, + handler: this.configDisabled, + validator: CheckBoolean, + suppressEvent: true + } + ); + + + /** + * @config selected + * @description Boolean indicating if the menu item should + * be highlighted. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "selected", + { + value: false, + handler: this.configSelected, + validator: CheckBoolean, + suppressEvent: true + } + ); + + + /** + * @config submenu + * @description Object specifying the submenu to be appended to the + * menu item. The value can be one of the following: + * @default null + * @type Menu|String|Object| + * HTMLElement + */ + oConfig.addProperty("submenu", { handler: this.configSubmenu }); + + }, + + + /** + * @method getNextEnabledSibling + * @description Finds the menu item's next enabled sibling. + * @return YAHOO.widget.MenuItem + */ + getNextEnabledSibling: function() { + + if(this.parent instanceof Menu) { + + var nGroupIndex = this.groupIndex; + + /** + * Finds the next item in an array. + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getNextArrayItem = function(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] || + getNextArrayItem(p_aArray, (p_nStartIndex+1)); + + }; + + + var aItemGroups = this.parent.getItemGroups(); + var oNextItem; + + + if(this.index < (aItemGroups[nGroupIndex].length - 1)) { + + oNextItem = getNextArrayItem( + aItemGroups[nGroupIndex], + (this.index+1) + ); + + } + else { + + var nNextGroupIndex; + + if(nGroupIndex < (aItemGroups.length - 1)) { + + nNextGroupIndex = nGroupIndex + 1; + + } + else { + + nNextGroupIndex = 0; + + } + + var aNextGroup = getNextArrayItem(aItemGroups, nNextGroupIndex); + + // Retrieve the first menu item in the next group + + oNextItem = getNextArrayItem(aNextGroup, 0); + + } + + return ( + oNextItem.cfg.getProperty("disabled") || + oNextItem.element.style.display == "none" + ) ? + oNextItem.getNextEnabledSibling() : oNextItem; + + } + + }, + + + /** + * @method getPreviousEnabledSibling + * @description Finds the menu item's previous enabled sibling. + * @return {YAHOO.widget.MenuItem} + */ + getPreviousEnabledSibling: function() { + + if(this.parent instanceof Menu) { + + var nGroupIndex = this.groupIndex; + + /** + * Returns the previous item in an array + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getPreviousArrayItem = function(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] || + getPreviousArrayItem(p_aArray, (p_nStartIndex-1)); + + }; + + + /** + * Get the index of the first item in an array + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getFirstItemIndex = function(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] ? + p_nStartIndex : + getFirstItemIndex(p_aArray, (p_nStartIndex+1)); + + }; + + var aItemGroups = this.parent.getItemGroups(); + var oPreviousItem; + + if( + this.index > getFirstItemIndex(aItemGroups[nGroupIndex], 0) + ) { + + oPreviousItem = + getPreviousArrayItem( + aItemGroups[nGroupIndex], + (this.index-1) + ); + + } + else { + + var nPreviousGroupIndex; + + if(nGroupIndex > getFirstItemIndex(aItemGroups, 0)) { + + nPreviousGroupIndex = nGroupIndex - 1; + + } + else { + + nPreviousGroupIndex = aItemGroups.length - 1; + + } + + var aPreviousGroup = + getPreviousArrayItem(aItemGroups, nPreviousGroupIndex); + + oPreviousItem = + getPreviousArrayItem( + aPreviousGroup, + (aPreviousGroup.length - 1) + ); + + } + + return ( + oPreviousItem.cfg.getProperty("disabled") || + oPreviousItem.element.style.display == "none" + ) ? + oPreviousItem.getPreviousEnabledSibling() : oPreviousItem; + + } + + }, + + + /** + * @method focus + * @description Causes the menu item to receive the focus and fires the + * focus event. + */ + focus: function() { + + var oParent = this.parent; + var oAnchor = this._oAnchor; + var oActiveItem = oParent.activeItem; + + if( + !this.cfg.getProperty("disabled") && + oParent && + oParent.cfg.getProperty("visible") && + this.element.style.display != "none" + ) { + + if(oActiveItem) { + + oActiveItem.blur(); + + } + + try { + + oAnchor.focus(); + + } + catch(e) { + + } + + this.focusEvent.fire(); + + } + + }, + + + /** + * @method blur + * @description Causes the menu item to lose focus and fires the + * onblur event. + */ + blur: function() { + + var oParent = this.parent; + + if( + !this.cfg.getProperty("disabled") && + oParent && + Dom.getStyle(oParent.element, "visibility") == "visible" + ) { + + this._oAnchor.blur(); + + this.blurEvent.fire(); + + } + + }, + + + /** + * @method destroy + * @description Removes the menu item's
- Object + * specifying a Menu instance.
- Object literal specifying the + * menu to be created. Format:
{ id: [menu id], itemdata: + * [array of values for + * items] }
.- String specifying the id attribute + * of the
<div>
element of the menu.- + * Object specifying the
<div>
element of the + * menu.<li>
element + * from its parent<ul>
element. + */ + destroy: function() { + + var oEl = this.element; + + if(oEl) { + + // Remove CustomEvent listeners + + this.mouseOverEvent.unsubscribeAll(); + this.mouseOutEvent.unsubscribeAll(); + this.mouseDownEvent.unsubscribeAll(); + this.mouseUpEvent.unsubscribeAll(); + this.clickEvent.unsubscribeAll(); + this.keyPressEvent.unsubscribeAll(); + this.keyDownEvent.unsubscribeAll(); + this.keyUpEvent.unsubscribeAll(); + this.focusEvent.unsubscribeAll(); + this.blurEvent.unsubscribeAll(); + this.cfg.configChangedEvent.unsubscribeAll(); + + + // Remove the element from the parent node + + var oParentNode = oEl.parentNode; + + if(oParentNode) { + + oParentNode.removeChild(oEl); + + this.destroyEvent.fire(); + + } + + this.destroyEvent.unsubscribeAll(); + + } + + }, + + + /** + * @method toString + * @description Returns a string representing the menu item. + * @return {String} + */ + toString: function() { + + return ("MenuItem: " + this.cfg.getProperty("text")); + + } + +}; + +})(); + + +/** +* Creates an item for a menu module. +* +* @param {String} p_oObject String specifying the text of the menu module item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the menu module item. +* @param {HTMLOptGroupElement} p_oObject Object specifying +* the<optgroup>
element of the menu module item. +* @param {HTMLOptionElement} p_oObject Object specifying the +*<option>
element of the menu module item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu module item. See configuration class documentation +* for more details. +* @class MenuModuleItem +* @constructor +* @deprecated As of version 0.12, all MenuModuleItem functionality has been +* implemented directly in YAHOO.widget.MenuItem, making YAHOO.widget.MenuItem +* the base class for all menu items. +*/ +YAHOO.widget.MenuModuleItem = YAHOO.widget.MenuItem; + + +/** +* Creates a list of options or commands which are made visible in response to +* an HTML element's "contextmenu" event ("mousedown" for Opera). +* +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the context menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* context menu. +* @param {HTMLDivElement} p_oElement Object specifying the +*<div>
element of the context menu. +* @param {HTMLSelectElement} p_oElement Object specifying +* the<select>
element to be used as the data source for +* the context menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu. See configuration class documentation +* for more details. +* @class ContextMenu +* @constructor +* @extends YAHOO.widget.Menu +* @namespace YAHOO.widget +*/ +YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) { + + YAHOO.widget.ContextMenu.superclass.constructor.call( + this, + p_oElement, + p_oConfig + ); + +}; + + +YAHOO.extend(YAHOO.widget.ContextMenu, YAHOO.widget.Menu, { + + + +// Private properties + + +/** +* @property _oTrigger +* @description Object reference to the current value of the "trigger" +* configuration property. +* @default null +* @private +* @type String|HTMLElement|Array +*/ +_oTrigger: null, + + + +// Public properties + + +/** +* @property contextEventTarget +* @description Object reference for the HTML element that was the target of the +* "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of +* the context menu. +* @default null +* @type HTMLElement +*/ +contextEventTarget: null, + + +/** +* @method init +* @description The ContextMenu class's initialization method. 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. +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the context menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for +* the context menu. +* @param {HTMLDivElement} p_oElement Object specifying the +*<div>
element of the context menu. +* @param {HTMLSelectElement} p_oElement Object specifying +* the<select>
element to be used as the data source for +* the context menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu. See configuration class documentation +* for more details. +*/ +init: function(p_oElement, p_oConfig) { + + if(!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.ContextMenuItem; + + } + + + // Call the init of the superclass (YAHOO.widget.Menu) + + YAHOO.widget.ContextMenu.superclass.init.call(this, p_oElement); + + + this.beforeInitEvent.fire(YAHOO.widget.ContextMenu); + + + if(p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + + this.initEvent.fire(YAHOO.widget.ContextMenu); + +}, + + + +// Private methods + + +/** +* @method _removeEventHandlers +* @description Removes all of the DOM event handlers from the HTML element(s) +* whose "context menu" event ("click" for Opera) trigger the display of +* the context menu. +* @private +*/ +_removeEventHandlers: function() { + + var Event = YAHOO.util.Event; + var oTrigger = this._oTrigger; + var bOpera = (this.browser == "opera"); + + + // Remove the event handlers from the trigger(s) + + Event.removeListener( + oTrigger, + (bOpera ? "mousedown" : "contextmenu"), + this._onTriggerContextMenu + ); + + if(bOpera) { + + Event.removeListener(oTrigger, "click", this._onTriggerClick); + + } + +}, + + + +// Private event handlers + + +/** +* @method _onTriggerClick +* @description "click" event handler for the HTML element(s) identified as the +* "trigger" for the context menu. Used to cancel default behaviors in Opera. +* @private +* @param {Event} p_oEvent Object representing the DOM event object passed back +* by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that is handling the event. +*/ +_onTriggerClick: function(p_oEvent, p_oMenu) { + + if(p_oEvent.ctrlKey) { + + YAHOO.util.Event.stopEvent(p_oEvent); + + } + +}, + + +/** +* @method _onTriggerContextMenu +* @description "contextmenu" event handler ("mousedown" for Opera) for the HTML +* element(s) that trigger the display of the context menu. +* @private +* @param {Event} p_oEvent Object representing the DOM event object passed back +* by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that is handling the event. +*/ +_onTriggerContextMenu: function(p_oEvent, p_oMenu) { + + // Hide any other ContextMenu instances that might be visible + + YAHOO.widget.MenuManager.hideVisible(); + + + var Event = YAHOO.util.Event; + var oConfig = this.cfg; + + if(p_oEvent.type == "mousedown" && !p_oEvent.ctrlKey) { + + return; + + } + + this.contextEventTarget = Event.getTarget(p_oEvent); + + + // Position and display the context menu + + var nX = Event.getPageX(p_oEvent); + var nY = Event.getPageY(p_oEvent); + + + oConfig.applyConfig( { xy:[nX, nY], visible:true } ); + oConfig.fireQueue(); + + + /* + Prevent the browser's default context menu from appearing and + stop the propagation of the "contextmenu" event so that + other ContextMenu instances are not displayed. + */ + + Event.stopEvent(p_oEvent); + +}, + + + +// Public methods + + +/** +* @method toString +* @description Returns a string representing the context menu. +* @return {String} +*/ +toString: function() { + + return ("ContextMenu " + this.id); + +}, + + +/** +* @method initDefaultConfig +* @description Initializes the class's configurable properties which can be +* changed using the context menu's Config object ("cfg"). +*/ +initDefaultConfig: function() { + + YAHOO.widget.ContextMenu.superclass.initDefaultConfig.call(this); + + /** + * @config trigger + * @description The HTML element(s) whose "contextmenu" event ("mousedown" + * for Opera) trigger the display of the context menu. Can be a string + * representing the id attribute of the HTML element, an object reference + * for the HTML element, or an array of strings or HTML element references. + * @default null + * @type String|HTMLElement|Array + */ + this.cfg.addProperty("trigger", { handler: this.configTrigger }); + +}, + + +/** +* @method destroy +* @description Removes the context menu's<div>
element +* (and accompanying child nodes) from the document. +*/ +destroy: function() { + + // Remove the DOM event handlers from the current trigger(s) + + this._removeEventHandlers(); + + + // Continue with the superclass implementation of this method + + YAHOO.widget.ContextMenu.superclass.destroy.call(this); + +}, + + + +// Public event handlers for configuration properties + + +/** +* @method configTrigger +* @description Event handler for when the value of the "trigger" configuration +* property changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that fired the event. +*/ +configTrigger: function(p_sType, p_aArgs, p_oMenu) { + + var Event = YAHOO.util.Event; + var oTrigger = p_aArgs[0]; + + if(oTrigger) { + + /* + If there is a current "trigger" - remove the event handlers + from that element(s) before assigning new ones + */ + + if(this._oTrigger) { + + this._removeEventHandlers(); + + } + + this._oTrigger = oTrigger; + + + /* + Listen for the "mousedown" event in Opera b/c it does not + support the "contextmenu" event + */ + + var bOpera = (this.browser == "opera"); + + Event.addListener( + oTrigger, + (bOpera ? "mousedown" : "contextmenu"), + this._onTriggerContextMenu, + this, + true + ); + + + /* + Assign a "click" event handler to the trigger element(s) for + Opera to prevent default browser behaviors. + */ + + if(bOpera) { + + Event.addListener( + oTrigger, + "click", + this._onTriggerClick, + this, + true + ); + + } + + } + else { + + this._removeEventHandlers(); + + } + +} + +}); // END YAHOO.extend + + +/** +* Creates an item for a context menu. +* +* @param {String} p_oObject String specifying the text of the context menu item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the context menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the context +* menu item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the context menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu item. See configuration class +* documentation for more details. +* @class ContextMenuItem +* @constructor +* @extends YAHOO.widget.MenuItem +*/ +YAHOO.widget.ContextMenuItem = function(p_oObject, p_oConfig) { + + YAHOO.widget.ContextMenuItem.superclass.constructor.call( + this, + p_oObject, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.ContextMenuItem, YAHOO.widget.MenuItem, { + + +/** +* @method init +* @description The ContextMenuItem class's initialization method. 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. +* @param {String} p_oObject String specifying the text of the context menu item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the context menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the context +* menu item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the context menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu item. See configuration class +* documentation for more details. +*/ +init: function(p_oObject, p_oConfig) { + + if(!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = YAHOO.widget.ContextMenu; + + } + + + /* + Call the init of the superclass (YAHOO.widget.MenuItem) + Note: We don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.ContextMenuItem.superclass.init.call(this, p_oObject); + + var oConfig = this.cfg; + + if(p_oConfig) { + + oConfig.applyConfig(p_oConfig, true); + + } + + oConfig.fireQueue(); + +}, + + + +// Public methods + + +/** +* @method toString +* @description Returns a string representing the context menu item. +* @return {String} +*/ +toString: function() { + + return ("MenuBarItem: " + this.cfg.getProperty("text")); + +} + +}); // END YAHOO.extend + + +/** +* Horizontal collection of items, each of which can contain a submenu. +* +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu bar. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* menu bar. +* @param {HTMLDivElement} p_oElement Object specifying +* the<div>
element of the menu bar. +* @param {HTMLSelectElement} p_oElement Object +* specifying the<select>
element to be used as the data +* source for the menu bar. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar. See configuration class documentation for +* more details. +* @class Menubar +* @constructor +* @extends YAHOO.widget.Menu +* @namespace YAHOO.widget +*/ +YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) { + + YAHOO.widget.MenuBar.superclass.constructor.call( + this, + p_oElement, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.MenuBar, YAHOO.widget.Menu, { + +/** +* @method init +* @description The MenuBar class's initialization method. 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. +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu bar. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* menu bar. +* @param {HTMLDivElement} p_oElement Object specifying +* the<div>
element of the menu bar. +* @param {HTMLSelectElement} p_oElement Object +* specifying the<select>
element to be used as the data +* source for the menu bar. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar. See configuration class documentation for +* more details. +*/ +init: function(p_oElement, p_oConfig) { + + if(!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.MenuBarItem; + + } + + + // Call the init of the superclass (YAHOO.widget.Menu) + + YAHOO.widget.MenuBar.superclass.init.call(this, p_oElement); + + + this.beforeInitEvent.fire(YAHOO.widget.MenuBar); + + + if(p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + this.initEvent.fire(YAHOO.widget.MenuBar); + +}, + + + +// Constants + + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the menu +* bar's<div>
element. +* @default "yuimenubar" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenubar", + + + +// Protected event handlers + + +/** +* @method _onKeyDown +* @description "keydown" Custom Event handler for the menu bar. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar +* that fired the event. +*/ +_onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) { + + var Event = YAHOO.util.Event; + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oItemCfg = oItem.cfg; + var oSubmenu; + + + switch(oEvent.keyCode) { + + case 27: // Esc key + + if(this.cfg.getProperty("position") == "dynamic") { + + this.hide(); + + if(this.parent) { + + this.parent.focus(); + + } + + } + else if(this.activeItem) { + + oSubmenu = this.activeItem.cfg.getProperty("submenu"); + + if(oSubmenu && oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + this.activeItem.focus(); + + } + else { + + this.activeItem.cfg.setProperty("selected", false); + this.activeItem.blur(); + + } + + } + + + Event.preventDefault(oEvent); + + break; + + case 37: // Left arrow + case 39: // Right arrow + + if( + oItem == this.activeItem && + !oItemCfg.getProperty("selected") + ) { + + oItemCfg.setProperty("selected", true); + + } + else { + + var oNextItem = (oEvent.keyCode == 37) ? + oItem.getPreviousEnabledSibling() : + oItem.getNextEnabledSibling(); + + if(oNextItem) { + + this.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + + + if(this.cfg.getProperty("autosubmenudisplay")) { + + oSubmenu = oNextItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.show(); + oSubmenu.activeItem.blur(); + oSubmenu.activeItem = null; + + } + + } + + oNextItem.focus(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + case 40: // Down arrow + + if(this.activeItem != oItem) { + + this.clearActiveItem(); + + oItemCfg.setProperty("selected", true); + oItem.focus(); + + } + + oSubmenu = oItemCfg.getProperty("submenu"); + + if(oSubmenu) { + + if(oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.setInitialSelection(); + oSubmenu.setInitialFocus(); + + } + else { + + oSubmenu.show(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + } + +}, + + +/** +* @method _onClick +* @description "click" event handler for the menu bar. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar +* that fired the event. +*/ +_onClick: function(p_sType, p_aArgs, p_oMenuBar) { + + YAHOO.widget.MenuBar.superclass._onClick.call( + this, + p_sType, + p_aArgs, + p_oMenuBar + ); + + + var oItem = p_aArgs[1]; + + if(oItem) { + + var Event = YAHOO.util.Event; + var Dom = YAHOO.util.Dom; + + var oEvent = p_aArgs[0]; + var oTarget = Event.getTarget(oEvent); + + var oActiveItem = this.activeItem; + var oConfig = this.cfg; + + + // Hide any other submenus that might be visible + + if(oActiveItem && oActiveItem != oItem) { + + this.clearActiveItem(); + + } + + + // Select and focus the current item + + oItem.cfg.setProperty("selected", true); + oItem.focus(); + + + // Show the submenu for the item + + var oSubmenu = oItem.cfg.getProperty("submenu"); + + + if(oSubmenu && oTarget != oItem.submenuIndicator) { + + if(oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + + } + else { + + oSubmenu.show(); + + } + + } + + } + +}, + + + +// Public methods + + +/** +* @method toString +* @description Returns a string representing the menu bar. +* @return {String} +*/ +toString: function() { + + return ("MenuBar " + this.id); + +}, + + +/** +* @description Initializes the class's configurable properties which can be +* changed using the menu bar's Config object ("cfg"). +* @method initDefaultConfig +*/ +initDefaultConfig: function() { + + YAHOO.widget.MenuBar.superclass.initDefaultConfig.call(this); + + var oConfig = this.cfg; + + // Add configuration properties + + + /* + Set the default value for the "position" configuration property + to "static" by re-adding the property. + */ + + /** + * @config position + * @description String indicating how a menu bar should be positioned on the + * screen. Possible values are "static" and "dynamic." Static menu bars + * are visible by default and reside in the normal flow of the document + * (CSS position: static). Dynamic menu bars are hidden by default, reside + * out of the normal flow of the document (CSS position: absolute), and can + * overlay other elements on the screen. + * @default static + * @type String + */ + oConfig.addProperty( + "position", + { + value: "static", + handler: this.configPosition, + validator: this._checkPosition, + supercedes: ["visible"] + } + ); + + + /* + Set the default value for the "submenualignment" configuration property + to ["tl","bl"] by re-adding the property. + */ + + /** + * @config submenualignment + * @description Array defining how submenus should be aligned to their + * parent menu bar item. The format is: [itemCorner, submenuCorner]. + * @default ["tl","bl"] + * @type Array + */ + oConfig.addProperty("submenualignment", { value: ["tl","bl"] } ); + + + /* + Change the default value for the "autosubmenudisplay" configuration + property to "false" by re-adding the property. + */ + + /** + * @config autosubmenudisplay + * @description Boolean indicating if submenus are automatically made + * visible when the user mouses over the menu bar's items. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "autosubmenudisplay", + { value: false, validator: oConfig.checkBoolean } + ); + +} + +}); // END YAHOO.extend + + +/** +* Creates an item for a menu bar. +* +* @param {String} p_oObject String specifying the text of the menu bar item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the menu bar item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the menu bar item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the menu bar item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar item. See configuration class documentation +* for more details. +* @class MenuBarItem +* @constructor +* @extends YAHOO.widget.MenuItem +*/ +YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) { + + YAHOO.widget.MenuBarItem.superclass.constructor.call( + this, + p_oObject, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, { + + +/** +* @method init +* @description The MenuBarItem class's initialization method. 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. +* @param {String} p_oObject String specifying the text of the menu bar item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the menu bar item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the menu bar item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the menu bar item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar item. See configuration class documentation +* for more details. +*/ +init: function(p_oObject, p_oConfig) { + + if(!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = YAHOO.widget.Menu; + + } + + + /* + Call the init of the superclass (YAHOO.widget.MenuItem) + Note: We don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject); + + + var oConfig = this.cfg; + + if(p_oConfig) { + + oConfig.applyConfig(p_oConfig, true); + + } + + oConfig.fireQueue(); + +}, + + + +// Constants + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the +*<li>
element of the menu bar item. +* @default "yuimenubaritem" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenubaritem", + + +/** +* @property SUBMENU_INDICATOR_IMAGE_PATH +* @description String representing the path to the image to be used for the +* menu bar item's submenu arrow indicator. +* @default "nt/ic/ut/alt1/menuarodwn8_nrm_1.gif" +* @final +* @type String +*/ +SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_nrm_1.gif", + + +/** +* @property SELECTED_SUBMENU_INDICATOR_IMAGE_PATH +* @description String representing the path to the image to be used for the +* submenu arrow indicator when the menu bar item is selected. +* @default "nt/ic/ut/alt1/menuarodwn8_hov_1.gif" +* @final +* @type String +*/ +SELECTED_SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_hov_1.gif", + + +/** +* @property DISABLED_SUBMENU_INDICATOR_IMAGE_PATH +* @description String representing the path to the image to be used for the +* submenu arrow indicator when the menu bar item is disabled. +* @default "nt/ic/ut/alt1/menuarodwn8_dim_1.gif" +* @final +* @type String +*/ +DISABLED_SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_dim_1.gif", + + + +// Public methods + + +/** +* @method toString +* @description Returns a string representing the menu bar item. +* @return {String} +*/ +toString: function() { + + return ("MenuBarItem: " + this.cfg.getProperty("text")); + +} + +}); // END YAHOO.extend \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/menu/menu-min.js b/source/web/scripts/ajax/yahoo/menu/menu-min.js new file mode 100644 index 0000000000..479cfd7fd3 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/menu/menu-min.js @@ -0,0 +1 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ (function(){var _1=YAHOO.util.Dom;var _2=YAHOO.util.Event;YAHOO.widget.MenuManager=new function(){var _3=false;var _4={};var _5={};var _6={};var _7=function(_8){var _9=_1.generateId();if(_8&&_5[_9]!=_8){_8.element.setAttribute("yuiid",_9);_5[_9]=_8;_8.destroyEvent.subscribe(onItemDestroy,_8);}};var _a=function(_b){var _c=_b.element.getAttribute("yuiid");if(_c&&_5[_c]){delete _5[_c];}};var _d=function(_e){var _f;if(_e&&_e.tagName){switch(_e.tagName.toUpperCase()){case "DIV":_f=_e.parentNode;if(_1.hasClass(_e,"bd")&&_f&&_f.tagName&&_f.tagName.toUpperCase()=="DIV"){return _f;}else{return _e;}break;case "LI":return _e;default:_f=_e.parentNode;if(_f){return _d(_f);}break;}}};var _10=function(_11){var _12=_2.getTarget(_11);var _13=_d(_12);var _14;var _15;if(_13){var _16=_13.tagName.toUpperCase();if(_16=="LI"){var _17=_13.getAttribute("yuiid");if(_17){_14=_5[_17];_15=_14.parent;}}else{if(_16=="DIV"){if(_13.id){_15=_4[_13.id];}}}}if(_15){var _18={"click":"clickEvent","mousedown":"mouseDownEvent","mouseup":"mouseUpEvent","mouseover":"mouseOverEvent","mouseout":"mouseOutEvent","keydown":"keyDownEvent","keyup":"keyUpEvent","keypress":"keyPressEvent"};var _19=_18[_11.type];if(_14&&!_14.cfg.getProperty("disabled")){_14[_19].fire(_11);}_15[_19].fire(_11,_14);}else{if(_11.type=="mousedown"){var _1a;for(var i in _4){if(_4.hasOwnProperty(i)){_15=_4[i];if(_15.cfg.getProperty("clicktohide")&&_15.cfg.getProperty("position")=="dynamic"){_15.hide();}else{_15.clearActiveItem(true);}}}}}};var _1c=function(_1d,_1e,_1f){this.removeMenu(_1f);};var _20=function(_21,_22,_23){var _24=_23.element.getAttribute("yuiid");if(_24){delete _5[_24];}};var _25=function(_26,_27,_28){var _29=_27[0];if(_29){_6[_28.id]=_28;}else{if(_6[_28.id]){delete _6[_28.id];}}};var _2a=function(_2b,_2c){_7(_2c[0]);};var _2d=function(_2e,_2f){_a(_2f[0]);};this.addMenu=function(_30){if(_30&&_30.id&&!_4[_30.id]){_4[_30.id]=_30;if(!_3){var _31=document;_2.addListener(_31,"mouseover",_10,this,true);_2.addListener(_31,"mouseout",_10,this,true);_2.addListener(_31,"mousedown",_10,this,true);_2.addListener(_31,"mouseup",_10,this,true);_2.addListener(_31,"click",_10,this,true);_2.addListener(_31,"keydown",_10,this,true);_2.addListener(_31,"keyup",_10,this,true);_2.addListener(_31,"keypress",_10,this,true);_3=true;}_30.destroyEvent.subscribe(_1c,_30,this);_30.cfg.subscribeToConfigEvent("visible",_25,_30);_30.itemAddedEvent.subscribe(_2a);_30.itemRemovedEvent.subscribe(_2d);}};this.removeMenu=function(_32){if(_32&&_4[_32.id]){delete _4[_32.id];}};this.hideVisible=function(){var _33;for(var i in _6){if(_6.hasOwnProperty(i)){_33=_6[i];if(_33.cfg.getProperty("position")=="dynamic"){_33.hide();}}}};this.getMenus=function(){return _4;};this.getMenu=function(_35){if(_4[_35]){return _4[_35];}};this.toString=function(){return ("MenuManager");};};})();(function(){var Dom=YAHOO.util.Dom;var _37=YAHOO.util.Event;YAHOO.widget.Menu=function(_38,_39){if(_39){this.parent=_39.parent;this.lazyLoad=_39.lazyLoad||_39.lazyload;this.itemData=_39.itemData||_39.itemdata;}YAHOO.widget.Menu.superclass.constructor.call(this,_38,_39);};YAHOO.extend(YAHOO.widget.Menu,YAHOO.widget.Overlay,{CSS_CLASS_NAME:"yuimenu",ITEM_TYPE:null,GROUP_TITLE_TAG_NAME:"h6",_nHideDelayId:null,_nShowDelayId:null,_hideDelayEventHandlersAssigned:false,_bHandledMouseOverEvent:false,_bHandledMouseOutEvent:false,_aGroupTitleElements:null,_aItemGroups:null,_aListElements:null,lazyLoad:false,itemData:null,activeItem:null,parent:null,srcElement:null,mouseOverEvent:null,mouseOutEvent:null,mouseDownEvent:null,mouseUpEvent:null,clickEvent:null,keyPressEvent:null,keyDownEvent:null,keyUpEvent:null,itemAddedEvent:null,itemRemovedEvent:null,init:function(_3a,_3b){this._aItemGroups=[];this._aListElements=[];this._aGroupTitleElements=[];if(!this.ITEM_TYPE){this.ITEM_TYPE=YAHOO.widget.MenuItem;}var _3c;if(typeof _3a=="string"){_3c=document.getElementById(_3a);}else{if(_3a.tagName){_3c=_3a;}}if(_3c&&_3c.tagName){switch(_3c.tagName.toUpperCase()){case "DIV":this.srcElement=_3c;if(!_3c.id){_3c.setAttribute("id",Dom.generateId());}YAHOO.widget.Menu.superclass.init.call(this,_3c);this.beforeInitEvent.fire(YAHOO.widget.Menu);break;case "SELECT":this.srcElement=_3c;YAHOO.widget.Menu.superclass.init.call(this,Dom.generateId());this.beforeInitEvent.fire(YAHOO.widget.Menu);break;}}else{YAHOO.widget.Menu.superclass.init.call(this,_3a);this.beforeInitEvent.fire(YAHOO.widget.Menu);}if(this.element){var oEl=this.element;Dom.addClass(oEl,this.CSS_CLASS_NAME);this.initEvent.subscribe(this._onInit,this,true);this.beforeRenderEvent.subscribe(this._onBeforeRender,this,true);this.renderEvent.subscribe(this._onRender,this,true);this.beforeShowEvent.subscribe(this._onBeforeShow,this,true);this.showEvent.subscribe(this._onShow,this,true);this.beforeHideEvent.subscribe(this._onBeforeHide,this,true);this.mouseOverEvent.subscribe(this._onMouseOver,this,true);this.mouseOutEvent.subscribe(this._onMouseOut,this,true);this.clickEvent.subscribe(this._onClick,this,true);this.keyDownEvent.subscribe(this._onKeyDown,this,true);if(_3b){this.cfg.applyConfig(_3b,true);}YAHOO.widget.MenuManager.addMenu(this);this.initEvent.fire(YAHOO.widget.Menu);}},_initSubTree:function(){var _3e;if(this.srcElement.tagName=="DIV"){_3e=this.body.firstChild;var _3f=0;var _40=this.GROUP_TITLE_TAG_NAME.toUpperCase();do{if(_3e&&_3e.tagName){switch(_3e.tagName.toUpperCase()){case _40:this._aGroupTitleElements[_3f]=_3e;break;case "UL":this._aListElements[_3f]=_3e;this._aItemGroups[_3f]=[];_3f++;break;}}}while((_3e=_3e.nextSibling));if(this._aListElements[0]){Dom.addClass(this._aListElements[0],"first-of-type");}}_3e=null;if(this.srcElement.tagName){switch(this.srcElement.tagName.toUpperCase()){case "DIV":if(this._aListElements.length>0){var i=this._aListElements.length-1;do{_3e=this._aListElements[i].firstChild;do{if(_3e&&_3e.tagName){switch(_3e.tagName.toUpperCase()){case "LI":this.addItem(new this.ITEM_TYPE(_3e,{parent:this}),i);break;}}}while((_3e=_3e.nextSibling));}while(i--);}break;case "SELECT":_3e=this.srcElement.firstChild;do{if(_3e&&_3e.tagName){switch(_3e.tagName.toUpperCase()){case "OPTGROUP":case "OPTION":this.addItem(new this.ITEM_TYPE(_3e,{parent:this}));break;}}}while((_3e=_3e.nextSibling));break;}}},_getFirstEnabledItem:function(){var _42=this._aItemGroups.length;var _43;var _44;for(var i=0;i<_42;i++){_44=this._aItemGroups[i];if(_44){var _46=_44.length;for(var n=0;n<_46;n++){_43=_44[n];if(!_43.cfg.getProperty("disabled")&&_43.element.style.display!="none"){return _43;}_43=null;}}}},_checkPosition:function(_48){if(typeof _48=="string"){var _49=_48.toLowerCase();return ("dynamic,static".indexOf(_49)!=-1);}},_addItemToGroup:function(_4a,_4b,_4c){var _4d;if(_4b instanceof this.ITEM_TYPE){_4d=_4b;_4d.parent=this;}else{if(typeof _4b=="string"){_4d=new this.ITEM_TYPE(_4b,{parent:this});}else{if(typeof _4b=="object"&&_4b.text){var _4e=_4b.text;delete _4b["text"];_4b.parent=this;_4d=new this.ITEM_TYPE(_4e,_4b);}}}if(_4d){var _4f=typeof _4a=="number"?_4a:0;var _50=this._getItemGroup(_4f);var _51;if(!_50){_50=this._createItemGroup(_4f);}if(typeof _4c=="number"){var _52=(_4c>=_50.length);if(_50[_4c]){_50.splice(_4c,0,_4d);}else{_50[_4c]=_4d;}_51=_50[_4c];if(_51){if(_52&&(!_51.element.parentNode||_51.element.parentNode.nodeType==11)){this._aListElements[_4f].appendChild(_51.element);}else{var _53=function(_54,_55){return (_54[_55]||_53(_54,(_55+1)));};var _56=_53(_50,(_4c+1));if(_56&&(!_51.element.parentNode||_51.element.parentNode.nodeType==11)){this._aListElements[_4f].insertBefore(_51.element,_56.element);}}_51.parent=this;this._subscribeToItemEvents(_51);this._configureSubmenu(_51);this._updateItemProperties(_4f);this.itemAddedEvent.fire(_51);return _51;}}else{var _57=_50.length;_50[_57]=_4d;_51=_50[_57];if(_51){if(!Dom.isAncestor(this._aListElements[_4f],_51.element)){this._aListElements[_4f].appendChild(_51.element);}_51.element.setAttribute("groupindex",_4f);_51.element.setAttribute("index",_57);_51.parent=this;_51.index=_57;_51.groupIndex=_4f;this._subscribeToItemEvents(_51);this._configureSubmenu(_51);if(_57===0){Dom.addClass(_51.element,"first-of-type");}this.itemAddedEvent.fire(_51);return _51;}}}},_removeItemFromGroupByIndex:function(_58,_59){var _5a=typeof _58=="number"?_58:0;var _5b=this._getItemGroup(_5a);if(_5b){var _5c=_5b.splice(_59,1);var _5d=_5c[0];if(_5d){this._updateItemProperties(_5a);if(_5b.length===0){var oUL=this._aListElements[_5a];if(this.body&&oUL){this.body.removeChild(oUL);}this._aItemGroups.splice(_5a,1);this._aListElements.splice(_5a,1);oUL=this._aListElements[0];if(oUL){Dom.addClass(oUL,"first-of-type");}}this.itemRemovedEvent.fire(_5d);return _5d;}}},_removeItemFromGroupByValue:function(_5f,_60){var _61=this._getItemGroup(_5f);if(_61){var _62=_61.length;var _63=-1;if(_62>0){var i=_62-1;do{if(_61[i]==_60){_63=i;break;}}while(i--);if(_63>-1){return this._removeItemFromGroupByIndex(_5f,_63);}}}},_updateItemProperties:function(_65){var _66=this._getItemGroup(_65);var _67=_66.length;if(_67>0){var i=_67-1;var _69;var oLI;do{_69=_66[i];if(_69){oLI=_69.element;_69.index=i;_69.groupIndex=_65;oLI.setAttribute("groupindex",_65);oLI.setAttribute("index",i);Dom.removeClass(oLI,"first-of-type");}}while(i--);if(oLI){Dom.addClass(oLI,"first-of-type");}}},_createItemGroup:function(_6b){if(!this._aItemGroups[_6b]){this._aItemGroups[_6b]=[];var oUL=document.createElement("ul");this._aListElements[_6b]=oUL;return this._aItemGroups[_6b];}},_getItemGroup:function(_6d){var _6e=((typeof _6d=="number")?_6d:0);return this._aItemGroups[_6e];},_configureSubmenu:function(_6f){var _70=_6f.cfg.getProperty("submenu");if(_70){this.cfg.configChangedEvent.subscribe(this._onParentMenuConfigChange,_70,true);this.renderEvent.subscribe(this._onParentMenuRender,_70,true);_70.beforeShowEvent.subscribe(this._onSubmenuBeforeShow,_70,true);_70.showEvent.subscribe(this._onSubmenuShow,_70,true);_70.hideEvent.subscribe(this._onSubmenuHide,_70,true);}},_subscribeToItemEvents:function(_71){_71.focusEvent.subscribe(this._onMenuItemFocus,_71,this);_71.blurEvent.subscribe(this._onMenuItemBlur,this,true);_71.cfg.configChangedEvent.subscribe(this._onMenuItemConfigChange,_71,this);},_getOffsetWidth:function(){var _72=this.element.cloneNode(true);Dom.setStyle(_72,"width","");document.body.appendChild(_72);var _73=_72.offsetWidth;document.body.removeChild(_72);return _73;},_cancelHideDelay:function(){var _74=this.getRoot();if(_74._nHideDelayId){window.clearTimeout(_74._nHideDelayId);}},_execHideDelay:function(){this._cancelHideDelay();var _75=this.getRoot();var me=this;var _77=function(){if(_75.activeItem){_75.clearActiveItem();}if(_75==me&&me.cfg.getProperty("position")=="dynamic"){me.hide();}};_75._nHideDelayId=window.setTimeout(_77,_75.cfg.getProperty("hidedelay"));},_cancelShowDelay:function(){var _78=this.getRoot();if(_78._nShowDelayId){window.clearTimeout(_78._nShowDelayId);}},_execShowDelay:function(_79){this._cancelShowDelay();var _7a=this.getRoot();var _7b=function(){_79.show();};_7a._nShowDelayId=window.setTimeout(_7b,_7a.cfg.getProperty("showdelay"));},_onMouseOver:function(_7c,_7d,_7e){var _7f=_7d[0];var _80=_7d[1];var _81=_37.getTarget(_7f);if(!this._bHandledMouseOverEvent&&(_81==this.element||Dom.isAncestor(this.element,_81))){this.clearActiveItem();this._bHandledMouseOverEvent=true;this._bHandledMouseOutEvent=false;}if(_80&&!_80.handledMouseOverEvent&&(_81==_80.element||Dom.isAncestor(_80.element,_81))){var _82=_80.cfg;_82.setProperty("selected",true);_80.focus();if(this.cfg.getProperty("autosubmenudisplay")){var _83=_82.getProperty("submenu");if(_83){if(this.cfg.getProperty("showdelay")>0){this._execShowDelay(_83);}else{_83.show();}}}_80.handledMouseOverEvent=true;_80.handledMouseOutEvent=false;}},_onMouseOut:function(_84,_85,_86){var _87=_85[0];var _88=_85[1];var _89=_37.getRelatedTarget(_87);var _8a=false;if(_88){var _8b=_88.cfg;var _8c=_8b.getProperty("submenu");if(_8c&&(_89==_8c.element||Dom.isAncestor(_8c.element,_89))){_8a=true;}if(!_88.handledMouseOutEvent&&((_89!=_88.element&&!Dom.isAncestor(_88.element,_89))||_8a)){if(this.cfg.getProperty("showdelay")>0){this._cancelShowDelay();}if(!_8a){_8b.setProperty("selected",false);}if(this.cfg.getProperty("autosubmenudisplay")){if(_8c){if(!(_89==_8c.element||YAHOO.util.Dom.isAncestor(_8c.element,_89))){_8c.hide();}}}_88.handledMouseOutEvent=true;_88.handledMouseOverEvent=false;}}if(!this._bHandledMouseOutEvent&&((_89!=this.element&&!Dom.isAncestor(this.element,_89))||_8a)){this._bHandledMouseOutEvent=true;this._bHandledMouseOverEvent=false;}},_onClick:function(_8d,_8e,_8f){var _90=_8e[0];var _91=_8e[1];var _92=_37.getTarget(_90);if(_91){var _93=_91.cfg;var _94=_93.getProperty("submenu");if(_92==_91.submenuIndicator&&_94){if(_94.cfg.getProperty("visible")){_94.hide();}else{this.clearActiveItem();this.activeItem=_91;_91.cfg.setProperty("selected",true);_94.show();}}else{var _95=_93.getProperty("url");var _96=(_95.substr((_95.length-1),1)=="#");var _97=_93.getProperty("target");var _98=(_97&&_97.length>0);if(_92.tagName.toUpperCase()=="A"&&_96&&!_98){_37.preventDefault(_90);}if(_92.tagName.toUpperCase()!="A"&&!_96&&!_98){document.location=_95;}if(_96&&!_94){var _99=this.getRoot();if(_99.cfg.getProperty("position")=="static"){_99.clearActiveItem();}else{_99.hide();}}}}},_onKeyDown:function(_9a,_9b,_9c){var _9d=_9b[0];var _9e=_9b[1];var _9f;if(_9e){var _a0=_9e.cfg;var _a1=this.parent;var _a2;var _a3;switch(_9d.keyCode){case 38:case 40:if(_9e==this.activeItem&&!_a0.getProperty("selected")){_a0.setProperty("selected",true);}else{_a3=(_9d.keyCode==38)?_9e.getPreviousEnabledSibling():_9e.getNextEnabledSibling();if(_a3){this.clearActiveItem();_a3.cfg.setProperty("selected",true);_a3.focus();}}_37.preventDefault(_9d);break;case 39:_9f=_a0.getProperty("submenu");if(_9f){if(!_a0.getProperty("selected")){_a0.setProperty("selected",true);}_9f.show();_9f.setInitialSelection();}else{_a2=this.getRoot();if(_a2 instanceof YAHOO.widget.MenuBar){_a3=_a2.activeItem.getNextEnabledSibling();if(_a3){_a2.clearActiveItem();_a3.cfg.setProperty("selected",true);_9f=_a3.cfg.getProperty("submenu");if(_9f){_9f.show();}_a3.focus();}}}_37.preventDefault(_9d);break;case 37:if(_a1){var _a4=_a1.parent;if(_a4 instanceof YAHOO.widget.MenuBar){_a3=_a4.activeItem.getPreviousEnabledSibling();if(_a3){_a4.clearActiveItem();_a3.cfg.setProperty("selected",true);_9f=_a3.cfg.getProperty("submenu");if(_9f){_9f.show();}_a3.focus();}}else{this.hide();_a1.focus();}}_37.preventDefault(_9d);break;}}if(_9d.keyCode==27){if(this.cfg.getProperty("position")=="dynamic"){this.hide();if(this.parent){this.parent.focus();}}else{if(this.activeItem){_9f=this.activeItem.cfg.getProperty("submenu");if(_9f&&_9f.cfg.getProperty("visible")){_9f.hide();this.activeItem.focus();}else{this.activeItem.cfg.setProperty("selected",false);this.activeItem.blur();}}}_37.preventDefault(_9d);}},_onInit:function(_a5,_a6,_a7){if(((this.parent&&!this.lazyLoad)||(!this.parent&&this.cfg.getProperty("position")=="static")||(!this.parent&&!this.lazyLoad&&this.cfg.getProperty("position")=="dynamic"))&&this.getItemGroups().length===0){if(this.srcElement){this._initSubTree();}if(this.itemData){this.addItems(this.itemData);}}else{if(this.lazyLoad){this.cfg.fireQueue();}}},_onBeforeRender:function(_a8,_a9,_aa){var _ab=this.cfg;var oEl=this.element;var _ad=this._aListElements.length;if(_ad>0){var i=0;var _af=true;var oUL;var _b1;do{oUL=this._aListElements[i];if(oUL){if(_af){Dom.addClass(oUL,"first-of-type");_af=false;}if(!Dom.isAncestor(oEl,oUL)){this.appendToBody(oUL);}_b1=this._aGroupTitleElements[i];if(_b1){if(!Dom.isAncestor(oEl,_b1)){oUL.parentNode.insertBefore(_b1,oUL);}Dom.addClass(oUL,"hastitle");}}i++;}while(i<_ad);}},_onRender:function(_b2,_b3,_b4){if(this.cfg.getProperty("position")=="dynamic"){var _b5=this.element.parentNode.tagName.toUpperCase()=="BODY"?this.element.offsetWidth:this._getOffsetWidth();this.cfg.setProperty("width",(_b5+"px"));}},_onBeforeShow:function(_b6,_b7,_b8){if(this.lazyLoad&&this.getItemGroups().length===0){if(this.srcElement){this._initSubTree();}if(this.itemData){if(this.parent&&this.parent.parent&&this.parent.parent.srcElement&&this.parent.parent.srcElement.tagName.toUpperCase()=="SELECT"){var _b9=this.itemData.length;for(var n=0;n<_b9;n++){if(this.itemData[n].tagName){this.addItem((new this.ITEM_TYPE(this.itemData[n])));}}}else{this.addItems(this.itemData);}}if(this.srcElement){this.render();}else{if(this.parent){this.render(this.parent.element);}else{this.render(this.cfg.getProperty("container"));}}}},_onShow:function(_bb,_bc,_bd){this.setInitialFocus();var _be=this.parent;if(_be){var _bf=_be.parent;var _c0=_bf.cfg.getProperty("submenualignment");var _c1=this.cfg.getProperty("submenualignment");if((_c0[0]!=_c1[0])&&(_c0[1]!=_c1[1])){this.cfg.setProperty("submenualignment",[_c0[0],_c0[1]]);}if(!_bf.cfg.getProperty("autosubmenudisplay")&&_bf.cfg.getProperty("position")=="static"){_bf.cfg.setProperty("autosubmenudisplay",true);var _c2=function(_c3){if(_c3.type=="mousedown"||(_c3.type=="keydown"&&_c3.keyCode==27)){var _c4=_37.getTarget(_c3);if(_c4!=_bf.element||!YAHOO.util.Dom.isAncestor(_bf.element,_c4)){_bf.cfg.setProperty("autosubmenudisplay",false);_37.removeListener(document,"mousedown",_c2);_37.removeListener(document,"keydown",_c2);}}};_37.addListener(document,"mousedown",_c2);_37.addListener(document,"keydown",_c2);}}},_onBeforeHide:function(_c5,_c6,_c7){this.clearActiveItem(true);},_onParentMenuConfigChange:function(_c8,_c9,_ca){var _cb=_c9[0][0];var _cc=_c9[0][1];switch(_cb){case "iframe":case "constraintoviewport":case "hidedelay":case "showdelay":case "clicktohide":case "effect":_ca.cfg.setProperty(_cb,_cc);break;}},_onParentMenuRender:function(_cd,_ce,_cf){var _d0=_cf.parent.parent;var _d1={constraintoviewport:_d0.cfg.getProperty("constraintoviewport"),xy:[0,0],clicktohide:_d0.cfg.getProperty("clicktohide"),effect:_d0.cfg.getProperty("effect")};var _d2=_d0.cfg.getProperty("showdelay");if(_d2>0){_d1.showdelay=_d2;}var _d3=_d0.cfg.getProperty("hidedelay");if(_d3>0){_d1.hidedelay=_d3;}if(this.cfg.getProperty("position")==_d0.cfg.getProperty("position")){_d1.iframe=_d0.cfg.getProperty("iframe");}_cf.cfg.applyConfig(_d1);if(!this.lazyLoad){if(Dom.inDocument(this.element)){this.render();}else{this.render(this.parent.element);}}},_onSubmenuBeforeShow:function(_d4,_d5,_d6){var _d7=this.parent;var _d8=_d7.parent.cfg.getProperty("submenualignment");this.cfg.setProperty("context",[_d7.element,_d8[0],_d8[1]]);_d7.submenuIndicator.alt=_d7.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT;},_onSubmenuShow:function(_d9,_da,_db){var _dc=this.parent;_dc.submenuIndicator.alt=_dc.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT;},_onSubmenuHide:function(_dd,_de,_df){var _e0=this.parent;_e0.submenuIndicator.alt=_e0.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;},_onMenuItemFocus:function(_e1,_e2,_e3){this.activeItem=_e3;},_onMenuItemBlur:function(_e4,_e5){this.activeItem=null;},_onMenuItemConfigChange:function(_e6,_e7,_e8){var _e9=_e7[0][0];switch(_e9){case "submenu":var _ea=_e7[0][1];if(_ea){this._configureSubmenu(_e8);}break;case "text":case "helptext":if(this.element.style.width){var _eb=this._getOffsetWidth()+"px";Dom.setStyle(this.element,"width",_eb);}break;}},enforceConstraints:function(_ec,_ed,obj){var _ef=this.cfg;var pos=_ed[0];var x=pos[0];var y=pos[1];var bod=document.getElementsByTagName("body")[0];var htm=document.getElementsByTagName("html")[0];var _f5=Dom.getStyle(bod,"overflow");var _f6=Dom.getStyle(htm,"overflow");var _f7=this.element.offsetHeight;var _f8=this.element.offsetWidth;var _f9=Dom.getClientWidth();var _fa=Dom.getClientHeight();var _fb=window.scrollX||document.body.scrollLeft;var _fc=window.scrollY||document.body.scrollTop;var _fd=_fc+10;var _fe=_fb+10;var _ff=_fc+_fa-_f7-10;var _100=_fb+_f9-_f8-10;var _101=_ef.getProperty("context");var _102=_101?_101[0]:null;if(x<10){x=_fe;}else{if((x+_f8)>_f9){if(_102&&((x-_102.offsetWidth)>_f8)){x=(x-(_102.offsetWidth+_f8));}else{x=_100;}}}if(y<10){y=_fd;}else{if(y>_ff){if(_102&&(y>_f7)){y=((y+_102.offsetHeight)-_f7);}else{y=_ff;}}}_ef.setProperty("x",x,true);_ef.setProperty("y",y,true);},configVisible:function(_103,_104,_105){if(this.cfg.getProperty("position")=="dynamic"){YAHOO.widget.Menu.superclass.configVisible.call(this,_103,_104,_105);}else{var _106=_104[0];var _107=Dom.getStyle(this.element,"display");if(_106){if(_107!="block"){this.beforeShowEvent.fire();Dom.setStyle(this.element,"display","block");this.showEvent.fire();}}else{if(_107=="block"){this.beforeHideEvent.fire();Dom.setStyle(this.element,"display","none");this.hideEvent.fire();}}}},configPosition:function(_108,_109,_10a){var _10b=_109[0]=="static"?"static":"absolute";var oCfg=this.cfg;Dom.setStyle(this.element,"position",_10b);if(_10b=="static"){oCfg.setProperty("iframe",false);Dom.setStyle(this.element,"display","block");oCfg.setProperty("visible",true);}else{Dom.setStyle(this.element,"visibility","hidden");}if(_10b=="absolute"){var _10d=oCfg.getProperty("zindex");if(!_10d||_10d===0){_10d=this.parent?(this.parent.parent.cfg.getProperty("zindex")+1):1;oCfg.setProperty("zindex",_10d);}}},configIframe:function(_10e,_10f,_110){if(this.cfg.getProperty("position")=="dynamic"){YAHOO.widget.Menu.superclass.configIframe.call(this,_10e,_10f,_110);}},configHideDelay:function(_111,_112,_113){var _114=_112[0];var _115=this.mouseOutEvent;var _116=this.mouseOverEvent;var _117=this.keyDownEvent;if(_114>0){if(!this._hideDelayEventHandlersAssigned){_115.subscribe(this._execHideDelay,true);_116.subscribe(this._cancelHideDelay,this,true);_117.subscribe(this._cancelHideDelay,this,true);this._hideDelayEventHandlersAssigned=true;}}else{_115.unsubscribe(this._execHideDelay,this);_116.unsubscribe(this._cancelHideDelay,this);_117.unsubscribe(this._cancelHideDelay,this);this._hideDelayEventHandlersAssigned=false;}},configContainer:function(_118,_119,_11a){var _11b=_119[0];if(typeof _11b=="string"){this.cfg.setProperty("container",document.getElementById(_11b),true);}},onDomResize:function(e,obj){if(!this._handleResize){this._handleResize=true;return;}var _11e=this.cfg;if(_11e.getProperty("position")=="dynamic"){_11e.setProperty("width",(this._getOffsetWidth()+"px"));}YAHOO.widget.Menu.superclass.onDomResize.call(this,e,obj);},initEvents:function(){YAHOO.widget.Menu.superclass.initEvents.call(this);var _11f=YAHOO.util.CustomEvent;this.mouseOverEvent=new _11f("mouseOverEvent",this);this.mouseOutEvent=new _11f("mouseOutEvent",this);this.mouseDownEvent=new _11f("mouseDownEvent",this);this.mouseUpEvent=new _11f("mouseUpEvent",this);this.clickEvent=new _11f("clickEvent",this);this.keyPressEvent=new _11f("keyPressEvent",this);this.keyDownEvent=new _11f("keyDownEvent",this);this.keyUpEvent=new _11f("keyUpEvent",this);this.itemAddedEvent=new _11f("itemAddedEvent",this);this.itemRemovedEvent=new _11f("itemRemovedEvent",this);},getRoot:function(){var _120=this.parent;if(_120){var _121=_120.parent;return _121?_121.getRoot():this;}else{return this;}},toString:function(){return ("Menu "+this.id);},setItemGroupTitle:function(_122,_123){if(typeof _122=="string"&&_122.length>0){var _124=typeof _123=="number"?_123:0;var _125=this._aGroupTitleElements[_124];if(_125){_125.innerHTML=_122;}else{_125=document.createElement(this.GROUP_TITLE_TAG_NAME);_125.innerHTML=_122;this._aGroupTitleElements[_124]=_125;}var i=this._aGroupTitleElements.length-1;var _127;do{if(this._aGroupTitleElements[i]){Dom.removeClass(this._aGroupTitleElements[i],"first-of-type");_127=i;}}while(i--);if(_127!==null){Dom.addClass(this._aGroupTitleElements[_127],"first-of-type");}}},addItem:function(_128,_129){if(_128){return this._addItemToGroup(_129,_128);}},addItems:function(_12a,_12b){function isArray(_12c){return (typeof _12c=="object"&&_12c.constructor==Array);}if(isArray(_12a)){var _12d=_12a.length;var _12e=[];var _12f;for(var i=0;i<_12d;i++){_12f=_12a[i];if(isArray(_12f)){_12e[_12e.length]=this.addItems(_12f,i);}else{_12e[_12e.length]=this._addItemToGroup(_12b,_12f);}}if(_12e.length){return _12e;}}},insertItem:function(_131,_132,_133){if(_131){return this._addItemToGroup(_133,_131,_132);}},removeItem:function(_134,_135){if(typeof _134!="undefined"){var _136;if(_134 instanceof YAHOO.widget.MenuItem){_136=this._removeItemFromGroupByValue(_135,_134);}else{if(typeof _134=="number"){_136=this._removeItemFromGroupByIndex(_135,_134);}}if(_136){_136.destroy();return _136;}}},getItemGroups:function(){return this._aItemGroups;},getItem:function(_137,_138){if(typeof _137=="number"){var _139=this._getItemGroup(_138);if(_139){return _139[_137];}}},destroy:function(){this.mouseOverEvent.unsubscribeAll();this.mouseOutEvent.unsubscribeAll();this.mouseDownEvent.unsubscribeAll();this.mouseUpEvent.unsubscribeAll();this.clickEvent.unsubscribeAll();this.keyPressEvent.unsubscribeAll();this.keyDownEvent.unsubscribeAll();this.keyUpEvent.unsubscribeAll();var _13a=this._aItemGroups.length;var _13b;var _13c;var _13d;var i;var n;if(_13a>0){i=_13a-1;do{_13c=this._aItemGroups[i];if(_13c){_13b=_13c.length;if(_13b>0){n=_13b-1;do{_13d=this._aItemGroups[i][n];if(_13d){_13d.destroy();}}while(n--);}}}while(i--);}YAHOO.widget.Menu.superclass.destroy.call(this);},setInitialFocus:function(){var _140=this._getFirstEnabledItem();if(_140){_140.focus();}},setInitialSelection:function(){var _141=this._getFirstEnabledItem();if(_141){_141.cfg.setProperty("selected",true);}},clearActiveItem:function(_142){if(this.cfg.getProperty("showdelay")>0){this._cancelShowDelay();}var _143=this.activeItem;if(_143){var _144=_143.cfg;_144.setProperty("selected",false);var _145=_144.getProperty("submenu");if(_145){_145.hide();}if(_142){_143.blur();}}},initDefaultConfig:function(){YAHOO.widget.Menu.superclass.initDefaultConfig.call(this);var _146=this.cfg;_146.addProperty("visible",{value:false,handler:this.configVisible,validator:this.cfg.checkBoolean});_146.addProperty("constraintoviewport",{value:true,handler:this.configConstrainToViewport,validator:this.cfg.checkBoolean,supercedes:["iframe","x","y","xy"]});_146.addProperty("position",{value:"dynamic",handler:this.configPosition,validator:this._checkPosition,supercedes:["visible"]});_146.addProperty("submenualignment",{value:["tl","tr"]});_146.addProperty("autosubmenudisplay",{value:true,validator:_146.checkBoolean});_146.addProperty("showdelay",{value:0,validator:_146.checkNumber});_146.addProperty("hidedelay",{value:0,validator:_146.checkNumber,handler:this.configHideDelay,suppressEvent:true});_146.addProperty("clicktohide",{value:true,validator:_146.checkBoolean});this.cfg.addProperty("container",{value:document.body,handler:this.configContainer});}});})();YAHOO.widget.MenuModule=YAHOO.widget.Menu;(function(){var Dom=YAHOO.util.Dom;var _148=YAHOO.widget.Module;var Menu=YAHOO.widget.Menu;YAHOO.widget.MenuItem=function(_14a,_14b){if(_14a){if(_14b){this.parent=_14b.parent;this.value=_14b.value;}this.init(_14a,_14b);}};YAHOO.widget.MenuItem.prototype={SUBMENU_INDICATOR_IMAGE_PATH:"nt/ic/ut/alt1/menuarorght8_nrm_1.gif",SELECTED_SUBMENU_INDICATOR_IMAGE_PATH:"nt/ic/ut/alt1/menuarorght8_hov_1.gif",DISABLED_SUBMENU_INDICATOR_IMAGE_PATH:"nt/ic/ut/alt1/menuarorght8_dim_1.gif",COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT:"Collapsed. Click to expand.",EXPANDED_SUBMENU_INDICATOR_ALT_TEXT:"Expanded. Click to collapse.",DISABLED_SUBMENU_INDICATOR_ALT_TEXT:"Disabled.",CHECKED_IMAGE_PATH:"nt/ic/ut/bsc/menuchk8_nrm_1.gif",SELECTED_CHECKED_IMAGE_PATH:"nt/ic/ut/bsc/menuchk8_hov_1.gif",DISABLED_CHECKED_IMAGE_PATH:"nt/ic/ut/bsc/menuchk8_dim_1.gif",CHECKED_IMAGE_ALT_TEXT:"Checked.",DISABLED_CHECKED_IMAGE_ALT_TEXT:"Checked. (Item disabled.)",CSS_CLASS_NAME:"yuimenuitem",SUBMENU_TYPE:null,IMG_ROOT:"http://us.i1.yimg.com/us.yimg.com/i/",IMG_ROOT_SSL:"https://a248.e.akamai.net/sec.yimg.com/i/",_oAnchor:null,_oText:null,_oHelpTextEM:null,_oSubmenu:null,_checkImage:null,constructor:YAHOO.widget.MenuItem,imageRoot:null,isSecure:_148.prototype.isSecure,index:null,groupIndex:null,parent:null,element:null,srcElement:null,value:null,submenuIndicator:null,browser:_148.prototype.browser,destroyEvent:null,mouseOverEvent:null,mouseOutEvent:null,mouseDownEvent:null,mouseUpEvent:null,clickEvent:null,keyPressEvent:null,keyDownEvent:null,keyUpEvent:null,focusEvent:null,blurEvent:null,init:function(_14c,_14d){this.imageRoot=(this.isSecure)?this.IMG_ROOT_SSL:this.IMG_ROOT;if(!this.SUBMENU_TYPE){this.SUBMENU_TYPE=Menu;}this.cfg=new YAHOO.util.Config(this);this.initDefaultConfig();var _14e=this.cfg;if(this._checkString(_14c)){this._createRootNodeStructure();_14e.setProperty("text",_14c);}else{if(this._checkDOMNode(_14c)){switch(_14c.tagName.toUpperCase()){case "OPTION":this._createRootNodeStructure();_14e.setProperty("text",_14c.text);this.srcElement=_14c;break;case "OPTGROUP":this._createRootNodeStructure();_14e.setProperty("text",_14c.label);this.srcElement=_14c;this._initSubTree();break;case "LI":var _14f=this._getFirstElement(_14c,"A");var sURL="#";var _151=null;var _152=null;if(_14f){sURL=_14f.getAttribute("href");_151=_14f.getAttribute("target");if(_14f.innerText){_152=_14f.innerText;}else{var _153=_14f.ownerDocument.createRange();_153.selectNodeContents(_14f);_152=_153.toString();}}else{var _154=_14c.firstChild;_152=_154.nodeValue;_14f=document.createElement("a");_14f.setAttribute("href",sURL);_14c.replaceChild(_14f,_154);_14f.appendChild(_154);}this.srcElement=_14c;this.element=_14c;this._oAnchor=_14f;var _155=this._getFirstElement(_14f);var _156=false;var _157=false;if(_155){this._oText=_155.firstChild;switch(_155.tagName.toUpperCase()){case "EM":_156=true;break;case "STRONG":_157=true;break;}}else{this._oText=_14f.firstChild;}_14e.setProperty("text",_152,true);_14e.setProperty("url",sURL,true);_14e.setProperty("target",_151,true);_14e.setProperty("emphasis",_156,true);_14e.setProperty("strongemphasis",_157,true);this._initSubTree();break;}}}if(this.element){Dom.addClass(this.element,this.CSS_CLASS_NAME);var _158=YAHOO.util.CustomEvent;this.destroyEvent=new _158("destroyEvent",this);this.mouseOverEvent=new _158("mouseOverEvent",this);this.mouseOutEvent=new _158("mouseOutEvent",this);this.mouseDownEvent=new _158("mouseDownEvent",this);this.mouseUpEvent=new _158("mouseUpEvent",this);this.clickEvent=new _158("clickEvent",this);this.keyPressEvent=new _158("keyPressEvent",this);this.keyDownEvent=new _158("keyDownEvent",this);this.keyUpEvent=new _158("keyUpEvent",this);this.focusEvent=new _158("focusEvent",this);this.blurEvent=new _158("blurEvent",this);if(_14d){_14e.applyConfig(_14d);}_14e.fireQueue();}},_getFirstElement:function(_159,_15a){var _15b;if(_159.firstChild&&_159.firstChild.nodeType==1){_15b=_159.firstChild;}else{if(_159.firstChild&&_159.firstChild.nextSibling&&_159.firstChild.nextSibling.nodeType==1){_15b=_159.firstChild.nextSibling;}}if(_15a){return (_15b&&_15b.tagName.toUpperCase()==_15a)?_15b:false;}return _15b;},_checkString:function(_15c){return (typeof _15c=="string");},_checkDOMNode:function(_15d){return (_15d&&_15d.tagName);},_createRootNodeStructure:function(){this.element=document.createElement("li");this._oText=document.createTextNode("");this._oAnchor=document.createElement("a");this._oAnchor.appendChild(this._oText);this.cfg.refireEvent("url");this.element.appendChild(this._oAnchor);},_initSubTree:function(){var _15e=this.srcElement;var _15f=this.cfg;if(_15e.childNodes.length>0){if(this.parent.lazyLoad&&this.parent.srcElement&&this.parent.srcElement.tagName.toUpperCase()=="SELECT"){_15f.setProperty("submenu",{id:Dom.generateId(),itemdata:_15e.childNodes});}else{var _160=_15e.firstChild;var _161=[];do{if(_160&&_160.tagName){switch(_160.tagName.toUpperCase()){case "DIV":_15f.setProperty("submenu",_160);break;case "OPTION":_161[_161.length]=_160;break;}}}while((_160=_160.nextSibling));var _162=_161.length;if(_162>0){var _163=new this.SUBMENU_TYPE(Dom.generateId());_15f.setProperty("submenu",_163);for(var n=0;n<_162;n++){_163.addItem((new _163.ITEM_TYPE(_161[n])));}}}}},_preloadImage:function(_165){var _166=this.imageRoot+_165;if(!document.images[_166]){var _167=document.createElement("img");_167.src=_166;_167.name=_166;_167.id=_166;_167.style.display="none";document.body.appendChild(_167);}},configText:function(_168,_169,_16a){var _16b=_169[0];if(this._oText){this._oText.nodeValue=_16b;}},configHelpText:function(_16c,_16d,_16e){var me=this;var _170=_16d[0];var oEl=this.element;var _172=this.cfg;var _173=[oEl,this._oAnchor];var oImg=this.submenuIndicator;var _175=function(){Dom.addClass(_173,"hashelptext");if(_172.getProperty("disabled")){_172.refireEvent("disabled");}if(_172.getProperty("selected")){_172.refireEvent("selected");}};var _176=function(){Dom.removeClass(_173,"hashelptext");oEl.removeChild(me._oHelpTextEM);me._oHelpTextEM=null;};if(this._checkDOMNode(_170)){if(this._oHelpTextEM){this._oHelpTextEM.parentNode.replaceChild(_170,this._oHelpTextEM);}else{this._oHelpTextEM=_170;oEl.insertBefore(this._oHelpTextEM,oImg);}_175();}else{if(this._checkString(_170)){if(_170.length===0){_176();}else{if(!this._oHelpTextEM){this._oHelpTextEM=document.createElement("em");oEl.insertBefore(this._oHelpTextEM,oImg);}this._oHelpTextEM.innerHTML=_170;_175();}}else{if(!_170&&this._oHelpTextEM){_176();}}}},configURL:function(_177,_178,_179){var sURL=_178[0];if(!sURL){sURL="#";}this._oAnchor.setAttribute("href",sURL);},configTarget:function(_17b,_17c,_17d){var _17e=_17c[0];var _17f=this._oAnchor;if(_17e&&_17e.length>0){_17f.setAttribute("target",_17e);}else{_17f.removeAttribute("target");}},configEmphasis:function(_180,_181,_182){var _183=_181[0];var _184=this._oAnchor;var _185=this._oText;var _186=this.cfg;var oEM;if(_183&&_186.getProperty("strongemphasis")){_186.setProperty("strongemphasis",false);}if(_184){if(_183){oEM=document.createElement("em");oEM.appendChild(_185);_184.appendChild(oEM);}else{oEM=this._getFirstElement(_184,"EM");_184.removeChild(oEM);_184.appendChild(_185);}}},configStrongEmphasis:function(_188,_189,_18a){var _18b=_189[0];var _18c=this._oAnchor;var _18d=this._oText;var _18e=this.cfg;var _18f;if(_18b&&_18e.getProperty("emphasis")){_18e.setProperty("emphasis",false);}if(_18c){if(_18b){_18f=document.createElement("strong");_18f.appendChild(_18d);_18c.appendChild(_18f);}else{_18f=this._getFirstElement(_18c,"STRONG");_18c.removeChild(_18f);_18c.appendChild(_18d);}}},configChecked:function(_190,_191,_192){var _193=_191[0];var oEl=this.element;var _195=this.cfg;var oImg;if(_193){this._preloadImage(this.CHECKED_IMAGE_PATH);this._preloadImage(this.SELECTED_CHECKED_IMAGE_PATH);this._preloadImage(this.DISABLED_CHECKED_IMAGE_PATH);oImg=document.createElement("img");oImg.src=(this.imageRoot+this.CHECKED_IMAGE_PATH);oImg.alt=this.CHECKED_IMAGE_ALT_TEXT;var _197=this.cfg.getProperty("submenu");if(_197){oEl.insertBefore(oImg,_197.element);}else{oEl.appendChild(oImg);}Dom.addClass([oEl,oImg],"checked");this._checkImage=oImg;if(_195.getProperty("disabled")){_195.refireEvent("disabled");}if(_195.getProperty("selected")){_195.refireEvent("selected");}}else{oImg=this._checkImage;Dom.removeClass([oEl,oImg],"checked");if(oImg){oEl.removeChild(oImg);}this._checkImage=null;}},configDisabled:function(_198,_199,_19a){var _19b=_199[0];var _19c=this._oAnchor;var _19d=[this.element,_19c];var oEM=this._oHelpTextEM;var _19f=this.cfg;var oImg;var _1a1;var _1a2;if(oEM){_19d[2]=oEM;}if(this.cfg.getProperty("checked")){_1a2=this.CHECKED_IMAGE_ALT_TEXT;_1a1=this.CHECKED_IMAGE_PATH;oImg=this._checkImage;if(_19b){_1a2=this.DISABLED_CHECKED_IMAGE_ALT_TEXT;_1a1=this.DISABLED_CHECKED_IMAGE_PATH;}oImg.src=document.images[(this.imageRoot+_1a1)].src;oImg.alt=_1a2;}oImg=this.submenuIndicator;if(_19b){if(_19f.getProperty("selected")){_19f.setProperty("selected",false);}_19c.removeAttribute("href");Dom.addClass(_19d,"disabled");_1a1=this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH;_1a2=this.DISABLED_SUBMENU_INDICATOR_ALT_TEXT;}else{_19c.setAttribute("href",_19f.getProperty("url"));Dom.removeClass(_19d,"disabled");_1a1=this.SUBMENU_INDICATOR_IMAGE_PATH;_1a2=this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;}if(oImg){oImg.src=this.imageRoot+_1a1;oImg.alt=_1a2;}},configSelected:function(_1a3,_1a4,_1a5){if(!this.cfg.getProperty("disabled")){var _1a6=_1a4[0];var oEM=this._oHelpTextEM;var _1a8=[this.element,this._oAnchor];var oImg=this.submenuIndicator;var _1aa;if(oEM){_1a8[_1a8.length]=oEM;}if(oImg){_1a8[_1a8.length]=oImg;}if(this.cfg.getProperty("checked")){_1aa=this.imageRoot+(_1a6?this.SELECTED_CHECKED_IMAGE_PATH:this.CHECKED_IMAGE_PATH);this._checkImage.src=document.images[_1aa].src;}if(_1a6){Dom.addClass(_1a8,"selected");_1aa=this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH;}else{Dom.removeClass(_1a8,"selected");_1aa=this.SUBMENU_INDICATOR_IMAGE_PATH;}if(oImg){oImg.src=document.images[(this.imageRoot+_1aa)].src;}}},configSubmenu:function(_1ab,_1ac,_1ad){var oEl=this.element;var _1af=_1ac[0];var oImg=this.submenuIndicator;var _1b1=this.cfg;var _1b2=[this.element,this._oAnchor];var _1b3;var _1b4=this.parent&&this.parent.lazyLoad;if(_1af){if(_1af instanceof Menu){_1b3=_1af;_1b3.parent=this;_1b3.lazyLoad=_1b4;}else{if(typeof _1af=="object"&&_1af.id&&!_1af.nodeType){var _1b5=_1af.id;var _1b6=_1af;delete _1af["id"];_1b6.lazyload=_1b4;_1b6.parent=this;_1b3=new this.SUBMENU_TYPE(_1b5,_1b6);this.cfg.setProperty("submenu",_1b3,true);}else{_1b3=new this.SUBMENU_TYPE(_1af,{lazyload:_1b4,parent:this});this.cfg.setProperty("submenu",_1b3,true);}}if(_1b3){this._oSubmenu=_1b3;if(!oImg){this._preloadImage(this.SUBMENU_INDICATOR_IMAGE_PATH);this._preloadImage(this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH);this._preloadImage(this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH);oImg=document.createElement("img");oImg.src=(this.imageRoot+this.SUBMENU_INDICATOR_IMAGE_PATH);oImg.alt=this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT;oEl.appendChild(oImg);this.submenuIndicator=oImg;Dom.addClass(_1b2,"hassubmenu");if(_1b1.getProperty("disabled")){_1b1.refireEvent("disabled");}if(_1b1.getProperty("selected")){_1b1.refireEvent("selected");}}}}else{Dom.removeClass(_1b2,"hassubmenu");if(oImg){oEl.removeChild(oImg);}if(this._oSubmenu){this._oSubmenu.destroy();}}},initDefaultConfig:function(){var _1b7=this.cfg;var _1b8=_1b7.checkBoolean;_1b7.addProperty("text",{value:"",handler:this.configText,validator:this._checkString,suppressEvent:true});_1b7.addProperty("helptext",{handler:this.configHelpText});_1b7.addProperty("url",{value:"#",handler:this.configURL,suppressEvent:true});_1b7.addProperty("target",{handler:this.configTarget,suppressEvent:true});_1b7.addProperty("emphasis",{value:false,handler:this.configEmphasis,validator:_1b8,suppressEvent:true});_1b7.addProperty("strongemphasis",{value:false,handler:this.configStrongEmphasis,validator:_1b8,suppressEvent:true});_1b7.addProperty("checked",{value:false,handler:this.configChecked,validator:this.cfg.checkBoolean,suppressEvent:true,supercedes:["disabled"]});_1b7.addProperty("disabled",{value:false,handler:this.configDisabled,validator:_1b8,suppressEvent:true});_1b7.addProperty("selected",{value:false,handler:this.configSelected,validator:_1b8,suppressEvent:true});_1b7.addProperty("submenu",{handler:this.configSubmenu});},getNextEnabledSibling:function(){if(this.parent instanceof Menu){var _1b9=this.groupIndex;var _1ba=function(_1bb,_1bc){return _1bb[_1bc]||_1ba(_1bb,(_1bc+1));};var _1bd=this.parent.getItemGroups();var _1be;if(this.index<(_1bd[_1b9].length-1)){_1be=_1ba(_1bd[_1b9],(this.index+1));}else{var _1bf;if(_1b9<(_1bd.length-1)){_1bf=_1b9+1;}else{_1bf=0;}var _1c0=_1ba(_1bd,_1bf);_1be=_1ba(_1c0,0);}return (_1be.cfg.getProperty("disabled")||_1be.element.style.display=="none")?_1be.getNextEnabledSibling():_1be;}},getPreviousEnabledSibling:function(){if(this.parent instanceof Menu){var _1c1=this.groupIndex;var _1c2=function(_1c3,_1c4){return _1c3[_1c4]||_1c2(_1c3,(_1c4-1));};var _1c5=function(_1c6,_1c7){return _1c6[_1c7]?_1c7:_1c5(_1c6,(_1c7+1));};var _1c8=this.parent.getItemGroups();var _1c9;if(this.index>_1c5(_1c8[_1c1],0)){_1c9=_1c2(_1c8[_1c1],(this.index-1));}else{var _1ca;if(_1c1>_1c5(_1c8,0)){_1ca=_1c1-1;}else{_1ca=_1c8.length-1;}var _1cb=_1c2(_1c8,_1ca);_1c9=_1c2(_1cb,(_1cb.length-1));}return (_1c9.cfg.getProperty("disabled")||_1c9.element.style.display=="none")?_1c9.getPreviousEnabledSibling():_1c9;}},focus:function(){var _1cc=this.parent;var _1cd=this._oAnchor;var _1ce=_1cc.activeItem;if(!this.cfg.getProperty("disabled")&&_1cc&&_1cc.cfg.getProperty("visible")&&this.element.style.display!="none"){if(_1ce){_1ce.blur();}try{_1cd.focus();}catch(e){}this.focusEvent.fire();}},blur:function(){var _1cf=this.parent;if(!this.cfg.getProperty("disabled")&&_1cf&&Dom.getStyle(_1cf.element,"visibility")=="visible"){this._oAnchor.blur();this.blurEvent.fire();}},destroy:function(){var oEl=this.element;if(oEl){this.mouseOverEvent.unsubscribeAll();this.mouseOutEvent.unsubscribeAll();this.mouseDownEvent.unsubscribeAll();this.mouseUpEvent.unsubscribeAll();this.clickEvent.unsubscribeAll();this.keyPressEvent.unsubscribeAll();this.keyDownEvent.unsubscribeAll();this.keyUpEvent.unsubscribeAll();this.focusEvent.unsubscribeAll();this.blurEvent.unsubscribeAll();this.cfg.configChangedEvent.unsubscribeAll();var _1d1=oEl.parentNode;if(_1d1){_1d1.removeChild(oEl);this.destroyEvent.fire();}this.destroyEvent.unsubscribeAll();}},toString:function(){return ("MenuItem: "+this.cfg.getProperty("text"));}};})();YAHOO.widget.MenuModuleItem=YAHOO.widget.MenuItem;YAHOO.widget.ContextMenu=function(_1d2,_1d3){YAHOO.widget.ContextMenu.superclass.constructor.call(this,_1d2,_1d3);};YAHOO.extend(YAHOO.widget.ContextMenu,YAHOO.widget.Menu,{_oTrigger:null,contextEventTarget:null,init:function(_1d4,_1d5){if(!this.ITEM_TYPE){this.ITEM_TYPE=YAHOO.widget.ContextMenuItem;}YAHOO.widget.ContextMenu.superclass.init.call(this,_1d4);this.beforeInitEvent.fire(YAHOO.widget.ContextMenu);if(_1d5){this.cfg.applyConfig(_1d5,true);}this.initEvent.fire(YAHOO.widget.ContextMenu);},_removeEventHandlers:function(){var _1d6=YAHOO.util.Event;var _1d7=this._oTrigger;var _1d8=(this.browser=="opera");_1d6.removeListener(_1d7,(_1d8?"mousedown":"contextmenu"),this._onTriggerContextMenu);if(_1d8){_1d6.removeListener(_1d7,"click",this._onTriggerClick);}},_onTriggerClick:function(_1d9,_1da){if(_1d9.ctrlKey){YAHOO.util.Event.stopEvent(_1d9);}},_onTriggerContextMenu:function(_1db,_1dc){YAHOO.widget.MenuManager.hideVisible();var _1dd=YAHOO.util.Event;var _1de=this.cfg;if(_1db.type=="mousedown"&&!_1db.ctrlKey){return;}this.contextEventTarget=_1dd.getTarget(_1db);var nX=_1dd.getPageX(_1db);var nY=_1dd.getPageY(_1db);_1de.applyConfig({xy:[nX,nY],visible:true});_1de.fireQueue();_1dd.stopEvent(_1db);},toString:function(){return ("ContextMenu "+this.id);},initDefaultConfig:function(){YAHOO.widget.ContextMenu.superclass.initDefaultConfig.call(this);this.cfg.addProperty("trigger",{handler:this.configTrigger});},destroy:function(){this._removeEventHandlers();YAHOO.widget.ContextMenu.superclass.destroy.call(this);},configTrigger:function(_1e1,_1e2,_1e3){var _1e4=YAHOO.util.Event;var _1e5=_1e2[0];if(_1e5){if(this._oTrigger){this._removeEventHandlers();}this._oTrigger=_1e5;var _1e6=(this.browser=="opera");_1e4.addListener(_1e5,(_1e6?"mousedown":"contextmenu"),this._onTriggerContextMenu,this,true);if(_1e6){_1e4.addListener(_1e5,"click",this._onTriggerClick,this,true);}}else{this._removeEventHandlers();}}});YAHOO.widget.ContextMenuItem=function(_1e7,_1e8){YAHOO.widget.ContextMenuItem.superclass.constructor.call(this,_1e7,_1e8);};YAHOO.extend(YAHOO.widget.ContextMenuItem,YAHOO.widget.MenuItem,{init:function(_1e9,_1ea){if(!this.SUBMENU_TYPE){this.SUBMENU_TYPE=YAHOO.widget.ContextMenu;}YAHOO.widget.ContextMenuItem.superclass.init.call(this,_1e9);var _1eb=this.cfg;if(_1ea){_1eb.applyConfig(_1ea,true);}_1eb.fireQueue();},toString:function(){return ("MenuBarItem: "+this.cfg.getProperty("text"));}});YAHOO.widget.MenuBar=function(_1ec,_1ed){YAHOO.widget.MenuBar.superclass.constructor.call(this,_1ec,_1ed);};YAHOO.extend(YAHOO.widget.MenuBar,YAHOO.widget.Menu,{init:function(_1ee,_1ef){if(!this.ITEM_TYPE){this.ITEM_TYPE=YAHOO.widget.MenuBarItem;}YAHOO.widget.MenuBar.superclass.init.call(this,_1ee);this.beforeInitEvent.fire(YAHOO.widget.MenuBar);if(_1ef){this.cfg.applyConfig(_1ef,true);}this.initEvent.fire(YAHOO.widget.MenuBar);},CSS_CLASS_NAME:"yuimenubar",_onKeyDown:function(_1f0,_1f1,_1f2){var _1f3=YAHOO.util.Event;var _1f4=_1f1[0];var _1f5=_1f1[1];var _1f6=_1f5.cfg;var _1f7;switch(_1f4.keyCode){case 27:if(this.cfg.getProperty("position")=="dynamic"){this.hide();if(this.parent){this.parent.focus();}}else{if(this.activeItem){_1f7=this.activeItem.cfg.getProperty("submenu");if(_1f7&&_1f7.cfg.getProperty("visible")){_1f7.hide();this.activeItem.focus();}else{this.activeItem.cfg.setProperty("selected",false);this.activeItem.blur();}}}_1f3.preventDefault(_1f4);break;case 37:case 39:if(_1f5==this.activeItem&&!_1f6.getProperty("selected")){_1f6.setProperty("selected",true);}else{var _1f8=(_1f4.keyCode==37)?_1f5.getPreviousEnabledSibling():_1f5.getNextEnabledSibling();if(_1f8){this.clearActiveItem();_1f8.cfg.setProperty("selected",true);if(this.cfg.getProperty("autosubmenudisplay")){_1f7=_1f8.cfg.getProperty("submenu");if(_1f7){_1f7.show();_1f7.activeItem.blur();_1f7.activeItem=null;}}_1f8.focus();}}_1f3.preventDefault(_1f4);break;case 40:if(this.activeItem!=_1f5){this.clearActiveItem();_1f6.setProperty("selected",true);_1f5.focus();}_1f7=_1f6.getProperty("submenu");if(_1f7){if(_1f7.cfg.getProperty("visible")){_1f7.setInitialSelection();_1f7.setInitialFocus();}else{_1f7.show();}}_1f3.preventDefault(_1f4);break;}},_onClick:function(_1f9,_1fa,_1fb){YAHOO.widget.MenuBar.superclass._onClick.call(this,_1f9,_1fa,_1fb);var _1fc=_1fa[1];if(_1fc){var _1fd=YAHOO.util.Event;var Dom=YAHOO.util.Dom;var _1ff=_1fa[0];var _200=_1fd.getTarget(_1ff);var _201=this.activeItem;var _202=this.cfg;if(_201&&_201!=_1fc){this.clearActiveItem();}_1fc.cfg.setProperty("selected",true);_1fc.focus();var _203=_1fc.cfg.getProperty("submenu");if(_203&&_200!=_1fc.submenuIndicator){if(_203.cfg.getProperty("visible")){_203.hide();}else{_203.show();}}}},toString:function(){return ("MenuBar "+this.id);},initDefaultConfig:function(){YAHOO.widget.MenuBar.superclass.initDefaultConfig.call(this);var _204=this.cfg;_204.addProperty("position",{value:"static",handler:this.configPosition,validator:this._checkPosition,supercedes:["visible"]});_204.addProperty("submenualignment",{value:["tl","bl"]});_204.addProperty("autosubmenudisplay",{value:false,validator:_204.checkBoolean});}});YAHOO.widget.MenuBarItem=function(_205,_206){YAHOO.widget.MenuBarItem.superclass.constructor.call(this,_205,_206);};YAHOO.extend(YAHOO.widget.MenuBarItem,YAHOO.widget.MenuItem,{init:function(_207,_208){if(!this.SUBMENU_TYPE){this.SUBMENU_TYPE=YAHOO.widget.Menu;}YAHOO.widget.MenuBarItem.superclass.init.call(this,_207);var _209=this.cfg;if(_208){_209.applyConfig(_208,true);}_209.fireQueue();},CSS_CLASS_NAME:"yuimenubaritem",SUBMENU_INDICATOR_IMAGE_PATH:"nt/ic/ut/alt1/menuarodwn8_nrm_1.gif",SELECTED_SUBMENU_INDICATOR_IMAGE_PATH:"nt/ic/ut/alt1/menuarodwn8_hov_1.gif",DISABLED_SUBMENU_INDICATOR_IMAGE_PATH:"nt/ic/ut/alt1/menuarodwn8_dim_1.gif",toString:function(){return ("MenuBarItem: "+this.cfg.getProperty("text"));}}); \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/menu/menu.js b/source/web/scripts/ajax/yahoo/menu/menu.js new file mode 100644 index 0000000000..48956e7c9b --- /dev/null +++ b/source/web/scripts/ajax/yahoo/menu/menu.js @@ -0,0 +1,6779 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.txt +version: 0.12.0 +*/ + +/** +* @module menu +* @descriptionThe Menu Library features a collection of widgets that make +* it easy to add menus to your website or web application. With the Menu +* Library you can create website fly-out menus, customized context menus, or +* application-style menu bars with just a small amount of scripting.
+*+*
+* @title Menu Library +* @namespace YAHOO.widget +* @requires Event, Dom, Container +*/ +(function() { + +var Dom = YAHOO.util.Dom; +var Event = YAHOO.util.Event; + +/** +* Singleton that manages a collection of all menus and menu items. Listens for +* DOM events at the document level and dispatches the events to the +* corresponding menu or menu item. +* +* @namespace YAHOO.widget +* @class MenuManager +* @static +*/ +YAHOO.widget.MenuManager = new function() { + + // Private member variables + + // Flag indicating if the DOM event handlers have been attached + + var m_bInitializedEventHandlers = false; + + // Collection of menus + + var m_oMenus = {}; + + + // Collection of menu items + + var m_oItems = {}; + + // Collection of visible menus + + var m_oVisibleMenus = {}; + + // Logger + + + // Private methods + + /** + * Adds an item to the collection of known menu items. + * @private + * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem + * instance to be added. + */ + var addItem = function(p_oItem) { + + var sYUIId = Dom.generateId(); + + if(p_oItem && m_oItems[sYUIId] != p_oItem) { + + p_oItem.element.setAttribute("yuiid", sYUIId); + + m_oItems[sYUIId] = p_oItem; + + p_oItem.destroyEvent.subscribe(onItemDestroy, p_oItem); + + + } + + }; + + /** + * Removes an item from the collection of known menu items. + * @private + * @param {YAHOO.widget.MenuItem} p_oItem Object specifying the MenuItem + * instance to be removed. + */ + var removeItem = function(p_oItem) { + + var sYUIId = p_oItem.element.getAttribute("yuiid"); + + if(sYUIId && m_oItems[sYUIId]) { + + delete m_oItems[sYUIId]; + + + } + + }; + + /** + * Finds the root DIV node of a menu or the root LI node of a menu item. + * @private + * @param {HTMLElement} p_oElement Object specifying + * an HTML element. + */ + var getMenuRootElement = function(p_oElement) { + + var oParentNode; + + if(p_oElement && p_oElement.tagName) { + + switch(p_oElement.tagName.toUpperCase()) { + + case "DIV": + + oParentNode = p_oElement.parentNode; + + // Check if the DIV is the inner "body" node of a menu + + if( + Dom.hasClass(p_oElement, "bd") && + oParentNode && + oParentNode.tagName && + oParentNode.tagName.toUpperCase() == "DIV" + ) { + + return oParentNode; + + } + else { + + return p_oElement; + + } + + break; + + case "LI": + + return p_oElement; + + default: + + oParentNode = p_oElement.parentNode; + + if(oParentNode) { + + return getMenuRootElement(oParentNode); + + } + + break; + + } + + } + + }; + + // Private event handlers + + /** + * Generic, global event handler for all of a menu's DOM-based + * events. This listens for events against the document object. If the + * target of a given event is a member of a menu or menu item's DOM, the + * instance's corresponding Custom Event is fired. + * @private + * @param {Event} p_oEvent Object representing the DOM event object passed + * back by the event utility (YAHOO.util.Event). + */ + var onDOMEvent = function(p_oEvent) { + + // Get the target node of the DOM event + + var oTarget = Event.getTarget(p_oEvent); + + // See if the target of the event was a menu, or a menu item + + var oElement = getMenuRootElement(oTarget); + + var oMenuItem; + var oMenu; + + if(oElement) { + + var sTagName = oElement.tagName.toUpperCase(); + + if(sTagName == "LI") { + + var sYUIId = oElement.getAttribute("yuiid"); + + if(sYUIId) { + + oMenuItem = m_oItems[sYUIId]; + oMenu = oMenuItem.parent; + + } + + } + else if(sTagName == "DIV") { + + if(oElement.id) { + + oMenu = m_oMenus[oElement.id]; + + } + + } + + } + + if(oMenu) { + + // Map of DOM event names to CustomEvent names + + var oEventTypes = { + "click": "clickEvent", + "mousedown": "mouseDownEvent", + "mouseup": "mouseUpEvent", + "mouseover": "mouseOverEvent", + "mouseout": "mouseOutEvent", + "keydown": "keyDownEvent", + "keyup": "keyUpEvent", + "keypress": "keyPressEvent" + }; + + var sCustomEventType = oEventTypes[p_oEvent.type]; + + // Fire the Custom Even that corresponds the current DOM event + + if(oMenuItem && !oMenuItem.cfg.getProperty("disabled")) { + + oMenuItem[sCustomEventType].fire(p_oEvent); + + } + + oMenu[sCustomEventType].fire(p_oEvent, oMenuItem); + + } + else if(p_oEvent.type == "mousedown") { + + /* + If the target of the event wasn't a menu, hide all + dynamically positioned menus + */ + + var oActiveItem; + + for(var i in m_oMenus) { + + if(m_oMenus.hasOwnProperty(i)) { + + oMenu = m_oMenus[i]; + + if( + oMenu.cfg.getProperty("clicktohide") && + oMenu.cfg.getProperty("position") == "dynamic" + ) { + + oMenu.hide(); + + } + else { + + oMenu.clearActiveItem(true); + + } + + } + + } + + } + + }; + + /** + * "destroy" event handler for a menu. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that + * fired the event. + */ + var onMenuDestroy = function(p_sType, p_aArgs, p_oMenu) { + + this.removeMenu(p_oMenu); + + }; + + /** + * "destroy" event handler for a MenuItem instance. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + var onItemDestroy = function(p_sType, p_aArgs, p_oItem) { + + var sYUIId = p_oItem.element.getAttribute("yuiid"); + + if(sYUIId) { + + delete m_oItems[sYUIId]; + + } + + }; + + /** + * Event handler for when the "visible" configuration property + * of a Menu instance changes. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that + * fired the event. + */ + var onMenuVisibleConfigChange = function(p_sType, p_aArgs, p_oMenu) { + + var bVisible = p_aArgs[0]; + + if(bVisible) { + + m_oVisibleMenus[p_oMenu.id] = p_oMenu; + + + } + else if(m_oVisibleMenus[p_oMenu.id]) { + + delete m_oVisibleMenus[p_oMenu.id]; + + + } + + }; + + /** + * "itemadded" event handler for a Menu instance. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + */ + var onItemAdded = function(p_sType, p_aArgs) { + + addItem(p_aArgs[0]); + + }; + + + /** + * "itemremoved" event handler for a Menu instance. + * @private + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + */ + var onItemRemoved = function(p_sType, p_aArgs) { + + removeItem(p_aArgs[0]); + + }; + + // Privileged methods + + /** + * @method addMenu + * @description Adds a menu to the collection of known menus. + * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu instance + * to be added. + */ + this.addMenu = function(p_oMenu) { + + if(p_oMenu && p_oMenu.id && !m_oMenus[p_oMenu.id]) { + + m_oMenus[p_oMenu.id] = p_oMenu; + + + if(!m_bInitializedEventHandlers) { + + var oDoc = document; + + Event.addListener(oDoc, "mouseover", onDOMEvent, this, true); + Event.addListener(oDoc, "mouseout", onDOMEvent, this, true); + Event.addListener(oDoc, "mousedown", onDOMEvent, this, true); + Event.addListener(oDoc, "mouseup", onDOMEvent, this, true); + Event.addListener(oDoc, "click", onDOMEvent, this, true); + Event.addListener(oDoc, "keydown", onDOMEvent, this, true); + Event.addListener(oDoc, "keyup", onDOMEvent, this, true); + Event.addListener(oDoc, "keypress", onDOMEvent, this, true); + + m_bInitializedEventHandlers = true; + + + } + + p_oMenu.destroyEvent.subscribe(onMenuDestroy, p_oMenu, this); + + p_oMenu.cfg.subscribeToConfigEvent( + "visible", + onMenuVisibleConfigChange, + p_oMenu + ); + + p_oMenu.itemAddedEvent.subscribe(onItemAdded); + p_oMenu.itemRemovedEvent.subscribe(onItemRemoved); + + + } + + }; + + /** + * @method removeMenu + * @description Removes a menu from the collection of known menus. + * @param {YAHOO.widget.Menu} p_oMenu Object specifying the Menu instance + * to be removed. + */ + this.removeMenu = function(p_oMenu) { + + if(p_oMenu && m_oMenus[p_oMenu.id]) { + + delete m_oMenus[p_oMenu.id]; + + + } + + }; + + /** + * @method hideVisible + * @description Hides all visible, dynamically positioned menus. + */ + this.hideVisible = function() { + + var oMenu; + + for(var i in m_oVisibleMenus) { + + if(m_oVisibleMenus.hasOwnProperty(i)) { + + oMenu = m_oVisibleMenus[i]; + + if(oMenu.cfg.getProperty("position") == "dynamic") { + + oMenu.hide(); + + } + + } + + } + + }; + + /** + * @method getMenus + * @description Returns an array of all menus registered with the + * menu manger. + * @return {Array} + */ + this.getMenus = function() { + + return m_oMenus; + + }; + + /** + * @method getMenu + * @description Returns a menu with the specified id. + * @param {String} p_sId String specifying the id of the menu to + * be retrieved. + * @return {YAHOO.widget.Menu} + */ + this.getMenu = function(p_sId) { + + if(m_oMenus[p_sId]) { + + return m_oMenus[p_sId]; + + } + + }; + + + /** + * @method toString + * @description Returns a string representing the menu manager. + * @return {String} + */ + this.toString = function() { + + return ("MenuManager"); + + }; + +}; + +})(); + +(function() { + +var Dom = YAHOO.util.Dom; +var Event = YAHOO.util.Event; + +/** +* The Menu class creates a container that holds a vertical list representing +* a set of options or commands. Menu is the base class for all +* menu containers. +* @param {String} p_oElement String specifying the id attribute of the +*- Screen-reader accessibility.
+*- Keyboard and mouse navigation.
+*- A rich event model that provides access to all of a menu's +* interesting moments.
+*- Support for +* Progressive +* Enhancement; Menus can be created from simple, +* semantic markup on the page or purely through JavaScript.
+*<div>
element of the menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source +* for the menu. +* @param {HTMLDivElement} p_oElement Object +* specifying the<div>
element of the menu. +* @param {HTMLSelectElement} p_oElement +* Object specifying the<select>
element to be used as +* the data source for the menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu. See configuration class documentation for +* more details. +* @namespace YAHOO.widget +* @class Menu +* @constructor +* @extends YAHOO.widget.Overlay +*/ +YAHOO.widget.Menu = function(p_oElement, p_oConfig) { + + if(p_oConfig) { + + this.parent = p_oConfig.parent; + + this.lazyLoad = p_oConfig.lazyLoad || p_oConfig.lazyload; + + this.itemData = p_oConfig.itemData || p_oConfig.itemdata; + + } + + YAHOO.widget.Menu.superclass.constructor.call( + this, + p_oElement, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.Menu, YAHOO.widget.Overlay, { + +// Constants + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the +* menu's<div>
element. +* @default "yuimenu" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenu", + +/** +* @property ITEM_TYPE +* @description Object representing the type of menu item to instantiate and +* add when parsing the child nodes (either<li>
element, +*<optgroup>
element or<option>
) +* of the menu's source HTML element. +* @default YAHOO.widget.MenuItem +* @final +* @type YAHOO.widget.MenuItem +*/ +ITEM_TYPE: null, + +/** +* @property GROUP_TITLE_TAG_NAME +* @description String representing the tagname of the HTML element used to +* title the menu's item groups. +* @default H6 +* @final +* @type String +*/ +GROUP_TITLE_TAG_NAME: "h6", + +// Private properties + +/** +* @property _nHideDelayId +* @description Number representing the time-out setting used to cancel the +* hiding of a menu. +* @default null +* @private +* @type Number +*/ +_nHideDelayId: null, + +/** +* @property _nShowDelayId +* @description Number representing the time-out setting used to cancel the +* showing of a menu. +* @default null +* @private +* @type Number +*/ +_nShowDelayId: null, + +/** +* @property _hideDelayEventHandlersAssigned +* @description Boolean indicating if the "mouseover" and "mouseout" event +* handlers used for hiding the menu via a call to "window.setTimeout" have +* already been assigned. +* @default false +* @private +* @type Boolean +*/ +_hideDelayEventHandlersAssigned: false, + +/** +* @property _bHandledMouseOverEvent +* @description Boolean indicating the current state of the menu's +* "mouseover" event. +* @default false +* @private +* @type Boolean +*/ +_bHandledMouseOverEvent: false, + +/** +* @property _bHandledMouseOutEvent +* @description Boolean indicating the current state of the menu's +* "mouseout" event. +* @default false +* @private +* @type Boolean +*/ +_bHandledMouseOutEvent: false, + +/** +* @property _aGroupTitleElements +* @description Array of HTML element used to title groups of menu items. +* @default [] +* @private +* @type Array +*/ +_aGroupTitleElements: null, + +/** +* @property _aItemGroups +* @description Array of menu items. +* @default [] +* @private +* @type Array +*/ +_aItemGroups: null, + +/** +* @property _aListElements +* @description Array of<ul>
elements, each of which is +* the parent node for each item's<li>
element. +* @default [] +* @private +* @type Array +*/ +_aListElements: null, + +// Public properties + +/** +* @property lazyLoad +* @description Boolean indicating if the menu's "lazy load" feature is +* enabled. If set to "true," initialization and rendering of the menu's +* items will be deferred until the first time it is made visible. This +* property should be set via the constructor using the configuration +* object literal. +* @default false +* @type Boolean +*/ +lazyLoad: false, + +/** +* @property itemData +* @description Array of items to be added to the menu. The array can contain +* strings representing the text for each item to be created, object literals +* representing the menu item configuration properties, or MenuItem instances. +* This property should be set via the constructor using the configuration +* object literal. +* @default null +* @type Array +*/ +itemData: null, + +/** +* @property activeItem +* @description Object reference to the item in the menu that has focus. +* @default null +* @type YAHOO.widget.MenuItem +*/ +activeItem: null, + +/** +* @property parent +* @description Object reference to the menu's parent menu or menu item. +* This property can be set via the constructor using the configuration +* object literal. +* @default null +* @type YAHOO.widget.MenuItem +*/ +parent: null, + +/** +* @property srcElement +* @description Object reference to the HTML element (either +*<select>
or<div>
) used to +* create the menu. +* @default null +* @type HTMLSelectElement|HTMLDivElement +*/ +srcElement: null, + +// Events + +/** +* @event mouseOverEvent +* @description Fires when the mouse has entered the menu. Passes back +* the DOM Event object as an argument. +*/ +mouseOverEvent: null, + +/** +* @event mouseOutEvent +* @description Fires when the mouse has left the menu. Passes back the DOM +* Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +mouseOutEvent: null, + +/** +* @event mouseDownEvent +* @description Fires when the user mouses down on the menu. Passes back the +* DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +mouseDownEvent: null, + +/** +* @event mouseUpEvent +* @description Fires when the user releases a mouse button while the mouse is +* over the menu. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +mouseUpEvent: null, + +/** +* @event clickEvent +* @description Fires when the user clicks the on the menu. Passes back the +* DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +clickEvent: null, + +/** +* @event keyPressEvent +* @description Fires when the user presses an alphanumeric key when one of the +* menu's items has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +keyPressEvent: null, + +/** +* @event keyDownEvent +* @description Fires when the user presses a key when one of the menu's items +* has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +keyDownEvent: null, + +/** +* @event keyUpEvent +* @description Fires when the user releases a key when one of the menu's items +* has focus. Passes back the DOM Event object as an argument. +* @type YAHOO.util.CustomEvent +*/ +keyUpEvent: null, + +/** +* @event itemAddedEvent +* @description Fires when an item is added to the menu. +* @type YAHOO.util.CustomEvent +*/ +itemAddedEvent: null, + +/** +* @event itemRemovedEvent +* @description Fires when an item is removed to the menu. +* @type YAHOO.util.CustomEvent +*/ +itemRemovedEvent: null, + +/** +* @method init +* @description The Menu class's initialization method. 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. +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source +* for the menu. +* @param {HTMLDivElement} p_oElement Object +* specifying the<div>
element of the menu. +* @param {HTMLSelectElement} p_oElement +* Object specifying the<select>
element to be used as +* the data source for the menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu. See configuration class documentation for +* more details. +*/ +init: function(p_oElement, p_oConfig) { + + this._aItemGroups = []; + this._aListElements = []; + this._aGroupTitleElements = []; + + if(!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.MenuItem; + + } + + var oElement; + + if(typeof p_oElement == "string") { + + oElement = document.getElementById(p_oElement); + + } + else if(p_oElement.tagName) { + + oElement = p_oElement; + + } + + if(oElement && oElement.tagName) { + + switch(oElement.tagName.toUpperCase()) { + + case "DIV": + + this.srcElement = oElement; + + if(!oElement.id) { + + oElement.setAttribute("id", Dom.generateId()); + + } + + /* + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.Menu.superclass.init.call(this, oElement); + + this.beforeInitEvent.fire(YAHOO.widget.Menu); + + + break; + + case "SELECT": + + this.srcElement = oElement; + + + /* + The source element is not something that we can use + outright, so we need to create a new Overlay + + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.Menu.superclass.init.call(this, Dom.generateId()); + + this.beforeInitEvent.fire(YAHOO.widget.Menu); + + break; + + } + + } + else { + + /* + Note: we don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.Menu.superclass.init.call(this, p_oElement); + + this.beforeInitEvent.fire(YAHOO.widget.Menu); + + } + + if(this.element) { + + var oEl = this.element; + + Dom.addClass(oEl, this.CSS_CLASS_NAME); + + // Subscribe to Custom Events + + this.initEvent.subscribe(this._onInit, this, true); + this.beforeRenderEvent.subscribe(this._onBeforeRender, this, true); + this.renderEvent.subscribe(this._onRender, this, true); + this.beforeShowEvent.subscribe(this._onBeforeShow, this, true); + this.showEvent.subscribe(this._onShow, this, true); + this.beforeHideEvent.subscribe(this._onBeforeHide, this, true); + this.mouseOverEvent.subscribe(this._onMouseOver, this, true); + this.mouseOutEvent.subscribe(this._onMouseOut, this, true); + this.clickEvent.subscribe(this._onClick, this, true); + this.keyDownEvent.subscribe(this._onKeyDown, this, true); + + if(p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + // Register the Menu instance with the MenuManager + + YAHOO.widget.MenuManager.addMenu(this); + + + this.initEvent.fire(YAHOO.widget.Menu); + + } + +}, + +// Private methods + +/** +* @method _initSubTree +* @description Iterates the childNodes of the source element to find nodes +* used to instantiate menu and menu items. +* @private +*/ +_initSubTree: function() { + + var oNode; + + if(this.srcElement.tagName == "DIV") { + + /* + Populate the collection of item groups and item + group titles + */ + + oNode = this.body.firstChild; + + var nGroup = 0; + var sGroupTitleTagName = this.GROUP_TITLE_TAG_NAME.toUpperCase(); + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case sGroupTitleTagName: + + this._aGroupTitleElements[nGroup] = oNode; + + break; + + case "UL": + + this._aListElements[nGroup] = oNode; + this._aItemGroups[nGroup] = []; + nGroup++; + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + /* + Apply the "first-of-type" class to the first UL to mimic + the "first-of-type" CSS3 psuedo class. + */ + + if(this._aListElements[0]) { + + Dom.addClass(this._aListElements[0], "first-of-type"); + + } + + } + + oNode = null; + + if(this.srcElement.tagName) { + + switch(this.srcElement.tagName.toUpperCase()) { + + case "DIV": + + if(this._aListElements.length > 0) { + + + var i = this._aListElements.length - 1; + + do { + + oNode = this._aListElements[i].firstChild; + + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case "LI": + + + this.addItem( + new this.ITEM_TYPE( + oNode, + { parent: this } + ), + i + ); + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + } + while(i--); + + } + + break; + + case "SELECT": + + + oNode = this.srcElement.firstChild; + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case "OPTGROUP": + case "OPTION": + + + this.addItem( + new this.ITEM_TYPE( + oNode, + { parent: this } + ) + ); + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + break; + + } + + } + +}, + +/** +* @method _getFirstEnabledItem +* @description Returns the first enabled item in the menu. +* @return {YAHOO.widget.MenuItem} +* @private +*/ +_getFirstEnabledItem: function() { + + var nGroups = this._aItemGroups.length; + var oItem; + var aItemGroup; + + for(var i=0; i= aGroup.length); + + if(aGroup[p_nItemIndex]) { + + aGroup.splice(p_nItemIndex, 0, oItem); + + } + else { + + aGroup[p_nItemIndex] = oItem; + + } + + oGroupItem = aGroup[p_nItemIndex]; + + if(oGroupItem) { + + if( + bAppend && + ( + !oGroupItem.element.parentNode || + oGroupItem.element.parentNode.nodeType == 11 + ) + ) { + + this._aListElements[nGroupIndex].appendChild( + oGroupItem.element + ); + + } + else { + + + /** + * Returns the next sibling of an item in an array. + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getNextItemSibling = + + function(p_aArray, p_nStartIndex) { + + return ( + p_aArray[p_nStartIndex] || + getNextItemSibling( + p_aArray, + (p_nStartIndex+1) + ) + ); + + }; + + + var oNextItemSibling = + getNextItemSibling(aGroup, (p_nItemIndex+1)); + + if( + oNextItemSibling && + ( + !oGroupItem.element.parentNode || + oGroupItem.element.parentNode.nodeType == 11 + ) + ) { + + this._aListElements[nGroupIndex].insertBefore( + oGroupItem.element, + oNextItemSibling.element + ); + + } + + } + + + oGroupItem.parent = this; + + this._subscribeToItemEvents(oGroupItem); + + this._configureSubmenu(oGroupItem); + + this._updateItemProperties(nGroupIndex); + + + this.itemAddedEvent.fire(oGroupItem); + + return oGroupItem; + + } + + } + else { + + var nItemIndex = aGroup.length; + + aGroup[nItemIndex] = oItem; + + oGroupItem = aGroup[nItemIndex]; + + + if(oGroupItem) { + + if( + !Dom.isAncestor( + this._aListElements[nGroupIndex], + oGroupItem.element + ) + ) { + + this._aListElements[nGroupIndex].appendChild( + oGroupItem.element + ); + + } + + oGroupItem.element.setAttribute("groupindex", nGroupIndex); + oGroupItem.element.setAttribute("index", nItemIndex); + + oGroupItem.parent = this; + + oGroupItem.index = nItemIndex; + oGroupItem.groupIndex = nGroupIndex; + + this._subscribeToItemEvents(oGroupItem); + + this._configureSubmenu(oGroupItem); + + if(nItemIndex === 0) { + + Dom.addClass(oGroupItem.element, "first-of-type"); + + } + + + + this.itemAddedEvent.fire(oGroupItem); + + return oGroupItem; + + } + + } + + } + +}, + +/** +* @method _removeItemFromGroupByIndex +* @description Removes a menu item from a group by index. Returns the menu +* item that was removed. +* @private +* @param {Number} p_nGroupIndex Number indicating the group to which the menu +* item belongs. +* @param {Number} p_nItemIndex Number indicating the index of the menu item +* to be removed. +* @return {YAHOO.widget.MenuItem} +*/ +_removeItemFromGroupByIndex: function(p_nGroupIndex, p_nItemIndex) { + + var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0; + var aGroup = this._getItemGroup(nGroupIndex); + + if(aGroup) { + + var aArray = aGroup.splice(p_nItemIndex, 1); + var oItem = aArray[0]; + + if(oItem) { + + // Update the index and className properties of each member + + this._updateItemProperties(nGroupIndex); + + if(aGroup.length === 0) { + + // Remove the UL + + var oUL = this._aListElements[nGroupIndex]; + + if(this.body && oUL) { + + this.body.removeChild(oUL); + + } + + // Remove the group from the array of items + + this._aItemGroups.splice(nGroupIndex, 1); + + + // Remove the UL from the array of ULs + + this._aListElements.splice(nGroupIndex, 1); + + + /* + Assign the "first-of-type" class to the new first UL + in the collection + */ + + oUL = this._aListElements[0]; + + if(oUL) { + + Dom.addClass(oUL, "first-of-type"); + + } + + } + + + this.itemRemovedEvent.fire(oItem); + + // Return a reference to the item that was removed + + return oItem; + + } + + } + +}, + +/** +* @method _removeItemFromGroupByValue +* @description Removes a menu item from a group by reference. Returns the +* menu item that was removed. +* @private +* @param {Number} p_nGroupIndex Number indicating the group to which the +* menu item belongs. +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance to be removed. +* @return {YAHOO.widget.MenuItem} +*/ +_removeItemFromGroupByValue: function(p_nGroupIndex, p_oItem) { + + var aGroup = this._getItemGroup(p_nGroupIndex); + + if(aGroup) { + + var nItems = aGroup.length; + var nItemIndex = -1; + + if(nItems > 0) { + + var i = nItems-1; + + do { + + if(aGroup[i] == p_oItem) { + + nItemIndex = i; + break; + + } + + } + while(i--); + + if(nItemIndex > -1) { + + return this._removeItemFromGroupByIndex( + p_nGroupIndex, + nItemIndex + ); + + } + + } + + } + +}, + +/** +* @method _updateItemProperties +* @description Updates the "index," "groupindex," and "className" properties +* of the menu items in the specified group. +* @private +* @param {Number} p_nGroupIndex Number indicating the group of items to update. +*/ +_updateItemProperties: function(p_nGroupIndex) { + + var aGroup = this._getItemGroup(p_nGroupIndex); + var nItems = aGroup.length; + + if(nItems > 0) { + + var i = nItems - 1; + var oItem; + var oLI; + + // Update the index and className properties of each member + + do { + + oItem = aGroup[i]; + + if(oItem) { + + oLI = oItem.element; + + oItem.index = i; + oItem.groupIndex = p_nGroupIndex; + + oLI.setAttribute("groupindex", p_nGroupIndex); + oLI.setAttribute("index", i); + + Dom.removeClass(oLI, "first-of-type"); + + } + + } + while(i--); + + if(oLI) { + + Dom.addClass(oLI, "first-of-type"); + + } + + } + +}, + +/** +* @method _createItemGroup +* @description Creates a new menu item group (array) and its associated +* <ul>
element. Returns an aray of menu item groups. +* @private +* @param {Number} p_nIndex Number indicating the group to create. +* @return {Array} +*/ +_createItemGroup: function(p_nIndex) { + + if(!this._aItemGroups[p_nIndex]) { + + this._aItemGroups[p_nIndex] = []; + + var oUL = document.createElement("ul"); + + this._aListElements[p_nIndex] = oUL; + + return this._aItemGroups[p_nIndex]; + + } + +}, + +/** +* @method _getItemGroup +* @description Returns the menu item group at the specified index. +* @private +* @param {Number} p_nIndex Number indicating the index of the menu item group +* to be retrieved. +* @return {Array} +*/ +_getItemGroup: function(p_nIndex) { + + var nIndex = ((typeof p_nIndex == "number") ? p_nIndex : 0); + + return this._aItemGroups[nIndex]; + +}, + +/** +* @method _configureSubmenu +* @description Subscribes the menu item's submenu to its parent menu's events. +* @private +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance with the submenu to be configured. +*/ +_configureSubmenu: function(p_oItem) { + + var oSubmenu = p_oItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + /* + Listen for configuration changes to the parent menu + so they they can be applied to the submenu. + */ + + this.cfg.configChangedEvent.subscribe( + this._onParentMenuConfigChange, + oSubmenu, + true + ); + + this.renderEvent.subscribe( + this._onParentMenuRender, + oSubmenu, + true + ); + + oSubmenu.beforeShowEvent.subscribe( + this._onSubmenuBeforeShow, + oSubmenu, + true + ); + + oSubmenu.showEvent.subscribe( + this._onSubmenuShow, + oSubmenu, + true + ); + + oSubmenu.hideEvent.subscribe( + this._onSubmenuHide, + oSubmenu, + true + ); + + } + +}, + +/** +* @method _subscribeToItemEvents +* @description Subscribes a menu to a menu item's event. +* @private +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance whose events should be subscribed to. +*/ +_subscribeToItemEvents: function(p_oItem) { + + p_oItem.focusEvent.subscribe(this._onMenuItemFocus, p_oItem, this); + + p_oItem.blurEvent.subscribe(this._onMenuItemBlur, this, true); + + p_oItem.cfg.configChangedEvent.subscribe( + this._onMenuItemConfigChange, + p_oItem, + this + ); + +}, + +/** +* @method _getOffsetWidth +* @description Returns the offset width of the menu's +*<div>
element. +* @private +*/ +_getOffsetWidth: function() { + + var oClone = this.element.cloneNode(true); + + Dom.setStyle(oClone, "width", ""); + + document.body.appendChild(oClone); + + var sWidth = oClone.offsetWidth; + + document.body.removeChild(oClone); + + return sWidth; + +}, + +/** +* @method _cancelHideDelay +* @description Cancels the call to "hideMenu." +* @private +*/ +_cancelHideDelay: function() { + + var oRoot = this.getRoot(); + + if(oRoot._nHideDelayId) { + + window.clearTimeout(oRoot._nHideDelayId); + + } + +}, + +/** +* @method _execHideDelay +* @description Hides the menu after the number of milliseconds specified by +* the "hidedelay" configuration property. +* @private +*/ +_execHideDelay: function() { + + this._cancelHideDelay(); + + var oRoot = this.getRoot(); + var me = this; + + var hideMenu = function() { + + if(oRoot.activeItem) { + + oRoot.clearActiveItem(); + + } + + if(oRoot == me && me.cfg.getProperty("position") == "dynamic") { + + me.hide(); + + } + + }; + + oRoot._nHideDelayId = + window.setTimeout(hideMenu, oRoot.cfg.getProperty("hidedelay")); + +}, + +/** +* @method _cancelShowDelay +* @description Cancels the call to the "showMenu." +* @private +*/ +_cancelShowDelay: function() { + + var oRoot = this.getRoot(); + + if(oRoot._nShowDelayId) { + + window.clearTimeout(oRoot._nShowDelayId); + + } + +}, + +/** +* @method _execShowDelay +* @description Shows the menu after the number of milliseconds specified by +* the "showdelay" configuration property have ellapsed. +* @private +* @param {YAHOO.widget.Menu} p_oMenu Object specifying the menu that should +* be made visible. +*/ +_execShowDelay: function(p_oMenu) { + + this._cancelShowDelay(); + + var oRoot = this.getRoot(); + + var showMenu = function() { + + p_oMenu.show(); + + }; + + oRoot._nShowDelayId = + window.setTimeout(showMenu, oRoot.cfg.getProperty("showdelay")); + +}, + +// Protected methods + +/** +* @method _onMouseOver +* @description "mouseover" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onMouseOver: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oTarget = Event.getTarget(oEvent); + + if( + !this._bHandledMouseOverEvent && + (oTarget == this.element || Dom.isAncestor(this.element, oTarget)) + ) { + + // MENU MOUSEOVER LOGIC HERE + + this.clearActiveItem(); + + this._bHandledMouseOverEvent = true; + this._bHandledMouseOutEvent = false; + + } + + if( + oItem && !oItem.handledMouseOverEvent && + (oTarget == oItem.element || Dom.isAncestor(oItem.element, oTarget)) + ) { + + var oItemCfg = oItem.cfg; + + // Select and focus the current menu item + + oItemCfg.setProperty("selected", true); + oItem.focus(); + + if(this.cfg.getProperty("autosubmenudisplay")) { + + // Show the submenu this menu item + + var oSubmenu = oItemCfg.getProperty("submenu"); + + if(oSubmenu) { + + if(this.cfg.getProperty("showdelay") > 0) { + + this._execShowDelay(oSubmenu); + + } + else { + + oSubmenu.show(); + + } + + } + + } + + oItem.handledMouseOverEvent = true; + oItem.handledMouseOutEvent = false; + + } + +}, + +/** +* @method _onMouseOut +* @description "mouseout" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onMouseOut: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oRelatedTarget = Event.getRelatedTarget(oEvent); + var bMovingToSubmenu = false; + + if(oItem) { + + var oItemCfg = oItem.cfg; + var oSubmenu = oItemCfg.getProperty("submenu"); + + if( + oSubmenu && + ( + oRelatedTarget == oSubmenu.element || + Dom.isAncestor(oSubmenu.element, oRelatedTarget) + ) + ) { + + bMovingToSubmenu = true; + + } + + if( + !oItem.handledMouseOutEvent && + ( + ( + oRelatedTarget != oItem.element && + !Dom.isAncestor(oItem.element, oRelatedTarget) + ) || bMovingToSubmenu + ) + ) { + + if(this.cfg.getProperty("showdelay") > 0) { + + this._cancelShowDelay(); + + } + + if(!bMovingToSubmenu) { + + oItemCfg.setProperty("selected", false); + + } + + if(this.cfg.getProperty("autosubmenudisplay")) { + + if(oSubmenu) { + + if( + !( + oRelatedTarget == oSubmenu.element || + YAHOO.util.Dom.isAncestor( + oSubmenu.element, + oRelatedTarget + ) + ) + ) { + + oSubmenu.hide(); + + } + + } + + } + + oItem.handledMouseOutEvent = true; + oItem.handledMouseOverEvent = false; + + } + + } + + if( + !this._bHandledMouseOutEvent && + ( + ( + oRelatedTarget != this.element && + !Dom.isAncestor(this.element, oRelatedTarget) + ) + || bMovingToSubmenu + ) + ) { + + this._bHandledMouseOutEvent = true; + this._bHandledMouseOverEvent = false; + + } + +}, + +/** +* @method _onClick +* @description "click" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onClick: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oTarget = Event.getTarget(oEvent); + + if(oItem) { + + var oItemCfg = oItem.cfg; + var oSubmenu = oItemCfg.getProperty("submenu"); + + /* + ACCESSIBILITY FEATURE FOR SCREEN READERS: + Expand/collapse the submenu when the user clicks + on the submenu indicator image. + */ + + if(oTarget == oItem.submenuIndicator && oSubmenu) { + + if(oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + + } + else { + + this.clearActiveItem(); + + this.activeItem = oItem; + + oItem.cfg.setProperty("selected", true); + + oSubmenu.show(); + + } + + } + else { + + var sURL = oItemCfg.getProperty("url"); + var bCurrentPageURL = (sURL.substr((sURL.length-1),1) == "#"); + var sTarget = oItemCfg.getProperty("target"); + var bHasTarget = (sTarget && sTarget.length > 0); + + /* + Prevent the browser from following links + equal to "#" + */ + + if( + oTarget.tagName.toUpperCase() == "A" && + bCurrentPageURL && !bHasTarget + ) { + + Event.preventDefault(oEvent); + + } + + if( + oTarget.tagName.toUpperCase() != "A" && + !bCurrentPageURL && !bHasTarget + ) { + + /* + Follow the URL of the item regardless of + whether or not the user clicked specifically + on the anchor element. + */ + + document.location = sURL; + + } + + /* + If the item doesn't navigate to a URL and it doesn't have + a submenu, then collapse the menu tree. + */ + + if(bCurrentPageURL && !oSubmenu) { + + var oRoot = this.getRoot(); + + if(oRoot.cfg.getProperty("position") == "static") { + + oRoot.clearActiveItem(); + + } + else { + + oRoot.hide(); + + } + + } + + } + + } + +}, + +/** +* @method _onKeyDown +* @description "keydown" event handler for the menu. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onKeyDown: function(p_sType, p_aArgs, p_oMenu) { + + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oSubmenu; + + if(oItem) { + + var oItemCfg = oItem.cfg; + var oParentItem = this.parent; + var oRoot; + var oNextItem; + + switch(oEvent.keyCode) { + + case 38: // Up arrow + case 40: // Down arrow + + if( + oItem == this.activeItem && + !oItemCfg.getProperty("selected") + ) { + + oItemCfg.setProperty("selected", true); + + } + else { + + oNextItem = (oEvent.keyCode == 38) ? + oItem.getPreviousEnabledSibling() : + oItem.getNextEnabledSibling(); + + if(oNextItem) { + + this.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + oNextItem.focus(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + + case 39: // Right arrow + + oSubmenu = oItemCfg.getProperty("submenu"); + + if(oSubmenu) { + + if(!oItemCfg.getProperty("selected")) { + + oItemCfg.setProperty("selected", true); + + } + + oSubmenu.show(); + + oSubmenu.setInitialSelection(); + + } + else { + + oRoot = this.getRoot(); + + if(oRoot instanceof YAHOO.widget.MenuBar) { + + oNextItem = oRoot.activeItem.getNextEnabledSibling(); + + if(oNextItem) { + + oRoot.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + + oSubmenu = oNextItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.show(); + + } + + oNextItem.focus(); + + } + + } + + } + + + Event.preventDefault(oEvent); + + break; + + + case 37: // Left arrow + + if(oParentItem) { + + var oParentMenu = oParentItem.parent; + + if(oParentMenu instanceof YAHOO.widget.MenuBar) { + + oNextItem = + oParentMenu.activeItem.getPreviousEnabledSibling(); + + if(oNextItem) { + + oParentMenu.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + + oSubmenu = oNextItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.show(); + + } + + oNextItem.focus(); + + } + + } + else { + + this.hide(); + + oParentItem.focus(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + } + + } + + if(oEvent.keyCode == 27) { // Esc key + + if(this.cfg.getProperty("position") == "dynamic") { + + this.hide(); + + if(this.parent) { + + this.parent.focus(); + + } + + } + else if(this.activeItem) { + + oSubmenu = this.activeItem.cfg.getProperty("submenu"); + + if(oSubmenu && oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + this.activeItem.focus(); + + } + else { + + this.activeItem.cfg.setProperty("selected", false); + this.activeItem.blur(); + + } + + } + + Event.preventDefault(oEvent); + + } + +}, + +// Private methods + +/** +* @method _onInit +* @description "init" event handler for the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onInit: function(p_sType, p_aArgs, p_oMenu) { + + if( + ( + (this.parent && !this.lazyLoad) || + (!this.parent && this.cfg.getProperty("position") == "static") || + ( + !this.parent && + !this.lazyLoad && + this.cfg.getProperty("position") == "dynamic" + ) + ) && + this.getItemGroups().length === 0 + ) { + + if(this.srcElement) { + + this._initSubTree(); + + } + + if(this.itemData) { + + this.addItems(this.itemData); + + } + + } + else if(this.lazyLoad) { + + this.cfg.fireQueue(); + + } + +}, + +/** +* @method _onBeforeRender +* @description "beforerender" event handler for the menu. Appends all of the +*<ul>
,<li>
and their accompanying +* title elements to the body element of the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onBeforeRender: function(p_sType, p_aArgs, p_oMenu) { + + var oConfig = this.cfg; + var oEl = this.element; + var nListElements = this._aListElements.length; + + if(nListElements > 0) { + + var i = 0; + var bFirstList = true; + var oUL; + var oGroupTitle; + + do { + + oUL = this._aListElements[i]; + + if(oUL) { + + if(bFirstList) { + + Dom.addClass(oUL, "first-of-type"); + bFirstList = false; + + } + + if(!Dom.isAncestor(oEl, oUL)) { + + this.appendToBody(oUL); + + } + + oGroupTitle = this._aGroupTitleElements[i]; + + if(oGroupTitle) { + + if(!Dom.isAncestor(oEl, oGroupTitle)) { + + oUL.parentNode.insertBefore(oGroupTitle, oUL); + + } + + Dom.addClass(oUL, "hastitle"); + + } + + } + + i++; + + } + while(i < nListElements); + + } + +}, + +/** +* @method _onRender +* @description "render" event handler for the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onRender: function(p_sType, p_aArgs, p_oMenu) { + + if(this.cfg.getProperty("position") == "dynamic") { + + var sWidth = + this.element.parentNode.tagName.toUpperCase() == "BODY" ? + this.element.offsetWidth : this._getOffsetWidth(); + + this.cfg.setProperty("width", (sWidth + "px")); + + } + +}, + +/** +* @method _onBeforeShow +* @description "beforeshow" event handler for the menu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +_onBeforeShow: function(p_sType, p_aArgs, p_oMenu) { + + if(this.lazyLoad && this.getItemGroups().length === 0) { + + if(this.srcElement) { + + this._initSubTree(); + + } + + if(this.itemData) { + + if( + this.parent && this.parent.parent && + this.parent.parent.srcElement && + this.parent.parent.srcElement.tagName.toUpperCase() == "SELECT" + ) { + + var nOptions = this.itemData.length; + + for(var n=0; n0) { + + oConfig.showdelay = nShowDelay; + + } + + var nHideDelay = oParentMenu.cfg.getProperty("hidedelay"); + + if(nHideDelay > 0) { + + oConfig.hidedelay = nHideDelay; + + } + + /* + Only sync the "iframe" configuration property if the parent + menu's "position" configuration is the same. + */ + + if( + this.cfg.getProperty("position") == + oParentMenu.cfg.getProperty("position") + ) { + + oConfig.iframe = oParentMenu.cfg.getProperty("iframe"); + + } + + + p_oSubmenu.cfg.applyConfig(oConfig); + + if(!this.lazyLoad) { + + if(Dom.inDocument(this.element)) { + + this.render(); + + } + else { + + this.render(this.parent.element); + + } + + } + +}, + +/** +* @method _onSubmenuBeforeShow +* @description "beforeshow" event handler for a submenu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onSubmenuBeforeShow: function(p_sType, p_aArgs, p_oSubmenu) { + + var oParent = this.parent; + var aAlignment = oParent.parent.cfg.getProperty("submenualignment"); + + this.cfg.setProperty( + "context", + [oParent.element, aAlignment[0], aAlignment[1]] + ); + + oParent.submenuIndicator.alt = oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT; + +}, + +/** +* @method _onSubmenuShow +* @description "show" event handler for a submenu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onSubmenuShow: function(p_sType, p_aArgs, p_oSubmenu) { + + var oParent = this.parent; + + oParent.submenuIndicator.alt = oParent.EXPANDED_SUBMENU_INDICATOR_ALT_TEXT; + +}, + +/** +* @method _onSubmenuHide +* @description "hide" Custom Event handler for a submenu. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oSubmenu Object representing the submenu that +* subscribed to the event. +*/ +_onSubmenuHide: function(p_sType, p_aArgs, p_oSubmenu) { + + var oParent = this.parent; + + oParent.submenuIndicator.alt = oParent.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT; + +}, + +/** +* @method _onMenuItemFocus +* @description "focus" event handler for the menu's items. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item +* that fired the event. +*/ +_onMenuItemFocus: function(p_sType, p_aArgs, p_oItem) { + + this.activeItem = p_oItem; + +}, + +/** +* @method _onMenuItemBlur +* @description "blur" event handler for the menu's items. +* @private +* @param {String} p_sType String representing the name of the event +* that was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +*/ +_onMenuItemBlur: function(p_sType, p_aArgs) { + + this.activeItem = null; + +}, + +/** +* @method _onMenuItemConfigChange +* @description "configchange" event handler for the menu's items. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item +* that fired the event. +*/ +_onMenuItemConfigChange: function(p_sType, p_aArgs, p_oItem) { + + var sProperty = p_aArgs[0][0]; + + switch(sProperty) { + + case "submenu": + + var oSubmenu = p_aArgs[0][1]; + + if(oSubmenu) { + + this._configureSubmenu(p_oItem); + + } + + break; + + case "text": + case "helptext": + + /* + A change to an item's "text" or "helptext" + configuration properties requires the width of the parent + menu to be recalculated. + */ + + if(this.element.style.width) { + + var sWidth = this._getOffsetWidth() + "px"; + + Dom.setStyle(this.element, "width", sWidth); + + } + + break; + + } + +}, + +// Public event handlers for configuration properties + +/** +* @method enforceConstraints +* @description The default event handler executed when the moveEvent is fired, +* if the "constraintoviewport" configuration property is set to true. +* @param {String} type The name of the event that was fired. +* @param {Array} args Collection of arguments sent when the +* event was fired. +* @param {Array} obj Array containing the current Menu instance +* and the item that fired the event. +*/ +enforceConstraints: function(type, args, obj) { + + var oConfig = this.cfg; + + var pos = args[0]; + + var x = pos[0]; + var y = pos[1]; + + var bod = document.getElementsByTagName('body')[0]; + var htm = document.getElementsByTagName('html')[0]; + + var bodyOverflow = Dom.getStyle(bod, "overflow"); + var htmOverflow = Dom.getStyle(htm, "overflow"); + + var offsetHeight = this.element.offsetHeight; + var offsetWidth = this.element.offsetWidth; + + var viewPortWidth = Dom.getClientWidth(); + var viewPortHeight = Dom.getClientHeight(); + + var scrollX = window.scrollX || document.body.scrollLeft; + var scrollY = window.scrollY || document.body.scrollTop; + + var topConstraint = scrollY + 10; + var leftConstraint = scrollX + 10; + var bottomConstraint = scrollY + viewPortHeight - offsetHeight - 10; + var rightConstraint = scrollX + viewPortWidth - offsetWidth - 10; + + var aContext = oConfig.getProperty("context"); + var oContextElement = aContext ? aContext[0] : null; + + + if (x < 10) { + + x = leftConstraint; + + } else if ((x + offsetWidth) > viewPortWidth) { + + if( + oContextElement && + ((x - oContextElement.offsetWidth) > offsetWidth) + ) { + + x = (x - (oContextElement.offsetWidth + offsetWidth)); + + } + else { + + x = rightConstraint; + + } + + } + + if (y < 10) { + + y = topConstraint; + + } else if (y > bottomConstraint) { + + if(oContextElement && (y > offsetHeight)) { + + y = ((y + oContextElement.offsetHeight) - offsetHeight); + + } + else { + + y = bottomConstraint; + + } + + } + + oConfig.setProperty("x", x, true); + oConfig.setProperty("y", y, true); + +}, + +/** +* @method configVisible +* @description Event handler for when the "visible" configuration property +* the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configVisible: function(p_sType, p_aArgs, p_oMenu) { + + if(this.cfg.getProperty("position") == "dynamic") { + + YAHOO.widget.Menu.superclass.configVisible.call( + this, + p_sType, + p_aArgs, + p_oMenu + ); + + } + else { + + var bVisible = p_aArgs[0]; + var sDisplay = Dom.getStyle(this.element, "display"); + + if(bVisible) { + + if(sDisplay != "block") { + this.beforeShowEvent.fire(); + Dom.setStyle(this.element, "display", "block"); + this.showEvent.fire(); + } + + } + else { + + if(sDisplay == "block") { + this.beforeHideEvent.fire(); + Dom.setStyle(this.element, "display", "none"); + this.hideEvent.fire(); + } + + } + + } + +}, + +/** +* @method configPosition +* @description Event handler for when the "position" configuration property +* of the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configPosition: function(p_sType, p_aArgs, p_oMenu) { + + var sCSSPosition = p_aArgs[0] == "static" ? "static" : "absolute"; + var oCfg = this.cfg; + + Dom.setStyle(this.element, "position", sCSSPosition); + + if(sCSSPosition == "static") { + + /* + Remove the iframe for statically positioned menus since it will + intercept mouse events. + */ + + oCfg.setProperty("iframe", false); + + // Statically positioned menus are visible by default + + Dom.setStyle(this.element, "display", "block"); + + oCfg.setProperty("visible", true); + + } + else { + + /* + Even though the "visible" property is queued to + "false" by default, we need to set the "visibility" property to + "hidden" since Overlay's "configVisible" implementation checks the + element's "visibility" style property before deciding whether + or not to show an Overlay instance. + */ + + Dom.setStyle(this.element, "visibility", "hidden"); + + } + + if(sCSSPosition == "absolute") { + + var nZIndex = oCfg.getProperty("zindex"); + + if(!nZIndex || nZIndex === 0) { + + nZIndex = this.parent ? + (this.parent.parent.cfg.getProperty("zindex") + 1) : 1; + + oCfg.setProperty("zindex", nZIndex); + + } + + } + +}, + +/** +* @method configIframe +* @description Event handler for when the "iframe" configuration property of +* the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configIframe: function(p_sType, p_aArgs, p_oMenu) { + + if(this.cfg.getProperty("position") == "dynamic") { + + YAHOO.widget.Menu.superclass.configIframe.call( + this, + p_sType, + p_aArgs, + p_oMenu + ); + + } + +}, + +/** +* @method configHideDelay +* @description Event handler for when the "hidedelay" configuration property +* of the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configHideDelay: function(p_sType, p_aArgs, p_oMenu) { + + var nHideDelay = p_aArgs[0]; + var oMouseOutEvent = this.mouseOutEvent; + var oMouseOverEvent = this.mouseOverEvent; + var oKeyDownEvent = this.keyDownEvent; + + if(nHideDelay > 0) { + + /* + Only assign event handlers once. This way the user change + the value for the hidedelay as many times as they want. + */ + + if(!this._hideDelayEventHandlersAssigned) { + + oMouseOutEvent.subscribe(this._execHideDelay, true); + oMouseOverEvent.subscribe(this._cancelHideDelay, this, true); + oKeyDownEvent.subscribe(this._cancelHideDelay, this, true); + + this._hideDelayEventHandlersAssigned = true; + + } + + } + else { + + oMouseOutEvent.unsubscribe(this._execHideDelay, this); + oMouseOverEvent.unsubscribe(this._cancelHideDelay, this); + oKeyDownEvent.unsubscribe(this._cancelHideDelay, this); + + this._hideDelayEventHandlersAssigned = false; + + } + +}, + +/** +* @method configContainer +* @description Event handler for when the "container" configuration property +of the menu changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.Menu} p_oMenu Object representing the menu that +* fired the event. +*/ +configContainer: function(p_sType, p_aArgs, p_oMenu) { + + var oElement = p_aArgs[0]; + + if(typeof oElement == 'string') { + + this.cfg.setProperty( + "container", + document.getElementById(oElement), + true + ); + + } + +}, + +// Public methods + +/** +* Event handler called when the resize monitor element's "resize" evet is fired. +*/ +onDomResize: function(e, obj) { + + if(!this._handleResize) { + + this._handleResize = true; + return; + + } + + var oConfig = this.cfg; + + if(oConfig.getProperty("position") == "dynamic") { + + oConfig.setProperty("width", (this._getOffsetWidth() + "px")); + + } + + YAHOO.widget.Menu.superclass.onDomResize.call(this, e, obj); + +}, + +/** +* @method initEvents +* @description Initializes the custom events for the menu. +*/ +initEvents: function() { + + YAHOO.widget.Menu.superclass.initEvents.call(this); + + // Create custom events + + var CustomEvent = YAHOO.util.CustomEvent; + + this.mouseOverEvent = new CustomEvent("mouseOverEvent", this); + this.mouseOutEvent = new CustomEvent("mouseOutEvent", this); + this.mouseDownEvent = new CustomEvent("mouseDownEvent", this); + this.mouseUpEvent = new CustomEvent("mouseUpEvent", this); + this.clickEvent = new CustomEvent("clickEvent", this); + this.keyPressEvent = new CustomEvent("keyPressEvent", this); + this.keyDownEvent = new CustomEvent("keyDownEvent", this); + this.keyUpEvent = new CustomEvent("keyUpEvent", this); + this.itemAddedEvent = new CustomEvent("itemAddedEvent", this); + this.itemRemovedEvent = new CustomEvent("itemRemovedEvent", this); + +}, + +/** +* @method getRoot +* @description Finds the menu's root menu. +*/ +getRoot: function() { + + var oItem = this.parent; + + if(oItem) { + + var oParentMenu = oItem.parent; + + return oParentMenu ? oParentMenu.getRoot() : this; + + } + else { + + return this; + + } + +}, + +/** +* @method toString +* @description Returns a string representing the menu. +* @return {String} +*/ +toString: function() { + + return ("Menu " + this.id); + +}, + +/** +* @method setItemGroupTitle +* @description Sets the title of a group of menu items. +* @param {String} p_sGroupTitle String specifying the title of the group. +* @param {Number} p_nGroupIndex Optional. Number specifying the group to which +* the title belongs. +*/ +setItemGroupTitle: function(p_sGroupTitle, p_nGroupIndex) { + + if(typeof p_sGroupTitle == "string" && p_sGroupTitle.length > 0) { + + var nGroupIndex = typeof p_nGroupIndex == "number" ? p_nGroupIndex : 0; + var oTitle = this._aGroupTitleElements[nGroupIndex]; + + if(oTitle) { + + oTitle.innerHTML = p_sGroupTitle; + + } + else { + + oTitle = document.createElement(this.GROUP_TITLE_TAG_NAME); + + oTitle.innerHTML = p_sGroupTitle; + + this._aGroupTitleElements[nGroupIndex] = oTitle; + + } + + var i = this._aGroupTitleElements.length - 1; + var nFirstIndex; + + do { + + if(this._aGroupTitleElements[i]) { + + Dom.removeClass(this._aGroupTitleElements[i], "first-of-type"); + + nFirstIndex = i; + + } + + } + while(i--); + + if(nFirstIndex !== null) { + + Dom.addClass( + this._aGroupTitleElements[nFirstIndex], + "first-of-type" + ); + + } + + } + +}, + +/** +* @method addItem +* @description Appends an item to the menu. +* @param {YAHOO.widget.MenuItem} p_oItem Object reference for the MenuItem +* instance to be added to the menu. +* @param {String} p_oItem String specifying the text of the item to be added +* to the menu. +* @param {Object} p_oItem Object literal containing a set of menu item +* configuration properties. +* @param {Number} p_nGroupIndex Optional. Number indicating the group to +* which the item belongs. +* @return {YAHOO.widget.MenuItem} +*/ +addItem: function(p_oItem, p_nGroupIndex) { + + if(p_oItem) { + + return this._addItemToGroup(p_nGroupIndex, p_oItem); + + } + +}, + +/** +* @method addItems +* @description Adds an array of items to the menu. +* @param {Array} p_aItems Array of items to be added to the menu. The array +* can contain strings specifying the text for each item to be created, object +* literals specifying each of the menu item configuration properties, +* or MenuItem instances. +* @param {Number} p_nGroupIndex Optional. Number specifying the group to +* which the items belongs. +* @return {Array} +*/ +addItems: function(p_aItems, p_nGroupIndex) { + + function isArray(p_oValue) { + + return (typeof p_oValue == "object" && p_oValue.constructor == Array); + + } + + if(isArray(p_aItems)) { + + var nItems = p_aItems.length; + var aItems = []; + var oItem; + + for(var i=0; i <div> element +* (and accompanying child nodes) from the document. +*/ +destroy: function() { + + // Remove Custom Event listeners + + this.mouseOverEvent.unsubscribeAll(); + this.mouseOutEvent.unsubscribeAll(); + this.mouseDownEvent.unsubscribeAll(); + this.mouseUpEvent.unsubscribeAll(); + this.clickEvent.unsubscribeAll(); + this.keyPressEvent.unsubscribeAll(); + this.keyDownEvent.unsubscribeAll(); + this.keyUpEvent.unsubscribeAll(); + + var nItemGroups = this._aItemGroups.length; + var nItems; + var oItemGroup; + var oItem; + var i; + var n; + + // Remove all items + + if(nItemGroups > 0) { + + i = nItemGroups - 1; + + do { + + oItemGroup = this._aItemGroups[i]; + + if(oItemGroup) { + + nItems = oItemGroup.length; + + if(nItems > 0) { + + n = nItems - 1; + + do { + + oItem = this._aItemGroups[i][n]; + + if(oItem) { + + oItem.destroy(); + } + + } + while(n--); + + } + + } + + } + while(i--); + + } + + // Continue with the superclass implementation of this method + + YAHOO.widget.Menu.superclass.destroy.call(this); + + +}, + +/** +* @method setInitialFocus +* @description Sets focus to the menu's first enabled item. +*/ +setInitialFocus: function() { + + var oItem = this._getFirstEnabledItem(); + + if(oItem) { + + oItem.focus(); + } + +}, + +/** +* @method setInitialSelection +* @description Sets the "selected" configuration property of the menu's first +* enabled item to "true." +*/ +setInitialSelection: function() { + + var oItem = this._getFirstEnabledItem(); + + if(oItem) { + + oItem.cfg.setProperty("selected", true); + } + +}, + +/** +* @method clearActiveItem +* @description Sets the "selected" configuration property of the menu's active +* item to "false" and hides the item's submenu. +* @param {Boolean} p_bBlur Boolean indicating if the menu's active item +* should be blurred. +*/ +clearActiveItem: function(p_bBlur) { + + if(this.cfg.getProperty("showdelay") > 0) { + + this._cancelShowDelay(); + + } + + var oActiveItem = this.activeItem; + + if(oActiveItem) { + + var oConfig = oActiveItem.cfg; + + oConfig.setProperty("selected", false); + + var oSubmenu = oConfig.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.hide(); + + } + + if(p_bBlur) { + + oActiveItem.blur(); + + } + + } + +}, + +/** +* @description Initializes the class's configurable properties which can be +* changed using the menu's Config object ("cfg"). +* @method initDefaultConfig +*/ +initDefaultConfig: function() { + + YAHOO.widget.Menu.superclass.initDefaultConfig.call(this); + + var oConfig = this.cfg; + + // Add configuration properties + + /* + Change the default value for the "visible" configuration + property to "false" by re-adding the property. + */ + + /** + * @config visible + * @description Boolean indicating whether or not the menu is visible. If + * the menu's "position" configuration property is set to "dynamic" (the + * default), this property toggles the menu's <div>
+ * element's "visibility" style property between "visible" (true) or + * "hidden" (false). If the menu's "position" configuration property is + * set to "static" this property toggles the menu's + *<div>
element's "display" style property + * between "block" (true) or "none" (false). + * @default false + * @type Boolean + */ + oConfig.addProperty( + "visible", + { + value:false, + handler:this.configVisible, + validator:this.cfg.checkBoolean + } + ); + + /* + Change the default value for the "constraintoviewport" configuration + property to "true" by re-adding the property. + */ + + /** + * @config constraintoviewport + * @description Boolean indicating if the menu will try to remain inside + * the boundaries of the size of viewport. + * @default true + * @type Boolean + */ + oConfig.addProperty( + "constraintoviewport", + { + value:true, + handler:this.configConstrainToViewport, + validator:this.cfg.checkBoolean, + supercedes:["iframe","x","y","xy"] + } + ); + + /** + * @config position + * @description String indicating how a menu should be positioned on the + * screen. Possible values are "static" and "dynamic." Static menus are + * visible by default and reside in the normal flow of the document + * (CSS position: static). Dynamic menus are hidden by default, reside + * out of the normal flow of the document (CSS position: absolute), and + * can overlay other elements on the screen. + * @default dynamic + * @type String + */ + oConfig.addProperty( + "position", + { + value: "dynamic", + handler: this.configPosition, + validator: this._checkPosition, + supercedes: ["visible"] + } + ); + + /** + * @config submenualignment + * @description Array defining how submenus should be aligned to their + * parent menu item. The format is: [itemCorner, submenuCorner]. By default + * a submenu's top left corner is aligned to its parent menu item's top + * right corner. + * @default ["tl","tr"] + * @type Array + */ + oConfig.addProperty("submenualignment", { value: ["tl","tr"] } ); + + /** + * @config autosubmenudisplay + * @description Boolean indicating if submenus are automatically made + * visible when the user mouses over the menu's items. + * @default true + * @type Boolean + */ + oConfig.addProperty( + "autosubmenudisplay", + { + value: true, + validator: oConfig.checkBoolean + } + ); + + /** + * @config showdelay + * @description Number indicating the time (in milliseconds) that should + * expire before a submenu is made visible when the user mouses over + * the menu's items. + * @default 0 + * @type Number + */ + oConfig.addProperty( + "showdelay", + { + value: 0, + validator: oConfig.checkNumber + } + ); + + /** + * @config hidedelay + * @description Number indicating the time (in milliseconds) that should + * expire before the menu is hidden. + * @default 0 + * @type Number + */ + oConfig.addProperty( + "hidedelay", + { + value: 0, + validator: oConfig.checkNumber, + handler: this.configHideDelay, + suppressEvent: true + } + ); + + /** + * @config clicktohide + * @description Boolean indicating if the menu will automatically be + * hidden if the user clicks outside of it. + * @default true + * @type Boolean + */ + oConfig.addProperty( + "clicktohide", + { + value: true, + validator: oConfig.checkBoolean + } + ); + + /** + * @config container + * @description HTML element reference or string specifying the id + * attribute of the HTML element that the menu's markup should be rendered into. + * @type HTMLElement|String + * @default document.body + */ + this.cfg.addProperty( + "container", + { value:document.body, handler:this.configContainer } + ); + +} + +}); // END YAHOO.extend + +})(); + +/** +* The base class for all menuing containers. +* +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu module. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* menu module. +* @param {HTMLDivElement} p_oElement Object +* specifying the<div>
element of the menu module. +* @param {HTMLSelectElement} p_oElement Object +* specifying the<select>
element to be used as the data +* source for the menu module. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu module. See configuration class documentation for +* more details. +* @class MenuModule +* @constructor +* @extends YAHOO.widget.Overlay +* @deprecated As of version 0.12, all MenuModule functionality has been +* implemented directly in YAHOO.widget.Menu, making YAHOO.widget.Menu the base +* class for all menuing containers. +*/ +YAHOO.widget.MenuModule = YAHOO.widget.Menu; + +(function() { + +var Dom = YAHOO.util.Dom; +var Module = YAHOO.widget.Module; +var Menu = YAHOO.widget.Menu; + +/** +* Creates an item for a menu. +* +* @param {String} p_oObject String specifying the text of the menu item. +* @param {HTMLLIElement} p_oObject Object specifying +* the<li>
element of the menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the menu item. +* @param {HTMLOptionElement} p_oObject Object +* specifying the<option>
element of the menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu item. See configuration class documentation +* for more details. +* @class MenuItem +* @constructor +*/ +YAHOO.widget.MenuItem = function(p_oObject, p_oConfig) { + + if(p_oObject) { + + if(p_oConfig) { + + this.parent = p_oConfig.parent; + this.value = p_oConfig.value; + + } + + this.init(p_oObject, p_oConfig); + + } + +}; + +YAHOO.widget.MenuItem.prototype = { + + // Constants + + /** + * @property SUBMENU_INDICATOR_IMAGE_PATH + * @description String representing the path to the image to be used for the + * menu item's submenu arrow indicator. + * @default "nt/ic/ut/alt1/menuarorght8_nrm_1.gif" + * @final + * @type String + */ + SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarorght8_nrm_1.gif", + + /** + * @property SELECTED_SUBMENU_INDICATOR_IMAGE_PATH + * @description String representing the path to the image to be used for the + * submenu arrow indicator when the menu item is selected. + * @default "nt/ic/ut/alt1/menuarorght8_hov_1.gif" + * @final + * @type String + */ + SELECTED_SUBMENU_INDICATOR_IMAGE_PATH: + "nt/ic/ut/alt1/menuarorght8_hov_1.gif", + + /** + * @property DISABLED_SUBMENU_INDICATOR_IMAGE_PATH + * @description String representing the path to the image to be used for the + * submenu arrow indicator when the menu item is disabled. + * @default "nt/ic/ut/alt1/menuarorght8_dim_1.gif" + * @final + * @type String + */ + DISABLED_SUBMENU_INDICATOR_IMAGE_PATH: + "nt/ic/ut/alt1/menuarorght8_dim_1.gif", + + /** + * @property COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the submenu arrow indicator. + * @default "Collapsed. Click to expand." + * @final + * @type String + */ + COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT: "Collapsed. Click to expand.", + + /** + * @property EXPANDED_SUBMENU_INDICATOR_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the submenu arrow indicator when the submenu is visible. + * @default "Expanded. Click to collapse." + * @final + * @type String + */ + EXPANDED_SUBMENU_INDICATOR_ALT_TEXT: "Expanded. Click to collapse.", + + /** + * @property DISABLED_SUBMENU_INDICATOR_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the submenu arrow indicator when the menu item is disabled. + * @default "Disabled." + * @final + * @type String + */ + DISABLED_SUBMENU_INDICATOR_ALT_TEXT: "Disabled.", + + /** + * @property CHECKED_IMAGE_PATH + * @description String representing the path to the image to be used for + * the checked state. + * @default "nt/ic/ut/bsc/menuchk8_nrm_1.gif" + * @final + * @type String + */ + CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_nrm_1.gif", + + + /** + * @property SELECTED_CHECKED_IMAGE_PATH + * @description String representing the path to the image to be used for + * the selected checked state. + * @default "nt/ic/ut/bsc/menuchk8_hov_1.gif" + * @final + * @type String + */ + SELECTED_CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_hov_1.gif", + + + /** + * @property DISABLED_CHECKED_IMAGE_PATH + * @description String representing the path to the image to be used for + * the disabled checked state. + * @default "nt/ic/ut/bsc/menuchk8_dim_1.gif" + * @final + * @type String + */ + DISABLED_CHECKED_IMAGE_PATH: "nt/ic/ut/bsc/menuchk8_dim_1.gif", + + + /** + * @property CHECKED_IMAGE_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the checked image. + * @default "Checked." + * @final + * @type String + */ + CHECKED_IMAGE_ALT_TEXT: "Checked.", + + + /** + * @property DISABLED_CHECKED_IMAGE_ALT_TEXT + * @description String representing the alt text for the image to be used + * for the checked image when the item is disabled. + * @default "Checked. (Item disabled.)" + * @final + * @type String + */ + DISABLED_CHECKED_IMAGE_ALT_TEXT: "Checked. (Item disabled.)", + + /** + * @property CSS_CLASS_NAME + * @description String representing the CSS class(es) to be applied to the + *<li>
element of the menu item. + * @default "yuimenuitem" + * @final + * @type String + */ + CSS_CLASS_NAME: "yuimenuitem", + + /** + * @property SUBMENU_TYPE + * @description Object representing the type of menu to instantiate and + * add when parsing the child nodes of the menu item's source HTML element. + * @final + * @type YAHOO.widget.Menu + */ + SUBMENU_TYPE: null, + + /** + * @property IMG_ROOT + * @description String representing the prefix path to use for + * non-secure images. + * @default "http://us.i1.yimg.com/us.yimg.com/i/" + * @type String + */ + IMG_ROOT: "http://us.i1.yimg.com/us.yimg.com/i/", + + + /** + * @property IMG_ROOT_SSL + * @description String representing the prefix path to use for securely + * served images. + * @default "https://a248.e.akamai.net/sec.yimg.com/i/" + * @type String + */ + IMG_ROOT_SSL: "https://a248.e.akamai.net/sec.yimg.com/i/", + + // Private member variables + + /** + * @property _oAnchor + * @description Object reference to the menu item's + *<a>
element. + * @default null + * @private + * @type HTMLAnchorElement + */ + _oAnchor: null, + + + /** + * @property _oText + * @description Object reference to the menu item's text node. + * @default null + * @private + * @type TextNode + */ + _oText: null, + + + /** + * @property _oHelpTextEM + * @description Object reference to the menu item's help text + *<em>
element. + * @default null + * @private + * @type HTMLElement + */ + _oHelpTextEM: null, + + + /** + * @property _oSubmenu + * @description Object reference to the menu item's submenu. + * @default null + * @private + * @type YAHOO.widget.Menu + */ + _oSubmenu: null, + + /** + * @property _checkImage + * @description Object reference to the menu item's checkmark image. + * @default null + * @private + * @type HTMLImageElement + */ + _checkImage: null, + + // Public properties + + /** + * @property constructor + * @description Object reference to the menu item's constructor function. + * @default YAHOO.widget.MenuItem + * @type YAHOO.widget.MenuItem + */ + constructor: YAHOO.widget.MenuItem, + + /** + * @property imageRoot + * @description String representing the root path for all of the menu + * item's images. + * @type String + */ + imageRoot: null, + + /** + * @property isSecure + * @description Boolean representing whether or not the current browsing + * context is secure (HTTPS). + * @type Boolean + */ + isSecure: Module.prototype.isSecure, + + /** + * @property index + * @description Number indicating the ordinal position of the menu item in + * its group. + * @default null + * @type Number + */ + index: null, + + /** + * @property groupIndex + * @description Number indicating the index of the group to which the menu + * item belongs. + * @default null + * @type Number + */ + groupIndex: null, + + /** + * @property parent + * @description Object reference to the menu item's parent menu. + * @default null + * @type YAHOO.widget.Menu + */ + parent: null, + + /** + * @property element + * @description Object reference to the menu item's + *<li>
element. + * @default HTMLLIElement + * @type HTMLLIElement + */ + element: null, + + /** + * @property srcElement + * @description Object reference to the HTML element (either + *<li>
,<optgroup>
or + *<option>
) used create the menu item. + * @default HTMLLIElement|HTMLOptGroupElement|HTMLOptionElement + * @type HTMLLIElement| + * HTMLOptGroupElement|HTMLOptionElement + */ + srcElement: null, + + /** + * @property value + * @description Object reference to the menu item's value. + * @default null + * @type Object + */ + value: null, + + /** + * @property submenuIndicator + * @description Object reference to the<img>
element + * used to create the submenu indicator for the menu item. + * @default HTMLImageElement + * @type HTMLImageElement + */ + submenuIndicator: null, + + /** + * @property browser + * @description String representing the browser. + * @type String + */ + browser: Module.prototype.browser, + + // Events + + /** + * @event destroyEvent + * @description Fires when the menu item's<li>
+ * element is removed from its parent<ul>
element. + * @type YAHOO.util.CustomEvent + */ + destroyEvent: null, + + /** + * @event mouseOverEvent + * @description Fires when the mouse has entered the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseOverEvent: null, + + /** + * @event mouseOutEvent + * @description Fires when the mouse has left the menu item. Passes back + * the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseOutEvent: null, + + /** + * @event mouseDownEvent + * @description Fires when the user mouses down on the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseDownEvent: null, + + /** + * @event mouseUpEvent + * @description Fires when the user releases a mouse button while the mouse + * is over the menu item. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + mouseUpEvent: null, + + /** + * @event clickEvent + * @description Fires when the user clicks the on the menu item. Passes + * back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + clickEvent: null, + + /** + * @event keyPressEvent + * @description Fires when the user presses an alphanumeric key when the + * menu item has focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + keyPressEvent: null, + + /** + * @event keyDownEvent + * @description Fires when the user presses a key when the menu item has + * focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + keyDownEvent: null, + + /** + * @event keyUpEvent + * @description Fires when the user releases a key when the menu item has + * focus. Passes back the DOM Event object as an argument. + * @type YAHOO.util.CustomEvent + */ + keyUpEvent: null, + + /** + * @event focusEvent + * @description Fires when the menu item receives focus. + * @type YAHOO.util.CustomEvent + */ + focusEvent: null, + + /** + * @event blurEvent + * @description Fires when the menu item loses the input focus. + * @type YAHOO.util.CustomEvent + */ + blurEvent: null, + + /** + * @method init + * @description The MenuItem class's initialization method. 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. + * @param {String} p_oObject String specifying the text of the menu item. + * @param {HTMLLIElement} p_oObject Object specifying + * the<li>
element of the menu item. + * @param {HTMLOptGroupElement} p_oObject Object + * specifying the<optgroup>
element of the menu item. + * @param {HTMLOptionElement} p_oObject Object + * specifying the<option>
element of the menu item. + * @param {Object} p_oConfig Optional. Object literal specifying the + * configuration for the menu item. See configuration class documentation + * for more details. + */ + init: function(p_oObject, p_oConfig) { + + this.imageRoot = (this.isSecure) ? this.IMG_ROOT_SSL : this.IMG_ROOT; + + if(!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = Menu; + + } + + // Create the config object + + this.cfg = new YAHOO.util.Config(this); + + this.initDefaultConfig(); + + var oConfig = this.cfg; + + if(this._checkString(p_oObject)) { + + this._createRootNodeStructure(); + + oConfig.setProperty("text", p_oObject); + + } + else if(this._checkDOMNode(p_oObject)) { + + switch(p_oObject.tagName.toUpperCase()) { + + case "OPTION": + + this._createRootNodeStructure(); + + oConfig.setProperty("text", p_oObject.text); + + this.srcElement = p_oObject; + + break; + + case "OPTGROUP": + + this._createRootNodeStructure(); + + oConfig.setProperty("text", p_oObject.label); + + this.srcElement = p_oObject; + + this._initSubTree(); + + break; + + case "LI": + + // Get the anchor node (if it exists) + + var oAnchor = this._getFirstElement(p_oObject, "A"); + var sURL = "#"; + var sTarget = null; + var sText = null; + + // Capture the "text" and/or the "URL" + + if(oAnchor) { + + sURL = oAnchor.getAttribute("href"); + sTarget = oAnchor.getAttribute("target"); + + if(oAnchor.innerText) { + + sText = oAnchor.innerText; + + } + else { + + var oRange = oAnchor.ownerDocument.createRange(); + + oRange.selectNodeContents(oAnchor); + + sText = oRange.toString(); + + } + + } + else { + + var oText = p_oObject.firstChild; + + sText = oText.nodeValue; + + oAnchor = document.createElement("a"); + + oAnchor.setAttribute("href", sURL); + + p_oObject.replaceChild(oAnchor, oText); + + oAnchor.appendChild(oText); + + } + + this.srcElement = p_oObject; + this.element = p_oObject; + this._oAnchor = oAnchor; + + + // Check if emphasis has been applied to the MenuItem + + var oEmphasisNode = this._getFirstElement(oAnchor); + var bEmphasis = false; + var bStrongEmphasis = false; + + if(oEmphasisNode) { + + // Set a reference to the text node + + this._oText = oEmphasisNode.firstChild; + + switch(oEmphasisNode.tagName.toUpperCase()) { + + case "EM": + + bEmphasis = true; + + break; + + case "STRONG": + + bStrongEmphasis = true; + + break; + + } + + } + else { + + // Set a reference to the text node + + this._oText = oAnchor.firstChild; + + } + + /* + Set these properties silently to sync up the + configuration object without making changes to the + element's DOM + */ + + oConfig.setProperty("text", sText, true); + oConfig.setProperty("url", sURL, true); + oConfig.setProperty("target", sTarget, true); + oConfig.setProperty("emphasis", bEmphasis, true); + oConfig.setProperty( + "strongemphasis", + bStrongEmphasis, + true + ); + + this._initSubTree(); + + break; + + } + + } + + if(this.element) { + + Dom.addClass(this.element, this.CSS_CLASS_NAME); + + // Create custom events + + var CustomEvent = YAHOO.util.CustomEvent; + + this.destroyEvent = new CustomEvent("destroyEvent", this); + this.mouseOverEvent = new CustomEvent("mouseOverEvent", this); + this.mouseOutEvent = new CustomEvent("mouseOutEvent", this); + this.mouseDownEvent = new CustomEvent("mouseDownEvent", this); + this.mouseUpEvent = new CustomEvent("mouseUpEvent", this); + this.clickEvent = new CustomEvent("clickEvent", this); + this.keyPressEvent = new CustomEvent("keyPressEvent", this); + this.keyDownEvent = new CustomEvent("keyDownEvent", this); + this.keyUpEvent = new CustomEvent("keyUpEvent", this); + this.focusEvent = new CustomEvent("focusEvent", this); + this.blurEvent = new CustomEvent("blurEvent", this); + + if(p_oConfig) { + + oConfig.applyConfig(p_oConfig); + + } + + oConfig.fireQueue(); + + } + + }, + + // Private methods + + /** + * @method _getFirstElement + * @description Returns an HTML element's first HTML element node. + * @private + * @param {HTMLElement} p_oElement Object + * reference specifying the element to be evaluated. + * @param {String} p_sTagName Optional. String specifying the tagname of + * the element to be retrieved. + * @return {HTMLElement} + */ + _getFirstElement: function(p_oElement, p_sTagName) { + + var oElement; + + if(p_oElement.firstChild && p_oElement.firstChild.nodeType == 1) { + + oElement = p_oElement.firstChild; + + } + else if( + p_oElement.firstChild && + p_oElement.firstChild.nextSibling && + p_oElement.firstChild.nextSibling.nodeType == 1 + ) { + + oElement = p_oElement.firstChild.nextSibling; + + } + + if(p_sTagName) { + + return (oElement && oElement.tagName.toUpperCase() == p_sTagName) ? + oElement : false; + + } + + return oElement; + + }, + + /** + * @method _checkString + * @description Determines if an object is a string. + * @private + * @param {Object} p_oObject Object to be evaluated. + * @return {Boolean} + */ + _checkString: function(p_oObject) { + + return (typeof p_oObject == "string"); + + }, + + /** + * @method _checkDOMNode + * @description Determines if an object is an HTML element. + * @private + * @param {Object} p_oObject Object to be evaluated. + * @return {Boolean} + */ + _checkDOMNode: function(p_oObject) { + + return (p_oObject && p_oObject.tagName); + + }, + + /** + * @method _createRootNodeStructure + * @description Creates the core DOM structure for the menu item. + * @private + */ + _createRootNodeStructure: function () { + + this.element = document.createElement("li"); + + this._oText = document.createTextNode(""); + + this._oAnchor = document.createElement("a"); + this._oAnchor.appendChild(this._oText); + + this.cfg.refireEvent("url"); + + this.element.appendChild(this._oAnchor); + + }, + + /** + * @method _initSubTree + * @description Iterates the source element's childNodes collection and uses + * the child nodes to instantiate other menus. + * @private + */ + _initSubTree: function() { + + var oSrcEl = this.srcElement; + var oConfig = this.cfg; + + if(oSrcEl.childNodes.length > 0) { + + if( + this.parent.lazyLoad && + this.parent.srcElement && + this.parent.srcElement.tagName.toUpperCase() == "SELECT" + ) { + + oConfig.setProperty( + "submenu", + { id: Dom.generateId(), itemdata: oSrcEl.childNodes } + ); + + } + else { + + var oNode = oSrcEl.firstChild; + var aOptions = []; + + do { + + if(oNode && oNode.tagName) { + + switch(oNode.tagName.toUpperCase()) { + + case "DIV": + + oConfig.setProperty("submenu", oNode); + + break; + + case "OPTION": + + aOptions[aOptions.length] = oNode; + + break; + + } + + } + + } + while((oNode = oNode.nextSibling)); + + + var nOptions = aOptions.length; + + if(nOptions > 0) { + + var oMenu = new this.SUBMENU_TYPE(Dom.generateId()); + + oConfig.setProperty("submenu", oMenu); + + for(var n=0; n0) { + + oAnchor.setAttribute("target", sTarget); + + } + else { + + oAnchor.removeAttribute("target"); + + } + + }, + + /** + * @method configEmphasis + * @description Event handler for when the "emphasis" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configEmphasis: function(p_sType, p_aArgs, p_oItem) { + + var bEmphasis = p_aArgs[0]; + var oAnchor = this._oAnchor; + var oText = this._oText; + var oConfig = this.cfg; + var oEM; + + if(bEmphasis && oConfig.getProperty("strongemphasis")) { + + oConfig.setProperty("strongemphasis", false); + + } + + if(oAnchor) { + + if(bEmphasis) { + + oEM = document.createElement("em"); + oEM.appendChild(oText); + + oAnchor.appendChild(oEM); + + } + else { + + oEM = this._getFirstElement(oAnchor, "EM"); + + oAnchor.removeChild(oEM); + oAnchor.appendChild(oText); + + } + + } + + }, + + /** + * @method configStrongEmphasis + * @description Event handler for when the "strongemphasis" configuration + * property of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configStrongEmphasis: function(p_sType, p_aArgs, p_oItem) { + + var bStrongEmphasis = p_aArgs[0]; + var oAnchor = this._oAnchor; + var oText = this._oText; + var oConfig = this.cfg; + var oStrong; + + if(bStrongEmphasis && oConfig.getProperty("emphasis")) { + + oConfig.setProperty("emphasis", false); + + } + + if(oAnchor) { + + if(bStrongEmphasis) { + + oStrong = document.createElement("strong"); + oStrong.appendChild(oText); + + oAnchor.appendChild(oStrong); + + } + else { + + oStrong = this._getFirstElement(oAnchor, "STRONG"); + + oAnchor.removeChild(oStrong); + oAnchor.appendChild(oText); + + } + + } + + }, + + /** + * @method configChecked + * @description Event handler for when the "checked" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configChecked: function(p_sType, p_aArgs, p_oItem) { + + var bChecked = p_aArgs[0]; + var oEl = this.element; + var oConfig = this.cfg; + var oImg; + + + if(bChecked) { + + this._preloadImage(this.CHECKED_IMAGE_PATH); + this._preloadImage(this.SELECTED_CHECKED_IMAGE_PATH); + this._preloadImage(this.DISABLED_CHECKED_IMAGE_PATH); + + oImg = document.createElement("img"); + oImg.src = (this.imageRoot + this.CHECKED_IMAGE_PATH); + oImg.alt = this.CHECKED_IMAGE_ALT_TEXT; + + var oSubmenu = this.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oEl.insertBefore(oImg, oSubmenu.element); + + } + else { + + oEl.appendChild(oImg); + + } + + Dom.addClass([oEl, oImg], "checked"); + + this._checkImage = oImg; + + if(oConfig.getProperty("disabled")) { + + oConfig.refireEvent("disabled"); + + } + + if(oConfig.getProperty("selected")) { + + oConfig.refireEvent("selected"); + + } + + } + else { + + oImg = this._checkImage; + + Dom.removeClass([oEl, oImg], "checked"); + + if(oImg) { + + oEl.removeChild(oImg); + + } + + this._checkImage = null; + + } + + }, + + /** + * @method configDisabled + * @description Event handler for when the "disabled" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configDisabled: function(p_sType, p_aArgs, p_oItem) { + + var bDisabled = p_aArgs[0]; + var oAnchor = this._oAnchor; + var aNodes = [this.element, oAnchor]; + var oEM = this._oHelpTextEM; + var oConfig = this.cfg; + var oImg; + var sImgSrc; + var sImgAlt; + + if(oEM) { + + aNodes[2] = oEM; + + } + + if(this.cfg.getProperty("checked")) { + + sImgAlt = this.CHECKED_IMAGE_ALT_TEXT; + sImgSrc = this.CHECKED_IMAGE_PATH; + oImg = this._checkImage; + + if(bDisabled) { + + sImgAlt = this.DISABLED_CHECKED_IMAGE_ALT_TEXT; + sImgSrc = this.DISABLED_CHECKED_IMAGE_PATH; + + } + + oImg.src = document.images[(this.imageRoot + sImgSrc)].src; + oImg.alt = sImgAlt; + + } + + oImg = this.submenuIndicator; + + if(bDisabled) { + + if(oConfig.getProperty("selected")) { + + oConfig.setProperty("selected", false); + + } + + oAnchor.removeAttribute("href"); + + Dom.addClass(aNodes, "disabled"); + + sImgSrc = this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH; + sImgAlt = this.DISABLED_SUBMENU_INDICATOR_ALT_TEXT; + + } + else { + + oAnchor.setAttribute("href", oConfig.getProperty("url")); + + Dom.removeClass(aNodes, "disabled"); + + sImgSrc = this.SUBMENU_INDICATOR_IMAGE_PATH; + sImgAlt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT; + + } + + if(oImg) { + + oImg.src = this.imageRoot + sImgSrc; + oImg.alt = sImgAlt; + + } + + }, + + /** + * @method configSelected + * @description Event handler for when the "selected" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configSelected: function(p_sType, p_aArgs, p_oItem) { + + if(!this.cfg.getProperty("disabled")) { + + var bSelected = p_aArgs[0]; + var oEM = this._oHelpTextEM; + var aNodes = [this.element, this._oAnchor]; + var oImg = this.submenuIndicator; + var sImgSrc; + + if(oEM) { + + aNodes[aNodes.length] = oEM; + + } + + if(oImg) { + + aNodes[aNodes.length] = oImg; + + } + + + if(this.cfg.getProperty("checked")) { + + sImgSrc = this.imageRoot + (bSelected ? + this.SELECTED_CHECKED_IMAGE_PATH : this.CHECKED_IMAGE_PATH); + + this._checkImage.src = document.images[sImgSrc].src; + + } + + if(bSelected) { + + Dom.addClass(aNodes, "selected"); + sImgSrc = this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH; + + } + else { + + Dom.removeClass(aNodes, "selected"); + sImgSrc = this.SUBMENU_INDICATOR_IMAGE_PATH; + + } + + if(oImg) { + + oImg.src = document.images[(this.imageRoot + sImgSrc)].src; + + } + + } + + }, + + /** + * @method configSubmenu + * @description Event handler for when the "submenu" configuration property + * of the menu item changes. + * @param {String} p_sType String representing the name of the event that + * was fired. + * @param {Array} p_aArgs Array of arguments sent when the event was fired. + * @param {YAHOO.widget.MenuItem} p_oItem Object representing the menu item + * that fired the event. + */ + configSubmenu: function(p_sType, p_aArgs, p_oItem) { + + var oEl = this.element; + var oSubmenu = p_aArgs[0]; + var oImg = this.submenuIndicator; + var oConfig = this.cfg; + var aNodes = [this.element, this._oAnchor]; + var oMenu; + var bLazyLoad = this.parent && this.parent.lazyLoad; + + if(oSubmenu) { + + if(oSubmenu instanceof Menu) { + + oMenu = oSubmenu; + oMenu.parent = this; + oMenu.lazyLoad = bLazyLoad; + + } + else if( + typeof oSubmenu == "object" && + oSubmenu.id && + !oSubmenu.nodeType + ) { + + var sSubmenuId = oSubmenu.id; + var oSubmenuConfig = oSubmenu; + + delete oSubmenu["id"]; + + oSubmenuConfig.lazyload = bLazyLoad; + oSubmenuConfig.parent = this; + + oMenu = new this.SUBMENU_TYPE(sSubmenuId, oSubmenuConfig); + + // Set the value of the property to the Menu instance + + this.cfg.setProperty("submenu", oMenu, true); + + } + else { + + oMenu = new this.SUBMENU_TYPE( + oSubmenu, + { lazyload: bLazyLoad, parent: this } + ); + + // Set the value of the property to the Menu instance + + this.cfg.setProperty("submenu", oMenu, true); + + } + + if(oMenu) { + + this._oSubmenu = oMenu; + + if(!oImg) { + + this._preloadImage(this.SUBMENU_INDICATOR_IMAGE_PATH); + this._preloadImage( + this.SELECTED_SUBMENU_INDICATOR_IMAGE_PATH + ); + + this._preloadImage( + this.DISABLED_SUBMENU_INDICATOR_IMAGE_PATH + ); + + oImg = document.createElement("img"); + + oImg.src = + (this.imageRoot + this.SUBMENU_INDICATOR_IMAGE_PATH); + + oImg.alt = this.COLLAPSED_SUBMENU_INDICATOR_ALT_TEXT; + + oEl.appendChild(oImg); + + this.submenuIndicator = oImg; + + Dom.addClass(aNodes, "hassubmenu"); + + if(oConfig.getProperty("disabled")) { + + oConfig.refireEvent("disabled"); + + } + + if(oConfig.getProperty("selected")) { + + oConfig.refireEvent("selected"); + + } + + } + + } + + } + else { + + Dom.removeClass(aNodes, "hassubmenu"); + + if(oImg) { + + oEl.removeChild(oImg); + + } + + if(this._oSubmenu) { + + this._oSubmenu.destroy(); + + } + + } + + }, + + // Public methods + + /** + * @method initDefaultConfig + * @description Initializes an item's configurable properties. + */ + initDefaultConfig : function() { + + var oConfig = this.cfg; + var CheckBoolean = oConfig.checkBoolean; + + // Define the config properties + + /** + * @config text + * @description String specifying the text label for the menu item. + * When building a menu from existing HTML the value of this property + * will be interpreted from the menu's markup. + * @default "" + * @type String + */ + oConfig.addProperty( + "text", + { + value: "", + handler: this.configText, + validator: this._checkString, + suppressEvent: true + } + ); + + + /** + * @config helptext + * @description String specifying additional instructional text to + * accompany the text for the nenu item. + * @default null + * @type String| + * HTMLElement + */ + oConfig.addProperty("helptext", { handler: this.configHelpText }); + + /** + * @config url + * @description String specifying the URL for the menu item's anchor's + * "href" attribute. When building a menu from existing HTML the value + * of this property will be interpreted from the menu's markup. + * @default "#" + * @type String + */ + oConfig.addProperty( + "url", + { value: "#", handler: this.configURL, suppressEvent: true } + ); + + /** + * @config target + * @description String specifying the value for the "target" attribute + * of the menu item's anchor element. Specifying a target will + * require the user to click directly on the menu item's anchor node in + * order to cause the browser to navigate to the specified URL. + * When building a menu from existing HTML the value of this property + * will be interpreted from the menu's markup. + * @default null + * @type String + */ + oConfig.addProperty( + "target", + { handler: this.configTarget, suppressEvent: true } + ); + + /** + * @config emphasis + * @description Boolean indicating if the text of the menu item will be + * rendered with emphasis. When building a menu from existing HTML the + * value of this property will be interpreted from the menu's markup. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "emphasis", + { + value: false, + handler: this.configEmphasis, + validator: CheckBoolean, + suppressEvent: true + } + ); + + /** + * @config strongemphasis + * @description Boolean indicating if the text of the menu item will be + * rendered with strong emphasis. When building a menu from existing + * HTML the value of this property will be interpreted from the + * menu's markup. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "strongemphasis", + { + value: false, + handler: this.configStrongEmphasis, + validator: CheckBoolean, + suppressEvent: true + } + ); + + /** + * @config checked + * @description Boolean indicating if the menu item should be rendered + * with a checkmark. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "checked", + { + value: false, + handler: this.configChecked, + validator: this.cfg.checkBoolean, + suppressEvent: true, + supercedes:["disabled"] + } + ); + + /** + * @config disabled + * @description Boolean indicating if the menu item should be disabled. + * (Disabled menu items are dimmed and will not respond to user input + * or fire events.) + * @default false + * @type Boolean + */ + oConfig.addProperty( + "disabled", + { + value: false, + handler: this.configDisabled, + validator: CheckBoolean, + suppressEvent: true + } + ); + + /** + * @config selected + * @description Boolean indicating if the menu item should + * be highlighted. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "selected", + { + value: false, + handler: this.configSelected, + validator: CheckBoolean, + suppressEvent: true + } + ); + + /** + * @config submenu + * @description Object specifying the submenu to be appended to the + * menu item. The value can be one of the following: + * @default null + * @type Menu|String|Object| + * HTMLElement + */ + oConfig.addProperty("submenu", { handler: this.configSubmenu }); + + }, + + /** + * @method getNextEnabledSibling + * @description Finds the menu item's next enabled sibling. + * @return YAHOO.widget.MenuItem + */ + getNextEnabledSibling: function() { + + if(this.parent instanceof Menu) { + + var nGroupIndex = this.groupIndex; + + /** + * Finds the next item in an array. + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getNextArrayItem = function(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] || + getNextArrayItem(p_aArray, (p_nStartIndex+1)); + + }; + + + var aItemGroups = this.parent.getItemGroups(); + var oNextItem; + + + if(this.index < (aItemGroups[nGroupIndex].length - 1)) { + + oNextItem = getNextArrayItem( + aItemGroups[nGroupIndex], + (this.index+1) + ); + + } + else { + + var nNextGroupIndex; + + if(nGroupIndex < (aItemGroups.length - 1)) { + + nNextGroupIndex = nGroupIndex + 1; + + } + else { + + nNextGroupIndex = 0; + + } + + var aNextGroup = getNextArrayItem(aItemGroups, nNextGroupIndex); + + // Retrieve the first menu item in the next group + + oNextItem = getNextArrayItem(aNextGroup, 0); + + } + + return ( + oNextItem.cfg.getProperty("disabled") || + oNextItem.element.style.display == "none" + ) ? + oNextItem.getNextEnabledSibling() : oNextItem; + + } + + }, + + /** + * @method getPreviousEnabledSibling + * @description Finds the menu item's previous enabled sibling. + * @return {YAHOO.widget.MenuItem} + */ + getPreviousEnabledSibling: function() { + + if(this.parent instanceof Menu) { + + var nGroupIndex = this.groupIndex; + + /** + * Returns the previous item in an array + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getPreviousArrayItem = function(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] || + getPreviousArrayItem(p_aArray, (p_nStartIndex-1)); + + }; + + /** + * Get the index of the first item in an array + * @private + * @param {p_aArray} Array to search. + * @param {p_nStartIndex} Number indicating the index to + * start searching the array. + * @return {Object} + */ + var getFirstItemIndex = function(p_aArray, p_nStartIndex) { + + return p_aArray[p_nStartIndex] ? + p_nStartIndex : + getFirstItemIndex(p_aArray, (p_nStartIndex+1)); + + }; + + var aItemGroups = this.parent.getItemGroups(); + var oPreviousItem; + + if( + this.index > getFirstItemIndex(aItemGroups[nGroupIndex], 0) + ) { + + oPreviousItem = + getPreviousArrayItem( + aItemGroups[nGroupIndex], + (this.index-1) + ); + + } + else { + + var nPreviousGroupIndex; + + if(nGroupIndex > getFirstItemIndex(aItemGroups, 0)) { + + nPreviousGroupIndex = nGroupIndex - 1; + + } + else { + + nPreviousGroupIndex = aItemGroups.length - 1; + + } + + var aPreviousGroup = + getPreviousArrayItem(aItemGroups, nPreviousGroupIndex); + + oPreviousItem = + getPreviousArrayItem( + aPreviousGroup, + (aPreviousGroup.length - 1) + ); + + } + + return ( + oPreviousItem.cfg.getProperty("disabled") || + oPreviousItem.element.style.display == "none" + ) ? + oPreviousItem.getPreviousEnabledSibling() : oPreviousItem; + + } + + }, + + /** + * @method focus + * @description Causes the menu item to receive the focus and fires the + * focus event. + */ + focus: function() { + + var oParent = this.parent; + var oAnchor = this._oAnchor; + var oActiveItem = oParent.activeItem; + + if( + !this.cfg.getProperty("disabled") && + oParent && + oParent.cfg.getProperty("visible") && + this.element.style.display != "none" + ) { + + if(oActiveItem) { + + oActiveItem.blur(); + + } + + try { + + oAnchor.focus(); + + } + catch(e) { + + } + + this.focusEvent.fire(); + + } + + }, + + /** + * @method blur + * @description Causes the menu item to lose focus and fires the + * onblur event. + */ + blur: function() { + + var oParent = this.parent; + + if( + !this.cfg.getProperty("disabled") && + oParent && + Dom.getStyle(oParent.element, "visibility") == "visible" + ) { + + this._oAnchor.blur(); + + this.blurEvent.fire(); + + } + + }, + + /** + * @method destroy + * @description Removes the menu item's
- Object + * specifying a Menu instance.
- Object literal specifying the + * menu to be created. Format:
{ id: [menu id], itemdata: + * [array of values for + * items] }
.- String specifying the id attribute + * of the
<div>
element of the menu.- + * Object specifying the
<div>
element of the + * menu.<li>
element + * from its parent<ul>
element. + */ + destroy: function() { + + var oEl = this.element; + + if(oEl) { + + // Remove CustomEvent listeners + + this.mouseOverEvent.unsubscribeAll(); + this.mouseOutEvent.unsubscribeAll(); + this.mouseDownEvent.unsubscribeAll(); + this.mouseUpEvent.unsubscribeAll(); + this.clickEvent.unsubscribeAll(); + this.keyPressEvent.unsubscribeAll(); + this.keyDownEvent.unsubscribeAll(); + this.keyUpEvent.unsubscribeAll(); + this.focusEvent.unsubscribeAll(); + this.blurEvent.unsubscribeAll(); + this.cfg.configChangedEvent.unsubscribeAll(); + + // Remove the element from the parent node + + var oParentNode = oEl.parentNode; + + if(oParentNode) { + + oParentNode.removeChild(oEl); + + this.destroyEvent.fire(); + + } + + this.destroyEvent.unsubscribeAll(); + + } + + }, + + /** + * @method toString + * @description Returns a string representing the menu item. + * @return {String} + */ + toString: function() { + + return ("MenuItem: " + this.cfg.getProperty("text")); + + } + +}; + +})(); + +/** +* Creates an item for a menu module. +* +* @param {String} p_oObject String specifying the text of the menu module item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the menu module item. +* @param {HTMLOptGroupElement} p_oObject Object specifying +* the<optgroup>
element of the menu module item. +* @param {HTMLOptionElement} p_oObject Object specifying the +*<option>
element of the menu module item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu module item. See configuration class documentation +* for more details. +* @class MenuModuleItem +* @constructor +* @deprecated As of version 0.12, all MenuModuleItem functionality has been +* implemented directly in YAHOO.widget.MenuItem, making YAHOO.widget.MenuItem +* the base class for all menu items. +*/ +YAHOO.widget.MenuModuleItem = YAHOO.widget.MenuItem; + +/** +* Creates a list of options or commands which are made visible in response to +* an HTML element's "contextmenu" event ("mousedown" for Opera). +* +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the context menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* context menu. +* @param {HTMLDivElement} p_oElement Object specifying the +*<div>
element of the context menu. +* @param {HTMLSelectElement} p_oElement Object specifying +* the<select>
element to be used as the data source for +* the context menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu. See configuration class documentation +* for more details. +* @class ContextMenu +* @constructor +* @extends YAHOO.widget.Menu +* @namespace YAHOO.widget +*/ +YAHOO.widget.ContextMenu = function(p_oElement, p_oConfig) { + + YAHOO.widget.ContextMenu.superclass.constructor.call( + this, + p_oElement, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.ContextMenu, YAHOO.widget.Menu, { + +// Private properties + +/** +* @property _oTrigger +* @description Object reference to the current value of the "trigger" +* configuration property. +* @default null +* @private +* @type String|HTMLElement|Array +*/ +_oTrigger: null, + +// Public properties + +/** +* @property contextEventTarget +* @description Object reference for the HTML element that was the target of the +* "contextmenu" DOM event ("mousedown" for Opera) that triggered the display of +* the context menu. +* @default null +* @type HTMLElement +*/ +contextEventTarget: null, + +/** +* @method init +* @description The ContextMenu class's initialization method. 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. +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the context menu. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for +* the context menu. +* @param {HTMLDivElement} p_oElement Object specifying the +*<div>
element of the context menu. +* @param {HTMLSelectElement} p_oElement Object specifying +* the<select>
element to be used as the data source for +* the context menu. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu. See configuration class documentation +* for more details. +*/ +init: function(p_oElement, p_oConfig) { + + if(!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.ContextMenuItem; + + } + + // Call the init of the superclass (YAHOO.widget.Menu) + + YAHOO.widget.ContextMenu.superclass.init.call(this, p_oElement); + + this.beforeInitEvent.fire(YAHOO.widget.ContextMenu); + + if(p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + + this.initEvent.fire(YAHOO.widget.ContextMenu); + +}, + +// Private methods + +/** +* @method _removeEventHandlers +* @description Removes all of the DOM event handlers from the HTML element(s) +* whose "context menu" event ("click" for Opera) trigger the display of +* the context menu. +* @private +*/ +_removeEventHandlers: function() { + + var Event = YAHOO.util.Event; + var oTrigger = this._oTrigger; + var bOpera = (this.browser == "opera"); + + // Remove the event handlers from the trigger(s) + + Event.removeListener( + oTrigger, + (bOpera ? "mousedown" : "contextmenu"), + this._onTriggerContextMenu + ); + + if(bOpera) { + + Event.removeListener(oTrigger, "click", this._onTriggerClick); + + } + +}, + +// Private event handlers + +/** +* @method _onTriggerClick +* @description "click" event handler for the HTML element(s) identified as the +* "trigger" for the context menu. Used to cancel default behaviors in Opera. +* @private +* @param {Event} p_oEvent Object representing the DOM event object passed back +* by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that is handling the event. +*/ +_onTriggerClick: function(p_oEvent, p_oMenu) { + + if(p_oEvent.ctrlKey) { + + YAHOO.util.Event.stopEvent(p_oEvent); + + } + +}, + +/** +* @method _onTriggerContextMenu +* @description "contextmenu" event handler ("mousedown" for Opera) for the HTML +* element(s) that trigger the display of the context menu. +* @private +* @param {Event} p_oEvent Object representing the DOM event object passed back +* by the event utility (YAHOO.util.Event). +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that is handling the event. +*/ +_onTriggerContextMenu: function(p_oEvent, p_oMenu) { + + // Hide any other ContextMenu instances that might be visible + + YAHOO.widget.MenuManager.hideVisible(); + + var Event = YAHOO.util.Event; + var oConfig = this.cfg; + + if(p_oEvent.type == "mousedown" && !p_oEvent.ctrlKey) { + + return; + + } + + this.contextEventTarget = Event.getTarget(p_oEvent); + + // Position and display the context menu + + var nX = Event.getPageX(p_oEvent); + var nY = Event.getPageY(p_oEvent); + + oConfig.applyConfig( { xy:[nX, nY], visible:true } ); + oConfig.fireQueue(); + + /* + Prevent the browser's default context menu from appearing and + stop the propagation of the "contextmenu" event so that + other ContextMenu instances are not displayed. + */ + + Event.stopEvent(p_oEvent); + +}, + +// Public methods + +/** +* @method toString +* @description Returns a string representing the context menu. +* @return {String} +*/ +toString: function() { + + return ("ContextMenu " + this.id); + +}, + +/** +* @method initDefaultConfig +* @description Initializes the class's configurable properties which can be +* changed using the context menu's Config object ("cfg"). +*/ +initDefaultConfig: function() { + + YAHOO.widget.ContextMenu.superclass.initDefaultConfig.call(this); + + /** + * @config trigger + * @description The HTML element(s) whose "contextmenu" event ("mousedown" + * for Opera) trigger the display of the context menu. Can be a string + * representing the id attribute of the HTML element, an object reference + * for the HTML element, or an array of strings or HTML element references. + * @default null + * @type String|HTMLElement|Array + */ + this.cfg.addProperty("trigger", { handler: this.configTrigger }); + +}, + +/** +* @method destroy +* @description Removes the context menu's<div>
element +* (and accompanying child nodes) from the document. +*/ +destroy: function() { + + // Remove the DOM event handlers from the current trigger(s) + + this._removeEventHandlers(); + + + // Continue with the superclass implementation of this method + + YAHOO.widget.ContextMenu.superclass.destroy.call(this); + +}, + +// Public event handlers for configuration properties + +/** +* @method configTrigger +* @description Event handler for when the value of the "trigger" configuration +* property changes. +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.ContextMenu} p_oMenu Object representing the context +* menu that fired the event. +*/ +configTrigger: function(p_sType, p_aArgs, p_oMenu) { + + var Event = YAHOO.util.Event; + var oTrigger = p_aArgs[0]; + + if(oTrigger) { + + /* + If there is a current "trigger" - remove the event handlers + from that element(s) before assigning new ones + */ + + if(this._oTrigger) { + + this._removeEventHandlers(); + + } + + this._oTrigger = oTrigger; + + /* + Listen for the "mousedown" event in Opera b/c it does not + support the "contextmenu" event + */ + + var bOpera = (this.browser == "opera"); + + Event.addListener( + oTrigger, + (bOpera ? "mousedown" : "contextmenu"), + this._onTriggerContextMenu, + this, + true + ); + + /* + Assign a "click" event handler to the trigger element(s) for + Opera to prevent default browser behaviors. + */ + + if(bOpera) { + + Event.addListener( + oTrigger, + "click", + this._onTriggerClick, + this, + true + ); + + } + + } + else { + + this._removeEventHandlers(); + + } + +} + +}); // END YAHOO.extend + +/** +* Creates an item for a context menu. +* +* @param {String} p_oObject String specifying the text of the context menu item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the context menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the context +* menu item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the context menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu item. See configuration class +* documentation for more details. +* @class ContextMenuItem +* @constructor +* @extends YAHOO.widget.MenuItem +*/ +YAHOO.widget.ContextMenuItem = function(p_oObject, p_oConfig) { + + YAHOO.widget.ContextMenuItem.superclass.constructor.call( + this, + p_oObject, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.ContextMenuItem, YAHOO.widget.MenuItem, { + +/** +* @method init +* @description The ContextMenuItem class's initialization method. 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. +* @param {String} p_oObject String specifying the text of the context menu item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the context menu item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the context +* menu item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the context menu item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the context menu item. See configuration class +* documentation for more details. +*/ +init: function(p_oObject, p_oConfig) { + + if(!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = YAHOO.widget.ContextMenu; + + } + + /* + Call the init of the superclass (YAHOO.widget.MenuItem) + Note: We don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.ContextMenuItem.superclass.init.call(this, p_oObject); + + var oConfig = this.cfg; + + if(p_oConfig) { + + oConfig.applyConfig(p_oConfig, true); + + } + + oConfig.fireQueue(); + +}, + +// Public methods + +/** +* @method toString +* @description Returns a string representing the context menu item. +* @return {String} +*/ +toString: function() { + + return ("MenuBarItem: " + this.cfg.getProperty("text")); + +} + +}); // END YAHOO.extend + +/** +* Horizontal collection of items, each of which can contain a submenu. +* +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu bar. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* menu bar. +* @param {HTMLDivElement} p_oElement Object specifying +* the<div>
element of the menu bar. +* @param {HTMLSelectElement} p_oElement Object +* specifying the<select>
element to be used as the data +* source for the menu bar. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar. See configuration class documentation for +* more details. +* @class Menubar +* @constructor +* @extends YAHOO.widget.Menu +* @namespace YAHOO.widget +*/ +YAHOO.widget.MenuBar = function(p_oElement, p_oConfig) { + + YAHOO.widget.MenuBar.superclass.constructor.call( + this, + p_oElement, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.MenuBar, YAHOO.widget.Menu, { + +/** +* @method init +* @description The MenuBar class's initialization method. 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. +* @param {String} p_oElement String specifying the id attribute of the +*<div>
element of the menu bar. +* @param {String} p_oElement String specifying the id attribute of the +*<select>
element to be used as the data source for the +* menu bar. +* @param {HTMLDivElement} p_oElement Object specifying +* the<div>
element of the menu bar. +* @param {HTMLSelectElement} p_oElement Object +* specifying the<select>
element to be used as the data +* source for the menu bar. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar. See configuration class documentation for +* more details. +*/ +init: function(p_oElement, p_oConfig) { + + if(!this.ITEM_TYPE) { + + this.ITEM_TYPE = YAHOO.widget.MenuBarItem; + + } + + // Call the init of the superclass (YAHOO.widget.Menu) + + YAHOO.widget.MenuBar.superclass.init.call(this, p_oElement); + + this.beforeInitEvent.fire(YAHOO.widget.MenuBar); + + if(p_oConfig) { + + this.cfg.applyConfig(p_oConfig, true); + + } + + this.initEvent.fire(YAHOO.widget.MenuBar); + +}, + +// Constants + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the menu +* bar's<div>
element. +* @default "yuimenubar" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenubar", + +// Protected event handlers + +/** +* @method _onKeyDown +* @description "keydown" Custom Event handler for the menu bar. +* @private +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar +* that fired the event. +*/ +_onKeyDown: function(p_sType, p_aArgs, p_oMenuBar) { + + var Event = YAHOO.util.Event; + var oEvent = p_aArgs[0]; + var oItem = p_aArgs[1]; + var oItemCfg = oItem.cfg; + var oSubmenu; + + switch(oEvent.keyCode) { + + case 27: // Esc key + + if(this.cfg.getProperty("position") == "dynamic") { + + this.hide(); + + if(this.parent) { + + this.parent.focus(); + + } + + } + else if(this.activeItem) { + + oSubmenu = this.activeItem.cfg.getProperty("submenu"); + + if(oSubmenu && oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + this.activeItem.focus(); + + } + else { + + this.activeItem.cfg.setProperty("selected", false); + this.activeItem.blur(); + + } + + } + + + Event.preventDefault(oEvent); + + break; + + case 37: // Left arrow + case 39: // Right arrow + + if( + oItem == this.activeItem && + !oItemCfg.getProperty("selected") + ) { + + oItemCfg.setProperty("selected", true); + + } + else { + + var oNextItem = (oEvent.keyCode == 37) ? + oItem.getPreviousEnabledSibling() : + oItem.getNextEnabledSibling(); + + if(oNextItem) { + + this.clearActiveItem(); + + oNextItem.cfg.setProperty("selected", true); + + if(this.cfg.getProperty("autosubmenudisplay")) { + + oSubmenu = oNextItem.cfg.getProperty("submenu"); + + if(oSubmenu) { + + oSubmenu.show(); + oSubmenu.activeItem.blur(); + oSubmenu.activeItem = null; + + } + + } + + oNextItem.focus(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + case 40: // Down arrow + + if(this.activeItem != oItem) { + + this.clearActiveItem(); + + oItemCfg.setProperty("selected", true); + oItem.focus(); + + } + + oSubmenu = oItemCfg.getProperty("submenu"); + + if(oSubmenu) { + + if(oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.setInitialSelection(); + oSubmenu.setInitialFocus(); + + } + else { + + oSubmenu.show(); + + } + + } + + Event.preventDefault(oEvent); + + break; + + } + +}, + +/** +* @method _onClick +* @description "click" event handler for the menu bar. +* @protected +* @param {String} p_sType String representing the name of the event that +* was fired. +* @param {Array} p_aArgs Array of arguments sent when the event was fired. +* @param {YAHOO.widget.MenuBar} p_oMenuBar Object representing the menu bar +* that fired the event. +*/ +_onClick: function(p_sType, p_aArgs, p_oMenuBar) { + + YAHOO.widget.MenuBar.superclass._onClick.call( + this, + p_sType, + p_aArgs, + p_oMenuBar + ); + + var oItem = p_aArgs[1]; + + if(oItem) { + + var Event = YAHOO.util.Event; + var Dom = YAHOO.util.Dom; + + var oEvent = p_aArgs[0]; + var oTarget = Event.getTarget(oEvent); + + var oActiveItem = this.activeItem; + var oConfig = this.cfg; + + // Hide any other submenus that might be visible + + if(oActiveItem && oActiveItem != oItem) { + + this.clearActiveItem(); + + } + + + // Select and focus the current item + + oItem.cfg.setProperty("selected", true); + oItem.focus(); + + + // Show the submenu for the item + + var oSubmenu = oItem.cfg.getProperty("submenu"); + + if(oSubmenu && oTarget != oItem.submenuIndicator) { + + if(oSubmenu.cfg.getProperty("visible")) { + + oSubmenu.hide(); + + } + else { + + oSubmenu.show(); + + } + + } + + } + +}, + +// Public methods + +/** +* @method toString +* @description Returns a string representing the menu bar. +* @return {String} +*/ +toString: function() { + + return ("MenuBar " + this.id); + +}, + +/** +* @description Initializes the class's configurable properties which can be +* changed using the menu bar's Config object ("cfg"). +* @method initDefaultConfig +*/ +initDefaultConfig: function() { + + YAHOO.widget.MenuBar.superclass.initDefaultConfig.call(this); + + var oConfig = this.cfg; + + // Add configuration properties + + /* + Set the default value for the "position" configuration property + to "static" by re-adding the property. + */ + + /** + * @config position + * @description String indicating how a menu bar should be positioned on the + * screen. Possible values are "static" and "dynamic." Static menu bars + * are visible by default and reside in the normal flow of the document + * (CSS position: static). Dynamic menu bars are hidden by default, reside + * out of the normal flow of the document (CSS position: absolute), and can + * overlay other elements on the screen. + * @default static + * @type String + */ + oConfig.addProperty( + "position", + { + value: "static", + handler: this.configPosition, + validator: this._checkPosition, + supercedes: ["visible"] + } + ); + + /* + Set the default value for the "submenualignment" configuration property + to ["tl","bl"] by re-adding the property. + */ + + /** + * @config submenualignment + * @description Array defining how submenus should be aligned to their + * parent menu bar item. The format is: [itemCorner, submenuCorner]. + * @default ["tl","bl"] + * @type Array + */ + oConfig.addProperty("submenualignment", { value: ["tl","bl"] } ); + + /* + Change the default value for the "autosubmenudisplay" configuration + property to "false" by re-adding the property. + */ + + /** + * @config autosubmenudisplay + * @description Boolean indicating if submenus are automatically made + * visible when the user mouses over the menu bar's items. + * @default false + * @type Boolean + */ + oConfig.addProperty( + "autosubmenudisplay", + { value: false, validator: oConfig.checkBoolean } + ); + +} + +}); // END YAHOO.extend + +/** +* Creates an item for a menu bar. +* +* @param {String} p_oObject String specifying the text of the menu bar item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the menu bar item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the menu bar item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the menu bar item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar item. See configuration class documentation +* for more details. +* @class MenuBarItem +* @constructor +* @extends YAHOO.widget.MenuItem +*/ +YAHOO.widget.MenuBarItem = function(p_oObject, p_oConfig) { + + YAHOO.widget.MenuBarItem.superclass.constructor.call( + this, + p_oObject, + p_oConfig + ); + +}; + +YAHOO.extend(YAHOO.widget.MenuBarItem, YAHOO.widget.MenuItem, { + +/** +* @method init +* @description The MenuBarItem class's initialization method. 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. +* @param {String} p_oObject String specifying the text of the menu bar item. +* @param {HTMLLIElement} p_oObject Object specifying the +*<li>
element of the menu bar item. +* @param {HTMLOptGroupElement} p_oObject Object +* specifying the<optgroup>
element of the menu bar item. +* @param {HTMLOptionElement} p_oObject Object specifying +* the<option>
element of the menu bar item. +* @param {Object} p_oConfig Optional. Object literal specifying the +* configuration for the menu bar item. See configuration class documentation +* for more details. +*/ +init: function(p_oObject, p_oConfig) { + + if(!this.SUBMENU_TYPE) { + + this.SUBMENU_TYPE = YAHOO.widget.Menu; + + } + + /* + Call the init of the superclass (YAHOO.widget.MenuItem) + Note: We don't pass the user config in here yet + because we only want it executed once, at the lowest + subclass level. + */ + + YAHOO.widget.MenuBarItem.superclass.init.call(this, p_oObject); + + var oConfig = this.cfg; + + if(p_oConfig) { + + oConfig.applyConfig(p_oConfig, true); + + } + + oConfig.fireQueue(); + +}, + +// Constants + +/** +* @property CSS_CLASS_NAME +* @description String representing the CSS class(es) to be applied to the +*<li>
element of the menu bar item. +* @default "yuimenubaritem" +* @final +* @type String +*/ +CSS_CLASS_NAME: "yuimenubaritem", + +/** +* @property SUBMENU_INDICATOR_IMAGE_PATH +* @description String representing the path to the image to be used for the +* menu bar item's submenu arrow indicator. +* @default "nt/ic/ut/alt1/menuarodwn8_nrm_1.gif" +* @final +* @type String +*/ +SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_nrm_1.gif", + +/** +* @property SELECTED_SUBMENU_INDICATOR_IMAGE_PATH +* @description String representing the path to the image to be used for the +* submenu arrow indicator when the menu bar item is selected. +* @default "nt/ic/ut/alt1/menuarodwn8_hov_1.gif" +* @final +* @type String +*/ +SELECTED_SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_hov_1.gif", + +/** +* @property DISABLED_SUBMENU_INDICATOR_IMAGE_PATH +* @description String representing the path to the image to be used for the +* submenu arrow indicator when the menu bar item is disabled. +* @default "nt/ic/ut/alt1/menuarodwn8_dim_1.gif" +* @final +* @type String +*/ +DISABLED_SUBMENU_INDICATOR_IMAGE_PATH: "nt/ic/ut/alt1/menuarodwn8_dim_1.gif", + +// Public methods + +/** +* @method toString +* @description Returns a string representing the menu bar item. +* @return {String} +*/ +toString: function() { + + return ("MenuBarItem: " + this.cfg.getProperty("text")); + +} + +}); // END YAHOO.extend \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/reset-fonts-grids/README b/source/web/scripts/ajax/yahoo/reset-fonts-grids/README new file mode 100644 index 0000000000..d8e7eeb879 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/reset-fonts-grids/README @@ -0,0 +1,8 @@ +YUI Library - Reset+Fonts+Grids (RFG) - Release Notes + +Version 0.12.0 + + * Initial release. + + * This file is a convenience file containing an in-order + concatenation of Reset, Fonts, and Grids. \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/reset-fonts-grids/reset-fonts-grids-min.css b/source/web/scripts/ajax/yahoo/reset-fonts-grids/reset-fonts-grids-min.css new file mode 100644 index 0000000000..bf76e56e84 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/reset-fonts-grids/reset-fonts-grids-min.css @@ -0,0 +1,4 @@ +/*Copyright (c) 2006,Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ +/*reset.css*/body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul {list-style:none;}caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;} +/*fonts.css*/body{font:13px arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}pre, code {font:115% monospace;*font-size:100%;}body * {line-height:1.22em;} +/*grids.css*/body{text-align:center;}#ft{clear:both;}#doc,#doc2,#doc3,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7{margin:auto;text-align:left;width:57.69em;*width:56.3em;min-width:750px;}#doc2{width:73.074em;*width:71.313em;min-width:950px;}#doc3{margin:auto 10px;width:auto;}.yui-b{position:relative;}.yui-b{_position:static;}#yui-main .yui-b{position:static;}#yui-main{width:100%;}.yui-t1 #yui-main,.yui-t2 #yui-main,.yui-t3 #yui-main{float:right;margin-left:-25em;}.yui-t4 #yui-main,.yui-t5 #yui-main,.yui-t6 #yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.3207em;*width:12.0106em;}.yui-t1 #yui-main .yui-b{margin-left:13.3207em;*margin-left:13.0106em;}.yui-t2 .yui-b{float:left;width:13.8456em;*width:13.512em;}.yui-t2 #yui-main .yui-b{margin-left:14.8456em;*margin-left:14.512em;}.yui-t3 .yui-b{float:left;width:23.0759em;*width:22.52em;}.yui-t3 #yui-main .yui-b{margin-left:24.0759em;*margin-left:23.52em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.512em;}.yui-t4 #yui-main .yui-b{margin-right:14.8456em;*margin-right:14.512em;}.yui-t5 .yui-b{float:right;width:18.4608em;*width:18.016em;}.yui-t5 #yui-main .yui-b{margin-right:19.4608em;*margin-right:19.016em;}.yui-t6 .yui-b{float:right;width:23.0759em;*width:22.52em;}.yui-t6 #yui-main .yui-b{margin-right:24.0759em;*margin-right:23.52em;}.yui-t7 #yui-main .yui-b{display:block;margin:0 0 1em 0;}#yui-main .yui-b{float:none;width:auto;}.yui-g .yui-u,.yui-g .yui-g,.yui-gc .yui-u,.yui-gc .yui-g .yui-u,.yui-ge .yui-u,.yui-gf .yui-u{float:right;display:inline;}.yui-g div.first,.yui-gc div.first,.yui-gc div.first div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g{width:49.1%;}.yui-g .yui-g .yui-u,.yui-gc .yui-g .yui-u{width:48.1%;}.yui-gb .yui-u,.yui-gc .yui-u,.yui-gd .yui-u{float:left;margin-left:2%;*margin-left:1.895%;width:32%;}.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-gc div.first,.yui-gd .yui-u{width:66%;}.yui-gd div.first{width:32%;}.yui-ge .yui-u{width:24%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;}.yui-gf div.first{width:24%;}.yui-ge div.first{width:74.2%;}#bd:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#bd,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf{zoom:1;} \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/reset-fonts-grids/reset-fonts-grids.css b/source/web/scripts/ajax/yahoo/reset-fonts-grids/reset-fonts-grids.css new file mode 100644 index 0000000000..507ed66483 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/reset-fonts-grids/reset-fonts-grids.css @@ -0,0 +1,174 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ + +/*=============*/ +/* reset.css */ +/*=============*/ + +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;} +table{border-collapse:collapse;border-spacing:0;} +fieldset,img{border:0;} +address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;} +ol,ul {list-style:none;} +caption,th {text-align:left;} +h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;} +q:before,q:after{content:'';} +abbr,acronym {border:0;} + +/*=============*/ +/* fonts.css */ +/*=============*/ + +/** + * 84.5% for !IE, keywords for IE to preserve user font-size adjustment + * Percents could work for IE, but for backCompat purposes, we are using keywords. + * x-small is for IE6/7 quirks mode. + * + */ +body {font:13px arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;} +table {font-size:inherit;font:100%;} +/** + * 99% for safari; 100% is too large + */ +select, input, textarea {font:99% arial,helvetica,clean,sans-serif;} +/** + * Bump up !IE to get to 13px equivalent + */ +pre, code {font:115% monospace;*font-size:100%;} +/** + * Default line-height based on font-size rather than "computed-value" + * see: http://www.w3.org/TR/CSS21/visudet.html#line-height + */ +body * {line-height:1.22em;} + +/*=============*/ +/* grids.css */ +/*=============*/ + +/* for all templates and grids */ +body{text-align:center;} +#ft{clear:both;} +/**/ +/* 750 centered, and backward compatibility */ +#doc,#doc2,#doc3,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6,.yui-t7 { + margin:auto;text-align:left; + width:57.69em;*width:56.3em;min-width:750px;} +/* 950 centered */ +#doc2 { + width:73.074em;*width:71.313em;min-width:950px;} +/* 100% with 10px viewport side matting */ +#doc3 { + margin:auto 10px; /* not for structure, but so content doesn't bleed to edge */ + width:auto;} + +/* below required for all fluid grids; adjust widths and margins above accordingly */ + + /* to preserve source-order independence for Gecko */ + .yui-b{position:relative;} + .yui-b{_position:static;} /* for IE < 7 */ + #yui-main .yui-b{position:static;} + +#yui-main {width:100%;} +.yui-t1 #yui-main, +.yui-t2 #yui-main, +.yui-t3 #yui-main{float:right;margin-left:-25em;/* IE: preserve layout at narrow widths */} + +.yui-t4 #yui-main, +.yui-t5 #yui-main, +.yui-t6 #yui-main{float:left;margin-right:-25em;/* IE: preserve layout at narrow widths */} + +.yui-t1 .yui-b { + float:left; + width:12.3207em;*width:12.0106em;} +.yui-t1 #yui-main .yui-b{ + margin-left:13.3207em;*margin-left:13.0106em; +} + +.yui-t2 .yui-b { + float:left; + width:13.8456em;*width:13.512em;} +.yui-t2 #yui-main .yui-b { + margin-left:14.8456em;*margin-left:14.512em; +} + +.yui-t3 .yui-b { + float:left; + width:23.0759em;*width:22.52em;} +.yui-t3 #yui-main .yui-b { + margin-left:24.0759em;*margin-left:23.52em; +} + +.yui-t4 .yui-b { + float:right; + width:13.8456em;*width:13.512em;} +.yui-t4 #yui-main .yui-b { + margin-right:14.8456em;*margin-right:14.512em; +} + +.yui-t5 .yui-b { + float:right; + width:18.4608em;*width:18.016em;} +.yui-t5 #yui-main .yui-b { + margin-right:19.4608em;*margin-right:19.016em; +} + +.yui-t6 .yui-b { + float:right; + width:23.0759em;*width:22.52em;} +.yui-t6 #yui-main .yui-b { + margin-right:24.0759em;*margin-right:23.52em; +} + +.yui-t7 #yui-main .yui-b { + display:block;margin:0 0 1em 0; +} +#yui-main .yui-b {float:none;width:auto;} +/* GRIDS (not TEMPLATES) */ +.yui-g .yui-u, +.yui-g .yui-g, +.yui-gc .yui-u, +.yui-gc .yui-g .yui-u, +.yui-ge .yui-u, +.yui-gf .yui-u{float:right;display:inline;} +.yui-g div.first, +.yui-gc div.first, +.yui-gc div.first div.first, +.yui-gd div.first, +.yui-ge div.first, +.yui-gf div.first{float:left;} +.yui-g .yui-u, +.yui-g .yui-g{width:49.1%;} +.yui-g .yui-g .yui-u, +.yui-gc .yui-g .yui-u {width:48.1%;} +.yui-gb .yui-u, +.yui-gc .yui-u, +.yui-gd .yui-u{float:left;margin-left:2%;*margin-left:1.895%;width:32%;} +.yui-gb div.first, +.yui-gc div.first, +.yui-gd div.first{margin-left:0;} +.yui-gc div.first, +.yui-gd .yui-u{width:66%;} +.yui-gd div.first{width:32%;} +.yui-ge .yui-u{width:24%;} +.yui-ge div.first, +.yui-gf .yui-u{width:74.2%;} +.yui-gf div.first{width:24%;} +.yui-ge div.first{width:74.2%;} +#bd:after, +.yui-g:after, +.yui-gb:after, +.yui-gc:after, +.yui-gd:after, +.yui-ge:after, +.yui-gf:after{content:".";display:block;height:0;clear:both;visibility:hidden;} +#bd, +.yui-g, +.yui-gb, +.yui-gc, +.yui-gd, +.yui-ge, +.yui-gf{zoom:1;} \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/reset/README b/source/web/scripts/ajax/yahoo/reset/README new file mode 100644 index 0000000000..990e8ccffa --- /dev/null +++ b/source/web/scripts/ajax/yahoo/reset/README @@ -0,0 +1,17 @@ +YUI Library - Reset - Release Notes + +Version 0.12.0 + + * Added: h1,h2,h3,h4,h5,h6{font-weight:normal;} + + * Added: abbr,acronym {border:0;} + + * Added: textarea {padding:0;margin:0;} + +Version 0.11.0 + + * No changes. + +Version 0.10.0 + + * Initial release. \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/reset/reset-min.css b/source/web/scripts/ajax/yahoo/reset/reset-min.css new file mode 100644 index 0000000000..d7e1533355 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/reset/reset-min.css @@ -0,0 +1 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}ol,ul {list-style:none;}caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;} \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/reset/reset.css b/source/web/scripts/ajax/yahoo/reset/reset.css new file mode 100644 index 0000000000..c488811147 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/reset/reset.css @@ -0,0 +1,15 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;} +table{border-collapse:collapse;border-spacing:0;} +fieldset,img{border:0;} +address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;} +ol,ul {list-style:none;} +caption,th {text-align:left;} +h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;} +q:before,q:after{content:'';} +abbr,acronym {border:0;} \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/slider/README b/source/web/scripts/ajax/yahoo/slider/README new file mode 100644 index 0000000000..dc317b4690 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/slider/README @@ -0,0 +1,54 @@ +Slider - Release Notes + +0.12.0 + + * Added "slideStart", "slideEnd", and "change" custom events. The abstract + methods these will eventually replace still work. + + * The default animation duration is 0.2 seconds (reduced from 0.4 seconds), + and is configurable via the animationDuration property. + + * Keyboard navigation is now built in. The background needs a tabindex for + keyboard nav to work. Keyboard nav can be disabled by setting enableKeys + to false. The number of pixels the slider moves when the arrow keys + are pressed is controlled by keyIncrement, and defaults to 20. Note, + Safari support limited to background element types that support focus + in that browser. http://bugs.webkit.org/show_bug.cgi?id=7138 + + * Fixed broken doctype in examples/index.html + + * Catching an unhandled script exception in FF that could occur when + attempting to focus the slider background while a text field without + autocomplete="false" has focus + +0.11.3 + + * No change + +0.11.0 + + * When the thumb is clicked and dragged, the click position delta is properly + applied. + + * The slider background can be disabled by setting backgroundEnabled to false. + + * Added SliderThumb->clearTicks + + * Incorporated updated drag and drop performance improvements + +0.10.0 + + * Drag and drop's autoscroll feature is now turned off by default + in the slider. + + * The slider no longer sets its initial value upon initialization + + * RGB slider example fixed for IE7. + + * Updated to work with the onAvailable feature in Drag and Drop. + + * Updated the basic slider example page to make the control more + accessible to non-FF1.5 browsers. + + * Split the examples into separate pages + diff --git a/source/web/scripts/ajax/yahoo/slider/slider-debug.js b/source/web/scripts/ajax/yahoo/slider/slider-debug.js new file mode 100644 index 0000000000..af921eed59 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/slider/slider-debug.js @@ -0,0 +1,1152 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ + +/** + * The Slider component is a UI control that enables the user to adjust + * values in a finite range along one or two axes. Typically, the Slider + * control is used in a web application as a rich, visual replacement + * for an input box that takes a number as input. The Slider control can + * also easily accommodate a second dimension, providing x,y output for + * a selection point chosen from a rectangular region. + * + * @module slider + * @title Slider Widget + * @namespace YAHOO.widget + * @requires yahoo,dom,dragdrop,event + * @optional animation + */ + +/** + * A DragDrop implementation that can be used as a background for a + * slider. It takes a reference to the thumb instance + * so it can delegate some of the events to it. The goal is to make the + * thumb jump to the location on the background when the background is + * clicked. + * + * @class Slider + * @extends YAHOO.util.DragDrop + * @constructor + * @param {String} id The id of the element linked to this instance + * @param {String} sGroup The group of related DragDrop items + * @param {String} sType The type of slider (horiz, vert, region) + */ +YAHOO.widget.Slider = function(sElementId, sGroup, oThumb, sType) { + if (sElementId) { + + /** + * The type of the slider (horiz, vert, region) + * @property type + * @type string + */ + this.type = sType; + + this.init(sElementId, sGroup, true); + + //this.removeInvalidHandleType("A"); + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + + var self = this; + + /** + * Event the fires when the value of the control changes. If + * the control is animated the event will fire every point + * along the way. + * @event change + * @param {int} new + * @param {int} firstOffset the number of pixels the thumb has moved + * from its start position. Normal horizontal and vertical sliders will only + * have the firstOffset. Regions will have both, the first is the horizontal + * offset, the second the vertical. + * @param {int} secondOffset the y offset for region sliders + */ + this.createEvent("change", this); + + /** + * Event that fires at the end of a slider thumb move. + * @event slideStart + */ + this.createEvent("slideStart", this); + + /** + * Event that fires at the end of a slider thumb move + * @event slideEnd + */ + this.createEvent("slideEnd", this); + + /** + * A YAHOO.widget.SliderThumb instance that we will use to + * reposition the thumb when the background is clicked + * @property thumb + * @type YAHOO.widget.SliderThumb + */ + this.thumb = oThumb; + + // add handler for the handle onchange event + oThumb.onChange = function() { + self.handleThumbChange(); + }; + + /** + * Overrides the isTarget property in YAHOO.util.DragDrop + * @property isTarget + * @private + */ + this.isTarget = false; + + /** + * Flag that determines if the thumb will animate when moved + * @property animate + * @type boolean + */ + this.animate = YAHOO.widget.Slider.ANIM_AVAIL; + + /** + * Set to false to disable a background click thumb move + * @property backgroundEnabled + * @type boolean + */ + this.backgroundEnabled = true; + + /** + * Adjustment factor for tick animation, the more ticks, the + * faster the animation (by default) + * @property tickPause + * @type int + */ + this.tickPause = 40; + + /** + * Enables the arrow, home and end keys, defaults to true. + * @property enableKeys + * @type boolean + */ + this.enableKeys = true; + + /** + * Specifies the number of pixels the arrow keys will move the slider. + * Default is 25. + * @property keyIncrement + * @type int + */ + this.keyIncrement = 20; + + /** + * moveComplete is set to true when the slider has moved to its final + * destination. For animated slider, this value can be checked in + * the onChange handler to make it possible to execute logic only + * when the move is complete rather than at all points along the way. + * + * @property moveComplete + * @type Boolean + */ + this.moveComplete = true; + + /** + * If animation is configured, specifies the length of the animation + * in seconds. + * @property animationDuration + * @type int + * @default 0.2 + */ + this.animationDuration = 0.2; + + if (oThumb._isHoriz && oThumb.xTicks && oThumb.xTicks.length) { + this.tickPause = Math.round(360 / oThumb.xTicks.length); + } else if (oThumb.yTicks && oThumb.yTicks.length) { + this.tickPause = Math.round(360 / oThumb.yTicks.length); + } + + this.logger.log("tickPause: " + this.tickPause); + + // delegate thumb methods + oThumb.onMouseDown = function () { return self.focus(); }; + //oThumb.b4MouseDown = function () { return self.b4MouseDown(); }; + // oThumb.lock = function() { self.lock(); }; + // oThumb.unlock = function() { self.unlock(); }; + oThumb.onMouseUp = function() { self.thumbMouseUp(); }; + oThumb.onDrag = function() { self.fireEvents(); }; + oThumb.onAvailable = function() { return self.setStartSliderState(); }; + } +}; + +/** + * Factory method for creating a horizontal slider + * @method YAHOO.widget.Slider.getHorizSlider + * @static + * @param {String} sBGElId the id of the slider's background element + * @param {String} sHandleElId the id of the thumb element + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + * @return {Slider} a horizontal slider control + */ +YAHOO.widget.Slider.getHorizSlider = + function (sBGElId, sHandleElId, iLeft, iRight, iTickSize) { + return new YAHOO.widget.Slider(sBGElId, sBGElId, + new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, + iLeft, iRight, 0, 0, iTickSize), "horiz"); +}; + +/** + * Factory method for creating a vertical slider + * @method YAHOO.widget.Slider.getVertSlider + * @static + * @param {String} sBGElId the id of the slider's background element + * @param {String} sHandleElId the id of the thumb element + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + * @return {Slider} a vertical slider control + */ +YAHOO.widget.Slider.getVertSlider = + function (sBGElId, sHandleElId, iUp, iDown, iTickSize) { + return new YAHOO.widget.Slider(sBGElId, sBGElId, + new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, 0, 0, + iUp, iDown, iTickSize), "vert"); +}; + +/** + * Factory method for creating a slider region like the one in the color + * picker example + * @method YAHOO.widget.Slider.getSliderRegion + * @static + * @param {String} sBGElId the id of the slider's background element + * @param {String} sHandleElId the id of the thumb element + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + * @return {Slider} a slider region control + */ +YAHOO.widget.Slider.getSliderRegion = + function (sBGElId, sHandleElId, iLeft, iRight, iUp, iDown, iTickSize) { + return new YAHOO.widget.Slider(sBGElId, sBGElId, + new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, iLeft, iRight, + iUp, iDown, iTickSize), "region"); +}; + +/** + * By default, animation is available if the animation library is detected. + * @property YAHOO.widget.Slider.ANIM_AVAIL + * @static + * @type boolean + */ +YAHOO.widget.Slider.ANIM_AVAIL = true; + +YAHOO.extend(YAHOO.widget.Slider, YAHOO.util.DragDrop, { + + onAvailable: function() { + var Event = YAHOO.util.Event; + Event.on(this.id, "keydown", this.handleKeyDown, this, true); + Event.on(this.id, "keypress", this.handleKeyPress, this, true); + }, + + handleKeyPress: function(e) { + if (this.enableKeys) { + var Event = YAHOO.util.Event; + var kc = Event.getCharCode(e); + switch (kc) { + case 0x25: // left + case 0x26: // up + case 0x27: // right + case 0x28: // down + case 0x24: // home + case 0x23: // end + Event.preventDefault(e); + break; + default: + } + } + }, + + handleKeyDown: function(e) { + if (this.enableKeys) { + var Event = YAHOO.util.Event; + + var kc = Event.getCharCode(e), t=this.thumb; + var h=this.getXValue(),v=this.getYValue(); + + var horiz = false; + var changeValue = true; + switch (kc) { + + // left + case 0x25: h -= this.keyIncrement; break; + + // up + case 0x26: v -= this.keyIncrement; break; + + // right + case 0x27: h += this.keyIncrement; break; + + // down + case 0x28: v += this.keyIncrement; break; + + // home + case 0x24: h = t.leftConstraint; + v = t.topConstraint; + break; + + // end + case 0x23: h = t.rightConstraint; + v = t.bottomConstraint; + break; + + default: changeValue = false; + } + + if (changeValue) { + if (t._isRegion) { + this.setRegionValue(h, v, true); + } else { + var newVal = (t._isHoriz) ? h : v; + this.setValue(newVal, true); + } + Event.stopEvent(e); + } + + } + }, + + /** + * Initialization that sets up the value offsets once the elements are ready + * @method setSliderStartState + */ + setStartSliderState: function() { + + this.logger.log("Fixing state"); + + this.setThumbCenterPoint(); + + /** + * The basline position of the background element, used + * to determine if the background has moved since the last + * operation. + * @property baselinePos + * @type [int, int] + */ + this.baselinePos = YAHOO.util.Dom.getXY(this.getEl()); + + this.thumb.startOffset = this.thumb.getOffsetFromParent(this.baselinePos); + + if (this.thumb._isRegion) { + if (this.deferredSetRegionValue) { + this.setRegionValue.apply(this, this.deferredSetRegionValue, true); + this.deferredSetRegionValue = null; + } else { + this.setRegionValue(0, 0, true); + } + } else { + if (this.deferredSetValue) { + this.setValue.apply(this, this.deferredSetValue, true); + this.deferredSetValue = null; + } else { + this.setValue(0, true, true); + } + } + }, + + /** + * When the thumb is available, we cache the centerpoint of the element so + * we can position the element correctly when the background is clicked + * @method setThumbCenterPoint + */ + setThumbCenterPoint: function() { + + var el = this.thumb.getEl(); + + if (el) { + /** + * The center of the slider element is stored so we can position + * place it in the correct position when the background is clicked + * @property thumbCenterPoint + * @type {"x": int, "y": int} + */ + this.thumbCenterPoint = { + x: parseInt(el.offsetWidth/2, 10), + y: parseInt(el.offsetHeight/2, 10) + }; + } + + }, + + /** + * Locks the slider, overrides YAHOO.util.DragDrop + * @method lock + */ + lock: function() { + this.logger.log("locking"); + this.thumb.lock(); + this.locked = true; + }, + + /** + * Unlocks the slider, overrides YAHOO.util.DragDrop + * @method unlock + */ + unlock: function() { + this.logger.log("unlocking"); + this.thumb.unlock(); + this.locked = false; + }, + + /** + * Handles mouseup event on the slider background + * @method thumbMouseUp + * @private + */ + thumbMouseUp: function() { + this.logger.log("bg mouseup"); + if (!this.isLocked() && !this.moveComplete) { + this.endMove(); + } + + }, + + /** + * Returns a reference to this slider's thumb + * @method getThumb + * @return {SliderThumb} this slider's thumb + */ + getThumb: function() { + return this.thumb; + }, + + /** + * Try to focus the element when clicked so we can add + * accessibility features + * @method focus + * @private + */ + focus: function() { + this.logger.log("focus"); + + // Focus the background element if possible + var el = this.getEl(); + + if (el.focus) { + try { + el.focus(); + } catch(e) { + // Prevent permission denied unhandled exception in FF that can + // happen when setting focus while another element is handling + // the blur. @TODO this is still writing to the error log + // (unhandled error) in FF1.5 with strict error checking on. + } + } + + this.verifyOffset(); + + if (this.isLocked()) { + return false; + } else { + this.onSlideStart(); + return true; + } + }, + + /** + * Event that fires when the value of the slider has changed + * @method onChange + * @param {int} firstOffset the number of pixels the thumb has moved + * from its start position. Normal horizontal and vertical sliders will only + * have the firstOffset. Regions will have both, the first is the horizontal + * offset, the second the vertical. + * @param {int} secondOffset the y offset for region sliders + * @deprecated use instance.subscribe("change") instead + */ + onChange: function (firstOffset, secondOffset) { + /* override me */ + this.logger.log("onChange: " + firstOffset + ", " + secondOffset); + }, + + /** + * Event that fires when the at the beginning of the slider thumb move + * @method onSlideStart + * @deprecated use instance.subscribe("slideStart") instead + */ + onSlideStart: function () { + /* override me */ + this.logger.log("onSlideStart"); + }, + + /** + * Event that fires at the end of a slider thumb move + * @method onSliderEnd + * @deprecated use instance.subscribe("slideEnd") instead + */ + onSlideEnd: function () { + /* override me */ + this.logger.log("onSlideEnd"); + }, + + /** + * Returns the slider's thumb offset from the start position + * @method getValue + * @return {int} the current value + */ + getValue: function () { + return this.thumb.getValue(); + }, + + /** + * Returns the slider's thumb X offset from the start position + * @method getXValue + * @return {int} the current horizontal offset + */ + getXValue: function () { + return this.thumb.getXValue(); + }, + + /** + * Returns the slider's thumb Y offset from the start position + * @method getYValue + * @return {int} the current vertical offset + */ + getYValue: function () { + return this.thumb.getYValue(); + }, + + /** + * Internal handler for the slider thumb's onChange event + * @method handleThumbChange + * @private + */ + handleThumbChange: function () { + var t = this.thumb; + if (t._isRegion) { + t.onChange(t.getXValue(), t.getYValue()); + this.fireEvent("change", { x: t.getXValue(), y: t.getYValue() } ); + } else { + t.onChange(t.getValue()); + this.fireEvent("change", t.getValue()); + } + + }, + + /** + * Provides a way to set the value of the slider in code. + * @method setValue + * @param {int} newOffset the number of pixels the thumb should be + * positioned away from the initial start point + * @param {boolean} skipAnim set to true to disable the animation + * for this move action (but not others). + * @param {boolean} force ignore the locked setting and set value anyway + * @return {boolean} true if the move was performed, false if it failed + */ + setValue: function(newOffset, skipAnim, force) { + this.logger.log("setValue " + newOffset); + + if (!this.thumb.available) { + this.logger.log("defer setValue until after onAvailble"); + this.deferredSetValue = arguments; + return false; + } + + if (this.isLocked() && !force) { + this.logger.log("Can't set the value, the control is locked"); + return false; + } + + if ( isNaN(newOffset) ) { + this.logger.log("setValue, Illegal argument: " + newOffset); + return false; + } + + var t = this.thumb; + var newX, newY; + this.verifyOffset(); + if (t._isRegion) { + return false; + } else if (t._isHoriz) { + this.onSlideStart(); + // this.fireEvent("slideStart"); + newX = t.initPageX + newOffset + this.thumbCenterPoint.x; + this.moveThumb(newX, t.initPageY, skipAnim); + } else { + this.onSlideStart(); + // this.fireEvent("slideStart"); + newY = t.initPageY + newOffset + this.thumbCenterPoint.y; + this.moveThumb(t.initPageX, newY, skipAnim); + } + + return true; + }, + + /** + * Provides a way to set the value of the region slider in code. + * @method setRegionValue + * @param {int} newOffset the number of pixels the thumb should be + * positioned away from the initial start point (x axis for region) + * @param {int} newOffset2 the number of pixels the thumb should be + * positioned away from the initial start point (y axis for region) + * @param {boolean} skipAnim set to true to disable the animation + * for this move action (but not others). + * @param {boolean} force ignore the locked setting and set value anyway + * @return {boolean} true if the move was performed, false if it failed + */ + setRegionValue: function(newOffset, newOffset2, skipAnim) { + + if (!this.thumb.available) { + this.logger.log("defer setRegionValue until after onAvailble"); + this.deferredSetRegionValue = arguments; + return false; + } + + if (this.isLocked() && !force) { + this.logger.log("Can't set the value, the control is locked"); + return false; + } + + if ( isNaN(newOffset) ) { + this.logger.log("setRegionValue, Illegal argument: " + newOffset); + return false; + } + + var t = this.thumb; + if (t._isRegion) { + this.onSlideStart(); + var newX = t.initPageX + newOffset + this.thumbCenterPoint.x; + var newY = t.initPageY + newOffset2 + this.thumbCenterPoint.y; + this.moveThumb(newX, newY, skipAnim); + return true; + } + + return false; + + }, + + /** + * Checks the background position element position. If it has moved from the + * baseline position, the constraints for the thumb are reset + * @method verifyOffset + * @return {boolean} True if the offset is the same as the baseline. + */ + verifyOffset: function() { + + var newPos = YAHOO.util.Dom.getXY(this.getEl()); + this.logger.log("newPos: " + newPos); + + if (newPos[0] != this.baselinePos[0] || newPos[1] != this.baselinePos[1]) { + this.logger.log("background moved, resetting constraints"); + this.thumb.resetConstraints(); + this.baselinePos = newPos; + return false; + } + + return true; + }, + + /** + * Move the associated slider moved to a timeout to try to get around the + * mousedown stealing moz does when I move the slider element between the + * cursor and the background during the mouseup event + * @method moveThumb + * @param {int} x the X coordinate of the click + * @param {int} y the Y coordinate of the click + * @param {boolean} skipAnim don't animate if the move happend onDrag + * @private + */ + moveThumb: function(x, y, skipAnim) { + + // this.logger.log("move thumb", "warn"); + + var t = this.thumb; + var self = this; + + if (!t.available) { + this.logger.log("thumb is not available yet, aborting move"); + return; + } + + this.logger.log("move thumb, x: " + x + ", y: " + y); + + // this.verifyOffset(); + + t.setDelta(this.thumbCenterPoint.x, this.thumbCenterPoint.y); + + var _p = t.getTargetCoord(x, y); + var p = [_p.x, _p.y]; + + + this.fireEvent("slideStart"); + + if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && t._graduated && !skipAnim) { + this.logger.log("graduated"); + // this.thumb._animating = true; + this.lock(); + + setTimeout( function() { self.moveOneTick(p); }, this.tickPause ); + + } else if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && !skipAnim) { + this.logger.log("animating to " + p); + + // this.thumb._animating = true; + this.lock(); + + var oAnim = new YAHOO.util.Motion( + t.id, { points: { to: p } }, + this.animationDuration, + YAHOO.util.Easing.easeOut ); + + oAnim.onComplete.subscribe( function() { self.endMove(); } ); + oAnim.animate(); + } else { + t.setDragElPos(x, y); + // this.fireEvents(); + this.endMove(); + } + }, + + /** + * Move the slider one tick mark towards its final coordinate. Used + * for the animation when tick marks are defined + * @method moveOneTick + * @param {int[]} the destination coordinate + * @private + */ + moveOneTick: function(finalCoord) { + + var t = this.thumb; + var curCoord = YAHOO.util.Dom.getXY(t.getEl()); + var tmp; + + // var thresh = Math.min(t.tickSize + (Math.floor(t.tickSize/2)), 10); + // var thresh = 10; + // var thresh = t.tickSize + (Math.floor(t.tickSize/2)); + + var nextCoord = null; + + if (t._isRegion) { + nextCoord = this._getNextX(curCoord, finalCoord); + var tmpX = (nextCoord) ? nextCoord[0] : curCoord[0]; + nextCoord = this._getNextY([tmpX, curCoord[1]], finalCoord); + + } else if (t._isHoriz) { + nextCoord = this._getNextX(curCoord, finalCoord); + } else { + nextCoord = this._getNextY(curCoord, finalCoord); + } + + this.logger.log("moveOneTick: " + + " finalCoord: " + finalCoord + + " curCoord: " + curCoord + + " nextCoord: " + nextCoord); + + if (nextCoord) { + + // move to the next coord + // YAHOO.util.Dom.setXY(t.getEl(), nextCoord); + + // var el = t.getEl(); + // YAHOO.util.Dom.setStyle(el, "left", (nextCoord[0] + this.thumb.deltaSetXY[0]) + "px"); + // YAHOO.util.Dom.setStyle(el, "top", (nextCoord[1] + this.thumb.deltaSetXY[1]) + "px"); + + this.thumb.alignElWithMouse(t.getEl(), nextCoord[0], nextCoord[1]); + + // check if we are in the final position, if not make a recursive call + if (!(nextCoord[0] == finalCoord[0] && nextCoord[1] == finalCoord[1])) { + var self = this; + setTimeout(function() { self.moveOneTick(finalCoord); }, + this.tickPause); + } else { + this.endMove(); + } + } else { + this.endMove(); + } + + //this.tickPause = Math.round(this.tickPause/2); + }, + + /** + * Returns the next X tick value based on the current coord and the target coord. + * @method _getNextX + * @private + */ + _getNextX: function(curCoord, finalCoord) { + this.logger.log("getNextX: " + curCoord + ", " + finalCoord); + var t = this.thumb; + var thresh; + var tmp = []; + var nextCoord = null; + if (curCoord[0] > finalCoord[0]) { + thresh = t.tickSize - this.thumbCenterPoint.x; + tmp = t.getTargetCoord( curCoord[0] - thresh, curCoord[1] ); + nextCoord = [tmp.x, tmp.y]; + } else if (curCoord[0] < finalCoord[0]) { + thresh = t.tickSize + this.thumbCenterPoint.x; + tmp = t.getTargetCoord( curCoord[0] + thresh, curCoord[1] ); + nextCoord = [tmp.x, tmp.y]; + } else { + // equal, do nothing + } + + return nextCoord; + }, + + /** + * Returns the next Y tick value based on the current coord and the target coord. + * @method _getNextY + * @private + */ + _getNextY: function(curCoord, finalCoord) { + var t = this.thumb; + var thresh; + var tmp = []; + var nextCoord = null; + + if (curCoord[1] > finalCoord[1]) { + thresh = t.tickSize - this.thumbCenterPoint.y; + tmp = t.getTargetCoord( curCoord[0], curCoord[1] - thresh ); + nextCoord = [tmp.x, tmp.y]; + } else if (curCoord[1] < finalCoord[1]) { + thresh = t.tickSize + this.thumbCenterPoint.y; + tmp = t.getTargetCoord( curCoord[0], curCoord[1] + thresh ); + nextCoord = [tmp.x, tmp.y]; + } else { + // equal, do nothing + } + + return nextCoord; + }, + + /** + * Resets the constraints before moving the thumb. + * @method b4MouseDown + * @private + */ + b4MouseDown: function(e) { + this.thumb.autoOffset(); + this.thumb.resetConstraints(); + }, + + + /** + * Handles the mousedown event for the slider background + * @method onMouseDown + * @private + */ + onMouseDown: function(e) { + // this.resetConstraints(true); + // this.thumb.resetConstraints(true); + + if (! this.isLocked() && this.backgroundEnabled) { + var x = YAHOO.util.Event.getPageX(e); + var y = YAHOO.util.Event.getPageY(e); + this.logger.log("bg mousedown: " + x + "," + y); + + this.focus(); + this.moveThumb(x, y); + } + + }, + + /** + * Handles the onDrag event for the slider background + * @method onDrag + * @private + */ + onDrag: function(e) { + if (! this.isLocked()) { + var x = YAHOO.util.Event.getPageX(e); + var y = YAHOO.util.Event.getPageY(e); + this.moveThumb(x, y, true); + } + }, + + /** + * Fired when the slider movement ends + * @method endMove + * @private + */ + endMove: function () { + // this._animating = false; + this.unlock(); + this.moveComplete = true; + this.fireEvents(); + }, + + /** + * Fires the change event if the value has been changed. Ignored if we are in + * the middle of an animation as the event will fire when the animation is + * complete + * @method fireEvents + * @private + */ + fireEvents: function () { + + var t = this.thumb; + // this.logger.log("FireEvents: " + t._isRegion); + + t.cachePosition(); + + if (! this.isLocked()) { + if (t._isRegion) { + this.logger.log("region"); + var newX = t.getXValue(); + var newY = t.getYValue(); + + if (newX != this.previousX || newY != this.previousY) { + // this.logger.log("Firing onchange"); + this.onChange(newX, newY); + this.fireEvent("change", { x: newX, y: newY }); + } + + this.previousX = newX; + this.previousY = newY; + + } else { + var newVal = t.getValue(); + if (newVal != this.previousVal) { + this.logger.log("Firing onchange: " + newVal); + this.onChange( newVal ); + this.fireEvent("change", newVal); + } + this.previousVal = newVal; + } + + if (this.moveComplete) { + this.onSlideEnd(); + this.fireEvent("slideEnd"); + this.moveComplete = false; + } + + } + }, + + /** + * Slider toString + * @method toString + * @return {string} string representation of the instance + */ + toString: function () { + return ("Slider (" + this.type +") " + this.id); + } + +}); + +YAHOO.augment(YAHOO.widget.Slider, YAHOO.util.EventProvider); + +/** + * A drag and drop implementation to be used as the thumb of a slider. + * @class SliderThumb + * @extends YAHOO.util.DD + * @constructor + * @param {String} id the id of the slider html element + * @param {String} sGroup the group of related DragDrop items + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + */ +YAHOO.widget.SliderThumb = function(id, sGroup, iLeft, iRight, iUp, iDown, iTickSize) { + + if (id) { + this.init(id, sGroup); + + /** + * The id of the thumbs parent HTML element (the slider background + * element). + * @property parentElId + * @type string + */ + this.parentElId = sGroup; + } + + + //this.removeInvalidHandleType("A"); + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + + /** + * Overrides the isTarget property in YAHOO.util.DragDrop + * @property isTarget + * @private + */ + this.isTarget = false; + + /** + * The tick size for this slider + * @property tickSize + * @type int + * @private + */ + this.tickSize = iTickSize; + + /** + * Informs the drag and drop util that the offsets should remain when + * resetting the constraints. This preserves the slider value when + * the constraints are reset + * @property maintainOffset + * @type boolean + * @private + */ + this.maintainOffset = true; + + this.initSlider(iLeft, iRight, iUp, iDown, iTickSize); + + /** + * Turns off the autoscroll feature in drag and drop + * @property scroll + * @private + */ + this.scroll = false; + +}; + +YAHOO.extend(YAHOO.widget.SliderThumb, YAHOO.util.DD, { + + /** + * The (X and Y) difference between the thumb location and its parent + * (the slider background) when the control is instantiated. + * @property startOffset + * @type [int, int] + */ + startOffset: null, + + /** + * Flag used to figure out if this is a horizontal or vertical slider + * @property _isHoriz + * @type boolean + * @private + */ + _isHoriz: false, + + /** + * Cache the last value so we can check for change + * @property _prevVal + * @type int + * @private + */ + _prevVal: 0, + + /** + * The slider is _graduated if there is a tick interval defined + * @property _graduated + * @type boolean + * @private + */ + _graduated: false, + + /** + * Returns the difference between the location of the thumb and its parent. + * @method getOffsetFromParent + * @param {[int, int]} parentPos Optionally accepts the position of the parent + * @type [int, int] + */ + getOffsetFromParent: function(parentPos) { + var myPos = YAHOO.util.Dom.getXY(this.getEl()); + var ppos = parentPos || YAHOO.util.Dom.getXY(this.parentElId); + + return [ (myPos[0] - ppos[0]), (myPos[1] - ppos[1]) ]; + }, + + /** + * Set up the slider, must be called in the constructor of all subclasses + * @method initSlider + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize the width of the tick interval. + */ + initSlider: function (iLeft, iRight, iUp, iDown, iTickSize) { + + this.setXConstraint(iLeft, iRight, iTickSize); + this.setYConstraint(iUp, iDown, iTickSize); + + if (iTickSize && iTickSize > 1) { + this._graduated = true; + } + + this._isHoriz = (iLeft || iRight); + this._isVert = (iUp || iDown); + this._isRegion = (this._isHoriz && this._isVert); + + }, + + /** + * Clear's the slider's ticks + * @method clearTicks + */ + clearTicks: function () { + YAHOO.widget.SliderThumb.superclass.clearTicks.call(this); + this._graduated = false; + }, + + /** + * Gets the current offset from the element's start position in + * pixels. + * @method getValue + * @return {int} the number of pixels (positive or negative) the + * slider has moved from the start position. + */ + getValue: function () { + if (!this.available) { return 0; } + var val = (this._isHoriz) ? this.getXValue() : this.getYValue(); + this.logger.log("getVal: " + val); + return val; + }, + + /** + * Gets the current X offset from the element's start position in + * pixels. + * @method getXValue + * @return {int} the number of pixels (positive or negative) the + * slider has moved horizontally from the start position. + */ + getXValue: function () { + if (!this.available) { return 0; } + var newOffset = this.getOffsetFromParent(); + return (newOffset[0] - this.startOffset[0]); + }, + + /** + * Gets the current Y offset from the element's start position in + * pixels. + * @method getYValue + * @return {int} the number of pixels (positive or negative) the + * slider has moved vertically from the start position. + */ + getYValue: function () { + if (!this.available) { return 0; } + var newOffset = this.getOffsetFromParent(); + return (newOffset[1] - this.startOffset[1]); + }, + + /** + * Thumb toString + * @method toString + * @return {string} string representation of the instance + */ + toString: function () { + return "SliderThumb " + this.id; + }, + + /** + * The onchange event for the handle/thumb is delegated to the YAHOO.widget.Slider + * instance it belongs to. + * @method onChange + * @private + */ + onChange: function (x, y) { + } + +}); + +if ("undefined" == typeof YAHOO.util.Anim) { + YAHOO.widget.Slider.ANIM_AVAIL = false; +} + diff --git a/source/web/scripts/ajax/yahoo/slider/slider-min.js b/source/web/scripts/ajax/yahoo/slider/slider-min.js new file mode 100644 index 0000000000..a872cf5683 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/slider/slider-min.js @@ -0,0 +1 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ YAHOO.widget.Slider=function(_1,_2,_3,_4){if(_1){this.type=_4;this.init(_1,_2,true);var _5=this;this.createEvent("change",this);this.createEvent("slideStart",this);this.createEvent("slideEnd",this);this.thumb=_3;_3.onChange=function(){_5.handleThumbChange();};this.isTarget=false;this.animate=YAHOO.widget.Slider.ANIM_AVAIL;this.backgroundEnabled=true;this.tickPause=40;this.enableKeys=true;this.keyIncrement=20;this.moveComplete=true;this.animationDuration=0.2;if(_3._isHoriz&&_3.xTicks&&_3.xTicks.length){this.tickPause=Math.round(360/_3.xTicks.length);}else{if(_3.yTicks&&_3.yTicks.length){this.tickPause=Math.round(360/_3.yTicks.length);}}_3.onMouseDown=function(){return _5.focus();};_3.onMouseUp=function(){_5.thumbMouseUp();};_3.onDrag=function(){_5.fireEvents();};_3.onAvailable=function(){return _5.setStartSliderState();};}};YAHOO.widget.Slider.getHorizSlider=function(_6,_7,_8,_9,_10){return new YAHOO.widget.Slider(_6,_6,new YAHOO.widget.SliderThumb(_7,_6,_8,_9,0,0,_10),"horiz");};YAHOO.widget.Slider.getVertSlider=function(_11,_12,iUp,_14,_15){return new YAHOO.widget.Slider(_11,_11,new YAHOO.widget.SliderThumb(_12,_11,0,0,iUp,_14,_15),"vert");};YAHOO.widget.Slider.getSliderRegion=function(_16,_17,_18,_19,iUp,_20,_21){return new YAHOO.widget.Slider(_16,_16,new YAHOO.widget.SliderThumb(_17,_16,_18,_19,iUp,_20,_21),"region");};YAHOO.widget.Slider.ANIM_AVAIL=true;YAHOO.extend(YAHOO.widget.Slider,YAHOO.util.DragDrop,{onAvailable:function(){var _22=YAHOO.util.Event;_22.on(this.id,"keydown",this.handleKeyDown,this,true);_22.on(this.id,"keypress",this.handleKeyPress,this,true);},handleKeyPress:function(e){if(this.enableKeys){var _24=YAHOO.util.Event;var kc=_24.getCharCode(e);switch(kc){case 37:case 38:case 39:case 40:case 36:case 35:_24.preventDefault(e);break;default:}}},handleKeyDown:function(e){if(this.enableKeys){var _26=YAHOO.util.Event;var kc=_26.getCharCode(e),t=this.thumb;var h=this.getXValue(),v=this.getYValue();var _28=false;var _29=true;switch(kc){case 37:h-=this.keyIncrement;break;case 38:v-=this.keyIncrement;break;case 39:h+=this.keyIncrement;break;case 40:v+=this.keyIncrement;break;case 36:h=t.leftConstraint;v=t.topConstraint;break;case 35:h=t.rightConstraint;v=t.bottomConstraint;break;default:_29=false;}if(_29){if(t._isRegion){this.setRegionValue(h,v,true);}else{var _30=(t._isHoriz)?h:v;this.setValue(_30,true);}_26.stopEvent(e);}}},setStartSliderState:function(){this.setThumbCenterPoint();this.baselinePos=YAHOO.util.Dom.getXY(this.getEl());this.thumb.startOffset=this.thumb.getOffsetFromParent(this.baselinePos);if(this.thumb._isRegion){if(this.deferredSetRegionValue){this.setRegionValue.apply(this,this.deferredSetRegionValue,true);this.deferredSetRegionValue=null;}else{this.setRegionValue(0,0,true);}}else{if(this.deferredSetValue){this.setValue.apply(this,this.deferredSetValue,true);this.deferredSetValue=null;}else{this.setValue(0,true,true);}}},setThumbCenterPoint:function(){var el=this.thumb.getEl();if(el){this.thumbCenterPoint={x:parseInt(el.offsetWidth/2,10),y:parseInt(el.offsetHeight/2,10)};}},lock:function(){this.thumb.lock();this.locked=true;},unlock:function(){this.thumb.unlock();this.locked=false;},thumbMouseUp:function(){if(!this.isLocked()&&!this.moveComplete){this.endMove();}},getThumb:function(){return this.thumb;},focus:function(){var el=this.getEl();if(el.focus){try{el.focus();}catch(e){}}this.verifyOffset();if(this.isLocked()){return false;}else{this.onSlideStart();return true;}},onChange:function(_32,_33){},onSlideStart:function(){},onSlideEnd:function(){},getValue:function(){return this.thumb.getValue();},getXValue:function(){return this.thumb.getXValue();},getYValue:function(){return this.thumb.getYValue();},handleThumbChange:function(){var t=this.thumb;if(t._isRegion){t.onChange(t.getXValue(),t.getYValue());this.fireEvent("change",{x:t.getXValue(),y:t.getYValue()});}else{t.onChange(t.getValue());this.fireEvent("change",t.getValue());}},setValue:function(_35,_36,_37){if(!this.thumb.available){this.deferredSetValue=arguments;return false;}if(this.isLocked()&&!_37){return false;}if(isNaN(_35)){return false;}var t=this.thumb;var _38,newY;this.verifyOffset();if(t._isRegion){return false;}else{if(t._isHoriz){this.onSlideStart();_38=t.initPageX+_35+this.thumbCenterPoint.x;this.moveThumb(_38,t.initPageY,_36);}else{this.onSlideStart();newY=t.initPageY+_35+this.thumbCenterPoint.y;this.moveThumb(t.initPageX,newY,_36);}}return true;},setRegionValue:function(_39,_40,_41){if(!this.thumb.available){this.deferredSetRegionValue=arguments;return false;}if(this.isLocked()&&!force){return false;}if(isNaN(_39)){return false;}var t=this.thumb;if(t._isRegion){this.onSlideStart();var _42=t.initPageX+_39+this.thumbCenterPoint.x;var _43=t.initPageY+_40+this.thumbCenterPoint.y;this.moveThumb(_42,_43,_41);return true;}return false;},verifyOffset:function(){var _44=YAHOO.util.Dom.getXY(this.getEl());if(_44[0]!=this.baselinePos[0]||_44[1]!=this.baselinePos[1]){this.thumb.resetConstraints();this.baselinePos=_44;return false;}return true;},moveThumb:function(x,y,_47){var t=this.thumb;var _48=this;if(!t.available){return;}t.setDelta(this.thumbCenterPoint.x,this.thumbCenterPoint.y);var _p=t.getTargetCoord(x,y);var p=[_p.x,_p.y];this.fireEvent("slideStart");if(this.animate&&YAHOO.widget.Slider.ANIM_AVAIL&&t._graduated&&!_47){this.lock();setTimeout(function(){_48.moveOneTick(p);},this.tickPause);}else{if(this.animate&&YAHOO.widget.Slider.ANIM_AVAIL&&!_47){this.lock();var _51=new YAHOO.util.Motion(t.id,{points:{to:p}},this.animationDuration,YAHOO.util.Easing.easeOut);_51.onComplete.subscribe(function(){_48.endMove();});_51.animate();}else{t.setDragElPos(x,y);this.endMove();}}},moveOneTick:function(_52){var t=this.thumb;var _53=YAHOO.util.Dom.getXY(t.getEl());var tmp;var _55=null;if(t._isRegion){_55=this._getNextX(_53,_52);var _56=(_55)?_55[0]:_53[0];_55=this._getNextY([_56,_53[1]],_52);}else{if(t._isHoriz){_55=this._getNextX(_53,_52);}else{_55=this._getNextY(_53,_52);}}if(_55){this.thumb.alignElWithMouse(t.getEl(),_55[0],_55[1]);if(!(_55[0]==_52[0]&&_55[1]==_52[1])){var _57=this;setTimeout(function(){_57.moveOneTick(_52);},this.tickPause);}else{this.endMove();}}else{this.endMove();}},_getNextX:function(_58,_59){var t=this.thumb;var _60;var tmp=[];var _61=null;if(_58[0]>_59[0]){_60=t.tickSize-this.thumbCenterPoint.x;tmp=t.getTargetCoord(_58[0]-_60,_58[1]);_61=[tmp.x,tmp.y];}else{if(_58[0]<_59[0]){_60=t.tickSize+this.thumbCenterPoint.x;tmp=t.getTargetCoord(_58[0]+_60,_58[1]);_61=[tmp.x,tmp.y];}else{}}return _61;},_getNextY:function(_62,_63){var t=this.thumb;var _64;var tmp=[];var _65=null;if(_62[1]>_63[1]){_64=t.tickSize-this.thumbCenterPoint.y;tmp=t.getTargetCoord(_62[0],_62[1]-_64);_65=[tmp.x,tmp.y];}else{if(_62[1]<_63[1]){_64=t.tickSize+this.thumbCenterPoint.y;tmp=t.getTargetCoord(_62[0],_62[1]+_64);_65=[tmp.x,tmp.y];}else{}}return _65;},b4MouseDown:function(e){this.thumb.autoOffset();this.thumb.resetConstraints();},onMouseDown:function(e){if(!this.isLocked()&&this.backgroundEnabled){var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);this.focus();this.moveThumb(x,y);}},onDrag:function(e){if(!this.isLocked()){var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);this.moveThumb(x,y,true);}},endMove:function(){this.unlock();this.moveComplete=true;this.fireEvents();},fireEvents:function(){var t=this.thumb;t.cachePosition();if(!this.isLocked()){if(t._isRegion){var _66=t.getXValue();var _67=t.getYValue();if(_66!=this.previousX||_67!=this.previousY){this.onChange(_66,_67);this.fireEvent("change",{x:_66,y:_67});}this.previousX=_66;this.previousY=_67;}else{var _68=t.getValue();if(_68!=this.previousVal){this.onChange(_68);this.fireEvent("change",_68);}this.previousVal=_68;}if(this.moveComplete){this.onSlideEnd();this.fireEvent("slideEnd");this.moveComplete=false;}}},toString:function(){return ("Slider ("+this.type+") "+this.id);}});YAHOO.augment(YAHOO.widget.Slider,YAHOO.util.EventProvider);YAHOO.widget.SliderThumb=function(id,_70,_71,_72,iUp,_73,_74){if(id){this.init(id,_70);this.parentElId=_70;}this.isTarget=false;this.tickSize=_74;this.maintainOffset=true;this.initSlider(_71,_72,iUp,_73,_74);this.scroll=false;};YAHOO.extend(YAHOO.widget.SliderThumb,YAHOO.util.DD,{startOffset:null,_isHoriz:false,_prevVal:0,_graduated:false,getOffsetFromParent:function(_75){var _76=YAHOO.util.Dom.getXY(this.getEl());var _77=_75||YAHOO.util.Dom.getXY(this.parentElId);return [(_76[0]-_77[0]),(_76[1]-_77[1])];},initSlider:function(_78,_79,iUp,_80,_81){this.setXConstraint(_78,_79,_81);this.setYConstraint(iUp,_80,_81);if(_81&&_81>1){this._graduated=true;}this._isHoriz=(_78||_79);this._isVert=(iUp||_80);this._isRegion=(this._isHoriz&&this._isVert);},clearTicks:function(){YAHOO.widget.SliderThumb.superclass.clearTicks.call(this);this._graduated=false;},getValue:function(){if(!this.available){return 0;}var val=(this._isHoriz)?this.getXValue():this.getYValue();return val;},getXValue:function(){if(!this.available){return 0;}var _83=this.getOffsetFromParent();return (_83[0]-this.startOffset[0]);},getYValue:function(){if(!this.available){return 0;}var _84=this.getOffsetFromParent();return (_84[1]-this.startOffset[1]);},toString:function(){return "SliderThumb "+this.id;},onChange:function(x,y){}});if("undefined"==typeof YAHOO.util.Anim){YAHOO.widget.Slider.ANIM_AVAIL=false;} \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/slider/slider.js b/source/web/scripts/ajax/yahoo/slider/slider.js new file mode 100644 index 0000000000..8d3cd62039 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/slider/slider.js @@ -0,0 +1,1113 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ + +/** + * The Slider component is a UI control that enables the user to adjust + * values in a finite range along one or two axes. Typically, the Slider + * control is used in a web application as a rich, visual replacement + * for an input box that takes a number as input. The Slider control can + * also easily accommodate a second dimension, providing x,y output for + * a selection point chosen from a rectangular region. + * + * @module slider + * @title Slider Widget + * @namespace YAHOO.widget + * @requires yahoo,dom,dragdrop,event + * @optional animation + */ + +/** + * A DragDrop implementation that can be used as a background for a + * slider. It takes a reference to the thumb instance + * so it can delegate some of the events to it. The goal is to make the + * thumb jump to the location on the background when the background is + * clicked. + * + * @class Slider + * @extends YAHOO.util.DragDrop + * @constructor + * @param {String} id The id of the element linked to this instance + * @param {String} sGroup The group of related DragDrop items + * @param {String} sType The type of slider (horiz, vert, region) + */ +YAHOO.widget.Slider = function(sElementId, sGroup, oThumb, sType) { + if (sElementId) { + + /** + * The type of the slider (horiz, vert, region) + * @property type + * @type string + */ + this.type = sType; + + this.init(sElementId, sGroup, true); + + //this.removeInvalidHandleType("A"); + + + var self = this; + + /** + * Event the fires when the value of the control changes. If + * the control is animated the event will fire every point + * along the way. + * @event change + * @param {int} new + * @param {int} firstOffset the number of pixels the thumb has moved + * from its start position. Normal horizontal and vertical sliders will only + * have the firstOffset. Regions will have both, the first is the horizontal + * offset, the second the vertical. + * @param {int} secondOffset the y offset for region sliders + */ + this.createEvent("change", this); + + /** + * Event that fires at the end of a slider thumb move. + * @event slideStart + */ + this.createEvent("slideStart", this); + + /** + * Event that fires at the end of a slider thumb move + * @event slideEnd + */ + this.createEvent("slideEnd", this); + + /** + * A YAHOO.widget.SliderThumb instance that we will use to + * reposition the thumb when the background is clicked + * @property thumb + * @type YAHOO.widget.SliderThumb + */ + this.thumb = oThumb; + + // add handler for the handle onchange event + oThumb.onChange = function() { + self.handleThumbChange(); + }; + + /** + * Overrides the isTarget property in YAHOO.util.DragDrop + * @property isTarget + * @private + */ + this.isTarget = false; + + /** + * Flag that determines if the thumb will animate when moved + * @property animate + * @type boolean + */ + this.animate = YAHOO.widget.Slider.ANIM_AVAIL; + + /** + * Set to false to disable a background click thumb move + * @property backgroundEnabled + * @type boolean + */ + this.backgroundEnabled = true; + + /** + * Adjustment factor for tick animation, the more ticks, the + * faster the animation (by default) + * @property tickPause + * @type int + */ + this.tickPause = 40; + + /** + * Enables the arrow, home and end keys, defaults to true. + * @property enableKeys + * @type boolean + */ + this.enableKeys = true; + + /** + * Specifies the number of pixels the arrow keys will move the slider. + * Default is 25. + * @property keyIncrement + * @type int + */ + this.keyIncrement = 20; + + /** + * moveComplete is set to true when the slider has moved to its final + * destination. For animated slider, this value can be checked in + * the onChange handler to make it possible to execute logic only + * when the move is complete rather than at all points along the way. + * + * @property moveComplete + * @type Boolean + */ + this.moveComplete = true; + + /** + * If animation is configured, specifies the length of the animation + * in seconds. + * @property animationDuration + * @type int + * @default 0.2 + */ + this.animationDuration = 0.2; + + if (oThumb._isHoriz && oThumb.xTicks && oThumb.xTicks.length) { + this.tickPause = Math.round(360 / oThumb.xTicks.length); + } else if (oThumb.yTicks && oThumb.yTicks.length) { + this.tickPause = Math.round(360 / oThumb.yTicks.length); + } + + + // delegate thumb methods + oThumb.onMouseDown = function () { return self.focus(); }; + //oThumb.b4MouseDown = function () { return self.b4MouseDown(); }; + // oThumb.lock = function() { self.lock(); }; + // oThumb.unlock = function() { self.unlock(); }; + oThumb.onMouseUp = function() { self.thumbMouseUp(); }; + oThumb.onDrag = function() { self.fireEvents(); }; + oThumb.onAvailable = function() { return self.setStartSliderState(); }; + } +}; + +/** + * Factory method for creating a horizontal slider + * @method YAHOO.widget.Slider.getHorizSlider + * @static + * @param {String} sBGElId the id of the slider's background element + * @param {String} sHandleElId the id of the thumb element + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + * @return {Slider} a horizontal slider control + */ +YAHOO.widget.Slider.getHorizSlider = + function (sBGElId, sHandleElId, iLeft, iRight, iTickSize) { + return new YAHOO.widget.Slider(sBGElId, sBGElId, + new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, + iLeft, iRight, 0, 0, iTickSize), "horiz"); +}; + +/** + * Factory method for creating a vertical slider + * @method YAHOO.widget.Slider.getVertSlider + * @static + * @param {String} sBGElId the id of the slider's background element + * @param {String} sHandleElId the id of the thumb element + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + * @return {Slider} a vertical slider control + */ +YAHOO.widget.Slider.getVertSlider = + function (sBGElId, sHandleElId, iUp, iDown, iTickSize) { + return new YAHOO.widget.Slider(sBGElId, sBGElId, + new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, 0, 0, + iUp, iDown, iTickSize), "vert"); +}; + +/** + * Factory method for creating a slider region like the one in the color + * picker example + * @method YAHOO.widget.Slider.getSliderRegion + * @static + * @param {String} sBGElId the id of the slider's background element + * @param {String} sHandleElId the id of the thumb element + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + * @return {Slider} a slider region control + */ +YAHOO.widget.Slider.getSliderRegion = + function (sBGElId, sHandleElId, iLeft, iRight, iUp, iDown, iTickSize) { + return new YAHOO.widget.Slider(sBGElId, sBGElId, + new YAHOO.widget.SliderThumb(sHandleElId, sBGElId, iLeft, iRight, + iUp, iDown, iTickSize), "region"); +}; + +/** + * By default, animation is available if the animation library is detected. + * @property YAHOO.widget.Slider.ANIM_AVAIL + * @static + * @type boolean + */ +YAHOO.widget.Slider.ANIM_AVAIL = true; + +YAHOO.extend(YAHOO.widget.Slider, YAHOO.util.DragDrop, { + + onAvailable: function() { + var Event = YAHOO.util.Event; + Event.on(this.id, "keydown", this.handleKeyDown, this, true); + Event.on(this.id, "keypress", this.handleKeyPress, this, true); + }, + + handleKeyPress: function(e) { + if (this.enableKeys) { + var Event = YAHOO.util.Event; + var kc = Event.getCharCode(e); + switch (kc) { + case 0x25: // left + case 0x26: // up + case 0x27: // right + case 0x28: // down + case 0x24: // home + case 0x23: // end + Event.preventDefault(e); + break; + default: + } + } + }, + + handleKeyDown: function(e) { + if (this.enableKeys) { + var Event = YAHOO.util.Event; + + var kc = Event.getCharCode(e), t=this.thumb; + var h=this.getXValue(),v=this.getYValue(); + + var horiz = false; + var changeValue = true; + switch (kc) { + + // left + case 0x25: h -= this.keyIncrement; break; + + // up + case 0x26: v -= this.keyIncrement; break; + + // right + case 0x27: h += this.keyIncrement; break; + + // down + case 0x28: v += this.keyIncrement; break; + + // home + case 0x24: h = t.leftConstraint; + v = t.topConstraint; + break; + + // end + case 0x23: h = t.rightConstraint; + v = t.bottomConstraint; + break; + + default: changeValue = false; + } + + if (changeValue) { + if (t._isRegion) { + this.setRegionValue(h, v, true); + } else { + var newVal = (t._isHoriz) ? h : v; + this.setValue(newVal, true); + } + Event.stopEvent(e); + } + + } + }, + + /** + * Initialization that sets up the value offsets once the elements are ready + * @method setSliderStartState + */ + setStartSliderState: function() { + + + this.setThumbCenterPoint(); + + /** + * The basline position of the background element, used + * to determine if the background has moved since the last + * operation. + * @property baselinePos + * @type [int, int] + */ + this.baselinePos = YAHOO.util.Dom.getXY(this.getEl()); + + this.thumb.startOffset = this.thumb.getOffsetFromParent(this.baselinePos); + + if (this.thumb._isRegion) { + if (this.deferredSetRegionValue) { + this.setRegionValue.apply(this, this.deferredSetRegionValue, true); + this.deferredSetRegionValue = null; + } else { + this.setRegionValue(0, 0, true); + } + } else { + if (this.deferredSetValue) { + this.setValue.apply(this, this.deferredSetValue, true); + this.deferredSetValue = null; + } else { + this.setValue(0, true, true); + } + } + }, + + /** + * When the thumb is available, we cache the centerpoint of the element so + * we can position the element correctly when the background is clicked + * @method setThumbCenterPoint + */ + setThumbCenterPoint: function() { + + var el = this.thumb.getEl(); + + if (el) { + /** + * The center of the slider element is stored so we can position + * place it in the correct position when the background is clicked + * @property thumbCenterPoint + * @type {"x": int, "y": int} + */ + this.thumbCenterPoint = { + x: parseInt(el.offsetWidth/2, 10), + y: parseInt(el.offsetHeight/2, 10) + }; + } + + }, + + /** + * Locks the slider, overrides YAHOO.util.DragDrop + * @method lock + */ + lock: function() { + this.thumb.lock(); + this.locked = true; + }, + + /** + * Unlocks the slider, overrides YAHOO.util.DragDrop + * @method unlock + */ + unlock: function() { + this.thumb.unlock(); + this.locked = false; + }, + + /** + * Handles mouseup event on the slider background + * @method thumbMouseUp + * @private + */ + thumbMouseUp: function() { + if (!this.isLocked() && !this.moveComplete) { + this.endMove(); + } + + }, + + /** + * Returns a reference to this slider's thumb + * @method getThumb + * @return {SliderThumb} this slider's thumb + */ + getThumb: function() { + return this.thumb; + }, + + /** + * Try to focus the element when clicked so we can add + * accessibility features + * @method focus + * @private + */ + focus: function() { + + // Focus the background element if possible + var el = this.getEl(); + + if (el.focus) { + try { + el.focus(); + } catch(e) { + // Prevent permission denied unhandled exception in FF that can + // happen when setting focus while another element is handling + // the blur. @TODO this is still writing to the error log + // (unhandled error) in FF1.5 with strict error checking on. + } + } + + this.verifyOffset(); + + if (this.isLocked()) { + return false; + } else { + this.onSlideStart(); + return true; + } + }, + + /** + * Event that fires when the value of the slider has changed + * @method onChange + * @param {int} firstOffset the number of pixels the thumb has moved + * from its start position. Normal horizontal and vertical sliders will only + * have the firstOffset. Regions will have both, the first is the horizontal + * offset, the second the vertical. + * @param {int} secondOffset the y offset for region sliders + * @deprecated use instance.subscribe("change") instead + */ + onChange: function (firstOffset, secondOffset) { + /* override me */ + }, + + /** + * Event that fires when the at the beginning of the slider thumb move + * @method onSlideStart + * @deprecated use instance.subscribe("slideStart") instead + */ + onSlideStart: function () { + /* override me */ + }, + + /** + * Event that fires at the end of a slider thumb move + * @method onSliderEnd + * @deprecated use instance.subscribe("slideEnd") instead + */ + onSlideEnd: function () { + /* override me */ + }, + + /** + * Returns the slider's thumb offset from the start position + * @method getValue + * @return {int} the current value + */ + getValue: function () { + return this.thumb.getValue(); + }, + + /** + * Returns the slider's thumb X offset from the start position + * @method getXValue + * @return {int} the current horizontal offset + */ + getXValue: function () { + return this.thumb.getXValue(); + }, + + /** + * Returns the slider's thumb Y offset from the start position + * @method getYValue + * @return {int} the current vertical offset + */ + getYValue: function () { + return this.thumb.getYValue(); + }, + + /** + * Internal handler for the slider thumb's onChange event + * @method handleThumbChange + * @private + */ + handleThumbChange: function () { + var t = this.thumb; + if (t._isRegion) { + t.onChange(t.getXValue(), t.getYValue()); + this.fireEvent("change", { x: t.getXValue(), y: t.getYValue() } ); + } else { + t.onChange(t.getValue()); + this.fireEvent("change", t.getValue()); + } + + }, + + /** + * Provides a way to set the value of the slider in code. + * @method setValue + * @param {int} newOffset the number of pixels the thumb should be + * positioned away from the initial start point + * @param {boolean} skipAnim set to true to disable the animation + * for this move action (but not others). + * @param {boolean} force ignore the locked setting and set value anyway + * @return {boolean} true if the move was performed, false if it failed + */ + setValue: function(newOffset, skipAnim, force) { + + if (!this.thumb.available) { + this.deferredSetValue = arguments; + return false; + } + + if (this.isLocked() && !force) { + return false; + } + + if ( isNaN(newOffset) ) { + return false; + } + + var t = this.thumb; + var newX, newY; + this.verifyOffset(); + if (t._isRegion) { + return false; + } else if (t._isHoriz) { + this.onSlideStart(); + // this.fireEvent("slideStart"); + newX = t.initPageX + newOffset + this.thumbCenterPoint.x; + this.moveThumb(newX, t.initPageY, skipAnim); + } else { + this.onSlideStart(); + // this.fireEvent("slideStart"); + newY = t.initPageY + newOffset + this.thumbCenterPoint.y; + this.moveThumb(t.initPageX, newY, skipAnim); + } + + return true; + }, + + /** + * Provides a way to set the value of the region slider in code. + * @method setRegionValue + * @param {int} newOffset the number of pixels the thumb should be + * positioned away from the initial start point (x axis for region) + * @param {int} newOffset2 the number of pixels the thumb should be + * positioned away from the initial start point (y axis for region) + * @param {boolean} skipAnim set to true to disable the animation + * for this move action (but not others). + * @param {boolean} force ignore the locked setting and set value anyway + * @return {boolean} true if the move was performed, false if it failed + */ + setRegionValue: function(newOffset, newOffset2, skipAnim) { + + if (!this.thumb.available) { + this.deferredSetRegionValue = arguments; + return false; + } + + if (this.isLocked() && !force) { + return false; + } + + if ( isNaN(newOffset) ) { + return false; + } + + var t = this.thumb; + if (t._isRegion) { + this.onSlideStart(); + var newX = t.initPageX + newOffset + this.thumbCenterPoint.x; + var newY = t.initPageY + newOffset2 + this.thumbCenterPoint.y; + this.moveThumb(newX, newY, skipAnim); + return true; + } + + return false; + + }, + + /** + * Checks the background position element position. If it has moved from the + * baseline position, the constraints for the thumb are reset + * @method verifyOffset + * @return {boolean} True if the offset is the same as the baseline. + */ + verifyOffset: function() { + + var newPos = YAHOO.util.Dom.getXY(this.getEl()); + + if (newPos[0] != this.baselinePos[0] || newPos[1] != this.baselinePos[1]) { + this.thumb.resetConstraints(); + this.baselinePos = newPos; + return false; + } + + return true; + }, + + /** + * Move the associated slider moved to a timeout to try to get around the + * mousedown stealing moz does when I move the slider element between the + * cursor and the background during the mouseup event + * @method moveThumb + * @param {int} x the X coordinate of the click + * @param {int} y the Y coordinate of the click + * @param {boolean} skipAnim don't animate if the move happend onDrag + * @private + */ + moveThumb: function(x, y, skipAnim) { + + + var t = this.thumb; + var self = this; + + if (!t.available) { + return; + } + + + // this.verifyOffset(); + + t.setDelta(this.thumbCenterPoint.x, this.thumbCenterPoint.y); + + var _p = t.getTargetCoord(x, y); + var p = [_p.x, _p.y]; + + this.fireEvent("slideStart"); + + if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && t._graduated && !skipAnim) { + // this.thumb._animating = true; + this.lock(); + + setTimeout( function() { self.moveOneTick(p); }, this.tickPause ); + + } else if (this.animate && YAHOO.widget.Slider.ANIM_AVAIL && !skipAnim) { + + // this.thumb._animating = true; + this.lock(); + + var oAnim = new YAHOO.util.Motion( + t.id, { points: { to: p } }, + this.animationDuration, + YAHOO.util.Easing.easeOut ); + + oAnim.onComplete.subscribe( function() { self.endMove(); } ); + oAnim.animate(); + } else { + t.setDragElPos(x, y); + // this.fireEvents(); + this.endMove(); + } + }, + + /** + * Move the slider one tick mark towards its final coordinate. Used + * for the animation when tick marks are defined + * @method moveOneTick + * @param {int[]} the destination coordinate + * @private + */ + moveOneTick: function(finalCoord) { + + var t = this.thumb; + var curCoord = YAHOO.util.Dom.getXY(t.getEl()); + var tmp; + + // var thresh = Math.min(t.tickSize + (Math.floor(t.tickSize/2)), 10); + // var thresh = 10; + // var thresh = t.tickSize + (Math.floor(t.tickSize/2)); + + var nextCoord = null; + + if (t._isRegion) { + nextCoord = this._getNextX(curCoord, finalCoord); + var tmpX = (nextCoord) ? nextCoord[0] : curCoord[0]; + nextCoord = this._getNextY([tmpX, curCoord[1]], finalCoord); + + } else if (t._isHoriz) { + nextCoord = this._getNextX(curCoord, finalCoord); + } else { + nextCoord = this._getNextY(curCoord, finalCoord); + } + + + if (nextCoord) { + + // move to the next coord + // YAHOO.util.Dom.setXY(t.getEl(), nextCoord); + + // var el = t.getEl(); + // YAHOO.util.Dom.setStyle(el, "left", (nextCoord[0] + this.thumb.deltaSetXY[0]) + "px"); + // YAHOO.util.Dom.setStyle(el, "top", (nextCoord[1] + this.thumb.deltaSetXY[1]) + "px"); + + this.thumb.alignElWithMouse(t.getEl(), nextCoord[0], nextCoord[1]); + + // check if we are in the final position, if not make a recursive call + if (!(nextCoord[0] == finalCoord[0] && nextCoord[1] == finalCoord[1])) { + var self = this; + setTimeout(function() { self.moveOneTick(finalCoord); }, + this.tickPause); + } else { + this.endMove(); + } + } else { + this.endMove(); + } + + //this.tickPause = Math.round(this.tickPause/2); + }, + + /** + * Returns the next X tick value based on the current coord and the target coord. + * @method _getNextX + * @private + */ + _getNextX: function(curCoord, finalCoord) { + var t = this.thumb; + var thresh; + var tmp = []; + var nextCoord = null; + if (curCoord[0] > finalCoord[0]) { + thresh = t.tickSize - this.thumbCenterPoint.x; + tmp = t.getTargetCoord( curCoord[0] - thresh, curCoord[1] ); + nextCoord = [tmp.x, tmp.y]; + } else if (curCoord[0] < finalCoord[0]) { + thresh = t.tickSize + this.thumbCenterPoint.x; + tmp = t.getTargetCoord( curCoord[0] + thresh, curCoord[1] ); + nextCoord = [tmp.x, tmp.y]; + } else { + // equal, do nothing + } + + return nextCoord; + }, + + /** + * Returns the next Y tick value based on the current coord and the target coord. + * @method _getNextY + * @private + */ + _getNextY: function(curCoord, finalCoord) { + var t = this.thumb; + var thresh; + var tmp = []; + var nextCoord = null; + + if (curCoord[1] > finalCoord[1]) { + thresh = t.tickSize - this.thumbCenterPoint.y; + tmp = t.getTargetCoord( curCoord[0], curCoord[1] - thresh ); + nextCoord = [tmp.x, tmp.y]; + } else if (curCoord[1] < finalCoord[1]) { + thresh = t.tickSize + this.thumbCenterPoint.y; + tmp = t.getTargetCoord( curCoord[0], curCoord[1] + thresh ); + nextCoord = [tmp.x, tmp.y]; + } else { + // equal, do nothing + } + + return nextCoord; + }, + + /** + * Resets the constraints before moving the thumb. + * @method b4MouseDown + * @private + */ + b4MouseDown: function(e) { + this.thumb.autoOffset(); + this.thumb.resetConstraints(); + }, + + /** + * Handles the mousedown event for the slider background + * @method onMouseDown + * @private + */ + onMouseDown: function(e) { + // this.resetConstraints(true); + // this.thumb.resetConstraints(true); + + if (! this.isLocked() && this.backgroundEnabled) { + var x = YAHOO.util.Event.getPageX(e); + var y = YAHOO.util.Event.getPageY(e); + + this.focus(); + this.moveThumb(x, y); + } + + }, + + /** + * Handles the onDrag event for the slider background + * @method onDrag + * @private + */ + onDrag: function(e) { + if (! this.isLocked()) { + var x = YAHOO.util.Event.getPageX(e); + var y = YAHOO.util.Event.getPageY(e); + this.moveThumb(x, y, true); + } + }, + + /** + * Fired when the slider movement ends + * @method endMove + * @private + */ + endMove: function () { + // this._animating = false; + this.unlock(); + this.moveComplete = true; + this.fireEvents(); + }, + + /** + * Fires the change event if the value has been changed. Ignored if we are in + * the middle of an animation as the event will fire when the animation is + * complete + * @method fireEvents + * @private + */ + fireEvents: function () { + + var t = this.thumb; + + t.cachePosition(); + + if (! this.isLocked()) { + if (t._isRegion) { + var newX = t.getXValue(); + var newY = t.getYValue(); + + if (newX != this.previousX || newY != this.previousY) { + this.onChange(newX, newY); + this.fireEvent("change", { x: newX, y: newY }); + } + + this.previousX = newX; + this.previousY = newY; + + } else { + var newVal = t.getValue(); + if (newVal != this.previousVal) { + this.onChange( newVal ); + this.fireEvent("change", newVal); + } + this.previousVal = newVal; + } + + if (this.moveComplete) { + this.onSlideEnd(); + this.fireEvent("slideEnd"); + this.moveComplete = false; + } + + } + }, + + /** + * Slider toString + * @method toString + * @return {string} string representation of the instance + */ + toString: function () { + return ("Slider (" + this.type +") " + this.id); + } + +}); + +YAHOO.augment(YAHOO.widget.Slider, YAHOO.util.EventProvider); + +/** + * A drag and drop implementation to be used as the thumb of a slider. + * @class SliderThumb + * @extends YAHOO.util.DD + * @constructor + * @param {String} id the id of the slider html element + * @param {String} sGroup the group of related DragDrop items + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize optional parameter for specifying that the element + * should move a certain number pixels at a time. + */ +YAHOO.widget.SliderThumb = function(id, sGroup, iLeft, iRight, iUp, iDown, iTickSize) { + + if (id) { + this.init(id, sGroup); + + /** + * The id of the thumbs parent HTML element (the slider background + * element). + * @property parentElId + * @type string + */ + this.parentElId = sGroup; + } + + //this.removeInvalidHandleType("A"); + + + /** + * Overrides the isTarget property in YAHOO.util.DragDrop + * @property isTarget + * @private + */ + this.isTarget = false; + + /** + * The tick size for this slider + * @property tickSize + * @type int + * @private + */ + this.tickSize = iTickSize; + + /** + * Informs the drag and drop util that the offsets should remain when + * resetting the constraints. This preserves the slider value when + * the constraints are reset + * @property maintainOffset + * @type boolean + * @private + */ + this.maintainOffset = true; + + this.initSlider(iLeft, iRight, iUp, iDown, iTickSize); + + /** + * Turns off the autoscroll feature in drag and drop + * @property scroll + * @private + */ + this.scroll = false; + +}; + +YAHOO.extend(YAHOO.widget.SliderThumb, YAHOO.util.DD, { + + /** + * The (X and Y) difference between the thumb location and its parent + * (the slider background) when the control is instantiated. + * @property startOffset + * @type [int, int] + */ + startOffset: null, + + /** + * Flag used to figure out if this is a horizontal or vertical slider + * @property _isHoriz + * @type boolean + * @private + */ + _isHoriz: false, + + /** + * Cache the last value so we can check for change + * @property _prevVal + * @type int + * @private + */ + _prevVal: 0, + + /** + * The slider is _graduated if there is a tick interval defined + * @property _graduated + * @type boolean + * @private + */ + _graduated: false, + + /** + * Returns the difference between the location of the thumb and its parent. + * @method getOffsetFromParent + * @param {[int, int]} parentPos Optionally accepts the position of the parent + * @type [int, int] + */ + getOffsetFromParent: function(parentPos) { + var myPos = YAHOO.util.Dom.getXY(this.getEl()); + var ppos = parentPos || YAHOO.util.Dom.getXY(this.parentElId); + + return [ (myPos[0] - ppos[0]), (myPos[1] - ppos[1]) ]; + }, + + /** + * Set up the slider, must be called in the constructor of all subclasses + * @method initSlider + * @param {int} iLeft the number of pixels the element can move left + * @param {int} iRight the number of pixels the element can move right + * @param {int} iUp the number of pixels the element can move up + * @param {int} iDown the number of pixels the element can move down + * @param {int} iTickSize the width of the tick interval. + */ + initSlider: function (iLeft, iRight, iUp, iDown, iTickSize) { + + this.setXConstraint(iLeft, iRight, iTickSize); + this.setYConstraint(iUp, iDown, iTickSize); + + if (iTickSize && iTickSize > 1) { + this._graduated = true; + } + + this._isHoriz = (iLeft || iRight); + this._isVert = (iUp || iDown); + this._isRegion = (this._isHoriz && this._isVert); + + }, + + /** + * Clear's the slider's ticks + * @method clearTicks + */ + clearTicks: function () { + YAHOO.widget.SliderThumb.superclass.clearTicks.call(this); + this._graduated = false; + }, + + /** + * Gets the current offset from the element's start position in + * pixels. + * @method getValue + * @return {int} the number of pixels (positive or negative) the + * slider has moved from the start position. + */ + getValue: function () { + if (!this.available) { return 0; } + var val = (this._isHoriz) ? this.getXValue() : this.getYValue(); + return val; + }, + + /** + * Gets the current X offset from the element's start position in + * pixels. + * @method getXValue + * @return {int} the number of pixels (positive or negative) the + * slider has moved horizontally from the start position. + */ + getXValue: function () { + if (!this.available) { return 0; } + var newOffset = this.getOffsetFromParent(); + return (newOffset[0] - this.startOffset[0]); + }, + + /** + * Gets the current Y offset from the element's start position in + * pixels. + * @method getYValue + * @return {int} the number of pixels (positive or negative) the + * slider has moved vertically from the start position. + */ + getYValue: function () { + if (!this.available) { return 0; } + var newOffset = this.getOffsetFromParent(); + return (newOffset[1] - this.startOffset[1]); + }, + + /** + * Thumb toString + * @method toString + * @return {string} string representation of the instance + */ + toString: function () { + return "SliderThumb " + this.id; + }, + + /** + * The onchange event for the handle/thumb is delegated to the YAHOO.widget.Slider + * instance it belongs to. + * @method onChange + * @private + */ + onChange: function (x, y) { + } + +}); + +if ("undefined" == typeof YAHOO.util.Anim) { + YAHOO.widget.Slider.ANIM_AVAIL = false; +} + diff --git a/source/web/scripts/ajax/yahoo/tabview/README b/source/web/scripts/ajax/yahoo/tabview/README new file mode 100644 index 0000000000..b24caa0d87 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/tabview/README @@ -0,0 +1,6 @@ +Tabview Release Notes + +*** version 0.12.0 *** + +* Initial release + diff --git a/source/web/scripts/ajax/yahoo/tabview/assets/border_tabs.css b/source/web/scripts/ajax/yahoo/tabview/assets/border_tabs.css new file mode 100644 index 0000000000..7a1bd32a2d --- /dev/null +++ b/source/web/scripts/ajax/yahoo/tabview/assets/border_tabs.css @@ -0,0 +1,44 @@ +.yui-navset .yui-nav li a, .yui-navset .yui-content { + border:1px solid #000; /* label and content borders */ +} + +.yui-navset .yui-nav .selected a, .yui-navset .yui-nav a:hover, .yui-navset .yui-content { + background-color:#f6f7ee; /* active tab, tab hover, and content bgcolor */ +} + +.yui-navset-top .yui-nav .selected a { + border-bottom:0; /* no bottom border for active tab */ + padding-bottom:1px; /* to match height of other tabs */ +} + +.yui-navset-top .yui-content { + margin-top:-1px; /* for active tab overlap */ +} + +.yui-navset-bottom .yui-nav .selected a { + border-top:0; /* no bottom border for active tab */ + padding-top:1px; /* to match height of other tabs */ +} + +.yui-navset-bottom .yui-content { + margin-bottom:-1px; /* for active tab overlap */ +} + +.yui-navset-left .yui-nav li.selected a { + border-right:0; /* no bottom border for active tab */ + padding-right:1px; /* to match height of other tabs */ +} + +.yui-navset-left .yui-content { + margin-left:-1px; /* for active tab overlap */ +} + +.yui-navset-right .yui-nav li.selected a { + border-left:0; /* no bottom border for active tab */ + padding-left:1px; /* to match height of other tabs */ +} + +.yui-navset-right .yui-content { + margin-right:-1px; /* for active tab overlap */ + *margin-right:0; /* except IE */ +} \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/tabview/assets/tabs.css b/source/web/scripts/ajax/yahoo/tabview/assets/tabs.css new file mode 100644 index 0000000000..da8ef13b60 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/tabview/assets/tabs.css @@ -0,0 +1,66 @@ +/* default space between tabs */ +.yui-navset-top .yui-nav li, .yui-navset-bottom .yui-nav li { + margin-right:0.5em; /* horizontal tabs */ +} +.yui-navset-left .yui-nav li, .yui-navset-right .yui-nav li { + margin-bottom:0.5em; /* vertical tabs */ +} + +.yui-navset .yui-nav li em { padding:.5em; } /* default tab padding */ + +/* default width for side tabs */ +.yui-navset-left .yui-nav, .yui-navset-right .yui-nav { width:6em; } +.yui-navset-left { padding-left:6em; } /* map to nav width */ +.yui-navset-right { padding-right:6em; } /* ditto */ + +/* core */ + +.yui-nav, .yui-nav li { + margin:0; + padding:0; + list-style:none; +} +.yui-navset li em { font-style:normal; } + +.yui-navset { + position:relative; /* contain absolute positioned tabs (left/right) */ + zoom:1; +} + +.yui-navset .yui-content { zoom:1; } + +.yui-navset-top .yui-nav li, .yui-navset-bottom .yui-nav li { + display:inline-block; + display:-moz-inline-stack; + *display:inline; /* IE */ + vertical-align:bottom; /* safari: for overlap */ + cursor:pointer; /* gecko: due to -moz-inline-stack on anchor */ + zoom:1; /* IE: kill space between horizontal tabs */ +} + +.yui-navset .yui-nav a { + outline:0; /* gecko: keep from shifting */ +} + +.yui-navset .yui-nav a { position:relative; } /* IE: to allow overlap */ + +.yui-navset .yui-nav li a { + display:block; + zoom:1; +} + +.yui-navset-top .yui-nav li a, .yui-navset-bottom .yui-nav li a { + display:inline-block; + vertical-align:bottom; /* safari: for overlap */ +} + +.yui-navset-bottom .yui-nav li a { + vertical-align:text-top; /* for inline overlap (reverse for Op border bug) */ +} + +.yui-navset .yui-nav li a em { display:block; } + +/* position left and right oriented tabs */ +.yui-navset-left .yui-nav, .yui-navset-right .yui-nav { position:absolute; z-index:1; } +.yui-navset-left .yui-nav { left:0; } +.yui-navset-right .yui-nav { right:0; } diff --git a/source/web/scripts/ajax/yahoo/tabview/tabview-debug.js b/source/web/scripts/ajax/yahoo/tabview/tabview-debug.js new file mode 100644 index 0000000000..498fb0d90a --- /dev/null +++ b/source/web/scripts/ajax/yahoo/tabview/tabview-debug.js @@ -0,0 +1,1951 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ +(function() { + + YAHOO.util.Lang = { + isArray: function(val) { // frames lose type, so test constructor string + if (val.constructor && val.constructor.toString().indexOf('Array') > -1) { + return true; + } else { + return YAHOO.util.Lang.isObject(val) && val.constructor == Array; + } + }, + + isBoolean: function(val) { + return typeof val == 'boolean'; + }, + + isFunction: function(val) { + return typeof val == 'function'; + }, + + isNull: function(val) { + return val === null; + }, + + isNumber: function(val) { + return !isNaN(val); + }, + + isObject: function(val) { + return typeof val == 'object' || YAHOO.util.Lang.isFunction(val); + }, + + isString: function(val) { + return typeof val == 'string'; + }, + + isUndefined: function(val) { + return typeof val == 'undefined'; + } + }; +})();/** + * Provides Attribute configurations. + * @namespace YAHOO.util + * @class Attribute + * @constructor + * @param hash {Object} The intial Attribute. + * @param {YAHOO.util.AttributeProvider} The owner of the Attribute instance. + */ + +YAHOO.util.Attribute = function(hash, owner) { + if (owner) { + this.owner = owner; + this.configure(hash, true); + } +}; + +YAHOO.util.Attribute.prototype = { + /** + * The name of the attribute. + * @property name + * @type String + */ + name: undefined, + + /** + * The value of the attribute. + * @property value + * @type String + */ + value: null, + + /** + * The owner of the attribute. + * @property owner + * @type YAHOO.util.AttributeProvider + */ + owner: null, + + /** + * Whether or not the attribute is read only. + * @property readOnly + * @type Boolean + */ + readOnly: false, + + /** + * Whether or not the attribute can only be written once. + * @property writeOnce + * @type Boolean + */ + writeOnce: false, + + /** + * The attribute's initial configuration. + * @private + * @property _initialConfig + * @type Object + */ + _initialConfig: null, + + /** + * Whether or not the attribute's value has been set. + * @private + * @property _written + * @type Boolean + */ + _written: false, + + /** + * The method to use when setting the attribute's value. + * The method recieves the new value as the only argument. + * @property method + * @type Function + */ + method: null, + + /** + * The validator to use when setting the attribute's value. + * @property validator + * @type Function + * @return Boolean + */ + validator: null, + + /** + * Retrieves the current value of the attribute. + * @method getValue + * @return {any} The current value of the attribute. + */ + getValue: function() { + return this.value; + }, + + /** + * Sets the value of the attribute and fires beforeChange and change events. + * @method setValue + * @param {Any} value The value to apply to the attribute. + * @param {Boolean} silent If true the change events will not be fired. + * @return {Boolean} Whether or not the value was set. + */ + setValue: function(value, silent) { + var beforeRetVal; + var owner = this.owner; + var name = this.name; + + var event = { + type: name, + prevValue: this.getValue(), + newValue: value + }; + + if (this.readOnly || ( this.writeOnce && this._written) ) { + return false; // write not allowed + } + + if (this.validator && !this.validator.call(owner, value) ) { + return false; // invalid value + } + + if (!silent) { + beforeRetVal = owner.fireBeforeChangeEvent(event); + if (beforeRetVal === false) { + YAHOO.log('setValue ' + name + + 'cancelled by beforeChange event', 'info', 'Attribute'); + return false; + } + } + + if (this.method) { + this.method.call(owner, value); + } + + this.value = value; + this._written = true; + + event.type = name; + + if (!silent) { + this.owner.fireChangeEvent(event); + } + + return true; + }, + + /** + * Allows for configuring the Attribute's properties. + * @method configure + * @param {Object} map A key-value map of Attribute properties. + * @param {Boolean} init Whether or not this should become the initial config. + */ + configure: function(map, init) { + map = map || {}; + this._written = false; // reset writeOnce + this._initialConfig = this._initialConfig || {}; + + for (var key in map) { + if ( key && map.hasOwnProperty(key) ) { + this[key] = map[key]; + if (init) { + this._initialConfig[key] = map[key]; + } + } + } + }, + + /** + * Resets the value to the initial config value. + * @method resetValue + * @return {Boolean} Whether or not the value was set. + */ + resetValue: function() { + return this.setValue(this._initialConfig.value); + }, + + /** + * Resets the attribute config to the initial config state. + * @method resetConfig + */ + resetConfig: function() { + this.configure(this._initialConfig); + }, + + /** + * Resets the value to the current value. + * Useful when values may have gotten out of sync with actual properties. + * @method refresh + * @return {Boolean} Whether or not the value was set. + */ + refresh: function(silent) { + this.setValue(this.value, silent); + } +};(function() { + var Lang = YAHOO.util.Lang; + + /* + Copyright (c) 2006, Yahoo! Inc. All rights reserved. + Code licensed under the BSD License: + http://developer.yahoo.net/yui/license.txt + */ + + /** + * Provides and manages YAHOO.util.Attribute instances + * @namespace YAHOO.util + * @class AttributeProvider + * @uses YAHOO.util.EventProvider + */ + YAHOO.util.AttributeProvider = function() {}; + + YAHOO.util.AttributeProvider.prototype = { + + /** + * A key-value map of Attribute configurations + * @property _configs + * @protected (may be used by subclasses and augmentors) + * @private + * @type {Object} + */ + _configs: null, + /** + * Returns the current value of the attribute. + * @method get + * @param {String} key The attribute whose value will be returned. + */ + get: function(key){ + var configs = this._configs || {}; + var config = configs[key]; + + if (!config) { + YAHOO.log(key + ' not found', 'error', 'AttributeProvider'); + return undefined; + } + + return config.value; + }, + + /** + * Sets the value of a config. + * @method set + * @param {String} key The name of the attribute + * @param {Any} value The value to apply to the attribute + * @param {Boolean} silent Whether or not to suppress change events + * @return {Boolean} Whether or not the value was set. + */ + set: function(key, value, silent){ + var configs = this._configs || {}; + var config = configs[key]; + + if (!config) { + YAHOO.log('set failed: ' + key + ' not found', + 'error', 'AttributeProvider'); + return false; + } + + return config.setValue(value, silent); + }, + + /** + * Returns an array of attribute names. + * @method getAttributeKeys + * @return {Array} An array of attribute names. + */ + getAttributeKeys: function(){ + var configs = this._configs; + var keys = []; + var config; + for (var key in configs) { + config = configs[key]; + if ( configs.hasOwnProperty(key) && + !Lang.isUndefined(config) ) { + keys[keys.length] = key; + } + } + + return keys; + }, + + /** + * Sets multiple attribute values. + * @method setAttributes + * @param {Object} map A key-value map of attributes + * @param {Boolean} silent Whether or not to suppress change events + */ + setAttributes: function(map, silent){ + for (var key in map) { + if ( map.hasOwnProperty(key) ) { + this.set(key, map[key], silent); + } + } + }, + + /** + * Resets the specified attribute's value to its initial value. + * @method resetValue + * @param {String} key The name of the attribute + * @param {Boolean} silent Whether or not to suppress change events + * @return {Boolean} Whether or not the value was set + */ + resetValue: function(key, silent){ + var configs = this._configs || {}; + if (configs[key]) { + this.set(key, configs[key]._initialConfig.value, silent); + return true; + } + return false; + }, + + /** + * Sets the attribute's value to its current value. + * @method refresh + * @param {String | Array} key The attribute(s) to refresh + * @param {Boolean} silent Whether or not to suppress change events + */ + refresh: function(key, silent){ + var configs = this._configs; + + key = ( ( Lang.isString(key) ) ? [key] : key ) || + this.getAttributeKeys(); + + for (var i = 0, len = key.length; i < len; ++i) { + if ( // only set if there is a value and not null + configs[key[i]] && + ! Lang.isUndefined(configs[key[i]].value) && + ! Lang.isNull(configs[key[i]].value) ) { + configs[key[i]].refresh(silent); + } + } + }, + + /** + * Adds an Attribute to the AttributeProvider instance. + * @method register + * @param {String} key The attribute's name + * @param {Object} map A key-value map containing the + * attribute's properties. + */ + register: function(key, map) { + this._configs = this._configs || {}; + + if (this._configs[key]) { // dont override + return false; + } + + map.name = key; + this._configs[key] = new YAHOO.util.Attribute(map, this); + return true; + }, + + /** + * Returns the attribute's properties. + * @method getAttributeConfig + * @param {String} key The attribute's name + * @private + * @return {object} A key-value map containing all of the + * attribute's properties. + */ + getAttributeConfig: function(key) { + var configs = this._configs || {}; + var config = configs[key] || {}; + var map = {}; // returning a copy to prevent overrides + + for (key in config) { + if ( config.hasOwnProperty(key) ) { + map[key] = config[key]; + } + } + + return map; + }, + + /** + * Sets or updates an Attribute instance's properties. + * @method configureAttribute + * @param {String} key The attribute's name. + * @param {Object} map A key-value map of attribute properties + * @param {Boolean} init Whether or not this should become the intial config. + */ + configureAttribute: function(key, map, init) { + var configs = this._configs || {}; + + if (!configs[key]) { + YAHOO.log('unable to configure, ' + key + ' not found', + 'error', 'AttributeProvider'); + return false; + } + + configs[key].configure(map, init); + }, + + /** + * Resets an attribute to its intial configuration. + * @method resetAttributeConfig + * @param {String} key The attribute's name. + * @private + */ + resetAttributeConfig: function(key){ + var configs = this._configs || {}; + configs[key].resetConfig(); + }, + + /** + * Fires the attribute's beforeChange event. + * @method fireBeforeChangeEvent + * @param {String} key The attribute's name. + * @param {Obj} e The event object to pass to handlers. + */ + fireBeforeChangeEvent: function(e) { + var type = 'before'; + type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change'; + e.type = type; + return this.fireEvent(e.type, e); + }, + + /** + * Fires the attribute's change event. + * @method fireChangeEvent + * @param {String} key The attribute's name. + * @param {Obj} e The event object to pass to the handlers. + */ + fireChangeEvent: function(e) { + e.type += 'Change'; + return this.fireEvent(e.type, e); + } + }; + + YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider); +})();(function() { +// internal shorthand +var Dom = YAHOO.util.Dom, + Lang = YAHOO.util.Lang, + EventPublisher = YAHOO.util.EventPublisher, + AttributeProvider = YAHOO.util.AttributeProvider; + +/** + * Element provides an interface to an HTMLElement's attributes and common + * methods. Other commonly used attributes are added as well. + * @namespace YAHOO.util + * @class Element + * @uses YAHOO.util.AttributeProvider + * @constructor + * @param el {HTMLElement | String} The html element that + * represents the Element. + * @param {Object} map A key-value map of initial config names and values + */ +YAHOO.util.Element = function(el, map) { + if (arguments.length) { + this.init(el, map); + } +}; + +YAHOO.util.Element.prototype = { + /** + * Dom events supported by the Element instance. + * @property DOM_EVENTS + * @type Object + */ + DOM_EVENTS: null, + + /** + * Wrapper for HTMLElement method. + * @method appendChild + * @param {Boolean} deep Whether or not to do a deep clone + */ + appendChild: function(child) { + child = child.get ? child.get('element') : child; + this.get('element').appendChild(child); + }, + + /** + * Wrapper for HTMLElement method. + * @method getElementsByTagName + * @param {String} tag The tagName to collect + */ + getElementsByTagName: function(tag) { + return this.get('element').getElementsByTagName(tag); + }, + + /** + * Wrapper for HTMLElement method. + * @method hasChildNodes + * @return {Boolean} Whether or not the element has childNodes + */ + hasChildNodes: function() { + return this.get('element').hasChildNodes(); + }, + + /** + * Wrapper for HTMLElement method. + * @method insertBefore + * @param {HTMLElement} element The HTMLElement to insert + * @param {HTMLElement} before The HTMLElement to insert + * the element before. + */ + insertBefore: function(element, before) { + element = element.get ? element.get('element') : element; + before = (before && before.get) ? before.get('element') : before; + + this.get('element').insertBefore(element, before); + }, + + /** + * Wrapper for HTMLElement method. + * @method removeChild + * @param {HTMLElement} child The HTMLElement to remove + */ + removeChild: function(child) { + child = child.get ? child.get('element') : child; + this.get('element').removeChild(child); + return true; + }, + + /** + * Wrapper for HTMLElement method. + * @method replaceChild + * @param {HTMLElement} newNode The HTMLElement to insert + * @param {HTMLElement} oldNode The HTMLElement to replace + */ + replaceChild: function(newNode, oldNode) { + newNode = newNode.get ? newNode.get('element') : newNode; + oldNode = oldNode.get ? oldNode.get('element') : oldNode; + return this.get('element').replaceChild(newNode, oldNode); + }, + + + /** + * Registers Element specific attributes. + * @method initAttributes + * @param {Object} map A key-value map of initial attribute configs + */ + initAttributes: function(map) { + map = map || {}; + var element = Dom.get(map.element) || null; + + /** + * The HTMLElement the Element instance refers to. + * @config element + * @type HTMLElement + */ + this.register('element', { + value: element, + readOnly: true + }); + }, + + /** + * Adds a listener for the given event. These may be DOM or + * customEvent listeners. Any event that is fired via fireEvent + * can be listened for. All handlers receive an event object. + * @method addListener + * @param {String} type The name of the event to listen for + * @param {Function} fn The handler to call when the event fires + * @param {Any} obj A variable to pass to the handler + * @param {Object} scope The object to use for the scope of the handler + */ + addListener: function(type, fn, obj, scope) { + var el = this.get('element'); + var scope = scope || this; + + el = this.get('id') || el; + + if (!this._events[type]) { // create on the fly + if ( this.DOM_EVENTS[type] ) { + YAHOO.util.Event.addListener(el, type, function(e) { + if (e.srcElement && !e.target) { // supplement IE with target + e.target = e.srcElement; + } + this.fireEvent(type, e); + }, obj, scope); + } + + this.createEvent(type, this); + this._events[type] = true; + } + + this.subscribe.apply(this, arguments); // notify via customEvent + }, + + + /** + * Alias for addListener + * @method on + * @param {String} type The name of the event to listen for + * @param {Function} fn The function call when the event fires + * @param {Any} obj A variable to pass to the handler + * @param {Object} scope The object to use for the scope of the handler + */ + on: function() { this.addListener.apply(this, arguments); }, + + + /** + * Remove an event listener + * @method removeListener + * @param {String} type The name of the event to listen for + * @param {Function} fn The function call when the event fires + */ + removeListener: function(type, fn) { + this.unsubscribe.apply(this, arguments); + }, + + /** + * Wrapper for Dom method. + * @method addClass + * @param {String} className The className to add + */ + addClass: function(className) { + Dom.addClass(this.get('element'), className); + }, + + /** + * Wrapper for Dom method. + * @method getElementsByClassName + * @param {String} className The className to collect + * @param {String} tag (optional) The tag to use in + * conjunction with class name + * @return {Array} Array of HTMLElements + */ + getElementsByClassName: function(className, tag) { + return Dom.getElementsByClassName(className, tag, + this.get('element') ); + }, + + /** + * Wrapper for Dom method. + * @method hasClass + * @param {String} className The className to add + * @return {Boolean} Whether or not the element has the class name + */ + hasClass: function(className) { + return Dom.hasClass(this.get('element'), className); + }, + + /** + * Wrapper for Dom method. + * @method removeClass + * @param {String} className The className to remove + */ + removeClass: function(className) { + return Dom.removeClass(this.get('element'), className); + }, + + /** + * Wrapper for Dom method. + * @method replaceClass + * @param {String} oldClassName The className to replace + * @param {String} newClassName The className to add + */ + replaceClass: function(oldClassName, newClassName) { + return Dom.replaceClass(this.get('element'), + oldClassName, newClassName); + }, + + /** + * Wrapper for Dom method. + * @method setStyle + * @param {String} property The style property to set + * @param {String} value The value to apply to the style property + */ + setStyle: function(property, value) { + return Dom.setStyle(this.get('element'), property, value); + }, + + /** + * Wrapper for Dom method. + * @method getStyle + * @param {String} property The style property to retrieve + * @return {String} The current value of the property + */ + getStyle: function(property) { + return Dom.getStyle(this.get('element'), property); + }, + + /** + * Apply any queued set calls. + * @method fireQueue + */ + fireQueue: function() { + var queue = this._queue; + for (var i = 0, len = queue.length; i < len; ++i) { + this[queue[i][0]].apply(this, queue[i][1]); + } + }, + + /** + * Appends the HTMLElement into either the supplied parentNode. + * @method appendTo + * @param {HTMLElement | Element} parentNode The node to append to + * @param {HTMLElement | Element} before An optional node to insert before + */ + appendTo: function(parent, before) { + parent = (parent.get) ? parent.get('element') : Dom.get(parent); + + before = (before && before.get) ? + before.get('element') : Dom.get(before); + var element = this.get('element'); + + var newAddition = !Dom.inDocument(element); + + if (!element) { + YAHOO.log('appendTo failed: element not available', + 'error', 'Element'); + return false; + } + + if (!parent) { + YAHOO.log('appendTo failed: parent not available', + 'error', 'Element'); + return false; + } + + if (element.parent != parent) { + if (before) { + parent.insertBefore(element, before); + } else { + parent.appendChild(element); + } + } + + YAHOO.log(element + 'appended to ' + parent); + + if (!newAddition) { + return false; // note return; no refresh if in document + } + + // if a new addition, refresh HTMLElement any applied attributes + var keys = this.getAttributeKeys(); + + for (var key in keys) { // only refresh HTMLElement attributes + if ( !Lang.isUndefined(element[key]) ) { + this.refresh(key); + } + } + }, + + get: function(key) { + var configs = this._configs || {}; + var el = configs.element; // avoid loop due to 'element' + if (el && !configs[key] && !Lang.isUndefined(el.value[key]) ) { + return el.value[key]; + } + + return AttributeProvider.prototype.get.call(this, key); + }, + + set: function(key, value, silent) { + var el = this.get('element'); + if (!el) { + this._queue[key] = ['set', arguments]; + return false; + } + + // set it on the element if not a property + if ( !this._configs[key] && !Lang.isUndefined(el[key]) ) { + _registerHTMLAttr(this, key); + } + + return AttributeProvider.prototype.set.apply(this, arguments); + }, + + register: function(key) { // protect html attributes + var configs = this._configs || {}; + var element = this.get('element') || null; + + if ( element && !Lang.isUndefined(element[key]) ) { + YAHOO.log(key + ' is reserved for ' + element, + 'error', 'Element'); + return false; + } + + return AttributeProvider.prototype.register.apply(this, arguments); + }, + + configureAttribute: function(property, map, init) { // protect html attributes + if (!this._configs[property] && this._configs.element && + !Lang.isUndefined(this._configs.element[property]) ) { + _registerHTMLAttr(this, property, map); + return false; + } + + return AttributeProvider.prototype.configure.apply(this, arguments); + }, + + getAttributeKeys: function() { + var el = this.get('element'); + var keys = AttributeProvider.prototype.getAttributeKeys.call(this); + + //add any unconfigured element keys + for (var key in el) { + if (!this._configs[key]) { + keys[key] = keys[key] || el[key]; + } + } + + return keys; + }, + + init: function(el, attr) { + this._queue = this._queue || []; + this._events = this._events || {}; + this._configs = this._configs || {}; + attr = attr || {}; + attr.element = attr.element || el || null; + + this.DOM_EVENTS = { + 'click': true, + 'keydown': true, + 'keypress': true, + 'keyup': true, + 'mousedown': true, + 'mousemove': true, + 'mouseout': true, + 'mouseover': true, + 'mouseup': true + }; + + var readyHandler = function() { + this.initAttributes(attr); + + this.setAttributes(attr, true); + this.fireQueue(); + this.fireEvent('contentReady', { + type: 'contentReady', + target: attr.element + }); + }; + + if ( Lang.isString(el) ) { + _registerHTMLAttr(this, 'id', { value: el }); + YAHOO.util.Event.onAvailable(el, function() { + attr.element = Dom.get(el); + this.fireEvent('available', { + type: 'available', + target: attr.element + }); + }, this, true); + + YAHOO.util.Event.onContentReady(el, function() { + readyHandler.call(this); + }, this, true); + } else { + readyHandler.call(this); + } + } +}; + +/** + * Sets the value of the property and fires beforeChange and change events. + * @private + * @method _registerHTMLAttr + * @param {YAHOO.util.Element} element The Element instance to + * register the config to. + * @param {String} key The name of the config to register + * @param {Object} map A key-value map of the config's params + */ +var _registerHTMLAttr = function(self, key, map) { + var el = self.get('element'); + map = map || {}; + map.name = key; + map.method = map.method || function(value) { + el[key] = value; + }; + map.value = map.value || el[key]; + self._configs[key] = new YAHOO.util.Attribute(map, self); +}; + +/** + * Fires when the Element's HTMLElement can be retrieved by Id. + *See: Element.addListener
+ *Event fields:
+ *<String> type
available
+ *<HTMLElement> + * target
the HTMLElement bound to this Element instance
+ *Usage:
+ * @event available + */ + +/** + * Fires when the Element's HTMLElement subtree is rendered. + *
+ *var handler = function(e) {var target = e.target};
+ * myTabs.addListener('available', handler);See: Element.addListener
+ *Event fields:
+ *<String> type
contentReady
+ *<HTMLElement> + * target
the HTMLElement bound to this Element instance
+ *Usage:
+ * @event contentReady + */ + + +YAHOO.augment(YAHOO.util.Element, AttributeProvider); +})();(function() { + var Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + Lang = YAHOO.util.Lang; + + /** + * A representation of a Tab's label and content. + * @namespace YAHOO.widget + * @class Tab + * @extends YAHOO.util.Element + * @constructor + * @param element {HTMLElement | String} (optional) The html element that + * represents the TabView. An element will be created if none provided. + * @param {Object} properties A key map of initial properties + */ + Tab = function(el, attr) { + attr = attr || {}; + if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) { + attr = el; + el = attr.element; + } + + if (!el && !attr.element) { + el = _createTabElement.call(this, attr); + } + + this.loadHandler = { + success: function(o) { + this.set('content', o.responseText); + }, + failure: function(o) { + YAHOO.log('loading failed: ' + o.statusText, + 'error', 'Tab'); + } + }; + + Tab.superclass.constructor.call(this, el, attr); + + this.DOM_EVENTS = {}; // delegating to tabView + }; + + YAHOO.extend(Tab, YAHOO.util.Element); + var proto = Tab.prototype; + + /** + * The default tag name for a Tab's inner element. + * @property LABEL_INNER_TAGNAME + * @type String + * @default "em" + */ + proto.LABEL_TAGNAME = 'em'; + + /** + * The class name applied to active tabs. + * @property ACTIVE_CLASSNAME + * @type String + * @default "on" + */ + proto.ACTIVE_CLASSNAME = 'selected'; + + /** + * The class name applied to disabled tabs. + * @property DISABLED_CLASSNAME + * @type String + * @default "disabled" + */ + proto.DISABLED_CLASSNAME = 'disabled'; + + /** + * The class name applied to dynamic tabs while loading. + * @property LOADING_CLASSNAME + * @type String + * @default "disabled" + */ + proto.LOADING_CLASSNAME = 'loading'; + + /** + * Provides a reference to the connection request object when data is + * loaded dynamically. + * @property dataConnection + * @type Object + */ + proto.dataConnection = null; + + /** + * Object containing success and failure callbacks for loading data. + * @property loadHandler + * @type object + */ + proto.loadHandler = null; + + /** + * Provides a readable name for the tab. + * @method toString + * @return String + */ + proto.toString = function() { + var el = this.get('element'); + var id = el.id || el.tagName; + return "Tab " + id; + }; + + /** + * Registers TabView specific properties. + * @method initAttributes + * @param {Object} attr Hash of initial attributes + */ + proto.initAttributes = function(attr) { + attr = attr || {}; + Tab.superclass.initAttributes.call(this, attr); + + var el = this.get('element'); + + /** + * The event that triggers the tab's activation. + * @config activationEvent + * @type String + */ + this.register('activationEvent', { + value: attr.activationEvent || 'click' + }); + + /** + * The element that contains the tab's label. + * @config labelEl + * @type HTMLElement + */ + this.register('labelEl', { + value: attr.labelEl || _getlabelEl.call(this), + method: function(value) { + var current = this.get('labelEl'); + + if (current) { + if (current == value) { + return false; // already set + } + + this.replaceChild(value, current); + } else if (el.firstChild) { // ensure label is firstChild by default + this.insertBefore(value, el.firstChild); + } else { + this.appendChild(value); + } + } + }); + + /** + * The tab's label text (or innerHTML). + * @config label + * @type String + */ + this.register('label', { + value: attr.label || _getLabel.call(this), + method: function(value) { + var labelEl = this.get('labelEl'); + if (!labelEl) { // create if needed + this.set('labelEl', _createlabelEl.call(this)); + } + + _setLabel.call(this, value); + } + }); + + /** + * The HTMLElement that contains the tab's content. + * @config contentEl + * @type HTMLElement + */ + this.register('contentEl', { // TODO: apply className? + value: attr.contentEl || document.createElement('div'), + method: function(value) { + var current = this.get('contentEl'); + + if (current) { + if (current == value) { + return false; // already set + } + this.replaceChild(value, current); + } + } + }); + + /** + * The tab's content. + * @config content + * @type String + */ + this.register('content', { + value: attr.content, // TODO: what about existing? + method: function(value) { + this.get('contentEl').innerHTML = value; + } + }); + + var _dataLoaded = false; + + /** + * The tab's data source, used for loading content dynamically. + * @config dataSrc + * @type String + */ + this.register('dataSrc', { + value: attr.dataSrc + }); + + /** + * Whether or not content should be reloaded for every view. + * @config cacheData + * @type Boolean + * @default false + */ + this.register('cacheData', { + value: attr.cacheData || false, + validator: Lang.isBoolean + }); + + /** + * The method to use for the data request. + * @config loadMethod + * @type String + * @default "GET" + */ + this.register('loadMethod', { + value: attr.loadMethod || 'GET', + validator: Lang.isString + }); + + /** + * Whether or not any data has been loaded from the server. + * @config dataLoaded + * @type Boolean + */ + this.register('dataLoaded', { + value: false, + validator: Lang.isBoolean, + writeOnce: true + }); + + /** + * Number if milliseconds before aborting and calling failure handler. + * @config dataTimeout + * @type Number + * @default null + */ + this.register('dataTimeout', { + value: attr.dataTimeout || null, + validator: Lang.isNumber + }); + + /** + * Whether or not the tab is currently active. + * If a dataSrc is set for the tab, the content will be loaded from + * the given source. + * @config active + * @type Boolean + */ + this.register('active', { + value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME), + method: function(value) { + if (value === true) { + this.addClass(this.ACTIVE_CLASSNAME); + this.set('title', 'active'); + } else { + this.removeClass(this.ACTIVE_CLASSNAME); + this.set('title', ''); + } + }, + validator: function(value) { + return Lang.isBoolean(value) && !this.get('disabled') ; + } + }); + + /** + * Whether or not the tab is disabled. + * @config disabled + * @type Boolean + */ + this.register('disabled', { + value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME), + method: function(value) { + if (value === true) { + Dom.addClass(this.get('element'), this.DISABLED_CLASSNAME); + } else { + Dom.removeClass(this.get('element'), this.DISABLED_CLASSNAME); + } + }, + validator: Lang.isBoolean + }); + + /** + * The href of the tab's anchor element. + * @config href + * @type String + * @default '#' + */ + this.register('href', { + value: attr.href || '#', + method: function(value) { + this.getElementsByTagName('a')[0].href = value; + }, + validator: Lang.isString + }); + + /** + * The Whether or not the tab's content is visible. + * @config contentVisible + * @type Boolean + * @default false + */ + this.register('contentVisible', { + value: attr.contentVisible, + method: function(value) { + if (value == true) { + this.get('contentEl').style.display = 'block'; + + if ( this.get('dataSrc') ) { + // load dynamic content unless already loaded and caching + if ( !this.get('dataLoaded') || !this.get('cacheData') ) { + _dataConnect.call(this); + } + } + } else { + this.get('contentEl').style.display = 'none'; + } + }, + validator: Lang.isBoolean + }); + }; + + var _createTabElement = function(attr) { + var el = document.createElement('li'); + var a = document.createElement('a'); + + a.href = attr.href || '#'; + + el.appendChild(a); + + var label = attr.label || null; + var labelEl = attr.labelEl || null; + + if (labelEl) { // user supplied labelEl + if (!label) { // user supplied label + label = _getLabel.call(this, labelEl); + } + } else { + labelEl = _createlabelEl.call(this); + } + + a.appendChild(labelEl); + + return el; + }; + + var _getlabelEl = function() { + return this.getElementsByTagName(this.LABEL_TAGNAME)[0]; + }; + + var _createlabelEl = function() { + var el = document.createElement(this.LABEL_TAGNAME); + return el; + }; + + var _setLabel = function(label) { + var el = this.get('labelEl'); + el.innerHTML = label; + }; + + var _getLabel = function() { + var label, + el = this.get('labelEl'); + + if (!el) { + return undefined; + } + + return el.innerHTML; + }; + + var _dataConnect = function() { + if (!YAHOO.util.Connect) { + YAHOO.log('YAHOO.util.Connect dependency not met', + 'error', 'Tab'); + return false; + } + + Dom.addClass(this.get('contentEl').parentNode, this.LOADING_CLASSNAME); + + this.dataConnection = YAHOO.util.Connect.asyncRequest( + this.get('loadMethod'), + this.get('dataSrc'), + { + success: function(o) { + this.loadHandler.success.call(this, o); + this.set('dataLoaded', true); + this.dataConnection = null; + Dom.removeClass(this.get('contentEl').parentNode, + this.LOADING_CLASSNAME); + }, + failure: function(o) { + this.loadHandler.failure.call(this, o); + this.dataConnection = null; + Dom.removeClass(this.get('contentEl').parentNode, + this.LOADING_CLASSNAME); + }, + scope: this, + timeout: this.get('dataTimeout') + } + ); + }; + + YAHOO.widget.Tab = Tab; + + /** + * Fires before the active state is changed. + *
+ *var handler = function(e) {var target = e.target};
+ * myTabs.addListener('contentReady', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeActiveChange
+ *<Boolean> + * prevValue
the current value
+ *<Boolean> + * newValue
the new valueUsage:
+ * @event beforeActiveChange + */ + + /** + * Fires after the active state is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeActiveChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
activeChange
+ *<Boolean> + * prevValue
the previous value
+ *<Boolean> + * newValue
the updated valueUsage:
+ * @event activeChange + */ + + /** + * Fires before the tab label is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('activeChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeLabelChange
+ *<String> + * prevValue
the current value
+ *<String> + * newValue
the new valueUsage:
+ * @event beforeLabelChange + */ + + /** + * Fires after the tab label is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeLabelChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
labelChange
+ *<String> + * prevValue
the previous value
+ *<String> + * newValue
the updated valueUsage:
+ * @event labelChange + */ + + /** + * Fires before the tab content is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('labelChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeContentChange
+ *<String> + * prevValue
the current value
+ *<String> + * newValue
the new valueUsage:
+ * @event beforeContentChange + */ + + /** + * Fires after the tab content is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeContentChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
contentChange
+ *<String> + * prevValue
the previous value
+ *<Boolean> + * newValue
the updated valueUsage:
+ * @event contentChange + */ +})();(function() { + + /** + * The tabview module provides a widget for managing content bound to tabs. + * @module tabview + * + */ + /** + * A widget to control tabbed views. + * @namespace YAHOO.widget + * @class TabView + * @extends YAHOO.util.Element + * @constructor + * @param {HTMLElement | String | Object} el(optional) The html + * element that represents the TabView, or the attribute object to use. + * An element will be created if none provided. + * @param {Object} attr (optional) A key map of the tabView's + * initial attributes. Ignored if first arg is attributes object. + */ + YAHOO.widget.TabView = function(el, attr) { + attr = attr || {}; + if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) { + attr = el; // treat first arg as attr object + el = attr.element || null; + } + + if (!el && !attr.element) { // create if we dont have one + el = _createTabViewElement.call(this, attr); + } + YAHOO.widget.TabView.superclass.constructor.call(this, el, attr); + }; + + YAHOO.extend(YAHOO.widget.TabView, YAHOO.util.Element); + + var proto = YAHOO.widget.TabView.prototype; + var Dom = YAHOO.util.Dom; + var Lang = YAHOO.util.Lang; + var Event = YAHOO.util.Event; + var Tab = YAHOO.widget.Tab; + + + /** + * The className to add when building from scratch. + * @property CLASSNAME + * @default "navset" + */ + proto.CLASSNAME = 'yui-navset'; + + /** + * The className of the HTMLElement containing the TabView's tab elements + * to look for when building from existing markup, or to add when building + * from scratch. + * All childNodes of the tab container are treated as Tabs when building + * from existing markup. + * @property TAB_PARENT_CLASSNAME + * @default "nav" + */ + proto.TAB_PARENT_CLASSNAME = 'yui-nav'; + + /** + * The className of the HTMLElement containing the TabView's label elements + * to look for when building from existing markup, or to add when building + * from scratch. + * All childNodes of the content container are treated as content elements when + * building from existing markup. + * @property CONTENT_PARENT_CLASSNAME + * @default "nav-content" + */ + proto.CONTENT_PARENT_CLASSNAME = 'yui-content'; + + proto._tabParent = null; + proto._contentParent = null; + + /** + * Adds a Tab to the TabView instance. + * If no index is specified, the tab is added to the end of the tab list. + * @method addTab + * @param {YAHOO.widget.Tab} tab A Tab instance to add. + * @param {Integer} index The position to add the tab. + * @return void + */ + proto.addTab = function(tab, index) { + var tabs = this.get('tabs'); + if (!tabs) { // not ready yet + this._queue[this._queue.length] = ['addTab', arguments]; + return false; + } + + index = (index === undefined) ? tabs.length : index; + + var before = this.getTab(index); + + var self = this; + var el = this.get('element'); + var tabParent = this._tabParent; + var contentParent = this._contentParent; + + var tabElement = tab.get('element'); + var contentEl = tab.get('contentEl'); + + if ( before ) { + tabParent.insertBefore(tabElement, before.get('element')); + } else { + tabParent.appendChild(tabElement); + } + + if ( contentEl && !Dom.isAncestor(contentParent, contentEl) ) { + contentParent.appendChild(contentEl); + } + + if ( !tab.get('active') ) { + tab.set('contentVisible', false, true); /* hide if not active */ + } else { + this.set('activeTab', tab, true); + + } + + var activate = function(e) { + YAHOO.util.Event.preventDefault(e); + self.set('activeTab', this); + }; + + tab.addListener( tab.get('activationEvent'), activate); + + tab.addListener('activationEventChange', function(e) { + if (e.prevValue != e.newValue) { + tab.removeListener(e.prevValue, activate); + tab.addListener(e.newValue, activate); + } + }); + + tabs.splice(index, 0, tab); + }; + + /** + * Routes childNode events. + * @method DOMEventHandler + * @param {event} e The Dom event that is being handled. + * @return void + */ + proto.DOMEventHandler = function(e) { + var el = this.get('element'); + var target = YAHOO.util.Event.getTarget(e); + var tabParent = this._tabParent; + + if (Dom.isAncestor(tabParent, target) ) { + var tabEl; + var tab = null; + var contentEl; + var tabs = this.get('tabs'); + + for (var i = 0, len = tabs.length; i < len; i++) { + tabEl = tabs[i].get('element'); + contentEl = tabs[i].get('contentEl'); + + if ( target == tabEl || Dom.isAncestor(tabEl, target) ) { + tab = tabs[i]; + break; // note break + } + } + + if (tab) { + tab.fireEvent(e.type, e); + } + } + }; + + /** + * Returns the Tab instance at the specified index. + * @method getTab + * @param {Integer} index The position of the Tab. + * @return YAHOO.widget.Tab + */ + proto.getTab = function(index) { + return this.get('tabs')[index]; + }; + + /** + * Returns the index of given tab. + * @method getTabIndex + * @param {YAHOO.widget.Tab} tab The tab whose index will be returned. + * @return int + */ + proto.getTabIndex = function(tab) { + var index = null; + var tabs = this.get('tabs'); + for (var i = 0, len = tabs.length; i < len; ++i) { + if (tab == tabs[i]) { + index = i; + break; + } + } + + return index; + }; + + /** + * Removes the specified Tab from the TabView. + * @method removeTab + * @param {YAHOO.widget.Tab} item The Tab instance to be removed. + * @return void + */ + proto.removeTab = function(tab) { + var tabCount = this.get('tabs').length; + + var index = this.getTabIndex(tab); + var nextIndex = index + 1; + if ( tab == this.get('activeTab') ) { // select next tab + if (tabCount > 1) { + if (index + 1 == tabCount) { + this.set('activeIndex', index - 1); + } else { + this.set('activeIndex', index + 1); + } + } + } + + this._tabParent.removeChild( tab.get('element') ); + this._contentParent.removeChild( tab.get('contentEl') ); + this._configs.tabs.value.splice(index, 1); + + }; + + /** + * Provides a readable name for the TabView instance. + * @method toString + * @return String + */ + proto.toString = function() { + var name = this.get('id') || this.get('tagName'); + return "TabView " + name; + }; + + /** + * The transiton to use when switching between tabs. + * @method contentTransition + */ + proto.contentTransition = function(newTab, oldTab) { + newTab.set('contentVisible', true); + oldTab.set('contentVisible', false); + }; + + /** + * Registers TabView specific properties. + * @method initAttributes + * @param {Object} attr Hash of initial attributes + */ + proto.initAttributes = function(attr) { + YAHOO.widget.TabView.superclass.initAttributes.call(this, attr); + + if (!attr.orientation) { + attr.orientation = 'top'; + } + + var el = this.get('element'); + + /** + * The Tabs belonging to the TabView instance. + * @config tabs + * @type Array + */ + this.register('tabs', { + value: [], + readOnly: true + }); + + /** + * The container of the tabView's label elements. + * @property _tabParent + * @private + * @type HTMLElement + */ + this._tabParent = + this.getElementsByClassName(this.TAB_PARENT_CLASSNAME, + 'ul' )[0] || _createTabParent.call(this); + + /** + * The container of the tabView's content elements. + * @property _contentParent + * @type HTMLElement + * @private + */ + this._contentParent = + this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME, + 'div')[0] || _createContentParent.call(this); + + /** + * How the Tabs should be oriented relative to the TabView. + * @config orientation + * @type String + * @default "top" + */ + this.register('orientation', { + value: attr.orientation, + method: function(value) { + var current = this.get('orientation'); + this.addClass('yui-navset-' + value); + + if (current != value) { + this.removeClass('yui-navset-' + current); + } + + switch(value) { + case 'bottom': + this.appendChild(this._tabParent); + break; + } + } + }); + + /** + * The index of the tab currently active. + * @config activeIndex + * @type Int + */ + this.register('activeIndex', { + value: attr.activeIndex, + method: function(value) { + this.set('activeTab', this.getTab(value)); + }, + validator: function(value) { + return !this.getTab(value).get('disabled'); // cannot activate if disabled + } + }); + + /** + * The tab currently active. + * @config activeTab + * @type YAHOO.widget.Tab + */ + this.register('activeTab', { + value: attr.activeTab, + method: function(tab) { + var activeTab = this.get('activeTab'); + + if (tab) { + tab.set('active', true); + } + + if (activeTab && activeTab != tab) { + activeTab.set('active', false); + } + + if (activeTab && tab != activeTab) { // no transition if only 1 + this.contentTransition(tab, activeTab); + } else if (tab) { + tab.set('contentVisible', true); + } + }, + validator: function(value) { + return !value.get('disabled'); // cannot activate if disabled + } + }); + + if ( this._tabParent ) { + _initTabs.call(this); + } + + for (var type in this.DOM_EVENTS) { + if ( this.DOM_EVENTS.hasOwnProperty(type) ) { + this.addListener.call(this, type, this.DOMEventHandler); + } + } + }; + + /** + * Creates Tab instances from a collection of HTMLElements. + * @method createTabs + * @private + * @param {Array|HTMLCollection} elements The elements to use for Tabs. + * @return void + */ + var _initTabs = function() { + var tab, + attr, + contentEl; + + var el = this.get('element'); + var tabs = _getChildNodes(this._tabParent); + var contentElements = _getChildNodes(this._contentParent); + + for (var i = 0, len = tabs.length; i < len; ++i) { + attr = {}; + + if (contentElements[i]) { + attr.contentEl = contentElements[i]; + } + + tab = new YAHOO.widget.Tab(tabs[i], attr); + this.addTab(tab); + + if (tab.hasClass(tab.ACTIVE_CLASSNAME) ) { + this._configs.activeTab.value = tab; // dont invoke method + } + } + }; + + var _createTabViewElement = function(attr) { + var el = document.createElement('div'); + + if ( this.CLASSNAME ) { + el.className = this.CLASSNAME; + } + + return el; + }; + + var _createTabParent = function(attr) { + var el = document.createElement('ul'); + + if ( this.TAB_PARENT_CLASSNAME ) { + el.className = this.TAB_PARENT_CLASSNAME; + } + + this.get('element').appendChild(el); + + return el; + }; + + var _createContentParent = function(attr) { + var el = document.createElement('div'); + + if ( this.CONTENT_PARENT_CLASSNAME ) { + el.className = this.CONTENT_PARENT_CLASSNAME; + } + + this.get('element').appendChild(el); + + return el; + }; + + var _getChildNodes = function(el) { + var nodes = []; + var childNodes = el.childNodes; + + for (var i = 0, len = childNodes.length; i < len; ++i) { + if (childNodes[i].nodeType == 1) { + nodes[nodes.length] = childNodes[i]; + } + } + + return nodes; + }; + +/** + * Fires before the activeTab is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('contentChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeActiveTabChange
+ *<YAHOO.widget.Tab> + * prevValue
the currently active tab
+ *<YAHOO.widget.Tab> + * newValue
the tab to be made activeUsage:
+ * @event beforeActiveTabChange + */ + +/** + * Fires after the activeTab is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeActiveTabChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
activeTabChange
+ *<YAHOO.widget.Tab> + * prevValue
the formerly active tab
+ *<YAHOO.widget.Tab> + * newValue
the new active tabUsage:
+ * @event activeTabChange + */ + +/** + * Fires before the orientation is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('activeTabChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeOrientationChange
+ *<String> + * prevValue
the current orientation
+ *<String> + * newValue
the new orientation to be appliedUsage:
+ * @event beforeOrientationChange + */ + +/** + * Fires after the orientation is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeOrientationChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
orientationChange
+ *<String> + * prevValue
the former orientation
+ *<String> + * newValue
the new orientationUsage:
+ * @event orientationChange + */ +})(); \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/tabview/tabview-min.js b/source/web/scripts/ajax/yahoo/tabview/tabview-min.js new file mode 100644 index 0000000000..e893de7588 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/tabview/tabview-min.js @@ -0,0 +1 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ (function(){YAHOO.util.Lang={isArray:function(val){if(val.constructor&&val.constructor.toString().indexOf('Array')>-1){return true;}else{return YAHOO.util.Lang.isObject(val)&&val.constructor==Array;}},isBoolean:function(val){return typeof val=='boolean';},isFunction:function(val){return typeof val=='function';},isNull:function(val){return val===null;},isNumber:function(val){return!isNaN(val);},isObject:function(val){return typeof val=='object'||YAHOO.util.Lang.isFunction(val);},isString:function(val){return typeof val=='string';},isUndefined:function(val){return typeof val=='undefined';}};})();YAHOO.util.Attribute=function(hash,owner){if(owner){this.owner=owner;this.configure(hash,true);}};YAHOO.util.Attribute.prototype={name:undefined,value:null,owner:null,readOnly:false,writeOnce:false,_initialConfig:null,_written:false,method:null,validator:null,getValue:function(){return this.value;},setValue:function(value,silent){var beforeRetVal;var owner=this.owner;var name=this.name;var event={type:name,prevValue:this.getValue(),newValue:value};if(this.readOnly||(this.writeOnce&&this._written)){return false;}if(this.validator&&!this.validator.call(owner,value)){return false;}if(!silent){beforeRetVal=owner.fireBeforeChangeEvent(event);if(beforeRetVal===false){YAHOO.log('setValue '+name+'cancelled by beforeChange event','info','Attribute');return false;}}if(this.method){this.method.call(owner,value);}this.value=value;this._written=true;event.type=name;if(!silent){this.owner.fireChangeEvent(event);}return true;},configure:function(map,init){map=map||{};this._written=false;this._initialConfig=this._initialConfig||{};for(var key in map){if(key&&map.hasOwnProperty(key)){this[key]=map[key];if(init){this._initialConfig[key]=map[key];}}}},resetValue:function(){return this.setValue(this._initialConfig.value);},resetConfig:function(){this.configure(this._initialConfig);},refresh:function(silent){this.setValue(this.value,silent);}};(function(){var Lang=YAHOO.util.Lang;YAHOO.util.AttributeProvider=function(){};YAHOO.util.AttributeProvider.prototype={_configs:null,get:function(key){var configs=this._configs||{};var config=configs[key];if(!config){YAHOO.log(key+' not found','error','AttributeProvider');return undefined;}return config.value;},set:function(key,value,silent){var configs=this._configs||{};var config=configs[key];if(!config){YAHOO.log('set failed: '+key+' not found','error','AttributeProvider');return false;}return config.setValue(value,silent);},getAttributeKeys:function(){var configs=this._configs;var keys=[];var config;for(var key in configs){config=configs[key];if(configs.hasOwnProperty(key)&&!Lang.isUndefined(config)){keys[keys.length]=key;}}return keys;},setAttributes:function(map,silent){for(var key in map){if(map.hasOwnProperty(key)){this.set(key,map[key],silent);}}},resetValue:function(key,silent){var configs=this._configs||{};if(configs[key]){this.set(key,configs[key]._initialConfig.value,silent);return true;}return false;},refresh:function(key,silent){var configs=this._configs;key=((Lang.isString(key))?[key]:key)||this.getAttributeKeys();for(var i=0,len=key.length;i
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('orientationChange', handler);1){if(index+1==tabCount){this.set('activeIndex',index-1);}else{this.set('activeIndex',index+1);}}}this._tabParent.removeChild(tab.get('element'));this._contentParent.removeChild(tab.get('contentEl'));this._configs.tabs.value.splice(index,1);};proto.toString=function(){var name=this.get('id')||this.get('tagName');return"TabView "+name;};proto.contentTransition=function(newTab,oldTab){newTab.set('contentVisible',true);oldTab.set('contentVisible',false);};proto.initAttributes=function(attr){YAHOO.widget.TabView.superclass.initAttributes.call(this,attr);if(!attr.orientation){attr.orientation='top';}var el=this.get('element');this.register('tabs',{value:[],readOnly:true});this._tabParent=this.getElementsByClassName(this.TAB_PARENT_CLASSNAME,'ul')[0]||_createTabParent.call(this);this._contentParent=this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME,'div')[0]||_createContentParent.call(this);this.register('orientation',{value:attr.orientation,method:function(value){var current=this.get('orientation');this.addClass('yui-navset-'+value);if(current!=value){this.removeClass('yui-navset-'+current);}switch(value){case'bottom':this.appendChild(this._tabParent);break;}}});this.register('activeIndex',{value:attr.activeIndex,method:function(value){this.set('activeTab',this.getTab(value));},validator:function(value){return!this.getTab(value).get('disabled');}});this.register('activeTab',{value:attr.activeTab,method:function(tab){var activeTab=this.get('activeTab');if(tab){tab.set('active',true);}if(activeTab&&activeTab!=tab){activeTab.set('active',false);}if(activeTab&&tab!=activeTab){this.contentTransition(tab,activeTab);}else if(tab){tab.set('contentVisible',true);}},validator:function(value){return!value.get('disabled');}});if(this._tabParent){_initTabs.call(this);}for(var type in this.DOM_EVENTS){if(this.DOM_EVENTS.hasOwnProperty(type)){this.addListener.call(this,type,this.DOMEventHandler);}}};var _initTabs=function(){var tab,attr,contentEl;var el=this.get('element');var tabs=_getChildNodes(this._tabParent);var contentElements=_getChildNodes(this._contentParent);for(var i=0,len=tabs.length;i -1) { + return true; + } else { + return YAHOO.util.Lang.isObject(val) && val.constructor == Array; + } + }, + + isBoolean: function(val) { + return typeof val == 'boolean'; + }, + + isFunction: function(val) { + return typeof val == 'function'; + }, + + isNull: function(val) { + return val === null; + }, + + isNumber: function(val) { + return !isNaN(val); + }, + + isObject: function(val) { + return typeof val == 'object' || YAHOO.util.Lang.isFunction(val); + }, + + isString: function(val) { + return typeof val == 'string'; + }, + + isUndefined: function(val) { + return typeof val == 'undefined'; + } + }; +})();/** + * Provides Attribute configurations. + * @namespace YAHOO.util + * @class Attribute + * @constructor + * @param hash {Object} The intial Attribute. + * @param {YAHOO.util.AttributeProvider} The owner of the Attribute instance. + */ + +YAHOO.util.Attribute = function(hash, owner) { + if (owner) { + this.owner = owner; + this.configure(hash, true); + } +}; + +YAHOO.util.Attribute.prototype = { + /** + * The name of the attribute. + * @property name + * @type String + */ + name: undefined, + + /** + * The value of the attribute. + * @property value + * @type String + */ + value: null, + + /** + * The owner of the attribute. + * @property owner + * @type YAHOO.util.AttributeProvider + */ + owner: null, + + /** + * Whether or not the attribute is read only. + * @property readOnly + * @type Boolean + */ + readOnly: false, + + /** + * Whether or not the attribute can only be written once. + * @property writeOnce + * @type Boolean + */ + writeOnce: false, + + /** + * The attribute's initial configuration. + * @private + * @property _initialConfig + * @type Object + */ + _initialConfig: null, + + /** + * Whether or not the attribute's value has been set. + * @private + * @property _written + * @type Boolean + */ + _written: false, + + /** + * The method to use when setting the attribute's value. + * The method recieves the new value as the only argument. + * @property method + * @type Function + */ + method: null, + + /** + * The validator to use when setting the attribute's value. + * @property validator + * @type Function + * @return Boolean + */ + validator: null, + + /** + * Retrieves the current value of the attribute. + * @method getValue + * @return {any} The current value of the attribute. + */ + getValue: function() { + return this.value; + }, + + /** + * Sets the value of the attribute and fires beforeChange and change events. + * @method setValue + * @param {Any} value The value to apply to the attribute. + * @param {Boolean} silent If true the change events will not be fired. + * @return {Boolean} Whether or not the value was set. + */ + setValue: function(value, silent) { + var beforeRetVal; + var owner = this.owner; + var name = this.name; + + var event = { + type: name, + prevValue: this.getValue(), + newValue: value + }; + + if (this.readOnly || ( this.writeOnce && this._written) ) { + return false; // write not allowed + } + + if (this.validator && !this.validator.call(owner, value) ) { + return false; // invalid value + } + + if (!silent) { + beforeRetVal = owner.fireBeforeChangeEvent(event); + if (beforeRetVal === false) { + YAHOO.log('setValue ' + name + + 'cancelled by beforeChange event', 'info', 'Attribute'); + return false; + } + } + + if (this.method) { + this.method.call(owner, value); + } + + this.value = value; + this._written = true; + + event.type = name; + + if (!silent) { + this.owner.fireChangeEvent(event); + } + + return true; + }, + + /** + * Allows for configuring the Attribute's properties. + * @method configure + * @param {Object} map A key-value map of Attribute properties. + * @param {Boolean} init Whether or not this should become the initial config. + */ + configure: function(map, init) { + map = map || {}; + this._written = false; // reset writeOnce + this._initialConfig = this._initialConfig || {}; + + for (var key in map) { + if ( key && map.hasOwnProperty(key) ) { + this[key] = map[key]; + if (init) { + this._initialConfig[key] = map[key]; + } + } + } + }, + + /** + * Resets the value to the initial config value. + * @method resetValue + * @return {Boolean} Whether or not the value was set. + */ + resetValue: function() { + return this.setValue(this._initialConfig.value); + }, + + /** + * Resets the attribute config to the initial config state. + * @method resetConfig + */ + resetConfig: function() { + this.configure(this._initialConfig); + }, + + /** + * Resets the value to the current value. + * Useful when values may have gotten out of sync with actual properties. + * @method refresh + * @return {Boolean} Whether or not the value was set. + */ + refresh: function(silent) { + this.setValue(this.value, silent); + } +};(function() { + var Lang = YAHOO.util.Lang; + + /* + Copyright (c) 2006, Yahoo! Inc. All rights reserved. + Code licensed under the BSD License: + http://developer.yahoo.net/yui/license.txt + */ + + /** + * Provides and manages YAHOO.util.Attribute instances + * @namespace YAHOO.util + * @class AttributeProvider + * @uses YAHOO.util.EventProvider + */ + YAHOO.util.AttributeProvider = function() {}; + + YAHOO.util.AttributeProvider.prototype = { + + /** + * A key-value map of Attribute configurations + * @property _configs + * @protected (may be used by subclasses and augmentors) + * @private + * @type {Object} + */ + _configs: null, + /** + * Returns the current value of the attribute. + * @method get + * @param {String} key The attribute whose value will be returned. + */ + get: function(key){ + var configs = this._configs || {}; + var config = configs[key]; + + if (!config) { + YAHOO.log(key + ' not found', 'error', 'AttributeProvider'); + return undefined; + } + + return config.value; + }, + + /** + * Sets the value of a config. + * @method set + * @param {String} key The name of the attribute + * @param {Any} value The value to apply to the attribute + * @param {Boolean} silent Whether or not to suppress change events + * @return {Boolean} Whether or not the value was set. + */ + set: function(key, value, silent){ + var configs = this._configs || {}; + var config = configs[key]; + + if (!config) { + YAHOO.log('set failed: ' + key + ' not found', + 'error', 'AttributeProvider'); + return false; + } + + return config.setValue(value, silent); + }, + + /** + * Returns an array of attribute names. + * @method getAttributeKeys + * @return {Array} An array of attribute names. + */ + getAttributeKeys: function(){ + var configs = this._configs; + var keys = []; + var config; + for (var key in configs) { + config = configs[key]; + if ( configs.hasOwnProperty(key) && + !Lang.isUndefined(config) ) { + keys[keys.length] = key; + } + } + + return keys; + }, + + /** + * Sets multiple attribute values. + * @method setAttributes + * @param {Object} map A key-value map of attributes + * @param {Boolean} silent Whether or not to suppress change events + */ + setAttributes: function(map, silent){ + for (var key in map) { + if ( map.hasOwnProperty(key) ) { + this.set(key, map[key], silent); + } + } + }, + + /** + * Resets the specified attribute's value to its initial value. + * @method resetValue + * @param {String} key The name of the attribute + * @param {Boolean} silent Whether or not to suppress change events + * @return {Boolean} Whether or not the value was set + */ + resetValue: function(key, silent){ + var configs = this._configs || {}; + if (configs[key]) { + this.set(key, configs[key]._initialConfig.value, silent); + return true; + } + return false; + }, + + /** + * Sets the attribute's value to its current value. + * @method refresh + * @param {String | Array} key The attribute(s) to refresh + * @param {Boolean} silent Whether or not to suppress change events + */ + refresh: function(key, silent){ + var configs = this._configs; + + key = ( ( Lang.isString(key) ) ? [key] : key ) || + this.getAttributeKeys(); + + for (var i = 0, len = key.length; i < len; ++i) { + if ( // only set if there is a value and not null + configs[key[i]] && + ! Lang.isUndefined(configs[key[i]].value) && + ! Lang.isNull(configs[key[i]].value) ) { + configs[key[i]].refresh(silent); + } + } + }, + + /** + * Adds an Attribute to the AttributeProvider instance. + * @method register + * @param {String} key The attribute's name + * @param {Object} map A key-value map containing the + * attribute's properties. + */ + register: function(key, map) { + this._configs = this._configs || {}; + + if (this._configs[key]) { // dont override + return false; + } + + map.name = key; + this._configs[key] = new YAHOO.util.Attribute(map, this); + return true; + }, + + /** + * Returns the attribute's properties. + * @method getAttributeConfig + * @param {String} key The attribute's name + * @private + * @return {object} A key-value map containing all of the + * attribute's properties. + */ + getAttributeConfig: function(key) { + var configs = this._configs || {}; + var config = configs[key] || {}; + var map = {}; // returning a copy to prevent overrides + + for (key in config) { + if ( config.hasOwnProperty(key) ) { + map[key] = config[key]; + } + } + + return map; + }, + + /** + * Sets or updates an Attribute instance's properties. + * @method configureAttribute + * @param {String} key The attribute's name. + * @param {Object} map A key-value map of attribute properties + * @param {Boolean} init Whether or not this should become the intial config. + */ + configureAttribute: function(key, map, init) { + var configs = this._configs || {}; + + if (!configs[key]) { + YAHOO.log('unable to configure, ' + key + ' not found', + 'error', 'AttributeProvider'); + return false; + } + + configs[key].configure(map, init); + }, + + /** + * Resets an attribute to its intial configuration. + * @method resetAttributeConfig + * @param {String} key The attribute's name. + * @private + */ + resetAttributeConfig: function(key){ + var configs = this._configs || {}; + configs[key].resetConfig(); + }, + + /** + * Fires the attribute's beforeChange event. + * @method fireBeforeChangeEvent + * @param {String} key The attribute's name. + * @param {Obj} e The event object to pass to handlers. + */ + fireBeforeChangeEvent: function(e) { + var type = 'before'; + type += e.type.charAt(0).toUpperCase() + e.type.substr(1) + 'Change'; + e.type = type; + return this.fireEvent(e.type, e); + }, + + /** + * Fires the attribute's change event. + * @method fireChangeEvent + * @param {String} key The attribute's name. + * @param {Obj} e The event object to pass to the handlers. + */ + fireChangeEvent: function(e) { + e.type += 'Change'; + return this.fireEvent(e.type, e); + } + }; + + YAHOO.augment(YAHOO.util.AttributeProvider, YAHOO.util.EventProvider); +})();(function() { +// internal shorthand +var Dom = YAHOO.util.Dom, + Lang = YAHOO.util.Lang, + EventPublisher = YAHOO.util.EventPublisher, + AttributeProvider = YAHOO.util.AttributeProvider; + +/** + * Element provides an interface to an HTMLElement's attributes and common + * methods. Other commonly used attributes are added as well. + * @namespace YAHOO.util + * @class Element + * @uses YAHOO.util.AttributeProvider + * @constructor + * @param el {HTMLElement | String} The html element that + * represents the Element. + * @param {Object} map A key-value map of initial config names and values + */ +YAHOO.util.Element = function(el, map) { + if (arguments.length) { + this.init(el, map); + } +}; + +YAHOO.util.Element.prototype = { + /** + * Dom events supported by the Element instance. + * @property DOM_EVENTS + * @type Object + */ + DOM_EVENTS: null, + + /** + * Wrapper for HTMLElement method. + * @method appendChild + * @param {Boolean} deep Whether or not to do a deep clone + */ + appendChild: function(child) { + child = child.get ? child.get('element') : child; + this.get('element').appendChild(child); + }, + + /** + * Wrapper for HTMLElement method. + * @method getElementsByTagName + * @param {String} tag The tagName to collect + */ + getElementsByTagName: function(tag) { + return this.get('element').getElementsByTagName(tag); + }, + + /** + * Wrapper for HTMLElement method. + * @method hasChildNodes + * @return {Boolean} Whether or not the element has childNodes + */ + hasChildNodes: function() { + return this.get('element').hasChildNodes(); + }, + + /** + * Wrapper for HTMLElement method. + * @method insertBefore + * @param {HTMLElement} element The HTMLElement to insert + * @param {HTMLElement} before The HTMLElement to insert + * the element before. + */ + insertBefore: function(element, before) { + element = element.get ? element.get('element') : element; + before = (before && before.get) ? before.get('element') : before; + + this.get('element').insertBefore(element, before); + }, + + /** + * Wrapper for HTMLElement method. + * @method removeChild + * @param {HTMLElement} child The HTMLElement to remove + */ + removeChild: function(child) { + child = child.get ? child.get('element') : child; + this.get('element').removeChild(child); + return true; + }, + + /** + * Wrapper for HTMLElement method. + * @method replaceChild + * @param {HTMLElement} newNode The HTMLElement to insert + * @param {HTMLElement} oldNode The HTMLElement to replace + */ + replaceChild: function(newNode, oldNode) { + newNode = newNode.get ? newNode.get('element') : newNode; + oldNode = oldNode.get ? oldNode.get('element') : oldNode; + return this.get('element').replaceChild(newNode, oldNode); + }, + + + /** + * Registers Element specific attributes. + * @method initAttributes + * @param {Object} map A key-value map of initial attribute configs + */ + initAttributes: function(map) { + map = map || {}; + var element = Dom.get(map.element) || null; + + /** + * The HTMLElement the Element instance refers to. + * @config element + * @type HTMLElement + */ + this.register('element', { + value: element, + readOnly: true + }); + }, + + /** + * Adds a listener for the given event. These may be DOM or + * customEvent listeners. Any event that is fired via fireEvent + * can be listened for. All handlers receive an event object. + * @method addListener + * @param {String} type The name of the event to listen for + * @param {Function} fn The handler to call when the event fires + * @param {Any} obj A variable to pass to the handler + * @param {Object} scope The object to use for the scope of the handler + */ + addListener: function(type, fn, obj, scope) { + var el = this.get('element'); + var scope = scope || this; + + el = this.get('id') || el; + + if (!this._events[type]) { // create on the fly + if ( this.DOM_EVENTS[type] ) { + YAHOO.util.Event.addListener(el, type, function(e) { + if (e.srcElement && !e.target) { // supplement IE with target + e.target = e.srcElement; + } + this.fireEvent(type, e); + }, obj, scope); + } + + this.createEvent(type, this); + this._events[type] = true; + } + + this.subscribe.apply(this, arguments); // notify via customEvent + }, + + + /** + * Alias for addListener + * @method on + * @param {String} type The name of the event to listen for + * @param {Function} fn The function call when the event fires + * @param {Any} obj A variable to pass to the handler + * @param {Object} scope The object to use for the scope of the handler + */ + on: function() { this.addListener.apply(this, arguments); }, + + + /** + * Remove an event listener + * @method removeListener + * @param {String} type The name of the event to listen for + * @param {Function} fn The function call when the event fires + */ + removeListener: function(type, fn) { + this.unsubscribe.apply(this, arguments); + }, + + /** + * Wrapper for Dom method. + * @method addClass + * @param {String} className The className to add + */ + addClass: function(className) { + Dom.addClass(this.get('element'), className); + }, + + /** + * Wrapper for Dom method. + * @method getElementsByClassName + * @param {String} className The className to collect + * @param {String} tag (optional) The tag to use in + * conjunction with class name + * @return {Array} Array of HTMLElements + */ + getElementsByClassName: function(className, tag) { + return Dom.getElementsByClassName(className, tag, + this.get('element') ); + }, + + /** + * Wrapper for Dom method. + * @method hasClass + * @param {String} className The className to add + * @return {Boolean} Whether or not the element has the class name + */ + hasClass: function(className) { + return Dom.hasClass(this.get('element'), className); + }, + + /** + * Wrapper for Dom method. + * @method removeClass + * @param {String} className The className to remove + */ + removeClass: function(className) { + return Dom.removeClass(this.get('element'), className); + }, + + /** + * Wrapper for Dom method. + * @method replaceClass + * @param {String} oldClassName The className to replace + * @param {String} newClassName The className to add + */ + replaceClass: function(oldClassName, newClassName) { + return Dom.replaceClass(this.get('element'), + oldClassName, newClassName); + }, + + /** + * Wrapper for Dom method. + * @method setStyle + * @param {String} property The style property to set + * @param {String} value The value to apply to the style property + */ + setStyle: function(property, value) { + return Dom.setStyle(this.get('element'), property, value); + }, + + /** + * Wrapper for Dom method. + * @method getStyle + * @param {String} property The style property to retrieve + * @return {String} The current value of the property + */ + getStyle: function(property) { + return Dom.getStyle(this.get('element'), property); + }, + + /** + * Apply any queued set calls. + * @method fireQueue + */ + fireQueue: function() { + var queue = this._queue; + for (var i = 0, len = queue.length; i < len; ++i) { + this[queue[i][0]].apply(this, queue[i][1]); + } + }, + + /** + * Appends the HTMLElement into either the supplied parentNode. + * @method appendTo + * @param {HTMLElement | Element} parentNode The node to append to + * @param {HTMLElement | Element} before An optional node to insert before + */ + appendTo: function(parent, before) { + parent = (parent.get) ? parent.get('element') : Dom.get(parent); + + before = (before && before.get) ? + before.get('element') : Dom.get(before); + var element = this.get('element'); + + var newAddition = !Dom.inDocument(element); + + if (!element) { + YAHOO.log('appendTo failed: element not available', + 'error', 'Element'); + return false; + } + + if (!parent) { + YAHOO.log('appendTo failed: parent not available', + 'error', 'Element'); + return false; + } + + if (element.parent != parent) { + if (before) { + parent.insertBefore(element, before); + } else { + parent.appendChild(element); + } + } + + YAHOO.log(element + 'appended to ' + parent); + + if (!newAddition) { + return false; // note return; no refresh if in document + } + + // if a new addition, refresh HTMLElement any applied attributes + var keys = this.getAttributeKeys(); + + for (var key in keys) { // only refresh HTMLElement attributes + if ( !Lang.isUndefined(element[key]) ) { + this.refresh(key); + } + } + }, + + get: function(key) { + var configs = this._configs || {}; + var el = configs.element; // avoid loop due to 'element' + if (el && !configs[key] && !Lang.isUndefined(el.value[key]) ) { + return el.value[key]; + } + + return AttributeProvider.prototype.get.call(this, key); + }, + + set: function(key, value, silent) { + var el = this.get('element'); + if (!el) { + this._queue[key] = ['set', arguments]; + return false; + } + + // set it on the element if not a property + if ( !this._configs[key] && !Lang.isUndefined(el[key]) ) { + _registerHTMLAttr(this, key); + } + + return AttributeProvider.prototype.set.apply(this, arguments); + }, + + register: function(key) { // protect html attributes + var configs = this._configs || {}; + var element = this.get('element') || null; + + if ( element && !Lang.isUndefined(element[key]) ) { + YAHOO.log(key + ' is reserved for ' + element, + 'error', 'Element'); + return false; + } + + return AttributeProvider.prototype.register.apply(this, arguments); + }, + + configureAttribute: function(property, map, init) { // protect html attributes + if (!this._configs[property] && this._configs.element && + !Lang.isUndefined(this._configs.element[property]) ) { + _registerHTMLAttr(this, property, map); + return false; + } + + return AttributeProvider.prototype.configure.apply(this, arguments); + }, + + getAttributeKeys: function() { + var el = this.get('element'); + var keys = AttributeProvider.prototype.getAttributeKeys.call(this); + + //add any unconfigured element keys + for (var key in el) { + if (!this._configs[key]) { + keys[key] = keys[key] || el[key]; + } + } + + return keys; + }, + + init: function(el, attr) { + this._queue = this._queue || []; + this._events = this._events || {}; + this._configs = this._configs || {}; + attr = attr || {}; + attr.element = attr.element || el || null; + + this.DOM_EVENTS = { + 'click': true, + 'keydown': true, + 'keypress': true, + 'keyup': true, + 'mousedown': true, + 'mousemove': true, + 'mouseout': true, + 'mouseover': true, + 'mouseup': true + }; + + var readyHandler = function() { + this.initAttributes(attr); + + this.setAttributes(attr, true); + this.fireQueue(); + this.fireEvent('contentReady', { + type: 'contentReady', + target: attr.element + }); + }; + + if ( Lang.isString(el) ) { + _registerHTMLAttr(this, 'id', { value: el }); + YAHOO.util.Event.onAvailable(el, function() { + attr.element = Dom.get(el); + this.fireEvent('available', { + type: 'available', + target: attr.element + }); + }, this, true); + + YAHOO.util.Event.onContentReady(el, function() { + readyHandler.call(this); + }, this, true); + } else { + readyHandler.call(this); + } + } +}; + +/** + * Sets the value of the property and fires beforeChange and change events. + * @private + * @method _registerHTMLAttr + * @param {YAHOO.util.Element} element The Element instance to + * register the config to. + * @param {String} key The name of the config to register + * @param {Object} map A key-value map of the config's params + */ +var _registerHTMLAttr = function(self, key, map) { + var el = self.get('element'); + map = map || {}; + map.name = key; + map.method = map.method || function(value) { + el[key] = value; + }; + map.value = map.value || el[key]; + self._configs[key] = new YAHOO.util.Attribute(map, self); +}; + +/** + * Fires when the Element's HTMLElement can be retrieved by Id. + * See: Element.addListener
+ *Event fields:
+ *<String> type
available
+ *<HTMLElement> + * target
the HTMLElement bound to this Element instance
+ *Usage:
+ * @event available + */ + +/** + * Fires when the Element's HTMLElement subtree is rendered. + *
+ *var handler = function(e) {var target = e.target};
+ * myTabs.addListener('available', handler);See: Element.addListener
+ *Event fields:
+ *<String> type
contentReady
+ *<HTMLElement> + * target
the HTMLElement bound to this Element instance
+ *Usage:
+ * @event contentReady + */ + +YAHOO.augment(YAHOO.util.Element, AttributeProvider); +})();(function() { + var Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + Lang = YAHOO.util.Lang; + + /** + * A representation of a Tab's label and content. + * @namespace YAHOO.widget + * @class Tab + * @extends YAHOO.util.Element + * @constructor + * @param element {HTMLElement | String} (optional) The html element that + * represents the TabView. An element will be created if none provided. + * @param {Object} properties A key map of initial properties + */ + Tab = function(el, attr) { + attr = attr || {}; + if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) { + attr = el; + el = attr.element; + } + + if (!el && !attr.element) { + el = _createTabElement.call(this, attr); + } + + this.loadHandler = { + success: function(o) { + this.set('content', o.responseText); + }, + failure: function(o) { + YAHOO.log('loading failed: ' + o.statusText, + 'error', 'Tab'); + } + }; + + Tab.superclass.constructor.call(this, el, attr); + + this.DOM_EVENTS = {}; // delegating to tabView + }; + + YAHOO.extend(Tab, YAHOO.util.Element); + var proto = Tab.prototype; + + /** + * The default tag name for a Tab's inner element. + * @property LABEL_INNER_TAGNAME + * @type String + * @default "em" + */ + proto.LABEL_TAGNAME = 'em'; + + /** + * The class name applied to active tabs. + * @property ACTIVE_CLASSNAME + * @type String + * @default "on" + */ + proto.ACTIVE_CLASSNAME = 'selected'; + + /** + * The class name applied to disabled tabs. + * @property DISABLED_CLASSNAME + * @type String + * @default "disabled" + */ + proto.DISABLED_CLASSNAME = 'disabled'; + + /** + * The class name applied to dynamic tabs while loading. + * @property LOADING_CLASSNAME + * @type String + * @default "disabled" + */ + proto.LOADING_CLASSNAME = 'loading'; + + /** + * Provides a reference to the connection request object when data is + * loaded dynamically. + * @property dataConnection + * @type Object + */ + proto.dataConnection = null; + + /** + * Object containing success and failure callbacks for loading data. + * @property loadHandler + * @type object + */ + proto.loadHandler = null; + + /** + * Provides a readable name for the tab. + * @method toString + * @return String + */ + proto.toString = function() { + var el = this.get('element'); + var id = el.id || el.tagName; + return "Tab " + id; + }; + + /** + * Registers TabView specific properties. + * @method initAttributes + * @param {Object} attr Hash of initial attributes + */ + proto.initAttributes = function(attr) { + attr = attr || {}; + Tab.superclass.initAttributes.call(this, attr); + + var el = this.get('element'); + + /** + * The event that triggers the tab's activation. + * @config activationEvent + * @type String + */ + this.register('activationEvent', { + value: attr.activationEvent || 'click' + }); + + /** + * The element that contains the tab's label. + * @config labelEl + * @type HTMLElement + */ + this.register('labelEl', { + value: attr.labelEl || _getlabelEl.call(this), + method: function(value) { + var current = this.get('labelEl'); + + if (current) { + if (current == value) { + return false; // already set + } + + this.replaceChild(value, current); + } else if (el.firstChild) { // ensure label is firstChild by default + this.insertBefore(value, el.firstChild); + } else { + this.appendChild(value); + } + } + }); + + /** + * The tab's label text (or innerHTML). + * @config label + * @type String + */ + this.register('label', { + value: attr.label || _getLabel.call(this), + method: function(value) { + var labelEl = this.get('labelEl'); + if (!labelEl) { // create if needed + this.set('labelEl', _createlabelEl.call(this)); + } + + _setLabel.call(this, value); + } + }); + + /** + * The HTMLElement that contains the tab's content. + * @config contentEl + * @type HTMLElement + */ + this.register('contentEl', { // TODO: apply className? + value: attr.contentEl || document.createElement('div'), + method: function(value) { + var current = this.get('contentEl'); + + if (current) { + if (current == value) { + return false; // already set + } + this.replaceChild(value, current); + } + } + }); + + /** + * The tab's content. + * @config content + * @type String + */ + this.register('content', { + value: attr.content, // TODO: what about existing? + method: function(value) { + this.get('contentEl').innerHTML = value; + } + }); + + var _dataLoaded = false; + + /** + * The tab's data source, used for loading content dynamically. + * @config dataSrc + * @type String + */ + this.register('dataSrc', { + value: attr.dataSrc + }); + + /** + * Whether or not content should be reloaded for every view. + * @config cacheData + * @type Boolean + * @default false + */ + this.register('cacheData', { + value: attr.cacheData || false, + validator: Lang.isBoolean + }); + + /** + * The method to use for the data request. + * @config loadMethod + * @type String + * @default "GET" + */ + this.register('loadMethod', { + value: attr.loadMethod || 'GET', + validator: Lang.isString + }); + + /** + * Whether or not any data has been loaded from the server. + * @config dataLoaded + * @type Boolean + */ + this.register('dataLoaded', { + value: false, + validator: Lang.isBoolean, + writeOnce: true + }); + + /** + * Number if milliseconds before aborting and calling failure handler. + * @config dataTimeout + * @type Number + * @default null + */ + this.register('dataTimeout', { + value: attr.dataTimeout || null, + validator: Lang.isNumber + }); + + /** + * Whether or not the tab is currently active. + * If a dataSrc is set for the tab, the content will be loaded from + * the given source. + * @config active + * @type Boolean + */ + this.register('active', { + value: attr.active || this.hasClass(this.ACTIVE_CLASSNAME), + method: function(value) { + if (value === true) { + this.addClass(this.ACTIVE_CLASSNAME); + this.set('title', 'active'); + } else { + this.removeClass(this.ACTIVE_CLASSNAME); + this.set('title', ''); + } + }, + validator: function(value) { + return Lang.isBoolean(value) && !this.get('disabled') ; + } + }); + + /** + * Whether or not the tab is disabled. + * @config disabled + * @type Boolean + */ + this.register('disabled', { + value: attr.disabled || this.hasClass(this.DISABLED_CLASSNAME), + method: function(value) { + if (value === true) { + Dom.addClass(this.get('element'), this.DISABLED_CLASSNAME); + } else { + Dom.removeClass(this.get('element'), this.DISABLED_CLASSNAME); + } + }, + validator: Lang.isBoolean + }); + + /** + * The href of the tab's anchor element. + * @config href + * @type String + * @default '#' + */ + this.register('href', { + value: attr.href || '#', + method: function(value) { + this.getElementsByTagName('a')[0].href = value; + }, + validator: Lang.isString + }); + + /** + * The Whether or not the tab's content is visible. + * @config contentVisible + * @type Boolean + * @default false + */ + this.register('contentVisible', { + value: attr.contentVisible, + method: function(value) { + if (value == true) { + this.get('contentEl').style.display = 'block'; + + if ( this.get('dataSrc') ) { + // load dynamic content unless already loaded and caching + if ( !this.get('dataLoaded') || !this.get('cacheData') ) { + _dataConnect.call(this); + } + } + } else { + this.get('contentEl').style.display = 'none'; + } + }, + validator: Lang.isBoolean + }); + }; + + var _createTabElement = function(attr) { + var el = document.createElement('li'); + var a = document.createElement('a'); + + a.href = attr.href || '#'; + + el.appendChild(a); + + var label = attr.label || null; + var labelEl = attr.labelEl || null; + + if (labelEl) { // user supplied labelEl + if (!label) { // user supplied label + label = _getLabel.call(this, labelEl); + } + } else { + labelEl = _createlabelEl.call(this); + } + + a.appendChild(labelEl); + + return el; + }; + + var _getlabelEl = function() { + return this.getElementsByTagName(this.LABEL_TAGNAME)[0]; + }; + + var _createlabelEl = function() { + var el = document.createElement(this.LABEL_TAGNAME); + return el; + }; + + var _setLabel = function(label) { + var el = this.get('labelEl'); + el.innerHTML = label; + }; + + var _getLabel = function() { + var label, + el = this.get('labelEl'); + + if (!el) { + return undefined; + } + + return el.innerHTML; + }; + + var _dataConnect = function() { + if (!YAHOO.util.Connect) { + YAHOO.log('YAHOO.util.Connect dependency not met', + 'error', 'Tab'); + return false; + } + + Dom.addClass(this.get('contentEl').parentNode, this.LOADING_CLASSNAME); + + this.dataConnection = YAHOO.util.Connect.asyncRequest( + this.get('loadMethod'), + this.get('dataSrc'), + { + success: function(o) { + this.loadHandler.success.call(this, o); + this.set('dataLoaded', true); + this.dataConnection = null; + Dom.removeClass(this.get('contentEl').parentNode, + this.LOADING_CLASSNAME); + }, + failure: function(o) { + this.loadHandler.failure.call(this, o); + this.dataConnection = null; + Dom.removeClass(this.get('contentEl').parentNode, + this.LOADING_CLASSNAME); + }, + scope: this, + timeout: this.get('dataTimeout') + } + ); + }; + + YAHOO.widget.Tab = Tab; + + /** + * Fires before the active state is changed. + *
+ *var handler = function(e) {var target = e.target};
+ * myTabs.addListener('contentReady', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeActiveChange
+ *<Boolean> + * prevValue
the current value
+ *<Boolean> + * newValue
the new valueUsage:
+ * @event beforeActiveChange + */ + + /** + * Fires after the active state is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeActiveChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
activeChange
+ *<Boolean> + * prevValue
the previous value
+ *<Boolean> + * newValue
the updated valueUsage:
+ * @event activeChange + */ + + /** + * Fires before the tab label is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('activeChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeLabelChange
+ *<String> + * prevValue
the current value
+ *<String> + * newValue
the new valueUsage:
+ * @event beforeLabelChange + */ + + /** + * Fires after the tab label is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeLabelChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
labelChange
+ *<String> + * prevValue
the previous value
+ *<String> + * newValue
the updated valueUsage:
+ * @event labelChange + */ + + /** + * Fires before the tab content is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('labelChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeContentChange
+ *<String> + * prevValue
the current value
+ *<String> + * newValue
the new valueUsage:
+ * @event beforeContentChange + */ + + /** + * Fires after the tab content is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeContentChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
contentChange
+ *<String> + * prevValue
the previous value
+ *<Boolean> + * newValue
the updated valueUsage:
+ * @event contentChange + */ +})();(function() { + + /** + * The tabview module provides a widget for managing content bound to tabs. + * @module tabview + * + */ + /** + * A widget to control tabbed views. + * @namespace YAHOO.widget + * @class TabView + * @extends YAHOO.util.Element + * @constructor + * @param {HTMLElement | String | Object} el(optional) The html + * element that represents the TabView, or the attribute object to use. + * An element will be created if none provided. + * @param {Object} attr (optional) A key map of the tabView's + * initial attributes. Ignored if first arg is attributes object. + */ + YAHOO.widget.TabView = function(el, attr) { + attr = attr || {}; + if (arguments.length == 1 && !Lang.isString(el) && !el.nodeName) { + attr = el; // treat first arg as attr object + el = attr.element || null; + } + + if (!el && !attr.element) { // create if we dont have one + el = _createTabViewElement.call(this, attr); + } + YAHOO.widget.TabView.superclass.constructor.call(this, el, attr); + }; + + YAHOO.extend(YAHOO.widget.TabView, YAHOO.util.Element); + + var proto = YAHOO.widget.TabView.prototype; + var Dom = YAHOO.util.Dom; + var Lang = YAHOO.util.Lang; + var Event = YAHOO.util.Event; + var Tab = YAHOO.widget.Tab; + + + /** + * The className to add when building from scratch. + * @property CLASSNAME + * @default "navset" + */ + proto.CLASSNAME = 'yui-navset'; + + /** + * The className of the HTMLElement containing the TabView's tab elements + * to look for when building from existing markup, or to add when building + * from scratch. + * All childNodes of the tab container are treated as Tabs when building + * from existing markup. + * @property TAB_PARENT_CLASSNAME + * @default "nav" + */ + proto.TAB_PARENT_CLASSNAME = 'yui-nav'; + + /** + * The className of the HTMLElement containing the TabView's label elements + * to look for when building from existing markup, or to add when building + * from scratch. + * All childNodes of the content container are treated as content elements when + * building from existing markup. + * @property CONTENT_PARENT_CLASSNAME + * @default "nav-content" + */ + proto.CONTENT_PARENT_CLASSNAME = 'yui-content'; + + proto._tabParent = null; + proto._contentParent = null; + + /** + * Adds a Tab to the TabView instance. + * If no index is specified, the tab is added to the end of the tab list. + * @method addTab + * @param {YAHOO.widget.Tab} tab A Tab instance to add. + * @param {Integer} index The position to add the tab. + * @return void + */ + proto.addTab = function(tab, index) { + var tabs = this.get('tabs'); + if (!tabs) { // not ready yet + this._queue[this._queue.length] = ['addTab', arguments]; + return false; + } + + index = (index === undefined) ? tabs.length : index; + + var before = this.getTab(index); + + var self = this; + var el = this.get('element'); + var tabParent = this._tabParent; + var contentParent = this._contentParent; + + var tabElement = tab.get('element'); + var contentEl = tab.get('contentEl'); + + if ( before ) { + tabParent.insertBefore(tabElement, before.get('element')); + } else { + tabParent.appendChild(tabElement); + } + + if ( contentEl && !Dom.isAncestor(contentParent, contentEl) ) { + contentParent.appendChild(contentEl); + } + + if ( !tab.get('active') ) { + tab.set('contentVisible', false, true); /* hide if not active */ + } else { + this.set('activeTab', tab, true); + + } + + var activate = function(e) { + YAHOO.util.Event.preventDefault(e); + self.set('activeTab', this); + }; + + tab.addListener( tab.get('activationEvent'), activate); + + tab.addListener('activationEventChange', function(e) { + if (e.prevValue != e.newValue) { + tab.removeListener(e.prevValue, activate); + tab.addListener(e.newValue, activate); + } + }); + + tabs.splice(index, 0, tab); + }; + + /** + * Routes childNode events. + * @method DOMEventHandler + * @param {event} e The Dom event that is being handled. + * @return void + */ + proto.DOMEventHandler = function(e) { + var el = this.get('element'); + var target = YAHOO.util.Event.getTarget(e); + var tabParent = this._tabParent; + + if (Dom.isAncestor(tabParent, target) ) { + var tabEl; + var tab = null; + var contentEl; + var tabs = this.get('tabs'); + + for (var i = 0, len = tabs.length; i < len; i++) { + tabEl = tabs[i].get('element'); + contentEl = tabs[i].get('contentEl'); + + if ( target == tabEl || Dom.isAncestor(tabEl, target) ) { + tab = tabs[i]; + break; // note break + } + } + + if (tab) { + tab.fireEvent(e.type, e); + } + } + }; + + /** + * Returns the Tab instance at the specified index. + * @method getTab + * @param {Integer} index The position of the Tab. + * @return YAHOO.widget.Tab + */ + proto.getTab = function(index) { + return this.get('tabs')[index]; + }; + + /** + * Returns the index of given tab. + * @method getTabIndex + * @param {YAHOO.widget.Tab} tab The tab whose index will be returned. + * @return int + */ + proto.getTabIndex = function(tab) { + var index = null; + var tabs = this.get('tabs'); + for (var i = 0, len = tabs.length; i < len; ++i) { + if (tab == tabs[i]) { + index = i; + break; + } + } + + return index; + }; + + /** + * Removes the specified Tab from the TabView. + * @method removeTab + * @param {YAHOO.widget.Tab} item The Tab instance to be removed. + * @return void + */ + proto.removeTab = function(tab) { + var tabCount = this.get('tabs').length; + + var index = this.getTabIndex(tab); + var nextIndex = index + 1; + if ( tab == this.get('activeTab') ) { // select next tab + if (tabCount > 1) { + if (index + 1 == tabCount) { + this.set('activeIndex', index - 1); + } else { + this.set('activeIndex', index + 1); + } + } + } + + this._tabParent.removeChild( tab.get('element') ); + this._contentParent.removeChild( tab.get('contentEl') ); + this._configs.tabs.value.splice(index, 1); + + }; + + /** + * Provides a readable name for the TabView instance. + * @method toString + * @return String + */ + proto.toString = function() { + var name = this.get('id') || this.get('tagName'); + return "TabView " + name; + }; + + /** + * The transiton to use when switching between tabs. + * @method contentTransition + */ + proto.contentTransition = function(newTab, oldTab) { + newTab.set('contentVisible', true); + oldTab.set('contentVisible', false); + }; + + /** + * Registers TabView specific properties. + * @method initAttributes + * @param {Object} attr Hash of initial attributes + */ + proto.initAttributes = function(attr) { + YAHOO.widget.TabView.superclass.initAttributes.call(this, attr); + + if (!attr.orientation) { + attr.orientation = 'top'; + } + + var el = this.get('element'); + + /** + * The Tabs belonging to the TabView instance. + * @config tabs + * @type Array + */ + this.register('tabs', { + value: [], + readOnly: true + }); + + /** + * The container of the tabView's label elements. + * @property _tabParent + * @private + * @type HTMLElement + */ + this._tabParent = + this.getElementsByClassName(this.TAB_PARENT_CLASSNAME, + 'ul' )[0] || _createTabParent.call(this); + + /** + * The container of the tabView's content elements. + * @property _contentParent + * @type HTMLElement + * @private + */ + this._contentParent = + this.getElementsByClassName(this.CONTENT_PARENT_CLASSNAME, + 'div')[0] || _createContentParent.call(this); + + /** + * How the Tabs should be oriented relative to the TabView. + * @config orientation + * @type String + * @default "top" + */ + this.register('orientation', { + value: attr.orientation, + method: function(value) { + var current = this.get('orientation'); + this.addClass('yui-navset-' + value); + + if (current != value) { + this.removeClass('yui-navset-' + current); + } + + switch(value) { + case 'bottom': + this.appendChild(this._tabParent); + break; + } + } + }); + + /** + * The index of the tab currently active. + * @config activeIndex + * @type Int + */ + this.register('activeIndex', { + value: attr.activeIndex, + method: function(value) { + this.set('activeTab', this.getTab(value)); + }, + validator: function(value) { + return !this.getTab(value).get('disabled'); // cannot activate if disabled + } + }); + + /** + * The tab currently active. + * @config activeTab + * @type YAHOO.widget.Tab + */ + this.register('activeTab', { + value: attr.activeTab, + method: function(tab) { + var activeTab = this.get('activeTab'); + + if (tab) { + tab.set('active', true); + } + + if (activeTab && activeTab != tab) { + activeTab.set('active', false); + } + + if (activeTab && tab != activeTab) { // no transition if only 1 + this.contentTransition(tab, activeTab); + } else if (tab) { + tab.set('contentVisible', true); + } + }, + validator: function(value) { + return !value.get('disabled'); // cannot activate if disabled + } + }); + + if ( this._tabParent ) { + _initTabs.call(this); + } + + for (var type in this.DOM_EVENTS) { + if ( this.DOM_EVENTS.hasOwnProperty(type) ) { + this.addListener.call(this, type, this.DOMEventHandler); + } + } + }; + + /** + * Creates Tab instances from a collection of HTMLElements. + * @method createTabs + * @private + * @param {Array|HTMLCollection} elements The elements to use for Tabs. + * @return void + */ + var _initTabs = function() { + var tab, + attr, + contentEl; + + var el = this.get('element'); + var tabs = _getChildNodes(this._tabParent); + var contentElements = _getChildNodes(this._contentParent); + + for (var i = 0, len = tabs.length; i < len; ++i) { + attr = {}; + + if (contentElements[i]) { + attr.contentEl = contentElements[i]; + } + + tab = new YAHOO.widget.Tab(tabs[i], attr); + this.addTab(tab); + + if (tab.hasClass(tab.ACTIVE_CLASSNAME) ) { + this._configs.activeTab.value = tab; // dont invoke method + } + } + }; + + var _createTabViewElement = function(attr) { + var el = document.createElement('div'); + + if ( this.CLASSNAME ) { + el.className = this.CLASSNAME; + } + + return el; + }; + + var _createTabParent = function(attr) { + var el = document.createElement('ul'); + + if ( this.TAB_PARENT_CLASSNAME ) { + el.className = this.TAB_PARENT_CLASSNAME; + } + + this.get('element').appendChild(el); + + return el; + }; + + var _createContentParent = function(attr) { + var el = document.createElement('div'); + + if ( this.CONTENT_PARENT_CLASSNAME ) { + el.className = this.CONTENT_PARENT_CLASSNAME; + } + + this.get('element').appendChild(el); + + return el; + }; + + var _getChildNodes = function(el) { + var nodes = []; + var childNodes = el.childNodes; + + for (var i = 0, len = childNodes.length; i < len; ++i) { + if (childNodes[i].nodeType == 1) { + nodes[nodes.length] = childNodes[i]; + } + } + + return nodes; + }; + +/** + * Fires before the activeTab is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('contentChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeActiveTabChange
+ *<YAHOO.widget.Tab> + * prevValue
the currently active tab
+ *<YAHOO.widget.Tab> + * newValue
the tab to be made activeUsage:
+ * @event beforeActiveTabChange + */ + +/** + * Fires after the activeTab is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeActiveTabChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
activeTabChange
+ *<YAHOO.widget.Tab> + * prevValue
the formerly active tab
+ *<YAHOO.widget.Tab> + * newValue
the new active tabUsage:
+ * @event activeTabChange + */ + +/** + * Fires before the orientation is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('activeTabChange', handler);See: Element.addListener
+ *If handler returns false, the change will be cancelled, and the value will not + * be set.
+ *Event fields:
+ *
+ *<String> type
beforeOrientationChange
+ *<String> + * prevValue
the current orientation
+ *<String> + * newValue
the new orientation to be appliedUsage:
+ * @event beforeOrientationChange + */ + +/** + * Fires after the orientation is changed. + *
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('beforeOrientationChange', handler);See: Element.addListener
+ *Event fields:
+ *
+ *<String> type
orientationChange
+ *<String> + * prevValue
the former orientation
+ *<String> + * newValue
the new orientationUsage:
+ * @event orientationChange + */ +})(); \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/treeview/README b/source/web/scripts/ajax/yahoo/treeview/README new file mode 100644 index 0000000000..cb715bb255 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/treeview/README @@ -0,0 +1,112 @@ +TreeView - Release Notes + +0.12.0 + + * TreeView now augments EventProvider, and has custom events for expand, + collapse, animStart, animComplete, and labelClick. Existing implementations + using abstract methods for these events (if they exist) will still work. + New events can be plugged into the tree by the Node implementation. For + example, TaskNode adds a checkClick event. EventProvider makes it safe + to do this because implementing code can call subscribe() prior to the + event creation. + + * YAHOO.util.Event is now a requirement for the widget + + * TreeView::removeChildren no longer expands and collapses the node. + + * Documented the moveComplete property + + * createElement("DIV") changed to createElement("div") + +0.11.4 + + * Fixed a javascript error on the HTML node example page. + +0.11.3 + + * popNode now clears the tree, previousSibling, nextSibling, and parent + properties of the node that is being removed from the tree. + + * Fixed the paths to the images in the tree.css file that is included in + build/assets. + +0.11.0 + + * Added TreeView -> popNode, which differs from removeNode in that the + node's children are preserved so the entire branch can be re-inserted + into another part of the tree, or another tree. + + * Added TreeView -> getNodesByProperty, which differs from + getNodeByProperty by returning an array of all nodes that have the + specified property rather than just the first one. + + * Added Node -> insertBefore, insertAfter, and appendTo. These method + can be used to reorganize the tree. + + * removeChildren properly resets the node icon when executed against a + collpased node. + + * Moved depth assignment from the constructor to appendChild so that the + value is set properly if the node is moved within the tree. + + * Fixed broken loop in removeChildren. + + * RootNode -> loadComplete calls tree.draw for hot-wiring the dynamic load + feature for the top level nodes. + + * The treeview constructor will accept an element instead of an id. + +0.10.0 + + * If the child collection changes prior to a refresh(), the icon is now + changed appropriately. + + * Added TreeView removeChildren(node). This will recursively purge the node's + children, and reset its dynamic load state. + + * previousSibling and nextSibling are now adjusted correctly when a + node is deleted. + + * expand()/collapse() now will not expand/collapse the node if false is + returned from the onExpand/onCollapse event handlers. + + * The onExpand and onCollapse events now fire before the node is expanded + or collapsed so that the implementer can modify the node's child collection + prior to display. + + * The childrenRendered property previous was used to indicate both that + a dynamically loaded node has received its data, and also to indicate + that the children html has been created. This has been split into + two properties. dynamicLoadComplete and childrenRendered. + + * Implemented the renderHidden flag. If set to true, the html for the entire + tree will be created when tree.draw() is called, not just the html for the + visible nodes. + + * Added an onLabelClick listener to TextNode + + * TreeView.removeNode no longer automatically refreshes the tree. It has an + optional parameter to perform the refresh. + + * removeNode can now be executed on nodes that have not been rendered. + + * You can now delete top-level nodes. + + * Added onCheckClick listener to the TaskNode example + + * Added Node.iconMode, which is a property that configures the way + dynamically loaded nodes are displayed when they are expanded and contain + no children. + + * TreeView/Node.setDynamicLoad now accepts an optional second parameter to + set the iconMode property. + + * Fixed an issue in the TaskNode.js example that prevented setting the checked + state of a node through the constructor. + + * Fixed an issue in HTMLNode that caused rendering issues when hasIcon was + set to true. + + * Added TreeView.removeNode + + * Added Node.refresh diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/lm.gif b/source/web/scripts/ajax/yahoo/treeview/assets/lm.gif new file mode 100644 index 0000000000..e7d0a3ce92 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/lm.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/lmh.gif b/source/web/scripts/ajax/yahoo/treeview/assets/lmh.gif new file mode 100644 index 0000000000..3ff6302723 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/lmh.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/ln.gif b/source/web/scripts/ajax/yahoo/treeview/assets/ln.gif new file mode 100644 index 0000000000..b7b3e55cd0 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/ln.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/loading.gif b/source/web/scripts/ajax/yahoo/treeview/assets/loading.gif new file mode 100644 index 0000000000..0bbf3bc0c0 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/loading.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/lp.gif b/source/web/scripts/ajax/yahoo/treeview/assets/lp.gif new file mode 100644 index 0000000000..b87f003154 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/lp.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/lph.gif b/source/web/scripts/ajax/yahoo/treeview/assets/lph.gif new file mode 100644 index 0000000000..e3478d8e37 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/lph.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/tm.gif b/source/web/scripts/ajax/yahoo/treeview/assets/tm.gif new file mode 100644 index 0000000000..e30abad890 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/tm.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/tmh.gif b/source/web/scripts/ajax/yahoo/treeview/assets/tmh.gif new file mode 100644 index 0000000000..ad7e557937 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/tmh.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/tn.gif b/source/web/scripts/ajax/yahoo/treeview/assets/tn.gif new file mode 100644 index 0000000000..4a280397de Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/tn.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/tp.gif b/source/web/scripts/ajax/yahoo/treeview/assets/tp.gif new file mode 100644 index 0000000000..d6d0ed0aed Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/tp.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/tph.gif b/source/web/scripts/ajax/yahoo/treeview/assets/tph.gif new file mode 100644 index 0000000000..e4d7d991e4 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/tph.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/tree.css b/source/web/scripts/ajax/yahoo/treeview/assets/tree.css new file mode 100644 index 0000000000..8686839b14 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/treeview/assets/tree.css @@ -0,0 +1,98 @@ +/* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */ + +/* first or middle sibling, no children */ +.ygtvtn { + width:16px; height:22px; + background: url(tn.gif) 0 0 no-repeat; +} + +/* first or middle sibling, collapsable */ +.ygtvtm { + width:16px; height:22px; + cursor:pointer ; + background: url(tm.gif) 0 0 no-repeat; +} + +/* first or middle sibling, collapsable, hover */ +.ygtvtmh { + width:16px; height:22px; + cursor:pointer ; + background: url(tmh.gif) 0 0 no-repeat; +} + +/* first or middle sibling, expandable */ +.ygtvtp { + width:16px; height:22px; + cursor:pointer ; + background: url(tp.gif) 0 0 no-repeat; +} + +/* first or middle sibling, expandable, hover */ +.ygtvtph { + width:16px; height:22px; + cursor:pointer ; + background: url(tph.gif) 0 0 no-repeat; +} + +/* last sibling, no children */ +.ygtvln { + width:16px; height:22px; + background: url(ln.gif) 0 0 no-repeat; +} + +/* Last sibling, collapsable */ +.ygtvlm { + width:16px; height:22px; + cursor:pointer ; + background: url(lm.gif) 0 0 no-repeat; +} + +/* Last sibling, collapsable, hover */ +.ygtvlmh { + width:16px; height:22px; + cursor:pointer ; + background: url(lmh.gif) 0 0 no-repeat; +} + +/* Last sibling, expandable */ +.ygtvlp { + width:16px; height:22px; + cursor:pointer ; + background: url(lp.gif) 0 0 no-repeat; +} + +/* Last sibling, expandable, hover */ +.ygtvlph { + width:16px; height:22px; cursor:pointer ; + background: url(lph.gif) 0 0 no-repeat; +} + +/* Loading icon */ +.ygtvloading { + width:16px; height:22px; + background: url(loading.gif) 0 0 no-repeat; +} + +/* the style for the empty cells that are used for rendering the depth + * of the node */ +.ygtvdepthcell { + width:16px; height:22px; + background: url(vline.gif) 0 0 no-repeat; +} + +.ygtvblankdepthcell { width:16px; height:22px; } + +/* the style of the div around each node */ +.ygtvitem { } + +/* the style of the div around each node's collection of children */ +.ygtvchildren { } +* html .ygtvchildren { height:2%; } + +/* the style of the text label in ygTextNode */ +.ygtvlabel, .ygtvlabel:link, .ygtvlabel:visited, .ygtvlabel:hover { + margin-left:2px; + text-decoration: none; +} + +.ygtvspacer { height: 10px; width: 10px; margin: 2px; } diff --git a/source/web/scripts/ajax/yahoo/treeview/assets/vline.gif b/source/web/scripts/ajax/yahoo/treeview/assets/vline.gif new file mode 100644 index 0000000000..1fb0de8bb2 Binary files /dev/null and b/source/web/scripts/ajax/yahoo/treeview/assets/vline.gif differ diff --git a/source/web/scripts/ajax/yahoo/treeview/treeview-debug.js b/source/web/scripts/ajax/yahoo/treeview/treeview-debug.js new file mode 100644 index 0000000000..94d0310753 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/treeview/treeview-debug.js @@ -0,0 +1,2228 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ + +/** + * The treeview widget is a generic tree building tool. + * @module treeview + * @title TreeView Widget + * @requires yahoo + * @optional animation + * @namespace YAHOO.widget + */ + +/** + * Contains the tree view state data and the root node. + * + * @class TreeView + * @constructor + * @param {string|HTMLElement} id The id of the element, or the element + * itself that the tree will be inserted into. + */ +YAHOO.widget.TreeView = function(id) { + if (id) { this.init(id); } +}; + + + +YAHOO.widget.TreeView.prototype = { + + /** + * The id of tree container element + * @property id + * @type String + */ + id: null, + + /** + * The host element for this tree + * @property _el + * @private + */ + _el: null, + + /** + * Flat collection of all nodes in this tree + * @property _nodes + * @type Node[] + * @private + */ + _nodes: null, + + /** + * We lock the tree control while waiting for the dynamic loader to return + * @property locked + * @type boolean + */ + locked: false, + + /** + * The animation to use for expanding children, if any + * @property _expandAnim + * @type string + * @private + */ + _expandAnim: null, + + /** + * The animation to use for collapsing children, if any + * @property _collapseAnim + * @type string + * @private + */ + _collapseAnim: null, + + /** + * The current number of animations that are executing + * @property _animCount + * @type int + * @private + */ + _animCount: 0, + + /** + * The maximum number of animations to run at one time. + * @property maxAnim + * @type int + */ + maxAnim: 2, + + /** + * Sets up the animation for expanding children + * @method setExpandAnim + * @param {string} type the type of animation (acceptable values defined + * in YAHOO.widget.TVAnim) + */ + setExpandAnim: function(type) { + if (YAHOO.widget.TVAnim.isValid(type)) { + this._expandAnim = type; + } + }, + + /** + * Sets up the animation for collapsing children + * @method setCollapseAnim + * @param {string} the type of animation (acceptable values defined in + * YAHOO.widget.TVAnim) + */ + setCollapseAnim: function(type) { + if (YAHOO.widget.TVAnim.isValid(type)) { + this._collapseAnim = type; + } + }, + + /** + * Perform the expand animation if configured, or just show the + * element if not configured or too many animations are in progress + * @method animateExpand + * @param el {HTMLElement} the element to animate + * @param node {YAHOO.util.Node} the node that was expanded + * @return {boolean} true if animation could be invoked, false otherwise + */ + animateExpand: function(el, node) { + this.logger.log("animating expand"); + + if (this._expandAnim && this._animCount < this.maxAnim) { + // this.locked = true; + var tree = this; + var a = YAHOO.widget.TVAnim.getAnim(this._expandAnim, el, + function() { tree.expandComplete(node); }); + if (a) { + ++this._animCount; + this.fireEvent("animStart", { + "node": node, + "type": "expand" + }); + a.animate(); + } + + return true; + } + + return false; + }, + + /** + * Perform the collapse animation if configured, or just show the + * element if not configured or too many animations are in progress + * @method animateCollapse + * @param el {HTMLElement} the element to animate + * @param node {YAHOO.util.Node} the node that was expanded + * @return {boolean} true if animation could be invoked, false otherwise + */ + animateCollapse: function(el, node) { + this.logger.log("animating collapse"); + + if (this._collapseAnim && this._animCount < this.maxAnim) { + // this.locked = true; + var tree = this; + var a = YAHOO.widget.TVAnim.getAnim(this._collapseAnim, el, + function() { tree.collapseComplete(node); }); + if (a) { + ++this._animCount; + this.fireEvent("animStart", { + "node": node, + "type": "collapse" + }); + a.animate(); + } + + return true; + } + + return false; + }, + + /** + * Function executed when the expand animation completes + * @method expandComplete + */ + expandComplete: function(node) { + this.logger.log("expand complete: " + this.id); + --this._animCount; + this.fireEvent("animComplete", { + "node": node, + "type": "expand" + }); + // this.locked = false; + }, + + /** + * Function executed when the collapse animation completes + * @method collapseComplete + */ + collapseComplete: function(node) { + this.logger.log("collapse complete: " + this.id); + --this._animCount; + this.fireEvent("animComplete", { + "node": node, + "type": "collapse" + }); + // this.locked = false; + }, + + /** + * Initializes the tree + * @method init + * @parm {string|HTMLElement} id the id of the element that will hold the tree + * @private + */ + init: function(id) { + + this.id = id; + + if ("string" !== typeof id) { + this._el = id; + this.id = this.generateId(id); + } + + /** + * When animation is enabled, this event fires when the animation + * starts + * @event animStart + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + * @parm {String} type the type of animation ("expand" or "collapse") + */ + this.createEvent("animStart", this); + + /** + * When animation is enabled, this event fires when the animation + * completes + * @event animComplete + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + * @parm {String} type the type of animation ("expand" or "collapse") + */ + this.createEvent("animComplete", this); + + /** + * Fires when a node is going to be expanded. Return false to stop + * the expand. + * @event collapse + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + */ + this.createEvent("collapse", this); + + /** + * Fires when a node is going to be collapsed. Return false to stop + * the collapse. + * @event expand + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + */ + this.createEvent("expand", this); + + this._nodes = []; + + // store a global reference + YAHOO.widget.TreeView.trees[this.id] = this; + + // Set up the root node + this.root = new YAHOO.widget.RootNode(this); + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + + this.logger.log("tree init: " + this.id); + }, + + /** + * Renders the tree boilerplate and visible nodes + * @method draw + */ + draw: function() { + var html = this.root.getHtml(); + this.getEl().innerHTML = html; + this.firstDraw = false; + }, + + /** + * Returns the tree's host element + * @method getEl + * @return {HTMLElement} the host element + */ + getEl: function() { + if (! this._el) { + this._el = document.getElementById(this.id); + } + return this._el; + }, + + /** + * Nodes register themselves with the tree instance when they are created. + * @method regNode + * @param node {Node} the node to register + * @private + */ + regNode: function(node) { + this._nodes[node.index] = node; + }, + + /** + * Returns the root node of this tree + * @method getRoot + * @return {Node} the root node + */ + getRoot: function() { + return this.root; + }, + + /** + * Configures this tree to dynamically load all child data + * @method setDynamicLoad + * @param {function} fnDataLoader the function that will be called to get the data + * @param iconMode {int} configures the icon that is displayed when a dynamic + * load node is expanded the first time without children. By default, the + * "collapse" icon will be used. If set to 1, the leaf node icon will be + * displayed. + */ + setDynamicLoad: function(fnDataLoader, iconMode) { + this.root.setDynamicLoad(fnDataLoader, iconMode); + }, + + /** + * Expands all child nodes. Note: this conflicts with the "multiExpand" + * node property. If expand all is called in a tree with nodes that + * do not allow multiple siblings to be displayed, only the last sibling + * will be expanded. + * @method expandAll + */ + expandAll: function() { + if (!this.locked) { + this.root.expandAll(); + } + }, + + /** + * Collapses all expanded child nodes in the entire tree. + * @method collapseAll + */ + collapseAll: function() { + if (!this.locked) { + this.root.collapseAll(); + } + }, + + /** + * Returns a node in the tree that has the specified index (this index + * is created internally, so this function probably will only be used + * in html generated for a given node.) + * @method getNodeByIndex + * @param {int} nodeIndex the index of the node wanted + * @return {Node} the node with index=nodeIndex, null if no match + */ + getNodeByIndex: function(nodeIndex) { + var n = this._nodes[nodeIndex]; + return (n) ? n : null; + }, + + /** + * Returns a node that has a matching property and value in the data + * object that was passed into its constructor. + * @method getNodeByProperty + * @param {object} property the property to search (usually a string) + * @param {object} value the value we want to find (usuall an int or string) + * @return {Node} the matching node, null if no match + */ + getNodeByProperty: function(property, value) { + for (var i in this._nodes) { + var n = this._nodes[i]; + if (n.data && value == n.data[property]) { + return n; + } + } + + return null; + }, + + /** + * Returns a collection of nodes that have a matching property + * and value in the data object that was passed into its constructor. + * @method getNodesByProperty + * @param {object} property the property to search (usually a string) + * @param {object} value the value we want to find (usuall an int or string) + * @return {Array} the matching collection of nodes, null if no match + */ + getNodesByProperty: function(property, value) { + var values = []; + for (var i in this._nodes) { + var n = this._nodes[i]; + if (n.data && value == n.data[property]) { + values.push(n); + } + } + + return (values.length) ? values : null; + }, + + /** + * Removes the node and its children, and optionally refreshes the + * branch of the tree that was affected. + * @method removeNode + * @param {Node} The node to remove + * @param {boolean} autoRefresh automatically refreshes branch if true + * @return {boolean} False is there was a problem, true otherwise. + */ + removeNode: function(node, autoRefresh) { + + // Don't delete the root node + if (node.isRoot()) { + return false; + } + + // Get the branch that we may need to refresh + var p = node.parent; + if (p.parent) { + p = p.parent; + } + + // Delete the node and its children + this._deleteNode(node); + + // Refresh the parent of the parent + if (autoRefresh && p && p.childrenRendered) { + p.refresh(); + } + + return true; + }, + + /** + * Deletes this nodes child collection, recursively. Also collapses + * the node, and resets the dynamic load flag. The primary use for + * this method is to purge a node and allow it to fetch its data + * dynamically again. + * @method removeChildren + * @param {Node} node the node to purge + */ + removeChildren: function(node) { + this.logger.log("Removing children for " + node); + while (node.children.length) { + this._deleteNode(node.children[0]); + } + + node.childrenRendered = false; + node.dynamicLoadComplete = false; + if (node.expanded) { + node.collapse(); + } else { + node.updateIcon(); + } + }, + + /** + * Deletes the node and recurses children + * @method _deleteNode + * @private + */ + _deleteNode: function(node) { + // Remove all the child nodes first + this.removeChildren(node); + + // Remove the node from the tree + this.popNode(node); + }, + + /** + * Removes the node from the tree, preserving the child collection + * to make it possible to insert the branch into another part of the + * tree, or another tree. + * @method popNode + * @param {Node} the node to remove + */ + popNode: function(node) { + var p = node.parent; + + // Update the parent's collection of children + var a = []; + + for (var i=0, len=p.children.length;i
+ *var handler = function(e) {var previous = e.prevValue};
+ * myTabs.addListener('orientationChange', handler);'; + } + + var f = document.createElement("div"); + var s = f.style; + s.position = "absolute"; + s.top = "-1000px"; + s.left = "-1000px"; + f.innerHTML = sb.join(""); + + document.body.appendChild(f); + + YAHOO.widget.TreeView.removeHandler(window, + "load", YAHOO.widget.TreeView.preload); + +}; + +YAHOO.widget.TreeView.addHandler(window, + "load", YAHOO.widget.TreeView.preload); + +/** + * The base class for all tree nodes. The node's presentation and behavior in + * response to mouse events is handled in Node subclasses. + * @namespace YAHOO.widget + * @class Node + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + * @constructor + */ +YAHOO.widget.Node = function(oData, oParent, expanded) { + if (oData) { this.init(oData, oParent, expanded); } +}; + +YAHOO.widget.Node.prototype = { + + /** + * The index for this instance obtained from global counter in YAHOO.widget.TreeView. + * @property index + * @type int + */ + index: 0, + + /** + * This node's child node collection. + * @property children + * @type Node[] + */ + children: null, + + /** + * Tree instance this node is part of + * @property tree + * @type TreeView + */ + tree: null, + + /** + * The data linked to this node. This can be any object or primitive + * value, and the data can be used in getNodeHtml(). + * @property data + * @type object + */ + data: null, + + /** + * Parent node + * @property parent + * @type Node + */ + parent: null, + + /** + * The depth of this node. We start at -1 for the root node. + * @property depth + * @type int + */ + depth: -1, + + /** + * The href for the node's label. If one is not specified, the href will + * be set so that it toggles the node. + * @property href + * @type string + */ + href: null, + + /** + * The label href target, defaults to current window + * @property target + * @type string + */ + target: "_self", + + /** + * The node's expanded/collapsed state + * @property expanded + * @type boolean + */ + expanded: false, + + /** + * Can multiple children be expanded at once? + * @property multiExpand + * @type boolean + */ + multiExpand: true, + + /** + * Should we render children for a collapsed node? It is possible that the + * implementer will want to render the hidden data... @todo verify that we + * need this, and implement it if we do. + * @property renderHidden + * @type boolean + */ + renderHidden: false, + + /** + * This flag is set to true when the html is generated for this node's + * children, and set to false when new children are added. + * @property childrenRendered + * @type boolean + */ + childrenRendered: false, + + /** + * Dynamically loaded nodes only fetch the data the first time they are + * expanded. This flag is set to true once the data has been fetched. + * @property dynamicLoadComplete + * @type boolean + */ + dynamicLoadComplete: false, + + /** + * This node's previous sibling + * @property previousSibling + * @type Node + */ + previousSibling: null, + + /** + * This node's next sibling + * @property nextSibling + * @type Node + */ + nextSibling: null, + + /** + * We can set the node up to call an external method to get the child + * data dynamically. + * @property _dynLoad + * @type boolean + * @private + */ + _dynLoad: false, + + /** + * Function to execute when we need to get this node's child data. + * @property dataLoader + * @type function + */ + dataLoader: null, + + /** + * This is true for dynamically loading nodes while waiting for the + * callback to return. + * @property isLoading + * @type boolean + */ + isLoading: false, + + /** + * The toggle/branch icon will not show if this is set to false. This + * could be useful if the implementer wants to have the child contain + * extra info about the parent, rather than an actual node. + * @property hasIcon + * @type boolean + */ + hasIcon: true, + + /** + * Used to configure what happens when a dynamic load node is expanded + * and we discover that it does not have children. By default, it is + * treated as if it still could have children (plus/minus icon). Set + * iconMode to have it display like a leaf node instead. + * @property iconMode + * @type int + */ + iconMode: 0, + + /** + * The node type + * @property _type + * @private + */ + _type: "Node", + + /* + spacerPath: "http://us.i1.yimg.com/us.yimg.com/i/space.gif", + expandedText: "Expanded", + collapsedText: "Collapsed", + loadingText: "Loading", + */ + + /** + * Initializes this node, gets some of the properties from the parent + * @method init + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + */ + init: function(oData, oParent, expanded) { + + this.data = oData; + this.children = []; + this.index = YAHOO.widget.TreeView.nodeCount; + ++YAHOO.widget.TreeView.nodeCount; + this.expanded = expanded; + this.logger = new YAHOO.widget.LogWriter(this.toString()); + + /** + * The parentChange event is fired when a parent element is applied + * to the node. This is useful if you need to apply tree-level + * properties to a tree that need to happen if a node is moved from + * one tre to another. + * + * @event parentChange + * @type CustomEvent + */ + this.createEvent("parentChange", this); + + // oParent should never be null except when we create the root node. + if (oParent) { + oParent.appendChild(this); + } + }, + + /** + * Certain properties for the node cannot be set until the parent + * is known. This is called after the node is inserted into a tree. + * the parent is also applied to this node's children in order to + * make it possible to move a branch from one tree to another. + * @method applyParent + * @param {Node} parentNode this node's parent node + * @return {boolean} true if the application was successful + */ + applyParent: function(parentNode) { + if (!parentNode) { + return false; + } + + this.tree = parentNode.tree; + this.parent = parentNode; + this.depth = parentNode.depth + 1; + + if (!this.href) { + this.href = "javascript:" + this.getToggleLink(); + } + + if (! this.multiExpand) { + this.multiExpand = parentNode.multiExpand; + } + + this.tree.regNode(this); + parentNode.childrenRendered = false; + + // cascade update existing children + for (var i=0, len=this.children.length;i 0 || + (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete) ); + }, + + /** + * Expands if node is collapsed, collapses otherwise. + * @method toggle + */ + toggle: function() { + if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) { + if (this.expanded) { this.collapse(); } else { this.expand(); } + } + }, + + /** + * Returns the markup for this node and its children. + * @method getHtml + * @return {string} the markup for this node and its expanded children. + */ + getHtml: function() { + + this.childrenRendered = false; + + var sb = []; + sb[sb.length] = ' '; + sb[sb.length] = this.getNodeHtml(); + sb[sb.length] = this.getChildrenHtml(); + sb[sb.length] = ''; + return sb.join(""); + }, + + /** + * Called when first rendering the tree. We always build the div that will + * contain this nodes children, but we don't render the children themselves + * unless this node is expanded. + * @method getChildrenHtml + * @return {string} the children container div html and any expanded children + * @private + */ + getChildrenHtml: function() { + + var sb = []; + sb[sb.length] = ' '; + + return sb.join(""); + }, + + /** + * Generates the markup for the child nodes. This is not done until the node + * is expanded. + * @method renderChildren + * @return {string} the html for this node's children + * @private + */ + renderChildren: function() { + + this.logger.log("rendering children for " + this.index); + + var node = this; + + if (this.isDynamic() && !this.dynamicLoadComplete) { + this.isLoading = true; + this.tree.locked = true; + + if (this.dataLoader) { + this.logger.log("Using dynamic loader defined for this node"); + + setTimeout( + function() { + node.dataLoader(node, + function() { + node.loadComplete(); + }); + }, 10); + + } else if (this.tree.root.dataLoader) { + this.logger.log("Using the tree-level dynamic loader"); + + setTimeout( + function() { + node.tree.root.dataLoader(node, + function() { + node.loadComplete(); + }); + }, 10); + + } else { + this.logger.log("no loader found"); + return "Error: data loader not found or not specified."; + } + + return ""; + + } else { + return this.completeRender(); + } + }, + + /** + * Called when we know we have all the child data. + * @method completeRender + * @return {string} children html + */ + completeRender: function() { + this.logger.log("completeRender: " + this.index + ", # of children: " + this.children.length); + var sb = []; + + for (var i=0; i < this.children.length; ++i) { + // this.children[i].childrenRendered = false; + sb[sb.length] = this.children[i].getHtml(); + } + + this.childrenRendered = true; + + return sb.join(""); + }, + + /** + * Load complete is the callback function we pass to the data provider + * in dynamic load situations. + * @method loadComplete + */ + loadComplete: function() { + this.logger.log("loadComplete: " + this.index); + this.getChildrenEl().innerHTML = this.completeRender(); + this.dynamicLoadComplete = true; + this.isLoading = false; + this.expand(); + this.tree.locked = false; + }, + + /** + * Returns this node's ancestor at the specified depth. + * @method getAncestor + * @param {int} depth the depth of the ancestor. + * @return {Node} the ancestor + */ + getAncestor: function(depth) { + if (depth >= this.depth || depth < 0) { + this.logger.log("illegal getAncestor depth: " + depth); + return null; + } + + var p = this.parent; + + while (p.depth > depth) { + p = p.parent; + } + + return p; + }, + + /** + * Returns the css class for the spacer at the specified depth for + * this node. If this node's ancestor at the specified depth + * has a next sibling the presentation is different than if it + * does not have a next sibling + * @method getDepthStyle + * @param {int} depth the depth of the ancestor. + * @return {string} the css class for the spacer + */ + getDepthStyle: function(depth) { + return (this.getAncestor(depth).nextSibling) ? + "ygtvdepthcell" : "ygtvblankdepthcell"; + }, + + /** + * Get the markup for the node. This is designed to be overrided so that we can + * support different types of nodes. + * @method getNodeHtml + * @return {string} The HTML that will render this node. + */ + getNodeHtml: function() { + this.logger.log("Generating html"); + return ""; + }, + + /** + * Regenerates the html for this node and its children. To be used when the + * node is expanded and new children have been added. + * @method refresh + */ + refresh: function() { + // this.loadComplete(); + this.getChildrenEl().innerHTML = this.completeRender(); + + if (this.hasIcon) { + var el = this.getToggleEl(); + if (el) { + el.className = this.getStyle(); + } + } + }, + + /** + * Node toString + * @method toString + * @return {string} string representation of the node + */ + toString: function() { + return "Node (" + this.index + ")"; + } + +}; + +YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider); + +/** + * A custom YAHOO.widget.Node that handles the unique nature of + * the virtual, presentationless root node. + * @namespace YAHOO.widget + * @class RootNode + * @extends YAHOO.widget.Node + * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to + * @constructor + */ +YAHOO.widget.RootNode = function(oTree) { + // Initialize the node with null params. The root node is a + // special case where the node has no presentation. So we have + // to alter the standard properties a bit. + this.init(null, null, true); + + /* + * For the root node, we get the tree reference from as a param + * to the constructor instead of from the parent element. + */ + this.tree = oTree; +}; + +YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, { + + // overrides YAHOO.widget.Node + getNodeHtml: function() { + return ""; + }, + + toString: function() { + return "RootNode"; + }, + + loadComplete: function() { + this.tree.draw(); + } + +}); +/** + * The default node presentation. The first parameter should be + * either a string that will be used as the node's label, or an object + * that has a string propery called label. By default, the clicking the + * label will toggle the expanded/collapsed state of the node. By + * changing the href property of the instance, this behavior can be + * changed so that the label will go to the specified href. + * @namespace YAHOO.widget + * @class TextNode + * @extends YAHOO.widget.Node + * @constructor + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {YAHOO.widget.Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + */ +YAHOO.widget.TextNode = function(oData, oParent, expanded) { + + if (oData) { + this.init(oData, oParent, expanded); + this.setUpLabel(oData); + } + + this.logger = new YAHOO.widget.LogWriter(this.toString()); +}; + +YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, { + + /** + * The CSS class for the label href. Defaults to ygtvlabel, but can be + * overridden to provide a custom presentation for a specific node. + * @property labelStyle + * @type string + */ + labelStyle: "ygtvlabel", + + /** + * The derived element id of the label for this node + * @property labelElId + * @type string + */ + labelElId: null, + + /** + * The text for the label. It is assumed that the oData parameter will + * either be a string that will be used as the label, or an object that + * has a property called "label" that we will use. + * @property label + * @type string + */ + label: null, + + textNodeParentChange: function() { + + /** + * Custom event that is fired when the text node label is clicked. The + * custom event is defined on the tree instance, so there is a single + * event that handles all nodes in the tree. The node clicked is + * provided as an argument + * + * @event labelClick + * @for YAHOO.widget.TreeView + * @param {YAHOO.widget.Node} node the node clicked + */ + if (this.tree && !this.tree.hasEvent("labelClick")) { + this.tree.createEvent("labelClick", this.tree); + } + + }, + + /** + * Sets up the node label + * @method setUpLabel + * @param oData string containing the label, or an object with a label property + */ + setUpLabel: function(oData) { + + // set up the custom event on the tree + this.textNodeParentChange(); + this.subscribe("parentChange", this.textNodeParentChange); + + if (typeof oData == "string") { + oData = { label: oData }; + } + this.label = oData.label; + + // update the link + if (oData.href) { + this.href = oData.href; + } + + // set the target + if (oData.target) { + this.target = oData.target; + } + + if (oData.style) { + this.labelStyle = oData.style; + } + + this.labelElId = "ygtvlabelel" + this.index; + }, + + /** + * Returns the label element + * @for YAHOO.widget.TextNode + * @method getLabelEl + * @return {object} the element + */ + getLabelEl: function() { + return document.getElementById(this.labelElId); + }, + + // overrides YAHOO.widget.Node + getNodeHtml: function() { + this.logger.log("Generating html"); + var sb = []; + + sb[sb.length] = ''; + sb[sb.length] = '
'; + + return sb.join(""); + }, + + + /** + * Executed when the label is clicked. Fires the labelClick custom event. + * @method onLabelClick + * @param me {Node} this node + * @scope the anchor tag clicked + * @return false to cancel the anchor click + */ + onLabelClick: function(me) { + me.logger.log("onLabelClick " + me.label); + return me.tree.fireEvent("labelClick", me); + //return true; + }, + + toString: function() { + return "TextNode (" + this.index + ") " + this.label; + } + +}); +/** + * A menu-specific implementation that differs from TextNode in that only + * one sibling can be expanded at a time. + * @namespace YAHOO.widget + * @class MenuNode + * @extends YAHOO.widget.TextNode + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {YAHOO.widget.Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + * @constructor + */ +YAHOO.widget.MenuNode = function(oData, oParent, expanded) { + if (oData) { + this.init(oData, oParent, expanded); + this.setUpLabel(oData); + } + + /* + * Menus usually allow only one branch to be open at a time. + */ + this.multiExpand = false; + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + +}; + +YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, { + + toString: function() { + return "MenuNode (" + this.index + ") " + this.label; + } + +}); +/** + * This implementation takes either a string or object for the + * oData argument. If is it a string, we will use it for the display + * of this node (and it can contain any html code). If the parameter + * is an object, we look for a parameter called "html" that will be + * used for this node's display. + * @namespace YAHOO.widget + * @class HTMLNode + * @extends YAHOO.widget.Node + * @constructor + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {YAHOO.widget.Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + * @param hasIcon {boolean} specifies whether or not leaf nodes should + * have an icon + */ +YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) { + if (oData) { + this.init(oData, oParent, expanded); + this.initContent(oData, hasIcon); + } +}; + +YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.Node, { + + /** + * The CSS class for the html content container. Defaults to ygtvhtml, but + * can be overridden to provide a custom presentation for a specific node. + * @property contentStyle + * @type string + */ + contentStyle: "ygtvhtml", + + /** + * The generated id that will contain the data passed in by the implementer. + * @property contentElId + * @type string + */ + contentElId: null, + + /** + * The HTML content to use for this node's display + * @property content + * @type string + */ + content: null, + + /** + * Sets up the node label + * @property initContent + * @param {object} An html string or object containing an html property + * @param {boolean} hasIcon determines if the node will be rendered with an + * icon or not + */ + initContent: function(oData, hasIcon) { + if (typeof oData == "string") { + oData = { html: oData }; + } + + this.html = oData.html; + this.contentElId = "ygtvcontentel" + this.index; + this.hasIcon = hasIcon; + + this.logger = new YAHOO.widget.LogWriter(this.toString()); + }, + + /** + * Returns the outer html element for this node's content + * @method getContentEl + * @return {HTMLElement} the element + */ + getContentEl: function() { + return document.getElementById(this.contentElId); + }, + + // overrides YAHOO.widget.Node + getNodeHtml: function() { + this.logger.log("Generating html"); + var sb = []; + + sb[sb.length] = ''; + + for (var i=0;i '; + sb[sb.length] = ''; + sb[sb.length] = ' '; + } + + var getNode = 'YAHOO.widget.TreeView.getNode(\'' + + this.tree.id + '\',' + this.index + ')'; + + sb[sb.length] = ' '; + + /* + sb[sb.length] = ' '; + sb[sb.length] = ''; + */ + + sb[sb.length] = ' '; + + sb[sb.length] = '
'; + sb[sb.length] = ''; + sb[sb.length] = this.label; + sb[sb.length] = ''; + sb[sb.length] = ' '; + sb[sb.length] = ''; + sb[sb.length] = '
'; + + return sb.join(""); + }, + + toString: function() { + return "HTMLNode (" + this.index + ")"; + } + +}); +/** + * A static factory class for tree view expand/collapse animations + * @class TVAnim + * @static + */ +YAHOO.widget.TVAnim = function() { + return { + /** + * Constant for the fade in animation + * @property FADE_IN + * @type string + * @static + */ + FADE_IN: "TVFadeIn", + + /** + * Constant for the fade out animation + * @property FADE_OUT + * @type string + * @static + */ + FADE_OUT: "TVFadeOut", + + /** + * Returns a ygAnim instance of the given type + * @method getAnim + * @param type {string} the type of animation + * @param el {HTMLElement} the element to element (probably the children div) + * @param callback {function} function to invoke when the animation is done. + * @return {YAHOO.util.Animation} the animation instance + * @static + */ + getAnim: function(type, el, callback) { + if (YAHOO.widget[type]) { + return new YAHOO.widget[type](el, callback); + } else { + return null; + } + }, + + /** + * Returns true if the specified animation class is available + * @method isValid + * @param type {string} the type of animation + * @return {boolean} true if valid, false if not + * @static + */ + isValid: function(type) { + return (YAHOO.widget[type]); + } + }; +} (); + +/** + * A 1/2 second fade-in animation. + * @class TVFadeIn + * @constructor + * @param el {HTMLElement} the element to animate + * @param callback {function} function to invoke when the animation is finished + */ +YAHOO.widget.TVFadeIn = function(el, callback) { + /** + * The element to animate + * @property el + * @type HTMLElement + */ + this.el = el; + + /** + * the callback to invoke when the animation is complete + * @property callback + * @type function + */ + this.callback = callback; + + this.logger = new YAHOO.widget.LogWriter(this.toString()); +}; + +YAHOO.widget.TVFadeIn.prototype = { + /** + * Performs the animation + * @method animate + */ + animate: function() { + var tvanim = this; + + var s = this.el.style; + s.opacity = 0.1; + s.filter = "alpha(opacity=10)"; + s.display = ""; + + var dur = 0.4; + var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}, dur); + a.onComplete.subscribe( function() { tvanim.onComplete(); } ); + a.animate(); + }, + + /** + * Clean up and invoke callback + * @method onComplete + */ + onComplete: function() { + this.callback(); + }, + + /** + * toString + * @method toString + * @return {string} the string representation of the instance + */ + toString: function() { + return "TVFadeIn"; + } +}; + +/** + * A 1/2 second fade out animation. + * @class TVFadeOut + * @constructor + * @param el {HTMLElement} the element to animate + * @param callback {Function} function to invoke when the animation is finished + */ +YAHOO.widget.TVFadeOut = function(el, callback) { + /** + * The element to animate + * @property el + * @type HTMLElement + */ + this.el = el; + + /** + * the callback to invoke when the animation is complete + * @property callback + * @type function + */ + this.callback = callback; + + this.logger = new YAHOO.widget.LogWriter(this.toString()); +}; + +YAHOO.widget.TVFadeOut.prototype = { + /** + * Performs the animation + * @method animate + */ + animate: function() { + var tvanim = this; + var dur = 0.4; + var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur); + a.onComplete.subscribe( function() { tvanim.onComplete(); } ); + a.animate(); + }, + + /** + * Clean up and invoke callback + * @method onComplete + */ + onComplete: function() { + var s = this.el.style; + s.display = "none"; + // s.opacity = 1; + s.filter = "alpha(opacity=100)"; + this.callback(); + }, + + /** + * toString + * @method toString + * @return {string} the string representation of the instance + */ + toString: function() { + return "TVFadeOut"; + } +}; + diff --git a/source/web/scripts/ajax/yahoo/treeview/treeview-min.js b/source/web/scripts/ajax/yahoo/treeview/treeview-min.js new file mode 100644 index 0000000000..9d808ff525 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/treeview/treeview-min.js @@ -0,0 +1 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txt version: 0.12.0 */ YAHOO.widget.TreeView=function(id){if(id){this.init(id);}};YAHOO.widget.TreeView.prototype={id:null,_el:null,_nodes:null,locked:false,_expandAnim:null,_collapseAnim:null,_animCount:0,maxAnim:2,setExpandAnim:function(_2){if(YAHOO.widget.TVAnim.isValid(_2)){this._expandAnim=_2;}},setCollapseAnim:function(_3){if(YAHOO.widget.TVAnim.isValid(_3)){this._collapseAnim=_3;}},animateExpand:function(el,_5){if(this._expandAnim&&this._animCount'; + + for (var i=0;i '; + sb[sb.length] = ''; + } + + if (this.hasIcon) { + sb[sb.length] = ' '; + } + + sb[sb.length] = ' '; + sb[sb.length] = this.html; + sb[sb.length] = ' '; + sb[sb.length] = '";}var f=document.createElement("div");var s=f.style;s.position="absolute";s.top="-1000px";s.left="-1000px";f.innerHTML=sb.join("");document.body.appendChild(f);YAHOO.widget.TreeView.removeHandler(window,"load",YAHOO.widget.TreeView.preload);};YAHOO.widget.TreeView.addHandler(window,"load",YAHOO.widget.TreeView.preload);YAHOO.widget.Node=function(_44,_45,_46){if(_44){this.init(_44,_45,_46);}};YAHOO.widget.Node.prototype={index:0,children:null,tree:null,data:null,parent:null,depth:-1,href:null,target:"_self",expanded:false,multiExpand:true,renderHidden:false,childrenRendered:false,dynamicLoadComplete:false,previousSibling:null,nextSibling:null,_dynLoad:false,dataLoader:null,isLoading:false,hasIcon:true,iconMode:0,_type:"Node",init:function(_47,_48,_49){this.data=_47;this.children=[];this.index=YAHOO.widget.TreeView.nodeCount;++YAHOO.widget.TreeView.nodeCount;this.expanded=_49;this.createEvent("parentChange",this);if(_48){_48.appendChild(this);}},applyParent:function(_50){if(!_50){return false;}this.tree=_50.tree;this.parent=_50;this.depth=_50.depth+1;if(!this.href){this.href="javascript:"+this.getToggleLink();}if(!this.multiExpand){this.multiExpand=_50.multiExpand;}this.tree.regNode(this);_50.childrenRendered=false;for(var i=0,len=this.children.length;i 0||(_67&&this.isDynamic()&&!this.dynamicLoadComplete));},toggle:function(){if(!this.tree.locked&&(this.hasChildren(true)||this.isDynamic())){if(this.expanded){this.collapse();}else{this.expand();}}},getHtml:function(){this.childrenRendered=false;var sb=[];sb[sb.length]=" ";sb[sb.length]=this.getNodeHtml();sb[sb.length]=this.getChildrenHtml();sb[sb.length]="";return sb.join("");},getChildrenHtml:function(){var sb=[];sb[sb.length]=" ";return sb.join("");},renderChildren:function(){var _68=this;if(this.isDynamic()&&!this.dynamicLoadComplete){this.isLoading=true;this.tree.locked=true;if(this.dataLoader){setTimeout(function(){_68.dataLoader(_68,function(){_68.loadComplete();});},10);}else{if(this.tree.root.dataLoader){setTimeout(function(){_68.tree.root.dataLoader(_68,function(){_68.loadComplete();});},10);}else{return "Error: data loader not found or not specified.";}}return "";}else{return this.completeRender();}},completeRender:function(){var sb=[];for(var i=0;i=this.depth||_69<0){return null;}var p=this.parent;while(p.depth>_69){p=p.parent;}return p;},getDepthStyle:function(_70){return (this.getAncestor(_70).nextSibling)?"ygtvdepthcell":"ygtvblankdepthcell";},getNodeHtml:function(){return "";},refresh:function(){this.getChildrenEl().innerHTML=this.completeRender();if(this.hasIcon){var el=this.getToggleEl();if(el){el.className=this.getStyle();}}},toString:function(){return "Node ("+this.index+")";}};YAHOO.augment(YAHOO.widget.Node,YAHOO.util.EventProvider);YAHOO.widget.RootNode=function(_71){this.init(null,null,true);this.tree=_71;};YAHOO.extend(YAHOO.widget.RootNode,YAHOO.widget.Node,{getNodeHtml:function(){return "";},toString:function(){return "RootNode";},loadComplete:function(){this.tree.draw();}});YAHOO.widget.TextNode=function(_72,_73,_74){if(_72){this.init(_72,_73,_74);this.setUpLabel(_72);}};YAHOO.extend(YAHOO.widget.TextNode,YAHOO.widget.Node,{labelStyle:"ygtvlabel",labelElId:null,label:null,textNodeParentChange:function(){if(this.tree&&!this.tree.hasEvent("labelClick")){this.tree.createEvent("labelClick",this.tree);}},setUpLabel:function(_75){this.textNodeParentChange();this.subscribe("parentChange",this.textNodeParentChange);if(typeof _75=="string"){_75={label:_75};}this.label=_75.label;if(_75.href){this.href=_75.href;}if(_75.target){this.target=_75.target;}if(_75.style){this.labelStyle=_75.style;}this.labelElId="ygtvlabelel"+this.index;},getLabelEl:function(){return document.getElementById(this.labelElId);},getNodeHtml:function(){var sb=[];sb[sb.length]=" ";sb[sb.length]="
";return sb.join("");},onLabelClick:function(me){return me.tree.fireEvent("labelClick",me);},toString:function(){return "TextNode ("+this.index+") "+this.label;}});YAHOO.widget.MenuNode=function(_78,_79,_80){if(_78){this.init(_78,_79,_80);this.setUpLabel(_78);}this.multiExpand=false;};YAHOO.extend(YAHOO.widget.MenuNode,YAHOO.widget.TextNode,{toString:function(){return "MenuNode ("+this.index+") "+this.label;}});YAHOO.widget.HTMLNode=function(_81,_82,_83,_84){if(_81){this.init(_81,_82,_83);this.initContent(_81,_84);}};YAHOO.extend(YAHOO.widget.HTMLNode,YAHOO.widget.Node,{contentStyle:"ygtvhtml",contentElId:null,content:null,initContent:function(_85,_86){if(typeof _85=="string"){_85={html:_85};}this.html=_85.html;this.contentElId="ygtvcontentel"+this.index;this.hasIcon=_86;},getContentEl:function(){return document.getElementById(this.contentElId);},getNodeHtml:function(){var sb=[];sb[sb.length]="";for(var i=0;i ";sb[sb.length]="";}var _76="YAHOO.widget.TreeView.getNode('"+this.tree.id+"',"+this.index+")";sb[sb.length]=" ";sb[sb.length]=" ";sb[sb.length]=" ";sb[sb.length]="";sb[sb.length]="";sb[sb.length]=this.label;sb[sb.length]="";sb[sb.length]=" ";sb[sb.length]="";sb[sb.length]="
";return sb.join("");},toString:function(){return "HTMLNode ("+this.index+")";}});YAHOO.widget.TVAnim=function(){return {FADE_IN:"TVFadeIn",FADE_OUT:"TVFadeOut",getAnim:function(_87,el,_88){if(YAHOO.widget[_87]){return new YAHOO.widget[_87](el,_88);}else{return null;}},isValid:function(_89){return (YAHOO.widget[_89]);}};}();YAHOO.widget.TVFadeIn=function(el,_90){this.el=el;this.callback=_90;};YAHOO.widget.TVFadeIn.prototype={animate:function(){var _91=this;var s=this.el.style;s.opacity=0.1;s.filter="alpha(opacity=10)";s.display="";var dur=0.4;var a=new YAHOO.util.Anim(this.el,{opacity:{from:0.1,to:1,unit:""}},dur);a.onComplete.subscribe(function(){_91.onComplete();});a.animate();},onComplete:function(){this.callback();},toString:function(){return "TVFadeIn";}};YAHOO.widget.TVFadeOut=function(el,_93){this.el=el;this.callback=_93;};YAHOO.widget.TVFadeOut.prototype={animate:function(){var _94=this;var dur=0.4;var a=new YAHOO.util.Anim(this.el,{opacity:{from:1,to:0.1,unit:""}},dur);a.onComplete.subscribe(function(){_94.onComplete();});a.animate();},onComplete:function(){var s=this.el.style;s.display="none";s.filter="alpha(opacity=100)";this.callback();},toString:function(){return "TVFadeOut";}}; \ No newline at end of file diff --git a/source/web/scripts/ajax/yahoo/treeview/treeview.js b/source/web/scripts/ajax/yahoo/treeview/treeview.js new file mode 100644 index 0000000000..358d85ca89 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/treeview/treeview.js @@ -0,0 +1,2182 @@ +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 0.12.0 +*/ + +/** + * The treeview widget is a generic tree building tool. + * @module treeview + * @title TreeView Widget + * @requires yahoo + * @optional animation + * @namespace YAHOO.widget + */ + +/** + * Contains the tree view state data and the root node. + * + * @class TreeView + * @constructor + * @param {string|HTMLElement} id The id of the element, or the element + * itself that the tree will be inserted into. + */ +YAHOO.widget.TreeView = function(id) { + if (id) { this.init(id); } +}; + +YAHOO.widget.TreeView.prototype = { + + /** + * The id of tree container element + * @property id + * @type String + */ + id: null, + + /** + * The host element for this tree + * @property _el + * @private + */ + _el: null, + + /** + * Flat collection of all nodes in this tree + * @property _nodes + * @type Node[] + * @private + */ + _nodes: null, + + /** + * We lock the tree control while waiting for the dynamic loader to return + * @property locked + * @type boolean + */ + locked: false, + + /** + * The animation to use for expanding children, if any + * @property _expandAnim + * @type string + * @private + */ + _expandAnim: null, + + /** + * The animation to use for collapsing children, if any + * @property _collapseAnim + * @type string + * @private + */ + _collapseAnim: null, + + /** + * The current number of animations that are executing + * @property _animCount + * @type int + * @private + */ + _animCount: 0, + + /** + * The maximum number of animations to run at one time. + * @property maxAnim + * @type int + */ + maxAnim: 2, + + /** + * Sets up the animation for expanding children + * @method setExpandAnim + * @param {string} type the type of animation (acceptable values defined + * in YAHOO.widget.TVAnim) + */ + setExpandAnim: function(type) { + if (YAHOO.widget.TVAnim.isValid(type)) { + this._expandAnim = type; + } + }, + + /** + * Sets up the animation for collapsing children + * @method setCollapseAnim + * @param {string} the type of animation (acceptable values defined in + * YAHOO.widget.TVAnim) + */ + setCollapseAnim: function(type) { + if (YAHOO.widget.TVAnim.isValid(type)) { + this._collapseAnim = type; + } + }, + + /** + * Perform the expand animation if configured, or just show the + * element if not configured or too many animations are in progress + * @method animateExpand + * @param el {HTMLElement} the element to animate + * @param node {YAHOO.util.Node} the node that was expanded + * @return {boolean} true if animation could be invoked, false otherwise + */ + animateExpand: function(el, node) { + + if (this._expandAnim && this._animCount < this.maxAnim) { + // this.locked = true; + var tree = this; + var a = YAHOO.widget.TVAnim.getAnim(this._expandAnim, el, + function() { tree.expandComplete(node); }); + if (a) { + ++this._animCount; + this.fireEvent("animStart", { + "node": node, + "type": "expand" + }); + a.animate(); + } + + return true; + } + + return false; + }, + + /** + * Perform the collapse animation if configured, or just show the + * element if not configured or too many animations are in progress + * @method animateCollapse + * @param el {HTMLElement} the element to animate + * @param node {YAHOO.util.Node} the node that was expanded + * @return {boolean} true if animation could be invoked, false otherwise + */ + animateCollapse: function(el, node) { + + if (this._collapseAnim && this._animCount < this.maxAnim) { + // this.locked = true; + var tree = this; + var a = YAHOO.widget.TVAnim.getAnim(this._collapseAnim, el, + function() { tree.collapseComplete(node); }); + if (a) { + ++this._animCount; + this.fireEvent("animStart", { + "node": node, + "type": "collapse" + }); + a.animate(); + } + + return true; + } + + return false; + }, + + /** + * Function executed when the expand animation completes + * @method expandComplete + */ + expandComplete: function(node) { + --this._animCount; + this.fireEvent("animComplete", { + "node": node, + "type": "expand" + }); + // this.locked = false; + }, + + /** + * Function executed when the collapse animation completes + * @method collapseComplete + */ + collapseComplete: function(node) { + --this._animCount; + this.fireEvent("animComplete", { + "node": node, + "type": "collapse" + }); + // this.locked = false; + }, + + /** + * Initializes the tree + * @method init + * @parm {string|HTMLElement} id the id of the element that will hold the tree + * @private + */ + init: function(id) { + + this.id = id; + + if ("string" !== typeof id) { + this._el = id; + this.id = this.generateId(id); + } + + /** + * When animation is enabled, this event fires when the animation + * starts + * @event animStart + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + * @parm {String} type the type of animation ("expand" or "collapse") + */ + this.createEvent("animStart", this); + + /** + * When animation is enabled, this event fires when the animation + * completes + * @event animComplete + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + * @parm {String} type the type of animation ("expand" or "collapse") + */ + this.createEvent("animComplete", this); + + /** + * Fires when a node is going to be expanded. Return false to stop + * the expand. + * @event collapse + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + */ + this.createEvent("collapse", this); + + /** + * Fires when a node is going to be collapsed. Return false to stop + * the collapse. + * @event expand + * @type CustomEvent + * @param {YAHOO.widget.Node} node the node that is expanding/collapsing + */ + this.createEvent("expand", this); + + this._nodes = []; + + // store a global reference + YAHOO.widget.TreeView.trees[this.id] = this; + + // Set up the root node + this.root = new YAHOO.widget.RootNode(this); + + + }, + + /** + * Renders the tree boilerplate and visible nodes + * @method draw + */ + draw: function() { + var html = this.root.getHtml(); + this.getEl().innerHTML = html; + this.firstDraw = false; + }, + + /** + * Returns the tree's host element + * @method getEl + * @return {HTMLElement} the host element + */ + getEl: function() { + if (! this._el) { + this._el = document.getElementById(this.id); + } + return this._el; + }, + + /** + * Nodes register themselves with the tree instance when they are created. + * @method regNode + * @param node {Node} the node to register + * @private + */ + regNode: function(node) { + this._nodes[node.index] = node; + }, + + /** + * Returns the root node of this tree + * @method getRoot + * @return {Node} the root node + */ + getRoot: function() { + return this.root; + }, + + /** + * Configures this tree to dynamically load all child data + * @method setDynamicLoad + * @param {function} fnDataLoader the function that will be called to get the data + * @param iconMode {int} configures the icon that is displayed when a dynamic + * load node is expanded the first time without children. By default, the + * "collapse" icon will be used. If set to 1, the leaf node icon will be + * displayed. + */ + setDynamicLoad: function(fnDataLoader, iconMode) { + this.root.setDynamicLoad(fnDataLoader, iconMode); + }, + + /** + * Expands all child nodes. Note: this conflicts with the "multiExpand" + * node property. If expand all is called in a tree with nodes that + * do not allow multiple siblings to be displayed, only the last sibling + * will be expanded. + * @method expandAll + */ + expandAll: function() { + if (!this.locked) { + this.root.expandAll(); + } + }, + + /** + * Collapses all expanded child nodes in the entire tree. + * @method collapseAll + */ + collapseAll: function() { + if (!this.locked) { + this.root.collapseAll(); + } + }, + + /** + * Returns a node in the tree that has the specified index (this index + * is created internally, so this function probably will only be used + * in html generated for a given node.) + * @method getNodeByIndex + * @param {int} nodeIndex the index of the node wanted + * @return {Node} the node with index=nodeIndex, null if no match + */ + getNodeByIndex: function(nodeIndex) { + var n = this._nodes[nodeIndex]; + return (n) ? n : null; + }, + + /** + * Returns a node that has a matching property and value in the data + * object that was passed into its constructor. + * @method getNodeByProperty + * @param {object} property the property to search (usually a string) + * @param {object} value the value we want to find (usuall an int or string) + * @return {Node} the matching node, null if no match + */ + getNodeByProperty: function(property, value) { + for (var i in this._nodes) { + var n = this._nodes[i]; + if (n.data && value == n.data[property]) { + return n; + } + } + + return null; + }, + + /** + * Returns a collection of nodes that have a matching property + * and value in the data object that was passed into its constructor. + * @method getNodesByProperty + * @param {object} property the property to search (usually a string) + * @param {object} value the value we want to find (usuall an int or string) + * @return {Array} the matching collection of nodes, null if no match + */ + getNodesByProperty: function(property, value) { + var values = []; + for (var i in this._nodes) { + var n = this._nodes[i]; + if (n.data && value == n.data[property]) { + values.push(n); + } + } + + return (values.length) ? values : null; + }, + + /** + * Removes the node and its children, and optionally refreshes the + * branch of the tree that was affected. + * @method removeNode + * @param {Node} The node to remove + * @param {boolean} autoRefresh automatically refreshes branch if true + * @return {boolean} False is there was a problem, true otherwise. + */ + removeNode: function(node, autoRefresh) { + + // Don't delete the root node + if (node.isRoot()) { + return false; + } + + // Get the branch that we may need to refresh + var p = node.parent; + if (p.parent) { + p = p.parent; + } + + // Delete the node and its children + this._deleteNode(node); + + // Refresh the parent of the parent + if (autoRefresh && p && p.childrenRendered) { + p.refresh(); + } + + return true; + }, + + /** + * Deletes this nodes child collection, recursively. Also collapses + * the node, and resets the dynamic load flag. The primary use for + * this method is to purge a node and allow it to fetch its data + * dynamically again. + * @method removeChildren + * @param {Node} node the node to purge + */ + removeChildren: function(node) { + while (node.children.length) { + this._deleteNode(node.children[0]); + } + + node.childrenRendered = false; + node.dynamicLoadComplete = false; + if (node.expanded) { + node.collapse(); + } else { + node.updateIcon(); + } + }, + + /** + * Deletes the node and recurses children + * @method _deleteNode + * @private + */ + _deleteNode: function(node) { + // Remove all the child nodes first + this.removeChildren(node); + + // Remove the node from the tree + this.popNode(node); + }, + + /** + * Removes the node from the tree, preserving the child collection + * to make it possible to insert the branch into another part of the + * tree, or another tree. + * @method popNode + * @param {Node} the node to remove + */ + popNode: function(node) { + var p = node.parent; + + // Update the parent's collection of children + var a = []; + + for (var i=0, len=p.children.length;i";for(var i=0;i ";sb[sb.length]="";}if(this.hasIcon){sb[sb.length]=" ";}sb[sb.length]=" ";sb[sb.length]=this.html;sb[sb.length]=" ";sb[sb.length]="'; + } + + var f = document.createElement("div"); + var s = f.style; + s.position = "absolute"; + s.top = "-1000px"; + s.left = "-1000px"; + f.innerHTML = sb.join(""); + + document.body.appendChild(f); + + YAHOO.widget.TreeView.removeHandler(window, + "load", YAHOO.widget.TreeView.preload); + +}; + +YAHOO.widget.TreeView.addHandler(window, + "load", YAHOO.widget.TreeView.preload); + +/** + * The base class for all tree nodes. The node's presentation and behavior in + * response to mouse events is handled in Node subclasses. + * @namespace YAHOO.widget + * @class Node + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + * @constructor + */ +YAHOO.widget.Node = function(oData, oParent, expanded) { + if (oData) { this.init(oData, oParent, expanded); } +}; + +YAHOO.widget.Node.prototype = { + + /** + * The index for this instance obtained from global counter in YAHOO.widget.TreeView. + * @property index + * @type int + */ + index: 0, + + /** + * This node's child node collection. + * @property children + * @type Node[] + */ + children: null, + + /** + * Tree instance this node is part of + * @property tree + * @type TreeView + */ + tree: null, + + /** + * The data linked to this node. This can be any object or primitive + * value, and the data can be used in getNodeHtml(). + * @property data + * @type object + */ + data: null, + + /** + * Parent node + * @property parent + * @type Node + */ + parent: null, + + /** + * The depth of this node. We start at -1 for the root node. + * @property depth + * @type int + */ + depth: -1, + + /** + * The href for the node's label. If one is not specified, the href will + * be set so that it toggles the node. + * @property href + * @type string + */ + href: null, + + /** + * The label href target, defaults to current window + * @property target + * @type string + */ + target: "_self", + + /** + * The node's expanded/collapsed state + * @property expanded + * @type boolean + */ + expanded: false, + + /** + * Can multiple children be expanded at once? + * @property multiExpand + * @type boolean + */ + multiExpand: true, + + /** + * Should we render children for a collapsed node? It is possible that the + * implementer will want to render the hidden data... @todo verify that we + * need this, and implement it if we do. + * @property renderHidden + * @type boolean + */ + renderHidden: false, + + /** + * This flag is set to true when the html is generated for this node's + * children, and set to false when new children are added. + * @property childrenRendered + * @type boolean + */ + childrenRendered: false, + + /** + * Dynamically loaded nodes only fetch the data the first time they are + * expanded. This flag is set to true once the data has been fetched. + * @property dynamicLoadComplete + * @type boolean + */ + dynamicLoadComplete: false, + + /** + * This node's previous sibling + * @property previousSibling + * @type Node + */ + previousSibling: null, + + /** + * This node's next sibling + * @property nextSibling + * @type Node + */ + nextSibling: null, + + /** + * We can set the node up to call an external method to get the child + * data dynamically. + * @property _dynLoad + * @type boolean + * @private + */ + _dynLoad: false, + + /** + * Function to execute when we need to get this node's child data. + * @property dataLoader + * @type function + */ + dataLoader: null, + + /** + * This is true for dynamically loading nodes while waiting for the + * callback to return. + * @property isLoading + * @type boolean + */ + isLoading: false, + + /** + * The toggle/branch icon will not show if this is set to false. This + * could be useful if the implementer wants to have the child contain + * extra info about the parent, rather than an actual node. + * @property hasIcon + * @type boolean + */ + hasIcon: true, + + /** + * Used to configure what happens when a dynamic load node is expanded + * and we discover that it does not have children. By default, it is + * treated as if it still could have children (plus/minus icon). Set + * iconMode to have it display like a leaf node instead. + * @property iconMode + * @type int + */ + iconMode: 0, + + /** + * The node type + * @property _type + * @private + */ + _type: "Node", + + /* + spacerPath: "http://us.i1.yimg.com/us.yimg.com/i/space.gif", + expandedText: "Expanded", + collapsedText: "Collapsed", + loadingText: "Loading", + */ + + /** + * Initializes this node, gets some of the properties from the parent + * @method init + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + */ + init: function(oData, oParent, expanded) { + + this.data = oData; + this.children = []; + this.index = YAHOO.widget.TreeView.nodeCount; + ++YAHOO.widget.TreeView.nodeCount; + this.expanded = expanded; + + /** + * The parentChange event is fired when a parent element is applied + * to the node. This is useful if you need to apply tree-level + * properties to a tree that need to happen if a node is moved from + * one tre to another. + * + * @event parentChange + * @type CustomEvent + */ + this.createEvent("parentChange", this); + + // oParent should never be null except when we create the root node. + if (oParent) { + oParent.appendChild(this); + } + }, + + /** + * Certain properties for the node cannot be set until the parent + * is known. This is called after the node is inserted into a tree. + * the parent is also applied to this node's children in order to + * make it possible to move a branch from one tree to another. + * @method applyParent + * @param {Node} parentNode this node's parent node + * @return {boolean} true if the application was successful + */ + applyParent: function(parentNode) { + if (!parentNode) { + return false; + } + + this.tree = parentNode.tree; + this.parent = parentNode; + this.depth = parentNode.depth + 1; + + if (!this.href) { + this.href = "javascript:" + this.getToggleLink(); + } + + if (! this.multiExpand) { + this.multiExpand = parentNode.multiExpand; + } + + this.tree.regNode(this); + parentNode.childrenRendered = false; + + // cascade update existing children + for (var i=0, len=this.children.length;i 0 || + (checkForLazyLoad && this.isDynamic() && !this.dynamicLoadComplete) ); + }, + + /** + * Expands if node is collapsed, collapses otherwise. + * @method toggle + */ + toggle: function() { + if (!this.tree.locked && ( this.hasChildren(true) || this.isDynamic()) ) { + if (this.expanded) { this.collapse(); } else { this.expand(); } + } + }, + + /** + * Returns the markup for this node and its children. + * @method getHtml + * @return {string} the markup for this node and its expanded children. + */ + getHtml: function() { + + this.childrenRendered = false; + + var sb = []; + sb[sb.length] = ' '; + sb[sb.length] = this.getNodeHtml(); + sb[sb.length] = this.getChildrenHtml(); + sb[sb.length] = ''; + return sb.join(""); + }, + + /** + * Called when first rendering the tree. We always build the div that will + * contain this nodes children, but we don't render the children themselves + * unless this node is expanded. + * @method getChildrenHtml + * @return {string} the children container div html and any expanded children + * @private + */ + getChildrenHtml: function() { + + var sb = []; + sb[sb.length] = ' '; + + return sb.join(""); + }, + + /** + * Generates the markup for the child nodes. This is not done until the node + * is expanded. + * @method renderChildren + * @return {string} the html for this node's children + * @private + */ + renderChildren: function() { + + + var node = this; + + if (this.isDynamic() && !this.dynamicLoadComplete) { + this.isLoading = true; + this.tree.locked = true; + + if (this.dataLoader) { + + setTimeout( + function() { + node.dataLoader(node, + function() { + node.loadComplete(); + }); + }, 10); + + } else if (this.tree.root.dataLoader) { + + setTimeout( + function() { + node.tree.root.dataLoader(node, + function() { + node.loadComplete(); + }); + }, 10); + + } else { + return "Error: data loader not found or not specified."; + } + + return ""; + + } else { + return this.completeRender(); + } + }, + + /** + * Called when we know we have all the child data. + * @method completeRender + * @return {string} children html + */ + completeRender: function() { + var sb = []; + + for (var i=0; i < this.children.length; ++i) { + // this.children[i].childrenRendered = false; + sb[sb.length] = this.children[i].getHtml(); + } + + this.childrenRendered = true; + + return sb.join(""); + }, + + /** + * Load complete is the callback function we pass to the data provider + * in dynamic load situations. + * @method loadComplete + */ + loadComplete: function() { + this.getChildrenEl().innerHTML = this.completeRender(); + this.dynamicLoadComplete = true; + this.isLoading = false; + this.expand(); + this.tree.locked = false; + }, + + /** + * Returns this node's ancestor at the specified depth. + * @method getAncestor + * @param {int} depth the depth of the ancestor. + * @return {Node} the ancestor + */ + getAncestor: function(depth) { + if (depth >= this.depth || depth < 0) { + return null; + } + + var p = this.parent; + + while (p.depth > depth) { + p = p.parent; + } + + return p; + }, + + /** + * Returns the css class for the spacer at the specified depth for + * this node. If this node's ancestor at the specified depth + * has a next sibling the presentation is different than if it + * does not have a next sibling + * @method getDepthStyle + * @param {int} depth the depth of the ancestor. + * @return {string} the css class for the spacer + */ + getDepthStyle: function(depth) { + return (this.getAncestor(depth).nextSibling) ? + "ygtvdepthcell" : "ygtvblankdepthcell"; + }, + + /** + * Get the markup for the node. This is designed to be overrided so that we can + * support different types of nodes. + * @method getNodeHtml + * @return {string} The HTML that will render this node. + */ + getNodeHtml: function() { + return ""; + }, + + /** + * Regenerates the html for this node and its children. To be used when the + * node is expanded and new children have been added. + * @method refresh + */ + refresh: function() { + // this.loadComplete(); + this.getChildrenEl().innerHTML = this.completeRender(); + + if (this.hasIcon) { + var el = this.getToggleEl(); + if (el) { + el.className = this.getStyle(); + } + } + }, + + /** + * Node toString + * @method toString + * @return {string} string representation of the node + */ + toString: function() { + return "Node (" + this.index + ")"; + } + +}; + +YAHOO.augment(YAHOO.widget.Node, YAHOO.util.EventProvider); + +/** + * A custom YAHOO.widget.Node that handles the unique nature of + * the virtual, presentationless root node. + * @namespace YAHOO.widget + * @class RootNode + * @extends YAHOO.widget.Node + * @param oTree {YAHOO.widget.TreeView} The tree instance this node belongs to + * @constructor + */ +YAHOO.widget.RootNode = function(oTree) { + // Initialize the node with null params. The root node is a + // special case where the node has no presentation. So we have + // to alter the standard properties a bit. + this.init(null, null, true); + + /* + * For the root node, we get the tree reference from as a param + * to the constructor instead of from the parent element. + */ + this.tree = oTree; +}; + +YAHOO.extend(YAHOO.widget.RootNode, YAHOO.widget.Node, { + + // overrides YAHOO.widget.Node + getNodeHtml: function() { + return ""; + }, + + toString: function() { + return "RootNode"; + }, + + loadComplete: function() { + this.tree.draw(); + } + +}); +/** + * The default node presentation. The first parameter should be + * either a string that will be used as the node's label, or an object + * that has a string propery called label. By default, the clicking the + * label will toggle the expanded/collapsed state of the node. By + * changing the href property of the instance, this behavior can be + * changed so that the label will go to the specified href. + * @namespace YAHOO.widget + * @class TextNode + * @extends YAHOO.widget.Node + * @constructor + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {YAHOO.widget.Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + */ +YAHOO.widget.TextNode = function(oData, oParent, expanded) { + + if (oData) { + this.init(oData, oParent, expanded); + this.setUpLabel(oData); + } + +}; + +YAHOO.extend(YAHOO.widget.TextNode, YAHOO.widget.Node, { + + /** + * The CSS class for the label href. Defaults to ygtvlabel, but can be + * overridden to provide a custom presentation for a specific node. + * @property labelStyle + * @type string + */ + labelStyle: "ygtvlabel", + + /** + * The derived element id of the label for this node + * @property labelElId + * @type string + */ + labelElId: null, + + /** + * The text for the label. It is assumed that the oData parameter will + * either be a string that will be used as the label, or an object that + * has a property called "label" that we will use. + * @property label + * @type string + */ + label: null, + + textNodeParentChange: function() { + + /** + * Custom event that is fired when the text node label is clicked. The + * custom event is defined on the tree instance, so there is a single + * event that handles all nodes in the tree. The node clicked is + * provided as an argument + * + * @event labelClick + * @for YAHOO.widget.TreeView + * @param {YAHOO.widget.Node} node the node clicked + */ + if (this.tree && !this.tree.hasEvent("labelClick")) { + this.tree.createEvent("labelClick", this.tree); + } + + }, + + /** + * Sets up the node label + * @method setUpLabel + * @param oData string containing the label, or an object with a label property + */ + setUpLabel: function(oData) { + + // set up the custom event on the tree + this.textNodeParentChange(); + this.subscribe("parentChange", this.textNodeParentChange); + + if (typeof oData == "string") { + oData = { label: oData }; + } + this.label = oData.label; + + // update the link + if (oData.href) { + this.href = oData.href; + } + + // set the target + if (oData.target) { + this.target = oData.target; + } + + if (oData.style) { + this.labelStyle = oData.style; + } + + this.labelElId = "ygtvlabelel" + this.index; + }, + + /** + * Returns the label element + * @for YAHOO.widget.TextNode + * @method getLabelEl + * @return {object} the element + */ + getLabelEl: function() { + return document.getElementById(this.labelElId); + }, + + // overrides YAHOO.widget.Node + getNodeHtml: function() { + var sb = []; + + sb[sb.length] = ''; + sb[sb.length] = '
'; + + return sb.join(""); + }, + + /** + * Executed when the label is clicked. Fires the labelClick custom event. + * @method onLabelClick + * @param me {Node} this node + * @scope the anchor tag clicked + * @return false to cancel the anchor click + */ + onLabelClick: function(me) { + return me.tree.fireEvent("labelClick", me); + //return true; + }, + + toString: function() { + return "TextNode (" + this.index + ") " + this.label; + } + +}); +/** + * A menu-specific implementation that differs from TextNode in that only + * one sibling can be expanded at a time. + * @namespace YAHOO.widget + * @class MenuNode + * @extends YAHOO.widget.TextNode + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {YAHOO.widget.Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + * @constructor + */ +YAHOO.widget.MenuNode = function(oData, oParent, expanded) { + if (oData) { + this.init(oData, oParent, expanded); + this.setUpLabel(oData); + } + + /* + * Menus usually allow only one branch to be open at a time. + */ + this.multiExpand = false; + + +}; + +YAHOO.extend(YAHOO.widget.MenuNode, YAHOO.widget.TextNode, { + + toString: function() { + return "MenuNode (" + this.index + ") " + this.label; + } + +}); +/** + * This implementation takes either a string or object for the + * oData argument. If is it a string, we will use it for the display + * of this node (and it can contain any html code). If the parameter + * is an object, we look for a parameter called "html" that will be + * used for this node's display. + * @namespace YAHOO.widget + * @class HTMLNode + * @extends YAHOO.widget.Node + * @constructor + * @param oData {object} a string or object containing the data that will + * be used to render this node + * @param oParent {YAHOO.widget.Node} this node's parent node + * @param expanded {boolean} the initial expanded/collapsed state + * @param hasIcon {boolean} specifies whether or not leaf nodes should + * have an icon + */ +YAHOO.widget.HTMLNode = function(oData, oParent, expanded, hasIcon) { + if (oData) { + this.init(oData, oParent, expanded); + this.initContent(oData, hasIcon); + } +}; + +YAHOO.extend(YAHOO.widget.HTMLNode, YAHOO.widget.Node, { + + /** + * The CSS class for the html content container. Defaults to ygtvhtml, but + * can be overridden to provide a custom presentation for a specific node. + * @property contentStyle + * @type string + */ + contentStyle: "ygtvhtml", + + /** + * The generated id that will contain the data passed in by the implementer. + * @property contentElId + * @type string + */ + contentElId: null, + + /** + * The HTML content to use for this node's display + * @property content + * @type string + */ + content: null, + + /** + * Sets up the node label + * @property initContent + * @param {object} An html string or object containing an html property + * @param {boolean} hasIcon determines if the node will be rendered with an + * icon or not + */ + initContent: function(oData, hasIcon) { + if (typeof oData == "string") { + oData = { html: oData }; + } + + this.html = oData.html; + this.contentElId = "ygtvcontentel" + this.index; + this.hasIcon = hasIcon; + + }, + + /** + * Returns the outer html element for this node's content + * @method getContentEl + * @return {HTMLElement} the element + */ + getContentEl: function() { + return document.getElementById(this.contentElId); + }, + + // overrides YAHOO.widget.Node + getNodeHtml: function() { + var sb = []; + + sb[sb.length] = ''; + + for (var i=0;i '; + sb[sb.length] = ''; + sb[sb.length] = ' '; + } + + var getNode = 'YAHOO.widget.TreeView.getNode(\'' + + this.tree.id + '\',' + this.index + ')'; + + sb[sb.length] = ' '; + + /* + sb[sb.length] = ' '; + sb[sb.length] = ''; + */ + + sb[sb.length] = ' '; + + sb[sb.length] = '
'; + sb[sb.length] = ''; + sb[sb.length] = this.label; + sb[sb.length] = ''; + sb[sb.length] = ' '; + sb[sb.length] = ''; + sb[sb.length] = '
'; + + return sb.join(""); + }, + + toString: function() { + return "HTMLNode (" + this.index + ")"; + } + +}); +/** + * A static factory class for tree view expand/collapse animations + * @class TVAnim + * @static + */ +YAHOO.widget.TVAnim = function() { + return { + /** + * Constant for the fade in animation + * @property FADE_IN + * @type string + * @static + */ + FADE_IN: "TVFadeIn", + + /** + * Constant for the fade out animation + * @property FADE_OUT + * @type string + * @static + */ + FADE_OUT: "TVFadeOut", + + /** + * Returns a ygAnim instance of the given type + * @method getAnim + * @param type {string} the type of animation + * @param el {HTMLElement} the element to element (probably the children div) + * @param callback {function} function to invoke when the animation is done. + * @return {YAHOO.util.Animation} the animation instance + * @static + */ + getAnim: function(type, el, callback) { + if (YAHOO.widget[type]) { + return new YAHOO.widget[type](el, callback); + } else { + return null; + } + }, + + /** + * Returns true if the specified animation class is available + * @method isValid + * @param type {string} the type of animation + * @return {boolean} true if valid, false if not + * @static + */ + isValid: function(type) { + return (YAHOO.widget[type]); + } + }; +} (); + +/** + * A 1/2 second fade-in animation. + * @class TVFadeIn + * @constructor + * @param el {HTMLElement} the element to animate + * @param callback {function} function to invoke when the animation is finished + */ +YAHOO.widget.TVFadeIn = function(el, callback) { + /** + * The element to animate + * @property el + * @type HTMLElement + */ + this.el = el; + + /** + * the callback to invoke when the animation is complete + * @property callback + * @type function + */ + this.callback = callback; + +}; + +YAHOO.widget.TVFadeIn.prototype = { + /** + * Performs the animation + * @method animate + */ + animate: function() { + var tvanim = this; + + var s = this.el.style; + s.opacity = 0.1; + s.filter = "alpha(opacity=10)"; + s.display = ""; + + var dur = 0.4; + var a = new YAHOO.util.Anim(this.el, {opacity: {from: 0.1, to: 1, unit:""}}, dur); + a.onComplete.subscribe( function() { tvanim.onComplete(); } ); + a.animate(); + }, + + /** + * Clean up and invoke callback + * @method onComplete + */ + onComplete: function() { + this.callback(); + }, + + /** + * toString + * @method toString + * @return {string} the string representation of the instance + */ + toString: function() { + return "TVFadeIn"; + } +}; + +/** + * A 1/2 second fade out animation. + * @class TVFadeOut + * @constructor + * @param el {HTMLElement} the element to animate + * @param callback {Function} function to invoke when the animation is finished + */ +YAHOO.widget.TVFadeOut = function(el, callback) { + /** + * The element to animate + * @property el + * @type HTMLElement + */ + this.el = el; + + /** + * the callback to invoke when the animation is complete + * @property callback + * @type function + */ + this.callback = callback; + +}; + +YAHOO.widget.TVFadeOut.prototype = { + /** + * Performs the animation + * @method animate + */ + animate: function() { + var tvanim = this; + var dur = 0.4; + var a = new YAHOO.util.Anim(this.el, {opacity: {from: 1, to: 0.1, unit:""}}, dur); + a.onComplete.subscribe( function() { tvanim.onComplete(); } ); + a.animate(); + }, + + /** + * Clean up and invoke callback + * @method onComplete + */ + onComplete: function() { + var s = this.el.style; + s.display = "none"; + // s.opacity = 1; + s.filter = "alpha(opacity=100)"; + this.callback(); + }, + + /** + * toString + * @method toString + * @return {string} the string representation of the instance + */ + toString: function() { + return "TVFadeOut"; + } +}; + diff --git a/source/web/scripts/ajax/yahoo/utilities/README b/source/web/scripts/ajax/yahoo/utilities/README new file mode 100644 index 0000000000..a174dd9e50 --- /dev/null +++ b/source/web/scripts/ajax/yahoo/utilities/README @@ -0,0 +1,20 @@ +utilities.js Release Notes + +*** version 0.12.0 *** + +The utilities.js file rolls up all of the YUI utility components into a single +file; it includes the following components: + +* Yahoo Global Object +* Event +* Dom +* Connection Manager +* Animation +* Drag & Drop + +For implementations that use a significant subset of these files, it may prove +more efficient to include utilities.js as opposed to including separate files +for each component. + +Note: If you are require only the Yahoo Global Object, Event Utility and Dom +collection, consider using the special yahoo-event-dom.js aggregate instead. diff --git a/source/web/scripts/ajax/yahoo/utilities/utilities.js b/source/web/scripts/ajax/yahoo/utilities/utilities.js new file mode 100644 index 0000000000..0874a9a36b --- /dev/null +++ b/source/web/scripts/ajax/yahoo/utilities/utilities.js @@ -0,0 +1,100 @@ +/* Copyright (c) 2006, Yahoo! Inc. All rights reserved.Code licensed under the BSD License:http://developer.yahoo.net/yui/license.txtversion: 0.12.0 */ +if(typeof YAHOO=="undefined"){var YAHOO={};}YAHOO.namespace=function(){var a=arguments,o=null,i,j,d;for(i=0;i'; + + for (var i=0;i '; + sb[sb.length] = ''; + } + + if (this.hasIcon) { + sb[sb.length] = '
'; + } + + sb[sb.length] = ' '; + sb[sb.length] = this.html; + sb[sb.length] = ' '; + sb[sb.length] = '-1),isSafari=(ua.indexOf('safari')>-1),isGecko=(!isOpera&&!isSafari&&ua.indexOf('gecko')>-1),isIE=(!isOpera&&ua.indexOf('msie')>-1);var patterns={HYPHEN:/(-[a-z])/i};var toCamel=function(property){if(!patterns.HYPHEN.test(property)){return property;}if(propertyCache[property]){return propertyCache[property];}while(patterns.HYPHEN.exec(property)){property=property.replace(RegExp.$1,RegExp.$1.substr(1).toUpperCase());}propertyCache[property]=property;return property;};if(document.defaultView&&document.defaultView.getComputedStyle){getStyle=function(el,property){var value=null;var computed=document.defaultView.getComputedStyle(el,'');if(computed){value=computed[toCamel(property)];}return el.style[property]||value;};}else if(document.documentElement.currentStyle&&isIE){getStyle=function(el,property){switch(toCamel(property)){case'opacity':var val=100;try{val=el.filters['DXImageTransform.Microsoft.Alpha'].opacity;}catch(e){try{val=el.filters('alpha').opacity;}catch(e){}}return val/100;break;default:var value=el.currentStyle?el.currentStyle[property]:null;return(el.style[property]||value);}};}else{getStyle=function(el,property){return el.style[property];};}if(isIE){setStyle=function(el,property,val){switch(property){case'opacity':if(typeof el.style.filter=='string'){el.style.filter='alpha(opacity='+val*100+')';if(!el.currentStyle||!el.currentStyle.hasLayout){el.style.zoom=1;}}break;default:el.style[property]=val;}};}else{setStyle=function(el,property,val){el.style[property]=val;};}YAHOO.util.Dom={get:function(el){if(!el){return null;}if(typeof el!='string'&&!(el instanceof Array)){return el;}if(typeof el=='string'){return document.getElementById(el);}else{var collection=[];for(var i=0,len=el.length;i =this.left&®ion.right<=this.right&®ion.top>=this.top&®ion.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(region){var t=Math.max(this.top,region.top);var r=Math.min(this.right,region.right);var b=Math.min(this.bottom,region.bottom);var l=Math.max(this.left,region.left);if(b>=t&&r>=l){return new YAHOO.util.Region(t,r,b,l);}else{return null;}};YAHOO.util.Region.prototype.union=function(region){var t=Math.min(this.top,region.top);var r=Math.max(this.right,region.right);var b=Math.max(this.bottom,region.bottom);var l=Math.min(this.left,region.left);return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+"}");};YAHOO.util.Region.getRegion=function(el){var p=YAHOO.util.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Point=function(x,y){if(x instanceof Array){y=x[1];x=x[0];}this.x=this.right=this.left=this[0]=x;this.y=this.top=this.bottom=this[1]=y;};YAHOO.util.Point.prototype=new YAHOO.util.Region();YAHOO.util.CustomEvent=function(_1,_2,_3,_4){this.type=_1;this.scope=_2||window;this.silent=_3;this.signature=_4||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var _5="_YUICEOnSubscribe";if(_1!==_5){this.subscribeEvent=new YAHOO.util.CustomEvent(_5,this,true);}};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(fn,_7,_8){if(this.subscribeEvent){this.subscribeEvent.fire(fn,_7,_8);}this.subscribers.push(new YAHOO.util.Subscriber(fn,_7,_8));},unsubscribe:function(fn,_9){var _10=false;for(var i=0,len=this.subscribers.length;i 0){_17=_14[0];}ret=s.fn.call(_16,_17,s.obj);}else{ret=s.fn.call(_16,this.type,_14,s.obj);}if(false===ret){if(!this.silent){}return false;}}}return true;},unsubscribeAll:function(){for(var i=0,len=this.subscribers.length;i =0){_60=_23[_61];}if(!el||!_60){return false;}if(this.useLegacyEvent(el,_59)){var _62=this.getLegacyIndex(el,_59);var _63=_26[_62];if(_63){for(i=0,len=_63.length;i 0);}var _76=[];for(var i=0,len=_28.length;i 0){for(var i=0,len=_23.length;i 0){j=_23.length;while(j){index=j-1;l=_23[index];if(l){EU.removeListener(l[EU.EL],l[EU.TYPE],l[EU.FN],index);}j=j-1;}l=null;EU.clearCache();}for(i=0,len=_25.length;i 0)?val:0;}YAHOO.util.Dom.setStyle(this.getEl(),attr,val+unit);},getAttribute:function(attr){var el=this.getEl();var val=YAHOO.util.Dom.getStyle(el,attr);if(val!=='auto'&&!this.patterns.offsetUnit.test(val)){return parseFloat(val);}var a=this.patterns.offsetAttribute.exec(attr)||[];var pos=!!(a[3]);var box=!!(a[2]);if(box||(YAHOO.util.Dom.getStyle(el,'position')=='absolute'&&pos)){val=el['offset'+a[0].charAt(0).toUpperCase()+a[0].substr(1)];}else{val=0;}return val;},getDefaultUnit:function(attr){if(this.patterns.defaultUnit.test(attr)){return'px';}return'';},setRuntimeAttribute:function(attr){var start;var end;var attributes=this.attributes;this.runtimeAttributes[attr]={};var isset=function(prop){return(typeof prop!=='undefined');};if(!isset(attributes[attr]['to'])&&!isset(attributes[attr]['by'])){return false;}start=(isset(attributes[attr]['from']))?attributes[attr]['from']:this.getAttribute(attr);if(isset(attributes[attr]['to'])){end=attributes[attr]['to'];}else if(isset(attributes[attr]['by'])){if(start.constructor==Array){end=[];for(var i=0,len=start.length;i 0&&isFinite(tweak)){if(tween.currentFrame+tweak>=frames){tweak=frames-(frame+1);}tween.currentFrame+=tweak;}};};YAHOO.util.Bezier=new function(){this.getPosition=function(points,t){var n=points.length;var tmp=[];for(var i=0;i 0&&!(control[0]instanceof Array)){control=[control];}else{var tmp=[];for(i=0,len=control.length;i 0){this.runtimeAttributes[attr]=this.runtimeAttributes[attr].concat(control);}this.runtimeAttributes[attr][this.runtimeAttributes[attr].length]=end;}else{superclass.setRuntimeAttribute.call(this,attr);}};var translateValues=function(val,start){var pageXY=Y.Dom.getXY(this.getEl());val=[val[0]-pageXY[0]+start[0],val[1]-pageXY[1]+start[1]];return val;};var isset=function(prop){return(typeof prop!=='undefined');};})();(function(){YAHOO.util.Scroll=function(el,attributes,duration,method){if(el){YAHOO.util.Scroll.superclass.constructor.call(this,el,attributes,duration,method);}};YAHOO.extend(YAHOO.util.Scroll,YAHOO.util.ColorAnim);var Y=YAHOO.util;var superclass=Y.Scroll.superclass;var proto=Y.Scroll.prototype;proto.toString=function(){var el=this.getEl();var id=el.id||el.tagName;return("Scroll "+id);};proto.doMethod=function(attr,start,end){var val=null;if(attr=='scroll'){val=[this.method(this.currentFrame,start[0],end[0]-start[0],this.totalFrames),this.method(this.currentFrame,start[1],end[1]-start[1],this.totalFrames)];}else{val=superclass.doMethod.call(this,attr,start,end);}return val;};proto.getAttribute=function(attr){var val=null;var el=this.getEl();if(attr=='scroll'){val=[el.scrollLeft,el.scrollTop];}else{val=superclass.getAttribute.call(this,attr);}return val;};proto.setAttribute=function(attr,val,unit){var el=this.getEl();if(attr=='scroll'){el.scrollLeft=val[0];el.scrollTop=val[1];}else{superclass.setAttribute.call(this,attr,val,unit);}};})();(function(){var _1=YAHOO.util.Event;var _2=YAHOO.util.Dom;YAHOO.util.DragDrop=function(id,_4,_5){if(id){this.init(id,_4,_5);}};YAHOO.util.DragDrop.prototype={id:null,config:null,dragElId:null,handleElId:null,invalidHandleTypes:null,invalidHandleIds:null,invalidHandleClasses:null,startPageX:0,startPageY:0,groups:null,locked:false,lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isTarget:true,padding:null,_domRef:null,__ygDragDrop:true,constrainX:false,constrainY:false,minX:0,maxX:0,minY:0,maxY:0,maintainOffset:false,xTicks:null,yTicks:null,primaryButtonOnly:true,available:false,hasOuterHandles:false,b4StartDrag:function(x,y){},startDrag:function(x,y){},b4Drag:function(e){},onDrag:function(e){},onDragEnter:function(e,id){},b4DragOver:function(e){},onDragOver:function(e,id){},b4DragOut:function(e){},onDragOut:function(e,id){},b4DragDrop:function(e){},onDragDrop:function(e,id){},onInvalidDrop:function(e){},b4EndDrag:function(e){},endDrag:function(e){},b4MouseDown:function(e){},onMouseDown:function(e){},onMouseUp:function(e){},onAvailable:function(){},getEl:function(){if(!this._domRef){this._domRef=_2.get(this.id);}return this._domRef;},getDragEl:function(){return _2.get(this.dragElId);},init:function(id,_9,_10){this.initTarget(id,_9,_10);_1.on(this.id,"mousedown",this.handleMouseDown,this,true);},initTarget:function(id,_11,_12){this.config=_12||{};this.DDM=YAHOO.util.DDM;this.groups={};if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.id=id;this.addToGroup((_11)?_11:"default");this.handleElId=id;_1.onAvailable(id,this.handleOnAvailable,this,true);this.setDragElId(id);this.invalidHandleTypes={A:"A"};this.invalidHandleIds={};this.invalidHandleClasses=[];this.applyConfig();},applyConfig:function(){this.padding=this.config.padding||[0,0,0,0];this.isTarget=(this.config.isTarget!==false);this.maintainOffset=(this.config.maintainOffset);this.primaryButtonOnly=(this.config.primaryButtonOnly!==false);},handleOnAvailable:function(){this.available=true;this.resetConstraints();this.onAvailable();},setPadding:function(_13,_14,_15,_16){if(!_14&&0!==_14){this.padding=[_13,_13,_13,_13];}else{if(!_15&&0!==_15){this.padding=[_13,_14,_13,_14];}else{this.padding=[_13,_14,_15,_16];}}},setInitPosition:function(_17,_18){var el=this.getEl();if(!this.DDM.verifyEl(el)){return;}var dx=_17||0;var dy=_18||0;var p=_2.getXY(el);this.initPageX=p[0]-dx;this.initPageY=p[1]-dy;this.lastPageX=p[0];this.lastPageY=p[1];this.setStartPosition(p);},setStartPosition:function(pos){var p=pos||_2.getXY(this.getEl());this.deltaSetXY=null;this.startPageX=p[0];this.startPageY=p[1];},addToGroup:function(_24){this.groups[_24]=true;this.DDM.regDragDrop(this,_24);},removeFromGroup:function(_25){if(this.groups[_25]){delete this.groups[_25];}this.DDM.removeDDFromGroup(this,_25);},setDragElId:function(id){this.dragElId=id;},setHandleElId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.handleElId=id;this.DDM.regHandle(this.id,id);},setOuterHandleElId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}_1.on(id,"mousedown",this.handleMouseDown,this,true);this.setHandleElId(id);this.hasOuterHandles=true;},unreg:function(){_1.removeListener(this.id,"mousedown",this.handleMouseDown);this._domRef=null;this.DDM._remove(this);},isLocked:function(){return (this.DDM.isLocked()||this.locked);},handleMouseDown:function(e,oDD){var _27=e.which||e.button;if(this.primaryButtonOnly&&_27>1){return;}if(this.isLocked()){return;}this.DDM.refreshCache(this.groups);var pt=new YAHOO.util.Point(_1.getPageX(e),_1.getPageY(e));if(!this.hasOuterHandles&&!this.DDM.isOverTarget(pt,this)){}else{if(this.clickValidator(e)){this.setStartPosition();this.b4MouseDown(e);this.onMouseDown(e);this.DDM.handleMouseDown(e,this);this.DDM.stopEvent(e);}else{}}},clickValidator:function(e){var _29=_1.getTarget(e);return (this.isValidHandleChild(_29)&&(this.id==this.handleElId||this.DDM.handleWasClicked(_29,this.id)));},addInvalidHandleType:function(_30){var _31=_30.toUpperCase();this.invalidHandleTypes[_31]=_31;},addInvalidHandleId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}this.invalidHandleIds[id]=id;},addInvalidHandleClass:function(_32){this.invalidHandleClasses.push(_32);},removeInvalidHandleType:function(_33){var _34=_33.toUpperCase();delete this.invalidHandleTypes[_34];},removeInvalidHandleId:function(id){if(typeof id!=="string"){YAHOO.log("id is not a string, assuming it is an HTMLElement");id=_2.generateId(id);}delete this.invalidHandleIds[id];},removeInvalidHandleClass:function(_35){for(var i=0,len=this.invalidHandleClasses.length;i =this.minX;i=i-_41){if(!_42[i]){this.xTicks[this.xTicks.length]=i;_42[i]=true;}}for(i=this.initPageX;i<=this.maxX;i=i+_41){if(!_42[i]){this.xTicks[this.xTicks.length]=i;_42[i]=true;}}this.xTicks.sort(this.DDM.numericSort);},setYTicks:function(_43,_44){this.yTicks=[];this.yTickSize=_44;var _45={};for(var i=this.initPageY;i>=this.minY;i=i-_44){if(!_45[i]){this.yTicks[this.yTicks.length]=i;_45[i]=true;}}for(i=this.initPageY;i<=this.maxY;i=i+_44){if(!_45[i]){this.yTicks[this.yTicks.length]=i;_45[i]=true;}}this.yTicks.sort(this.DDM.numericSort);},setXConstraint:function(_46,_47,_48){this.leftConstraint=_46;this.rightConstraint=_47;this.minX=this.initPageX-_46;this.maxX=this.initPageX+_47;if(_48){this.setXTicks(this.initPageX,_48);}this.constrainX=true;},clearConstraints:function(){this.constrainX=false;this.constrainY=false;this.clearTicks();},clearTicks:function(){this.xTicks=null;this.yTicks=null;this.xTickSize=0;this.yTickSize=0;},setYConstraint:function(iUp,_50,_51){this.topConstraint=iUp;this.bottomConstraint=_50;this.minY=this.initPageY-iUp;this.maxY=this.initPageY+_50;if(_51){this.setYTicks(this.initPageY,_51);}this.constrainY=true;},resetConstraints:function(){if(this.initPageX||this.initPageX===0){var dx=(this.maintainOffset)?this.lastPageX-this.initPageX:0;var dy=(this.maintainOffset)?this.lastPageY-this.initPageY:0;this.setInitPosition(dx,dy);}else{this.setInitPosition();}if(this.constrainX){this.setXConstraint(this.leftConstraint,this.rightConstraint,this.xTickSize);}if(this.constrainY){this.setYConstraint(this.topConstraint,this.bottomConstraint,this.yTickSize);}},getTick:function(val,_53){if(!_53){return val;}else{if(_53[0]>=val){return _53[0];}else{for(var i=0,len=_53.length;i =val){var _55=val-_53[i];var _56=_53[_54]-val;return (_56>_55)?_53[i]:_53[_54];}}return _53[_53.length-1];}}},toString:function(){return ("DragDrop "+this.id);}};})();if(!YAHOO.util.DragDropMgr){YAHOO.util.DragDropMgr=function(){var _57=YAHOO.util.Event;return {ids:{},handleIds:{},dragCurrent:null,dragOvers:{},deltaX:0,deltaY:0,preventDefault:true,stopPropagation:true,initalized:false,locked:false,init:function(){this.initialized=true;},POINT:0,INTERSECT:1,mode:0,_execOnAll:function(_58,_59){for(var i in this.ids){for(var j in this.ids[i]){var oDD=this.ids[i][j];if(!this.isTypeOfDD(oDD)){continue;}oDD[_58].apply(oDD,_59);}}},_onLoad:function(){this.init();_57.on(document,"mouseup",this.handleMouseUp,this,true);_57.on(document,"mousemove",this.handleMouseMove,this,true);_57.on(window,"unload",this._onUnload,this,true);_57.on(window,"resize",this._onResize,this,true);},_onResize:function(e){this._execOnAll("resetConstraints",[]);},lock:function(){this.locked=true;},unlock:function(){this.locked=false;},isLocked:function(){return this.locked;},locationCache:{},useCache:true,clickPixelThresh:3,clickTimeThresh:1000,dragThreshMet:false,clickTimeout:null,startX:0,startY:0,regDragDrop:function(oDD,_61){if(!this.initialized){this.init();}if(!this.ids[_61]){this.ids[_61]={};}this.ids[_61][oDD.id]=oDD;},removeDDFromGroup:function(oDD,_62){if(!this.ids[_62]){this.ids[_62]={};}var obj=this.ids[_62];if(obj&&obj[oDD.id]){delete obj[oDD.id];}},_remove:function(oDD){for(var g in oDD.groups){if(g&&this.ids[g][oDD.id]){delete this.ids[g][oDD.id];}}delete this.handleIds[oDD.id];},regHandle:function(_65,_66){if(!this.handleIds[_65]){this.handleIds[_65]={};}this.handleIds[_65][_66]=_66;},isDragDrop:function(id){return (this.getDDById(id))?true:false;},getRelated:function(_67,_68){var _69=[];for(var i in _67.groups){for(j in this.ids[i]){var dd=this.ids[i][j];if(!this.isTypeOfDD(dd)){continue;}if(!_68||dd.isTarget){_69[_69.length]=dd;}}}return _69;},isLegalTarget:function(oDD,_71){var _72=this.getRelated(oDD,true);for(var i=0,len=_72.length;i this.clickPixelThresh||_77>this.clickPixelThresh){this.startDrag(this.startX,this.startY);}}if(this.dragThreshMet){this.dragCurrent.b4Drag(e);this.dragCurrent.onDrag(e);this.fireEvents(e,false);}this.stopEvent(e);return true;},fireEvents:function(e,_78){var dc=this.dragCurrent;if(!dc||dc.isLocked()){return;}var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);var pt=new YAHOO.util.Point(x,y);var _80=[];var _81=[];var _82=[];var _83=[];var _84=[];for(var i in this.dragOvers){var ddo=this.dragOvers[i];if(!this.isTypeOfDD(ddo)){continue;}if(!this.isOverTarget(pt,ddo,this.mode)){_81.push(ddo);}_80[i]=true;delete this.dragOvers[i];}for(var _86 in dc.groups){if("string"!=typeof _86){continue;}for(i in this.ids[_86]){var oDD=this.ids[_86][i];if(!this.isTypeOfDD(oDD)){continue;}if(oDD.isTarget&&!oDD.isLocked()&&oDD!=dc){if(this.isOverTarget(pt,oDD,this.mode)){if(_78){_83.push(oDD);}else{if(!_80[oDD.id]){_84.push(oDD);}else{_82.push(oDD);}this.dragOvers[oDD.id]=oDD;}}}}}if(this.mode){if(_81.length){dc.b4DragOut(e,_81);dc.onDragOut(e,_81);}if(_84.length){dc.onDragEnter(e,_84);}if(_82.length){dc.b4DragOver(e,_82);dc.onDragOver(e,_82);}if(_83.length){dc.b4DragDrop(e,_83);dc.onDragDrop(e,_83);}}else{var len=0;for(i=0,len=_81.length;i 2000){}else{setTimeout(DDM._addListeners,10);if(document&&document.body){DDM._timeoutCount+=1;}}}},handleWasClicked:function(node,id){if(this.isHandle(id,node.id)){return true;}else{var p=node.parentNode;while(p){if(this.isHandle(id,p.id)){return true;}else{p=p.parentNode;}}}return false;}};}();YAHOO.util.DDM=YAHOO.util.DragDropMgr;YAHOO.util.DDM._addListeners();}YAHOO.util.DD=function(id,_111,_112){if(id){this.init(id,_111,_112);}};YAHOO.extend(YAHOO.util.DD,YAHOO.util.DragDrop,{scroll:true,autoOffset:function(_113,_114){var x=_113-this.startPageX;var y=_114-this.startPageY;this.setDelta(x,y);},setDelta:function(_115,_116){this.deltaX=_115;this.deltaY=_116;},setDragElPos:function(_117,_118){var el=this.getDragEl();this.alignElWithMouse(el,_117,_118);},alignElWithMouse:function(el,_119,_120){var _121=this.getTargetCoord(_119,_120);if(!this.deltaSetXY){var _122=[_121.x,_121.y];YAHOO.util.Dom.setXY(el,_122);var _123=parseInt(YAHOO.util.Dom.getStyle(el,"left"),10);var _124=parseInt(YAHOO.util.Dom.getStyle(el,"top"),10);this.deltaSetXY=[_123-_121.x,_124-_121.y];}else{YAHOO.util.Dom.setStyle(el,"left",(_121.x+this.deltaSetXY[0])+"px");YAHOO.util.Dom.setStyle(el,"top",(_121.y+this.deltaSetXY[1])+"px");}this.cachePosition(_121.x,_121.y);this.autoScroll(_121.x,_121.y,el.offsetHeight,el.offsetWidth);},cachePosition:function(_125,_126){if(_125){this.lastPageX=_125;this.lastPageY=_126;}else{var _127=YAHOO.util.Dom.getXY(this.getEl());this.lastPageX=_127[0];this.lastPageY=_127[1];}},autoScroll:function(x,y,h,w){if(this.scroll){var _130=this.DDM.getClientHeight();var _131=this.DDM.getClientWidth();var st=this.DDM.getScrollTop();var sl=this.DDM.getScrollLeft();var bot=h+y;var _135=w+x;var _136=(_130+st-y-this.deltaY);var _137=(_131+sl-x-this.deltaX);var _138=40;var _139=(document.all)?80:30;if(bot>_130&&_136<_138){window.scrollTo(sl,st+_139);}if(y 0&&y-st<_138){window.scrollTo(sl,st-_139);}if(_135>_131&&_137<_138){window.scrollTo(sl+_139,st);}if(x 0&&x-sl<_138){window.scrollTo(sl-_139,st);}}},getTargetCoord:function(_140,_141){var x=_140-this.deltaX;var y=_141-this.deltaY;if(this.constrainX){if(x this.maxX){x=this.maxX;}}if(this.constrainY){if(y this.maxY){y=this.maxY;}}x=this.getTick(x,this.xTicks);y=this.getTick(y,this.yTicks);return {x:x,y:y};},applyConfig:function(){YAHOO.util.DD.superclass.applyConfig.call(this);this.scroll=(this.config.scroll!==false);},b4MouseDown:function(e){this.autoOffset(YAHOO.util.Event.getPageX(e),YAHOO.util.Event.getPageY(e));},b4Drag:function(e){this.setDragElPos(YAHOO.util.Event.getPageX(e),YAHOO.util.Event.getPageY(e));},toString:function(){return ("DD "+this.id);}});YAHOO.util.DDProxy=function(id,_142,_143){if(id){this.init(id,_142,_143);this.initFrame();}};YAHOO.util.DDProxy.dragElId="ygddfdiv";YAHOO.extend(YAHOO.util.DDProxy,YAHOO.util.DD,{resizeFrame:true,centerFrame:false,createFrame:function(){var self=this;var body=document.body;if(!body||!body.firstChild){setTimeout(function(){self.createFrame();},50);return;}var div=this.getDragEl();if(!div){div=document.createElement("div");div.id=this.dragElId;var s=div.style;s.position="absolute";s.visibility="hidden";s.cursor="move";s.border="2px solid #aaa";s.zIndex=999;body.insertBefore(div,body.firstChild);}},initFrame:function(){this.createFrame();},applyConfig:function(){YAHOO.util.DDProxy.superclass.applyConfig.call(this);this.resizeFrame=(this.config.resizeFrame!==false);this.centerFrame=(this.config.centerFrame);this.setDragElId(this.config.dragElId||YAHOO.util.DDProxy.dragElId);},showFrame:function(_147,_148){var el=this.getEl();var _149=this.getDragEl();var s=_149.style;this._resizeProxy();if(this.centerFrame){this.setDelta(Math.round(parseInt(s.width,10)/2),Math.round(parseInt(s.height,10)/2));}this.setDragElPos(_147,_148);YAHOO.util.Dom.setStyle(_149,"visibility","visible");},_resizeProxy:function(){if(this.resizeFrame){var DOM=YAHOO.util.Dom;var el=this.getEl();var _151=this.getDragEl();var bt=parseInt(DOM.getStyle(_151,"borderTopWidth"),10);var br=parseInt(DOM.getStyle(_151,"borderRightWidth"),10);var bb=parseInt(DOM.getStyle(_151,"borderBottomWidth"),10);var bl=parseInt(DOM.getStyle(_151,"borderLeftWidth"),10);if(isNaN(bt)){bt=0;}if(isNaN(br)){br=0;}if(isNaN(bb)){bb=0;}if(isNaN(bl)){bl=0;}var _156=Math.max(0,el.offsetWidth-br-bl);var _157=Math.max(0,el.offsetHeight-bt-bb);DOM.setStyle(_151,"width",_156+"px");DOM.setStyle(_151,"height",_157+"px");}},b4MouseDown:function(e){var x=YAHOO.util.Event.getPageX(e);var y=YAHOO.util.Event.getPageY(e);this.autoOffset(x,y);this.setDragElPos(x,y);},b4StartDrag:function(x,y){this.showFrame(x,y);},b4EndDrag:function(e){YAHOO.util.Dom.setStyle(this.getDragEl(),"visibility","hidden");},endDrag:function(e){var DOM=YAHOO.util.Dom;var lel=this.getEl();var del=this.getDragEl();DOM.setStyle(del,"visibility","");DOM.setStyle(lel,"visibility","hidden");YAHOO.util.DDM.moveToEl(lel,del);DOM.setStyle(del,"visibility","hidden");DOM.setStyle(lel,"visibility","");},toString:function(){return ("DDProxy "+this.id);}});YAHOO.util.DDTarget=function(id,_160,_161){if(id){this.initTarget(id,_160,_161);}};YAHOO.extend(YAHOO.util.DDTarget,YAHOO.util.DragDrop,{toString:function(){return ("DDTarget "+this.id);}}); +YAHOO.util.Connect={_msxml_progid:['MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP'],_http_header:{},_has_http_headers:false,_use_default_post_header:true,_default_post_header:'application/x-www-form-urlencoded',_isFormSubmit:false,_isFileUpload:false,_formNode:null,_sFormData:null,_poll:{},_timeOut:{},_polling_interval:50,_transaction_id:0,setProgId:function(id) +{this._msxml_progid.unshift(id);},setDefaultPostHeader:function(b) +{this._use_default_post_header=b;},setPollingInterval:function(i) +{if(typeof i=='number'&&isFinite(i)){this._polling_interval=i;}},createXhrObject:function(transactionId) +{var obj,http;try +{http=new XMLHttpRequest();obj={conn:http,tId:transactionId};} +catch(e) +{for(var i=0;i =200&&httpStatus<300){try +{responseObject=this.createResponseObject(o,callback.argument);if(callback.success){if(!callback.scope){callback.success(responseObject);} +else{callback.success.apply(callback.scope,[responseObject]);}}} +catch(e){}} +else{try +{switch(httpStatus){case 12002:case 12029:case 12030:case 12031:case 12152:case 13030:responseObject=this.createExceptionObject(o.tId,callback.argument,(isAbort?isAbort:false));if(callback.failure){if(!callback.scope){callback.failure(responseObject);} +else{callback.failure.apply(callback.scope,[responseObject]);}} +break;default:responseObject=this.createResponseObject(o,callback.argument);if(callback.failure){if(!callback.scope){callback.failure(responseObject);} +else{callback.failure.apply(callback.scope,[responseObject]);}}}} +catch(e){}} +this.releaseObject(o);responseObject=null;},createResponseObject:function(o,callbackArg) +{var obj={};var headerObj={};try +{var headerStr=o.conn.getAllResponseHeaders();var header=headerStr.split('\n');for(var i=0;i ');if(typeof secureUri=='boolean'){io.src='javascript:false';} +else if(typeof secureURI=='string'){io.src=secureUri;}} +else{var io=document.createElement('iframe');io.id=frameId;io.name=frameId;} +io.style.position='absolute';io.style.top='-1000px';io.style.left='-1000px';document.body.appendChild(io);},appendPostData:function(postData) +{var formElements=new Array();var postMessage=postData.split('&');for(var i=0;i 0){try +{for(var i=0;i -1),isSafari=(ua.indexOf('safari')>-1),isGecko=(!isOpera&&!isSafari&&ua.indexOf('gecko')>-1),isIE=(!isOpera&&ua.indexOf('msie')>-1);var patterns={HYPHEN:/(-[a-z])/i};var toCamel=function(property){if(!patterns.HYPHEN.test(property)){return property;}if(propertyCache[property]){return propertyCache[property];}while(patterns.HYPHEN.exec(property)){property=property.replace(RegExp.$1,RegExp.$1.substr(1).toUpperCase());}propertyCache[property]=property;return property;};if(document.defaultView&&document.defaultView.getComputedStyle){getStyle=function(el,property){var value=null;var computed=document.defaultView.getComputedStyle(el,'');if(computed){value=computed[toCamel(property)];}return el.style[property]||value;};}else if(document.documentElement.currentStyle&&isIE){getStyle=function(el,property){switch(toCamel(property)){case'opacity':var val=100;try{val=el.filters['DXImageTransform.Microsoft.Alpha'].opacity;}catch(e){try{val=el.filters('alpha').opacity;}catch(e){}}return val/100;break;default:var value=el.currentStyle?el.currentStyle[property]:null;return(el.style[property]||value);}};}else{getStyle=function(el,property){return el.style[property];};}if(isIE){setStyle=function(el,property,val){switch(property){case'opacity':if(typeof el.style.filter=='string'){el.style.filter='alpha(opacity='+val*100+')';if(!el.currentStyle||!el.currentStyle.hasLayout){el.style.zoom=1;}}break;default:el.style[property]=val;}};}else{setStyle=function(el,property,val){el.style[property]=val;};}YAHOO.util.Dom={get:function(el){if(!el){return null;}if(typeof el!='string'&&!(el instanceof Array)){return el;}if(typeof el=='string'){return document.getElementById(el);}else{var collection=[];for(var i=0,len=el.length;i =this.left&®ion.right<=this.right&®ion.top>=this.top&®ion.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(region){var t=Math.max(this.top,region.top);var r=Math.min(this.right,region.right);var b=Math.min(this.bottom,region.bottom);var l=Math.max(this.left,region.left);if(b>=t&&r>=l){return new YAHOO.util.Region(t,r,b,l);}else{return null;}};YAHOO.util.Region.prototype.union=function(region){var t=Math.min(this.top,region.top);var r=Math.max(this.right,region.right);var b=Math.max(this.bottom,region.bottom);var l=Math.min(this.left,region.left);return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+"}");};YAHOO.util.Region.getRegion=function(el){var p=YAHOO.util.Dom.getXY(el);var t=p[1];var r=p[0]+el.offsetWidth;var b=p[1]+el.offsetHeight;var l=p[0];return new YAHOO.util.Region(t,r,b,l);};YAHOO.util.Point=function(x,y){if(x instanceof Array){y=x[1];x=x[0];}this.x=this.right=this.left=this[0]=x;this.y=this.top=this.bottom=this[1]=y;};YAHOO.util.Point.prototype=new YAHOO.util.Region();YAHOO.util.CustomEvent=function(_1,_2,_3,_4){this.type=_1;this.scope=_2||window;this.silent=_3;this.signature=_4||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent){}var _5="_YUICEOnSubscribe";if(_1!==_5){this.subscribeEvent=new YAHOO.util.CustomEvent(_5,this,true);}};YAHOO.util.CustomEvent.LIST=0;YAHOO.util.CustomEvent.FLAT=1;YAHOO.util.CustomEvent.prototype={subscribe:function(fn,_7,_8){if(this.subscribeEvent){this.subscribeEvent.fire(fn,_7,_8);}this.subscribers.push(new YAHOO.util.Subscriber(fn,_7,_8));},unsubscribe:function(fn,_9){var _10=false;for(var i=0,len=this.subscribers.length;i 0){_17=_14[0];}ret=s.fn.call(_16,_17,s.obj);}else{ret=s.fn.call(_16,this.type,_14,s.obj);}if(false===ret){if(!this.silent){}return false;}}}return true;},unsubscribeAll:function(){for(var i=0,len=this.subscribers.length;i =0){_60=_23[_61];}if(!el||!_60){return false;}if(this.useLegacyEvent(el,_59)){var _62=this.getLegacyIndex(el,_59);var _63=_26[_62];if(_63){for(i=0,len=_63.length;i 0);}var _76=[];for(var i=0,len=_28.length;i 0){for(var i=0,len=_23.length;i 0){j=_23.length;while(j){index=j-1;l=_23[index];if(l){EU.removeListener(l[EU.EL],l[EU.TYPE],l[EU.FN],index);}j=j-1;}l=null;EU.clearCache();}for(i=0,len=_25.length;i + * YAHOO.namespace("property.package"); + * YAHOO.namespace("YAHOO.property.package"); + * + * YAHOO.namespace("really.long.nested.namespace"); + *+ * This fails because "long" is a future reserved word in ECMAScript + * + * @method namespace + * @static + * @param {String*} arguments 1-n namespaces to create + * @return {Object} A reference to the last namespace object created + */ +YAHOO.namespace = function() { + var a=arguments, o=null, i, j, d; + for (i=0; i+ * YAHOO.namespace("property.package"); + * YAHOO.namespace("YAHOO.property.package"); + *
+ * YAHOO.namespace("really.long.nested.namespace"); + *+ * This fails because "long" is a future reserved word in ECMAScript + * + * @method namespace + * @static + * @param {String*} arguments 1-n namespaces to create + * @return {Object} A reference to the last namespace object created + */ +YAHOO.namespace = function() { + var a=arguments, o=null, i, j, d; + for (i=0; i