diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/animation/animation-debug.js b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/animation/animation-debug.js new file mode 100644 index 0000000000..58451e7802 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/animation/animation-debug.js @@ -0,0 +1,1396 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.8.0r4 +*/ +(function() { + +var Y = YAHOO.util; + +/* +Copyright (c) 2006, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +*/ + +/** + * The animation module provides allows effects to be added to HTMLElements. + * @module animation + * @requires yahoo, event, dom + */ + +/** + * + * 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) + */ + +var Anim = function(el, attributes, duration, method) { + if (!el) { + YAHOO.log('element required to create Anim instance', 'error', 'Anim'); + } + this.init(el, attributes, duration, method); +}; + +Anim.NAME = 'Anim'; + +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 (this.constructor.NAME + ': ' + 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) { + var el = this.getEl(); + if ( this.patterns.noNegatives.test(attr) ) { + val = (val > 0) ? val : 0; + } + + if (attr in el && !('style' in el && attr in el.style)) { + el[attr] = val; + } else { + Y.Dom.setStyle(el, 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 = Y.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 + + if ('style' in el) { + // use offsets for width/height and abs pos top/left + if ( box || (Y.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; + } + } else if (attr in el) { + val = el[attr]; + } + + 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. Should only be needed for subclass use. + * @method setRuntimeAttribute + * @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] * 1; // times 1 to cast "by" + } + } else { + end = start + attributes[attr]['by'] * 1; + } + } + + 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); + return true; + }, + + /** + * 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 = Y.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 = !YAHOO.lang.isUndefined(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 || Y.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 = Y.AnimMgr.fps; + + /** + * Changes the animated element + * @method setEl + */ + this.setEl = function(element) { + el = Y.Dom.get(element); + }; + + /** + * 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(Y.AnimMgr.fps * this.duration) : this.duration; + + if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration + this.totalFrames = 1; + } + Y.AnimMgr.registerElement(this); + return true; + }; + + /** + * 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 (!this.isAnimated()) { // nothing to stop + return false; + } + + if (finish) { + this.currentFrame = this.totalFrames; + this._onTween.fire(); + } + Y.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 Y.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 Y.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 Y.CustomEvent('tween', this); + + /** + * Custom event that fires after onTween + * @private + */ + this._onTween = new Y.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 Y.CustomEvent('complete', this); + /** + * Custom event that fires after onComplete + * @private + */ + this._onComplete = new Y.CustomEvent('_complete', this, true); + + this._onStart.subscribe(onStart); + this._onTween.subscribe(onTween); + this._onComplete.subscribe(onComplete); + } +}; + + Y.Anim = Anim; +})(); +/** + * 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 = 1000; + + /** + * 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) { + index = index || getIndex(tween); + if (!tween.isAnimated() || index === -1) { + return false; + } + + tween._onComplete.fire(); + queue.splice(index, 1); + + tweenCount -= 1; + if (tweenCount <= 0) { + this.stop(); + } + + return true; + }; + + /** + * 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) { + this.unRegister(queue[0], 0); + } + + 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; + } + }; + this._queue = queue; + this._getIndex = getIndex; +}; +/** + * 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] ]; + + }; +}; +(function() { +/** + * 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) + */ + +var Anim = function(el, attributes, duration, method) { + if (!el) { + } + this.init(el, attributes, duration, method); +}; + +Anim.NAME = 'Anim'; + +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 (this.constructor.NAME + ': ' + 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) { + var el = this.getEl(); + if ( this.patterns.noNegatives.test(attr) ) { + val = (val > 0) ? val : 0; + } + + if (attr in el && !('style' in el && attr in el.style)) { + el[attr] = val; + } else { + Y.Dom.setStyle(el, 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 = Y.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 + + if ('style' in el) { + // use offsets for width/height and abs pos top/left + if ( box || (Y.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; + } + } else if (attr in el) { + val = el[attr]; + } + + 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. Should only be needed for subclass use. + * @method setRuntimeAttribute + * @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] * 1; // times 1 to cast "by" + } + } else { + end = start + attributes[attr]['by'] * 1; + } + } + + 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); + return true; + }, + + /** + * 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 = Y.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 = !YAHOO.lang.isUndefined(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 || Y.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 = Y.AnimMgr.fps; + + /** + * Changes the animated element + * @method setEl + */ + this.setEl = function(element) { + el = Y.Dom.get(element); + }; + + /** + * 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(Y.AnimMgr.fps * this.duration) : this.duration; + + if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration + this.totalFrames = 1; + } + Y.AnimMgr.registerElement(this); + return true; + }; + + /** + * 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 (!this.isAnimated()) { // nothing to stop + return false; + } + + if (finish) { + this.currentFrame = this.totalFrames; + this._onTween.fire(); + } + Y.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 Y.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 Y.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 Y.CustomEvent('tween', this); + + /** + * Custom event that fires after onTween + * @private + */ + this._onTween = new Y.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 Y.CustomEvent('complete', this); + /** + * Custom event that fires after onComplete + * @private + */ + this._onComplete = new Y.CustomEvent('_complete', this, true); + + this._onStart.subscribe(onStart); + this._onTween.subscribe(onTween); + this._onComplete.subscribe(onComplete); + } +}; + + Y.Anim = Anim; +})(); +/** + * 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 = 1000; + + /** + * 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) { + index = index || getIndex(tween); + if (!tween.isAnimated() || index === -1) { + return false; + } + + tween._onComplete.fire(); + queue.splice(index, 1); + + tweenCount -= 1; + if (tweenCount <= 0) { + this.stop(); + } + + return true; + }; + + /** + * 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) { + this.unRegister(queue[0], 0); + } + + 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; + } + }; + this._queue = queue; + this._getIndex = getIndex; +}; +/** + * 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] ]; + + }; +}; +(function() { +/** + * 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);
The Button Control supports the following types:
+*<input>, <button>,
+ * <a>, or <span> element to
+ * be used to create the button.
+ * @param {HTMLInputElement|
+ * HTMLButtonElement|HTMLElement} p_oElement Object reference for the
+ * <input>, <button>,
+ * <a>, or <span> element to be
+ * used to create the button.
+ * @param {Object} p_oElement Object literal specifying a set of
+ * configuration attributes used to create the button.
+ * @param {Object} p_oAttributes Optional. Object literal specifying a set
+ * of configuration attributes used to create the button.
+ * @namespace YAHOO.widget
+ * @class Button
+ * @constructor
+ * @extends YAHOO.util.Element
+ */
+
+
+
+ // Shorthard for utilities
+
+ var Dom = YAHOO.util.Dom,
+ Event = YAHOO.util.Event,
+ Lang = YAHOO.lang,
+ UA = YAHOO.env.ua,
+ Overlay = YAHOO.widget.Overlay,
+ Menu = YAHOO.widget.Menu,
+
+
+ // Private member variables
+
+ m_oButtons = {}, // Collection of all Button instances
+ m_oOverlayManager = null, // YAHOO.widget.OverlayManager instance
+ m_oSubmitTrigger = null, // The button that submitted the form
+ m_oFocusedButton = null; // The button that has focus
+
+
+
+ // Private methods
+
+
+
+ /**
+ * @method createInputElement
+ * @description Creates an <input> element of the
+ * specified type.
+ * @private
+ * @param {String} p_sType String specifying the type of
+ * <input> element to create.
+ * @param {String} p_sName String specifying the name of
+ * <input> element to create.
+ * @param {String} p_sValue String specifying the value of
+ * <input> element to create.
+ * @param {String} p_bChecked Boolean specifying if the
+ * <input> element is to be checked.
+ * @return {HTMLInputElement}
+ */
+ function createInputElement(p_sType, p_sName, p_sValue, p_bChecked) {
+
+ var oInput,
+ sInput;
+
+ if (Lang.isString(p_sType) && Lang.isString(p_sName)) {
+
+ if (UA.ie) {
+
+ /*
+ For IE it is necessary to create the element with the
+ "type," "name," "value," and "checked" properties set all
+ at once.
+ */
+
+ sInput = "";
+
+ oInput = document.createElement(sInput);
+
+ }
+ else {
+
+ oInput = document.createElement("input");
+ oInput.name = p_sName;
+ oInput.type = p_sType;
+
+ if (p_bChecked) {
+
+ oInput.checked = true;
+
+ }
+
+ }
+
+ oInput.value = p_sValue;
+
+ }
+
+ return oInput;
+
+ }
+
+
+ /**
+ * @method setAttributesFromSrcElement
+ * @description Gets the values for all the attributes of the source element
+ * (either <input> or <a>) that
+ * map to Button configuration attributes and sets them into a collection
+ * that is passed to the Button constructor.
+ * @private
+ * @param {HTMLInputElement|HTMLAnchorElement} p_oElement Object reference to the HTML
+ * element (either <input> or <span>
+ * ) used to create the button.
+ * @param {Object} p_oAttributes Object reference for the collection of
+ * configuration attributes used to create the button.
+ */
+ function setAttributesFromSrcElement(p_oElement, p_oAttributes) {
+
+ var sSrcElementNodeName = p_oElement.nodeName.toUpperCase(),
+ sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME),
+ me = this,
+ oAttribute,
+ oRootNode,
+ sText;
+
+
+ /**
+ * @method setAttributeFromDOMAttribute
+ * @description Gets the value of the specified DOM attribute and sets it
+ * into the collection of configuration attributes used to configure
+ * the button.
+ * @private
+ * @param {String} p_sAttribute String representing the name of the
+ * attribute to retrieve from the DOM element.
+ */
+ function setAttributeFromDOMAttribute(p_sAttribute) {
+
+ if (!(p_sAttribute in p_oAttributes)) {
+
+ /*
+ Need to use "getAttributeNode" instead of "getAttribute"
+ because using "getAttribute," IE will return the innerText
+ of a <button> for the value attribute
+ rather than the value of the "value" attribute.
+ */
+
+ oAttribute = p_oElement.getAttributeNode(p_sAttribute);
+
+
+ if (oAttribute && ("value" in oAttribute)) {
+
+ YAHOO.log("Setting attribute \"" + p_sAttribute +
+ "\" using source element's attribute value of \"" +
+ oAttribute.value + "\"", "info", me.toString());
+
+ p_oAttributes[p_sAttribute] = oAttribute.value;
+
+ }
+
+ }
+
+ }
+
+
+ /**
+ * @method setFormElementProperties
+ * @description Gets the value of the attributes from the form element
+ * and sets them into the collection of configuration attributes used to
+ * configure the button.
+ * @private
+ */
+ function setFormElementProperties() {
+
+ setAttributeFromDOMAttribute("type");
+
+ if (p_oAttributes.type == "button") {
+
+ p_oAttributes.type = "push";
+
+ }
+
+ if (!("disabled" in p_oAttributes)) {
+
+ p_oAttributes.disabled = p_oElement.disabled;
+
+ }
+
+ setAttributeFromDOMAttribute("name");
+ setAttributeFromDOMAttribute("value");
+ setAttributeFromDOMAttribute("title");
+
+ }
+
+
+ switch (sSrcElementNodeName) {
+
+ case "A":
+
+ p_oAttributes.type = "link";
+
+ setAttributeFromDOMAttribute("href");
+ setAttributeFromDOMAttribute("target");
+
+ break;
+
+ case "INPUT":
+
+ setFormElementProperties();
+
+ if (!("checked" in p_oAttributes)) {
+
+ p_oAttributes.checked = p_oElement.checked;
+
+ }
+
+ break;
+
+ case "BUTTON":
+
+ setFormElementProperties();
+
+ oRootNode = p_oElement.parentNode.parentNode;
+
+ if (Dom.hasClass(oRootNode, sClass + "-checked")) {
+
+ p_oAttributes.checked = true;
+
+ }
+
+ if (Dom.hasClass(oRootNode, sClass + "-disabled")) {
+
+ p_oAttributes.disabled = true;
+
+ }
+
+ p_oElement.removeAttribute("value");
+
+ p_oElement.setAttribute("type", "button");
+
+ break;
+
+ }
+
+ p_oElement.removeAttribute("id");
+ p_oElement.removeAttribute("name");
+
+ if (!("tabindex" in p_oAttributes)) {
+
+ p_oAttributes.tabindex = p_oElement.tabIndex;
+
+ }
+
+ if (!("label" in p_oAttributes)) {
+
+ // Set the "label" property
+
+ sText = sSrcElementNodeName == "INPUT" ?
+ p_oElement.value : p_oElement.innerHTML;
+
+
+ if (sText && sText.length > 0) {
+
+ p_oAttributes.label = sText;
+
+ }
+
+ }
+
+ }
+
+
+ /**
+ * @method initConfig
+ * @description Initializes the set of configuration attributes that are
+ * used to instantiate the button.
+ * @private
+ * @param {Object} Object representing the button's set of
+ * configuration attributes.
+ */
+ function initConfig(p_oConfig) {
+
+ var oAttributes = p_oConfig.attributes,
+ oSrcElement = oAttributes.srcelement,
+ sSrcElementNodeName = oSrcElement.nodeName.toUpperCase(),
+ me = this;
+
+
+ if (sSrcElementNodeName == this.NODE_NAME) {
+
+ p_oConfig.element = oSrcElement;
+ p_oConfig.id = oSrcElement.id;
+
+ Dom.getElementsBy(function (p_oElement) {
+
+ switch (p_oElement.nodeName.toUpperCase()) {
+
+ case "BUTTON":
+ case "A":
+ case "INPUT":
+
+ setAttributesFromSrcElement.call(me, p_oElement,
+ oAttributes);
+
+ break;
+
+ }
+
+ }, "*", oSrcElement);
+
+ }
+ else {
+
+ switch (sSrcElementNodeName) {
+
+ case "BUTTON":
+ case "A":
+ case "INPUT":
+
+ setAttributesFromSrcElement.call(this, oSrcElement,
+ oAttributes);
+
+ break;
+
+ }
+
+ }
+
+ }
+
+
+
+ // Constructor
+
+ YAHOO.widget.Button = function (p_oElement, p_oAttributes) {
+
+ if (!Overlay && YAHOO.widget.Overlay) {
+
+ Overlay = YAHOO.widget.Overlay;
+
+ }
+
+
+ if (!Menu && YAHOO.widget.Menu) {
+
+ Menu = YAHOO.widget.Menu;
+
+ }
+
+
+ var fnSuperClass = YAHOO.widget.Button.superclass.constructor,
+ oConfig,
+ oElement;
+
+
+ if (arguments.length == 1 && !Lang.isString(p_oElement) && !p_oElement.nodeName) {
+
+ if (!p_oElement.id) {
+
+ p_oElement.id = Dom.generateId();
+
+ YAHOO.log("No value specified for the button's \"id\" " +
+ "attribute. Setting button id to \"" + p_oElement.id +
+ "\".", "info", this.toString());
+
+ }
+
+ YAHOO.log("No source HTML element. Building the button " +
+ "using the set of configuration attributes.", "info", this.toString());
+
+ fnSuperClass.call(this, (this.createButtonElement(p_oElement.type)), p_oElement);
+
+ }
+ else {
+
+ oConfig = { element: null, attributes: (p_oAttributes || {}) };
+
+
+ if (Lang.isString(p_oElement)) {
+
+ oElement = Dom.get(p_oElement);
+
+ if (oElement) {
+
+ if (!oConfig.attributes.id) {
+
+ oConfig.attributes.id = p_oElement;
+
+ }
+
+ YAHOO.log("Building the button using an existing " +
+ "HTML element as a source element.", "info", this.toString());
+
+
+ oConfig.attributes.srcelement = oElement;
+
+ initConfig.call(this, oConfig);
+
+
+ if (!oConfig.element) {
+
+ YAHOO.log("Source element could not be used " +
+ "as is. Creating a new HTML element for " +
+ "the button.", "info", this.toString());
+
+ oConfig.element = this.createButtonElement(oConfig.attributes.type);
+
+ }
+
+ fnSuperClass.call(this, oConfig.element, oConfig.attributes);
+
+ }
+
+ }
+ else if (p_oElement.nodeName) {
+
+ if (!oConfig.attributes.id) {
+
+ if (p_oElement.id) {
+
+ oConfig.attributes.id = p_oElement.id;
+
+ }
+ else {
+
+ oConfig.attributes.id = Dom.generateId();
+
+ YAHOO.log("No value specified for the button's " +
+ "\"id\" attribute. Setting button id to \"" +
+ oConfig.attributes.id + "\".", "info", this.toString());
+
+ }
+
+ }
+
+ YAHOO.log("Building the button using an existing HTML " +
+ "element as a source element.", "info", this.toString());
+
+
+ oConfig.attributes.srcelement = p_oElement;
+
+ initConfig.call(this, oConfig);
+
+
+ if (!oConfig.element) {
+
+ YAHOO.log("Source element could not be used as is." +
+ " Creating a new HTML element for the button.",
+ "info", this.toString());
+
+ oConfig.element = this.createButtonElement(oConfig.attributes.type);
+
+ }
+
+ fnSuperClass.call(this, oConfig.element, oConfig.attributes);
+
+ }
+
+ }
+
+ };
+
+
+
+ YAHOO.extend(YAHOO.widget.Button, YAHOO.util.Element, {
+
+
+ // Protected properties
+
+
+ /**
+ * @property _button
+ * @description Object reference to the button's internal
+ * <a> or <button> element.
+ * @default null
+ * @protected
+ * @type HTMLAnchorElement|HTMLButtonElement
+ */
+ _button: null,
+
+
+ /**
+ * @property _menu
+ * @description Object reference to the button's menu.
+ * @default null
+ * @protected
+ * @type {YAHOO.widget.Overlay|
+ * YAHOO.widget.Menu}
+ */
+ _menu: null,
+
+
+ /**
+ * @property _hiddenFields
+ * @description Object reference to the <input>
+ * element, or array of HTML form elements used to represent the button
+ * when its parent form is submitted.
+ * @default null
+ * @protected
+ * @type HTMLInputElement|Array
+ */
+ _hiddenFields: null,
+
+
+ /**
+ * @property _onclickAttributeValue
+ * @description Object reference to the button's current value for the
+ * "onclick" configuration attribute.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _onclickAttributeValue: null,
+
+
+ /**
+ * @property _activationKeyPressed
+ * @description Boolean indicating if the key(s) that toggle the button's
+ * "active" state have been pressed.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _activationKeyPressed: false,
+
+
+ /**
+ * @property _activationButtonPressed
+ * @description Boolean indicating if the mouse button that toggles
+ * the button's "active" state has been pressed.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _activationButtonPressed: false,
+
+
+ /**
+ * @property _hasKeyEventHandlers
+ * @description Boolean indicating if the button's "blur", "keydown" and
+ * "keyup" event handlers are assigned
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _hasKeyEventHandlers: false,
+
+
+ /**
+ * @property _hasMouseEventHandlers
+ * @description Boolean indicating if the button's "mouseout,"
+ * "mousedown," and "mouseup" event handlers are assigned
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _hasMouseEventHandlers: false,
+
+
+ /**
+ * @property _nOptionRegionX
+ * @description Number representing the X coordinate of the leftmost edge of the Button's
+ * option region. Applies only to Buttons of type "split".
+ * @default 0
+ * @protected
+ * @type Number
+ */
+ _nOptionRegionX: 0,
+
+
+
+ // Constants
+
+ /**
+ * @property CLASS_NAME_PREFIX
+ * @description Prefix used for all class names applied to a Button.
+ * @default "yui-"
+ * @final
+ * @type String
+ */
+ CLASS_NAME_PREFIX: "yui-",
+
+
+ /**
+ * @property NODE_NAME
+ * @description The name of the node to be used for the button's
+ * root element.
+ * @default "SPAN"
+ * @final
+ * @type String
+ */
+ NODE_NAME: "SPAN",
+
+
+ /**
+ * @property CHECK_ACTIVATION_KEYS
+ * @description Array of numbers representing keys that (when pressed)
+ * toggle the button's "checked" attribute.
+ * @default [32]
+ * @final
+ * @type Array
+ */
+ CHECK_ACTIVATION_KEYS: [32],
+
+
+ /**
+ * @property ACTIVATION_KEYS
+ * @description Array of numbers representing keys that (when presed)
+ * toggle the button's "active" state.
+ * @default [13, 32]
+ * @final
+ * @type Array
+ */
+ ACTIVATION_KEYS: [13, 32],
+
+
+ /**
+ * @property OPTION_AREA_WIDTH
+ * @description Width (in pixels) of the area of a split button that
+ * when pressed will display a menu.
+ * @default 20
+ * @final
+ * @type Number
+ */
+ OPTION_AREA_WIDTH: 20,
+
+
+ /**
+ * @property CSS_CLASS_NAME
+ * @description String representing the CSS class(es) to be applied to
+ * the button's root element.
+ * @default "button"
+ * @final
+ * @type String
+ */
+ CSS_CLASS_NAME: "button",
+
+
+
+ // Protected attribute setter methods
+
+
+ /**
+ * @method _setType
+ * @description Sets the value of the button's "type" attribute.
+ * @protected
+ * @param {String} p_sType String indicating the value for the button's
+ * "type" attribute.
+ */
+ _setType: function (p_sType) {
+
+ if (p_sType == "split") {
+
+ this.on("option", this._onOption);
+
+ }
+
+ },
+
+
+ /**
+ * @method _setLabel
+ * @description Sets the value of the button's "label" attribute.
+ * @protected
+ * @param {String} p_sLabel String indicating the value for the button's
+ * "label" attribute.
+ */
+ _setLabel: function (p_sLabel) {
+
+ this._button.innerHTML = p_sLabel;
+
+
+ /*
+ Remove and add the default class name from the root element
+ for Gecko to ensure that the button shrinkwraps to the label.
+ Without this the button will not be rendered at the correct
+ width when the label changes. The most likely cause for this
+ bug is button's use of the Gecko-specific CSS display type of
+ "-moz-inline-box" to simulate "inline-block" supported by IE,
+ Safari and Opera.
+ */
+
+ var sClass,
+ nGeckoVersion = UA.gecko;
+
+
+ if (nGeckoVersion && nGeckoVersion < 1.9 && Dom.inDocument(this.get("element"))) {
+
+ sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME);
+
+ this.removeClass(sClass);
+
+ Lang.later(0, this, this.addClass, sClass);
+
+ }
+
+ },
+
+
+ /**
+ * @method _setTabIndex
+ * @description Sets the value of the button's "tabindex" attribute.
+ * @protected
+ * @param {Number} p_nTabIndex Number indicating the value for the
+ * button's "tabindex" attribute.
+ */
+ _setTabIndex: function (p_nTabIndex) {
+
+ this._button.tabIndex = p_nTabIndex;
+
+ },
+
+
+ /**
+ * @method _setTitle
+ * @description Sets the value of the button's "title" attribute.
+ * @protected
+ * @param {String} p_nTabIndex Number indicating the value for
+ * the button's "title" attribute.
+ */
+ _setTitle: function (p_sTitle) {
+
+ if (this.get("type") != "link") {
+
+ this._button.title = p_sTitle;
+
+ }
+
+ },
+
+
+ /**
+ * @method _setDisabled
+ * @description Sets the value of the button's "disabled" attribute.
+ * @protected
+ * @param {Boolean} p_bDisabled Boolean indicating the value for
+ * the button's "disabled" attribute.
+ */
+ _setDisabled: function (p_bDisabled) {
+
+ if (this.get("type") != "link") {
+
+ if (p_bDisabled) {
+
+ if (this._menu) {
+
+ this._menu.hide();
+
+ }
+
+ if (this.hasFocus()) {
+
+ this.blur();
+
+ }
+
+ this._button.setAttribute("disabled", "disabled");
+
+ this.addStateCSSClasses("disabled");
+
+ this.removeStateCSSClasses("hover");
+ this.removeStateCSSClasses("active");
+ this.removeStateCSSClasses("focus");
+
+ }
+ else {
+
+ this._button.removeAttribute("disabled");
+
+ this.removeStateCSSClasses("disabled");
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _setHref
+ * @description Sets the value of the button's "href" attribute.
+ * @protected
+ * @param {String} p_sHref String indicating the value for the button's
+ * "href" attribute.
+ */
+ _setHref: function (p_sHref) {
+
+ if (this.get("type") == "link") {
+
+ this._button.href = p_sHref;
+
+ }
+
+ },
+
+
+ /**
+ * @method _setTarget
+ * @description Sets the value of the button's "target" attribute.
+ * @protected
+ * @param {String} p_sTarget String indicating the value for the button's
+ * "target" attribute.
+ */
+ _setTarget: function (p_sTarget) {
+
+ if (this.get("type") == "link") {
+
+ this._button.setAttribute("target", p_sTarget);
+
+ }
+
+ },
+
+
+ /**
+ * @method _setChecked
+ * @description Sets the value of the button's "target" attribute.
+ * @protected
+ * @param {Boolean} p_bChecked Boolean indicating the value for
+ * the button's "checked" attribute.
+ */
+ _setChecked: function (p_bChecked) {
+
+ var sType = this.get("type");
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ if (p_bChecked) {
+ this.addStateCSSClasses("checked");
+ }
+ else {
+ this.removeStateCSSClasses("checked");
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _setMenu
+ * @description Sets the value of the button's "menu" attribute.
+ * @protected
+ * @param {Object} p_oMenu Object indicating the value for the button's
+ * "menu" attribute.
+ */
+ _setMenu: function (p_oMenu) {
+
+ var bLazyLoad = this.get("lazyloadmenu"),
+ oButtonElement = this.get("element"),
+ sMenuCSSClassName,
+
+ /*
+ Boolean indicating if the value of p_oMenu is an instance
+ of YAHOO.widget.Menu or YAHOO.widget.Overlay.
+ */
+
+ bInstance = false,
+ oMenu,
+ oMenuElement,
+ oSrcElement;
+
+
+ function onAppendTo() {
+
+ oMenu.render(oButtonElement.parentNode);
+
+ this.removeListener("appendTo", onAppendTo);
+
+ }
+
+
+ function setMenuContainer() {
+
+ oMenu.cfg.queueProperty("container", oButtonElement.parentNode);
+
+ this.removeListener("appendTo", setMenuContainer);
+
+ }
+
+
+ function initMenu() {
+
+ var oContainer;
+
+ if (oMenu) {
+
+ Dom.addClass(oMenu.element, this.get("menuclassname"));
+ Dom.addClass(oMenu.element, this.CLASS_NAME_PREFIX + this.get("type") + "-button-menu");
+
+ oMenu.showEvent.subscribe(this._onMenuShow, null, this);
+ oMenu.hideEvent.subscribe(this._onMenuHide, null, this);
+ oMenu.renderEvent.subscribe(this._onMenuRender, null, this);
+
+
+ if (Menu && oMenu instanceof Menu) {
+
+ if (bLazyLoad) {
+
+ oContainer = this.get("container");
+
+ if (oContainer) {
+
+ oMenu.cfg.queueProperty("container", oContainer);
+
+ }
+ else {
+
+ this.on("appendTo", setMenuContainer);
+
+ }
+
+ }
+
+ oMenu.cfg.queueProperty("clicktohide", false);
+
+ oMenu.keyDownEvent.subscribe(this._onMenuKeyDown, this, true);
+ oMenu.subscribe("click", this._onMenuClick, this, true);
+
+ this.on("selectedMenuItemChange", this._onSelectedMenuItemChange);
+
+ oSrcElement = oMenu.srcElement;
+
+ if (oSrcElement && oSrcElement.nodeName.toUpperCase() == "SELECT") {
+
+ oSrcElement.style.display = "none";
+ oSrcElement.parentNode.removeChild(oSrcElement);
+
+ }
+
+ }
+ else if (Overlay && oMenu instanceof Overlay) {
+
+ if (!m_oOverlayManager) {
+
+ m_oOverlayManager = new YAHOO.widget.OverlayManager();
+
+ }
+
+ m_oOverlayManager.register(oMenu);
+
+ }
+
+
+ this._menu = oMenu;
+
+
+ if (!bInstance && !bLazyLoad) {
+
+ if (Dom.inDocument(oButtonElement)) {
+
+ oMenu.render(oButtonElement.parentNode);
+
+ }
+ else {
+
+ this.on("appendTo", onAppendTo);
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ if (Overlay) {
+
+ if (Menu) {
+
+ sMenuCSSClassName = Menu.prototype.CSS_CLASS_NAME;
+
+ }
+
+ if (p_oMenu && Menu && (p_oMenu instanceof Menu)) {
+
+ oMenu = p_oMenu;
+ bInstance = true;
+
+ initMenu.call(this);
+
+ }
+ else if (Overlay && p_oMenu && (p_oMenu instanceof Overlay)) {
+
+ oMenu = p_oMenu;
+ bInstance = true;
+
+ oMenu.cfg.queueProperty("visible", false);
+
+ initMenu.call(this);
+
+ }
+ else if (Menu && Lang.isArray(p_oMenu)) {
+
+ oMenu = new Menu(Dom.generateId(), { lazyload: bLazyLoad, itemdata: p_oMenu });
+
+ this._menu = oMenu;
+
+ this.on("appendTo", initMenu);
+
+ }
+ else if (Lang.isString(p_oMenu)) {
+
+ oMenuElement = Dom.get(p_oMenu);
+
+ if (oMenuElement) {
+
+ if (Menu && Dom.hasClass(oMenuElement, sMenuCSSClassName) ||
+ oMenuElement.nodeName.toUpperCase() == "SELECT") {
+
+ oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad });
+
+ initMenu.call(this);
+
+ }
+ else if (Overlay) {
+
+ oMenu = new Overlay(p_oMenu, { visible: false });
+
+ initMenu.call(this);
+
+ }
+
+ }
+
+ }
+ else if (p_oMenu && p_oMenu.nodeName) {
+
+ if (Menu && Dom.hasClass(p_oMenu, sMenuCSSClassName) ||
+ p_oMenu.nodeName.toUpperCase() == "SELECT") {
+
+ oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad });
+
+ initMenu.call(this);
+
+ }
+ else if (Overlay) {
+
+ if (!p_oMenu.id) {
+
+ Dom.generateId(p_oMenu);
+
+ }
+
+ oMenu = new Overlay(p_oMenu, { visible: false });
+
+ initMenu.call(this);
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _setOnClick
+ * @description Sets the value of the button's "onclick" attribute.
+ * @protected
+ * @param {Object} p_oObject Object indicating the value for the button's
+ * "onclick" attribute.
+ */
+ _setOnClick: function (p_oObject) {
+
+ /*
+ Remove any existing listeners if a "click" event handler
+ has already been specified.
+ */
+
+ if (this._onclickAttributeValue &&
+ (this._onclickAttributeValue != p_oObject)) {
+
+ this.removeListener("click", this._onclickAttributeValue.fn);
+
+ this._onclickAttributeValue = null;
+
+ }
+
+
+ if (!this._onclickAttributeValue &&
+ Lang.isObject(p_oObject) &&
+ Lang.isFunction(p_oObject.fn)) {
+
+ this.on("click", p_oObject.fn, p_oObject.obj, p_oObject.scope);
+
+ this._onclickAttributeValue = p_oObject;
+
+ }
+
+ },
+
+
+
+ // Protected methods
+
+
+
+ /**
+ * @method _isActivationKey
+ * @description Determines if the specified keycode is one that toggles
+ * the button's "active" state.
+ * @protected
+ * @param {Number} p_nKeyCode Number representing the keycode to
+ * be evaluated.
+ * @return {Boolean}
+ */
+ _isActivationKey: function (p_nKeyCode) {
+
+ var sType = this.get("type"),
+ aKeyCodes = (sType == "checkbox" || sType == "radio") ?
+ this.CHECK_ACTIVATION_KEYS : this.ACTIVATION_KEYS,
+
+ nKeyCodes = aKeyCodes.length,
+ bReturnVal = false,
+ i;
+
+
+ if (nKeyCodes > 0) {
+
+ i = nKeyCodes - 1;
+
+ do {
+
+ if (p_nKeyCode == aKeyCodes[i]) {
+
+ bReturnVal = true;
+ break;
+
+ }
+
+ }
+ while (i--);
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _isSplitButtonOptionKey
+ * @description Determines if the specified keycode is one that toggles
+ * the display of the split button's menu.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ * @return {Boolean}
+ */
+ _isSplitButtonOptionKey: function (p_oEvent) {
+
+ var bShowMenu = (Event.getCharCode(p_oEvent) == 40);
+
+
+ var onKeyPress = function (p_oEvent) {
+
+ Event.preventDefault(p_oEvent);
+
+ this.removeListener("keypress", onKeyPress);
+
+ };
+
+
+ // Prevent the browser from scrolling the window
+ if (bShowMenu) {
+
+ if (UA.opera) {
+
+ this.on("keypress", onKeyPress);
+
+ }
+
+ Event.preventDefault(p_oEvent);
+ }
+
+ return bShowMenu;
+
+ },
+
+
+ /**
+ * @method _addListenersToForm
+ * @description Adds event handlers to the button's form.
+ * @protected
+ */
+ _addListenersToForm: function () {
+
+ var oForm = this.getForm(),
+ onFormKeyPress = YAHOO.widget.Button.onFormKeyPress,
+ bHasKeyPressListener,
+ oSrcElement,
+ aListeners,
+ nListeners,
+ i;
+
+
+ if (oForm) {
+
+ Event.on(oForm, "reset", this._onFormReset, null, this);
+ Event.on(oForm, "submit", this._onFormSubmit, null, this);
+
+ oSrcElement = this.get("srcelement");
+
+
+ if (this.get("type") == "submit" ||
+ (oSrcElement && oSrcElement.type == "submit"))
+ {
+
+ aListeners = Event.getListeners(oForm, "keypress");
+ bHasKeyPressListener = false;
+
+ if (aListeners) {
+
+ nListeners = aListeners.length;
+
+ if (nListeners > 0) {
+
+ i = nListeners - 1;
+
+ do {
+
+ if (aListeners[i].fn == onFormKeyPress) {
+
+ bHasKeyPressListener = true;
+ break;
+
+ }
+
+ }
+ while (i--);
+
+ }
+
+ }
+
+
+ if (!bHasKeyPressListener) {
+
+ Event.on(oForm, "keypress", onFormKeyPress);
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+
+ /**
+ * @method _showMenu
+ * @description Shows the button's menu.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event) that triggered
+ * the display of the menu.
+ */
+ _showMenu: function (p_oEvent) {
+
+ if (YAHOO.widget.MenuManager) {
+ YAHOO.widget.MenuManager.hideVisible();
+ }
+
+
+ if (m_oOverlayManager) {
+ m_oOverlayManager.hideAll();
+ }
+
+
+ var oMenu = this._menu,
+ aMenuAlignment = this.get("menualignment"),
+ bFocusMenu = this.get("focusmenu"),
+ fnFocusMethod;
+
+
+ if (this._renderedMenu) {
+
+ oMenu.cfg.setProperty("context",
+ [this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]);
+
+ oMenu.cfg.setProperty("preventcontextoverlap", true);
+ oMenu.cfg.setProperty("constraintoviewport", true);
+
+ }
+ else {
+
+ oMenu.cfg.queueProperty("context",
+ [this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]);
+
+ oMenu.cfg.queueProperty("preventcontextoverlap", true);
+ oMenu.cfg.queueProperty("constraintoviewport", true);
+
+ }
+
+
+ /*
+ Refocus the Button before showing its Menu in case the call to
+ YAHOO.widget.MenuManager.hideVisible() resulted in another element in the
+ DOM being focused after another Menu was hidden.
+ */
+
+ this.focus();
+
+
+ if (Menu && oMenu && (oMenu instanceof Menu)) {
+
+ // Since Menus automatically focus themselves when made visible, temporarily
+ // replace the Menu focus method so that the value of the Button's "focusmenu"
+ // attribute determines if the Menu should be focus when made visible.
+
+ fnFocusMethod = oMenu.focus;
+
+ oMenu.focus = function () {};
+
+ if (this._renderedMenu) {
+
+ oMenu.cfg.setProperty("minscrollheight", this.get("menuminscrollheight"));
+ oMenu.cfg.setProperty("maxheight", this.get("menumaxheight"));
+
+ }
+ else {
+
+ oMenu.cfg.queueProperty("minscrollheight", this.get("menuminscrollheight"));
+ oMenu.cfg.queueProperty("maxheight", this.get("menumaxheight"));
+
+ }
+
+
+ oMenu.show();
+
+ oMenu.focus = fnFocusMethod;
+
+ oMenu.align();
+
+
+ /*
+ Stop the propagation of the event so that the MenuManager
+ doesn't blur the menu after it gets focus.
+ */
+
+ if (p_oEvent.type == "mousedown") {
+ Event.stopPropagation(p_oEvent);
+ }
+
+
+ if (bFocusMenu) {
+ oMenu.focus();
+ }
+
+ }
+ else if (Overlay && oMenu && (oMenu instanceof Overlay)) {
+
+ if (!this._renderedMenu) {
+ oMenu.render(this.get("element").parentNode);
+ }
+
+ oMenu.show();
+ oMenu.align();
+
+ }
+
+ },
+
+
+ /**
+ * @method _hideMenu
+ * @description Hides the button's menu.
+ * @protected
+ */
+ _hideMenu: function () {
+
+ var oMenu = this._menu;
+
+ if (oMenu) {
+
+ oMenu.hide();
+
+ }
+
+ },
+
+
+
+
+ // Protected event handlers
+
+
+ /**
+ * @method _onMouseOver
+ * @description "mouseover" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseOver: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oElement,
+ nOptionRegionX;
+
+
+ if (sType === "split") {
+
+ oElement = this.get("element");
+ nOptionRegionX =
+ (Dom.getX(oElement) + (oElement.offsetWidth - this.OPTION_AREA_WIDTH));
+
+ this._nOptionRegionX = nOptionRegionX;
+
+ }
+
+
+ if (!this._hasMouseEventHandlers) {
+
+ if (sType === "split") {
+
+ this.on("mousemove", this._onMouseMove);
+
+ }
+
+ this.on("mouseout", this._onMouseOut);
+
+ this._hasMouseEventHandlers = true;
+
+ }
+
+
+ this.addStateCSSClasses("hover");
+
+
+ if (sType === "split" && (Event.getPageX(p_oEvent) > nOptionRegionX)) {
+
+ this.addStateCSSClasses("hoveroption");
+
+ }
+
+
+ if (this._activationButtonPressed) {
+
+ this.addStateCSSClasses("active");
+
+ }
+
+
+ if (this._bOptionPressed) {
+
+ this.addStateCSSClasses("activeoption");
+
+ }
+
+
+ if (this._activationButtonPressed || this._bOptionPressed) {
+
+ Event.removeListener(document, "mouseup", this._onDocumentMouseUp);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMouseMove
+ * @description "mousemove" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseMove: function (p_oEvent) {
+
+ var nOptionRegionX = this._nOptionRegionX;
+
+ if (nOptionRegionX) {
+
+ if (Event.getPageX(p_oEvent) > nOptionRegionX) {
+
+ this.addStateCSSClasses("hoveroption");
+
+ }
+ else {
+
+ this.removeStateCSSClasses("hoveroption");
+
+ }
+
+ }
+
+ },
+
+ /**
+ * @method _onMouseOut
+ * @description "mouseout" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseOut: function (p_oEvent) {
+
+ var sType = this.get("type");
+
+ this.removeStateCSSClasses("hover");
+
+
+ if (sType != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+
+ if (this._activationButtonPressed || this._bOptionPressed) {
+
+ Event.on(document, "mouseup", this._onDocumentMouseUp, null, this);
+
+ }
+
+
+ if (sType === "split" && (Event.getPageX(p_oEvent) > this._nOptionRegionX)) {
+
+ this.removeStateCSSClasses("hoveroption");
+
+ }
+
+ },
+
+
+ /**
+ * @method _onDocumentMouseUp
+ * @description "mouseup" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDocumentMouseUp: function (p_oEvent) {
+
+ this._activationButtonPressed = false;
+ this._bOptionPressed = false;
+
+ var sType = this.get("type"),
+ oTarget,
+ oMenuElement;
+
+ if (sType == "menu" || sType == "split") {
+
+ oTarget = Event.getTarget(p_oEvent);
+ oMenuElement = this._menu.element;
+
+ if (oTarget != oMenuElement &&
+ !Dom.isAncestor(oMenuElement, oTarget)) {
+
+ this.removeStateCSSClasses((sType == "menu" ?
+ "active" : "activeoption"));
+
+ this._hideMenu();
+
+ }
+
+ }
+
+ Event.removeListener(document, "mouseup", this._onDocumentMouseUp);
+
+ },
+
+
+ /**
+ * @method _onMouseDown
+ * @description "mousedown" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseDown: function (p_oEvent) {
+
+ var sType,
+ bReturnVal = true;
+
+
+ function onMouseUp() {
+
+ this._hideMenu();
+ this.removeListener("mouseup", onMouseUp);
+
+ }
+
+
+ if ((p_oEvent.which || p_oEvent.button) == 1) {
+
+
+ if (!this.hasFocus()) {
+
+ this.focus();
+
+ }
+
+
+ sType = this.get("type");
+
+
+ if (sType == "split") {
+
+ if (Event.getPageX(p_oEvent) > this._nOptionRegionX) {
+
+ this.fireEvent("option", p_oEvent);
+ bReturnVal = false;
+
+ }
+ else {
+
+ this.addStateCSSClasses("active");
+
+ this._activationButtonPressed = true;
+
+ }
+
+ }
+ else if (sType == "menu") {
+
+ if (this.isActive()) {
+
+ this._hideMenu();
+
+ this._activationButtonPressed = false;
+
+ }
+ else {
+
+ this._showMenu(p_oEvent);
+
+ this._activationButtonPressed = true;
+
+ }
+
+ }
+ else {
+
+ this.addStateCSSClasses("active");
+
+ this._activationButtonPressed = true;
+
+ }
+
+
+
+ if (sType == "split" || sType == "menu") {
+
+ this._hideMenuTimer = Lang.later(250, this, this.on, ["mouseup", onMouseUp]);
+
+ }
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onMouseUp
+ * @description "mouseup" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onMouseUp: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oHideMenuTimer = this._hideMenuTimer,
+ bReturnVal = true;
+
+
+ if (oHideMenuTimer) {
+
+ oHideMenuTimer.cancel();
+
+ }
+
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ this.set("checked", !(this.get("checked")));
+
+ }
+
+
+ this._activationButtonPressed = false;
+
+
+ if (sType != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+
+ if (sType == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) {
+
+ bReturnVal = false;
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onFocus
+ * @description "focus" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onFocus: function (p_oEvent) {
+
+ var oElement;
+
+ this.addStateCSSClasses("focus");
+
+ if (this._activationKeyPressed) {
+
+ this.addStateCSSClasses("active");
+
+ }
+
+ m_oFocusedButton = this;
+
+
+ if (!this._hasKeyEventHandlers) {
+
+ oElement = this._button;
+
+ Event.on(oElement, "blur", this._onBlur, null, this);
+ Event.on(oElement, "keydown", this._onKeyDown, null, this);
+ Event.on(oElement, "keyup", this._onKeyUp, null, this);
+
+ this._hasKeyEventHandlers = true;
+
+ }
+
+
+ this.fireEvent("focus", p_oEvent);
+
+ },
+
+
+ /**
+ * @method _onBlur
+ * @description "blur" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onBlur: function (p_oEvent) {
+
+ this.removeStateCSSClasses("focus");
+
+ if (this.get("type") != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+ if (this._activationKeyPressed) {
+
+ Event.on(document, "keyup", this._onDocumentKeyUp, null, this);
+
+ }
+
+
+ m_oFocusedButton = null;
+
+ this.fireEvent("blur", p_oEvent);
+
+ },
+
+
+ /**
+ * @method _onDocumentKeyUp
+ * @description "keyup" event handler for the document.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDocumentKeyUp: function (p_oEvent) {
+
+ if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
+
+ this._activationKeyPressed = false;
+
+ Event.removeListener(document, "keyup", this._onDocumentKeyUp);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyDown
+ * @description "keydown" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onKeyDown: function (p_oEvent) {
+
+ var oMenu = this._menu;
+
+
+ if (this.get("type") == "split" &&
+ this._isSplitButtonOptionKey(p_oEvent)) {
+
+ this.fireEvent("option", p_oEvent);
+
+ }
+ else if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
+
+ if (this.get("type") == "menu") {
+
+ this._showMenu(p_oEvent);
+
+ }
+ else {
+
+ this._activationKeyPressed = true;
+
+ this.addStateCSSClasses("active");
+
+ }
+
+ }
+
+
+ if (oMenu && oMenu.cfg.getProperty("visible") &&
+ Event.getCharCode(p_oEvent) == 27) {
+
+ oMenu.hide();
+ this.focus();
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyUp
+ * @description "keyup" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onKeyUp: function (p_oEvent) {
+
+ var sType;
+
+ if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
+
+ sType = this.get("type");
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ this.set("checked", !(this.get("checked")));
+
+ }
+
+ this._activationKeyPressed = false;
+
+ if (this.get("type") != "menu") {
+
+ this.removeStateCSSClasses("active");
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onClick
+ * @description "click" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onClick: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oForm,
+ oSrcElement,
+ bReturnVal;
+
+
+ switch (sType) {
+
+ case "submit":
+
+ if (p_oEvent.returnValue !== false) {
+
+ this.submitForm();
+
+ }
+
+ break;
+
+ case "reset":
+
+ oForm = this.getForm();
+
+ if (oForm) {
+
+ oForm.reset();
+
+ }
+
+ break;
+
+
+ case "split":
+
+ if (this._nOptionRegionX > 0 &&
+ (Event.getPageX(p_oEvent) > this._nOptionRegionX)) {
+
+ bReturnVal = false;
+
+ }
+ else {
+
+ this._hideMenu();
+
+ oSrcElement = this.get("srcelement");
+
+ if (oSrcElement && oSrcElement.type == "submit" &&
+ p_oEvent.returnValue !== false) {
+
+ this.submitForm();
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onDblClick
+ * @description "dblclick" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDblClick: function (p_oEvent) {
+
+ var bReturnVal = true;
+
+ if (this.get("type") == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) {
+
+ bReturnVal = false;
+
+ }
+
+ return bReturnVal;
+
+ },
+
+
+ /**
+ * @method _onAppendTo
+ * @description "appendTo" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onAppendTo: function (p_oEvent) {
+
+ /*
+ It is necessary to call "_addListenersToForm" using
+ "setTimeout" to make sure that the button's "form" property
+ returns a node reference. Sometimes, if you try to get the
+ reference immediately after appending the field, it is null.
+ */
+
+ Lang.later(0, this, this._addListenersToForm);
+
+ },
+
+
+ /**
+ * @method _onFormReset
+ * @description "reset" event handler for the button's form.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event
+ * object passed back by the event utility (YAHOO.util.Event).
+ */
+ _onFormReset: function (p_oEvent) {
+
+ var sType = this.get("type"),
+ oMenu = this._menu;
+
+ if (sType == "checkbox" || sType == "radio") {
+
+ this.resetValue("checked");
+
+ }
+
+
+ if (Menu && oMenu && (oMenu instanceof Menu)) {
+
+ this.resetValue("selectedMenuItem");
+
+ }
+
+ },
+
+
+ /**
+ * @method _onFormSubmit
+ * @description "submit" event handler for the button's form.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event
+ * object passed back by the event utility (YAHOO.util.Event).
+ */
+ _onFormSubmit: function (p_oEvent) {
+
+ this.createHiddenFields();
+
+ },
+
+
+ /**
+ * @method _onDocumentMouseDown
+ * @description "mousedown" event handler for the document.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onDocumentMouseDown: function (p_oEvent) {
+
+ var oTarget = Event.getTarget(p_oEvent),
+ oButtonElement = this.get("element"),
+ oMenuElement = this._menu.element;
+
+
+ if (oTarget != oButtonElement &&
+ !Dom.isAncestor(oButtonElement, oTarget) &&
+ oTarget != oMenuElement &&
+ !Dom.isAncestor(oMenuElement, oTarget)) {
+
+ this._hideMenu();
+
+ // In IE when the user mouses down on a focusable element
+ // that element will be focused and become the "activeElement".
+ // (http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx)
+ // However, there is a bug in IE where if there is a
+ // positioned element with a focused descendant that is
+ // hidden in response to the mousedown event, the target of
+ // the mousedown event will appear to have focus, but will
+ // not be set as the activeElement. This will result
+ // in the element not firing key events, even though it
+ // appears to have focus. The following call to "setActive"
+ // fixes this bug.
+
+ if (UA.ie && oTarget.focus) {
+ oTarget.setActive();
+ }
+
+ Event.removeListener(document, "mousedown",
+ this._onDocumentMouseDown);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onOption
+ * @description "option" event handler for the button.
+ * @protected
+ * @param {Event} p_oEvent Object representing the DOM event object
+ * passed back by the event utility (YAHOO.util.Event).
+ */
+ _onOption: function (p_oEvent) {
+
+ if (this.hasClass(this.CLASS_NAME_PREFIX + "split-button-activeoption")) {
+
+ this._hideMenu();
+
+ this._bOptionPressed = false;
+
+ }
+ else {
+
+ this._showMenu(p_oEvent);
+
+ this._bOptionPressed = true;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuShow
+ * @description "show" event handler for the button's menu.
+ * @private
+ * @param {String} p_sType String representing the name of the event
+ * that was fired.
+ */
+ _onMenuShow: function (p_sType) {
+
+ Event.on(document, "mousedown", this._onDocumentMouseDown,
+ null, this);
+
+ var sState = (this.get("type") == "split") ? "activeoption" : "active";
+
+ this.addStateCSSClasses(sState);
+
+ },
+
+
+ /**
+ * @method _onMenuHide
+ * @description "hide" event handler for the button's menu.
+ * @private
+ * @param {String} p_sType String representing the name of the event
+ * that was fired.
+ */
+ _onMenuHide: function (p_sType) {
+
+ var sState = (this.get("type") == "split") ? "activeoption" : "active";
+
+ this.removeStateCSSClasses(sState);
+
+
+ if (this.get("type") == "split") {
+
+ this._bOptionPressed = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuKeyDown
+ * @description "keydown" event handler for the button's 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.
+ */
+ _onMenuKeyDown: function (p_sType, p_aArgs) {
+
+ var oEvent = p_aArgs[0];
+
+ if (Event.getCharCode(oEvent) == 27) {
+
+ this.focus();
+
+ if (this.get("type") == "split") {
+
+ this._bOptionPressed = false;
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuRender
+ * @description "render" event handler for the button's menu.
+ * @private
+ * @param {String} p_sType String representing the name of the
+ * event thatwas fired.
+ */
+ _onMenuRender: function (p_sType) {
+
+ var oButtonElement = this.get("element"),
+ oButtonParent = oButtonElement.parentNode,
+ oMenu = this._menu,
+ oMenuElement = oMenu.element,
+ oSrcElement = oMenu.srcElement,
+ oItem;
+
+
+ if (oButtonParent != oMenuElement.parentNode) {
+
+ oButtonParent.appendChild(oMenuElement);
+
+ }
+
+ this._renderedMenu = true;
+
+ // If the user has designated an ";}E[E.length]="";}return E;},renderYear:function(B){var E=YAHOO.widget.CalendarNavigator,F=E.CLASSES;var G=this.id+E.YEAR_SUFFIX,A=E.YR_MAX_DIGITS,D=B;D[D.length]='";D[D.length]='';return D;},renderButtons:function(A){var D=YAHOO.widget.CalendarNavigator.CLASSES;var B=A;B[B.length]='';B[B.length]='";B[B.length]="";B[B.length]='';B[B.length]='";B[B.length]="";return B;},applyListeners:function(){var B=YAHOO.util.Event;function A(){if(this.validate()){this.setYear(this._getYearFromUI());}}function C(){this.setMonth(this._getMonthFromUI());}B.on(this.submitEl,"click",this.submit,this,true);B.on(this.cancelEl,"click",this.cancel,this,true);B.on(this.yearEl,"blur",A,this,true);B.on(this.monthEl,"change",C,this,true);if(this.__isIEQuirks){YAHOO.util.Event.on(this.cal.oDomContainer,"resize",this._syncMask,this,true);}this.applyKeyListeners();},purgeListeners:function(){var A=YAHOO.util.Event;A.removeListener(this.submitEl,"click",this.submit);A.removeListener(this.cancelEl,"click",this.cancel);A.removeListener(this.yearEl,"blur");A.removeListener(this.monthEl,"change");if(this.__isIEQuirks){A.removeListener(this.cal.oDomContainer,"resize",this._syncMask);}this.purgeKeyListeners();},applyKeyListeners:function(){var D=YAHOO.util.Event,A=YAHOO.env.ua;var C=(A.ie||A.webkit)?"keydown":"keypress";var B=(A.ie||A.opera||A.webkit)?"keydown":"keypress";D.on(this.yearEl,"keypress",this._handleEnterKey,this,true);D.on(this.yearEl,C,this._handleDirectionKeys,this,true);D.on(this.lastCtrl,B,this._handleTabKey,this,true);D.on(this.firstCtrl,B,this._handleShiftTabKey,this,true);},purgeKeyListeners:function(){var D=YAHOO.util.Event,A=YAHOO.env.ua;var C=(A.ie||A.webkit)?"keydown":"keypress";var B=(A.ie||A.opera||A.webkit)?"keydown":"keypress";D.removeListener(this.yearEl,"keypress",this._handleEnterKey);D.removeListener(this.yearEl,C,this._handleDirectionKeys);D.removeListener(this.lastCtrl,B,this._handleTabKey);D.removeListener(this.firstCtrl,B,this._handleShiftTabKey);},submit:function(){if(this.validate()){this.hide();this.setMonth(this._getMonthFromUI());this.setYear(this._getYearFromUI());var B=this.cal;var A=YAHOO.widget.CalendarNavigator.UPDATE_DELAY;if(A>0){var C=this;window.setTimeout(function(){C._update(B);},A);}else{this._update(B);}}},_update:function(B){var A=YAHOO.widget.DateMath.getDate(this.getYear()-B.cfg.getProperty("YEAR_OFFSET"),this.getMonth(),1);B.cfg.setProperty("pagedate",A);B.render();},cancel:function(){this.hide();},validate:function(){if(this._getYearFromUI()!==null){this.clearErrors();return true;}else{this.setYearError();this.setError(this.__getCfg("invalidYear",true));return false;}},setError:function(A){if(this.errorEl){this.errorEl.innerHTML=A;this._show(this.errorEl,true);}},clearError:function(){if(this.errorEl){this.errorEl.innerHTML="";this._show(this.errorEl,false);}},setYearError:function(){YAHOO.util.Dom.addClass(this.yearEl,YAHOO.widget.CalendarNavigator.CLASSES.INVALID);},clearYearError:function(){YAHOO.util.Dom.removeClass(this.yearEl,YAHOO.widget.CalendarNavigator.CLASSES.INVALID);},clearErrors:function(){this.clearError();this.clearYearError();},setInitialFocus:function(){var A=this.submitEl,C=this.__getCfg("initialFocus");if(C&&C.toLowerCase){C=C.toLowerCase();if(C=="year"){A=this.yearEl;try{this.yearEl.select();}catch(B){}}else{if(C=="month"){A=this.monthEl;}}}if(A&&YAHOO.lang.isFunction(A.focus)){try{A.focus();}catch(D){}}},erase:function(){if(this.__rendered){this.purgeListeners();this.yearEl=null;this.monthEl=null;this.errorEl=null;this.submitEl=null;this.cancelEl=null;this.firstCtrl=null;this.lastCtrl=null;if(this.navEl){this.navEl.innerHTML="";}var B=this.navEl.parentNode;if(B){B.removeChild(this.navEl);}this.navEl=null;var A=this.maskEl.parentNode;
+if(A){A.removeChild(this.maskEl);}this.maskEl=null;this.__rendered=false;}},destroy:function(){this.erase();this._doc=null;this.cal=null;this.id=null;},_show:function(B,A){if(B){YAHOO.util.Dom.setStyle(B,"display",(A)?"block":"none");}},_getMonthFromUI:function(){if(this.monthEl){return this.monthEl.selectedIndex;}else{return 0;}},_getYearFromUI:function(){var B=YAHOO.widget.CalendarNavigator;var A=null;if(this.yearEl){var C=this.yearEl.value;C=C.replace(B.TRIM,"$1");if(B.YR_PATTERN.test(C)){A=parseInt(C,10);}}return A;},_updateYearUI:function(){if(this.yearEl&&this._year!==null){this.yearEl.value=this._year;}},_updateMonthUI:function(){if(this.monthEl){this.monthEl.selectedIndex=this._month;}},_setFirstLastElements:function(){this.firstCtrl=this.monthEl;this.lastCtrl=this.cancelEl;if(this.__isMac){if(YAHOO.env.ua.webkit&&YAHOO.env.ua.webkit<420){this.firstCtrl=this.monthEl;this.lastCtrl=this.yearEl;}if(YAHOO.env.ua.gecko){this.firstCtrl=this.yearEl;this.lastCtrl=this.yearEl;}}},_handleEnterKey:function(B){var A=YAHOO.util.KeyListener.KEY;if(YAHOO.util.Event.getCharCode(B)==A.ENTER){YAHOO.util.Event.preventDefault(B);this.submit();}},_handleDirectionKeys:function(H){var G=YAHOO.util.Event,A=YAHOO.util.KeyListener.KEY,D=YAHOO.widget.CalendarNavigator;var F=(this.yearEl.value)?parseInt(this.yearEl.value,10):null;if(isFinite(F)){var B=false;switch(G.getCharCode(H)){case A.UP:this.yearEl.value=F+D.YR_MINOR_INC;B=true;break;case A.DOWN:this.yearEl.value=Math.max(F-D.YR_MINOR_INC,0);B=true;break;case A.PAGE_UP:this.yearEl.value=F+D.YR_MAJOR_INC;B=true;break;case A.PAGE_DOWN:this.yearEl.value=Math.max(F-D.YR_MAJOR_INC,0);B=true;break;default:break;}if(B){G.preventDefault(H);try{this.yearEl.select();}catch(C){}}}},_handleTabKey:function(D){var C=YAHOO.util.Event,A=YAHOO.util.KeyListener.KEY;if(C.getCharCode(D)==A.TAB&&!D.shiftKey){try{C.preventDefault(D);this.firstCtrl.focus();}catch(B){}}},_handleShiftTabKey:function(D){var C=YAHOO.util.Event,A=YAHOO.util.KeyListener.KEY;if(D.shiftKey&&C.getCharCode(D)==A.TAB){try{C.preventDefault(D);this.lastCtrl.focus();}catch(B){}}},__getCfg:function(D,B){var C=YAHOO.widget.CalendarNavigator.DEFAULT_CONFIG;var A=this.cal.cfg.getProperty("navigator");if(B){return(A!==true&&A.strings&&A.strings[D])?A.strings[D]:C.strings[D];}else{return(A!==true&&A[D])?A[D]:C[D];}},__isMac:(navigator.userAgent.toLowerCase().indexOf("macintosh")!=-1)};YAHOO.register("calendar",YAHOO.widget.Calendar,{version:"2.8.0r4",build:"2449"});
\ No newline at end of file
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/calendar/calendar.js b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/calendar/calendar.js
new file mode 100644
index 0000000000..d6e20162d7
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/calendar/calendar.js
@@ -0,0 +1,7294 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.8.0r4
+*/
+(function () {
+
+ /**
+ * Config is a utility used within an Object to allow the implementer to
+ * maintain a list of local configuration properties and listen for changes
+ * to those properties dynamically using CustomEvent. The initial values are
+ * also maintained so that the configuration can be reset at any given point
+ * to its initial state.
+ * @namespace YAHOO.util
+ * @class Config
+ * @constructor
+ * @param {Object} owner The owner Object to which this Config Object belongs
+ */
+ YAHOO.util.Config = function (owner) {
+
+ if (owner) {
+ this.init(owner);
+ }
+
+
+ };
+
+
+ var Lang = YAHOO.lang,
+ CustomEvent = YAHOO.util.CustomEvent,
+ Config = YAHOO.util.Config;
+
+
+ /**
+ * Constant representing the CustomEvent type for the config changed event.
+ * @property YAHOO.util.Config.CONFIG_CHANGED_EVENT
+ * @private
+ * @static
+ * @final
+ */
+ Config.CONFIG_CHANGED_EVENT = "configChanged";
+
+ /**
+ * Constant representing the boolean type string
+ * @property YAHOO.util.Config.BOOLEAN_TYPE
+ * @private
+ * @static
+ * @final
+ */
+ Config.BOOLEAN_TYPE = "boolean";
+
+ Config.prototype = {
+
+ /**
+ * Object reference to the owner of this Config Object
+ * @property owner
+ * @type Object
+ */
+ owner: null,
+
+ /**
+ * Boolean flag that specifies whether a queue is currently
+ * being executed
+ * @property queueInProgress
+ * @type Boolean
+ */
+ queueInProgress: false,
+
+ /**
+ * Maintains the local collection of configuration property objects and
+ * their specified values
+ * @property config
+ * @private
+ * @type Object
+ */
+ config: null,
+
+ /**
+ * Maintains the local collection of configuration property objects as
+ * they were initially applied.
+ * This object is used when resetting a property.
+ * @property initialConfig
+ * @private
+ * @type Object
+ */
+ initialConfig: null,
+
+ /**
+ * Maintains the local, normalized CustomEvent queue
+ * @property eventQueue
+ * @private
+ * @type Object
+ */
+ eventQueue: null,
+
+ /**
+ * Custom Event, notifying subscribers when Config properties are set
+ * (setProperty is called without the silent flag
+ * @event configChangedEvent
+ */
+ configChangedEvent: null,
+
+ /**
+ * Initializes the configuration Object and all of its local members.
+ * @method init
+ * @param {Object} owner The owner Object to which this Config
+ * Object belongs
+ */
+ init: function (owner) {
+
+ this.owner = owner;
+
+ this.configChangedEvent =
+ this.createEvent(Config.CONFIG_CHANGED_EVENT);
+
+ this.configChangedEvent.signature = CustomEvent.LIST;
+ this.queueInProgress = false;
+ this.config = {};
+ this.initialConfig = {};
+ this.eventQueue = [];
+
+ },
+
+ /**
+ * Validates that the value passed in is a Boolean.
+ * @method checkBoolean
+ * @param {Object} val The value to validate
+ * @return {Boolean} true, if the value is valid
+ */
+ checkBoolean: function (val) {
+ return (typeof val == Config.BOOLEAN_TYPE);
+ },
+
+ /**
+ * Validates that the value passed in is a number.
+ * @method checkNumber
+ * @param {Object} val The value to validate
+ * @return {Boolean} true, if the value is valid
+ */
+ checkNumber: function (val) {
+ return (!isNaN(val));
+ },
+
+ /**
+ * Fires a configuration property event using the specified value.
+ * @method fireEvent
+ * @private
+ * @param {String} key The configuration property's name
+ * @param {value} Object The value of the correct type for the property
+ */
+ fireEvent: function ( key, value ) {
+ var property = this.config[key];
+
+ if (property && property.event) {
+ property.event.fire(value);
+ }
+ },
+
+ /**
+ * Adds a property to the Config Object's private config hash.
+ * @method addProperty
+ * @param {String} key The configuration property's name
+ * @param {Object} propertyObject The Object containing all of this
+ * property's arguments
+ */
+ addProperty: function ( key, propertyObject ) {
+ key = key.toLowerCase();
+
+ this.config[key] = propertyObject;
+
+ propertyObject.event = this.createEvent(key, { scope: this.owner });
+ propertyObject.event.signature = CustomEvent.LIST;
+
+
+ propertyObject.key = key;
+
+ if (propertyObject.handler) {
+ propertyObject.event.subscribe(propertyObject.handler,
+ this.owner);
+ }
+
+ this.setProperty(key, propertyObject.value, true);
+
+ if (! propertyObject.suppressEvent) {
+ this.queueProperty(key, propertyObject.value);
+ }
+
+ },
+
+ /**
+ * Returns a key-value configuration map of the values currently set in
+ * the Config Object.
+ * @method getConfig
+ * @return {Object} The current config, represented in a key-value map
+ */
+ getConfig: function () {
+
+ var cfg = {},
+ currCfg = this.config,
+ prop,
+ property;
+
+ for (prop in currCfg) {
+ if (Lang.hasOwnProperty(currCfg, prop)) {
+ property = currCfg[prop];
+ if (property && property.event) {
+ cfg[prop] = property.value;
+ }
+ }
+ }
+
+ return cfg;
+ },
+
+ /**
+ * Returns the value of specified property.
+ * @method getProperty
+ * @param {String} key The name of the property
+ * @return {Object} The value of the specified property
+ */
+ getProperty: function (key) {
+ var property = this.config[key.toLowerCase()];
+ if (property && property.event) {
+ return property.value;
+ } else {
+ return undefined;
+ }
+ },
+
+ /**
+ * Resets the specified property's value to its initial value.
+ * @method resetProperty
+ * @param {String} key The name of the property
+ * @return {Boolean} True is the property was reset, false if not
+ */
+ resetProperty: function (key) {
+
+ key = key.toLowerCase();
+
+ var property = this.config[key];
+
+ if (property && property.event) {
+
+ if (this.initialConfig[key] &&
+ !Lang.isUndefined(this.initialConfig[key])) {
+
+ this.setProperty(key, this.initialConfig[key]);
+
+ return true;
+
+ }
+
+ } else {
+
+ return false;
+ }
+
+ },
+
+ /**
+ * Sets the value of a property. If the silent property is passed as
+ * true, the property's event will not be fired.
+ * @method setProperty
+ * @param {String} key The name of the property
+ * @param {String} value The value to set the property to
+ * @param {Boolean} silent Whether the value should be set silently,
+ * without firing the property event.
+ * @return {Boolean} True, if the set was successful, false if it failed.
+ */
+ setProperty: function (key, value, silent) {
+
+ var property;
+
+ key = key.toLowerCase();
+
+ if (this.queueInProgress && ! silent) {
+ // Currently running through a queue...
+ this.queueProperty(key,value);
+ return true;
+
+ } else {
+ property = this.config[key];
+ if (property && property.event) {
+ if (property.validator && !property.validator(value)) {
+ return false;
+ } else {
+ property.value = value;
+ if (! silent) {
+ this.fireEvent(key, value);
+ this.configChangedEvent.fire([key, value]);
+ }
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+ },
+
+ /**
+ * Sets the value of a property and queues its event to execute. If the
+ * event is already scheduled to execute, it is
+ * moved from its current position to the end of the queue.
+ * @method queueProperty
+ * @param {String} key The name of the property
+ * @param {String} value The value to set the property to
+ * @return {Boolean} true, if the set was successful, false if
+ * it failed.
+ */
+ queueProperty: function (key, value) {
+
+ key = key.toLowerCase();
+
+ var property = this.config[key],
+ foundDuplicate = false,
+ iLen,
+ queueItem,
+ queueItemKey,
+ queueItemValue,
+ sLen,
+ supercedesCheck,
+ qLen,
+ queueItemCheck,
+ queueItemCheckKey,
+ queueItemCheckValue,
+ i,
+ s,
+ q;
+
+ if (property && property.event) {
+
+ if (!Lang.isUndefined(value) && property.validator &&
+ !property.validator(value)) { // validator
+ return false;
+ } else {
+
+ if (!Lang.isUndefined(value)) {
+ property.value = value;
+ } else {
+ value = property.value;
+ }
+
+ foundDuplicate = false;
+ iLen = this.eventQueue.length;
+
+ for (i = 0; i < iLen; i++) {
+ queueItem = this.eventQueue[i];
+
+ if (queueItem) {
+ queueItemKey = queueItem[0];
+ queueItemValue = queueItem[1];
+
+ if (queueItemKey == key) {
+
+ /*
+ found a dupe... push to end of queue, null
+ current item, and break
+ */
+
+ this.eventQueue[i] = null;
+
+ this.eventQueue.push(
+ [key, (!Lang.isUndefined(value) ?
+ value : queueItemValue)]);
+
+ foundDuplicate = true;
+ break;
+ }
+ }
+ }
+
+ // this is a refire, or a new property in the queue
+
+ if (! foundDuplicate && !Lang.isUndefined(value)) {
+ this.eventQueue.push([key, value]);
+ }
+ }
+
+ if (property.supercedes) {
+
+ sLen = property.supercedes.length;
+
+ for (s = 0; s < sLen; s++) {
+
+ supercedesCheck = property.supercedes[s];
+ qLen = this.eventQueue.length;
+
+ for (q = 0; q < qLen; q++) {
+ queueItemCheck = this.eventQueue[q];
+
+ if (queueItemCheck) {
+ queueItemCheckKey = queueItemCheck[0];
+ queueItemCheckValue = queueItemCheck[1];
+
+ if (queueItemCheckKey ==
+ supercedesCheck.toLowerCase() ) {
+
+ this.eventQueue.push([queueItemCheckKey,
+ queueItemCheckValue]);
+
+ this.eventQueue[q] = null;
+ break;
+
+ }
+ }
+ }
+ }
+ }
+
+
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Fires the event for a property using the property's current value.
+ * @method refireEvent
+ * @param {String} key The name of the property
+ */
+ refireEvent: function (key) {
+
+ key = key.toLowerCase();
+
+ var property = this.config[key];
+
+ if (property && property.event &&
+
+ !Lang.isUndefined(property.value)) {
+
+ if (this.queueInProgress) {
+
+ this.queueProperty(key);
+
+ } else {
+
+ this.fireEvent(key, property.value);
+
+ }
+
+ }
+ },
+
+ /**
+ * Applies a key-value Object literal to the configuration, replacing
+ * any existing values, and queueing the property events.
+ * Although the values will be set, fireQueue() must be called for their
+ * associated events to execute.
+ * @method applyConfig
+ * @param {Object} userConfig The configuration Object literal
+ * @param {Boolean} init When set to true, the initialConfig will
+ * be set to the userConfig passed in, so that calling a reset will
+ * reset the properties to the passed values.
+ */
+ applyConfig: function (userConfig, init) {
+
+ var sKey,
+ oConfig;
+
+ if (init) {
+ oConfig = {};
+ for (sKey in userConfig) {
+ if (Lang.hasOwnProperty(userConfig, sKey)) {
+ oConfig[sKey.toLowerCase()] = userConfig[sKey];
+ }
+ }
+ this.initialConfig = oConfig;
+ }
+
+ for (sKey in userConfig) {
+ if (Lang.hasOwnProperty(userConfig, sKey)) {
+ this.queueProperty(sKey, userConfig[sKey]);
+ }
+ }
+ },
+
+ /**
+ * Refires the events for all configuration properties using their
+ * current values.
+ * @method refresh
+ */
+ refresh: function () {
+
+ var prop;
+
+ for (prop in this.config) {
+ if (Lang.hasOwnProperty(this.config, prop)) {
+ this.refireEvent(prop);
+ }
+ }
+ },
+
+ /**
+ * Fires the normalized list of queued property change events
+ * @method fireQueue
+ */
+ fireQueue: function () {
+
+ var i,
+ queueItem,
+ key,
+ value,
+ property;
+
+ this.queueInProgress = true;
+ for (i = 0;i < this.eventQueue.length; i++) {
+ queueItem = this.eventQueue[i];
+ if (queueItem) {
+
+ key = queueItem[0];
+ value = queueItem[1];
+ property = this.config[key];
+
+ property.value = value;
+
+ // Clear out queue entry, to avoid it being
+ // re-added to the queue by any queueProperty/supercedes
+ // calls which are invoked during fireEvent
+ this.eventQueue[i] = null;
+
+ this.fireEvent(key,value);
+ }
+ }
+
+ this.queueInProgress = false;
+ this.eventQueue = [];
+ },
+
+ /**
+ * Subscribes an external handler to the change event for any
+ * given property.
+ * @method subscribeToConfigEvent
+ * @param {String} key The property name
+ * @param {Function} handler The handler function to use subscribe to
+ * the property's event
+ * @param {Object} obj The Object to use for scoping the event handler
+ * (see CustomEvent documentation)
+ * @param {Boolean} overrideContext Optional. If true, will override
+ * "this" within the handler to map to the scope Object passed into the
+ * method.
+ * @return {Boolean} True, if the subscription was successful,
+ * otherwise false.
+ */
+ subscribeToConfigEvent: function (key, handler, obj, overrideContext) {
+
+ var property = this.config[key.toLowerCase()];
+
+ if (property && property.event) {
+ if (!Config.alreadySubscribed(property.event, handler, obj)) {
+ property.event.subscribe(handler, obj, overrideContext);
+ }
+ return true;
+ } else {
+ return false;
+ }
+
+ },
+
+ /**
+ * Unsubscribes an external handler from the change event for any
+ * given property.
+ * @method unsubscribeFromConfigEvent
+ * @param {String} key The property name
+ * @param {Function} handler The handler function to use subscribe to
+ * the property's event
+ * @param {Object} obj The Object to use for scoping the event
+ * handler (see CustomEvent documentation)
+ * @return {Boolean} True, if the unsubscription was successful,
+ * otherwise false.
+ */
+ unsubscribeFromConfigEvent: function (key, handler, obj) {
+ var property = this.config[key.toLowerCase()];
+ if (property && property.event) {
+ return property.event.unsubscribe(handler, obj);
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * Returns a string representation of the Config object
+ * @method toString
+ * @return {String} The Config object in string format.
+ */
+ toString: function () {
+ var output = "Config";
+ if (this.owner) {
+ output += " [" + this.owner.toString() + "]";
+ }
+ return output;
+ },
+
+ /**
+ * Returns a string representation of the Config object's current
+ * CustomEvent queue
+ * @method outputEventQueue
+ * @return {String} The string list of CustomEvents currently queued
+ * for execution
+ */
+ outputEventQueue: function () {
+
+ var output = "",
+ queueItem,
+ q,
+ nQueue = this.eventQueue.length;
+
+ for (q = 0; q < nQueue; q++) {
+ queueItem = this.eventQueue[q];
+ if (queueItem) {
+ output += queueItem[0] + "=" + queueItem[1] + ", ";
+ }
+ }
+ return output;
+ },
+
+ /**
+ * Sets all properties to null, unsubscribes all listeners from each
+ * property's change event and all listeners from the configChangedEvent.
+ * @method destroy
+ */
+ destroy: function () {
+
+ var oConfig = this.config,
+ sProperty,
+ oProperty;
+
+
+ for (sProperty in oConfig) {
+
+ if (Lang.hasOwnProperty(oConfig, sProperty)) {
+
+ oProperty = oConfig[sProperty];
+
+ oProperty.event.unsubscribeAll();
+ oProperty.event = null;
+
+ }
+
+ }
+
+ this.configChangedEvent.unsubscribeAll();
+
+ this.configChangedEvent = null;
+ this.owner = null;
+ this.config = null;
+ this.initialConfig = null;
+ this.eventQueue = null;
+
+ }
+
+ };
+
+
+
+ /**
+ * Checks to determine if a particular function/Object pair are already
+ * subscribed to the specified CustomEvent
+ * @method YAHOO.util.Config.alreadySubscribed
+ * @static
+ * @param {YAHOO.util.CustomEvent} evt The CustomEvent for which to check
+ * the subscriptions
+ * @param {Function} fn The function to look for in the subscribers list
+ * @param {Object} obj The execution scope Object for the subscription
+ * @return {Boolean} true, if the function/Object pair is already subscribed
+ * to the CustomEvent passed in
+ */
+ Config.alreadySubscribed = function (evt, fn, obj) {
+
+ var nSubscribers = evt.subscribers.length,
+ subsc,
+ i;
+
+ if (nSubscribers > 0) {
+ i = nSubscribers - 1;
+ do {
+ subsc = evt.subscribers[i];
+ if (subsc && subsc.obj == obj && subsc.fn == fn) {
+ return true;
+ }
+ }
+ while (i--);
+ }
+
+ return false;
+
+ };
+
+ YAHOO.lang.augmentProto(Config, YAHOO.util.EventProvider);
+
+}());
+/**
+* The datemath module provides utility methods for basic JavaScript Date object manipulation and
+* comparison.
+*
+* @module datemath
+*/
+
+/**
+* YAHOO.widget.DateMath is used for simple date manipulation. The class is a static utility
+* used for adding, subtracting, and comparing dates.
+* @namespace YAHOO.widget
+* @class DateMath
+*/
+YAHOO.widget.DateMath = {
+ /**
+ * Constant field representing Day
+ * @property DAY
+ * @static
+ * @final
+ * @type String
+ */
+ DAY : "D",
+
+ /**
+ * Constant field representing Week
+ * @property WEEK
+ * @static
+ * @final
+ * @type String
+ */
+ WEEK : "W",
+
+ /**
+ * Constant field representing Year
+ * @property YEAR
+ * @static
+ * @final
+ * @type String
+ */
+ YEAR : "Y",
+
+ /**
+ * Constant field representing Month
+ * @property MONTH
+ * @static
+ * @final
+ * @type String
+ */
+ MONTH : "M",
+
+ /**
+ * Constant field representing one day, in milliseconds
+ * @property ONE_DAY_MS
+ * @static
+ * @final
+ * @type Number
+ */
+ ONE_DAY_MS : 1000*60*60*24,
+
+ /**
+ * Constant field representing the date in first week of January
+ * which identifies the first week of the year.
+ * + * In the U.S, Jan 1st is normally used based on a Sunday start of week. + * ISO 8601, used widely throughout Europe, uses Jan 4th, based on a Monday start of week. + *
+ * @property WEEK_ONE_JAN_DATE + * @static + * @type Number + */ + WEEK_ONE_JAN_DATE : 1, + + /** + * Adds the specified amount of time to the this instance. + * @method add + * @param {Date} date The JavaScript Date object to perform addition on + * @param {String} field The field constant to be used for performing addition. + * @param {Number} amount The number of units (measured in the field constant) to add to the date. + * @return {Date} The resulting Date object + */ + add : function(date, field, amount) { + var d = new Date(date.getTime()); + switch (field) { + case this.MONTH: + var newMonth = date.getMonth() + amount; + var years = 0; + + if (newMonth < 0) { + while (newMonth < 0) { + newMonth += 12; + years -= 1; + } + } else if (newMonth > 11) { + while (newMonth > 11) { + newMonth -= 12; + years += 1; + } + } + + d.setMonth(newMonth); + d.setFullYear(date.getFullYear() + years); + break; + case this.DAY: + this._addDays(d, amount); + // d.setDate(date.getDate() + amount); + break; + case this.YEAR: + d.setFullYear(date.getFullYear() + amount); + break; + case this.WEEK: + this._addDays(d, (amount * 7)); + // d.setDate(date.getDate() + (amount * 7)); + break; + } + return d; + }, + + /** + * Private helper method to account for bug in Safari 2 (webkit < 420) + * when Date.setDate(n) is called with n less than -128 or greater than 127. + *+ * Fix approach and original findings are available here: + * http://brianary.blogspot.com/2006/03/safari-date-bug.html + *
+ * @method _addDays + * @param {Date} d JavaScript date object + * @param {Number} nDays The number of days to add to the date object (can be negative) + * @private + */ + _addDays : function(d, nDays) { + if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420) { + if (nDays < 0) { + // Ensure we don't go below -128 (getDate() is always 1 to 31, so we won't go above 127) + for(var min = -128; nDays < min; nDays -= min) { + d.setDate(d.getDate() + min); + } + } else { + // Ensure we don't go above 96 + 31 = 127 + for(var max = 96; nDays > max; nDays -= max) { + d.setDate(d.getDate() + max); + } + } + // nDays should be remainder between -128 and 96 + } + d.setDate(d.getDate() + nDays); + }, + + /** + * Subtracts the specified amount of time from the this instance. + * @method subtract + * @param {Date} date The JavaScript Date object to perform subtraction on + * @param {Number} field The this field constant to be used for performing subtraction. + * @param {Number} amount The number of units (measured in the field constant) to subtract from the date. + * @return {Date} The resulting Date object + */ + subtract : function(date, field, amount) { + return this.add(date, field, (amount*-1)); + }, + + /** + * Determines whether a given date is before another date on the calendar. + * @method before + * @param {Date} date The Date object to compare with the compare argument + * @param {Date} compareTo The Date object to use for the comparison + * @return {Boolean} true if the date occurs before the compared date; false if not. + */ + before : function(date, compareTo) { + var ms = compareTo.getTime(); + if (date.getTime() < ms) { + return true; + } else { + return false; + } + }, + + /** + * Determines whether a given date is after another date on the calendar. + * @method after + * @param {Date} date The Date object to compare with the compare argument + * @param {Date} compareTo The Date object to use for the comparison + * @return {Boolean} true if the date occurs after the compared date; false if not. + */ + after : function(date, compareTo) { + var ms = compareTo.getTime(); + if (date.getTime() > ms) { + return true; + } else { + return false; + } + }, + + /** + * Determines whether a given date is between two other dates on the calendar. + * @method between + * @param {Date} date The date to check for + * @param {Date} dateBegin The start of the range + * @param {Date} dateEnd The end of the range + * @return {Boolean} true if the date occurs between the compared dates; false if not. + */ + between : function(date, dateBegin, dateEnd) { + if (this.after(date, dateBegin) && this.before(date, dateEnd)) { + return true; + } else { + return false; + } + }, + + /** + * Retrieves a JavaScript Date object representing January 1 of any given year. + * @method getJan1 + * @param {Number} calendarYear The calendar year for which to retrieve January 1 + * @return {Date} January 1 of the calendar year specified. + */ + getJan1 : function(calendarYear) { + return this.getDate(calendarYear,0,1); + }, + + /** + * Calculates the number of days the specified date is from January 1 of the specified calendar year. + * Passing January 1 to this function would return an offset value of zero. + * @method getDayOffset + * @param {Date} date The JavaScript date for which to find the offset + * @param {Number} calendarYear The calendar year to use for determining the offset + * @return {Number} The number of days since January 1 of the given year + */ + getDayOffset : function(date, calendarYear) { + var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1. + + // Find the number of days the passed in date is away from the calendar year start + var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS); + return dayOffset; + }, + + /** + * Calculates the week number for the given date. Can currently support standard + * U.S. week numbers, based on Jan 1st defining the 1st week of the year, and + * ISO8601 week numbers, based on Jan 4th defining the 1st week of the year. + * + * @method getWeekNumber + * @param {Date} date The JavaScript date for which to find the week number + * @param {Number} firstDayOfWeek The index of the first day of the week (0 = Sun, 1 = Mon ... 6 = Sat). + * Defaults to 0 + * @param {Number} janDate The date in the first week of January which defines week one for the year + * Defaults to the value of YAHOO.widget.DateMath.WEEK_ONE_JAN_DATE, which is 1 (Jan 1st). + * For the U.S, this is normally Jan 1st. ISO8601 uses Jan 4th to define the first week of the year. + * + * @return {Number} The number of the week containing the given date. + */ + getWeekNumber : function(date, firstDayOfWeek, janDate) { + + // Setup Defaults + firstDayOfWeek = firstDayOfWeek || 0; + janDate = janDate || this.WEEK_ONE_JAN_DATE; + + var targetDate = this.clearTime(date), + startOfWeek, + endOfWeek; + + if (targetDate.getDay() === firstDayOfWeek) { + startOfWeek = targetDate; + } else { + startOfWeek = this.getFirstDayOfWeek(targetDate, firstDayOfWeek); + } + + var startYear = startOfWeek.getFullYear(); + + // DST shouldn't be a problem here, math is quicker than setDate(); + endOfWeek = new Date(startOfWeek.getTime() + 6*this.ONE_DAY_MS); + + var weekNum; + if (startYear !== endOfWeek.getFullYear() && endOfWeek.getDate() >= janDate) { + // If years don't match, endOfWeek is in Jan. and if the + // week has WEEK_ONE_JAN_DATE in it, it's week one by definition. + weekNum = 1; + } else { + // Get the 1st day of the 1st week, and + // find how many days away we are from it. + var weekOne = this.clearTime(this.getDate(startYear, 0, janDate)), + weekOneDayOne = this.getFirstDayOfWeek(weekOne, firstDayOfWeek); + + // Round days to smoothen out 1 hr DST diff + var daysDiff = Math.round((targetDate.getTime() - weekOneDayOne.getTime())/this.ONE_DAY_MS); + + // Calc. Full Weeks + var rem = daysDiff % 7; + var weeksDiff = (daysDiff - rem)/7; + weekNum = weeksDiff + 1; + } + return weekNum; + }, + + /** + * Get the first day of the week, for the give date. + * @param {Date} dt The date in the week for which the first day is required. + * @param {Number} startOfWeek The index for the first day of the week, 0 = Sun, 1 = Mon ... 6 = Sat (defaults to 0) + * @return {Date} The first day of the week + */ + getFirstDayOfWeek : function (dt, startOfWeek) { + startOfWeek = startOfWeek || 0; + var dayOfWeekIndex = dt.getDay(), + dayOfWeek = (dayOfWeekIndex - startOfWeek + 7) % 7; + + return this.subtract(dt, this.DAY, dayOfWeek); + }, + + /** + * Determines if a given week overlaps two different years. + * @method isYearOverlapWeek + * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week. + * @return {Boolean} true if the date overlaps two different years. + */ + isYearOverlapWeek : function(weekBeginDate) { + var overlaps = false; + var nextWeek = this.add(weekBeginDate, this.DAY, 6); + if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) { + overlaps = true; + } + return overlaps; + }, + + /** + * Determines if a given week overlaps two different months. + * @method isMonthOverlapWeek + * @param {Date} weekBeginDate The JavaScript Date representing the first day of the week. + * @return {Boolean} true if the date overlaps two different months. + */ + isMonthOverlapWeek : function(weekBeginDate) { + var overlaps = false; + var nextWeek = this.add(weekBeginDate, this.DAY, 6); + if (nextWeek.getMonth() != weekBeginDate.getMonth()) { + overlaps = true; + } + return overlaps; + }, + + /** + * Gets the first day of a month containing a given date. + * @method findMonthStart + * @param {Date} date The JavaScript Date used to calculate the month start + * @return {Date} The JavaScript Date representing the first day of the month + */ + findMonthStart : function(date) { + var start = this.getDate(date.getFullYear(), date.getMonth(), 1); + return start; + }, + + /** + * Gets the last day of a month containing a given date. + * @method findMonthEnd + * @param {Date} date The JavaScript Date used to calculate the month end + * @return {Date} The JavaScript Date representing the last day of the month + */ + findMonthEnd : function(date) { + var start = this.findMonthStart(date); + var nextMonth = this.add(start, this.MONTH, 1); + var end = this.subtract(nextMonth, this.DAY, 1); + return end; + }, + + /** + * Clears the time fields from a given date, effectively setting the time to 12 noon. + * @method clearTime + * @param {Date} date The JavaScript Date for which the time fields will be cleared + * @return {Date} The JavaScript Date cleared of all time fields + */ + clearTime : function(date) { + date.setHours(12,0,0,0); + return date; + }, + + /** + * Returns a new JavaScript Date object, representing the given year, month and date. Time fields (hr, min, sec, ms) on the new Date object + * are set to 0. The method allows Date instances to be created with the a year less than 100. "new Date(year, month, date)" implementations + * set the year to 19xx if a year (xx) which is less than 100 is provided. + *+ * NOTE:Validation on argument values is not performed. It is the caller's responsibility to ensure + * arguments are valid as per the ECMAScript-262 Date object specification for the new Date(year, month[, date]) constructor. + *
+ * @method getDate + * @param {Number} y Year. + * @param {Number} m Month index from 0 (Jan) to 11 (Dec). + * @param {Number} d (optional) Date from 1 to 31. If not provided, defaults to 1. + * @return {Date} The JavaScript date object with year, month, date set as provided. + */ + getDate : function(y, m, d) { + var dt = null; + if (YAHOO.lang.isUndefined(d)) { + d = 1; + } + if (y >= 100) { + dt = new Date(y, m, d); + } else { + dt = new Date(); + dt.setFullYear(y); + dt.setMonth(m); + dt.setDate(d); + dt.setHours(0,0,0,0); + } + return dt; + } +}; +/** +* The Calendar component is a UI control that enables users to choose one or more dates from a graphical calendar presented in a one-month or +* multi-month interface. Calendars are generated entirely via script and can be navigated without any page refreshes. +* @module calendar +* @title Calendar +* @namespace YAHOO.widget +* @requires yahoo,dom,event +*/ +(function(){ + + var Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + Lang = YAHOO.lang, + DateMath = YAHOO.widget.DateMath; + +/** +* Calendar is the base class for the Calendar widget. In its most basic +* implementation, it has the ability to render a calendar widget on the page +* that can be manipulated to select a single date, move back and forth between +* months and years. +*To construct the placeholder for the calendar widget, the code is as +* follows: +*
+* NOTE: As of 2.4.0, the constructor's ID argument is optional. +* The Calendar can be constructed by simply providing a container ID string, +* or a reference to a container DIV HTMLElement (the element needs to exist +* in the document). +* +* E.g.: +*
+* If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix. +* For example if an ID is not provided, and the container's ID is "calContainer", the Calendar's ID will be set to "calContainer_t". +*
+* +* @namespace YAHOO.widget +* @class Calendar +* @constructor +* @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional. +* @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document. +* @param {Object} config optional The configuration object containing the initial configuration values for the Calendar. +*/ +function Calendar(id, containerId, config) { + this.init.apply(this, arguments); +} + +/** +* The path to be used for images loaded for the Calendar +* @property YAHOO.widget.Calendar.IMG_ROOT +* @static +* @deprecated You can now customize images by overriding the calclose, calnavleft and calnavright default CSS classes for the close icon, left arrow and right arrow respectively +* @type String +*/ +Calendar.IMG_ROOT = null; + +/** +* Type constant used for renderers to represent an individual date (M/D/Y) +* @property YAHOO.widget.Calendar.DATE +* @static +* @final +* @type String +*/ +Calendar.DATE = "D"; + +/** +* Type constant used for renderers to represent an individual date across any year (M/D) +* @property YAHOO.widget.Calendar.MONTH_DAY +* @static +* @final +* @type String +*/ +Calendar.MONTH_DAY = "MD"; + +/** +* Type constant used for renderers to represent a weekday +* @property YAHOO.widget.Calendar.WEEKDAY +* @static +* @final +* @type String +*/ +Calendar.WEEKDAY = "WD"; + +/** +* Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y) +* @property YAHOO.widget.Calendar.RANGE +* @static +* @final +* @type String +*/ +Calendar.RANGE = "R"; + +/** +* Type constant used for renderers to represent a month across any year +* @property YAHOO.widget.Calendar.MONTH +* @static +* @final +* @type String +*/ +Calendar.MONTH = "M"; + +/** +* Constant that represents the total number of date cells that are displayed in a given month +* @property YAHOO.widget.Calendar.DISPLAY_DAYS +* @static +* @final +* @type Number +*/ +Calendar.DISPLAY_DAYS = 42; + +/** +* Constant used for halting the execution of the remainder of the render stack +* @property YAHOO.widget.Calendar.STOP_RENDER +* @static +* @final +* @type String +*/ +Calendar.STOP_RENDER = "S"; + +/** +* Constant used to represent short date field string formats (e.g. Tu or Feb) +* @property YAHOO.widget.Calendar.SHORT +* @static +* @final +* @type String +*/ +Calendar.SHORT = "short"; + +/** +* Constant used to represent long date field string formats (e.g. Monday or February) +* @property YAHOO.widget.Calendar.LONG +* @static +* @final +* @type String +*/ +Calendar.LONG = "long"; + +/** +* Constant used to represent medium date field string formats (e.g. Mon) +* @property YAHOO.widget.Calendar.MEDIUM +* @static +* @final +* @type String +*/ +Calendar.MEDIUM = "medium"; + +/** +* Constant used to represent single character date field string formats (e.g. M, T, W) +* @property YAHOO.widget.Calendar.ONE_CHAR +* @static +* @final +* @type String +*/ +Calendar.ONE_CHAR = "1char"; + +/** +* The set of default Config property keys and values for the Calendar. +* +*+* NOTE: This property is made public in order to allow users to change +* the default values of configuration properties. Users should not +* modify the key string, unless they are overriding the Calendar implementation +*
+* +*
+* The property is an object with key/value pairs, the key being the
+* uppercase configuration property name and the value being an object
+* literal with a key string property, and a value property, specifying the
+* default value of the property. To override a default value, you can set
+* the value property, for example, YAHOO.widget.Calendar.DEFAULT_CONFIG.MULTI_SELECT.value = true;
+*
+* @property YAHOO.widget.Calendar.DEFAULT_CONFIG
+* @static
+* @type Object
+*/
+
+Calendar.DEFAULT_CONFIG = {
+ YEAR_OFFSET : {key:"year_offset", value:0, supercedes:["pagedate", "selected", "mindate","maxdate"]},
+ TODAY : {key:"today", value:new Date(), supercedes:["pagedate"]},
+ PAGEDATE : {key:"pagedate", value:null},
+ SELECTED : {key:"selected", value:[]},
+ TITLE : {key:"title", value:""},
+ CLOSE : {key:"close", value:false},
+ IFRAME : {key:"iframe", value:(YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) ? true : false},
+ MINDATE : {key:"mindate", value:null},
+ MAXDATE : {key:"maxdate", value:null},
+ MULTI_SELECT : {key:"multi_select", value:false},
+ START_WEEKDAY : {key:"start_weekday", value:0},
+ SHOW_WEEKDAYS : {key:"show_weekdays", value:true},
+ SHOW_WEEK_HEADER : {key:"show_week_header", value:false},
+ SHOW_WEEK_FOOTER : {key:"show_week_footer", value:false},
+ HIDE_BLANK_WEEKS : {key:"hide_blank_weeks", value:false},
+ NAV_ARROW_LEFT: {key:"nav_arrow_left", value:null} ,
+ NAV_ARROW_RIGHT : {key:"nav_arrow_right", value:null} ,
+ MONTHS_SHORT : {key:"months_short", value:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]},
+ MONTHS_LONG: {key:"months_long", value:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]},
+ WEEKDAYS_1CHAR: {key:"weekdays_1char", value:["S", "M", "T", "W", "T", "F", "S"]},
+ WEEKDAYS_SHORT: {key:"weekdays_short", value:["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]},
+ WEEKDAYS_MEDIUM: {key:"weekdays_medium", value:["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]},
+ WEEKDAYS_LONG: {key:"weekdays_long", value:["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]},
+ LOCALE_MONTHS:{key:"locale_months", value:"long"},
+ LOCALE_WEEKDAYS:{key:"locale_weekdays", value:"short"},
+ DATE_DELIMITER:{key:"date_delimiter", value:","},
+ DATE_FIELD_DELIMITER:{key:"date_field_delimiter", value:"/"},
+ DATE_RANGE_DELIMITER:{key:"date_range_delimiter", value:"-"},
+ MY_MONTH_POSITION:{key:"my_month_position", value:1},
+ MY_YEAR_POSITION:{key:"my_year_position", value:2},
+ MD_MONTH_POSITION:{key:"md_month_position", value:1},
+ MD_DAY_POSITION:{key:"md_day_position", value:2},
+ MDY_MONTH_POSITION:{key:"mdy_month_position", value:1},
+ MDY_DAY_POSITION:{key:"mdy_day_position", value:2},
+ MDY_YEAR_POSITION:{key:"mdy_year_position", value:3},
+ MY_LABEL_MONTH_POSITION:{key:"my_label_month_position", value:1},
+ MY_LABEL_YEAR_POSITION:{key:"my_label_year_position", value:2},
+ MY_LABEL_MONTH_SUFFIX:{key:"my_label_month_suffix", value:" "},
+ MY_LABEL_YEAR_SUFFIX:{key:"my_label_year_suffix", value:""},
+ NAV: {key:"navigator", value: null},
+ STRINGS : {
+ key:"strings",
+ value: {
+ previousMonth : "Previous Month",
+ nextMonth : "Next Month",
+ close: "Close"
+ },
+ supercedes : ["close", "title"]
+ }
+};
+
+/**
+* The set of default Config property keys and values for the Calendar
+* @property YAHOO.widget.Calendar._DEFAULT_CONFIG
+* @deprecated Made public. See the public DEFAULT_CONFIG property for details
+* @final
+* @static
+* @private
+* @type Object
+*/
+Calendar._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG;
+
+var DEF_CFG = Calendar.DEFAULT_CONFIG;
+
+/**
+* The set of Custom Event types supported by the Calendar
+* @property YAHOO.widget.Calendar._EVENT_TYPES
+* @final
+* @static
+* @private
+* @type Object
+*/
+Calendar._EVENT_TYPES = {
+ BEFORE_SELECT : "beforeSelect",
+ SELECT : "select",
+ BEFORE_DESELECT : "beforeDeselect",
+ DESELECT : "deselect",
+ CHANGE_PAGE : "changePage",
+ BEFORE_RENDER : "beforeRender",
+ RENDER : "render",
+ BEFORE_DESTROY : "beforeDestroy",
+ DESTROY : "destroy",
+ RESET : "reset",
+ CLEAR : "clear",
+ BEFORE_HIDE : "beforeHide",
+ HIDE : "hide",
+ BEFORE_SHOW : "beforeShow",
+ SHOW : "show",
+ BEFORE_HIDE_NAV : "beforeHideNav",
+ HIDE_NAV : "hideNav",
+ BEFORE_SHOW_NAV : "beforeShowNav",
+ SHOW_NAV : "showNav",
+ BEFORE_RENDER_NAV : "beforeRenderNav",
+ RENDER_NAV : "renderNav"
+};
+
+/**
+* The set of default style constants for the Calendar
+* @property YAHOO.widget.Calendar.STYLES
+* @static
+* @type Object An object with name/value pairs for the class name identifier/value.
+*/
+Calendar.STYLES = {
+ CSS_ROW_HEADER: "calrowhead",
+ CSS_ROW_FOOTER: "calrowfoot",
+ CSS_CELL : "calcell",
+ CSS_CELL_SELECTOR : "selector",
+ CSS_CELL_SELECTED : "selected",
+ CSS_CELL_SELECTABLE : "selectable",
+ CSS_CELL_RESTRICTED : "restricted",
+ CSS_CELL_TODAY : "today",
+ CSS_CELL_OOM : "oom",
+ CSS_CELL_OOB : "previous",
+ CSS_HEADER : "calheader",
+ CSS_HEADER_TEXT : "calhead",
+ CSS_BODY : "calbody",
+ CSS_WEEKDAY_CELL : "calweekdaycell",
+ CSS_WEEKDAY_ROW : "calweekdayrow",
+ CSS_FOOTER : "calfoot",
+ CSS_CALENDAR : "yui-calendar",
+ CSS_SINGLE : "single",
+ CSS_CONTAINER : "yui-calcontainer",
+ CSS_NAV_LEFT : "calnavleft",
+ CSS_NAV_RIGHT : "calnavright",
+ CSS_NAV : "calnav",
+ CSS_CLOSE : "calclose",
+ CSS_CELL_TOP : "calcelltop",
+ CSS_CELL_LEFT : "calcellleft",
+ CSS_CELL_RIGHT : "calcellright",
+ CSS_CELL_BOTTOM : "calcellbottom",
+ CSS_CELL_HOVER : "calcellhover",
+ CSS_CELL_HIGHLIGHT1 : "highlight1",
+ CSS_CELL_HIGHLIGHT2 : "highlight2",
+ CSS_CELL_HIGHLIGHT3 : "highlight3",
+ CSS_CELL_HIGHLIGHT4 : "highlight4",
+ CSS_WITH_TITLE: "withtitle",
+ CSS_FIXED_SIZE: "fixedsize",
+ CSS_LINK_CLOSE: "link-close"
+};
+
+/**
+* The set of default style constants for the Calendar
+* @property YAHOO.widget.Calendar._STYLES
+* @deprecated Made public. See the public STYLES property for details
+* @final
+* @static
+* @private
+* @type Object
+*/
+Calendar._STYLES = Calendar.STYLES;
+
+Calendar.prototype = {
+
+ /**
+ * The configuration object used to set up the calendars various locale and style options.
+ * @property Config
+ * @private
+ * @deprecated Configuration properties should be set by calling Calendar.cfg.setProperty.
+ * @type Object
+ */
+ Config : null,
+
+ /**
+ * The parent CalendarGroup, only to be set explicitly by the parent group
+ * @property parent
+ * @type CalendarGroup
+ */
+ parent : null,
+
+ /**
+ * The index of this item in the parent group
+ * @property index
+ * @type Number
+ */
+ index : -1,
+
+ /**
+ * The collection of calendar table cells
+ * @property cells
+ * @type HTMLTableCellElement[]
+ */
+ cells : null,
+
+ /**
+ * The collection of calendar cell dates that is parallel to the cells collection. The array contains dates field arrays in the format of [YYYY, M, D].
+ * @property cellDates
+ * @type Array[](Number[])
+ */
+ cellDates : null,
+
+ /**
+ * The id that uniquely identifies this Calendar.
+ * @property id
+ * @type String
+ */
+ id : null,
+
+ /**
+ * The unique id associated with the Calendar's container
+ * @property containerId
+ * @type String
+ */
+ containerId: null,
+
+ /**
+ * The DOM element reference that points to this calendar's container element. The calendar will be inserted into this element when the shell is rendered.
+ * @property oDomContainer
+ * @type HTMLElement
+ */
+ oDomContainer : null,
+
+ /**
+ * A Date object representing today's date.
+ * @deprecated Use the "today" configuration property
+ * @property today
+ * @type Date
+ */
+ today : null,
+
+ /**
+ * The list of render functions, along with required parameters, used to render cells.
+ * @property renderStack
+ * @type Array[]
+ */
+ renderStack : null,
+
+ /**
+ * A copy of the initial render functions created before rendering.
+ * @property _renderStack
+ * @private
+ * @type Array
+ */
+ _renderStack : null,
+
+ /**
+ * A reference to the CalendarNavigator instance created for this Calendar.
+ * Will be null if the "navigator" configuration property has not been set
+ * @property oNavigator
+ * @type CalendarNavigator
+ */
+ oNavigator : null,
+
+ /**
+ * The private list of initially selected dates.
+ * @property _selectedDates
+ * @private
+ * @type Array
+ */
+ _selectedDates : null,
+
+ /**
+ * A map of DOM event handlers to attach to cells associated with specific CSS class names
+ * @property domEventMap
+ * @type Object
+ */
+ domEventMap : null,
+
+ /**
+ * Protected helper used to parse Calendar constructor/init arguments.
+ *
+ * As of 2.4.0, Calendar supports a simpler constructor
+ * signature. This method reconciles arguments
+ * received in the pre 2.4.0 and 2.4.0 formats.
+ *
+ * @protected
+ * @method _parseArgs
+ * @param {Array} Function "arguments" array
+ * @return {Object} Object with id, container, config properties containing
+ * the reconciled argument values.
+ **/
+ _parseArgs : function(args) {
+ /*
+ 2.4.0 Constructors signatures
+
+ new Calendar(String)
+ new Calendar(HTMLElement)
+ new Calendar(String, ConfigObject)
+ new Calendar(HTMLElement, ConfigObject)
+
+ Pre 2.4.0 Constructor signatures
+
+ new Calendar(String, String)
+ new Calendar(String, HTMLElement)
+ new Calendar(String, String, ConfigObject)
+ new Calendar(String, HTMLElement, ConfigObject)
+ */
+ var nArgs = {id:null, container:null, config:null};
+
+ if (args && args.length && args.length > 0) {
+ switch (args.length) {
+ case 1:
+ nArgs.id = null;
+ nArgs.container = args[0];
+ nArgs.config = null;
+ break;
+ case 2:
+ if (Lang.isObject(args[1]) && !args[1].tagName && !(args[1] instanceof String)) {
+ nArgs.id = null;
+ nArgs.container = args[0];
+ nArgs.config = args[1];
+ } else {
+ nArgs.id = args[0];
+ nArgs.container = args[1];
+ nArgs.config = null;
+ }
+ break;
+ default: // 3+
+ nArgs.id = args[0];
+ nArgs.container = args[1];
+ nArgs.config = args[2];
+ break;
+ }
+ } else {
+ }
+ return nArgs;
+ },
+
+ /**
+ * Initializes the Calendar widget.
+ * @method init
+ *
+ * @param {String} id optional The id of the table element that will represent the Calendar widget. As of 2.4.0, this argument is optional.
+ * @param {String | HTMLElement} container The id of the container div element that will wrap the Calendar table, or a reference to a DIV element which exists in the document.
+ * @param {Object} config optional The configuration object containing the initial configuration values for the Calendar.
+ */
+ init : function(id, container, config) {
+ // Normalize 2.4.0, pre 2.4.0 args
+ var nArgs = this._parseArgs(arguments);
+
+ id = nArgs.id;
+ container = nArgs.container;
+ config = nArgs.config;
+
+ this.oDomContainer = Dom.get(container);
+
+ if (!this.oDomContainer.id) {
+ this.oDomContainer.id = Dom.generateId();
+ }
+ if (!id) {
+ id = this.oDomContainer.id + "_t";
+ }
+
+ this.id = id;
+ this.containerId = this.oDomContainer.id;
+
+ this.initEvents();
+
+ /**
+ * The Config object used to hold the configuration variables for the Calendar
+ * @property cfg
+ * @type YAHOO.util.Config
+ */
+ this.cfg = new YAHOO.util.Config(this);
+
+ /**
+ * The local object which contains the Calendar's options
+ * @property Options
+ * @type Object
+ */
+ this.Options = {};
+
+ /**
+ * The local object which contains the Calendar's locale settings
+ * @property Locale
+ * @type Object
+ */
+ this.Locale = {};
+
+ this.initStyles();
+
+ Dom.addClass(this.oDomContainer, this.Style.CSS_CONTAINER);
+ Dom.addClass(this.oDomContainer, this.Style.CSS_SINGLE);
+
+ this.cellDates = [];
+ this.cells = [];
+ this.renderStack = [];
+ this._renderStack = [];
+
+ this.setupConfig();
+
+ if (config) {
+ this.cfg.applyConfig(config, true);
+ }
+
+ this.cfg.fireQueue();
+
+ this.today = this.cfg.getProperty("today");
+ },
+
+ /**
+ * Default Config listener for the iframe property. If the iframe config property is set to true,
+ * renders the built-in IFRAME shim if the container is relatively or absolutely positioned.
+ *
+ * @method configIframe
+ */
+ configIframe : function(type, args, obj) {
+ var useIframe = args[0];
+
+ if (!this.parent) {
+ if (Dom.inDocument(this.oDomContainer)) {
+ if (useIframe) {
+ var pos = Dom.getStyle(this.oDomContainer, "position");
+
+ if (pos == "absolute" || pos == "relative") {
+
+ if (!Dom.inDocument(this.iframe)) {
+ this.iframe = document.createElement("iframe");
+ this.iframe.src = "javascript:false;";
+
+ Dom.setStyle(this.iframe, "opacity", "0");
+
+ if (YAHOO.env.ua.ie && YAHOO.env.ua.ie <= 6) {
+ Dom.addClass(this.iframe, this.Style.CSS_FIXED_SIZE);
+ }
+
+ this.oDomContainer.insertBefore(this.iframe, this.oDomContainer.firstChild);
+ }
+ }
+ } else {
+ if (this.iframe) {
+ if (this.iframe.parentNode) {
+ this.iframe.parentNode.removeChild(this.iframe);
+ }
+ this.iframe = null;
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Default handler for the "title" property
+ * @method configTitle
+ */
+ configTitle : function(type, args, obj) {
+ var title = args[0];
+
+ // "" disables title bar
+ if (title) {
+ this.createTitleBar(title);
+ } else {
+ var close = this.cfg.getProperty(DEF_CFG.CLOSE.key);
+ if (!close) {
+ this.removeTitleBar();
+ } else {
+ this.createTitleBar(" ");
+ }
+ }
+ },
+
+ /**
+ * Default handler for the "close" property
+ * @method configClose
+ */
+ configClose : function(type, args, obj) {
+ var close = args[0],
+ title = this.cfg.getProperty(DEF_CFG.TITLE.key);
+
+ if (close) {
+ if (!title) {
+ this.createTitleBar(" ");
+ }
+ this.createCloseButton();
+ } else {
+ this.removeCloseButton();
+ if (!title) {
+ this.removeTitleBar();
+ }
+ }
+ },
+
+ /**
+ * Initializes Calendar's built-in CustomEvents
+ * @method initEvents
+ */
+ initEvents : function() {
+
+ var defEvents = Calendar._EVENT_TYPES,
+ CE = YAHOO.util.CustomEvent,
+ cal = this; // To help with minification
+
+ /**
+ * Fired before a date selection is made
+ * @event beforeSelectEvent
+ */
+ cal.beforeSelectEvent = new CE(defEvents.BEFORE_SELECT);
+
+ /**
+ * Fired when a date selection is made
+ * @event selectEvent
+ * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
+ */
+ cal.selectEvent = new CE(defEvents.SELECT);
+
+ /**
+ * Fired before a date or set of dates is deselected
+ * @event beforeDeselectEvent
+ */
+ cal.beforeDeselectEvent = new CE(defEvents.BEFORE_DESELECT);
+
+ /**
+ * Fired when a date or set of dates is deselected
+ * @event deselectEvent
+ * @param {Array} Array of Date field arrays in the format [YYYY, MM, DD].
+ */
+ cal.deselectEvent = new CE(defEvents.DESELECT);
+
+ /**
+ * Fired when the Calendar page is changed
+ * @event changePageEvent
+ * @param {Date} prevDate The date before the page was changed
+ * @param {Date} newDate The date after the page was changed
+ */
+ cal.changePageEvent = new CE(defEvents.CHANGE_PAGE);
+
+ /**
+ * Fired before the Calendar is rendered
+ * @event beforeRenderEvent
+ */
+ cal.beforeRenderEvent = new CE(defEvents.BEFORE_RENDER);
+
+ /**
+ * Fired when the Calendar is rendered
+ * @event renderEvent
+ */
+ cal.renderEvent = new CE(defEvents.RENDER);
+
+ /**
+ * Fired just before the Calendar is to be destroyed
+ * @event beforeDestroyEvent
+ */
+ cal.beforeDestroyEvent = new CE(defEvents.BEFORE_DESTROY);
+
+ /**
+ * Fired after the Calendar is destroyed. This event should be used
+ * for notification only. When this event is fired, important Calendar instance
+ * properties, dom references and event listeners have already been
+ * removed/dereferenced, and hence the Calendar instance is not in a usable
+ * state.
+ *
+ * @event destroyEvent
+ */
+ cal.destroyEvent = new CE(defEvents.DESTROY);
+
+ /**
+ * Fired when the Calendar is reset
+ * @event resetEvent
+ */
+ cal.resetEvent = new CE(defEvents.RESET);
+
+ /**
+ * Fired when the Calendar is cleared
+ * @event clearEvent
+ */
+ cal.clearEvent = new CE(defEvents.CLEAR);
+
+ /**
+ * Fired just before the Calendar is to be shown
+ * @event beforeShowEvent
+ */
+ cal.beforeShowEvent = new CE(defEvents.BEFORE_SHOW);
+
+ /**
+ * Fired after the Calendar is shown
+ * @event showEvent
+ */
+ cal.showEvent = new CE(defEvents.SHOW);
+
+ /**
+ * Fired just before the Calendar is to be hidden
+ * @event beforeHideEvent
+ */
+ cal.beforeHideEvent = new CE(defEvents.BEFORE_HIDE);
+
+ /**
+ * Fired after the Calendar is hidden
+ * @event hideEvent
+ */
+ cal.hideEvent = new CE(defEvents.HIDE);
+
+ /**
+ * Fired just before the CalendarNavigator is to be shown
+ * @event beforeShowNavEvent
+ */
+ cal.beforeShowNavEvent = new CE(defEvents.BEFORE_SHOW_NAV);
+
+ /**
+ * Fired after the CalendarNavigator is shown
+ * @event showNavEvent
+ */
+ cal.showNavEvent = new CE(defEvents.SHOW_NAV);
+
+ /**
+ * Fired just before the CalendarNavigator is to be hidden
+ * @event beforeHideNavEvent
+ */
+ cal.beforeHideNavEvent = new CE(defEvents.BEFORE_HIDE_NAV);
+
+ /**
+ * Fired after the CalendarNavigator is hidden
+ * @event hideNavEvent
+ */
+ cal.hideNavEvent = new CE(defEvents.HIDE_NAV);
+
+ /**
+ * Fired just before the CalendarNavigator is to be rendered
+ * @event beforeRenderNavEvent
+ */
+ cal.beforeRenderNavEvent = new CE(defEvents.BEFORE_RENDER_NAV);
+
+ /**
+ * Fired after the CalendarNavigator is rendered
+ * @event renderNavEvent
+ */
+ cal.renderNavEvent = new CE(defEvents.RENDER_NAV);
+
+ cal.beforeSelectEvent.subscribe(cal.onBeforeSelect, this, true);
+ cal.selectEvent.subscribe(cal.onSelect, this, true);
+ cal.beforeDeselectEvent.subscribe(cal.onBeforeDeselect, this, true);
+ cal.deselectEvent.subscribe(cal.onDeselect, this, true);
+ cal.changePageEvent.subscribe(cal.onChangePage, this, true);
+ cal.renderEvent.subscribe(cal.onRender, this, true);
+ cal.resetEvent.subscribe(cal.onReset, this, true);
+ cal.clearEvent.subscribe(cal.onClear, this, true);
+ },
+
+ /**
+ * The default event handler for clicks on the "Previous Month" navigation UI
+ *
+ * @method doPreviousMonthNav
+ * @param {DOMEvent} e The DOM event
+ * @param {Calendar} cal A reference to the calendar
+ */
+ doPreviousMonthNav : function(e, cal) {
+ Event.preventDefault(e);
+ // previousMonth invoked in a timeout, to allow
+ // event to bubble up, with correct target. Calling
+ // previousMonth, will call render which will remove
+ // HTML which generated the event, resulting in an
+ // invalid event target in certain browsers.
+ setTimeout(function() {
+ cal.previousMonth();
+ var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_LEFT, "a", cal.oDomContainer);
+ if (navs && navs[0]) {
+ try {
+ navs[0].focus();
+ } catch (ex) {
+ // ignore
+ }
+ }
+ }, 0);
+ },
+
+ /**
+ * The default event handler for clicks on the "Next Month" navigation UI
+ *
+ * @method doNextMonthNav
+ * @param {DOMEvent} e The DOM event
+ * @param {Calendar} cal A reference to the calendar
+ */
+ doNextMonthNav : function(e, cal) {
+ Event.preventDefault(e);
+ setTimeout(function() {
+ cal.nextMonth();
+ var navs = Dom.getElementsByClassName(cal.Style.CSS_NAV_RIGHT, "a", cal.oDomContainer);
+ if (navs && navs[0]) {
+ try {
+ navs[0].focus();
+ } catch (ex) {
+ // ignore
+ }
+ }
+ }, 0);
+ },
+
+ /**
+ * The default event handler for date cell selection. Currently attached to
+ * the Calendar's bounding box, referenced by it's oDomContainer property.
+ *
+ * @method doSelectCell
+ * @param {DOMEvent} e The DOM event
+ * @param {Calendar} cal A reference to the calendar
+ */
+ doSelectCell : function(e, cal) {
+ var cell, d, date, index;
+
+ var target = Event.getTarget(e),
+ tagName = target.tagName.toLowerCase(),
+ defSelector = false;
+
+ while (tagName != "td" && !Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
+
+ if (!defSelector && tagName == "a" && Dom.hasClass(target, cal.Style.CSS_CELL_SELECTOR)) {
+ defSelector = true;
+ }
+
+ target = target.parentNode;
+ tagName = target.tagName.toLowerCase();
+
+ if (target == this.oDomContainer || tagName == "html") {
+ return;
+ }
+ }
+
+ if (defSelector) {
+ // Stop link href navigation for default renderer
+ Event.preventDefault(e);
+ }
+
+ cell = target;
+
+ if (Dom.hasClass(cell, cal.Style.CSS_CELL_SELECTABLE)) {
+ index = cal.getIndexFromId(cell.id);
+ if (index > -1) {
+ d = cal.cellDates[index];
+ if (d) {
+ date = DateMath.getDate(d[0],d[1]-1,d[2]);
+
+ var link;
+
+ if (cal.Options.MULTI_SELECT) {
+ link = cell.getElementsByTagName("a")[0];
+ if (link) {
+ link.blur();
+ }
+
+ var cellDate = cal.cellDates[index];
+ var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
+
+ if (cellDateIndex > -1) {
+ cal.deselectCell(index);
+ } else {
+ cal.selectCell(index);
+ }
+
+ } else {
+ link = cell.getElementsByTagName("a")[0];
+ if (link) {
+ link.blur();
+ }
+ cal.selectCell(index);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * The event that is executed when the user hovers over a cell
+ * @method doCellMouseOver
+ * @param {DOMEvent} e The event
+ * @param {Calendar} cal A reference to the calendar passed by the Event utility
+ */
+ doCellMouseOver : function(e, cal) {
+ var target;
+ if (e) {
+ target = Event.getTarget(e);
+ } else {
+ target = this;
+ }
+
+ while (target.tagName && target.tagName.toLowerCase() != "td") {
+ target = target.parentNode;
+ if (!target.tagName || target.tagName.toLowerCase() == "html") {
+ return;
+ }
+ }
+
+ if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
+ Dom.addClass(target, cal.Style.CSS_CELL_HOVER);
+ }
+ },
+
+ /**
+ * The event that is executed when the user moves the mouse out of a cell
+ * @method doCellMouseOut
+ * @param {DOMEvent} e The event
+ * @param {Calendar} cal A reference to the calendar passed by the Event utility
+ */
+ doCellMouseOut : function(e, cal) {
+ var target;
+ if (e) {
+ target = Event.getTarget(e);
+ } else {
+ target = this;
+ }
+
+ while (target.tagName && target.tagName.toLowerCase() != "td") {
+ target = target.parentNode;
+ if (!target.tagName || target.tagName.toLowerCase() == "html") {
+ return;
+ }
+ }
+
+ if (Dom.hasClass(target, cal.Style.CSS_CELL_SELECTABLE)) {
+ Dom.removeClass(target, cal.Style.CSS_CELL_HOVER);
+ }
+ },
+
+ setupConfig : function() {
+
+ var cfg = this.cfg;
+
+ /**
+ * The date to use to represent "Today".
+ *
+ * @config today
+ * @type Date
+ * @default The client side date (new Date()) when the Calendar is instantiated.
+ */
+ cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler:this.configToday, suppressEvent:true } );
+
+ /**
+ * The month/year representing the current visible Calendar date (mm/yyyy)
+ * @config pagedate
+ * @type String | Date
+ * @default Today's date
+ */
+ cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } );
+
+ /**
+ * The date or range of dates representing the current Calendar selection
+ * @config selected
+ * @type String
+ * @default []
+ */
+ cfg.addProperty(DEF_CFG.SELECTED.key, { value:DEF_CFG.SELECTED.value.concat(), handler:this.configSelected } );
+
+ /**
+ * The title to display above the Calendar's month header
+ * @config title
+ * @type String
+ * @default ""
+ */
+ cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } );
+
+ /**
+ * Whether or not a close button should be displayed for this Calendar
+ * @config close
+ * @type Boolean
+ * @default false
+ */
+ cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } );
+
+ /**
+ * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below.
+ * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be
+ * enabled if required.
+ *
+ * @config iframe
+ * @type Boolean
+ * @default true for IE6 and below, false for all other browsers
+ */
+ cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } );
+
+ /**
+ * The minimum selectable date in the current Calendar (mm/dd/yyyy)
+ * @config mindate
+ * @type String | Date
+ * @default null
+ */
+ cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.configMinDate } );
+
+ /**
+ * The maximum selectable date in the current Calendar (mm/dd/yyyy)
+ * @config maxdate
+ * @type String | Date
+ * @default null
+ */
+ cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.configMaxDate } );
+
+ // Options properties
+
+ /**
+ * True if the Calendar should allow multiple selections. False by default.
+ * @config MULTI_SELECT
+ * @type Boolean
+ * @default false
+ */
+ cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.configOptions, validator:cfg.checkBoolean } );
+
+ /**
+ * The weekday the week begins on. Default is 0 (Sunday = 0, Monday = 1 ... Saturday = 6).
+ * @config START_WEEKDAY
+ * @type number
+ * @default 0
+ */
+ cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.configOptions, validator:cfg.checkNumber } );
+
+ /**
+ * True if the Calendar should show weekday labels. True by default.
+ * @config SHOW_WEEKDAYS
+ * @type Boolean
+ * @default true
+ */
+ cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
+
+ /**
+ * True if the Calendar should show week row headers. False by default.
+ * @config SHOW_WEEK_HEADER
+ * @type Boolean
+ * @default false
+ */
+ cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key, { value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
+
+ /**
+ * True if the Calendar should show week row footers. False by default.
+ * @config SHOW_WEEK_FOOTER
+ * @type Boolean
+ * @default false
+ */
+ cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.configOptions, validator:cfg.checkBoolean } );
+
+ /**
+ * True if the Calendar should suppress weeks that are not a part of the current month. False by default.
+ * @config HIDE_BLANK_WEEKS
+ * @type Boolean
+ * @default false
+ */
+ cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key, { value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.configOptions, validator:cfg.checkBoolean } );
+
+ /**
+ * The image that should be used for the left navigation arrow.
+ * @config NAV_ARROW_LEFT
+ * @type String
+ * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft"
+ * @default null
+ */
+ cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.configOptions } );
+
+ /**
+ * The image that should be used for the right navigation arrow.
+ * @config NAV_ARROW_RIGHT
+ * @type String
+ * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright"
+ * @default null
+ */
+ cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.configOptions } );
+
+ // Locale properties
+
+ /**
+ * The short month labels for the current locale.
+ * @config MONTHS_SHORT
+ * @type String[]
+ * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ */
+ cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.configLocale } );
+
+ /**
+ * The long month labels for the current locale.
+ * @config MONTHS_LONG
+ * @type String[]
+ * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
+ */
+ cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.configLocale } );
+
+ /**
+ * The 1-character weekday labels for the current locale.
+ * @config WEEKDAYS_1CHAR
+ * @type String[]
+ * @default ["S", "M", "T", "W", "T", "F", "S"]
+ */
+ cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.configLocale } );
+
+ /**
+ * The short weekday labels for the current locale.
+ * @config WEEKDAYS_SHORT
+ * @type String[]
+ * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
+ */
+ cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.configLocale } );
+
+ /**
+ * The medium weekday labels for the current locale.
+ * @config WEEKDAYS_MEDIUM
+ * @type String[]
+ * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
+ */
+ cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.configLocale } );
+
+ /**
+ * The long weekday labels for the current locale.
+ * @config WEEKDAYS_LONG
+ * @type String[]
+ * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
+ */
+ cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.configLocale } );
+
+ /**
+ * Refreshes the locale values used to build the Calendar.
+ * @method refreshLocale
+ * @private
+ */
+ var refreshLocale = function() {
+ cfg.refireEvent(DEF_CFG.LOCALE_MONTHS.key);
+ cfg.refireEvent(DEF_CFG.LOCALE_WEEKDAYS.key);
+ };
+
+ cfg.subscribeToConfigEvent(DEF_CFG.START_WEEKDAY.key, refreshLocale, this, true);
+ cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_SHORT.key, refreshLocale, this, true);
+ cfg.subscribeToConfigEvent(DEF_CFG.MONTHS_LONG.key, refreshLocale, this, true);
+ cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_1CHAR.key, refreshLocale, this, true);
+ cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_SHORT.key, refreshLocale, this, true);
+ cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_MEDIUM.key, refreshLocale, this, true);
+ cfg.subscribeToConfigEvent(DEF_CFG.WEEKDAYS_LONG.key, refreshLocale, this, true);
+
+ /**
+ * The setting that determines which length of month labels should be used. Possible values are "short" and "long".
+ * @config LOCALE_MONTHS
+ * @type String
+ * @default "long"
+ */
+ cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.configLocaleValues } );
+
+ /**
+ * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long".
+ * @config LOCALE_WEEKDAYS
+ * @type String
+ * @default "short"
+ */
+ cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.configLocaleValues } );
+
+ /**
+ * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to
+ * be used when displaying and parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by
+ * methods will always represent the Gregorian year, in order to maintain date/month/week values.
+ *
+ * @config YEAR_OFFSET
+ * @type Number
+ * @default 0
+ */
+ cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, handler:this.configLocale } );
+
+ /**
+ * The value used to delimit individual dates in a date string passed to various Calendar functions.
+ * @config DATE_DELIMITER
+ * @type String
+ * @default ","
+ */
+ cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.configLocale } );
+
+ /**
+ * The value used to delimit date fields in a date string passed to various Calendar functions.
+ * @config DATE_FIELD_DELIMITER
+ * @type String
+ * @default "/"
+ */
+ cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key, { value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.configLocale } );
+
+ /**
+ * The value used to delimit date ranges in a date string passed to various Calendar functions.
+ * @config DATE_RANGE_DELIMITER
+ * @type String
+ * @default "-"
+ */
+ cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key, { value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.configLocale } );
+
+ /**
+ * The position of the month in a month/year date string
+ * @config MY_MONTH_POSITION
+ * @type Number
+ * @default 1
+ */
+ cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the year in a month/year date string
+ * @config MY_YEAR_POSITION
+ * @type Number
+ * @default 2
+ */
+ cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the month in a month/day date string
+ * @config MD_MONTH_POSITION
+ * @type Number
+ * @default 1
+ */
+ cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the day in a month/year date string
+ * @config MD_DAY_POSITION
+ * @type Number
+ * @default 2
+ */
+ cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the month in a month/day/year date string
+ * @config MDY_MONTH_POSITION
+ * @type Number
+ * @default 1
+ */
+ cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the day in a month/day/year date string
+ * @config MDY_DAY_POSITION
+ * @type Number
+ * @default 2
+ */
+ cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the year in a month/day/year date string
+ * @config MDY_YEAR_POSITION
+ * @type Number
+ * @default 3
+ */
+ cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the month in the month year label string used as the Calendar header
+ * @config MY_LABEL_MONTH_POSITION
+ * @type Number
+ * @default 1
+ */
+ cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The position of the year in the month year label string used as the Calendar header
+ * @config MY_LABEL_YEAR_POSITION
+ * @type Number
+ * @default 2
+ */
+ cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.configLocale, validator:cfg.checkNumber } );
+
+ /**
+ * The suffix used after the month when rendering the Calendar header
+ * @config MY_LABEL_MONTH_SUFFIX
+ * @type String
+ * @default " "
+ */
+ cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.configLocale } );
+
+ /**
+ * The suffix used after the year when rendering the Calendar header
+ * @config MY_LABEL_YEAR_SUFFIX
+ * @type String
+ * @default ""
+ */
+ cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.configLocale } );
+
+ /**
+ * Configuration for the Month/Year CalendarNavigator UI which allows the user to jump directly to a
+ * specific Month/Year without having to scroll sequentially through months.
+ *
+ * Setting this property to null (default value) or false, will disable the CalendarNavigator UI. + *
+ *+ * Setting this property to true will enable the CalendarNavigatior UI with the default CalendarNavigator configuration values. + *
+ *+ * This property can also be set to an object literal containing configuration properties for the CalendarNavigator UI. + * The configuration object expects the the following case-sensitive properties, with the "strings" property being a nested object. + * Any properties which are not provided will use the default values (defined in the CalendarNavigator class). + *
+ *E.g.
+ *
+ * var navConfig = {
+ * strings: {
+ * month:"Calendar Month",
+ * year:"Calendar Year",
+ * submit: "Submit",
+ * cancel: "Cancel",
+ * invalidYear: "Please enter a valid year"
+ * },
+ * monthFormat: YAHOO.widget.Calendar.SHORT,
+ * initialFocus: "month"
+ * }
+ *
+ * @config navigator
+ * @type {Object|Boolean}
+ * @default null
+ */
+ cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } );
+
+ /**
+ * The map of UI strings which the Calendar UI uses.
+ *
+ * @config strings
+ * @type {Object}
+ * @default An object with the properties shown below:
+ * + * The returned index can be used to lookup the cell HTMLElement + * using the Calendar's cells array or passed to selectCell to select + * cells by index. + *
+ * + * See cells, selectCell. + * + * @method getCellIndex + * @param {Date} date JavaScript Date object, for which to find a cell index. + * @return {Number} The index of the date in Calendars cellDates/cells arrays, or -1 if the date + * is not on the curently rendered Calendar page. + */ + getCellIndex : function(date) { + var idx = -1; + if (date) { + var m = date.getMonth(), + y = date.getFullYear(), + d = date.getDate(), + dates = this.cellDates; + + for (var i = 0; i < dates.length; ++i) { + var cellDate = dates[i]; + if (cellDate[0] === y && cellDate[1] === m+1 && cellDate[2] === d) { + idx = i; + break; + } + } + } + return idx; + }, + + /** + * Given the id used to mark each Calendar cell, this method + * extracts the index number from the id. + * + * @param {String} strId The cell id + * @return {Number} The index of the cell, or -1 if id does not contain an index number + */ + getIndexFromId : function(strId) { + var idx = -1, + li = strId.lastIndexOf("_cell"); + + if (li > -1) { + idx = parseInt(strId.substring(li + 5), 10); + } + + return idx; + }, + + // BEGIN BUILT-IN TABLE CELL RENDERERS + + /** + * Renders a cell that falls before the minimum date or after the maximum date. + * widget class. + * @method renderOutOfBoundsDate + * @param {Date} workingDate The current working Date object being used to generate the calendar + * @param {HTMLTableCellElement} cell The current working cell in the calendar + * @return {String} YAHOO.widget.Calendar.STOP_RENDER if rendering should stop with this style, null or nothing if rendering + * should not be terminated + */ + renderOutOfBoundsDate : function(workingDate, cell) { + Dom.addClass(cell, this.Style.CSS_CELL_OOB); + cell.innerHTML = workingDate.getDate(); + return Calendar.STOP_RENDER; + }, + + /** + * Renders the row header for a week. + * @method renderRowHeader + * @param {Number} weekNum The week number of the current row + * @param {Array} cell The current working HTML array + */ + renderRowHeader : function(weekNum, html) { + html[html.length] = '+* NOTE: As of 2.4.0, the constructor's ID argument is optional. +* The CalendarGroup can be constructed by simply providing a container ID string, +* or a reference to a container DIV HTMLElement (the element needs to exist +* in the document). +* +* E.g.: +*
+* If not provided, the ID will be generated from the container DIV ID by adding an "_t" suffix. +* For example if an ID is not provided, and the container's ID is "calContainer", the CalendarGroup's ID will be set to "calContainer_t". +*
+* +* @namespace YAHOO.widget +* @class CalendarGroup +* @constructor +* @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional. +* @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document. +* @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup. +*/ +function CalendarGroup(id, containerId, config) { + if (arguments.length > 0) { + this.init.apply(this, arguments); + } +} + +/** +* The set of default Config property keys and values for the CalendarGroup. +* +*+* NOTE: This property is made public in order to allow users to change +* the default values of configuration properties. Users should not +* modify the key string, unless they are overriding the Calendar implementation +*
+* +* @property YAHOO.widget.CalendarGroup.DEFAULT_CONFIG +* @static +* @type Object An object with key/value pairs, the key being the +* uppercase configuration property name and the value being an objec +* literal with a key string property, and a value property, specifying the +* default value of the property +*/ + +/** +* The set of default Config property keys and values for the CalendarGroup +* @property YAHOO.widget.CalendarGroup._DEFAULT_CONFIG +* @deprecated Made public. See the public DEFAULT_CONFIG property for details +* @private +* @static +* @type Object +*/ +CalendarGroup.DEFAULT_CONFIG = CalendarGroup._DEFAULT_CONFIG = Calendar.DEFAULT_CONFIG; +CalendarGroup.DEFAULT_CONFIG.PAGES = {key:"pages", value:2}; + +var DEF_CFG = CalendarGroup.DEFAULT_CONFIG; + +CalendarGroup.prototype = { + + /** + * Initializes the calendar group. All subclasses must call this method in order for the + * group to be initialized properly. + * @method init + * @param {String} id optional The id of the table element that will represent the CalendarGroup widget. As of 2.4.0, this argument is optional. + * @param {String | HTMLElement} container The id of the container div element that will wrap the CalendarGroup table, or a reference to a DIV element which exists in the document. + * @param {Object} config optional The configuration object containing the initial configuration values for the CalendarGroup. + */ + init : function(id, container, config) { + + // Normalize 2.4.0, pre 2.4.0 args + var nArgs = this._parseArgs(arguments); + + id = nArgs.id; + container = nArgs.container; + config = nArgs.config; + + this.oDomContainer = Dom.get(container); + + if (!this.oDomContainer.id) { + this.oDomContainer.id = Dom.generateId(); + } + if (!id) { + id = this.oDomContainer.id + "_t"; + } + + /** + * The unique id associated with the CalendarGroup + * @property id + * @type String + */ + this.id = id; + + /** + * The unique id associated with the CalendarGroup container + * @property containerId + * @type String + */ + this.containerId = this.oDomContainer.id; + + this.initEvents(); + this.initStyles(); + + /** + * The collection of Calendar pages contained within the CalendarGroup + * @property pages + * @type YAHOO.widget.Calendar[] + */ + this.pages = []; + + Dom.addClass(this.oDomContainer, CalendarGroup.CSS_CONTAINER); + Dom.addClass(this.oDomContainer, CalendarGroup.CSS_MULTI_UP); + + /** + * The Config object used to hold the configuration variables for the CalendarGroup + * @property cfg + * @type YAHOO.util.Config + */ + this.cfg = new YAHOO.util.Config(this); + + /** + * The local object which contains the CalendarGroup's options + * @property Options + * @type Object + */ + this.Options = {}; + + /** + * The local object which contains the CalendarGroup's locale settings + * @property Locale + * @type Object + */ + this.Locale = {}; + + this.setupConfig(); + + if (config) { + this.cfg.applyConfig(config, true); + } + + this.cfg.fireQueue(); + + // OPERA HACK FOR MISWRAPPED FLOATS + if (YAHOO.env.ua.opera){ + this.renderEvent.subscribe(this._fixWidth, this, true); + this.showEvent.subscribe(this._fixWidth, this, true); + } + + }, + + setupConfig : function() { + + var cfg = this.cfg; + + /** + * The number of pages to include in the CalendarGroup. This value can only be set once, in the CalendarGroup's constructor arguments. + * @config pages + * @type Number + * @default 2 + */ + cfg.addProperty(DEF_CFG.PAGES.key, { value:DEF_CFG.PAGES.value, validator:cfg.checkNumber, handler:this.configPages } ); + + /** + * The positive or negative year offset from the Gregorian calendar year (assuming a January 1st rollover) to + * be used when displaying or parsing dates. NOTE: All JS Date objects returned by methods, or expected as input by + * methods will always represent the Gregorian year, in order to maintain date/month/week values. + * + * @config year_offset + * @type Number + * @default 0 + */ + cfg.addProperty(DEF_CFG.YEAR_OFFSET.key, { value:DEF_CFG.YEAR_OFFSET.value, handler: this.delegateConfig, supercedes:DEF_CFG.YEAR_OFFSET.supercedes, suppressEvent:true } ); + + /** + * The date to use to represent "Today". + * + * @config today + * @type Date + * @default Today's date + */ + cfg.addProperty(DEF_CFG.TODAY.key, { value: new Date(DEF_CFG.TODAY.value.getTime()), supercedes:DEF_CFG.TODAY.supercedes, handler: this.configToday, suppressEvent:false } ); + + /** + * The month/year representing the current visible Calendar date (mm/yyyy) + * @config pagedate + * @type String | Date + * @default Today's date + */ + cfg.addProperty(DEF_CFG.PAGEDATE.key, { value: DEF_CFG.PAGEDATE.value || new Date(DEF_CFG.TODAY.value.getTime()), handler:this.configPageDate } ); + + /** + * The date or range of dates representing the current Calendar selection + * + * @config selected + * @type String + * @default [] + */ + cfg.addProperty(DEF_CFG.SELECTED.key, { value:[], handler:this.configSelected } ); + + /** + * The title to display above the CalendarGroup's month header + * @config title + * @type String + * @default "" + */ + cfg.addProperty(DEF_CFG.TITLE.key, { value:DEF_CFG.TITLE.value, handler:this.configTitle } ); + + /** + * Whether or not a close button should be displayed for this CalendarGroup + * @config close + * @type Boolean + * @default false + */ + cfg.addProperty(DEF_CFG.CLOSE.key, { value:DEF_CFG.CLOSE.value, handler:this.configClose } ); + + /** + * Whether or not an iframe shim should be placed under the Calendar to prevent select boxes from bleeding through in Internet Explorer 6 and below. + * This property is enabled by default for IE6 and below. It is disabled by default for other browsers for performance reasons, but can be + * enabled if required. + * + * @config iframe + * @type Boolean + * @default true for IE6 and below, false for all other browsers + */ + cfg.addProperty(DEF_CFG.IFRAME.key, { value:DEF_CFG.IFRAME.value, handler:this.configIframe, validator:cfg.checkBoolean } ); + + /** + * The minimum selectable date in the current Calendar (mm/dd/yyyy) + * @config mindate + * @type String | Date + * @default null + */ + cfg.addProperty(DEF_CFG.MINDATE.key, { value:DEF_CFG.MINDATE.value, handler:this.delegateConfig } ); + + /** + * The maximum selectable date in the current Calendar (mm/dd/yyyy) + * @config maxdate + * @type String | Date + * @default null + */ + cfg.addProperty(DEF_CFG.MAXDATE.key, { value:DEF_CFG.MAXDATE.value, handler:this.delegateConfig } ); + + // Options properties + + /** + * True if the Calendar should allow multiple selections. False by default. + * @config MULTI_SELECT + * @type Boolean + * @default false + */ + cfg.addProperty(DEF_CFG.MULTI_SELECT.key, { value:DEF_CFG.MULTI_SELECT.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); + + /** + * The weekday the week begins on. Default is 0 (Sunday). + * @config START_WEEKDAY + * @type number + * @default 0 + */ + cfg.addProperty(DEF_CFG.START_WEEKDAY.key, { value:DEF_CFG.START_WEEKDAY.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * True if the Calendar should show weekday labels. True by default. + * @config SHOW_WEEKDAYS + * @type Boolean + * @default true + */ + cfg.addProperty(DEF_CFG.SHOW_WEEKDAYS.key, { value:DEF_CFG.SHOW_WEEKDAYS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); + + /** + * True if the Calendar should show week row headers. False by default. + * @config SHOW_WEEK_HEADER + * @type Boolean + * @default false + */ + cfg.addProperty(DEF_CFG.SHOW_WEEK_HEADER.key,{ value:DEF_CFG.SHOW_WEEK_HEADER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); + + /** + * True if the Calendar should show week row footers. False by default. + * @config SHOW_WEEK_FOOTER + * @type Boolean + * @default false + */ + cfg.addProperty(DEF_CFG.SHOW_WEEK_FOOTER.key,{ value:DEF_CFG.SHOW_WEEK_FOOTER.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); + + /** + * True if the Calendar should suppress weeks that are not a part of the current month. False by default. + * @config HIDE_BLANK_WEEKS + * @type Boolean + * @default false + */ + cfg.addProperty(DEF_CFG.HIDE_BLANK_WEEKS.key,{ value:DEF_CFG.HIDE_BLANK_WEEKS.value, handler:this.delegateConfig, validator:cfg.checkBoolean } ); + + /** + * The image that should be used for the left navigation arrow. + * @config NAV_ARROW_LEFT + * @type String + * @deprecated You can customize the image by overriding the default CSS class for the left arrow - "calnavleft" + * @default null + */ + cfg.addProperty(DEF_CFG.NAV_ARROW_LEFT.key, { value:DEF_CFG.NAV_ARROW_LEFT.value, handler:this.delegateConfig } ); + + /** + * The image that should be used for the right navigation arrow. + * @config NAV_ARROW_RIGHT + * @type String + * @deprecated You can customize the image by overriding the default CSS class for the right arrow - "calnavright" + * @default null + */ + cfg.addProperty(DEF_CFG.NAV_ARROW_RIGHT.key, { value:DEF_CFG.NAV_ARROW_RIGHT.value, handler:this.delegateConfig } ); + + // Locale properties + + /** + * The short month labels for the current locale. + * @config MONTHS_SHORT + * @type String[] + * @default ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + */ + cfg.addProperty(DEF_CFG.MONTHS_SHORT.key, { value:DEF_CFG.MONTHS_SHORT.value, handler:this.delegateConfig } ); + + /** + * The long month labels for the current locale. + * @config MONTHS_LONG + * @type String[] + * @default ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + */ + cfg.addProperty(DEF_CFG.MONTHS_LONG.key, { value:DEF_CFG.MONTHS_LONG.value, handler:this.delegateConfig } ); + + /** + * The 1-character weekday labels for the current locale. + * @config WEEKDAYS_1CHAR + * @type String[] + * @default ["S", "M", "T", "W", "T", "F", "S"] + */ + cfg.addProperty(DEF_CFG.WEEKDAYS_1CHAR.key, { value:DEF_CFG.WEEKDAYS_1CHAR.value, handler:this.delegateConfig } ); + + /** + * The short weekday labels for the current locale. + * @config WEEKDAYS_SHORT + * @type String[] + * @default ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] + */ + cfg.addProperty(DEF_CFG.WEEKDAYS_SHORT.key, { value:DEF_CFG.WEEKDAYS_SHORT.value, handler:this.delegateConfig } ); + + /** + * The medium weekday labels for the current locale. + * @config WEEKDAYS_MEDIUM + * @type String[] + * @default ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + */ + cfg.addProperty(DEF_CFG.WEEKDAYS_MEDIUM.key, { value:DEF_CFG.WEEKDAYS_MEDIUM.value, handler:this.delegateConfig } ); + + /** + * The long weekday labels for the current locale. + * @config WEEKDAYS_LONG + * @type String[] + * @default ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] + */ + cfg.addProperty(DEF_CFG.WEEKDAYS_LONG.key, { value:DEF_CFG.WEEKDAYS_LONG.value, handler:this.delegateConfig } ); + + /** + * The setting that determines which length of month labels should be used. Possible values are "short" and "long". + * @config LOCALE_MONTHS + * @type String + * @default "long" + */ + cfg.addProperty(DEF_CFG.LOCALE_MONTHS.key, { value:DEF_CFG.LOCALE_MONTHS.value, handler:this.delegateConfig } ); + + /** + * The setting that determines which length of weekday labels should be used. Possible values are "1char", "short", "medium", and "long". + * @config LOCALE_WEEKDAYS + * @type String + * @default "short" + */ + cfg.addProperty(DEF_CFG.LOCALE_WEEKDAYS.key, { value:DEF_CFG.LOCALE_WEEKDAYS.value, handler:this.delegateConfig } ); + + /** + * The value used to delimit individual dates in a date string passed to various Calendar functions. + * @config DATE_DELIMITER + * @type String + * @default "," + */ + cfg.addProperty(DEF_CFG.DATE_DELIMITER.key, { value:DEF_CFG.DATE_DELIMITER.value, handler:this.delegateConfig } ); + + /** + * The value used to delimit date fields in a date string passed to various Calendar functions. + * @config DATE_FIELD_DELIMITER + * @type String + * @default "/" + */ + cfg.addProperty(DEF_CFG.DATE_FIELD_DELIMITER.key,{ value:DEF_CFG.DATE_FIELD_DELIMITER.value, handler:this.delegateConfig } ); + + /** + * The value used to delimit date ranges in a date string passed to various Calendar functions. + * @config DATE_RANGE_DELIMITER + * @type String + * @default "-" + */ + cfg.addProperty(DEF_CFG.DATE_RANGE_DELIMITER.key,{ value:DEF_CFG.DATE_RANGE_DELIMITER.value, handler:this.delegateConfig } ); + + /** + * The position of the month in a month/year date string + * @config MY_MONTH_POSITION + * @type Number + * @default 1 + */ + cfg.addProperty(DEF_CFG.MY_MONTH_POSITION.key, { value:DEF_CFG.MY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the year in a month/year date string + * @config MY_YEAR_POSITION + * @type Number + * @default 2 + */ + cfg.addProperty(DEF_CFG.MY_YEAR_POSITION.key, { value:DEF_CFG.MY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the month in a month/day date string + * @config MD_MONTH_POSITION + * @type Number + * @default 1 + */ + cfg.addProperty(DEF_CFG.MD_MONTH_POSITION.key, { value:DEF_CFG.MD_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the day in a month/year date string + * @config MD_DAY_POSITION + * @type Number + * @default 2 + */ + cfg.addProperty(DEF_CFG.MD_DAY_POSITION.key, { value:DEF_CFG.MD_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the month in a month/day/year date string + * @config MDY_MONTH_POSITION + * @type Number + * @default 1 + */ + cfg.addProperty(DEF_CFG.MDY_MONTH_POSITION.key, { value:DEF_CFG.MDY_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the day in a month/day/year date string + * @config MDY_DAY_POSITION + * @type Number + * @default 2 + */ + cfg.addProperty(DEF_CFG.MDY_DAY_POSITION.key, { value:DEF_CFG.MDY_DAY_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the year in a month/day/year date string + * @config MDY_YEAR_POSITION + * @type Number + * @default 3 + */ + cfg.addProperty(DEF_CFG.MDY_YEAR_POSITION.key, { value:DEF_CFG.MDY_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the month in the month year label string used as the Calendar header + * @config MY_LABEL_MONTH_POSITION + * @type Number + * @default 1 + */ + cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_POSITION.key, { value:DEF_CFG.MY_LABEL_MONTH_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The position of the year in the month year label string used as the Calendar header + * @config MY_LABEL_YEAR_POSITION + * @type Number + * @default 2 + */ + cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_POSITION.key, { value:DEF_CFG.MY_LABEL_YEAR_POSITION.value, handler:this.delegateConfig, validator:cfg.checkNumber } ); + + /** + * The suffix used after the month when rendering the Calendar header + * @config MY_LABEL_MONTH_SUFFIX + * @type String + * @default " " + */ + cfg.addProperty(DEF_CFG.MY_LABEL_MONTH_SUFFIX.key, { value:DEF_CFG.MY_LABEL_MONTH_SUFFIX.value, handler:this.delegateConfig } ); + + /** + * The suffix used after the year when rendering the Calendar header + * @config MY_LABEL_YEAR_SUFFIX + * @type String + * @default "" + */ + cfg.addProperty(DEF_CFG.MY_LABEL_YEAR_SUFFIX.key, { value:DEF_CFG.MY_LABEL_YEAR_SUFFIX.value, handler:this.delegateConfig } ); + + /** + * Configuration for the Month Year Navigation UI. By default it is disabled + * @config NAV + * @type Object + * @default null + */ + cfg.addProperty(DEF_CFG.NAV.key, { value:DEF_CFG.NAV.value, handler:this.configNavigator } ); + + /** + * The map of UI strings which the CalendarGroup UI uses. + * + * @config strings + * @type {Object} + * @default An object with the properties shown below: + *+ * The method is also registered as an HTMLElement resize listener on the Calendars container element. + *
+ * @protected + * @method _syncMask + */ + _syncMask : function() { + var c = this.cal.oDomContainer; + if (c && this.maskEl) { + var r = YAHOO.util.Dom.getRegion(c); + YAHOO.util.Dom.setStyle(this.maskEl, "width", r.right - r.left + "px"); + YAHOO.util.Dom.setStyle(this.maskEl, "height", r.bottom - r.top + "px"); + } + }, + + /** + * Renders the contents of the navigator + * + * @method renderNavContents + * + * @param {Array} html The HTML buffer to append the HTML to. + * @return {Array} A reference to the buffer passed in. + */ + renderNavContents : function(html) { + var NAV = YAHOO.widget.CalendarNavigator, + C = NAV.CLASSES, + h = html; // just to use a shorter name + + h[h.length] = '+ * The method will call applyKeyListeners, to setup keyboard specific + * listeners + *
+ * @method applyListeners + */ + applyListeners : function() { + var E = YAHOO.util.Event; + + function yearUpdateHandler() { + if (this.validate()) { + this.setYear(this._getYearFromUI()); + } + } + + function monthUpdateHandler() { + this.setMonth(this._getMonthFromUI()); + } + + E.on(this.submitEl, "click", this.submit, this, true); + E.on(this.cancelEl, "click", this.cancel, this, true); + E.on(this.yearEl, "blur", yearUpdateHandler, this, true); + E.on(this.monthEl, "change", monthUpdateHandler, this, true); + + if (this.__isIEQuirks) { + YAHOO.util.Event.on(this.cal.oDomContainer, "resize", this._syncMask, this, true); + } + + this.applyKeyListeners(); + }, + + /** + * Removes/purges DOM event listeners from the rendered elements + * + * @method purgeListeners + */ + purgeListeners : function() { + var E = YAHOO.util.Event; + E.removeListener(this.submitEl, "click", this.submit); + E.removeListener(this.cancelEl, "click", this.cancel); + E.removeListener(this.yearEl, "blur"); + E.removeListener(this.monthEl, "change"); + if (this.__isIEQuirks) { + E.removeListener(this.cal.oDomContainer, "resize", this._syncMask); + } + + this.purgeKeyListeners(); + }, + + /** + * Attaches DOM listeners for keyboard support. + * Tab/Shift-Tab looping, Enter Key Submit on Year element, + * Up/Down/PgUp/PgDown year increment on Year element + *+ * NOTE: MacOSX Safari 2.x doesn't let you tab to buttons and + * MacOSX Gecko does not let you tab to buttons or select controls, + * so for these browsers, Tab/Shift-Tab looping is limited to the + * elements which can be reached using the tab key. + *
+ * @method applyKeyListeners + */ + applyKeyListeners : function() { + var E = YAHOO.util.Event, + ua = YAHOO.env.ua; + + // IE/Safari 3.1 doesn't fire keypress for arrow/pg keys (non-char keys) + var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress"; + + // - IE/Safari 3.1 doesn't fire keypress for non-char keys + // - Opera doesn't allow us to cancel keydown or keypress for tab, but + // changes focus successfully on keydown (keypress is too late to change focus - opera's already moved on). + var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress"; + + // Everyone likes keypress for Enter (char keys) - whoo hoo! + E.on(this.yearEl, "keypress", this._handleEnterKey, this, true); + + E.on(this.yearEl, arrowEvt, this._handleDirectionKeys, this, true); + E.on(this.lastCtrl, tabEvt, this._handleTabKey, this, true); + E.on(this.firstCtrl, tabEvt, this._handleShiftTabKey, this, true); + }, + + /** + * Removes/purges DOM listeners for keyboard support + * + * @method purgeKeyListeners + */ + purgeKeyListeners : function() { + var E = YAHOO.util.Event, + ua = YAHOO.env.ua; + + var arrowEvt = (ua.ie || ua.webkit) ? "keydown" : "keypress"; + var tabEvt = (ua.ie || ua.opera || ua.webkit) ? "keydown" : "keypress"; + + E.removeListener(this.yearEl, "keypress", this._handleEnterKey); + E.removeListener(this.yearEl, arrowEvt, this._handleDirectionKeys); + E.removeListener(this.lastCtrl, tabEvt, this._handleTabKey); + E.removeListener(this.firstCtrl, tabEvt, this._handleShiftTabKey); + }, + + /** + * Updates the Calendar/CalendarGroup's pagedate with the currently set month and year if valid. + *+ * If the currently set month/year is invalid, a validation error will be displayed and the + * Calendar/CalendarGroup's pagedate will not be updated. + *
+ * @method submit + */ + submit : function() { + if (this.validate()) { + this.hide(); + + this.setMonth(this._getMonthFromUI()); + this.setYear(this._getYearFromUI()); + + var cal = this.cal; + + // Artificial delay, just to help the user see something changed + var delay = YAHOO.widget.CalendarNavigator.UPDATE_DELAY; + if (delay > 0) { + var nav = this; + window.setTimeout(function(){ nav._update(cal); }, delay); + } else { + this._update(cal); + } + } + }, + + /** + * Updates the Calendar rendered state, based on the state of the CalendarNavigator + * @method _update + * @param cal The Calendar instance to update + * @protected + */ + _update : function(cal) { + var date = YAHOO.widget.DateMath.getDate(this.getYear() - cal.cfg.getProperty("YEAR_OFFSET"), this.getMonth(), 1); + cal.cfg.setProperty("pagedate", date); + cal.render(); + }, + + /** + * Hides the navigator and mask, without updating the Calendar/CalendarGroup's state + * + * @method cancel + */ + cancel : function() { + this.hide(); + }, + + /** + * Validates the current state of the UI controls + * + * @method validate + * @return {Boolean} true, if the current UI state contains valid values, false if not + */ + validate : function() { + if (this._getYearFromUI() !== null) { + this.clearErrors(); + return true; + } else { + this.setYearError(); + this.setError(this.__getCfg("invalidYear", true)); + return false; + } + }, + + /** + * Displays an error message in the Navigator's error panel + * @method setError + * @param {String} msg The error message to display + */ + setError : function(msg) { + if (this.errorEl) { + this.errorEl.innerHTML = msg; + this._show(this.errorEl, true); + } + }, + + /** + * Clears the navigator's error message and hides the error panel + * @method clearError + */ + clearError : function() { + if (this.errorEl) { + this.errorEl.innerHTML = ""; + this._show(this.errorEl, false); + } + }, + + /** + * Displays the validation error UI for the year control + * @method setYearError + */ + setYearError : function() { + YAHOO.util.Dom.addClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID); + }, + + /** + * Removes the validation error UI for the year control + * @method clearYearError + */ + clearYearError : function() { + YAHOO.util.Dom.removeClass(this.yearEl, YAHOO.widget.CalendarNavigator.CLASSES.INVALID); + }, + + /** + * Clears all validation and error messages in the UI + * @method clearErrors + */ + clearErrors : function() { + this.clearError(); + this.clearYearError(); + }, + + /** + * Sets the initial focus, based on the configured value + * @method setInitialFocus + */ + setInitialFocus : function() { + var el = this.submitEl, + f = this.__getCfg("initialFocus"); + + if (f && f.toLowerCase) { + f = f.toLowerCase(); + if (f == "year") { + el = this.yearEl; + try { + this.yearEl.select(); + } catch (selErr) { + // Ignore; + } + } else if (f == "month") { + el = this.monthEl; + } + } + + if (el && YAHOO.lang.isFunction(el.focus)) { + try { + el.focus(); + } catch (focusErr) { + // TODO: Fall back if focus fails? + } + } + }, + + /** + * Removes all renderered HTML elements for the Navigator from + * the DOM, purges event listeners and clears (nulls) any property + * references to HTML references + * @method erase + */ + erase : function() { + if (this.__rendered) { + this.purgeListeners(); + + // Clear out innerHTML references + this.yearEl = null; + this.monthEl = null; + this.errorEl = null; + this.submitEl = null; + this.cancelEl = null; + this.firstCtrl = null; + this.lastCtrl = null; + if (this.navEl) { + this.navEl.innerHTML = ""; + } + + var p = this.navEl.parentNode; + if (p) { + p.removeChild(this.navEl); + } + this.navEl = null; + + var pm = this.maskEl.parentNode; + if (pm) { + pm.removeChild(this.maskEl); + } + this.maskEl = null; + this.__rendered = false; + } + }, + + /** + * Destroys the Navigator object and any HTML references + * @method destroy + */ + destroy : function() { + this.erase(); + this._doc = null; + this.cal = null; + this.id = null; + }, + + /** + * Protected implementation to handle how UI elements are + * hidden/shown. + * + * @method _show + * @protected + */ + _show : function(el, bShow) { + if (el) { + YAHOO.util.Dom.setStyle(el, "display", (bShow) ? "block" : "none"); + } + }, + + /** + * Returns the month value (index), from the month UI element + * @protected + * @method _getMonthFromUI + * @return {Number} The month index, or 0 if a UI element for the month + * is not found + */ + _getMonthFromUI : function() { + if (this.monthEl) { + return this.monthEl.selectedIndex; + } else { + return 0; // Default to Jan + } + }, + + /** + * Returns the year value, from the Navitator's year UI element + * @protected + * @method _getYearFromUI + * @return {Number} The year value set in the UI, if valid. null is returned if + * the UI does not contain a valid year value. + */ + _getYearFromUI : function() { + var NAV = YAHOO.widget.CalendarNavigator; + + var yr = null; + if (this.yearEl) { + var value = this.yearEl.value; + value = value.replace(NAV.TRIM, "$1"); + + if (NAV.YR_PATTERN.test(value)) { + yr = parseInt(value, 10); + } + } + return yr; + }, + + /** + * Updates the Navigator's year UI, based on the year value set on the Navigator object + * @protected + * @method _updateYearUI + */ + _updateYearUI : function() { + if (this.yearEl && this._year !== null) { + this.yearEl.value = this._year; + } + }, + + /** + * Updates the Navigator's month UI, based on the month value set on the Navigator object + * @protected + * @method _updateMonthUI + */ + _updateMonthUI : function() { + if (this.monthEl) { + this.monthEl.selectedIndex = this._month; + } + }, + + /** + * Sets up references to the first and last focusable element in the Navigator's UI + * in terms of tab order (Naviagator's firstEl and lastEl properties). The references + * are used to control modality by looping around from the first to the last control + * and visa versa for tab/shift-tab navigation. + *+ * See applyKeyListeners + *
+ * @protected + * @method _setFirstLastElements + */ + _setFirstLastElements : function() { + this.firstCtrl = this.monthEl; + this.lastCtrl = this.cancelEl; + + // Special handling for MacOSX. + // - Safari 2.x can't focus on buttons + // - Gecko can't focus on select boxes or buttons + if (this.__isMac) { + if (YAHOO.env.ua.webkit && YAHOO.env.ua.webkit < 420){ + this.firstCtrl = this.monthEl; + this.lastCtrl = this.yearEl; + } + if (YAHOO.env.ua.gecko) { + this.firstCtrl = this.yearEl; + this.lastCtrl = this.yearEl; + } + } + }, + + /** + * Default Keyboard event handler to capture Enter + * on the Navigator's year control (yearEl) + * + * @method _handleEnterKey + * @protected + * @param {Event} e The DOM event being handled + */ + _handleEnterKey : function(e) { + var KEYS = YAHOO.util.KeyListener.KEY; + + if (YAHOO.util.Event.getCharCode(e) == KEYS.ENTER) { + YAHOO.util.Event.preventDefault(e); + this.submit(); + } + }, + + /** + * Default Keyboard event handler to capture up/down/pgup/pgdown + * on the Navigator's year control (yearEl). + * + * @method _handleDirectionKeys + * @protected + * @param {Event} e The DOM event being handled + */ + _handleDirectionKeys : function(e) { + var E = YAHOO.util.Event, + KEYS = YAHOO.util.KeyListener.KEY, + NAV = YAHOO.widget.CalendarNavigator; + + var value = (this.yearEl.value) ? parseInt(this.yearEl.value, 10) : null; + if (isFinite(value)) { + var dir = false; + switch(E.getCharCode(e)) { + case KEYS.UP: + this.yearEl.value = value + NAV.YR_MINOR_INC; + dir = true; + break; + case KEYS.DOWN: + this.yearEl.value = Math.max(value - NAV.YR_MINOR_INC, 0); + dir = true; + break; + case KEYS.PAGE_UP: + this.yearEl.value = value + NAV.YR_MAJOR_INC; + dir = true; + break; + case KEYS.PAGE_DOWN: + this.yearEl.value = Math.max(value - NAV.YR_MAJOR_INC, 0); + dir = true; + break; + default: + break; + } + if (dir) { + E.preventDefault(e); + try { + this.yearEl.select(); + } catch(err) { + // Ignore + } + } + } + }, + + /** + * Default Keyboard event handler to capture Tab + * on the last control (lastCtrl) in the Navigator. + * + * @method _handleTabKey + * @protected + * @param {Event} e The DOM event being handled + */ + _handleTabKey : function(e) { + var E = YAHOO.util.Event, + KEYS = YAHOO.util.KeyListener.KEY; + + if (E.getCharCode(e) == KEYS.TAB && !e.shiftKey) { + try { + E.preventDefault(e); + this.firstCtrl.focus(); + } catch (err) { + // Ignore - mainly for focus edge cases + } + } + }, + + /** + * Default Keyboard event handler to capture Shift-Tab + * on the first control (firstCtrl) in the Navigator. + * + * @method _handleShiftTabKey + * @protected + * @param {Event} e The DOM event being handled + */ + _handleShiftTabKey : function(e) { + var E = YAHOO.util.Event, + KEYS = YAHOO.util.KeyListener.KEY; + + if (e.shiftKey && E.getCharCode(e) == KEYS.TAB) { + try { + E.preventDefault(e); + this.lastCtrl.focus(); + } catch (err) { + // Ignore - mainly for focus edge cases + } + } + }, + + /** + * Retrieve Navigator configuration values from + * the parent Calendar/CalendarGroup's config value. + *+ * If it has not been set in the user provided configuration, the method will + * return the default value of the configuration property, as set in DEFAULT_CONFIG + *
+ * @private + * @method __getCfg + * @param {String} Case sensitive property name. + * @param {Boolean} true, if the property is a string property, false if not. + * @return The value of the configuration property + */ + __getCfg : function(prop, bIsStr) { + var DEF_CFG = YAHOO.widget.CalendarNavigator.DEFAULT_CONFIG; + var cfg = this.cal.cfg.getProperty("navigator"); + + if (bIsStr) { + return (cfg !== true && cfg.strings && cfg.strings[prop]) ? cfg.strings[prop] : DEF_CFG.strings[prop]; + } else { + return (cfg !== true && cfg[prop]) ? cfg[prop] : DEF_CFG[prop]; + } + }, + + /** + * Private flag, to identify MacOS + * @private + * @property __isMac + */ + __isMac : (navigator.userAgent.toLowerCase().indexOf("macintosh") != -1) + +}; +YAHOO.register("calendar", YAHOO.widget.Calendar, {version: "2.8.0r4", build: "2449"}); diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/ajax-loader.gif b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/ajax-loader.gif new file mode 100644 index 0000000000..fe2cd23b3a Binary files /dev/null and b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/ajax-loader.gif differ diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/carousel-core.css b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/carousel-core.css new file mode 100644 index 0000000000..444c21f5ac --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/carousel-core.css @@ -0,0 +1,88 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.8.0r4 +*/ +.yui-carousel { + visibility: hidden; + overflow: hidden; + position: relative; + text-align: left; + zoom: 1; +} + +.yui-carousel.yui-carousel-visible { + visibility: visible; +} + +.yui-carousel-content { + overflow: hidden; + position: relative; + text-align:center; +} + +.yui-carousel-element li { + border: 1px solid #ccc; + list-style: none; + margin: 1px; + overflow: hidden; + padding: 0; + position: absolute; + text-align: center; +} + +.yui-carousel-vertical .yui-carousel-element li { + display: block; + float: none; +} + +.yui-log .carousel { + background: #f2e886; +} + +.yui-carousel-nav { + zoom: 1; +} + +.yui-carousel-nav:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.yui-carousel-button-focus { + outline: 1px dotted #000; +} + +.yui-carousel-min-width { + min-width: 115px; +} + +.yui-carousel-element { + overflow: hidden; + position:relative; + margin: 0 auto; + padding:0; + text-align:left; + *margin:0; +} + +.yui-carousel-horizontal .yui-carousel-element { + width: 320000px; +} + +.yui-carousel-vertical .yui-carousel-element { + height:320000px; +} + +.yui-skin-sam .yui-carousel-nav select { + position:static; +} + +.yui-carousel .yui-carousel-item-selected { + border: 1px dashed #000; + margin: 1px; +} \ No newline at end of file diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/ajax-loader.gif b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/ajax-loader.gif new file mode 100644 index 0000000000..fe2cd23b3a Binary files /dev/null and b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/ajax-loader.gif differ diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/carousel-skin.css b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/carousel-skin.css new file mode 100644 index 0000000000..efa48ebda9 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/carousel-skin.css @@ -0,0 +1,142 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.8.0r4 +*/ +.yui-skin-sam .yui-carousel, +.yui-skin-sam .yui-carousel-vertical { + border: 1px solid #808080; +} + +.yui-skin-sam .yui-carousel-nav { + background: url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0; + padding: 3px; + text-align: right; +} + +.yui-skin-sam .yui-carousel-button { + background: url(../../../../assets/skins/sam/sprite.png) no-repeat 0 -600px; + float: right; + height: 19px; + margin: 5px; + overflow: hidden; + width: 40px; +} + +.yui-skin-sam .yui-carousel-vertical .yui-carousel-button { + background-position: 0 -800px; +} + +.yui-skin-sam .yui-carousel-button-disabled { + background-position: 0 -2000px; +} + +.yui-skin-sam .yui-carousel-vertical .yui-carousel-button-disabled { + background-position: 0 -2100px; +} + +.yui-skin-sam .yui-carousel-button input, +.yui-skin-sam .yui-carousel-button button { + background-color: transparent; + border: 0; + cursor: pointer; + display: block; + height: 44px; + margin: -2px 0 0 -2px; + padding: 0 0 0 50px; +} + +.yui-skin-sam span.yui-carousel-first-button { + background-position: 0px -550px; + margin-left: -100px; + margin-right: 50px; + *margin: 5px 5px 5px -90px; +} + +.yui-skin-sam .yui-carousel-vertical span.yui-carousel-first-button { + background-position: 0px -750px; +} + +.yui-skin-sam span.yui-carousel-first-button-disabled { + background-position: 0 -1950px; +} + +.yui-skin-sam .yui-carousel-vertical span.yui-carousel-first-button-disabled { + background-position: 0 -2050px; +} + +.yui-skin-sam .yui-carousel-nav ul { + float: right; + height: 19px; + margin: 0; + margin-left: -220px; + margin-right: 100px; + *margin-left: -160px; + *margin-right: 0; + padding: 0; +} + +.yui-skin-sam .yui-carousel-min-width .yui-carousel-nav ul { + *margin-left: -170px; +} + +.yui-skin-sam .yui-carousel-nav select { + position: relative; + *right: 50px; + top: 4px; +} + +.yui-skin-sam .yui-carousel-vertical .yui-carousel-nav select { + position: static; +} + +.yui-skin-sam .yui-carousel-vertical .yui-carousel-nav ul, +.yui-skin-sam .yui-carousel-vertical .yui-carousel-nav select { + float: none; + margin: 0; + *zoom: 1; +} + +.yui-skin-sam .yui-carousel-nav ul li { + background: url(../../../../assets/skins/sam/sprite.png) no-repeat 0 -650px; + cursor: pointer; + float: left; + height: 9px; + list-style: none; + margin: 10px 0 0 5px; + overflow: hidden; + padding: 0; + width: 9px; +} + +.yui-skin-sam .yui-carousel-nav ul:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.yui-skin-sam .yui-carousel-nav ul li a { + display:block; + width: 100%; + height: 100%; + text-indent: -10000px; + text-align:left; + overflow:hidden; +} + +.yui-skin-sam .yui-carousel-nav ul li.yui-carousel-nav-page-focus { + outline: 1px dotted #000; +} + +.yui-skin-sam .yui-carousel-nav ul li.yui-carousel-nav-page-selected { + background-position: 0 -700px; +} + +.yui-skin-sam .yui-carousel-item-loading { + background: url(ajax-loader.gif) no-repeat 50% 50%; + position: absolute; + text-indent: -150px; +} diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/carousel.css b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/carousel.css new file mode 100644 index 0000000000..8dbb121307 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/assets/skins/sam/carousel.css @@ -0,0 +1,7 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.8.0r4 +*/ +.yui-carousel{visibility:hidden;overflow:hidden;position:relative;text-align:left;zoom:1;}.yui-carousel.yui-carousel-visible{visibility:visible;}.yui-carousel-content{overflow:hidden;position:relative;text-align:center;}.yui-carousel-element li{border:1px solid #ccc;list-style:none;margin:1px;overflow:hidden;padding:0;position:absolute;text-align:center;}.yui-carousel-vertical .yui-carousel-element li{display:block;float:none;}.yui-log .carousel{background:#f2e886;}.yui-carousel-nav{zoom:1;}.yui-carousel-nav:after{content:".";display:block;height:0;clear:both;visibility:hidden;}.yui-carousel-button-focus{outline:1px dotted #000;}.yui-carousel-min-width{min-width:115px;}.yui-carousel-element{overflow:hidden;position:relative;margin:0 auto;padding:0;text-align:left;*margin:0;}.yui-carousel-horizontal .yui-carousel-element{width:320000px;}.yui-carousel-vertical .yui-carousel-element{height:320000px;}.yui-skin-sam .yui-carousel-nav select{position:static;}.yui-carousel .yui-carousel-item-selected{border:1px dashed #000;margin:1px;}.yui-skin-sam .yui-carousel,.yui-skin-sam .yui-carousel-vertical{border:1px solid #808080;}.yui-skin-sam .yui-carousel-nav{background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0;padding:3px;text-align:right;}.yui-skin-sam .yui-carousel-button{background:url(../../../../assets/skins/sam/sprite.png) no-repeat 0 -600px;float:right;height:19px;margin:5px;overflow:hidden;width:40px;}.yui-skin-sam .yui-carousel-vertical .yui-carousel-button{background-position:0 -800px;}.yui-skin-sam .yui-carousel-button-disabled{background-position:0 -2000px;}.yui-skin-sam .yui-carousel-vertical .yui-carousel-button-disabled{background-position:0 -2100px;}.yui-skin-sam .yui-carousel-button input,.yui-skin-sam .yui-carousel-button button{background-color:transparent;border:0;cursor:pointer;display:block;height:44px;margin:-2px 0 0 -2px;padding:0 0 0 50px;}.yui-skin-sam span.yui-carousel-first-button{background-position:0 -550px;margin-left:-100px;margin-right:50px;*margin:5px 5px 5px -90px;}.yui-skin-sam .yui-carousel-vertical span.yui-carousel-first-button{background-position:0 -750px;}.yui-skin-sam span.yui-carousel-first-button-disabled{background-position:0 -1950px;}.yui-skin-sam .yui-carousel-vertical span.yui-carousel-first-button-disabled{background-position:0 -2050px;}.yui-skin-sam .yui-carousel-nav ul{float:right;height:19px;margin:0;margin-left:-220px;margin-right:100px;*margin-left:-160px;*margin-right:0;padding:0;}.yui-skin-sam .yui-carousel-min-width .yui-carousel-nav ul{*margin-left:-170px;}.yui-skin-sam .yui-carousel-nav select{position:relative;*right:50px;top:4px;}.yui-skin-sam .yui-carousel-vertical .yui-carousel-nav select{position:static;}.yui-skin-sam .yui-carousel-vertical .yui-carousel-nav ul,.yui-skin-sam .yui-carousel-vertical .yui-carousel-nav select{float:none;margin:0;*zoom:1;}.yui-skin-sam .yui-carousel-nav ul li{background:url(../../../../assets/skins/sam/sprite.png) no-repeat 0 -650px;cursor:pointer;float:left;height:9px;list-style:none;margin:10px 0 0 5px;overflow:hidden;padding:0;width:9px;}.yui-skin-sam .yui-carousel-nav ul:after{content:".";display:block;height:0;clear:both;visibility:hidden;}.yui-skin-sam .yui-carousel-nav ul li a{display:block;width:100%;height:100%;text-indent:-10000px;text-align:left;overflow:hidden;}.yui-skin-sam .yui-carousel-nav ul li.yui-carousel-nav-page-focus{outline:1px dotted #000;}.yui-skin-sam .yui-carousel-nav ul li.yui-carousel-nav-page-selected{background-position:0 -700px;}.yui-skin-sam .yui-carousel-item-loading{background:url(ajax-loader.gif) no-repeat 50% 50%;position:absolute;text-indent:-150px;} diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/carousel-debug.js b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/carousel-debug.js new file mode 100644 index 0000000000..63dcba8347 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/carousel/carousel-debug.js @@ -0,0 +1,4390 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.8.0r4 +*/ +/** + * The Carousel module provides a widget for browsing among a set of like + * objects represented pictorially. + * + * @module carousel + * @requires yahoo, dom, event, element + * @optional animation + * @namespace YAHOO.widget + * @title Carousel Widget + * @beta + */ +(function () { + + var WidgetName; // forward declaration + + /** + * The Carousel widget. + * + * @class Carousel + * @extends YAHOO.util.Element + * @constructor + * @param el {HTMLElement | String} The HTML element that represents the + * the container that houses the Carousel. + * @param cfg {Object} (optional) The configuration values + */ + YAHOO.widget.Carousel = function (el, cfg) { + YAHOO.log("Component creation", WidgetName); + + YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg); + }; + + /* + * Private variables of the Carousel component + */ + + /* Some abbreviations to avoid lengthy typing and lookups. */ + var Carousel = YAHOO.widget.Carousel, + Dom = YAHOO.util.Dom, + Event = YAHOO.util.Event, + JS = YAHOO.lang; + + /** + * The widget name. + * @private + * @static + */ + WidgetName = "Carousel"; + + /** + * The internal table of Carousel instances. + * @private + * @static + */ + var instances = {}, + + /* + * Custom events of the Carousel component + */ + + /** + * @event afterScroll + * @description Fires when the Carousel has scrolled to the previous or + * next page. Passes back the index of the first and last visible items in + * the Carousel. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + afterScrollEvent = "afterScroll", + + /** + * @event allItemsRemovedEvent + * @description Fires when all items have been removed from the Carousel. + * See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + allItemsRemovedEvent = "allItemsRemoved", + + /** + * @event beforeHide + * @description Fires before the Carousel is hidden. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + beforeHideEvent = "beforeHide", + + /** + * @event beforePageChange + * @description Fires when the Carousel is about to scroll to the previous + * or next page. Passes back the page number of the current page. Note + * that the first page number is zero. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + beforePageChangeEvent = "beforePageChange", + + /** + * @event beforeScroll + * @description Fires when the Carousel is about to scroll to the previous + * or next page. Passes back the index of the first and last visible items + * in the Carousel and the direction (backward/forward) of the scroll. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + beforeScrollEvent = "beforeScroll", + + /** + * @event beforeShow + * @description Fires when the Carousel is about to be shown. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + beforeShowEvent = "beforeShow", + + /** + * @event blur + * @description Fires when the Carousel loses focus. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + blurEvent = "blur", + + /** + * @event focus + * @description Fires when the Carousel gains focus. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + focusEvent = "focus", + + /** + * @event hide + * @description Fires when the Carousel is hidden. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + hideEvent = "hide", + + /** + * @event itemAdded + * @description Fires when an item has been added to the Carousel. Passes + * back the content of the item that would be added, the index at which the + * item would be added, and the event itself. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + itemAddedEvent = "itemAdded", + + /** + * @event itemRemoved + * @description Fires when an item has been removed from the Carousel. + * Passes back the content of the item that would be removed, the index + * from which the item would be removed, and the event itself. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + itemRemovedEvent = "itemRemoved", + + /** + * @event itemReplaced + * @description Fires when an item has been replaced in the Carousel. + * Passes back the content of the item that was replaced, the content + * of the new item, the index where the replacement occurred, and the event + * itself. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + itemReplacedEvent = "itemReplaced", + + /** + * @event itemSelected + * @description Fires when an item has been selected in the Carousel. + * Passes back the index of the selected item in the Carousel. Note, that + * the index begins from zero. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + itemSelectedEvent = "itemSelected", + + /** + * @event loadItems + * @description Fires when the Carousel needs more items to be loaded for + * displaying them. Passes back the first and last visible items in the + * Carousel, and the number of items needed to be loaded. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + loadItemsEvent = "loadItems", + + /** + * @event navigationStateChange + * @description Fires when the state of either one of the navigation + * buttons are changed from enabled to disabled or vice versa. Passes back + * the state (true/false) of the previous and next buttons. The value true + * signifies the button is enabled, false signifies disabled. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + navigationStateChangeEvent = "navigationStateChange", + + /** + * @event pageChange + * @description Fires after the Carousel has scrolled to the previous or + * next page. Passes back the page number of the current page. Note + * that the first page number is zero. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + pageChangeEvent = "pageChange", + + /* + * Internal event. + * @event render + * @description Fires when the Carousel is rendered. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + renderEvent = "render", + + /** + * @event show + * @description Fires when the Carousel is shown. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + showEvent = "show", + + /** + * @event startAutoPlay + * @description Fires when the auto play has started in the Carousel. See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + startAutoPlayEvent = "startAutoPlay", + + /** + * @event stopAutoPlay + * @description Fires when the auto play has been stopped in the Carousel. + * See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + stopAutoPlayEvent = "stopAutoPlay", + + /* + * Internal event. + * @event uiUpdateEvent + * @description Fires when the UI has been updated. + * See + * Element.addListener + * for more information on listening for this event. + * @type YAHOO.util.CustomEvent + */ + uiUpdateEvent = "uiUpdate"; + + /* + * Private helper functions used by the Carousel component + */ + + /** + * Set multiple styles on one element. + * @method setStyles + * @param el {HTMLElement} The element to set styles on + * @param style {Object} top:"10px", left:"0px", etc. + * @private + */ + function setStyles(el, styles) { + var which; + + for (which in styles) { + if (styles.hasOwnProperty(which)) { + Dom.setStyle(el, which, styles[which]); + } + } + } + + /** + * Create an element, set its class name and optionally install the element + * to its parent. + * @method createElement + * @param el {String} The element to be created + * @param attrs {Object} Configuration of parent, class and id attributes. + * If the content is specified, it is inserted after creation of the + * element. The content can also be an HTML element in which case it would + * be appended as a child node of the created element. + * @private + */ + function createElement(el, attrs) { + var newEl = document.createElement(el); + + attrs = attrs || {}; + if (attrs.className) { + Dom.addClass(newEl, attrs.className); + } + + if (attrs.styles) { + setStyles(newEl, attrs.styles); + } + + if (attrs.parent) { + attrs.parent.appendChild(newEl); + } + + if (attrs.id) { + newEl.setAttribute("id", attrs.id); + } + + if (attrs.content) { + if (attrs.content.nodeName) { + newEl.appendChild(attrs.content); + } else { + newEl.innerHTML = attrs.content; + } + } + + return newEl; + } + + /** + * Get the computed style of an element. + * + * @method getStyle + * @param el {HTMLElement} The element for which the style needs to be + * returned. + * @param style {String} The style attribute + * @param type {String} "int", "float", etc. (defaults to int) + * @private + */ + function getStyle(el, style, type) { + var value; + + if (!el) { + return 0; + } + + function getStyleIntVal(el, style) { + var val; + + /* + * XXX: Safari calculates incorrect marginRight for an element + * which has its parent element style set to overflow: hidden + * https://bugs.webkit.org/show_bug.cgi?id=13343 + * Let us assume marginLeft == marginRight + */ + if (style == "marginRight" && YAHOO.env.ua.webkit) { + val = parseInt(Dom.getStyle(el, "marginLeft"), 10); + } else { + val = parseInt(Dom.getStyle(el, style), 10); + } + + return JS.isNumber(val) ? val : 0; + } + + function getStyleFloatVal(el, style) { + var val; + + /* + * XXX: Safari calculates incorrect marginRight for an element + * which has its parent element style set to overflow: hidden + * https://bugs.webkit.org/show_bug.cgi?id=13343 + * Let us assume marginLeft == marginRight + */ + if (style == "marginRight" && YAHOO.env.ua.webkit) { + val = parseFloat(Dom.getStyle(el, "marginLeft")); + } else { + val = parseFloat(Dom.getStyle(el, style)); + } + + return JS.isNumber(val) ? val : 0; + } + + if (typeof type == "undefined") { + type = "int"; + } + + switch (style) { + case "height": + value = el.offsetHeight; + if (value > 0) { + value += getStyleIntVal(el, "marginTop") + + getStyleIntVal(el, "marginBottom"); + } else { + value = getStyleFloatVal(el, "height") + + getStyleIntVal(el, "marginTop") + + getStyleIntVal(el, "marginBottom") + + getStyleIntVal(el, "borderTopWidth") + + getStyleIntVal(el, "borderBottomWidth") + + getStyleIntVal(el, "paddingTop") + + getStyleIntVal(el, "paddingBottom"); + } + break; + case "width": + value = el.offsetWidth; + if (value > 0) { + value += getStyleIntVal(el, "marginLeft") + + getStyleIntVal(el, "marginRight"); + } else { + value = getStyleFloatVal(el, "width") + + getStyleIntVal(el, "marginLeft") + + getStyleIntVal(el, "marginRight") + + getStyleIntVal(el, "borderLeftWidth") + + getStyleIntVal(el, "borderRightWidth") + + getStyleIntVal(el, "paddingLeft") + + getStyleIntVal(el, "paddingRight"); + } + break; + default: + if (type == "int") { + value = getStyleIntVal(el, style); + } else if (type == "float") { + value = getStyleFloatVal(el, style); + } else { + value = Dom.getStyle(el, style); + } + break; + } + + return value; + } + + /** + * Compute and return the height or width of a single Carousel item + * depending upon the orientation. + * + * @method getCarouselItemSize + * @param which {String} "height" or "width" to be returned. If this is + * passed explicitly, the calculated size is not cached. + * @private + */ + function getCarouselItemSize(which) { + var carousel = this, + child, + item, + size = 0, + first = carousel.get("firstVisible"), + vertical = false; + + if (carousel._itemsTable.numItems === 0) { + return 0; + } + + item = carousel._itemsTable.items[first] || + carousel._itemsTable.loading[first]; + + if (JS.isUndefined(item)) { + return 0; + } + + child = Dom.get(item.id); + + if (typeof which == "undefined") { + vertical = carousel.get("isVertical"); + } else { + vertical = which == "height"; + } + + if (this._itemAttrCache[which]) { + return this._itemAttrCache[which]; + } + + if (vertical) { + size = getStyle(child, "height"); + } else { + size = getStyle(child, "width"); + } + + this._itemAttrCache[which] = size; + + return size; + } + + /** + * Return the size of a part of the item (reveal). + * + * @method getRevealSize + * @private + */ + function getRevealSize() { + var carousel = this, isVertical, sz; + + isVertical = carousel.get("isVertical"); + sz = getCarouselItemSize.call(carousel, + isVertical ? "height" : "width"); + return (sz * carousel.get("revealAmount") / 100); + } + + /** + * Compute and return the position of a Carousel item based on its + * position. + * + * @method getCarouselItemPosition + * @param position {Number} The position of the Carousel item. + * @private + */ + function getCarouselItemPosition(pos) { + var carousel = this, + itemsPerRow = carousel._cols, + itemsPerCol = carousel._rows, + page, + sz, + isVertical, + itemsCol, + itemsRow, + sentinel, + delta = 0, + top, + left, + rsz, + styles = {}, + index = 0, + itemsTable = carousel._itemsTable, + items = itemsTable.items, + loading = itemsTable.loading; + + isVertical = carousel.get("isVertical"); + sz = getCarouselItemSize.call(carousel, + isVertical ? "height" : "width"); + rsz = getRevealSize.call(carousel); + + // adjust for items not yet loaded + while (index < pos) { + if (!items[index] && !loading[index]) { + delta++; + } + index++; + } + pos -= delta; + + if (itemsPerCol) { + page = this.getPageForItem(pos); + if (isVertical) { + itemsRow = Math.floor(pos/itemsPerRow); + delta = itemsRow; + top = delta * sz; + styles.top = (top + rsz) + "px"; + + sz = getCarouselItemSize.call(carousel, "width"); + + itemsCol = pos % itemsPerRow; + delta = itemsCol; + left = delta * sz; + styles.left = left + "px"; + } else { + itemsCol = pos % itemsPerRow; + sentinel = (page - 1) * itemsPerRow; + delta = itemsCol + sentinel; + left = delta * sz; + styles.left = (left + rsz) + "px"; + + sz = getCarouselItemSize.call(carousel, "height"); + + itemsRow = Math.floor(pos/itemsPerRow); + sentinel = (page - 1) * itemsPerCol; + delta = itemsRow - sentinel; + top = delta * sz; + + styles.top = top + "px"; + } + } else { + if (isVertical) { + styles.left = 0; + styles.top = ((pos * sz) + rsz) + "px"; + } else { + styles.top = 0; + styles.left = ((pos * sz) + rsz) + "px"; + } + } + + return styles; + } + + /** + * Return the index of the first item in the view port for displaying item + * in "pos". + * + * @method getFirstVisibleForPosition + * @param pos {Number} The position of the item to be displayed + * @private + */ + function getFirstVisibleForPosition(pos) { + var num = this.get("numVisible"); + return Math.floor(pos / num) * num; + } + + /** + * Return the scrolling offset size given the number of elements to + * scroll. + * + * @method getScrollOffset + * @param delta {Number} The delta number of elements to scroll by. + * @private + */ + function getScrollOffset(delta) { + var itemSize = 0, + size = 0; + + itemSize = getCarouselItemSize.call(this); + size = itemSize * delta; + + return size; + } + + /** + * Scroll the Carousel by a page backward. + * + * @method scrollPageBackward + * @param {Event} ev The event object + * @param {Object} obj The context object + * @private + */ + function scrollPageBackward(ev, obj) { + obj.scrollPageBackward(); + Event.preventDefault(ev); + } + + /** + * Scroll the Carousel by a page forward. + * + * @method scrollPageForward + * @param {Event} ev The event object + * @param {Object} obj The context object + * @private + */ + function scrollPageForward(ev, obj) { + obj.scrollPageForward(); + Event.preventDefault(ev); + } + + /** + * Set the selected item. + * + * @method setItemSelection + * @param {Number} newpos The index of the new position + * @param {Number} oldpos The index of the previous position + * @private + */ + function setItemSelection(newpos, oldpos) { + var carousel = this, + cssClass = carousel.CLASSES, + el, + firstItem = carousel._firstItem, + isCircular = carousel.get("isCircular"), + numItems = carousel.get("numItems"), + numVisible = carousel.get("numVisible"), + position = oldpos, + sentinel = firstItem + numVisible - 1; + + if (position >= 0 && position < numItems) { + if (!JS.isUndefined(carousel._itemsTable.items[position])) { + el = Dom.get(carousel._itemsTable.items[position].id); + if (el) { + Dom.removeClass(el, cssClass.SELECTED_ITEM); + } + } + } + + if (JS.isNumber(newpos)) { + newpos = parseInt(newpos, 10); + newpos = JS.isNumber(newpos) ? newpos : 0; + } else { + newpos = firstItem; + } + + if (JS.isUndefined(carousel._itemsTable.items[newpos])) { + newpos = getFirstVisibleForPosition.call(carousel, newpos); + carousel.scrollTo(newpos); // still loading the item + } + + if (!JS.isUndefined(carousel._itemsTable.items[newpos])) { + el = Dom.get(carousel._itemsTable.items[newpos].id); + if (el) { + Dom.addClass(el, cssClass.SELECTED_ITEM); + } + } + + if (newpos < firstItem || newpos > sentinel) { // out of focus + newpos = getFirstVisibleForPosition.call(carousel, newpos); + carousel.scrollTo(newpos); + } + } + + /** + * Fire custom events for enabling/disabling navigation elements. + * + * @method syncNavigation + * @private + */ + function syncNavigation() { + var attach = false, + carousel = this, + cssClass = carousel.CLASSES, + i, + navigation, + sentinel; + + // Don't do anything if the Carousel is not rendered + if (!carousel._hasRendered) { + return; + } + + navigation = carousel.get("navigation"); + sentinel = carousel._firstItem + carousel.get("numVisible"); + + if (navigation.prev) { + if (carousel.get("numItems") === 0 || carousel._firstItem === 0) { + if (carousel.get("numItems") === 0 || + !carousel.get("isCircular")) { + Event.removeListener(navigation.prev, "click", + scrollPageBackward); + Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED); + for (i = 0; i < carousel._navBtns.prev.length; i++) { + carousel._navBtns.prev[i].setAttribute("disabled", + "true"); + } + carousel._prevEnabled = false; + } else { + attach = !carousel._prevEnabled; + } + } else { + attach = !carousel._prevEnabled; + } + + if (attach) { + Event.on(navigation.prev, "click", scrollPageBackward, + carousel); + Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED); + for (i = 0; i < carousel._navBtns.prev.length; i++) { + carousel._navBtns.prev[i].removeAttribute("disabled"); + } + carousel._prevEnabled = true; + } + } + + attach = false; + if (navigation.next) { + if (sentinel >= carousel.get("numItems")) { + if (!carousel.get("isCircular")) { + Event.removeListener(navigation.next, "click", + scrollPageForward); + Dom.addClass(navigation.next, cssClass.DISABLED); + for (i = 0; i < carousel._navBtns.next.length; i++) { + carousel._navBtns.next[i].setAttribute("disabled", + "true"); + } + carousel._nextEnabled = false; + } else { + attach = !carousel._nextEnabled; + } + } else { + attach = !carousel._nextEnabled; + } + + if (attach) { + Event.on(navigation.next, "click", scrollPageForward, + carousel); + Dom.removeClass(navigation.next, cssClass.DISABLED); + for (i = 0; i < carousel._navBtns.next.length; i++) { + carousel._navBtns.next[i].removeAttribute("disabled"); + } + carousel._nextEnabled = true; + } + } + + carousel.fireEvent(navigationStateChangeEvent, + { next: carousel._nextEnabled, prev: carousel._prevEnabled }); + } + + /** + * Synchronize and redraw the Pager UI if necessary. + * + * @method syncPagerUi + * @private + */ + function syncPagerUi(page) { + var carousel = this, numPages, numVisible; + + // Don't do anything if the Carousel is not rendered + if (!carousel._hasRendered) { + return; + } + + numVisible = carousel.get("numVisible"); + + if (!JS.isNumber(page)) { + page = Math.floor(carousel.get("selectedItem") / numVisible); + } + + numPages = Math.ceil(carousel.get("numItems") / numVisible); + + carousel._pages.num = numPages; + carousel._pages.cur = page; + + if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) { + carousel._updatePagerMenu(); + } else { + carousel._updatePagerButtons(); + } + } + + /** + * Get full dimensions of an element. + * + * @method getDimensions + * @param {Object} el The element to get the dimensions of + * @param {String} which Get the height or width of an element + * @private + */ + function getDimensions(el, which) { + switch (which) { + case 'height': + return getStyle(el, "marginTop") + + getStyle(el, "marginBottom") + + getStyle(el, "paddingTop") + + getStyle(el, "paddingBottom") + + getStyle(el, "borderTopWidth") + + getStyle(el, "borderBottomWidth"); + case 'width': + return getStyle(el, "marginLeft") + + getStyle(el, "marginRight") + + getStyle(el, "paddingLeft") + + getStyle(el, "paddingRight") + + getStyle(el, "borderLeftWidth") + + getStyle(el, "borderRightWidth"); + default: + break; + } + + return getStyle(el, which); + } + + /** + * Handle UI update. + * Call the appropriate methods on events fired when an item is added, or + * removed for synchronizing the DOM. + * + * @method syncUi + * @param {Object} o The item that needs to be added or removed + * @private + */ + function syncUi(o) { + var carousel = this; + + if (!JS.isObject(o)) { + return; + } + + switch (o.ev) { + case itemAddedEvent: + carousel._syncUiForItemAdd(o); + break; + case itemRemovedEvent: + carousel._syncUiForItemRemove(o); + break; + case itemReplacedEvent: + carousel._syncUiForItemReplace(o); + break; + case loadItemsEvent: + carousel._syncUiForLazyLoading(o); + break; + } + + carousel.fireEvent(uiUpdateEvent); + } + + /** + * Update the state variables after scrolling the Carousel view port. + * + * @method updateStateAfterScroll + * @param {Integer} item The index to which the Carousel has scrolled to. + * @param {Integer} sentinel The last element in the view port. + * @private + */ + function updateStateAfterScroll(item, sentinel) { + var carousel = this, + page = carousel.get("currentPage"), + newPage, + numPerPage = carousel.get("numVisible"); + + newPage = parseInt(carousel._firstItem / numPerPage, 10); + if (newPage != page) { + carousel.setAttributeConfig("currentPage", { value: newPage }); + carousel.fireEvent(pageChangeEvent, newPage); + } + + if (carousel.get("selectOnScroll")) { + if (carousel.get("selectedItem") != carousel._selectedItem) { + carousel.set("selectedItem", carousel._selectedItem); + } + } + + clearTimeout(carousel._autoPlayTimer); + delete carousel._autoPlayTimer; + if (carousel.isAutoPlayOn()) { + carousel.startAutoPlay(); + } + + carousel.fireEvent(afterScrollEvent, + { first: carousel._firstItem, + last: sentinel }, + carousel); + } + + /* + * Static members and methods of the Carousel component + */ + + /** + * Return the appropriate Carousel object based on the id associated with + * the Carousel element or false if none match. + * @method getById + * @public + * @static + */ + Carousel.getById = function (id) { + return instances[id] ? instances[id].object : false; + }; + + YAHOO.extend(Carousel, YAHOO.util.Element, { + + /* + * Internal variables used within the Carousel component + */ + + /** + * Number of rows for a multirow carousel. + * + * @property _rows + * @private + */ + _rows: null, + + /** + * Number of cols for a multirow carousel. + * + * @property _cols + * @private + */ + _cols: null, + + /** + * The Animation object. + * + * @property _animObj + * @private + */ + _animObj: null, + + /** + * The Carousel element. + * + * @property _carouselEl + * @private + */ + _carouselEl: null, + + /** + * The Carousel clipping container element. + * + * @property _clipEl + * @private + */ + _clipEl: null, + + /** + * The current first index of the Carousel. + * + * @property _firstItem + * @private + */ + _firstItem: 0, + + /** + * Does the Carousel element have focus? + * + * @property _hasFocus + * @private + */ + _hasFocus: false, + + /** + * Is the Carousel rendered already? + * + * @property _hasRendered + * @private + */ + _hasRendered: false, + + /** + * Is the animation still in progress? + * + * @property _isAnimationInProgress + * @private + */ + _isAnimationInProgress: false, + + /** + * Is the auto-scrolling of Carousel in progress? + * + * @property _isAutoPlayInProgress + * @private + */ + _isAutoPlayInProgress: false, + + /** + * The table of items in the Carousel. + * The numItems is the number of items in the Carousel, items being the + * array of items in the Carousel. The size is the size of a single + * item in the Carousel. It is cached here for efficiency (to avoid + * computing the size multiple times). + * + * @property _itemsTable + * @private + */ + _itemsTable: null, + + /** + * The Carousel navigation buttons. + * + * @property _navBtns + * @private + */ + _navBtns: null, + + /** + * The Carousel navigation. + * + * @property _navEl + * @private + */ + _navEl: null, + + /** + * Status of the next navigation item. + * + * @property _nextEnabled + * @private + */ + _nextEnabled: true, + + /** + * The Carousel pages structure. + * This is an object of the total number of pages and the current page. + * + * @property _pages + * @private + */ + _pages: null, + + /** + * The Carousel pagination structure. + * + * @property _pagination + * @private + */ + _pagination: {}, + + /** + * Status of the previous navigation item. + * + * @property _prevEnabled + * @private + */ + _prevEnabled: true, + + /** + * Whether the Carousel size needs to be recomputed or not? + * + * @property _recomputeSize + * @private + */ + _recomputeSize: true, + + /** + * Cache the Carousel item attributes. + * + * @property _itemAttrCache + * @private + */ + _itemAttrCache: {}, + + /* + * CSS classes used by the Carousel component + */ + + CLASSES: { + + /** + * The class name of the Carousel navigation buttons. + * + * @property BUTTON + * @default "yui-carousel-button" + */ + BUTTON: "yui-carousel-button", + + /** + * The class name of the Carousel element. + * + * @property CAROUSEL + * @default "yui-carousel" + */ + CAROUSEL: "yui-carousel", + + /** + * The class name of the container of the items in the Carousel. + * + * @property CAROUSEL_EL + * @default "yui-carousel-element" + */ + CAROUSEL_EL: "yui-carousel-element", + + /** + * The class name of the Carousel's container element. + * + * @property CONTAINER + * @default "yui-carousel-container" + */ + CONTAINER: "yui-carousel-container", + + /** + * The class name of the Carousel's container element. + * + * @property CONTENT + * @default "yui-carousel-content" + */ + CONTENT: "yui-carousel-content", + + /** + * The class name of a disabled navigation button. + * + * @property DISABLED + * @default "yui-carousel-button-disabled" + */ + DISABLED: "yui-carousel-button-disabled", + + /** + * The class name of the first Carousel navigation button. + * + * @property FIRST_NAV + * @default " yui-carousel-first-button" + */ + FIRST_NAV: " yui-carousel-first-button", + + /** + * The class name of a first disabled navigation button. + * + * @property FIRST_NAV_DISABLED + * @default "yui-carousel-first-button-disabled" + */ + FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled", + + /** + * The class name of a first page element. + * + * @property FIRST_PAGE + * @default "yui-carousel-nav-first-page" + */ + FIRST_PAGE: "yui-carousel-nav-first-page", + + /** + * The class name of the Carousel navigation button that has focus. + * + * @property FOCUSSED_BUTTON + * @default "yui-carousel-button-focus" + */ + FOCUSSED_BUTTON: "yui-carousel-button-focus", + + /** + * The class name of a horizontally oriented Carousel. + * + * @property HORIZONTAL + * @default "yui-carousel-horizontal" + */ + HORIZONTAL: "yui-carousel-horizontal", + + /** + * The element to be used as the progress indicator when the item + * is still being loaded. + * + * @property ITEM_LOADING + * @default The progress indicator (spinner) image CSS class + */ + ITEM_LOADING: "yui-carousel-item-loading", + + /** + * The class name that will be set if the Carousel adjusts itself + * for a minimum width. + * + * @property MIN_WIDTH + * @default "yui-carousel-min-width" + */ + MIN_WIDTH: "yui-carousel-min-width", + + /** + * The navigation element container class name. + * + * @property NAVIGATION + * @default "yui-carousel-nav" + */ + NAVIGATION: "yui-carousel-nav", + + /** + * The class name of the next Carousel navigation button. + * + * @property NEXT_NAV + * @default " yui-carousel-next-button" + */ + NEXT_NAV: " yui-carousel-next-button", + + /** + * The class name of the next navigation link. This variable is + * not only used for styling, but also for identifying the link + * within the Carousel container. + * + * @property NEXT_PAGE + * @default "yui-carousel-next" + */ + NEXT_PAGE: "yui-carousel-next", + + /** + * The class name for the navigation container for prev/next. + * + * @property NAV_CONTAINER + * @default "yui-carousel-buttons" + */ + NAV_CONTAINER: "yui-carousel-buttons", + + /** + * The class name for an item in the pager UL or dropdown menu. + * + * @property PAGER_ITEM + * @default "yui-carousel-pager-item" + */ + PAGER_ITEM: "yui-carousel-pager-item", + + /** + * The class name for the pagination container + * + * @property PAGINATION + * @default "yui-carousel-pagination" + */ + PAGINATION: "yui-carousel-pagination", + + /** + * The class name of the focussed page navigation. This class is + * specifically used for the ugly focus handling in Opera. + * + * @property PAGE_FOCUS + * @default "yui-carousel-nav-page-focus" + */ + PAGE_FOCUS: "yui-carousel-nav-page-focus", + + /** + * The class name of the previous navigation link. This variable + * is not only used for styling, but also for identifying the link + * within the Carousel container. + * + * @property PREV_PAGE + * @default "yui-carousel-prev" + */ + PREV_PAGE: "yui-carousel-prev", + + /** + * The class name of the selected item. + * + * @property SELECTED_ITEM + * @default "yui-carousel-item-selected" + */ + SELECTED_ITEM: "yui-carousel-item-selected", + + /** + * The class name of the selected paging navigation. + * + * @property SELECTED_NAV + * @default "yui-carousel-nav-page-selected" + */ + SELECTED_NAV: "yui-carousel-nav-page-selected", + + /** + * The class name of a vertically oriented Carousel. + * + * @property VERTICAL + * @default "yui-carousel-vertical" + */ + VERTICAL: "yui-carousel-vertical", + + /** + * The class name of a multirow Carousel. + * + * @property MULTI_ROW + * @default "yui-carousel-multi-row" + */ + MULTI_ROW: "yui-carousel-multi-row", + + /** + * The class name of a row in a multirow Carousel. + * + * @property ROW + * @default "yui-carousel-new-row" + */ + ROW: "yui-carousel-row", + + /** + * The class name of a vertical Carousel's container element. + * + * @property VERTICAL_CONTAINER + * @default "yui-carousel-vertical-container" + */ + VERTICAL_CONTAINER: "yui-carousel-vertical-container", + + /** + * The class name of a visible Carousel. + * + * @property VISIBLE + * @default "yui-carousel-visible" + */ + VISIBLE: "yui-carousel-visible" + + }, + + /* + * Configuration attributes for configuring the Carousel component + */ + + CONFIG: { + + /** + * The offset of the first visible item in the Carousel. + * + * @property FIRST_VISIBLE + * @default 0 + */ + FIRST_VISIBLE: 0, + + /** + * The minimum width of the horizontal Carousel container to support + * the navigation buttons. + * + * @property HORZ_MIN_WIDTH + * @default 180 + */ + HORZ_MIN_WIDTH: 180, + + /** + * The maximum number of pager buttons allowed beyond which the UI + * of the pager would be a drop-down of pages instead of buttons. + * + * @property MAX_PAGER_BUTTONS + * @default 5 + */ + MAX_PAGER_BUTTONS: 5, + + /** + * The minimum width of the vertical Carousel container to support + * the navigation buttons. + * + * @property VERT_MIN_WIDTH + * @default 155 + */ + VERT_MIN_WIDTH: 115, + + /** + * The number of visible items in the Carousel. + * + * @property NUM_VISIBLE + * @default 3 + */ + NUM_VISIBLE: 3 + + }, + + /* + * Internationalizable strings in the Carousel component + */ + + STRINGS: { + + /** + * The content to be used as the progress indicator when the item + * is still being loaded. + * + * @property ITEM_LOADING_CONTENT + * @default "Loading" + */ + ITEM_LOADING_CONTENT: "Loading", + + /** + * The next navigation button name/text. + * + * @property NEXT_BUTTON_TEXT + * @default "Next Page" + */ + NEXT_BUTTON_TEXT: "Next Page", + + /** + * The prefix text for the pager in case the UI is a drop-down. + * + * @property PAGER_PREFIX_TEXT + * @default "Go to page " + */ + PAGER_PREFIX_TEXT: "Go to page ", + + /** + * The previous navigation button name/text. + * + * @property PREVIOUS_BUTTON_TEXT + * @default "Previous Page" + */ + PREVIOUS_BUTTON_TEXT: "Previous Page" + + }, + + /* + * Public methods of the Carousel component + */ + + /** + * Insert or append an item to the Carousel. + * E.g. if Object: ({content:"Your Content", id:"", className:""}, index) + * + * @method addItem + * @public + * @param item {String | Object | HTMLElement} The item to be appended + * to the Carousel. If the parameter is a string, it is assumed to be + * the content of the newly created item. If the parameter is an + * object, it is assumed to supply the content and an optional class + * and an optional id of the newly created item. + * @param index {Number} optional The position to where in the list + * (starts from zero). + * @return {Boolean} Return true on success, false otherwise + */ + addItem: function (item, index) { + var carousel = this, + className, + content, + elId, + replaceItems = 0, + newIndex, // Add newIndex as workaround for undefined pos + numItems = carousel.get("numItems"); + + if (!item) { + return false; + } + + if (JS.isString(item) || item.nodeName) { + content = item.nodeName ? item.innerHTML : item; + } else if (JS.isObject(item)) { + content = item.content; + } else { + YAHOO.log("Invalid argument to addItem", "error", WidgetName); + return false; + } + + className = item.className || ""; + elId = item.id ? item.id : Dom.generateId(); + + if (JS.isUndefined(index)) { + carousel._itemsTable.items.push({ + item : content, + className : className, + id : elId + }); + // Add newIndex as workaround for undefined pos + newIndex = carousel._itemsTable.items.length-1; + } else { + if (index < 0 || index > numItems) { + YAHOO.log("Index out of bounds", "error", WidgetName); + return false; + } + + // make sure we splice into the correct position + if(!carousel._itemsTable.items[index]){ + carousel._itemsTable.items[index] = undefined; + replaceItems = 1; + } + + carousel._itemsTable.items.splice(index, replaceItems, { + item : content, + className : className, + id : elId + }); + } + carousel._itemsTable.numItems++; + + if (numItems < carousel._itemsTable.items.length) { + carousel.set("numItems", carousel._itemsTable.items.length); + } + + // Add newPos as workaround for undefined pos + carousel.fireEvent(itemAddedEvent, { pos: index, ev: itemAddedEvent, newPos:newIndex }); + + return true; + }, + + /** + * Insert or append multiple items to the Carousel. + * + * @method addItems + * @public + * @param items {Array} An array containing an array of new items each linked to the + * index where the insertion should take place. + * E.g. [[{content:'Series
+ * object shouldn't be instantiated directly. Instead, a subclass with a
+ * concrete implementation should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class Series
+ * @constructor
+ */
+YAHOO.widget.Series = function() {};
+
+YAHOO.widget.Series.prototype =
+{
+ /**
+ * The type of series.
+ *
+ * @property type
+ * @type String
+ */
+ type: null,
+
+ /**
+ * The human-readable name of the series.
+ *
+ * @property displayName
+ * @type String
+ */
+ displayName: null
+};
+
+/**
+ * Functionality common to most series appearing in cartesian charts.
+ * Generally, a CartesianSeries object shouldn't be
+ * instantiated directly. Instead, a subclass with a concrete implementation
+ * should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class CartesianSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.CartesianSeries = function()
+{
+ YAHOO.widget.CartesianSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CartesianSeries, YAHOO.widget.Series,
+{
+ /**
+ * The field used to access the x-axis value from the items from the data source.
+ *
+ * @property xField
+ * @type String
+ */
+ xField: null,
+
+ /**
+ * The field used to access the y-axis value from the items from the data source.
+ *
+ * @property yField
+ * @type String
+ */
+ yField: null,
+
+ /**
+ * Indicates which axis the series will bind to
+ *
+ * @property axis
+ * @type String
+ */
+ axis: "primary",
+
+ /**
+ * When a Legend is present, indicates whether the series will show in the legend.
+ *
+ * @property showInLegend
+ * @type Boolean
+ */
+ showInLegend: true
+});
+
+/**
+ * ColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class ColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.ColumnSeries = function()
+{
+ YAHOO.widget.ColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.ColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "column"
+});
+
+/**
+ * LineSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class LineSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.LineSeries = function()
+{
+ YAHOO.widget.LineSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.LineSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "line"
+});
+
+
+/**
+ * BarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class BarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.BarSeries = function()
+{
+ YAHOO.widget.BarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.BarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "bar"
+});
+
+
+/**
+ * PieSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class PieSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.PieSeries = function()
+{
+ YAHOO.widget.PieSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.PieSeries, YAHOO.widget.Series,
+{
+ type: "pie",
+
+ /**
+ * The field used to access the data value from the items from the data source.
+ *
+ * @property dataField
+ * @type String
+ */
+ dataField: null,
+
+ /**
+ * The field used to access the category value from the items from the data source.
+ *
+ * @property categoryField
+ * @type String
+ */
+ categoryField: null,
+
+ /**
+ * A string reference to the globally-accessible function that may be called to
+ * determine each of the label values for this series. Also accepts function references.
+ *
+ * @property labelFunction
+ * @type String
+ */
+ labelFunction: null
+});
+
+/**
+ * StackedBarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedBarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedBarSeries = function()
+{
+ YAHOO.widget.StackedBarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedBarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackbar"
+});
+
+/**
+ * StackedColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedColumnSeries = function()
+{
+ YAHOO.widget.StackedColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackcolumn"
+});
+YAHOO.register("charts", YAHOO.widget.Chart, {version: "2.8.0r4", build: "2449"});
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/charts/charts-min.js b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/charts/charts-min.js
new file mode 100644
index 0000000000..fcf6144fc2
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/charts/charts-min.js
@@ -0,0 +1,9 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.8.0r4
+*/
+YAHOO.widget.Chart=function(I,F,A,J){this._type=I;this._dataSource=A;var B={align:"",allowNetworking:"",allowScriptAccess:"",base:"",bgcolor:"",menu:"",name:"",quality:"",salign:"",scale:"",tabindex:"",wmode:""};var D={fixedAttributes:{allowScriptAccess:"always"},flashVars:{allowedDomain:document.location.hostname},backgroundColor:"#ffffff",host:this,version:9.045};for(var E in J){if(B.hasOwnProperty(E)){D.fixedAttributes[E]=J[E];}else{D[E]=J[E];}}this._id=D.id=D.id||YAHOO.util.Dom.generateId(null,"yuigen");if(D.version&&D.version!=null&&D.version!=undefined&&D.version!="undefined"){var H=(/\w*.\w*/.exec(((D.version).toString()).replace(/.0./g,"."))).toString();var C=H.split(".");H=C[0]+".";switch((C[1].toString()).length){case 1:H+="00";break;case 2:H+="0";break;}H+=C[1];D.version=parseFloat(H);}this._swfURL=YAHOO.widget.Chart.SWFURL;this._containerID=F;this._attributes=D;this._swfEmbed=new YAHOO.widget.SWF(F,YAHOO.widget.Chart.SWFURL,D);this._swf=this._swfEmbed.swf;this._swfEmbed.subscribe("swfReady",this._eventHandler,this,true);try{this.createEvent("contentReady");}catch(G){}this.createEvent("itemMouseOverEvent");this.createEvent("itemMouseOutEvent");this.createEvent("itemClickEvent");this.createEvent("itemDoubleClickEvent");this.createEvent("itemDragStartEvent");this.createEvent("itemDragEvent");this.createEvent("itemDragEndEvent");};YAHOO.extend(YAHOO.widget.Chart,YAHOO.util.AttributeProvider,{_type:null,_pollingID:null,_pollingInterval:null,_dataTipFunction:null,_legendLabelFunction:null,_seriesFunctions:null,toString:function(){return"Chart "+this._id;},setStyle:function(A,B){B=YAHOO.lang.JSON.stringify(B);this._swf.setStyle(A,B);},setStyles:function(A){A=YAHOO.lang.JSON.stringify(A);this._swf.setStyles(A);},setSeriesStyles:function(B){for(var A=0;ASeries
+ * object shouldn't be instantiated directly. Instead, a subclass with a
+ * concrete implementation should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class Series
+ * @constructor
+ */
+YAHOO.widget.Series = function() {};
+
+YAHOO.widget.Series.prototype =
+{
+ /**
+ * The type of series.
+ *
+ * @property type
+ * @type String
+ */
+ type: null,
+
+ /**
+ * The human-readable name of the series.
+ *
+ * @property displayName
+ * @type String
+ */
+ displayName: null
+};
+
+/**
+ * Functionality common to most series appearing in cartesian charts.
+ * Generally, a CartesianSeries object shouldn't be
+ * instantiated directly. Instead, a subclass with a concrete implementation
+ * should be used.
+ *
+ * @namespace YAHOO.widget
+ * @class CartesianSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.CartesianSeries = function()
+{
+ YAHOO.widget.CartesianSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.CartesianSeries, YAHOO.widget.Series,
+{
+ /**
+ * The field used to access the x-axis value from the items from the data source.
+ *
+ * @property xField
+ * @type String
+ */
+ xField: null,
+
+ /**
+ * The field used to access the y-axis value from the items from the data source.
+ *
+ * @property yField
+ * @type String
+ */
+ yField: null,
+
+ /**
+ * Indicates which axis the series will bind to
+ *
+ * @property axis
+ * @type String
+ */
+ axis: "primary",
+
+ /**
+ * When a Legend is present, indicates whether the series will show in the legend.
+ *
+ * @property showInLegend
+ * @type Boolean
+ */
+ showInLegend: true
+});
+
+/**
+ * ColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class ColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.ColumnSeries = function()
+{
+ YAHOO.widget.ColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.ColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "column"
+});
+
+/**
+ * LineSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class LineSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.LineSeries = function()
+{
+ YAHOO.widget.LineSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.LineSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "line"
+});
+
+
+/**
+ * BarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class BarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.BarSeries = function()
+{
+ YAHOO.widget.BarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.BarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "bar"
+});
+
+
+/**
+ * PieSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class PieSeries
+ * @uses YAHOO.widget.Series
+ * @constructor
+ */
+YAHOO.widget.PieSeries = function()
+{
+ YAHOO.widget.PieSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.PieSeries, YAHOO.widget.Series,
+{
+ type: "pie",
+
+ /**
+ * The field used to access the data value from the items from the data source.
+ *
+ * @property dataField
+ * @type String
+ */
+ dataField: null,
+
+ /**
+ * The field used to access the category value from the items from the data source.
+ *
+ * @property categoryField
+ * @type String
+ */
+ categoryField: null,
+
+ /**
+ * A string reference to the globally-accessible function that may be called to
+ * determine each of the label values for this series. Also accepts function references.
+ *
+ * @property labelFunction
+ * @type String
+ */
+ labelFunction: null
+});
+
+/**
+ * StackedBarSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedBarSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedBarSeries = function()
+{
+ YAHOO.widget.StackedBarSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedBarSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackbar"
+});
+
+/**
+ * StackedColumnSeries class for the YUI Charts widget.
+ *
+ * @namespace YAHOO.widget
+ * @class StackedColumnSeries
+ * @uses YAHOO.widget.CartesianSeries
+ * @constructor
+ */
+YAHOO.widget.StackedColumnSeries = function()
+{
+ YAHOO.widget.StackedColumnSeries.superclass.constructor.call(this);
+};
+
+YAHOO.lang.extend(YAHOO.widget.StackedColumnSeries, YAHOO.widget.CartesianSeries,
+{
+ type: "stackcolumn"
+});
+YAHOO.register("charts", YAHOO.widget.Chart, {version: "2.8.0r4", build: "2449"});
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/colorpicker-core.css b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/colorpicker-core.css
new file mode 100644
index 0000000000..62d05f6031
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/colorpicker-core.css
@@ -0,0 +1,6 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.8.0r4
+*/
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/hue_thumb.png b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/hue_thumb.png
new file mode 100644
index 0000000000..14d5db4862
Binary files /dev/null and b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/hue_thumb.png differ
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/picker_mask.png b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/picker_mask.png
new file mode 100644
index 0000000000..f8d91932b3
Binary files /dev/null and b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/picker_mask.png differ
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/picker_thumb.png b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/picker_thumb.png
new file mode 100644
index 0000000000..78445a2fe0
Binary files /dev/null and b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/picker_thumb.png differ
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/colorpicker-skin.css b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/colorpicker-skin.css
new file mode 100644
index 0000000000..3012589fbd
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/colorpicker-skin.css
@@ -0,0 +1,105 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.8.0r4
+*/
+
+.yui-picker-panel {
+ background: #e3e3e3;
+ border-color: #888;
+}
+
+.yui-picker-panel .hd {
+ background-color:#ccc;
+ font-size:100%;
+ line-height:100%;
+ border:1px solid #e3e3e3;
+ font-weight:bold;
+ overflow:hidden;
+ padding: 6px;
+ color: #000;
+}
+
+.yui-picker-panel .bd {
+ background: #e8e8e8;
+ margin: 1px;
+ height: 200px;
+}
+
+.yui-picker-panel .ft {
+ background: #e8e8e8;
+ margin: 1px;
+ padding: 1px;
+ /*
+ text-align: center;
+ */
+}
+
+.yui-picker {
+ position: relative;
+}
+
+.yui-picker-hue-thumb { cursor:default; width:18px; height:18px;
+top: -8px;
+left: -2px;
+ z-index: 9; position:absolute; }
+.yui-picker-hue-bg {-moz-outline: none; outline:0px none;
+ position:absolute; left:200px; height:183px; width:14px;
+ background:url(hue_bg.png) no-repeat;
+ top:4px;
+}
+
+.yui-picker-bg {
+ -moz-outline: none;
+ outline:0px none;
+ position:absolute;
+ top:4px;
+ left:4px;
+ height:182px;
+ width:182px;
+ background-color:#F00;
+ background-image: url(picker_mask.png);
+}
+
+*html .yui-picker-bg {
+ background-image: none;
+ filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../../build/colorpicker/assets/picker_mask.png', sizingMethod='scale');
+}
+
+
+.yui-picker-mask { position:absolute; z-index: 1; top:0px; left:0px; }
+
+.yui-picker-thumb { cursor:default; width:11px; height:11px; z-index: 9; position:absolute;
+ top:-4px; left:-4px; }
+
+.yui-picker-swatch { position:absolute; left:240px; top:4px; height:60px;
+ width:55px; border:1px solid #888; }
+.yui-picker-websafe-swatch { position:absolute; left:304px; top:4px;
+ height:24px; width:24px; border:1px solid #888; }
+
+.yui-picker-controls { position:absolute; top: 72px; left:226px; font:1em monospace;}
+.yui-picker-controls .hd { background: transparent; border-width: 0px !important;}
+.yui-picker-controls .bd { height: 100px; border-width: 0px !important;}
+.yui-picker-controls ul {float:left;padding:0 2px 0 0;margin:0}
+.yui-picker-controls li {padding:2px;list-style:none;margin:0}
+.yui-picker-controls input {
+ font-size: 0.85em;
+ width: 2.4em;
+}
+.yui-picker-hex-controls {
+ clear: both;
+ padding: 2px;
+}
+.yui-picker-hex-controls input {
+ width: 4.6em;
+}
+
+.yui-picker-controls a {
+ font: 1em arial,helvetica,clean,sans-serif;
+ display:block;
+ *display:inline-block; /* IE */
+ padding: 0;
+ color: #000;
+
+}
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/colorpicker.css b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/colorpicker.css
new file mode 100644
index 0000000000..aaec21a06d
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/colorpicker.css
@@ -0,0 +1,7 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.8.0r4
+*/
+.yui-picker-panel{background:#e3e3e3;border-color:#888;}.yui-picker-panel .hd{background-color:#ccc;font-size:100%;line-height:100%;border:1px solid #e3e3e3;font-weight:bold;overflow:hidden;padding:6px;color:#000;}.yui-picker-panel .bd{background:#e8e8e8;margin:1px;height:200px;}.yui-picker-panel .ft{background:#e8e8e8;margin:1px;padding:1px;}.yui-picker{position:relative;}.yui-picker-hue-thumb{cursor:default;width:18px;height:18px;top:-8px;left:-2px;z-index:9;position:absolute;}.yui-picker-hue-bg{-moz-outline:none;outline:0 none;position:absolute;left:200px;height:183px;width:14px;background:url(hue_bg.png) no-repeat;top:4px;}.yui-picker-bg{-moz-outline:none;outline:0 none;position:absolute;top:4px;left:4px;height:182px;width:182px;background-color:#F00;background-image:url(picker_mask.png);}*html .yui-picker-bg{background-image:none;filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../../build/colorpicker/assets/picker_mask.png',sizingMethod='scale');}.yui-picker-mask{position:absolute;z-index:1;top:0;left:0;}.yui-picker-thumb{cursor:default;width:11px;height:11px;z-index:9;position:absolute;top:-4px;left:-4px;}.yui-picker-swatch{position:absolute;left:240px;top:4px;height:60px;width:55px;border:1px solid #888;}.yui-picker-websafe-swatch{position:absolute;left:304px;top:4px;height:24px;width:24px;border:1px solid #888;}.yui-picker-controls{position:absolute;top:72px;left:226px;font:1em monospace;}.yui-picker-controls .hd{background:transparent;border-width:0!important;}.yui-picker-controls .bd{height:100px;border-width:0!important;}.yui-picker-controls ul{float:left;padding:0 2px 0 0;margin:0;}.yui-picker-controls li{padding:2px;list-style:none;margin:0;}.yui-picker-controls input{font-size:.85em;width:2.4em;}.yui-picker-hex-controls{clear:both;padding:2px;}.yui-picker-hex-controls input{width:4.6em;}.yui-picker-controls a{font:1em arial,helvetica,clean,sans-serif;display:block;*display:inline-block;padding:0;color:#000;}
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/hue_bg.png b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/hue_bg.png
new file mode 100644
index 0000000000..d9bcdeb5c4
Binary files /dev/null and b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/hue_bg.png differ
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/picker_mask.png b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/picker_mask.png
new file mode 100644
index 0000000000..f8d91932b3
Binary files /dev/null and b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/assets/skins/sam/picker_mask.png differ
diff --git a/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/colorpicker-debug.js b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/colorpicker-debug.js
new file mode 100644
index 0000000000..9920c0ecf2
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/lib/yui/colorpicker/colorpicker-debug.js
@@ -0,0 +1,1783 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.8.0r4
+*/
+/**
+ * Provides color conversion and validation utils
+ * @class YAHOO.util.Color
+ * @namespace YAHOO.util
+ */
+YAHOO.util.Color = function() {
+
+ var ZERO = "0",
+ isArray = YAHOO.lang.isArray,
+ isNumber = YAHOO.lang.isNumber;
+
+ return {
+
+ /**
+ * Converts 0-1 to 0-255
+ * @method real2dec
+ * @param n {float} the number to convert
+ * @return {int} a number 0-255
+ */
+ real2dec: function(n) {
+ return Math.min(255, Math.round(n*256));
+ },
+
+ /**
+ * Converts HSV (h[0-360], s[0-1]), v[0-1] to RGB [255,255,255]
+ * @method hsv2rgb
+ * @param h {int|[int, float, float]} the hue, or an
+ * array containing all three parameters
+ * @param s {float} the saturation
+ * @param v {float} the value/brightness
+ * @return {[int, int, int]} the red, green, blue values in
+ * decimal.
+ */
+ hsv2rgb: function(h, s, v) {
+
+ if (isArray(h)) {
+ return this.hsv2rgb.call(this, h[0], h[1], h[2]);
+ }
+
+ var r, g, b,
+ i = Math.floor((h/60)%6),
+ f = (h/60)-i,
+ p = v*(1-s),
+ q = v*(1-f*s),
+ t = v*(1-(1-f)*s),
+ fn;
+
+ switch (i) {
+ case 0: r=v; g=t; b=p; break;
+ case 1: r=q; g=v; b=p; break;
+ case 2: r=p; g=v; b=t; break;
+ case 3: r=p; g=q; b=v; break;
+ case 4: r=t; g=p; b=v; break;
+ case 5: r=v; g=p; b=q; break;
+ }
+
+ fn=this.real2dec;
+
+ return [fn(r), fn(g), fn(b)];
+ },
+
+ /**
+ * Converts to RGB [255,255,255] to HSV (h[0-360], s[0-1]), v[0-1]
+ * @method rgb2hsv
+ * @param r {int|[int, int, int]} the red value, or an
+ * array containing all three parameters
+ * @param g {int} the green value
+ * @param b {int} the blue value
+ * @return {[int, float, float]} the value converted to hsv
+ */
+ rgb2hsv: function(r, g, b) {
+
+ if (isArray(r)) {
+ return this.rgb2hsv.apply(this, r);
+ }
+
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ var h,s,
+ min = Math.min(Math.min(r,g),b),
+ max = Math.max(Math.max(r,g),b),
+ delta = max-min,
+ hsv;
+
+ switch (max) {
+ case min: h=0; break;
+ case r: h=60*(g-b)/delta;
+ if (g FFFFFF
+ * @method rgb2hex
+ * @param r {int|[int, int, int]} the red value, or an
+ * array containing all three parameters
+ * @param g {int} the green value
+ * @param b {int} the blue value
+ * @return {string} the hex string
+ */
+ rgb2hex: function(r, g, b) {
+ if (isArray(r)) {
+ return this.rgb2hex.apply(this, r);
+ }
+
+ var f=this.dec2hex;
+ return f(r) + f(g) + f(b);
+ },
+
+ /**
+ * Converts an int 0...255 to hex pair 00...FF
+ * @method dec2hex
+ * @param n {int} the number to convert
+ * @return {string} the hex equivalent
+ */
+ dec2hex: function(n) {
+ n = parseInt(n,10)|0;
+ n = (n > 255 || n < 0) ? 0 : n;
+
+ return (ZERO+n.toString(16)).slice(-2).toUpperCase();
+ },
+
+ /**
+ * Converts a hex pair 00...FF to an int 0...255
+ * @method hex2dec
+ * @param str {string} the hex pair to convert
+ * @return {int} the decimal
+ */
+ hex2dec: function(str) {
+ return parseInt(str,16);
+ },
+
+ /**
+ * Converts a hex string to rgb
+ * @method hex2rgb
+ * @param str {string} the hex string
+ * @return {[int, int, int]} an array containing the rgb values
+ */
+ hex2rgb: function(s) {
+ var f = this.hex2dec;
+ return [f(s.slice(0, 2)), f(s.slice(2, 4)), f(s.slice(4, 6))];
+ },
+
+ /**
+ * Returns the closest websafe color to the supplied rgb value.
+ * @method websafe
+ * @param r {int|[int, int, int]} the red value, or an
+ * array containing all three parameters
+ * @param g {int} the green value
+ * @param b {int} the blue value
+ * @return {[int, int, int]} an array containing the closes
+ * websafe rgb colors.
+ */
+ websafe: function(r, g, b) {
+
+ if (isArray(r)) {
+ return this.websafe.apply(this, r);
+ }
+
+ // returns the closest match [0, 51, 102, 153, 204, 255]
+ var f = function(v) {
+ if (isNumber(v)) {
+ v = Math.min(Math.max(0, v), 255);
+ var i, next;
+ for (i=0; i<256; i=i+51) {
+ next = i+51;
+ if (v >= i && v <= next) {
+ return (v-i > 25) ? next : i;
+ }
+ }
+ YAHOO.log("Error calculating the websafe value for " + v, "warn");
+ }
+
+ return v;
+ };
+
+ return [f(r), f(g), f(b)];
+ }
+ };
+}();
+
+
+/**
+ * The colorpicker module provides a widget for selecting colors
+ * @module colorpicker
+ * @requires yahoo, dom, event, element, slider
+ */
+(function() {
+
+ var _pickercount = 0,
+ util = YAHOO.util,
+ lang = YAHOO.lang,
+ Slider = YAHOO.widget.Slider,
+ Color = util.Color,
+ Dom = util.Dom,
+ Event = util.Event,
+ sub = lang.substitute,
+
+ b = "yui-picker";
+
+
+ /**
+ * A widget to select colors
+ * @namespace YAHOO.widget
+ * @class YAHOO.widget.ColorPicker
+ * @extends YAHOO.util.Element
+ * @constructor
+ * @param {HTMLElement | String | Object} el(optional) The html
+ * element that represents the colorpicker, or the attribute object to use.
+ * An element will be created if none provided.
+ * @param {Object} attr (optional) A key map of the colorpicker's
+ * initial attributes. Ignored if first arg is attributes object.
+ */
+ function ColorPicker(el, attr) {
+ _pickercount = _pickercount + 1;
+ this.logger = new YAHOO.widget.LogWriter("ColorPicker");
+ attr = attr || {};
+ if (arguments.length === 1 && !YAHOO.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
+ this.logger.log("creating host element");
+ el = this._createHostElement(attr);
+ }
+
+ ColorPicker.superclass.constructor.call(this, el, attr);
+
+ this.initPicker();
+ }
+
+ YAHOO.extend(ColorPicker, YAHOO.util.Element, {
+
+ /**
+ * The element ids used by this control
+ * @property ID
+ * @final
+ */
+ ID : {
+
+ /**
+ * The id for the "red" form field
+ * @property ID.R
+ * @type String
+ * @final
+ * @default yui-picker-r
+ */
+ R: b + "-r",
+
+ /**
+ * The id for the "red" hex pair output
+ * @property ID.R_HEX
+ * @type String
+ * @final
+ * @default yui-picker-rhex
+ */
+ R_HEX: b + "-rhex",
+
+ /**
+ * The id for the "green" form field
+ * @property ID.G
+ * @type String
+ * @final
+ * @default yui-picker-g
+ */
+ G: b + "-g",
+
+ /**
+ * The id for the "green" hex pair output
+ * @property ID.G_HEX
+ * @type String
+ * @final
+ * @default yui-picker-ghex
+ */
+ G_HEX: b + "-ghex",
+
+
+ /**
+ * The id for the "blue" form field
+ * @property ID.B
+ * @type String
+ * @final
+ * @default yui-picker-b
+ */
+ B: b + "-b",
+
+ /**
+ * The id for the "blue" hex pair output
+ * @property ID.B_HEX
+ * @type String
+ * @final
+ * @default yui-picker-bhex
+ */
+ B_HEX: b + "-bhex",
+
+ /**
+ * The id for the "hue" form field
+ * @property ID.H
+ * @type String
+ * @final
+ * @default yui-picker-h
+ */
+ H: b + "-h",
+
+ /**
+ * The id for the "saturation" form field
+ * @property ID.S
+ * @type String
+ * @final
+ * @default yui-picker-s
+ */
+ S: b + "-s",
+
+ /**
+ * The id for the "value" form field
+ * @property ID.V
+ * @type String
+ * @final
+ * @default yui-picker-v
+ */
+ V: b + "-v",
+
+ /**
+ * The id for the picker region slider
+ * @property ID.PICKER_BG
+ * @type String
+ * @final
+ * @default yui-picker-bg
+ */
+ PICKER_BG: b + "-bg",
+
+ /**
+ * The id for the picker region thumb
+ * @property ID.PICKER_THUMB
+ * @type String
+ * @final
+ * @default yui-picker-thumb
+ */
+ PICKER_THUMB: b + "-thumb",
+
+ /**
+ * The id for the hue slider
+ * @property ID.HUE_BG
+ * @type String
+ * @final
+ * @default yui-picker-hue-bg
+ */
+ HUE_BG: b + "-hue-bg",
+
+ /**
+ * The id for the hue thumb
+ * @property ID.HUE_THUMB
+ * @type String
+ * @final
+ * @default yui-picker-hue-thumb
+ */
+ HUE_THUMB: b + "-hue-thumb",
+
+ /**
+ * The id for the hex value form field
+ * @property ID.HEX
+ * @type String
+ * @final
+ * @default yui-picker-hex
+ */
+ HEX: b + "-hex",
+
+ /**
+ * The id for the color swatch
+ * @property ID.SWATCH
+ * @type String
+ * @final
+ * @default yui-picker-swatch
+ */
+ SWATCH: b + "-swatch",
+
+ /**
+ * The id for the websafe color swatch
+ * @property ID.WEBSAFE_SWATCH
+ * @type String
+ * @final
+ * @default yui-picker-websafe-swatch
+ */
+ WEBSAFE_SWATCH: b + "-websafe-swatch",
+
+ /**
+ * The id for the control details
+ * @property ID.CONTROLS
+ * @final
+ * @default yui-picker-controls
+ */
+ CONTROLS: b + "-controls",
+
+ /**
+ * The id for the rgb controls
+ * @property ID.RGB_CONTROLS
+ * @final
+ * @default yui-picker-rgb-controls
+ */
+ RGB_CONTROLS: b + "-rgb-controls",
+
+ /**
+ * The id for the hsv controls
+ * @property ID.HSV_CONTROLS
+ * @final
+ * @default yui-picker-hsv-controls
+ */
+ HSV_CONTROLS: b + "-hsv-controls",
+
+ /**
+ * The id for the hsv controls
+ * @property ID.HEX_CONTROLS
+ * @final
+ * @default yui-picker-hex-controls
+ */
+ HEX_CONTROLS: b + "-hex-controls",
+
+ /**
+ * The id for the hex summary
+ * @property ID.HEX_SUMMARY
+ * @final
+ * @default yui-picker-hex-summary
+ */
+ HEX_SUMMARY: b + "-hex-summary",
+
+ /**
+ * The id for the controls section header
+ * @property ID.CONTROLS_LABEL
+ * @final
+ * @default yui-picker-controls-label
+ */
+ CONTROLS_LABEL: b + "-controls-label"
+ },
+
+ /**
+ * Constants for any script-generated messages. The values here
+ * are the default messages. They can be updated by providing
+ * the complete list to the constructor for the "txt" attribute.
+ * @property TXT
+ * @final
+ */
+ TXT : {
+ ILLEGAL_HEX: "Illegal hex value entered",
+ SHOW_CONTROLS: "Show color details",
+ HIDE_CONTROLS: "Hide color details",
+ CURRENT_COLOR: "Currently selected color: {rgb}",
+ CLOSEST_WEBSAFE: "Closest websafe color: {rgb}. Click to select.",
+ R: "R",
+ G: "G",
+ B: "B",
+ H: "H",
+ S: "S",
+ V: "V",
+ HEX: "#",
+ DEG: "\u00B0",
+ PERCENT: "%"
+ },
+
+ /**
+ * Constants for the default image locations for img tags that are
+ * generated by the control. They can be modified by passing the
+ * complete list to the contructor for the "images" attribute
+ * @property IMAGE
+ * @final
+ */
+ IMAGE : {
+ PICKER_THUMB: "../../build/colorpicker/assets/picker_thumb.png",
+ HUE_THUMB: "../../build/colorpicker/assets/hue_thumb.png"
+ },
+
+ /**
+ * Constants for the control's default default values
+ * @property DEFAULT
+ * @final
+ */
+ DEFAULT : {
+ PICKER_SIZE: 180
+ },
+
+ /**
+ * Constants for the control's configuration attributes
+ * @property OPT
+ * @final
+ */
+ OPT : {
+ HUE : "hue",
+ SATURATION : "saturation",
+ VALUE : "value",
+ RED : "red",
+ GREEN : "green",
+ BLUE : "blue",
+ HSV : "hsv",
+ RGB : "rgb",
+ WEBSAFE : "websafe",
+ HEX : "hex",
+ PICKER_SIZE : "pickersize",
+ SHOW_CONTROLS : "showcontrols",
+ SHOW_RGB_CONTROLS : "showrgbcontrols",
+ SHOW_HSV_CONTROLS : "showhsvcontrols",
+ SHOW_HEX_CONTROLS : "showhexcontrols",
+ SHOW_HEX_SUMMARY : "showhexsummary",
+ SHOW_WEBSAFE : "showwebsafe",
+ CONTAINER : "container",
+ IDS : "ids",
+ ELEMENTS : "elements",
+ TXT : "txt",
+ IMAGES : "images",
+ ANIMATE : "animate"
+ },
+
+ /**
+ * Flag to allow individual UI updates to forego animation if available.
+ * True during construction for initial thumb placement. Set to false
+ * after that.
+ *
+ * @property skipAnim
+ * @type Boolean
+ * @default true
+ */
+ skipAnim : true,
+
+ /**
+ * Creates the host element if it doesn't exist
+ * @method _createHostElement
+ * @protected
+ */
+ _createHostElement : function () {
+ var el = document.createElement('div');
+
+ if (this.CSS.BASE) {
+ el.className = this.CSS.BASE;
+ }
+
+ return el;
+ },
+
+ /**
+ * Moves the hue slider into the position dictated by the current state
+ * of the control
+ * @method _updateHueSlider
+ * @protected
+ */
+ _updateHueSlider : function() {
+ var size = this.get(this.OPT.PICKER_SIZE),
+ h = this.get(this.OPT.HUE);
+
+ h = size - Math.round(h / 360 * size);
+
+ // 0 is at the top and bottom of the hue slider. Always go to
+ // the top so we don't end up sending the thumb to the bottom
+ // when the value didn't actually change (e.g., a conversion
+ // produced 360 instead of 0 and the value was already 0).
+ if (h === size) {
+ h = 0;
+ }
+ this.logger.log("Hue slider is being set to " + h);
+
+ this.hueSlider.setValue(h, this.skipAnim);
+ },
+
+ /**
+ * Moves the picker slider into the position dictated by the current state
+ * of the control
+ * @method _updatePickerSlider
+ * @protected
+ */
+ _updatePickerSlider : function() {
+ var size = this.get(this.OPT.PICKER_SIZE),
+ s = this.get(this.OPT.SATURATION),
+ v = this.get(this.OPT.VALUE);
+
+ s = Math.round(s * size / 100);
+ v = Math.round(size - (v * size / 100));
+
+ this.logger.log("Setting picker slider to " + [s, v]);
+
+ this.pickerSlider.setRegionValue(s, v, this.skipAnim);
+ },
+
+ /**
+ * Moves the sliders into the position dictated by the current state
+ * of the control
+ * @method _updateSliders
+ * @protected
+ */
+ _updateSliders : function() {
+ this._updateHueSlider();
+ this._updatePickerSlider();
+ },
+
+ /**
+ * Sets the control to the specified rgb value and
+ * moves the sliders to the proper positions
+ * @method setValue
+ * @param rgb {[int, int, int]} the rgb value
+ * @param silent {boolean} whether or not to fire the change event
+ */
+ setValue : function(rgb, silent) {
+ silent = (silent) || false;
+ this.set(this.OPT.RGB, rgb, silent);
+ this._updateSliders();
+ },
+
+ /**
+ * The hue slider
+ * @property hueSlider
+ * @type YAHOO.widget.Slider
+ */
+ hueSlider : null,
+
+ /**
+ * The picker region
+ * @property pickerSlider
+ * @type YAHOO.widget.Slider
+ */
+ pickerSlider : null,
+
+ /**
+ * Translates the slider value into hue, int[0,359]
+ * @method _getH
+ * @protected
+ * @return {int} the hue from 0 to 359
+ */
+ _getH : function() {
+ var size = this.get(this.OPT.PICKER_SIZE),
+ h = (size - this.hueSlider.getValue()) / size;
+ h = Math.round(h*360);
+ return (h === 360) ? 0 : h;
+ },
+
+ /**
+ * Translates the slider value into saturation, int[0,1], left to right
+ * @method _getS
+ * @protected
+ * @return {int} the saturation from 0 to 1
+ */
+ _getS : function() {
+ return this.pickerSlider.getXValue() / this.get(this.OPT.PICKER_SIZE);
+ },
+
+ /**
+ * Translates the slider value into value/brightness, int[0,1], top
+ * to bottom
+ * @method _getV
+ * @protected
+ * @return {int} the value from 0 to 1
+ */
+ _getV : function() {
+ var size = this.get(this.OPT.PICKER_SIZE);
+ return (size - this.pickerSlider.getYValue()) / size;
+ },
+
+ /**
+ * Updates the background of the swatch with the current rbg value.
+ * Also updates the websafe swatch to the closest websafe color
+ * @method _updateSwatch
+ * @protected
+ */
+ _updateSwatch : function() {
+ var rgb = this.get(this.OPT.RGB),
+ websafe = this.get(this.OPT.WEBSAFE),
+ el = this.getElement(this.ID.SWATCH),
+ color = rgb.join(","),
+ txt = this.get(this.OPT.TXT);
+
+ Dom.setStyle(el, "background-color", "rgb(" + color + ")");
+ el.title = sub(txt.CURRENT_COLOR, {
+ "rgb": "#" + this.get(this.OPT.HEX)
+ });
+
+
+ el = this.getElement(this.ID.WEBSAFE_SWATCH);
+ color = websafe.join(",");
+
+ Dom.setStyle(el, "background-color", "rgb(" + color + ")");
+ el.title = sub(txt.CLOSEST_WEBSAFE, {
+ "rgb": "#" + Color.rgb2hex(websafe)
+ });
+
+ },
+
+ /**
+ * Reads the sliders and converts the values to RGB, updating the
+ * internal state for all the individual form fields
+ * @method _getValuesFromSliders
+ * @protected
+ */
+ _getValuesFromSliders : function() {
+ this.logger.log("hsv " + [this._getH(),this._getS(),this._getV()]);
+ this.set(this.OPT.RGB, Color.hsv2rgb(this._getH(), this._getS(), this._getV()));
+ },
+
+ /**
+ * Updates the form field controls with the state data contained
+ * in the control.
+ * @method _updateFormFields
+ * @protected
+ */
+ _updateFormFields : function() {
+ this.getElement(this.ID.H).value = this.get(this.OPT.HUE);
+ this.getElement(this.ID.S).value = this.get(this.OPT.SATURATION);
+ this.getElement(this.ID.V).value = this.get(this.OPT.VALUE);
+ this.getElement(this.ID.R).value = this.get(this.OPT.RED);
+ this.getElement(this.ID.R_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.RED));
+ this.getElement(this.ID.G).value = this.get(this.OPT.GREEN);
+ this.getElement(this.ID.G_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.GREEN));
+ this.getElement(this.ID.B).value = this.get(this.OPT.BLUE);
+ this.getElement(this.ID.B_HEX).innerHTML = Color.dec2hex(this.get(this.OPT.BLUE));
+ this.getElement(this.ID.HEX).value = this.get(this.OPT.HEX);
+ },
+
+ /**
+ * Event handler for the hue slider.
+ * @method _onHueSliderChange
+ * @param newOffset {int} pixels from the start position
+ * @protected
+ */
+ _onHueSliderChange : function(newOffset) {
+ this.logger.log("hue update: " + newOffset , "warn");
+
+ var h = this._getH(),
+ rgb = Color.hsv2rgb(h, 1, 1),
+ styleDef = "rgb(" + rgb.join(",") + ")";
+
+ this.set(this.OPT.HUE, h, true);
+
+ // set picker background to the hue
+ Dom.setStyle(this.getElement(this.ID.PICKER_BG), "background-color", styleDef);
+
+ if (this.hueSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
+ this._getValuesFromSliders();
+ }
+
+ this._updateFormFields();
+ this._updateSwatch();
+ },
+
+ /**
+ * Event handler for the picker slider, which controls the
+ * saturation and value/brightness.
+ * @method _onPickerSliderChange
+ * @param newOffset {{x: int, y: int}} x/y pixels from the start position
+ * @protected
+ */
+ _onPickerSliderChange : function(newOffset) {
+ this.logger.log(sub("picker update [{x}, {y}]", newOffset));
+
+ var s=this._getS(), v=this._getV();
+ this.set(this.OPT.SATURATION, Math.round(s*100), true);
+ this.set(this.OPT.VALUE, Math.round(v*100), true);
+
+ if (this.pickerSlider.valueChangeSource !== Slider.SOURCE_SET_VALUE) {
+ this._getValuesFromSliders();
+ }
+
+ this._updateFormFields();
+ this._updateSwatch();
+ },
+
+
+ /**
+ * Key map to well-known commands for txt field input
+ * @method _getCommand
+ * @param e {Event} the keypress or keydown event
+ * @return {int} a command code
+ *