// exact version of currently used yui2 and 3 library
$CFG->yui2version = '2.8.0r4';
+$CFG->yui3version = '3.0.0';
// Load up standard libraries
require_once($CFG->libdir .'/textlib.class.php'); // Functions to handle multibyte strings
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-base', function(Y) {
+
+/**
+* The Animation Utility provides an API for creating advanced transitions.
+* @module anim
+*/
+
+/**
+* Provides the base Anim class, for animating numeric properties.
+*
+* @module anim
+* @submodule anim-base
+*/
+
+ /**
+ * A class for constructing animation instances.
+ * @class Anim
+ * @for Anim
+ * @constructor
+ * @extends Base
+ */
+
+ var RUNNING = 'running',
+ START_TIME = 'startTime',
+ ELAPSED_TIME = 'elapsedTime',
+ /**
+ * @for Anim
+ * @event start
+ * @description fires when an animation begins.
+ * @param {Event} ev The start event.
+ * @type Event.Custom
+ */
+ START = 'start',
+
+ /**
+ * @event tween
+ * @description fires every frame of the animation.
+ * @param {Event} ev The tween event.
+ * @type Event.Custom
+ */
+ TWEEN = 'tween',
+
+ /**
+ * @event end
+ * @description fires after the animation completes.
+ * @param {Event} ev The end event.
+ * @type Event.Custom
+ */
+ END = 'end',
+ NODE = 'node',
+ PAUSED = 'paused',
+ REVERSE = 'reverse', // TODO: cleanup
+ ITERATION_COUNT = 'iterationCount',
+
+ NUM = Number;
+
+ var _running = {},
+ _instances = {},
+ _timer;
+
+ Y.Anim = function() {
+ Y.Anim.superclass.constructor.apply(this, arguments);
+ _instances[Y.stamp(this)] = this;
+ };
+
+ Y.Anim.NAME = 'anim';
+
+ /**
+ * Regex of properties that should use the default unit.
+ *
+ * @property RE_DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
+
+ /**
+ * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
+ *
+ * @property DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.DEFAULT_UNIT = 'px';
+
+ Y.Anim.DEFAULT_EASING = function (t, b, c, d) {
+ return c * t / d + b; // linear easing
+ };
+
+ /**
+ * Bucket for custom getters and setters
+ *
+ * @property behaviors
+ * @static
+ */
+ Y.Anim.behaviors = {
+ left: {
+ get: function(anim, attr) {
+ return anim._getOffset(attr);
+ }
+ }
+ };
+
+ Y.Anim.behaviors.top = Y.Anim.behaviors.left;
+
+ /**
+ * The default setter to use when setting object properties.
+ *
+ * @property DEFAULT_SETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
+ unit = unit || '';
+ anim._node.setStyle(att, fn(elapsed, NUM(from), NUM(to) - NUM(from), duration) + unit);
+ };
+
+ /**
+ * The default getter to use when getting object properties.
+ *
+ * @property DEFAULT_GETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_GETTER = function(anim, prop) {
+ return anim._node.getComputedStyle(prop);
+ };
+
+ Y.Anim.ATTRS = {
+ /**
+ * The object to be animated.
+ * @attribute node
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ node = Y.get(node);
+ this._node = node;
+ if (!node) {
+ Y.log(node + ' is not a valid node', 'warn', 'Anim');
+ }
+ return node;
+ }
+ },
+
+ /**
+ * The length of the animation. Defaults to "1" (second).
+ * @attribute duration
+ * @type NUM
+ */
+ duration: {
+ value: 1
+ },
+
+ /**
+ * The method that will provide values to the attribute(s) during the animation.
+ * Defaults to "Easing.easeNone".
+ * @attribute easing
+ * @type Function
+ */
+ easing: {
+ value: Y.Anim.DEFAULT_EASING,
+
+ setter: function(val) {
+ if (typeof val === 'string' && Y.Easing) {
+ return Y.Easing[val];
+ }
+ }
+ },
+
+ /**
+ * The starting values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * If a function is used, the return value becomes the from value.
+ * If no from value is specified, the DEFAULT_GETTER will be used.
+ * @attribute from
+ * @type Object
+ */
+ from: {},
+
+ /**
+ * The ending values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * @attribute to
+ * @type Object
+ */
+ to: {},
+
+ /**
+ * Date stamp for the first frame of the animation.
+ * @attribute startTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ startTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Current time the animation has been running.
+ * @attribute elapsedTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ elapsedTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Whether or not the animation is currently running.
+ * @attribute running
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ running: {
+ getter: function() {
+ return !!_running[Y.stamp(this)];
+ },
+ value: false,
+ readOnly: true
+ },
+
+ /**
+ * The number of times the animation should run
+ * @attribute iterations
+ * @type Int
+ * @default 1
+ */
+ iterations: {
+ value: 1
+ },
+
+ /**
+ * The number of iterations that have occurred.
+ * Resets when an animation ends (reaches iteration count or stop() called).
+ * @attribute iterationCount
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ iterationCount: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * How iterations of the animation should behave.
+ * Possible values are "normal" and "alternate".
+ * Normal will repeat the animation, alternate will reverse on every other pass.
+ *
+ * @attribute direction
+ * @type String
+ * @default "normal"
+ */
+ direction: {
+ value: 'normal' // | alternate (fwd on odd, rev on even per spec)
+ },
+
+ /**
+ * Whether or not the animation is currently paused.
+ * @attribute paused
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ paused: {
+ readOnly: true,
+ value: false
+ },
+
+ /**
+ * If true, animation begins from last frame
+ * @attribute reverse
+ * @type Boolean
+ * @default false
+ */
+ reverse: {
+ value: false
+ }
+
+
+ };
+
+ /**
+ * Runs all animation instances.
+ * @method run
+ * @static
+ */
+ Y.Anim.run = function() {
+ for (var i in _instances) {
+ if (_instances[i].run) {
+ _instances[i].run();
+ }
+ }
+ };
+
+ /**
+ * Pauses all animation instances.
+ * @method pause
+ * @static
+ */
+ Y.Anim.pause = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].pause) {
+ _running[i].pause();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ /**
+ * Stops all animation instances.
+ * @method stop
+ * @static
+ */
+ Y.Anim.stop = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].stop) {
+ _running[i].stop();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ Y.Anim._startTimer = function() {
+ if (!_timer) {
+ _timer = setInterval(Y.Anim._runFrame, 1);
+ }
+ };
+
+ Y.Anim._stopTimer = function() {
+ clearInterval(_timer);
+ _timer = 0;
+ };
+
+ /**
+ * Called per Interval to handle each animation frame.
+ * @method _runFrame
+ * @private
+ * @static
+ */
+ Y.Anim._runFrame = function() {
+ var done = true;
+ for (var anim in _running) {
+ if (_running[anim]._runFrame) {
+ done = false;
+ _running[anim]._runFrame();
+ }
+ }
+
+ if (done) {
+ Y.Anim._stopTimer();
+ }
+ };
+
+ Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
+
+ var proto = {
+ /**
+ * Starts or resumes an animation.
+ * percent start time marker.
+ * @method run
+ * @chainable
+ */
+ run: function() {
+ if (!this.get(RUNNING)) {
+ this._start();
+ } else if (this.get(PAUSED)) {
+ this._resume();
+ }
+ return this;
+ },
+
+ /**
+ * Pauses the animation and
+ * freezes it in its current state and time.
+ * Calling run() will continue where it left off.
+ * @method pause
+ * @chainable
+ */
+ pause: function() {
+ if (this.get(RUNNING)) {
+ this._pause();
+ }
+ return this;
+ },
+
+ /**
+ * Stops the animation and resets its time.
+ * @method stop
+ * @chainable
+ */
+ stop: function(finish) {
+ if (this.get(RUNNING) || this.get(PAUSED)) {
+ this._end(finish);
+ }
+ return this;
+ },
+
+ _added: false,
+
+ _start: function() {
+ this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
+ this._actualFrames = 0;
+ if (!this.get(PAUSED)) {
+ this._initAnimAttr();
+ }
+ _running[Y.stamp(this)] = this;
+ Y.Anim._startTimer();
+
+ this.fire(START);
+ },
+
+ _pause: function() {
+ this._set(START_TIME, null);
+ this._set(PAUSED, true);
+ delete _running[Y.stamp(this)];
+
+ /**
+ * @event pause
+ * @description fires when an animation is paused.
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('pause');
+ },
+
+ _resume: function() {
+ this._set(PAUSED, false);
+ _running[Y.stamp(this)] = this;
+
+ /**
+ * @event resume
+ * @description fires when an animation is resumed (run from pause).
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('resume');
+ },
+
+ _end: function(finish) {
+ this._set(START_TIME, null);
+ this._set(ELAPSED_TIME, 0);
+ this._set(PAUSED, false);
+
+ delete _running[Y.stamp(this)];
+ this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
+ },
+
+ _runFrame: function() {
+ var attr = this._runtimeAttr,
+ customAttr = Y.Anim.behaviors,
+ easing = attr.easing,
+ d = attr.duration,
+ t = new Date() - this.get(START_TIME),
+ reversed = this.get(REVERSE),
+ done = (t >= d),
+ lastFrame = d,
+ attribute,
+ setter;
+
+ if (reversed) {
+ t = d - t;
+ done = (t <= 0);
+ lastFrame = 0;
+ }
+
+ for (var i in attr) {
+ if (attr[i].to) {
+ attribute = attr[i];
+ setter = (i in customAttr && 'set' in customAttr[i]) ?
+ customAttr[i].set : Y.Anim.DEFAULT_SETTER;
+
+ if (!done) {
+ setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit);
+ } else { // ensure final frame value is set
+ // TODO: handle keyframes
+ setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit);
+ }
+ }
+ }
+
+ this._actualFrames += 1;
+ this._set(ELAPSED_TIME, t);
+
+ this.fire(TWEEN);
+ if (done) {
+ this._lastFrame();
+ }
+ },
+
+ _lastFrame: function() {
+ var iter = this.get('iterations'),
+ iterCount = this.get(ITERATION_COUNT);
+
+ iterCount += 1;
+ if (iter === 'infinite' || iterCount < iter) {
+ if (this.get('direction') === 'alternate') {
+ this.set(REVERSE, !this.get(REVERSE)); // flip it
+ }
+ /**
+ * @event iteration
+ * @description fires when an animation begins an iteration.
+ * @param {Event} ev The iteration event.
+ * @type Event.Custom
+ */
+ this.fire('iteration');
+ } else {
+ iterCount = 0;
+ this._end();
+ }
+
+ this._set(START_TIME, new Date());
+ this._set(ITERATION_COUNT, iterCount);
+ },
+
+ _initAnimAttr: function() {
+ var from = this.get('from') || {},
+ to = this.get('to') || {},
+ dur = this.get('duration') * 1000,
+ node = this.get(NODE),
+ easing = this.get('easing') || {},
+ attr = {},
+ customAttr = Y.Anim.behaviors,
+ unit, begin, end;
+
+ Y.each(to, function(val, name) {
+ if (typeof val === 'function') {
+ val = val.call(this, node);
+ }
+
+ begin = from[name];
+ if (begin === undefined) {
+ begin = (name in customAttr && 'get' in customAttr[name]) ?
+ customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
+ } else if (typeof begin === 'function') {
+ begin = begin.call(this, node);
+ }
+
+ var mFrom = Y.Anim.RE_UNITS.exec(begin);
+ var mTo = Y.Anim.RE_UNITS.exec(val);
+
+ begin = mFrom ? mFrom[1] : begin;
+ end = mTo ? mTo[1] : val;
+ unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+
+ if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
+ unit = Y.Anim.DEFAULT_UNIT;
+ }
+
+ if (!begin || !end) {
+ Y.error('invalid "from" or "to" for "' + name + '"', 'Anim');
+ return;
+ }
+
+ attr[name] = {
+ from: begin,
+ to: end,
+ unit: unit
+ };
+
+ attr.duration = dur;
+ attr.easing = easing;
+
+ }, this);
+
+ this._runtimeAttr = attr;
+ },
+
+
+ // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
+ _getOffset: function(attr) {
+ var node = this._node,
+ val = node.getComputedStyle(attr),
+ get = (attr === 'left') ? 'getX': 'getY',
+ set = (attr === 'left') ? 'setX': 'setY';
+
+ if (val === 'auto') {
+ var position = node.getStyle('position');
+ if (position === 'absolute' || position === 'fixed') {
+ val = node[get]();
+ node[set](val);
+ } else {
+ val = 0;
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.extend(Y.Anim, Y.Base, proto);
+
+
+}, '3.0.0' ,{requires:['base-base', 'node-style']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-base",function(B){var C="running",N="startTime",L="elapsedTime",J="start",I="tween",M="end",D="node",K="paused",P="reverse",H="iterationCount",A=Number;var F={},O={},E;B.Anim=function(){B.Anim.superclass.constructor.apply(this,arguments);O[B.stamp(this)]=this;};B.Anim.NAME="anim";B.Anim.RE_DEFAULT_UNIT=/^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;B.Anim.DEFAULT_UNIT="px";B.Anim.DEFAULT_EASING=function(R,Q,T,S){return T*R/S+Q;};B.Anim.behaviors={left:{get:function(R,Q){return R._getOffset(Q);}}};B.Anim.behaviors.top=B.Anim.behaviors.left;B.Anim.DEFAULT_SETTER=function(U,R,X,W,Q,V,S,T){T=T||"";U._node.setStyle(R,S(Q,A(X),A(W)-A(X),V)+T);};B.Anim.DEFAULT_GETTER=function(Q,R){return Q._node.getComputedStyle(R);};B.Anim.ATTRS={node:{setter:function(Q){Q=B.get(Q);this._node=Q;if(!Q){}return Q;}},duration:{value:1},easing:{value:B.Anim.DEFAULT_EASING,setter:function(Q){if(typeof Q==="string"&&B.Easing){return B.Easing[Q];}}},from:{},to:{},startTime:{value:0,readOnly:true},elapsedTime:{value:0,readOnly:true},running:{getter:function(){return !!F[B.stamp(this)];},value:false,readOnly:true},iterations:{value:1},iterationCount:{value:0,readOnly:true},direction:{value:"normal"},paused:{readOnly:true,value:false},reverse:{value:false}};B.Anim.run=function(){for(var Q in O){if(O[Q].run){O[Q].run();}}};B.Anim.pause=function(){for(var Q in F){if(F[Q].pause){F[Q].pause();}}B.Anim._stopTimer();};B.Anim.stop=function(){for(var Q in F){if(F[Q].stop){F[Q].stop();}}B.Anim._stopTimer();};B.Anim._startTimer=function(){if(!E){E=setInterval(B.Anim._runFrame,1);}};B.Anim._stopTimer=function(){clearInterval(E);E=0;};B.Anim._runFrame=function(){var Q=true;for(var R in F){if(F[R]._runFrame){Q=false;F[R]._runFrame();}}if(Q){B.Anim._stopTimer();}};B.Anim.RE_UNITS=/^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;var G={run:function(){if(!this.get(C)){this._start();}else{if(this.get(K)){this._resume();}}return this;},pause:function(){if(this.get(C)){this._pause();}return this;},stop:function(Q){if(this.get(C)||this.get(K)){this._end(Q);}return this;},_added:false,_start:function(){this._set(N,new Date()-this.get(L));this._actualFrames=0;if(!this.get(K)){this._initAnimAttr();}F[B.stamp(this)]=this;B.Anim._startTimer();this.fire(J);},_pause:function(){this._set(N,null);this._set(K,true);delete F[B.stamp(this)];this.fire("pause");},_resume:function(){this._set(K,false);F[B.stamp(this)]=this;this.fire("resume");},_end:function(Q){this._set(N,null);this._set(L,0);this._set(K,false);delete F[B.stamp(this)];this.fire(M,{elapsed:this.get(L)});},_runFrame:function(){var X=this._runtimeAttr,S=B.Anim.behaviors,Y=X.easing,Z=X.duration,a=new Date()-this.get(N),W=this.get(P),U=(a>=Z),Q=Z,R,T;if(W){a=Z-a;U=(a<=0);Q=0;}for(var V in X){if(X[V].to){R=X[V];T=(V in S&&"set" in S[V])?S[V].set:B.Anim.DEFAULT_SETTER;if(!U){T(this,V,R.from,R.to,a,Z,Y,R.unit);}else{T(this,V,R.from,R.to,Q,Z,Y,R.unit);}}}this._actualFrames+=1;this._set(L,a);this.fire(I);if(U){this._lastFrame();}},_lastFrame:function(){var Q=this.get("iterations"),R=this.get(H);R+=1;if(Q==="infinite"||R<Q){if(this.get("direction")==="alternate"){this.set(P,!this.get(P));}this.fire("iteration");}else{R=0;this._end();}this._set(N,new Date());this._set(H,R);},_initAnimAttr:function(){var X=this.get("from")||{},Y=this.get("to")||{},Q=this.get("duration")*1000,T=this.get(D),W=this.get("easing")||{},V={},R=B.Anim.behaviors,Z,S,U;B.each(Y,function(d,b){if(typeof d==="function"){d=d.call(this,T);}S=X[b];if(S===undefined){S=(b in R&&"get" in R[b])?R[b].get(this,b):B.Anim.DEFAULT_GETTER(this,b);}else{if(typeof S==="function"){S=S.call(this,T);}}var a=B.Anim.RE_UNITS.exec(S);var c=B.Anim.RE_UNITS.exec(d);S=a?a[1]:S;U=c?c[1]:d;Z=c?c[2]:a?a[2]:"";if(!Z&&B.Anim.RE_DEFAULT_UNIT.test(b)){Z=B.Anim.DEFAULT_UNIT;}if(!S||!U){B.error('invalid "from" or "to" for "'+b+'"',"Anim");return;}V[b]={from:S,to:U,unit:Z};V.duration=Q;V.easing=W;},this);this._runtimeAttr=V;},_getOffset:function(R){var T=this._node,U=T.getComputedStyle(R),S=(R==="left")?"getX":"getY",V=(R==="left")?"setX":"setY";if(U==="auto"){var Q=T.getStyle("position");if(Q==="absolute"||Q==="fixed"){U=T[S]();T[V](U);}else{U=0;}}return U;}};B.extend(B.Anim,B.Base,G);},"3.0.0",{requires:["base-base","node-style"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-base', function(Y) {
+
+/**
+* The Animation Utility provides an API for creating advanced transitions.
+* @module anim
+*/
+
+/**
+* Provides the base Anim class, for animating numeric properties.
+*
+* @module anim
+* @submodule anim-base
+*/
+
+ /**
+ * A class for constructing animation instances.
+ * @class Anim
+ * @for Anim
+ * @constructor
+ * @extends Base
+ */
+
+ var RUNNING = 'running',
+ START_TIME = 'startTime',
+ ELAPSED_TIME = 'elapsedTime',
+ /**
+ * @for Anim
+ * @event start
+ * @description fires when an animation begins.
+ * @param {Event} ev The start event.
+ * @type Event.Custom
+ */
+ START = 'start',
+
+ /**
+ * @event tween
+ * @description fires every frame of the animation.
+ * @param {Event} ev The tween event.
+ * @type Event.Custom
+ */
+ TWEEN = 'tween',
+
+ /**
+ * @event end
+ * @description fires after the animation completes.
+ * @param {Event} ev The end event.
+ * @type Event.Custom
+ */
+ END = 'end',
+ NODE = 'node',
+ PAUSED = 'paused',
+ REVERSE = 'reverse', // TODO: cleanup
+ ITERATION_COUNT = 'iterationCount',
+
+ NUM = Number;
+
+ var _running = {},
+ _instances = {},
+ _timer;
+
+ Y.Anim = function() {
+ Y.Anim.superclass.constructor.apply(this, arguments);
+ _instances[Y.stamp(this)] = this;
+ };
+
+ Y.Anim.NAME = 'anim';
+
+ /**
+ * Regex of properties that should use the default unit.
+ *
+ * @property RE_DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
+
+ /**
+ * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
+ *
+ * @property DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.DEFAULT_UNIT = 'px';
+
+ Y.Anim.DEFAULT_EASING = function (t, b, c, d) {
+ return c * t / d + b; // linear easing
+ };
+
+ /**
+ * Bucket for custom getters and setters
+ *
+ * @property behaviors
+ * @static
+ */
+ Y.Anim.behaviors = {
+ left: {
+ get: function(anim, attr) {
+ return anim._getOffset(attr);
+ }
+ }
+ };
+
+ Y.Anim.behaviors.top = Y.Anim.behaviors.left;
+
+ /**
+ * The default setter to use when setting object properties.
+ *
+ * @property DEFAULT_SETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
+ unit = unit || '';
+ anim._node.setStyle(att, fn(elapsed, NUM(from), NUM(to) - NUM(from), duration) + unit);
+ };
+
+ /**
+ * The default getter to use when getting object properties.
+ *
+ * @property DEFAULT_GETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_GETTER = function(anim, prop) {
+ return anim._node.getComputedStyle(prop);
+ };
+
+ Y.Anim.ATTRS = {
+ /**
+ * The object to be animated.
+ * @attribute node
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ node = Y.get(node);
+ this._node = node;
+ if (!node) {
+ }
+ return node;
+ }
+ },
+
+ /**
+ * The length of the animation. Defaults to "1" (second).
+ * @attribute duration
+ * @type NUM
+ */
+ duration: {
+ value: 1
+ },
+
+ /**
+ * The method that will provide values to the attribute(s) during the animation.
+ * Defaults to "Easing.easeNone".
+ * @attribute easing
+ * @type Function
+ */
+ easing: {
+ value: Y.Anim.DEFAULT_EASING,
+
+ setter: function(val) {
+ if (typeof val === 'string' && Y.Easing) {
+ return Y.Easing[val];
+ }
+ }
+ },
+
+ /**
+ * The starting values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * If a function is used, the return value becomes the from value.
+ * If no from value is specified, the DEFAULT_GETTER will be used.
+ * @attribute from
+ * @type Object
+ */
+ from: {},
+
+ /**
+ * The ending values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * @attribute to
+ * @type Object
+ */
+ to: {},
+
+ /**
+ * Date stamp for the first frame of the animation.
+ * @attribute startTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ startTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Current time the animation has been running.
+ * @attribute elapsedTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ elapsedTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Whether or not the animation is currently running.
+ * @attribute running
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ running: {
+ getter: function() {
+ return !!_running[Y.stamp(this)];
+ },
+ value: false,
+ readOnly: true
+ },
+
+ /**
+ * The number of times the animation should run
+ * @attribute iterations
+ * @type Int
+ * @default 1
+ */
+ iterations: {
+ value: 1
+ },
+
+ /**
+ * The number of iterations that have occurred.
+ * Resets when an animation ends (reaches iteration count or stop() called).
+ * @attribute iterationCount
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ iterationCount: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * How iterations of the animation should behave.
+ * Possible values are "normal" and "alternate".
+ * Normal will repeat the animation, alternate will reverse on every other pass.
+ *
+ * @attribute direction
+ * @type String
+ * @default "normal"
+ */
+ direction: {
+ value: 'normal' // | alternate (fwd on odd, rev on even per spec)
+ },
+
+ /**
+ * Whether or not the animation is currently paused.
+ * @attribute paused
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ paused: {
+ readOnly: true,
+ value: false
+ },
+
+ /**
+ * If true, animation begins from last frame
+ * @attribute reverse
+ * @type Boolean
+ * @default false
+ */
+ reverse: {
+ value: false
+ }
+
+
+ };
+
+ /**
+ * Runs all animation instances.
+ * @method run
+ * @static
+ */
+ Y.Anim.run = function() {
+ for (var i in _instances) {
+ if (_instances[i].run) {
+ _instances[i].run();
+ }
+ }
+ };
+
+ /**
+ * Pauses all animation instances.
+ * @method pause
+ * @static
+ */
+ Y.Anim.pause = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].pause) {
+ _running[i].pause();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ /**
+ * Stops all animation instances.
+ * @method stop
+ * @static
+ */
+ Y.Anim.stop = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].stop) {
+ _running[i].stop();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ Y.Anim._startTimer = function() {
+ if (!_timer) {
+ _timer = setInterval(Y.Anim._runFrame, 1);
+ }
+ };
+
+ Y.Anim._stopTimer = function() {
+ clearInterval(_timer);
+ _timer = 0;
+ };
+
+ /**
+ * Called per Interval to handle each animation frame.
+ * @method _runFrame
+ * @private
+ * @static
+ */
+ Y.Anim._runFrame = function() {
+ var done = true;
+ for (var anim in _running) {
+ if (_running[anim]._runFrame) {
+ done = false;
+ _running[anim]._runFrame();
+ }
+ }
+
+ if (done) {
+ Y.Anim._stopTimer();
+ }
+ };
+
+ Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
+
+ var proto = {
+ /**
+ * Starts or resumes an animation.
+ * percent start time marker.
+ * @method run
+ * @chainable
+ */
+ run: function() {
+ if (!this.get(RUNNING)) {
+ this._start();
+ } else if (this.get(PAUSED)) {
+ this._resume();
+ }
+ return this;
+ },
+
+ /**
+ * Pauses the animation and
+ * freezes it in its current state and time.
+ * Calling run() will continue where it left off.
+ * @method pause
+ * @chainable
+ */
+ pause: function() {
+ if (this.get(RUNNING)) {
+ this._pause();
+ }
+ return this;
+ },
+
+ /**
+ * Stops the animation and resets its time.
+ * @method stop
+ * @chainable
+ */
+ stop: function(finish) {
+ if (this.get(RUNNING) || this.get(PAUSED)) {
+ this._end(finish);
+ }
+ return this;
+ },
+
+ _added: false,
+
+ _start: function() {
+ this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
+ this._actualFrames = 0;
+ if (!this.get(PAUSED)) {
+ this._initAnimAttr();
+ }
+ _running[Y.stamp(this)] = this;
+ Y.Anim._startTimer();
+
+ this.fire(START);
+ },
+
+ _pause: function() {
+ this._set(START_TIME, null);
+ this._set(PAUSED, true);
+ delete _running[Y.stamp(this)];
+
+ /**
+ * @event pause
+ * @description fires when an animation is paused.
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('pause');
+ },
+
+ _resume: function() {
+ this._set(PAUSED, false);
+ _running[Y.stamp(this)] = this;
+
+ /**
+ * @event resume
+ * @description fires when an animation is resumed (run from pause).
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('resume');
+ },
+
+ _end: function(finish) {
+ this._set(START_TIME, null);
+ this._set(ELAPSED_TIME, 0);
+ this._set(PAUSED, false);
+
+ delete _running[Y.stamp(this)];
+ this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
+ },
+
+ _runFrame: function() {
+ var attr = this._runtimeAttr,
+ customAttr = Y.Anim.behaviors,
+ easing = attr.easing,
+ d = attr.duration,
+ t = new Date() - this.get(START_TIME),
+ reversed = this.get(REVERSE),
+ done = (t >= d),
+ lastFrame = d,
+ attribute,
+ setter;
+
+ if (reversed) {
+ t = d - t;
+ done = (t <= 0);
+ lastFrame = 0;
+ }
+
+ for (var i in attr) {
+ if (attr[i].to) {
+ attribute = attr[i];
+ setter = (i in customAttr && 'set' in customAttr[i]) ?
+ customAttr[i].set : Y.Anim.DEFAULT_SETTER;
+
+ if (!done) {
+ setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit);
+ } else { // ensure final frame value is set
+ // TODO: handle keyframes
+ setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit);
+ }
+ }
+ }
+
+ this._actualFrames += 1;
+ this._set(ELAPSED_TIME, t);
+
+ this.fire(TWEEN);
+ if (done) {
+ this._lastFrame();
+ }
+ },
+
+ _lastFrame: function() {
+ var iter = this.get('iterations'),
+ iterCount = this.get(ITERATION_COUNT);
+
+ iterCount += 1;
+ if (iter === 'infinite' || iterCount < iter) {
+ if (this.get('direction') === 'alternate') {
+ this.set(REVERSE, !this.get(REVERSE)); // flip it
+ }
+ /**
+ * @event iteration
+ * @description fires when an animation begins an iteration.
+ * @param {Event} ev The iteration event.
+ * @type Event.Custom
+ */
+ this.fire('iteration');
+ } else {
+ iterCount = 0;
+ this._end();
+ }
+
+ this._set(START_TIME, new Date());
+ this._set(ITERATION_COUNT, iterCount);
+ },
+
+ _initAnimAttr: function() {
+ var from = this.get('from') || {},
+ to = this.get('to') || {},
+ dur = this.get('duration') * 1000,
+ node = this.get(NODE),
+ easing = this.get('easing') || {},
+ attr = {},
+ customAttr = Y.Anim.behaviors,
+ unit, begin, end;
+
+ Y.each(to, function(val, name) {
+ if (typeof val === 'function') {
+ val = val.call(this, node);
+ }
+
+ begin = from[name];
+ if (begin === undefined) {
+ begin = (name in customAttr && 'get' in customAttr[name]) ?
+ customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
+ } else if (typeof begin === 'function') {
+ begin = begin.call(this, node);
+ }
+
+ var mFrom = Y.Anim.RE_UNITS.exec(begin);
+ var mTo = Y.Anim.RE_UNITS.exec(val);
+
+ begin = mFrom ? mFrom[1] : begin;
+ end = mTo ? mTo[1] : val;
+ unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+
+ if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
+ unit = Y.Anim.DEFAULT_UNIT;
+ }
+
+ if (!begin || !end) {
+ Y.error('invalid "from" or "to" for "' + name + '"', 'Anim');
+ return;
+ }
+
+ attr[name] = {
+ from: begin,
+ to: end,
+ unit: unit
+ };
+
+ attr.duration = dur;
+ attr.easing = easing;
+
+ }, this);
+
+ this._runtimeAttr = attr;
+ },
+
+
+ // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
+ _getOffset: function(attr) {
+ var node = this._node,
+ val = node.getComputedStyle(attr),
+ get = (attr === 'left') ? 'getX': 'getY',
+ set = (attr === 'left') ? 'setX': 'setY';
+
+ if (val === 'auto') {
+ var position = node.getStyle('position');
+ if (position === 'absolute' || position === 'fixed') {
+ val = node[get]();
+ node[set](val);
+ } else {
+ val = 0;
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.extend(Y.Anim, Y.Base, proto);
+
+
+}, '3.0.0' ,{requires:['base-base', 'node-style']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-color', function(Y) {
+
+/**
+ * Adds support for color properties in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-color
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.color = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = Y.Color.re_RGB.exec(Y.Color.toRGB(from));
+ to = Y.Color.re_RGB.exec(Y.Color.toRGB(to));
+
+ if (!from || from.length < 3 || !to || to.length < 3) {
+ Y.error('invalid from or to passed to color behavior');
+ }
+
+ anim._node.setStyle(att, 'rgb(' + [
+ Math.floor(fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)),
+ Math.floor(fn(elapsed, NUM(from[2]), NUM(to[2]) - NUM(from[2]), duration)),
+ Math.floor(fn(elapsed, NUM(from[3]), NUM(to[3]) - NUM(from[3]), duration))
+ ].join(', ') + ')');
+ },
+
+ // TODO: default bgcolor const
+ get: function(anim, att) {
+ var val = anim._node.getComputedStyle(att);
+ val = (val === 'transparent') ? 'rgb(255, 255, 255)' : val;
+ return val;
+ }
+};
+
+Y.each(['backgroundColor',
+ 'borderColor',
+ 'borderTopColor',
+ 'borderRightColor',
+ 'borderBottomColor',
+ 'borderLeftColor'],
+ function(v, i) {
+ Y.Anim.behaviors[v] = Y.Anim.behaviors.color;
+ }
+);
+
+
+}, '3.0.0' ,{requires:['anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-color",function(B){var A=Number;B.Anim.behaviors.color={set:function(F,D,I,H,C,G,E){I=B.Color.re_RGB.exec(B.Color.toRGB(I));H=B.Color.re_RGB.exec(B.Color.toRGB(H));if(!I||I.length<3||!H||H.length<3){B.error("invalid from or to passed to color behavior");}F._node.setStyle(D,"rgb("+[Math.floor(E(C,A(I[1]),A(H[1])-A(I[1]),G)),Math.floor(E(C,A(I[2]),A(H[2])-A(I[2]),G)),Math.floor(E(C,A(I[3]),A(H[3])-A(I[3]),G))].join(", ")+")");},get:function(D,C){var E=D._node.getComputedStyle(C);E=(E==="transparent")?"rgb(255, 255, 255)":E;return E;}};B.each(["backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],function(C,D){B.Anim.behaviors[C]=B.Anim.behaviors.color;});},"3.0.0",{requires:["anim-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-color', function(Y) {
+
+/**
+ * Adds support for color properties in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-color
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.color = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = Y.Color.re_RGB.exec(Y.Color.toRGB(from));
+ to = Y.Color.re_RGB.exec(Y.Color.toRGB(to));
+
+ if (!from || from.length < 3 || !to || to.length < 3) {
+ Y.error('invalid from or to passed to color behavior');
+ }
+
+ anim._node.setStyle(att, 'rgb(' + [
+ Math.floor(fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)),
+ Math.floor(fn(elapsed, NUM(from[2]), NUM(to[2]) - NUM(from[2]), duration)),
+ Math.floor(fn(elapsed, NUM(from[3]), NUM(to[3]) - NUM(from[3]), duration))
+ ].join(', ') + ')');
+ },
+
+ // TODO: default bgcolor const
+ get: function(anim, att) {
+ var val = anim._node.getComputedStyle(att);
+ val = (val === 'transparent') ? 'rgb(255, 255, 255)' : val;
+ return val;
+ }
+};
+
+Y.each(['backgroundColor',
+ 'borderColor',
+ 'borderTopColor',
+ 'borderRightColor',
+ 'borderBottomColor',
+ 'borderLeftColor'],
+ function(v, i) {
+ Y.Anim.behaviors[v] = Y.Anim.behaviors.color;
+ }
+);
+
+
+}, '3.0.0' ,{requires:['anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-curve', function(Y) {
+
+/**
+ * Adds support for the <code>curve</code> property for the <code>to</code>
+ * attribute. A curve is zero or more control points and an end point.
+ * @module anim
+ * @submodule anim-curve
+ */
+
+Y.Anim.behaviors.curve = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = from.slice.call(from);
+ to = to.slice.call(to);
+ var t = fn(elapsed, 0, 100, duration) / 100;
+ to.unshift(from);
+ anim._node.setXY(Y.Anim.getBezier(to, t));
+ },
+
+ get: function(anim, att) {
+ return anim._node.getXY();
+ }
+};
+
+/**
+ * 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.
+ * @for Anim
+ * @method getBezier
+ * @static
+ * @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
+ */
+Y.Anim.getBezier = 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] ];
+
+};
+
+
+}, '3.0.0' ,{requires:['anim-xy']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-curve",function(A){A.Anim.behaviors.curve={set:function(F,C,I,H,B,G,E){I=I.slice.call(I);H=H.slice.call(H);var D=E(B,0,100,G)/100;H.unshift(I);F._node.setXY(A.Anim.getBezier(H,D));},get:function(C,B){return C._node.getXY();}};A.Anim.getBezier=function(F,E){var G=F.length;var D=[];for(var C=0;C<G;++C){D[C]=[F[C][0],F[C][1]];}for(var B=1;B<G;++B){for(C=0;C<G-B;++C){D[C][0]=(1-E)*D[C][0]+E*D[parseInt(C+1,10)][0];D[C][1]=(1-E)*D[C][1]+E*D[parseInt(C+1,10)][1];}}return[D[0][0],D[0][1]];};},"3.0.0",{requires:["anim-xy"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-curve', function(Y) {
+
+/**
+ * Adds support for the <code>curve</code> property for the <code>to</code>
+ * attribute. A curve is zero or more control points and an end point.
+ * @module anim
+ * @submodule anim-curve
+ */
+
+Y.Anim.behaviors.curve = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = from.slice.call(from);
+ to = to.slice.call(to);
+ var t = fn(elapsed, 0, 100, duration) / 100;
+ to.unshift(from);
+ anim._node.setXY(Y.Anim.getBezier(to, t));
+ },
+
+ get: function(anim, att) {
+ return anim._node.getXY();
+ }
+};
+
+/**
+ * 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.
+ * @for Anim
+ * @method getBezier
+ * @static
+ * @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
+ */
+Y.Anim.getBezier = 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] ];
+
+};
+
+
+}, '3.0.0' ,{requires:['anim-xy']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-base', function(Y) {
+
+/**
+* The Animation Utility provides an API for creating advanced transitions.
+* @module anim
+*/
+
+/**
+* Provides the base Anim class, for animating numeric properties.
+*
+* @module anim
+* @submodule anim-base
+*/
+
+ /**
+ * A class for constructing animation instances.
+ * @class Anim
+ * @for Anim
+ * @constructor
+ * @extends Base
+ */
+
+ var RUNNING = 'running',
+ START_TIME = 'startTime',
+ ELAPSED_TIME = 'elapsedTime',
+ /**
+ * @for Anim
+ * @event start
+ * @description fires when an animation begins.
+ * @param {Event} ev The start event.
+ * @type Event.Custom
+ */
+ START = 'start',
+
+ /**
+ * @event tween
+ * @description fires every frame of the animation.
+ * @param {Event} ev The tween event.
+ * @type Event.Custom
+ */
+ TWEEN = 'tween',
+
+ /**
+ * @event end
+ * @description fires after the animation completes.
+ * @param {Event} ev The end event.
+ * @type Event.Custom
+ */
+ END = 'end',
+ NODE = 'node',
+ PAUSED = 'paused',
+ REVERSE = 'reverse', // TODO: cleanup
+ ITERATION_COUNT = 'iterationCount',
+
+ NUM = Number;
+
+ var _running = {},
+ _instances = {},
+ _timer;
+
+ Y.Anim = function() {
+ Y.Anim.superclass.constructor.apply(this, arguments);
+ _instances[Y.stamp(this)] = this;
+ };
+
+ Y.Anim.NAME = 'anim';
+
+ /**
+ * Regex of properties that should use the default unit.
+ *
+ * @property RE_DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
+
+ /**
+ * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
+ *
+ * @property DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.DEFAULT_UNIT = 'px';
+
+ Y.Anim.DEFAULT_EASING = function (t, b, c, d) {
+ return c * t / d + b; // linear easing
+ };
+
+ /**
+ * Bucket for custom getters and setters
+ *
+ * @property behaviors
+ * @static
+ */
+ Y.Anim.behaviors = {
+ left: {
+ get: function(anim, attr) {
+ return anim._getOffset(attr);
+ }
+ }
+ };
+
+ Y.Anim.behaviors.top = Y.Anim.behaviors.left;
+
+ /**
+ * The default setter to use when setting object properties.
+ *
+ * @property DEFAULT_SETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
+ unit = unit || '';
+ anim._node.setStyle(att, fn(elapsed, NUM(from), NUM(to) - NUM(from), duration) + unit);
+ };
+
+ /**
+ * The default getter to use when getting object properties.
+ *
+ * @property DEFAULT_GETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_GETTER = function(anim, prop) {
+ return anim._node.getComputedStyle(prop);
+ };
+
+ Y.Anim.ATTRS = {
+ /**
+ * The object to be animated.
+ * @attribute node
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ node = Y.get(node);
+ this._node = node;
+ if (!node) {
+ Y.log(node + ' is not a valid node', 'warn', 'Anim');
+ }
+ return node;
+ }
+ },
+
+ /**
+ * The length of the animation. Defaults to "1" (second).
+ * @attribute duration
+ * @type NUM
+ */
+ duration: {
+ value: 1
+ },
+
+ /**
+ * The method that will provide values to the attribute(s) during the animation.
+ * Defaults to "Easing.easeNone".
+ * @attribute easing
+ * @type Function
+ */
+ easing: {
+ value: Y.Anim.DEFAULT_EASING,
+
+ setter: function(val) {
+ if (typeof val === 'string' && Y.Easing) {
+ return Y.Easing[val];
+ }
+ }
+ },
+
+ /**
+ * The starting values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * If a function is used, the return value becomes the from value.
+ * If no from value is specified, the DEFAULT_GETTER will be used.
+ * @attribute from
+ * @type Object
+ */
+ from: {},
+
+ /**
+ * The ending values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * @attribute to
+ * @type Object
+ */
+ to: {},
+
+ /**
+ * Date stamp for the first frame of the animation.
+ * @attribute startTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ startTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Current time the animation has been running.
+ * @attribute elapsedTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ elapsedTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Whether or not the animation is currently running.
+ * @attribute running
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ running: {
+ getter: function() {
+ return !!_running[Y.stamp(this)];
+ },
+ value: false,
+ readOnly: true
+ },
+
+ /**
+ * The number of times the animation should run
+ * @attribute iterations
+ * @type Int
+ * @default 1
+ */
+ iterations: {
+ value: 1
+ },
+
+ /**
+ * The number of iterations that have occurred.
+ * Resets when an animation ends (reaches iteration count or stop() called).
+ * @attribute iterationCount
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ iterationCount: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * How iterations of the animation should behave.
+ * Possible values are "normal" and "alternate".
+ * Normal will repeat the animation, alternate will reverse on every other pass.
+ *
+ * @attribute direction
+ * @type String
+ * @default "normal"
+ */
+ direction: {
+ value: 'normal' // | alternate (fwd on odd, rev on even per spec)
+ },
+
+ /**
+ * Whether or not the animation is currently paused.
+ * @attribute paused
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ paused: {
+ readOnly: true,
+ value: false
+ },
+
+ /**
+ * If true, animation begins from last frame
+ * @attribute reverse
+ * @type Boolean
+ * @default false
+ */
+ reverse: {
+ value: false
+ }
+
+
+ };
+
+ /**
+ * Runs all animation instances.
+ * @method run
+ * @static
+ */
+ Y.Anim.run = function() {
+ for (var i in _instances) {
+ if (_instances[i].run) {
+ _instances[i].run();
+ }
+ }
+ };
+
+ /**
+ * Pauses all animation instances.
+ * @method pause
+ * @static
+ */
+ Y.Anim.pause = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].pause) {
+ _running[i].pause();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ /**
+ * Stops all animation instances.
+ * @method stop
+ * @static
+ */
+ Y.Anim.stop = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].stop) {
+ _running[i].stop();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ Y.Anim._startTimer = function() {
+ if (!_timer) {
+ _timer = setInterval(Y.Anim._runFrame, 1);
+ }
+ };
+
+ Y.Anim._stopTimer = function() {
+ clearInterval(_timer);
+ _timer = 0;
+ };
+
+ /**
+ * Called per Interval to handle each animation frame.
+ * @method _runFrame
+ * @private
+ * @static
+ */
+ Y.Anim._runFrame = function() {
+ var done = true;
+ for (var anim in _running) {
+ if (_running[anim]._runFrame) {
+ done = false;
+ _running[anim]._runFrame();
+ }
+ }
+
+ if (done) {
+ Y.Anim._stopTimer();
+ }
+ };
+
+ Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
+
+ var proto = {
+ /**
+ * Starts or resumes an animation.
+ * percent start time marker.
+ * @method run
+ * @chainable
+ */
+ run: function() {
+ if (!this.get(RUNNING)) {
+ this._start();
+ } else if (this.get(PAUSED)) {
+ this._resume();
+ }
+ return this;
+ },
+
+ /**
+ * Pauses the animation and
+ * freezes it in its current state and time.
+ * Calling run() will continue where it left off.
+ * @method pause
+ * @chainable
+ */
+ pause: function() {
+ if (this.get(RUNNING)) {
+ this._pause();
+ }
+ return this;
+ },
+
+ /**
+ * Stops the animation and resets its time.
+ * @method stop
+ * @chainable
+ */
+ stop: function(finish) {
+ if (this.get(RUNNING) || this.get(PAUSED)) {
+ this._end(finish);
+ }
+ return this;
+ },
+
+ _added: false,
+
+ _start: function() {
+ this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
+ this._actualFrames = 0;
+ if (!this.get(PAUSED)) {
+ this._initAnimAttr();
+ }
+ _running[Y.stamp(this)] = this;
+ Y.Anim._startTimer();
+
+ this.fire(START);
+ },
+
+ _pause: function() {
+ this._set(START_TIME, null);
+ this._set(PAUSED, true);
+ delete _running[Y.stamp(this)];
+
+ /**
+ * @event pause
+ * @description fires when an animation is paused.
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('pause');
+ },
+
+ _resume: function() {
+ this._set(PAUSED, false);
+ _running[Y.stamp(this)] = this;
+
+ /**
+ * @event resume
+ * @description fires when an animation is resumed (run from pause).
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('resume');
+ },
+
+ _end: function(finish) {
+ this._set(START_TIME, null);
+ this._set(ELAPSED_TIME, 0);
+ this._set(PAUSED, false);
+
+ delete _running[Y.stamp(this)];
+ this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
+ },
+
+ _runFrame: function() {
+ var attr = this._runtimeAttr,
+ customAttr = Y.Anim.behaviors,
+ easing = attr.easing,
+ d = attr.duration,
+ t = new Date() - this.get(START_TIME),
+ reversed = this.get(REVERSE),
+ done = (t >= d),
+ lastFrame = d,
+ attribute,
+ setter;
+
+ if (reversed) {
+ t = d - t;
+ done = (t <= 0);
+ lastFrame = 0;
+ }
+
+ for (var i in attr) {
+ if (attr[i].to) {
+ attribute = attr[i];
+ setter = (i in customAttr && 'set' in customAttr[i]) ?
+ customAttr[i].set : Y.Anim.DEFAULT_SETTER;
+
+ if (!done) {
+ setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit);
+ } else { // ensure final frame value is set
+ // TODO: handle keyframes
+ setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit);
+ }
+ }
+ }
+
+ this._actualFrames += 1;
+ this._set(ELAPSED_TIME, t);
+
+ this.fire(TWEEN);
+ if (done) {
+ this._lastFrame();
+ }
+ },
+
+ _lastFrame: function() {
+ var iter = this.get('iterations'),
+ iterCount = this.get(ITERATION_COUNT);
+
+ iterCount += 1;
+ if (iter === 'infinite' || iterCount < iter) {
+ if (this.get('direction') === 'alternate') {
+ this.set(REVERSE, !this.get(REVERSE)); // flip it
+ }
+ /**
+ * @event iteration
+ * @description fires when an animation begins an iteration.
+ * @param {Event} ev The iteration event.
+ * @type Event.Custom
+ */
+ this.fire('iteration');
+ } else {
+ iterCount = 0;
+ this._end();
+ }
+
+ this._set(START_TIME, new Date());
+ this._set(ITERATION_COUNT, iterCount);
+ },
+
+ _initAnimAttr: function() {
+ var from = this.get('from') || {},
+ to = this.get('to') || {},
+ dur = this.get('duration') * 1000,
+ node = this.get(NODE),
+ easing = this.get('easing') || {},
+ attr = {},
+ customAttr = Y.Anim.behaviors,
+ unit, begin, end;
+
+ Y.each(to, function(val, name) {
+ if (typeof val === 'function') {
+ val = val.call(this, node);
+ }
+
+ begin = from[name];
+ if (begin === undefined) {
+ begin = (name in customAttr && 'get' in customAttr[name]) ?
+ customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
+ } else if (typeof begin === 'function') {
+ begin = begin.call(this, node);
+ }
+
+ var mFrom = Y.Anim.RE_UNITS.exec(begin);
+ var mTo = Y.Anim.RE_UNITS.exec(val);
+
+ begin = mFrom ? mFrom[1] : begin;
+ end = mTo ? mTo[1] : val;
+ unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+
+ if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
+ unit = Y.Anim.DEFAULT_UNIT;
+ }
+
+ if (!begin || !end) {
+ Y.error('invalid "from" or "to" for "' + name + '"', 'Anim');
+ return;
+ }
+
+ attr[name] = {
+ from: begin,
+ to: end,
+ unit: unit
+ };
+
+ attr.duration = dur;
+ attr.easing = easing;
+
+ }, this);
+
+ this._runtimeAttr = attr;
+ },
+
+
+ // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
+ _getOffset: function(attr) {
+ var node = this._node,
+ val = node.getComputedStyle(attr),
+ get = (attr === 'left') ? 'getX': 'getY',
+ set = (attr === 'left') ? 'setX': 'setY';
+
+ if (val === 'auto') {
+ var position = node.getStyle('position');
+ if (position === 'absolute' || position === 'fixed') {
+ val = node[get]();
+ node[set](val);
+ } else {
+ val = 0;
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.extend(Y.Anim, Y.Base, proto);
+
+
+}, '3.0.0' ,{requires:['base-base', 'node-style']});
+YUI.add('anim-color', function(Y) {
+
+/**
+ * Adds support for color properties in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-color
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.color = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = Y.Color.re_RGB.exec(Y.Color.toRGB(from));
+ to = Y.Color.re_RGB.exec(Y.Color.toRGB(to));
+
+ if (!from || from.length < 3 || !to || to.length < 3) {
+ Y.error('invalid from or to passed to color behavior');
+ }
+
+ anim._node.setStyle(att, 'rgb(' + [
+ Math.floor(fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)),
+ Math.floor(fn(elapsed, NUM(from[2]), NUM(to[2]) - NUM(from[2]), duration)),
+ Math.floor(fn(elapsed, NUM(from[3]), NUM(to[3]) - NUM(from[3]), duration))
+ ].join(', ') + ')');
+ },
+
+ // TODO: default bgcolor const
+ get: function(anim, att) {
+ var val = anim._node.getComputedStyle(att);
+ val = (val === 'transparent') ? 'rgb(255, 255, 255)' : val;
+ return val;
+ }
+};
+
+Y.each(['backgroundColor',
+ 'borderColor',
+ 'borderTopColor',
+ 'borderRightColor',
+ 'borderBottomColor',
+ 'borderLeftColor'],
+ function(v, i) {
+ Y.Anim.behaviors[v] = Y.Anim.behaviors.color;
+ }
+);
+
+
+}, '3.0.0' ,{requires:['anim-base']});
+YUI.add('anim-curve', function(Y) {
+
+/**
+ * Adds support for the <code>curve</code> property for the <code>to</code>
+ * attribute. A curve is zero or more control points and an end point.
+ * @module anim
+ * @submodule anim-curve
+ */
+
+Y.Anim.behaviors.curve = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = from.slice.call(from);
+ to = to.slice.call(to);
+ var t = fn(elapsed, 0, 100, duration) / 100;
+ to.unshift(from);
+ anim._node.setXY(Y.Anim.getBezier(to, t));
+ },
+
+ get: function(anim, att) {
+ return anim._node.getXY();
+ }
+};
+
+/**
+ * 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.
+ * @for Anim
+ * @method getBezier
+ * @static
+ * @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
+ */
+Y.Anim.getBezier = 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] ];
+
+};
+
+
+}, '3.0.0' ,{requires:['anim-xy']});
+YUI.add('anim-easing', function(Y) {
+
+/*
+TERMS OF USE - EASING EQUATIONS
+Open source under the BSD License.
+Copyright 2001 Robert Penner All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The easing module provides methods for customizing
+ * how an animation behaves during each run.
+ * @class Easing
+ * @module anim
+ * @submodule anim-easing
+ */
+
+Y.Easing = {
+
+ /**
+ * Uniform speed between points.
+ * @for Easing
+ * @method easeNone
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeNone: function (t, b, c, d) {
+ return c*t/d + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quadratic)
+ * @method easeIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeIn: function (t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quadratic)
+ * @method easeOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOut: function (t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quadratic)
+ * @method easeBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBoth: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t + b;
+ }
+
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quartic)
+ * @method easeInStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeInStrong: function (t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quartic)
+ * @method easeOutStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOutStrong: function (t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quartic)
+ * @method easeBothStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBothStrong: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t*t*t + b;
+ }
+
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+
+ /**
+ * Snap in elastic effect.
+ * @method elasticIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+
+ elasticIn: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p = d* 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+
+ /**
+ * Snap out elastic effect.
+ * @method elasticOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticOut: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d * 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+
+ /**
+ * Snap both elastic effect.
+ * @method elasticBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticBoth: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+
+ if ( (t /= d/2) === 2 ) {
+ return b+c;
+ }
+
+ if (!p) {
+ p = d*(0.3*1.5);
+ }
+
+ if ( !a || a < Math.abs(c) ) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ if (t < 1) {
+ return -0.5*(a*Math.pow(2,10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ }
+ return a*Math.pow(2,-10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
+ },
+
+
+ /**
+ * Backtracks slightly, then reverses direction and moves to end.
+ * @method backIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backIn: function (t, b, c, d, s) {
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ if (t === d) {
+ t -= 0.001;
+ }
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+
+ /**
+ * Overshoots end, then reverses and comes back to end.
+ * @method backOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backOut: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+
+ /**
+ * Backtracks slightly, then reverses direction, overshoots end,
+ * then reverses and comes back to end.
+ * @method backBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backBoth: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+
+ if ((t /= d/2 ) < 1) {
+ return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ }
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+
+ /**
+ * Bounce off of start.
+ * @method bounceIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceIn: function (t, b, c, d) {
+ return c - Y.Easing.bounceOut(d-t, 0, c, d) + b;
+ },
+
+ /**
+ * Bounces off end.
+ * @method bounceOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceOut: function (t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
+ }
+ return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
+ },
+
+ /**
+ * Bounces off start and end.
+ * @method bounceBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceBoth: function (t, b, c, d) {
+ if (t < d/2) {
+ return Y.Easing.bounceIn(t * 2, 0, c, d) * 0.5 + b;
+ }
+ return Y.Easing.bounceOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
+ }
+};
+
+
+}, '3.0.0' ,{requires:['anim-base']});
+YUI.add('anim-node-plugin', function(Y) {
+
+/**
+ * Binds an Anim instance to a Node instance
+ * @module anim
+ * @class Plugin.NodeFX
+ * @extends Base
+ * @submodule anim-node-plugin
+ */
+
+var NodeFX = function(config) {
+ config = (config) ? Y.merge(config) : {};
+ config.node = config.host;
+ NodeFX.superclass.constructor.apply(this, arguments);
+};
+
+NodeFX.NAME = "nodefx";
+NodeFX.NS = "fx";
+
+Y.extend(NodeFX, Y.Anim);
+
+Y.namespace('Plugin');
+Y.Plugin.NodeFX = NodeFX;
+
+
+}, '3.0.0' ,{requires:['node-pluginhost', 'anim-base']});
+YUI.add('anim-scroll', function(Y) {
+
+/**
+ * Adds support for the <code>scroll</code> property in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-scroll
+ */
+
+var NUM = Number;
+
+//TODO: deprecate for scrollTop/Left properties?
+Y.Anim.behaviors.scroll = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ var
+ node = anim._node,
+ val = ([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+
+ if (val[0]) {
+ node.set('scrollLeft', val[0]);
+ }
+
+ if (val[1]) {
+ node.set('scrollTop', val[1]);
+ }
+ },
+ get: function(anim) {
+ var node = anim._node;
+ return [node.get('scrollLeft'), node.get('scrollTop')];
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base']});
+YUI.add('anim-xy', function(Y) {
+
+/**
+ * Adds support for the <code>xy</code> property in <code>from</code> and
+ * <code>to</code> attributes.
+ * @module anim
+ * @submodule anim-xy
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.xy = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ anim._node.setXY([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+ },
+ get: function(anim) {
+ return anim._node.getXY();
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base', 'node-screen']});
+
+
+YUI.add('anim', function(Y){}, '3.0.0' ,{use:['anim-base', 'anim-color', 'anim-curve', 'anim-easing', 'anim-node-plugin', 'anim-scroll', 'anim-xy'], skinnable:false});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-easing', function(Y) {
+
+/*
+TERMS OF USE - EASING EQUATIONS
+Open source under the BSD License.
+Copyright 2001 Robert Penner All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The easing module provides methods for customizing
+ * how an animation behaves during each run.
+ * @class Easing
+ * @module anim
+ * @submodule anim-easing
+ */
+
+Y.Easing = {
+
+ /**
+ * Uniform speed between points.
+ * @for Easing
+ * @method easeNone
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeNone: function (t, b, c, d) {
+ return c*t/d + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quadratic)
+ * @method easeIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeIn: function (t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quadratic)
+ * @method easeOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOut: function (t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quadratic)
+ * @method easeBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBoth: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t + b;
+ }
+
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quartic)
+ * @method easeInStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeInStrong: function (t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quartic)
+ * @method easeOutStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOutStrong: function (t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quartic)
+ * @method easeBothStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBothStrong: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t*t*t + b;
+ }
+
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+
+ /**
+ * Snap in elastic effect.
+ * @method elasticIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+
+ elasticIn: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p = d* 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+
+ /**
+ * Snap out elastic effect.
+ * @method elasticOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticOut: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d * 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+
+ /**
+ * Snap both elastic effect.
+ * @method elasticBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticBoth: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+
+ if ( (t /= d/2) === 2 ) {
+ return b+c;
+ }
+
+ if (!p) {
+ p = d*(0.3*1.5);
+ }
+
+ if ( !a || a < Math.abs(c) ) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ if (t < 1) {
+ return -0.5*(a*Math.pow(2,10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ }
+ return a*Math.pow(2,-10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
+ },
+
+
+ /**
+ * Backtracks slightly, then reverses direction and moves to end.
+ * @method backIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backIn: function (t, b, c, d, s) {
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ if (t === d) {
+ t -= 0.001;
+ }
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+
+ /**
+ * Overshoots end, then reverses and comes back to end.
+ * @method backOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backOut: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+
+ /**
+ * Backtracks slightly, then reverses direction, overshoots end,
+ * then reverses and comes back to end.
+ * @method backBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backBoth: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+
+ if ((t /= d/2 ) < 1) {
+ return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ }
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+
+ /**
+ * Bounce off of start.
+ * @method bounceIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceIn: function (t, b, c, d) {
+ return c - Y.Easing.bounceOut(d-t, 0, c, d) + b;
+ },
+
+ /**
+ * Bounces off end.
+ * @method bounceOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceOut: function (t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
+ }
+ return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
+ },
+
+ /**
+ * Bounces off start and end.
+ * @method bounceBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceBoth: function (t, b, c, d) {
+ if (t < d/2) {
+ return Y.Easing.bounceIn(t * 2, 0, c, d) * 0.5 + b;
+ }
+ return Y.Easing.bounceOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
+ }
+};
+
+
+}, '3.0.0' ,{requires:['anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-easing",function(A){A.Easing={easeNone:function(C,B,E,D){return E*C/D+B;},easeIn:function(C,B,E,D){return E*(C/=D)*C+B;},easeOut:function(C,B,E,D){return -E*(C/=D)*(C-2)+B;},easeBoth:function(C,B,E,D){if((C/=D/2)<1){return E/2*C*C+B;}return -E/2*((--C)*(C-2)-1)+B;},easeInStrong:function(C,B,E,D){return E*(C/=D)*C*C*C+B;},easeOutStrong:function(C,B,E,D){return -E*((C=C/D-1)*C*C*C-1)+B;},easeBothStrong:function(C,B,E,D){if((C/=D/2)<1){return E/2*C*C*C*C+B;}return -E/2*((C-=2)*C*C*C-2)+B;},elasticIn:function(D,B,H,G,C,F){var E;if(D===0){return B;}if((D/=G)===1){return B+H;}if(!F){F=G*0.3;}if(!C||C<Math.abs(H)){C=H;E=F/4;}else{E=F/(2*Math.PI)*Math.asin(H/C);}return -(C*Math.pow(2,10*(D-=1))*Math.sin((D*G-E)*(2*Math.PI)/F))+B;},elasticOut:function(D,B,H,G,C,F){var E;if(D===0){return B;}if((D/=G)===1){return B+H;}if(!F){F=G*0.3;}if(!C||C<Math.abs(H)){C=H;E=F/4;}else{E=F/(2*Math.PI)*Math.asin(H/C);}return C*Math.pow(2,-10*D)*Math.sin((D*G-E)*(2*Math.PI)/F)+H+B;},elasticBoth:function(D,B,H,G,C,F){var E;if(D===0){return B;}if((D/=G/2)===2){return B+H;}if(!F){F=G*(0.3*1.5);}if(!C||C<Math.abs(H)){C=H;E=F/4;}else{E=F/(2*Math.PI)*Math.asin(H/C);}if(D<1){return -0.5*(C*Math.pow(2,10*(D-=1))*Math.sin((D*G-E)*(2*Math.PI)/F))+B;}return C*Math.pow(2,-10*(D-=1))*Math.sin((D*G-E)*(2*Math.PI)/F)*0.5+H+B;},backIn:function(C,B,F,E,D){if(D===undefined){D=1.70158;}if(C===E){C-=0.001;}return F*(C/=E)*C*((D+1)*C-D)+B;},backOut:function(C,B,F,E,D){if(typeof D==="undefined"){D=1.70158;}return F*((C=C/E-1)*C*((D+1)*C+D)+1)+B;},backBoth:function(C,B,F,E,D){if(typeof D==="undefined"){D=1.70158;}if((C/=E/2)<1){return F/2*(C*C*(((D*=(1.525))+1)*C-D))+B;}return F/2*((C-=2)*C*(((D*=(1.525))+1)*C+D)+2)+B;},bounceIn:function(C,B,E,D){return E-A.Easing.bounceOut(D-C,0,E,D)+B;},bounceOut:function(C,B,E,D){if((C/=D)<(1/2.75)){return E*(7.5625*C*C)+B;}else{if(C<(2/2.75)){return E*(7.5625*(C-=(1.5/2.75))*C+0.75)+B;}else{if(C<(2.5/2.75)){return E*(7.5625*(C-=(2.25/2.75))*C+0.9375)+B;}}}return E*(7.5625*(C-=(2.625/2.75))*C+0.984375)+B;},bounceBoth:function(C,B,E,D){if(C<D/2){return A.Easing.bounceIn(C*2,0,E,D)*0.5+B;}return A.Easing.bounceOut(C*2-D,0,E,D)*0.5+E*0.5+B;}};},"3.0.0",{requires:["anim-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-easing', function(Y) {
+
+/*
+TERMS OF USE - EASING EQUATIONS
+Open source under the BSD License.
+Copyright 2001 Robert Penner All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The easing module provides methods for customizing
+ * how an animation behaves during each run.
+ * @class Easing
+ * @module anim
+ * @submodule anim-easing
+ */
+
+Y.Easing = {
+
+ /**
+ * Uniform speed between points.
+ * @for Easing
+ * @method easeNone
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeNone: function (t, b, c, d) {
+ return c*t/d + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quadratic)
+ * @method easeIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeIn: function (t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quadratic)
+ * @method easeOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOut: function (t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quadratic)
+ * @method easeBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBoth: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t + b;
+ }
+
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quartic)
+ * @method easeInStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeInStrong: function (t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quartic)
+ * @method easeOutStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOutStrong: function (t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quartic)
+ * @method easeBothStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBothStrong: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t*t*t + b;
+ }
+
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+
+ /**
+ * Snap in elastic effect.
+ * @method elasticIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+
+ elasticIn: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p = d* 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+
+ /**
+ * Snap out elastic effect.
+ * @method elasticOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticOut: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d * 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+
+ /**
+ * Snap both elastic effect.
+ * @method elasticBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticBoth: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+
+ if ( (t /= d/2) === 2 ) {
+ return b+c;
+ }
+
+ if (!p) {
+ p = d*(0.3*1.5);
+ }
+
+ if ( !a || a < Math.abs(c) ) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ if (t < 1) {
+ return -0.5*(a*Math.pow(2,10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ }
+ return a*Math.pow(2,-10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
+ },
+
+
+ /**
+ * Backtracks slightly, then reverses direction and moves to end.
+ * @method backIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backIn: function (t, b, c, d, s) {
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ if (t === d) {
+ t -= 0.001;
+ }
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+
+ /**
+ * Overshoots end, then reverses and comes back to end.
+ * @method backOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backOut: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+
+ /**
+ * Backtracks slightly, then reverses direction, overshoots end,
+ * then reverses and comes back to end.
+ * @method backBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backBoth: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+
+ if ((t /= d/2 ) < 1) {
+ return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ }
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+
+ /**
+ * Bounce off of start.
+ * @method bounceIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceIn: function (t, b, c, d) {
+ return c - Y.Easing.bounceOut(d-t, 0, c, d) + b;
+ },
+
+ /**
+ * Bounces off end.
+ * @method bounceOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceOut: function (t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
+ }
+ return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
+ },
+
+ /**
+ * Bounces off start and end.
+ * @method bounceBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceBoth: function (t, b, c, d) {
+ if (t < d/2) {
+ return Y.Easing.bounceIn(t * 2, 0, c, d) * 0.5 + b;
+ }
+ return Y.Easing.bounceOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
+ }
+};
+
+
+}, '3.0.0' ,{requires:['anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-base",function(B){var C="running",N="startTime",L="elapsedTime",J="start",I="tween",M="end",D="node",K="paused",P="reverse",H="iterationCount",A=Number;var F={},O={},E;B.Anim=function(){B.Anim.superclass.constructor.apply(this,arguments);O[B.stamp(this)]=this;};B.Anim.NAME="anim";B.Anim.RE_DEFAULT_UNIT=/^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;B.Anim.DEFAULT_UNIT="px";B.Anim.DEFAULT_EASING=function(R,Q,T,S){return T*R/S+Q;};B.Anim.behaviors={left:{get:function(R,Q){return R._getOffset(Q);}}};B.Anim.behaviors.top=B.Anim.behaviors.left;B.Anim.DEFAULT_SETTER=function(U,R,X,W,Q,V,S,T){T=T||"";U._node.setStyle(R,S(Q,A(X),A(W)-A(X),V)+T);};B.Anim.DEFAULT_GETTER=function(Q,R){return Q._node.getComputedStyle(R);};B.Anim.ATTRS={node:{setter:function(Q){Q=B.get(Q);this._node=Q;if(!Q){}return Q;}},duration:{value:1},easing:{value:B.Anim.DEFAULT_EASING,setter:function(Q){if(typeof Q==="string"&&B.Easing){return B.Easing[Q];}}},from:{},to:{},startTime:{value:0,readOnly:true},elapsedTime:{value:0,readOnly:true},running:{getter:function(){return !!F[B.stamp(this)];},value:false,readOnly:true},iterations:{value:1},iterationCount:{value:0,readOnly:true},direction:{value:"normal"},paused:{readOnly:true,value:false},reverse:{value:false}};B.Anim.run=function(){for(var Q in O){if(O[Q].run){O[Q].run();}}};B.Anim.pause=function(){for(var Q in F){if(F[Q].pause){F[Q].pause();}}B.Anim._stopTimer();};B.Anim.stop=function(){for(var Q in F){if(F[Q].stop){F[Q].stop();}}B.Anim._stopTimer();};B.Anim._startTimer=function(){if(!E){E=setInterval(B.Anim._runFrame,1);}};B.Anim._stopTimer=function(){clearInterval(E);E=0;};B.Anim._runFrame=function(){var Q=true;for(var R in F){if(F[R]._runFrame){Q=false;F[R]._runFrame();}}if(Q){B.Anim._stopTimer();}};B.Anim.RE_UNITS=/^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;var G={run:function(){if(!this.get(C)){this._start();}else{if(this.get(K)){this._resume();}}return this;},pause:function(){if(this.get(C)){this._pause();}return this;},stop:function(Q){if(this.get(C)||this.get(K)){this._end(Q);}return this;},_added:false,_start:function(){this._set(N,new Date()-this.get(L));this._actualFrames=0;if(!this.get(K)){this._initAnimAttr();}F[B.stamp(this)]=this;B.Anim._startTimer();this.fire(J);},_pause:function(){this._set(N,null);this._set(K,true);delete F[B.stamp(this)];this.fire("pause");},_resume:function(){this._set(K,false);F[B.stamp(this)]=this;this.fire("resume");},_end:function(Q){this._set(N,null);this._set(L,0);this._set(K,false);delete F[B.stamp(this)];this.fire(M,{elapsed:this.get(L)});},_runFrame:function(){var X=this._runtimeAttr,S=B.Anim.behaviors,Y=X.easing,Z=X.duration,a=new Date()-this.get(N),W=this.get(P),U=(a>=Z),Q=Z,R,T;if(W){a=Z-a;U=(a<=0);Q=0;}for(var V in X){if(X[V].to){R=X[V];T=(V in S&&"set" in S[V])?S[V].set:B.Anim.DEFAULT_SETTER;if(!U){T(this,V,R.from,R.to,a,Z,Y,R.unit);}else{T(this,V,R.from,R.to,Q,Z,Y,R.unit);}}}this._actualFrames+=1;this._set(L,a);this.fire(I);if(U){this._lastFrame();}},_lastFrame:function(){var Q=this.get("iterations"),R=this.get(H);R+=1;if(Q==="infinite"||R<Q){if(this.get("direction")==="alternate"){this.set(P,!this.get(P));}this.fire("iteration");}else{R=0;this._end();}this._set(N,new Date());this._set(H,R);},_initAnimAttr:function(){var X=this.get("from")||{},Y=this.get("to")||{},Q=this.get("duration")*1000,T=this.get(D),W=this.get("easing")||{},V={},R=B.Anim.behaviors,Z,S,U;B.each(Y,function(d,b){if(typeof d==="function"){d=d.call(this,T);}S=X[b];if(S===undefined){S=(b in R&&"get" in R[b])?R[b].get(this,b):B.Anim.DEFAULT_GETTER(this,b);}else{if(typeof S==="function"){S=S.call(this,T);}}var a=B.Anim.RE_UNITS.exec(S);var c=B.Anim.RE_UNITS.exec(d);S=a?a[1]:S;U=c?c[1]:d;Z=c?c[2]:a?a[2]:"";if(!Z&&B.Anim.RE_DEFAULT_UNIT.test(b)){Z=B.Anim.DEFAULT_UNIT;}if(!S||!U){B.error('invalid "from" or "to" for "'+b+'"',"Anim");return;}V[b]={from:S,to:U,unit:Z};V.duration=Q;V.easing=W;},this);this._runtimeAttr=V;},_getOffset:function(R){var T=this._node,U=T.getComputedStyle(R),S=(R==="left")?"getX":"getY",V=(R==="left")?"setX":"setY";if(U==="auto"){var Q=T.getStyle("position");if(Q==="absolute"||Q==="fixed"){U=T[S]();T[V](U);}else{U=0;}}return U;}};B.extend(B.Anim,B.Base,G);},"3.0.0",{requires:["base-base","node-style"]});YUI.add("anim-color",function(B){var A=Number;B.Anim.behaviors.color={set:function(F,D,I,H,C,G,E){I=B.Color.re_RGB.exec(B.Color.toRGB(I));H=B.Color.re_RGB.exec(B.Color.toRGB(H));if(!I||I.length<3||!H||H.length<3){B.error("invalid from or to passed to color behavior");}F._node.setStyle(D,"rgb("+[Math.floor(E(C,A(I[1]),A(H[1])-A(I[1]),G)),Math.floor(E(C,A(I[2]),A(H[2])-A(I[2]),G)),Math.floor(E(C,A(I[3]),A(H[3])-A(I[3]),G))].join(", ")+")");},get:function(D,C){var E=D._node.getComputedStyle(C);E=(E==="transparent")?"rgb(255, 255, 255)":E;return E;}};B.each(["backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor"],function(C,D){B.Anim.behaviors[C]=B.Anim.behaviors.color;});},"3.0.0",{requires:["anim-base"]});YUI.add("anim-curve",function(A){A.Anim.behaviors.curve={set:function(F,C,I,H,B,G,E){I=I.slice.call(I);H=H.slice.call(H);var D=E(B,0,100,G)/100;H.unshift(I);F._node.setXY(A.Anim.getBezier(H,D));},get:function(C,B){return C._node.getXY();}};A.Anim.getBezier=function(F,E){var G=F.length;var D=[];for(var C=0;C<G;++C){D[C]=[F[C][0],F[C][1]];}for(var B=1;B<G;++B){for(C=0;C<G-B;++C){D[C][0]=(1-E)*D[C][0]+E*D[parseInt(C+1,10)][0];D[C][1]=(1-E)*D[C][1]+E*D[parseInt(C+1,10)][1];}}return[D[0][0],D[0][1]];};},"3.0.0",{requires:["anim-xy"]});YUI.add("anim-easing",function(A){A.Easing={easeNone:function(C,B,E,D){return E*C/D+B;},easeIn:function(C,B,E,D){return E*(C/=D)*C+B;},easeOut:function(C,B,E,D){return -E*(C/=D)*(C-2)+B;},easeBoth:function(C,B,E,D){if((C/=D/2)<1){return E/2*C*C+B;}return -E/2*((--C)*(C-2)-1)+B;},easeInStrong:function(C,B,E,D){return E*(C/=D)*C*C*C+B;},easeOutStrong:function(C,B,E,D){return -E*((C=C/D-1)*C*C*C-1)+B;},easeBothStrong:function(C,B,E,D){if((C/=D/2)<1){return E/2*C*C*C*C+B;
+}return -E/2*((C-=2)*C*C*C-2)+B;},elasticIn:function(D,B,H,G,C,F){var E;if(D===0){return B;}if((D/=G)===1){return B+H;}if(!F){F=G*0.3;}if(!C||C<Math.abs(H)){C=H;E=F/4;}else{E=F/(2*Math.PI)*Math.asin(H/C);}return -(C*Math.pow(2,10*(D-=1))*Math.sin((D*G-E)*(2*Math.PI)/F))+B;},elasticOut:function(D,B,H,G,C,F){var E;if(D===0){return B;}if((D/=G)===1){return B+H;}if(!F){F=G*0.3;}if(!C||C<Math.abs(H)){C=H;E=F/4;}else{E=F/(2*Math.PI)*Math.asin(H/C);}return C*Math.pow(2,-10*D)*Math.sin((D*G-E)*(2*Math.PI)/F)+H+B;},elasticBoth:function(D,B,H,G,C,F){var E;if(D===0){return B;}if((D/=G/2)===2){return B+H;}if(!F){F=G*(0.3*1.5);}if(!C||C<Math.abs(H)){C=H;E=F/4;}else{E=F/(2*Math.PI)*Math.asin(H/C);}if(D<1){return -0.5*(C*Math.pow(2,10*(D-=1))*Math.sin((D*G-E)*(2*Math.PI)/F))+B;}return C*Math.pow(2,-10*(D-=1))*Math.sin((D*G-E)*(2*Math.PI)/F)*0.5+H+B;},backIn:function(C,B,F,E,D){if(D===undefined){D=1.70158;}if(C===E){C-=0.001;}return F*(C/=E)*C*((D+1)*C-D)+B;},backOut:function(C,B,F,E,D){if(typeof D==="undefined"){D=1.70158;}return F*((C=C/E-1)*C*((D+1)*C+D)+1)+B;},backBoth:function(C,B,F,E,D){if(typeof D==="undefined"){D=1.70158;}if((C/=E/2)<1){return F/2*(C*C*(((D*=(1.525))+1)*C-D))+B;}return F/2*((C-=2)*C*(((D*=(1.525))+1)*C+D)+2)+B;},bounceIn:function(C,B,E,D){return E-A.Easing.bounceOut(D-C,0,E,D)+B;},bounceOut:function(C,B,E,D){if((C/=D)<(1/2.75)){return E*(7.5625*C*C)+B;}else{if(C<(2/2.75)){return E*(7.5625*(C-=(1.5/2.75))*C+0.75)+B;}else{if(C<(2.5/2.75)){return E*(7.5625*(C-=(2.25/2.75))*C+0.9375)+B;}}}return E*(7.5625*(C-=(2.625/2.75))*C+0.984375)+B;},bounceBoth:function(C,B,E,D){if(C<D/2){return A.Easing.bounceIn(C*2,0,E,D)*0.5+B;}return A.Easing.bounceOut(C*2-D,0,E,D)*0.5+E*0.5+B;}};},"3.0.0",{requires:["anim-base"]});YUI.add("anim-node-plugin",function(B){var A=function(C){C=(C)?B.merge(C):{};C.node=C.host;A.superclass.constructor.apply(this,arguments);};A.NAME="nodefx";A.NS="fx";B.extend(A,B.Anim);B.namespace("Plugin");B.Plugin.NodeFX=A;},"3.0.0",{requires:["node-pluginhost","anim-base"]});YUI.add("anim-scroll",function(B){var A=Number;B.Anim.behaviors.scroll={set:function(F,G,I,J,K,E,H){var D=F._node,C=([H(K,A(I[0]),A(J[0])-A(I[0]),E),H(K,A(I[1]),A(J[1])-A(I[1]),E)]);if(C[0]){D.set("scrollLeft",C[0]);}if(C[1]){D.set("scrollTop",C[1]);}},get:function(D){var C=D._node;return[C.get("scrollLeft"),C.get("scrollTop")];}};},"3.0.0",{requires:["anim-base"]});YUI.add("anim-xy",function(B){var A=Number;B.Anim.behaviors.xy={set:function(F,D,I,H,C,G,E){F._node.setXY([E(C,A(I[0]),A(H[0])-A(I[0]),G),E(C,A(I[1]),A(H[1])-A(I[1]),G)]);},get:function(C){return C._node.getXY();}};},"3.0.0",{requires:["anim-base","node-screen"]});YUI.add("anim",function(A){},"3.0.0",{use:["anim-base","anim-color","anim-curve","anim-easing","anim-node-plugin","anim-scroll","anim-xy"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-node-plugin', function(Y) {
+
+/**
+ * Binds an Anim instance to a Node instance
+ * @module anim
+ * @class Plugin.NodeFX
+ * @extends Base
+ * @submodule anim-node-plugin
+ */
+
+var NodeFX = function(config) {
+ config = (config) ? Y.merge(config) : {};
+ config.node = config.host;
+ NodeFX.superclass.constructor.apply(this, arguments);
+};
+
+NodeFX.NAME = "nodefx";
+NodeFX.NS = "fx";
+
+Y.extend(NodeFX, Y.Anim);
+
+Y.namespace('Plugin');
+Y.Plugin.NodeFX = NodeFX;
+
+
+}, '3.0.0' ,{requires:['node-pluginhost', 'anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-node-plugin",function(B){var A=function(C){C=(C)?B.merge(C):{};C.node=C.host;A.superclass.constructor.apply(this,arguments);};A.NAME="nodefx";A.NS="fx";B.extend(A,B.Anim);B.namespace("Plugin");B.Plugin.NodeFX=A;},"3.0.0",{requires:["node-pluginhost","anim-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-node-plugin', function(Y) {
+
+/**
+ * Binds an Anim instance to a Node instance
+ * @module anim
+ * @class Plugin.NodeFX
+ * @extends Base
+ * @submodule anim-node-plugin
+ */
+
+var NodeFX = function(config) {
+ config = (config) ? Y.merge(config) : {};
+ config.node = config.host;
+ NodeFX.superclass.constructor.apply(this, arguments);
+};
+
+NodeFX.NAME = "nodefx";
+NodeFX.NS = "fx";
+
+Y.extend(NodeFX, Y.Anim);
+
+Y.namespace('Plugin');
+Y.Plugin.NodeFX = NodeFX;
+
+
+}, '3.0.0' ,{requires:['node-pluginhost', 'anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-scroll', function(Y) {
+
+/**
+ * Adds support for the <code>scroll</code> property in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-scroll
+ */
+
+var NUM = Number;
+
+//TODO: deprecate for scrollTop/Left properties?
+Y.Anim.behaviors.scroll = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ var
+ node = anim._node,
+ val = ([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+
+ if (val[0]) {
+ node.set('scrollLeft', val[0]);
+ }
+
+ if (val[1]) {
+ node.set('scrollTop', val[1]);
+ }
+ },
+ get: function(anim) {
+ var node = anim._node;
+ return [node.get('scrollLeft'), node.get('scrollTop')];
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-scroll",function(B){var A=Number;B.Anim.behaviors.scroll={set:function(F,G,I,J,K,E,H){var D=F._node,C=([H(K,A(I[0]),A(J[0])-A(I[0]),E),H(K,A(I[1]),A(J[1])-A(I[1]),E)]);if(C[0]){D.set("scrollLeft",C[0]);}if(C[1]){D.set("scrollTop",C[1]);}},get:function(D){var C=D._node;return[C.get("scrollLeft"),C.get("scrollTop")];}};},"3.0.0",{requires:["anim-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-scroll', function(Y) {
+
+/**
+ * Adds support for the <code>scroll</code> property in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-scroll
+ */
+
+var NUM = Number;
+
+//TODO: deprecate for scrollTop/Left properties?
+Y.Anim.behaviors.scroll = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ var
+ node = anim._node,
+ val = ([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+
+ if (val[0]) {
+ node.set('scrollLeft', val[0]);
+ }
+
+ if (val[1]) {
+ node.set('scrollTop', val[1]);
+ }
+ },
+ get: function(anim) {
+ var node = anim._node;
+ return [node.get('scrollLeft'), node.get('scrollTop')];
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-xy', function(Y) {
+
+/**
+ * Adds support for the <code>xy</code> property in <code>from</code> and
+ * <code>to</code> attributes.
+ * @module anim
+ * @submodule anim-xy
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.xy = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ anim._node.setXY([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+ },
+ get: function(anim) {
+ return anim._node.getXY();
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base', 'node-screen']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("anim-xy",function(B){var A=Number;B.Anim.behaviors.xy={set:function(F,D,I,H,C,G,E){F._node.setXY([E(C,A(I[0]),A(H[0])-A(I[0]),G),E(C,A(I[1]),A(H[1])-A(I[1]),G)]);},get:function(C){return C._node.getXY();}};},"3.0.0",{requires:["anim-base","node-screen"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-xy', function(Y) {
+
+/**
+ * Adds support for the <code>xy</code> property in <code>from</code> and
+ * <code>to</code> attributes.
+ * @module anim
+ * @submodule anim-xy
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.xy = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ anim._node.setXY([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+ },
+ get: function(anim) {
+ return anim._node.getXY();
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base', 'node-screen']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('anim-base', function(Y) {
+
+/**
+* The Animation Utility provides an API for creating advanced transitions.
+* @module anim
+*/
+
+/**
+* Provides the base Anim class, for animating numeric properties.
+*
+* @module anim
+* @submodule anim-base
+*/
+
+ /**
+ * A class for constructing animation instances.
+ * @class Anim
+ * @for Anim
+ * @constructor
+ * @extends Base
+ */
+
+ var RUNNING = 'running',
+ START_TIME = 'startTime',
+ ELAPSED_TIME = 'elapsedTime',
+ /**
+ * @for Anim
+ * @event start
+ * @description fires when an animation begins.
+ * @param {Event} ev The start event.
+ * @type Event.Custom
+ */
+ START = 'start',
+
+ /**
+ * @event tween
+ * @description fires every frame of the animation.
+ * @param {Event} ev The tween event.
+ * @type Event.Custom
+ */
+ TWEEN = 'tween',
+
+ /**
+ * @event end
+ * @description fires after the animation completes.
+ * @param {Event} ev The end event.
+ * @type Event.Custom
+ */
+ END = 'end',
+ NODE = 'node',
+ PAUSED = 'paused',
+ REVERSE = 'reverse', // TODO: cleanup
+ ITERATION_COUNT = 'iterationCount',
+
+ NUM = Number;
+
+ var _running = {},
+ _instances = {},
+ _timer;
+
+ Y.Anim = function() {
+ Y.Anim.superclass.constructor.apply(this, arguments);
+ _instances[Y.stamp(this)] = this;
+ };
+
+ Y.Anim.NAME = 'anim';
+
+ /**
+ * Regex of properties that should use the default unit.
+ *
+ * @property RE_DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.RE_DEFAULT_UNIT = /^width|height|top|right|bottom|left|margin.*|padding.*|border.*$/i;
+
+ /**
+ * The default unit to use with properties that pass the RE_DEFAULT_UNIT test.
+ *
+ * @property DEFAULT_UNIT
+ * @static
+ */
+ Y.Anim.DEFAULT_UNIT = 'px';
+
+ Y.Anim.DEFAULT_EASING = function (t, b, c, d) {
+ return c * t / d + b; // linear easing
+ };
+
+ /**
+ * Bucket for custom getters and setters
+ *
+ * @property behaviors
+ * @static
+ */
+ Y.Anim.behaviors = {
+ left: {
+ get: function(anim, attr) {
+ return anim._getOffset(attr);
+ }
+ }
+ };
+
+ Y.Anim.behaviors.top = Y.Anim.behaviors.left;
+
+ /**
+ * The default setter to use when setting object properties.
+ *
+ * @property DEFAULT_SETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_SETTER = function(anim, att, from, to, elapsed, duration, fn, unit) {
+ unit = unit || '';
+ anim._node.setStyle(att, fn(elapsed, NUM(from), NUM(to) - NUM(from), duration) + unit);
+ };
+
+ /**
+ * The default getter to use when getting object properties.
+ *
+ * @property DEFAULT_GETTER
+ * @static
+ */
+ Y.Anim.DEFAULT_GETTER = function(anim, prop) {
+ return anim._node.getComputedStyle(prop);
+ };
+
+ Y.Anim.ATTRS = {
+ /**
+ * The object to be animated.
+ * @attribute node
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ node = Y.get(node);
+ this._node = node;
+ if (!node) {
+ }
+ return node;
+ }
+ },
+
+ /**
+ * The length of the animation. Defaults to "1" (second).
+ * @attribute duration
+ * @type NUM
+ */
+ duration: {
+ value: 1
+ },
+
+ /**
+ * The method that will provide values to the attribute(s) during the animation.
+ * Defaults to "Easing.easeNone".
+ * @attribute easing
+ * @type Function
+ */
+ easing: {
+ value: Y.Anim.DEFAULT_EASING,
+
+ setter: function(val) {
+ if (typeof val === 'string' && Y.Easing) {
+ return Y.Easing[val];
+ }
+ }
+ },
+
+ /**
+ * The starting values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * If a function is used, the return value becomes the from value.
+ * If no from value is specified, the DEFAULT_GETTER will be used.
+ * @attribute from
+ * @type Object
+ */
+ from: {},
+
+ /**
+ * The ending values for the animated properties.
+ * Fields may be strings, numbers, or functions.
+ * @attribute to
+ * @type Object
+ */
+ to: {},
+
+ /**
+ * Date stamp for the first frame of the animation.
+ * @attribute startTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ startTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Current time the animation has been running.
+ * @attribute elapsedTime
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ elapsedTime: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * Whether or not the animation is currently running.
+ * @attribute running
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ running: {
+ getter: function() {
+ return !!_running[Y.stamp(this)];
+ },
+ value: false,
+ readOnly: true
+ },
+
+ /**
+ * The number of times the animation should run
+ * @attribute iterations
+ * @type Int
+ * @default 1
+ */
+ iterations: {
+ value: 1
+ },
+
+ /**
+ * The number of iterations that have occurred.
+ * Resets when an animation ends (reaches iteration count or stop() called).
+ * @attribute iterationCount
+ * @type Int
+ * @default 0
+ * @readOnly
+ */
+ iterationCount: {
+ value: 0,
+ readOnly: true
+ },
+
+ /**
+ * How iterations of the animation should behave.
+ * Possible values are "normal" and "alternate".
+ * Normal will repeat the animation, alternate will reverse on every other pass.
+ *
+ * @attribute direction
+ * @type String
+ * @default "normal"
+ */
+ direction: {
+ value: 'normal' // | alternate (fwd on odd, rev on even per spec)
+ },
+
+ /**
+ * Whether or not the animation is currently paused.
+ * @attribute paused
+ * @type Boolean
+ * @default false
+ * @readOnly
+ */
+ paused: {
+ readOnly: true,
+ value: false
+ },
+
+ /**
+ * If true, animation begins from last frame
+ * @attribute reverse
+ * @type Boolean
+ * @default false
+ */
+ reverse: {
+ value: false
+ }
+
+
+ };
+
+ /**
+ * Runs all animation instances.
+ * @method run
+ * @static
+ */
+ Y.Anim.run = function() {
+ for (var i in _instances) {
+ if (_instances[i].run) {
+ _instances[i].run();
+ }
+ }
+ };
+
+ /**
+ * Pauses all animation instances.
+ * @method pause
+ * @static
+ */
+ Y.Anim.pause = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].pause) {
+ _running[i].pause();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ /**
+ * Stops all animation instances.
+ * @method stop
+ * @static
+ */
+ Y.Anim.stop = function() {
+ for (var i in _running) { // stop timer if nothing running
+ if (_running[i].stop) {
+ _running[i].stop();
+ }
+ }
+ Y.Anim._stopTimer();
+ };
+
+ Y.Anim._startTimer = function() {
+ if (!_timer) {
+ _timer = setInterval(Y.Anim._runFrame, 1);
+ }
+ };
+
+ Y.Anim._stopTimer = function() {
+ clearInterval(_timer);
+ _timer = 0;
+ };
+
+ /**
+ * Called per Interval to handle each animation frame.
+ * @method _runFrame
+ * @private
+ * @static
+ */
+ Y.Anim._runFrame = function() {
+ var done = true;
+ for (var anim in _running) {
+ if (_running[anim]._runFrame) {
+ done = false;
+ _running[anim]._runFrame();
+ }
+ }
+
+ if (done) {
+ Y.Anim._stopTimer();
+ }
+ };
+
+ Y.Anim.RE_UNITS = /^(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)*$/;
+
+ var proto = {
+ /**
+ * Starts or resumes an animation.
+ * percent start time marker.
+ * @method run
+ * @chainable
+ */
+ run: function() {
+ if (!this.get(RUNNING)) {
+ this._start();
+ } else if (this.get(PAUSED)) {
+ this._resume();
+ }
+ return this;
+ },
+
+ /**
+ * Pauses the animation and
+ * freezes it in its current state and time.
+ * Calling run() will continue where it left off.
+ * @method pause
+ * @chainable
+ */
+ pause: function() {
+ if (this.get(RUNNING)) {
+ this._pause();
+ }
+ return this;
+ },
+
+ /**
+ * Stops the animation and resets its time.
+ * @method stop
+ * @chainable
+ */
+ stop: function(finish) {
+ if (this.get(RUNNING) || this.get(PAUSED)) {
+ this._end(finish);
+ }
+ return this;
+ },
+
+ _added: false,
+
+ _start: function() {
+ this._set(START_TIME, new Date() - this.get(ELAPSED_TIME));
+ this._actualFrames = 0;
+ if (!this.get(PAUSED)) {
+ this._initAnimAttr();
+ }
+ _running[Y.stamp(this)] = this;
+ Y.Anim._startTimer();
+
+ this.fire(START);
+ },
+
+ _pause: function() {
+ this._set(START_TIME, null);
+ this._set(PAUSED, true);
+ delete _running[Y.stamp(this)];
+
+ /**
+ * @event pause
+ * @description fires when an animation is paused.
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('pause');
+ },
+
+ _resume: function() {
+ this._set(PAUSED, false);
+ _running[Y.stamp(this)] = this;
+
+ /**
+ * @event resume
+ * @description fires when an animation is resumed (run from pause).
+ * @param {Event} ev The pause event.
+ * @type Event.Custom
+ */
+ this.fire('resume');
+ },
+
+ _end: function(finish) {
+ this._set(START_TIME, null);
+ this._set(ELAPSED_TIME, 0);
+ this._set(PAUSED, false);
+
+ delete _running[Y.stamp(this)];
+ this.fire(END, {elapsed: this.get(ELAPSED_TIME)});
+ },
+
+ _runFrame: function() {
+ var attr = this._runtimeAttr,
+ customAttr = Y.Anim.behaviors,
+ easing = attr.easing,
+ d = attr.duration,
+ t = new Date() - this.get(START_TIME),
+ reversed = this.get(REVERSE),
+ done = (t >= d),
+ lastFrame = d,
+ attribute,
+ setter;
+
+ if (reversed) {
+ t = d - t;
+ done = (t <= 0);
+ lastFrame = 0;
+ }
+
+ for (var i in attr) {
+ if (attr[i].to) {
+ attribute = attr[i];
+ setter = (i in customAttr && 'set' in customAttr[i]) ?
+ customAttr[i].set : Y.Anim.DEFAULT_SETTER;
+
+ if (!done) {
+ setter(this, i, attribute.from, attribute.to, t, d, easing, attribute.unit);
+ } else { // ensure final frame value is set
+ // TODO: handle keyframes
+ setter(this, i, attribute.from, attribute.to, lastFrame, d, easing, attribute.unit);
+ }
+ }
+ }
+
+ this._actualFrames += 1;
+ this._set(ELAPSED_TIME, t);
+
+ this.fire(TWEEN);
+ if (done) {
+ this._lastFrame();
+ }
+ },
+
+ _lastFrame: function() {
+ var iter = this.get('iterations'),
+ iterCount = this.get(ITERATION_COUNT);
+
+ iterCount += 1;
+ if (iter === 'infinite' || iterCount < iter) {
+ if (this.get('direction') === 'alternate') {
+ this.set(REVERSE, !this.get(REVERSE)); // flip it
+ }
+ /**
+ * @event iteration
+ * @description fires when an animation begins an iteration.
+ * @param {Event} ev The iteration event.
+ * @type Event.Custom
+ */
+ this.fire('iteration');
+ } else {
+ iterCount = 0;
+ this._end();
+ }
+
+ this._set(START_TIME, new Date());
+ this._set(ITERATION_COUNT, iterCount);
+ },
+
+ _initAnimAttr: function() {
+ var from = this.get('from') || {},
+ to = this.get('to') || {},
+ dur = this.get('duration') * 1000,
+ node = this.get(NODE),
+ easing = this.get('easing') || {},
+ attr = {},
+ customAttr = Y.Anim.behaviors,
+ unit, begin, end;
+
+ Y.each(to, function(val, name) {
+ if (typeof val === 'function') {
+ val = val.call(this, node);
+ }
+
+ begin = from[name];
+ if (begin === undefined) {
+ begin = (name in customAttr && 'get' in customAttr[name]) ?
+ customAttr[name].get(this, name) : Y.Anim.DEFAULT_GETTER(this, name);
+ } else if (typeof begin === 'function') {
+ begin = begin.call(this, node);
+ }
+
+ var mFrom = Y.Anim.RE_UNITS.exec(begin);
+ var mTo = Y.Anim.RE_UNITS.exec(val);
+
+ begin = mFrom ? mFrom[1] : begin;
+ end = mTo ? mTo[1] : val;
+ unit = mTo ? mTo[2] : mFrom ? mFrom[2] : ''; // one might be zero TODO: mixed units
+
+ if (!unit && Y.Anim.RE_DEFAULT_UNIT.test(name)) {
+ unit = Y.Anim.DEFAULT_UNIT;
+ }
+
+ if (!begin || !end) {
+ Y.error('invalid "from" or "to" for "' + name + '"', 'Anim');
+ return;
+ }
+
+ attr[name] = {
+ from: begin,
+ to: end,
+ unit: unit
+ };
+
+ attr.duration = dur;
+ attr.easing = easing;
+
+ }, this);
+
+ this._runtimeAttr = attr;
+ },
+
+
+ // TODO: move to computedStyle? (browsers dont agree on default computed offsets)
+ _getOffset: function(attr) {
+ var node = this._node,
+ val = node.getComputedStyle(attr),
+ get = (attr === 'left') ? 'getX': 'getY',
+ set = (attr === 'left') ? 'setX': 'setY';
+
+ if (val === 'auto') {
+ var position = node.getStyle('position');
+ if (position === 'absolute' || position === 'fixed') {
+ val = node[get]();
+ node[set](val);
+ } else {
+ val = 0;
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.extend(Y.Anim, Y.Base, proto);
+
+
+}, '3.0.0' ,{requires:['base-base', 'node-style']});
+YUI.add('anim-color', function(Y) {
+
+/**
+ * Adds support for color properties in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-color
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.color = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = Y.Color.re_RGB.exec(Y.Color.toRGB(from));
+ to = Y.Color.re_RGB.exec(Y.Color.toRGB(to));
+
+ if (!from || from.length < 3 || !to || to.length < 3) {
+ Y.error('invalid from or to passed to color behavior');
+ }
+
+ anim._node.setStyle(att, 'rgb(' + [
+ Math.floor(fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)),
+ Math.floor(fn(elapsed, NUM(from[2]), NUM(to[2]) - NUM(from[2]), duration)),
+ Math.floor(fn(elapsed, NUM(from[3]), NUM(to[3]) - NUM(from[3]), duration))
+ ].join(', ') + ')');
+ },
+
+ // TODO: default bgcolor const
+ get: function(anim, att) {
+ var val = anim._node.getComputedStyle(att);
+ val = (val === 'transparent') ? 'rgb(255, 255, 255)' : val;
+ return val;
+ }
+};
+
+Y.each(['backgroundColor',
+ 'borderColor',
+ 'borderTopColor',
+ 'borderRightColor',
+ 'borderBottomColor',
+ 'borderLeftColor'],
+ function(v, i) {
+ Y.Anim.behaviors[v] = Y.Anim.behaviors.color;
+ }
+);
+
+
+}, '3.0.0' ,{requires:['anim-base']});
+YUI.add('anim-curve', function(Y) {
+
+/**
+ * Adds support for the <code>curve</code> property for the <code>to</code>
+ * attribute. A curve is zero or more control points and an end point.
+ * @module anim
+ * @submodule anim-curve
+ */
+
+Y.Anim.behaviors.curve = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ from = from.slice.call(from);
+ to = to.slice.call(to);
+ var t = fn(elapsed, 0, 100, duration) / 100;
+ to.unshift(from);
+ anim._node.setXY(Y.Anim.getBezier(to, t));
+ },
+
+ get: function(anim, att) {
+ return anim._node.getXY();
+ }
+};
+
+/**
+ * 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.
+ * @for Anim
+ * @method getBezier
+ * @static
+ * @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
+ */
+Y.Anim.getBezier = 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] ];
+
+};
+
+
+}, '3.0.0' ,{requires:['anim-xy']});
+YUI.add('anim-easing', function(Y) {
+
+/*
+TERMS OF USE - EASING EQUATIONS
+Open source under the BSD License.
+Copyright 2001 Robert Penner All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * The easing module provides methods for customizing
+ * how an animation behaves during each run.
+ * @class Easing
+ * @module anim
+ * @submodule anim-easing
+ */
+
+Y.Easing = {
+
+ /**
+ * Uniform speed between points.
+ * @for Easing
+ * @method easeNone
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeNone: function (t, b, c, d) {
+ return c*t/d + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quadratic)
+ * @method easeIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeIn: function (t, b, c, d) {
+ return c*(t/=d)*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quadratic)
+ * @method easeOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOut: function (t, b, c, d) {
+ return -c *(t/=d)*(t-2) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quadratic)
+ * @method easeBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBoth: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t + b;
+ }
+
+ return -c/2 * ((--t)*(t-2) - 1) + b;
+ },
+
+ /**
+ * Begins slowly and accelerates towards end. (quartic)
+ * @method easeInStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeInStrong: function (t, b, c, d) {
+ return c*(t/=d)*t*t*t + b;
+ },
+
+ /**
+ * Begins quickly and decelerates towards end. (quartic)
+ * @method easeOutStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeOutStrong: function (t, b, c, d) {
+ return -c * ((t=t/d-1)*t*t*t - 1) + b;
+ },
+
+ /**
+ * Begins slowly and decelerates towards end. (quartic)
+ * @method easeBothStrong
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ easeBothStrong: function (t, b, c, d) {
+ if ((t/=d/2) < 1) {
+ return c/2*t*t*t*t + b;
+ }
+
+ return -c/2 * ((t-=2)*t*t*t - 2) + b;
+ },
+
+ /**
+ * Snap in elastic effect.
+ * @method elasticIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+
+ elasticIn: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p = d* 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ },
+
+ /**
+ * Snap out elastic effect.
+ * @method elasticOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticOut: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+ if ( (t /= d) === 1 ) {
+ return b+c;
+ }
+ if (!p) {
+ p=d * 0.3;
+ }
+
+ if (!a || a < Math.abs(c)) {
+ a = c;
+ s = p / 4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
+ },
+
+ /**
+ * Snap both elastic effect.
+ * @method elasticBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} a Amplitude (optional)
+ * @param {Number} p Period (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ elasticBoth: function (t, b, c, d, a, p) {
+ var s;
+ if (t === 0) {
+ return b;
+ }
+
+ if ( (t /= d/2) === 2 ) {
+ return b+c;
+ }
+
+ if (!p) {
+ p = d*(0.3*1.5);
+ }
+
+ if ( !a || a < Math.abs(c) ) {
+ a = c;
+ s = p/4;
+ }
+ else {
+ s = p/(2*Math.PI) * Math.asin (c/a);
+ }
+
+ if (t < 1) {
+ return -0.5*(a*Math.pow(2,10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
+ }
+ return a*Math.pow(2,-10*(t-=1)) *
+ Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
+ },
+
+
+ /**
+ * Backtracks slightly, then reverses direction and moves to end.
+ * @method backIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backIn: function (t, b, c, d, s) {
+ if (s === undefined) {
+ s = 1.70158;
+ }
+ if (t === d) {
+ t -= 0.001;
+ }
+ return c*(t/=d)*t*((s+1)*t - s) + b;
+ },
+
+ /**
+ * Overshoots end, then reverses and comes back to end.
+ * @method backOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backOut: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+ return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
+ },
+
+ /**
+ * Backtracks slightly, then reverses direction, overshoots end,
+ * then reverses and comes back to end.
+ * @method backBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @param {Number} s Overshoot (optional)
+ * @return {Number} The computed value for the current animation frame
+ */
+ backBoth: function (t, b, c, d, s) {
+ if (typeof s === 'undefined') {
+ s = 1.70158;
+ }
+
+ if ((t /= d/2 ) < 1) {
+ return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
+ }
+ return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
+ },
+
+ /**
+ * Bounce off of start.
+ * @method bounceIn
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceIn: function (t, b, c, d) {
+ return c - Y.Easing.bounceOut(d-t, 0, c, d) + b;
+ },
+
+ /**
+ * Bounces off end.
+ * @method bounceOut
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceOut: function (t, b, c, d) {
+ if ((t/=d) < (1/2.75)) {
+ return c*(7.5625*t*t) + b;
+ } else if (t < (2/2.75)) {
+ return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
+ } else if (t < (2.5/2.75)) {
+ return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
+ }
+ return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
+ },
+
+ /**
+ * Bounces off start and end.
+ * @method bounceBoth
+ * @param {Number} t Time value used to compute current value
+ * @param {Number} b Starting value
+ * @param {Number} c Delta between start and end values
+ * @param {Number} d Total length of animation
+ * @return {Number} The computed value for the current animation frame
+ */
+ bounceBoth: function (t, b, c, d) {
+ if (t < d/2) {
+ return Y.Easing.bounceIn(t * 2, 0, c, d) * 0.5 + b;
+ }
+ return Y.Easing.bounceOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b;
+ }
+};
+
+
+}, '3.0.0' ,{requires:['anim-base']});
+YUI.add('anim-node-plugin', function(Y) {
+
+/**
+ * Binds an Anim instance to a Node instance
+ * @module anim
+ * @class Plugin.NodeFX
+ * @extends Base
+ * @submodule anim-node-plugin
+ */
+
+var NodeFX = function(config) {
+ config = (config) ? Y.merge(config) : {};
+ config.node = config.host;
+ NodeFX.superclass.constructor.apply(this, arguments);
+};
+
+NodeFX.NAME = "nodefx";
+NodeFX.NS = "fx";
+
+Y.extend(NodeFX, Y.Anim);
+
+Y.namespace('Plugin');
+Y.Plugin.NodeFX = NodeFX;
+
+
+}, '3.0.0' ,{requires:['node-pluginhost', 'anim-base']});
+YUI.add('anim-scroll', function(Y) {
+
+/**
+ * Adds support for the <code>scroll</code> property in <code>to</code>
+ * and <code>from</code> attributes.
+ * @module anim
+ * @submodule anim-scroll
+ */
+
+var NUM = Number;
+
+//TODO: deprecate for scrollTop/Left properties?
+Y.Anim.behaviors.scroll = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ var
+ node = anim._node,
+ val = ([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+
+ if (val[0]) {
+ node.set('scrollLeft', val[0]);
+ }
+
+ if (val[1]) {
+ node.set('scrollTop', val[1]);
+ }
+ },
+ get: function(anim) {
+ var node = anim._node;
+ return [node.get('scrollLeft'), node.get('scrollTop')];
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base']});
+YUI.add('anim-xy', function(Y) {
+
+/**
+ * Adds support for the <code>xy</code> property in <code>from</code> and
+ * <code>to</code> attributes.
+ * @module anim
+ * @submodule anim-xy
+ */
+
+var NUM = Number;
+
+Y.Anim.behaviors.xy = {
+ set: function(anim, att, from, to, elapsed, duration, fn) {
+ anim._node.setXY([
+ fn(elapsed, NUM(from[0]), NUM(to[0]) - NUM(from[0]), duration),
+ fn(elapsed, NUM(from[1]), NUM(to[1]) - NUM(from[1]), duration)
+ ]);
+ },
+ get: function(anim) {
+ return anim._node.getXY();
+ }
+};
+
+
+
+}, '3.0.0' ,{requires:['anim-base', 'node-screen']});
+
+
+YUI.add('anim', function(Y){}, '3.0.0' ,{use:['anim-base', 'anim-color', 'anim-curve', 'anim-easing', 'anim-node-plugin', 'anim-scroll', 'anim-xy'], skinnable:false});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-console-ft .yui-console-filters-categories,.yui-skin-sam .yui-console-ft .yui-console-filters-sources{text-align:left;padding:5px 0;border:1px inset;margin:0 2px;}.yui-skin-sam .yui-console-ft .yui-console-filters-categories{background:#fff;border-bottom:2px ridge;}.yui-skin-sam .yui-console-ft .yui-console-filters-sources{background:#fff;margin-bottom:2px;border-top:0 none;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-moz-border-radius-bottomright:10px;-moz-border-radius-bottomleft:10px;-webkit-border-bottom-right-radius:10px;-webkit-border-bottom-left-radius:10px;}.yui-skin-sam .yui-console-filter-label{white-space:nowrap;margin-left:1ex;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-separate-console{position:absolute;right:1em;top:1em;z-index:999;}.yui-skin-sam .yui-inline-console{display:-moz-inline-stack;display:inline-block;*display:inline;zoom:1;vertical-align:top;}.yui-skin-sam .yui-inline-console .yui-console-content{position:relative;}.yui-skin-sam .yui-console-content{background:#777;_background:#D8D8DA url(bg.png) repeat-x 0 0;font:normal 13px/1.3 Arial,sans-serif;text-align:left;border:1px solid #777;border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;}.yui-skin-sam .yui-console-hd,.yui-skin-sam .yui-console-bd,.yui-skin-sam .yui-console-ft{position:relative;}.yui-skin-sam .yui-console-hd,.yui-skin-sam .yui-console-ft .yui-console-controls{text-align:right;}.yui-skin-sam .yui-console-hd{background:#D8D8DA url(bg.png) repeat-x 0 0;padding:1ex;border:1px solid transparent;_border:0 none;border-top-right-radius:10px;border-top-left-radius:10px;-moz-border-radius-topright:10px;-moz-border-radius-topleft:10px;-webkit-border-top-right-radius:10px;-webkit-border-top-left-radius:10px;}.yui-skin-sam .yui-console-bd{background:#fff;border-top:1px solid #777;border-bottom:1px solid #777;color:#000;font-size:11px;overflow:auto;overflow-x:auto;overflow-y:scroll;_width:100%;}.yui-skin-sam .yui-console-ft{background:#D8D8DA url(bg.png) repeat-x 0 0;border:1px solid transparent;_border:0 none;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-moz-border-radius-bottomright:10px;-moz-border-radius-bottomleft:10px;-webkit-border-bottom-right-radius:10px;-webkit-border-bottom-left-radius:10px;}.yui-skin-sam .yui-console-controls{padding:4px 1ex;zoom:1;}.yui-skin-sam .yui-console-title{color:#000;display:inline;float:left;font-weight:bold;font-size:13px;height:24px;line-height:24px;margin:0;padding-left:1ex;}.yui-skin-sam .yui-console-pause-label{float:left;}.yui-skin-sam .yui-console-button{line-height:1.3;}.yui-skin-sam .yui-console-collapsed .yui-console-bd,.yui-skin-sam .yui-console-collapsed .yui-console-ft{display:none;}.yui-skin-sam .yui-console-content.yui-console-collapsed{-webkit-border-radius:0;}.yui-skin-sam .yui-console-collapsed .yui-console-hd{border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:0;}.yui-skin-sam .yui-console-entry{border-bottom:1px solid #aaa;min-height:32px;_height:32px;}.yui-skin-sam .yui-console-entry-meta{margin:0;overflow:hidden;}.yui-skin-sam .yui-console-entry-content{margin:0;padding:0 1ex;white-space:pre-wrap;word-wrap:break-word;}.yui-skin-sam .yui-console-entry-meta .yui-console-entry-src{color:#000;font-style:italic;font-weight:bold;float:right;margin:2px 5px 0 0;}.yui-skin-sam .yui-console-entry-meta .yui-console-entry-time{color:#777;padding-left:1ex;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-time{color:#555;}.yui-skin-sam .yui-console-entry-info .yui-console-entry-meta .yui-console-entry-cat,.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-cat,.yui-skin-sam .yui-console-entry-error .yui-console-entry-meta .yui-console-entry-cat{display:none;}.yui-skin-sam .yui-console-entry-warn{background:#aee url(warn_error.png) no-repeat -15px 15px;}.yui-skin-sam .yui-console-entry-error{background:#ffa url(warn_error.png) no-repeat 5px -24px;color:#900;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-content,.yui-skin-sam .yui-console-entry-error .yui-console-entry-content{padding-left:24px;}.yui-skin-sam .yui-console-entry-cat{text-transform:uppercase;padding:1px 4px;background-color:#ccc;}.yui-skin-sam .yui-console-entry-info .yui-console-entry-cat{background-color:#ac2;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-cat{background-color:#e81;}.yui-skin-sam .yui-console-entry-error .yui-console-entry-cat{background-color:#b00;color:#fff;}.yui-skin-sam .yui-console-hidden{display:none;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-menu .yui-menu{position:absolute;z-index:1;}.yui-menu .yui-shim{position:absolute;top:0;left:0;z-index:-1;opacity:0;filter:alpha(opacity=0);border:none;margin:0;padding:0;height:100%;width:100%;}.yui-menu-hidden{top:-10000px;left:-10000px;visibility:hidden;}.yui-menu li{list-style-type:none;}.yui-menu ul,.yui-menu li{margin:0;padding:0;}.yui-menu-label,.yui-menuitem-content{text-align:left;white-space:nowrap;display:block;}.yui-menu-horizontal li{float:left;width:auto;}.yui-menu-horizontal li li{float:none;}.yui-menu-horizontal ul{*zoom:1;}.yui-menu-horizontal ul ul{*zoom:normal;}.yui-menu-horizontal>.yui-menu-content>ul:after{content:"";display:block;clear:both;line-height:0;font-size:0;visibility:hidden;}.yui-menu-content{*zoom:1;}.yui-menu-hidden .yui-menu-content{*zoom:normal;}.yui-menuitem-content,.yui-menu-label{_zoom:1;}.yui-menu-hiden .yui-menuitem-content,.yui-menu-hiden .yui-menu-label{_zoom:normal;}.yui-skin-sam .yui-menu-content,.yui-skin-sam .yui-menu .yui-menu .yui-menu-content{font-size:93%;line-height:1.5;*line-height:1.45;border:solid 1px #808080;background:#fff;padding:3px 0;}.yui-skin-sam .yui-menu .yui-menu .yui-menu-content{font-size:100%;}.yui-skin-sam .yui-menu-horizontal .yui-menu-content{line-height:2;*line-height:1.9;background:url(sprite.png) repeat-x 0 0;padding:0;}.yui-skin-sam .yui-menu ul,.yui-skin-sam .yui-menu ul ul{margin-top:3px;padding-top:3px;border-top:solid 1px #ccc;}.yui-skin-sam .yui-menu ul.first-of-type{border:0;margin:0;padding:0;}.yui-skin-sam .yui-menu-horizontal ul{padding:0;margin:0;border:0;}.yui-skin-sam .yui-menu li,.yui-skin-sam .yui-menu .yui-menu li{_border-bottom:solid 1px #fff;}.yui-skin-sam .yui-menu-horizontal li{_border-bottom:0;}.yui-skin-sam .yui-menubuttonnav li{border-right:solid 1px #ccc;}.yui-skin-sam .yui-splitbuttonnav li{border-right:solid 1px #808080;}.yui-skin-sam .yui-menubuttonnav li li,.yui-skin-sam .yui-splitbuttonnav li li{border-right:0;}.yui-skin-sam .yui-menu-label,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label,.yui-skin-sam .yui-menuitem-content,.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-content{padding:0 20px;color:#000;text-decoration:none;cursor:default;float:none;border:0;margin:0;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label,.yui-skin-sam .yui-menu-horizontal .yui-menuitem-content{padding:0 10px;border-style:solid;border-color:#808080;border-width:1px 0;margin:-1px 0;float:left;width:auto;}.yui-skin-sam .yui-menu-label,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label{background:url(vertical-menu-submenu-indicator.png) right center no-repeat;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label{background:url(sprite.png) repeat-x 0 0;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label{background-image:none;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label{padding-right:0;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label em{font-style:normal;padding-right:20px;display:block;background:url(horizontal-menu-submenu-indicator.png) right center no-repeat;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label{padding:0;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label a{float:left;width:auto;color:#000;text-decoration:none;cursor:default;padding:0 5px 0 10px;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label .yui-menu-toggle{padding:0;border-left:solid 1px #ccc;width:15px;overflow:hidden;text-indent:-1000px;background:url(horizontal-menu-submenu-indicator.png) 3px center no-repeat;}.yui-skin-sam .yui-menu-label-active,.yui-skin-sam .yui-menu-label-menuvisible,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-active,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-menuvisible{background-color:#B3D4FF;}.yui-skin-sam .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-active .yui-menuitem-content{background-image:none;background-color:#B3D4FF;border-left-width:0;margin-left:0;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label-active,.yui-skin-sam .yui-menu-horizontal .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menu-horizontal .yui-menu-label-menuvisible{border-color:#7D98B8;background:url(sprite.png) repeat-x 0 -1700px;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label-active,.yui-skin-sam .yui-menubuttonnav .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menubuttonnav .yui-menu-label-menuvisible,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-active,.yui-skin-sam .yui-splitbuttonnav .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible{border-left-width:1px;margin-left:-1px;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible{border-color:#808080;background:transparent;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible .yui-menu-toggle{border-color:#7D98B8;background:url(horizontal-menu-submenu-toggle.png) left center no-repeat;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-overlay{position:absolute;}.yui-overlay-hidden{visibility:hidden;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-console-ft .yui-console-filters-categories,.yui-skin-sam .yui-console-ft .yui-console-filters-sources{text-align:left;padding:5px 0;border:1px inset;margin:0 2px;}.yui-skin-sam .yui-console-ft .yui-console-filters-categories{background:#fff;border-bottom:2px ridge;}.yui-skin-sam .yui-console-ft .yui-console-filters-sources{background:#fff;margin-bottom:2px;border-top:0 none;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-moz-border-radius-bottomright:10px;-moz-border-radius-bottomleft:10px;-webkit-border-bottom-right-radius:10px;-webkit-border-bottom-left-radius:10px;}.yui-skin-sam .yui-console-filter-label{white-space:nowrap;margin-left:1ex;}
+.yui-skin-sam .yui-separate-console{position:absolute;right:1em;top:1em;z-index:999;}.yui-skin-sam .yui-inline-console{display:-moz-inline-stack;display:inline-block;*display:inline;zoom:1;vertical-align:top;}.yui-skin-sam .yui-inline-console .yui-console-content{position:relative;}.yui-skin-sam .yui-console-content{background:#777;_background:#D8D8DA url(bg.png) repeat-x 0 0;font:normal 13px/1.3 Arial,sans-serif;text-align:left;border:1px solid #777;border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;}.yui-skin-sam .yui-console-hd,.yui-skin-sam .yui-console-bd,.yui-skin-sam .yui-console-ft{position:relative;}.yui-skin-sam .yui-console-hd,.yui-skin-sam .yui-console-ft .yui-console-controls{text-align:right;}.yui-skin-sam .yui-console-hd{background:#D8D8DA url(bg.png) repeat-x 0 0;padding:1ex;border:1px solid transparent;_border:0 none;border-top-right-radius:10px;border-top-left-radius:10px;-moz-border-radius-topright:10px;-moz-border-radius-topleft:10px;-webkit-border-top-right-radius:10px;-webkit-border-top-left-radius:10px;}.yui-skin-sam .yui-console-bd{background:#fff;border-top:1px solid #777;border-bottom:1px solid #777;color:#000;font-size:11px;overflow:auto;overflow-x:auto;overflow-y:scroll;_width:100%;}.yui-skin-sam .yui-console-ft{background:#D8D8DA url(bg.png) repeat-x 0 0;border:1px solid transparent;_border:0 none;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-moz-border-radius-bottomright:10px;-moz-border-radius-bottomleft:10px;-webkit-border-bottom-right-radius:10px;-webkit-border-bottom-left-radius:10px;}.yui-skin-sam .yui-console-controls{padding:4px 1ex;zoom:1;}.yui-skin-sam .yui-console-title{color:#000;display:inline;float:left;font-weight:bold;font-size:13px;height:24px;line-height:24px;margin:0;padding-left:1ex;}.yui-skin-sam .yui-console-pause-label{float:left;}.yui-skin-sam .yui-console-button{line-height:1.3;}.yui-skin-sam .yui-console-collapsed .yui-console-bd,.yui-skin-sam .yui-console-collapsed .yui-console-ft{display:none;}.yui-skin-sam .yui-console-content.yui-console-collapsed{-webkit-border-radius:0;}.yui-skin-sam .yui-console-collapsed .yui-console-hd{border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:0;}.yui-skin-sam .yui-console-entry{border-bottom:1px solid #aaa;min-height:32px;_height:32px;}.yui-skin-sam .yui-console-entry-meta{margin:0;overflow:hidden;}.yui-skin-sam .yui-console-entry-content{margin:0;padding:0 1ex;white-space:pre-wrap;word-wrap:break-word;}.yui-skin-sam .yui-console-entry-meta .yui-console-entry-src{color:#000;font-style:italic;font-weight:bold;float:right;margin:2px 5px 0 0;}.yui-skin-sam .yui-console-entry-meta .yui-console-entry-time{color:#777;padding-left:1ex;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-time{color:#555;}.yui-skin-sam .yui-console-entry-info .yui-console-entry-meta .yui-console-entry-cat,.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-cat,.yui-skin-sam .yui-console-entry-error .yui-console-entry-meta .yui-console-entry-cat{display:none;}.yui-skin-sam .yui-console-entry-warn{background:#aee url(warn_error.png) no-repeat -15px 15px;}.yui-skin-sam .yui-console-entry-error{background:#ffa url(warn_error.png) no-repeat 5px -24px;color:#900;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-content,.yui-skin-sam .yui-console-entry-error .yui-console-entry-content{padding-left:24px;}.yui-skin-sam .yui-console-entry-cat{text-transform:uppercase;padding:1px 4px;background-color:#ccc;}.yui-skin-sam .yui-console-entry-info .yui-console-entry-cat{background-color:#ac2;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-cat{background-color:#e81;}.yui-skin-sam .yui-console-entry-error .yui-console-entry-cat{background-color:#b00;color:#fff;}.yui-skin-sam .yui-console-hidden{display:none;}
+.yui-menu .yui-menu{position:absolute;z-index:1;}.yui-menu .yui-shim{position:absolute;top:0;left:0;z-index:-1;opacity:0;filter:alpha(opacity=0);border:none;margin:0;padding:0;height:100%;width:100%;}.yui-menu-hidden{top:-10000px;left:-10000px;visibility:hidden;}.yui-menu li{list-style-type:none;}.yui-menu ul,.yui-menu li{margin:0;padding:0;}.yui-menu-label,.yui-menuitem-content{text-align:left;white-space:nowrap;display:block;}.yui-menu-horizontal li{float:left;width:auto;}.yui-menu-horizontal li li{float:none;}.yui-menu-horizontal ul{*zoom:1;}.yui-menu-horizontal ul ul{*zoom:normal;}.yui-menu-horizontal>.yui-menu-content>ul:after{content:"";display:block;clear:both;line-height:0;font-size:0;visibility:hidden;}.yui-menu-content{*zoom:1;}.yui-menu-hidden .yui-menu-content{*zoom:normal;}.yui-menuitem-content,.yui-menu-label{_zoom:1;}.yui-menu-hiden .yui-menuitem-content,.yui-menu-hiden .yui-menu-label{_zoom:normal;}.yui-skin-sam .yui-menu-content,.yui-skin-sam .yui-menu .yui-menu .yui-menu-content{font-size:93%;line-height:1.5;*line-height:1.45;border:solid 1px #808080;background:#fff;padding:3px 0;}.yui-skin-sam .yui-menu .yui-menu .yui-menu-content{font-size:100%;}.yui-skin-sam .yui-menu-horizontal .yui-menu-content{line-height:2;*line-height:1.9;background:url(sprite.png) repeat-x 0 0;padding:0;}.yui-skin-sam .yui-menu ul,.yui-skin-sam .yui-menu ul ul{margin-top:3px;padding-top:3px;border-top:solid 1px #ccc;}.yui-skin-sam .yui-menu ul.first-of-type{border:0;margin:0;padding:0;}.yui-skin-sam .yui-menu-horizontal ul{padding:0;margin:0;border:0;}.yui-skin-sam .yui-menu li,.yui-skin-sam .yui-menu .yui-menu li{_border-bottom:solid 1px #fff;}.yui-skin-sam .yui-menu-horizontal li{_border-bottom:0;}.yui-skin-sam .yui-menubuttonnav li{border-right:solid 1px #ccc;}.yui-skin-sam .yui-splitbuttonnav li{border-right:solid 1px #808080;}.yui-skin-sam .yui-menubuttonnav li li,.yui-skin-sam .yui-splitbuttonnav li li{border-right:0;}.yui-skin-sam .yui-menu-label,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label,.yui-skin-sam .yui-menuitem-content,.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-content{padding:0 20px;color:#000;text-decoration:none;cursor:default;float:none;border:0;margin:0;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label,.yui-skin-sam .yui-menu-horizontal .yui-menuitem-content{padding:0 10px;border-style:solid;border-color:#808080;border-width:1px 0;margin:-1px 0;float:left;width:auto;}.yui-skin-sam .yui-menu-label,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label{background:url(vertical-menu-submenu-indicator.png) right center no-repeat;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label{background:url(sprite.png) repeat-x 0 0;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label{background-image:none;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label{padding-right:0;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label em{font-style:normal;padding-right:20px;display:block;background:url(horizontal-menu-submenu-indicator.png) right center no-repeat;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label{padding:0;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label a{float:left;width:auto;color:#000;text-decoration:none;cursor:default;padding:0 5px 0 10px;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label .yui-menu-toggle{padding:0;border-left:solid 1px #ccc;width:15px;overflow:hidden;text-indent:-1000px;background:url(horizontal-menu-submenu-indicator.png) 3px center no-repeat;}.yui-skin-sam .yui-menu-label-active,.yui-skin-sam .yui-menu-label-menuvisible,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-active,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-menuvisible{background-color:#B3D4FF;}.yui-skin-sam .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-active .yui-menuitem-content{background-image:none;background-color:#B3D4FF;border-left-width:0;margin-left:0;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label-active,.yui-skin-sam .yui-menu-horizontal .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menu-horizontal .yui-menu-label-menuvisible{border-color:#7D98B8;background:url(sprite.png) repeat-x 0 -1700px;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label-active,.yui-skin-sam .yui-menubuttonnav .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menubuttonnav .yui-menu-label-menuvisible,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-active,.yui-skin-sam .yui-splitbuttonnav .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible{border-left-width:1px;margin-left:-1px;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible{border-color:#808080;background:transparent;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible .yui-menu-toggle{border-color:#7D98B8;background:url(horizontal-menu-submenu-toggle.png) left center no-repeat;}
+.yui-overlay{position:absolute;}.yui-overlay-hidden{visibility:hidden;}
+.yui-slider{display:-moz-inline-stack;display:inline-block;*display:inline;zoom:1;vertical-align:middle;}.yui-slider-content{position:relative;}.yui-slider-rail{position:relative;}.yui-slider-thumb{position:absolute;}.yui-slider-thumb-image{display:block;}.yui-slider-image-error .yui-slider-thumb{height:10px;width:10px;background:#000;color:#000;overflow:hidden;}.yui-slider-image-error .yui-slider-thumb-image{display:none;}.yui-skin-sam .yui-slider-rail-x{background:url("rail-classic-x.png") repeat-x 0 7px;min-height:19px;*height:19px;}.yui-skin-sam .yui-slider-rail-y{background:url("rail-classic-y.png") repeat-y 7px 0;min-width:19px;*width:19px;}
+.yui-widget-stacked .yui-widget-shim{opacity:0;filter:alpha(opacity=0);position:absolute;border:none;top:0;left:0;padding:0;margin:0;z-index:-1;width:100%;height:100%;_width:0;_height:0;}
+.yui-widget-hidden{display:none;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-slider{display:-moz-inline-stack;display:inline-block;*display:inline;zoom:1;vertical-align:middle;}.yui-slider-content{position:relative;}.yui-slider-rail{position:relative;}.yui-slider-thumb{position:absolute;}.yui-slider-thumb-image{display:block;}.yui-slider-image-error .yui-slider-thumb{height:10px;width:10px;background:#000;color:#000;overflow:hidden;}.yui-slider-image-error .yui-slider-thumb-image{display:none;}.yui-skin-sam .yui-slider-rail-x{background:url("rail-classic-x.png") repeat-x 0 7px;min-height:19px;*height:19px;}.yui-skin-sam .yui-slider-rail-y{background:url("rail-classic-y.png") repeat-y 7px 0;min-width:19px;*width:19px;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-widget-stacked .yui-widget-shim{opacity:0;filter:alpha(opacity=0);position:absolute;border:none;top:0;left:0;padding:0;margin:0;z-index:-1;width:100%;height:100%;_width:0;_height:0;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-widget-hidden{display:none;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('async-queue', function(Y) {
+
+/**
+ * <p>AsyncQueue allows you create a chain of function callbacks executed
+ * via setTimeout (or synchronously) that are guaranteed to run in order.
+ * Items in the queue can be promoted or removed. Start or resume the
+ * execution chain with run(). pause() to temporarily delay execution, or
+ * stop() to halt and clear the queue.</p>
+ *
+ * @module async-queue
+ */
+
+/**
+ * <p>A specialized queue class that supports scheduling callbacks to execute
+ * sequentially, iteratively, even asynchronously.</p>
+ *
+ * <p>Callbacks can be function refs or objects with the following keys. Only
+ * the <code>fn</code> key is required.</p>
+ *
+ * <ul>
+ * <li><code>fn</code> -- The callback function</li>
+ * <li><code>context</code> -- The execution context for the callbackFn.</li>
+ * <li><code>args</code> -- Arguments to pass to callbackFn.</li>
+ * <li><code>timeout</code> -- Millisecond delay before executing callbackFn.
+ * (Applies to each iterative execution of callback)</li>
+ * <li><code>iterations</code> -- Number of times to repeat the callback.
+ * <li><code>until</code> -- Repeat the callback until this function returns
+ * true. This setting trumps iterations.</li>
+ * <li><code>autoContinue</code> -- Set to false to prevent the AsyncQueue from
+ * executing the next callback in the Queue after
+ * the callback completes.</li>
+ * <li><code>id</code> -- Name that can be used to get, promote, get the
+ * indexOf, or delete this callback.</li>
+ * </ul>
+ *
+ * @class AsyncQueue
+ * @extends EventTarget
+ * @constructor
+ * @param callback* {Function|Object} 0..n callbacks to seed the queue
+ */
+Y.AsyncQueue = function() {
+ this._init();
+ this.add.apply(this, arguments);
+};
+
+var Queue = Y.AsyncQueue,
+ EXECUTE = 'execute',
+ SHIFT = 'shift',
+ PROMOTE = 'promote',
+ REMOVE = 'remove',
+
+ isObject = Y.Lang.isObject,
+ isFunction = Y.Lang.isFunction;
+
+/**
+ * <p>Static default values used to populate callback configuration properties.
+ * Preconfigured defaults include:</p>
+ *
+ * <ul>
+ * <li><code>autoContinue</code>: <code>true</code></li>
+ * <li><code>iterations</code>: 1</li>
+ * <li><code>timeout</code>: 10 (10ms between callbacks)</li>
+ * <li><code>until</code>: (function to run until iterations <= 0)</li>
+ * </ul>
+ *
+ * @property AsyncQueue.defaults
+ * @type {Object}
+ * @static
+ */
+Queue.defaults = Y.mix({
+ autoContinue : true,
+ iterations : 1,
+ timeout : 10,
+ until : function () {
+ this.iterations |= 0;
+ return this.iterations <= 0;
+ }
+}, Y.config.queueDefaults || {});
+
+Y.extend(Queue, Y.EventTarget, {
+ /**
+ * Used to indicate the queue is currently executing a callback.
+ *
+ * @property _running
+ * @type {Boolean|Object} true for synchronous callback execution, the
+ * return handle from Y.later for async callbacks.
+ * Otherwise false.
+ * @protected
+ */
+ _running : false,
+
+ /**
+ * Initializes the AsyncQueue instance properties and events.
+ *
+ * @method _init
+ * @protected
+ */
+ _init : function () {
+ Y.EventTarget.call(this, { emitFacade: true });
+
+ this._q = [];
+
+ /**
+ * Callback defaults for this instance. Static defaults that are not
+ * overridden are also included.
+ *
+ * @property defaults
+ * @type {Object}
+ */
+ this.defaults = {};
+
+ this._initEvents();
+ },
+
+ /**
+ * Initializes the instance events.
+ *
+ * @method _initEvents
+ * @protected
+ */
+ _initEvents : function () {
+ /*
+ this.publish({
+ 'execute' : { defaultFn : this._defExecFn },
+ 'shift' : { defaultFn : this._defShiftFn },
+ 'add' : { defaultFn : this._defAddFn },
+ 'promote' : { defaultFn : this._defPromoteFn },
+ 'remove' : { defaultFn : this._defRemoveFn }
+ });
+ */
+ this.publish('execute' , { defaultFn : this._defExecFn, emitFacade: true });
+ this.publish('shift' , { defaultFn : this._defShiftFn, emitFacade: true });
+ this.publish('add' , { defaultFn : this._defAddFn, emitFacade: true });
+ this.publish('promote' , { defaultFn : this._defPromoteFn, emitFacade: true });
+ this.publish('remove' , { defaultFn : this._defRemoveFn, emitFacade: true });
+ },
+
+ /**
+ * Returns the next callback needing execution. If a callback is
+ * configured to repeat via iterations or until, it will be returned until
+ * the completion criteria is met.
+ *
+ * When the queue is empty, null is returned.
+ *
+ * @method next
+ * @return {Function} the callback to execute
+ */
+ next : function () {
+ var callback;
+
+ while (this._q.length) {
+ callback = this._q[0] = this._prepare(this._q[0]);
+ if (callback && callback.until()) {
+ this.fire(SHIFT, { callback: callback });
+ callback = null;
+ } else {
+ break;
+ }
+ }
+
+ return callback || null;
+ },
+
+ /**
+ * Default functionality for the "shift" event. Shifts the
+ * callback stored in the event object's <em>callback</em> property from
+ * the queue if it is the first item.
+ *
+ * @method _defShiftFn
+ * @param e {Event} The event object
+ * @protected
+ */
+ _defShiftFn : function (e) {
+ if (this.indexOf(e.callback) === 0) {
+ this._q.shift();
+ }
+ },
+
+ /**
+ * Creates a wrapper function to execute the callback using the aggregated
+ * configuration generated by combining the static AsyncQueue.defaults, the
+ * instance defaults, and the specified callback settings.
+ *
+ * The wrapper function is decorated with the callback configuration as
+ * properties for runtime modification.
+ *
+ * @method _prepare
+ * @param callback {Object|Function} the raw callback
+ * @return {Function} a decorated function wrapper to execute the callback
+ * @protected
+ */
+ _prepare: function (callback) {
+ if (isFunction(callback) && callback._prepared) {
+ return callback;
+ }
+
+ var config = Y.merge(
+ Queue.defaults,
+ { context : this, args: [], _prepared: true },
+ this.defaults,
+ (isFunction(callback) ? { fn: callback } : callback)),
+
+ wrapper = Y.bind(function () {
+ if (!wrapper._running) {
+ wrapper.iterations--;
+ }
+ if (isFunction(wrapper.fn)) {
+ wrapper.fn.apply(wrapper.context || Y,
+ Y.Array(wrapper.args));
+ }
+ }, this);
+
+ return Y.mix(wrapper, config);
+ },
+
+ /**
+ * Sets the queue in motion. All queued callbacks will be executed in
+ * order unless pause() or stop() is called or if one of the callbacks is
+ * configured with autoContinue: false.
+ *
+ * @method run
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ run : function () {
+ var callback,
+ cont = true;
+
+ for (callback = this.next();
+ cont && callback && !this.isRunning();
+ callback = this.next())
+ {
+ cont = (callback.timeout < 0) ?
+ this._execute(callback) :
+ this._schedule(callback);
+ }
+
+ if (!callback) {
+ /**
+ * Event fired after the last queued callback is executed.
+ * @event complete
+ */
+ this.fire('complete');
+ }
+
+ return this;
+ },
+
+ /**
+ * Handles the execution of callbacks. Returns a boolean indicating
+ * whether it is appropriate to continue running.
+ *
+ * @method _execute
+ * @param callback {Object} the callback object to execute
+ * @return {Boolean} whether the run loop should continue
+ * @protected
+ */
+ _execute : function (callback) {
+ this._running = callback._running = true;
+
+ callback.iterations--;
+ this.fire(EXECUTE, { callback: callback });
+
+ var cont = this._running && callback.autoContinue;
+
+ this._running = callback._running = false;
+
+ return cont;
+ },
+
+ /**
+ * Schedules the execution of asynchronous callbacks.
+ *
+ * @method _schedule
+ * @param callback {Object} the callback object to execute
+ * @return {Boolean} whether the run loop should continue
+ * @protected
+ */
+ _schedule : function (callback) {
+ this._running = Y.later(callback.timeout, this, function () {
+ if (this._execute(callback)) {
+ this.run();
+ }
+ });
+
+ return false;
+ },
+
+ /**
+ * Determines if the queue is waiting for a callback to complete execution.
+ *
+ * @method isRunning
+ * @return {Boolean} true if queue is waiting for a
+ * from any initiated transactions
+ */
+ isRunning : function () {
+ return !!this._running;
+ },
+
+ /**
+ * Default functionality for the "execute" event. Executes the
+ * callback function
+ *
+ * @method _defExecFn
+ * @param e {Event} the event object
+ * @protected
+ */
+ _defExecFn : function (e) {
+ e.callback();
+ },
+
+ /**
+ * Add any number of callbacks to the end of the queue. Callbacks may be
+ * provided as functions or objects.
+ *
+ * @method add
+ * @param callback* {Function|Object} 0..n callbacks
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ add : function () {
+ this.fire('add', { callbacks: Y.Array(arguments,0,true) });
+
+ return this;
+ },
+
+ /**
+ * Default functionality for the "add" event. Adds the callbacks
+ * in the event facade to the queue. Callbacks successfully added to the
+ * queue are present in the event's <code>added</code> property in the
+ * after phase.
+ *
+ * @method _defAddFn
+ * @param e {Event} the event object
+ * @protected
+ */
+ _defAddFn : function(e) {
+ var _q = this._q,
+ added = [];
+
+ Y.Array.each(e.callbacks, function (c) {
+ if (isObject(c)) {
+ _q.push(c);
+ added.push(c);
+ }
+ });
+
+ e.added = added;
+ },
+
+ /**
+ * Pause the execution of the queue after the execution of the current
+ * callback completes. If called from code outside of a queued callback,
+ * clears the timeout for the pending callback. Paused queue can be
+ * restarted with q.run()
+ *
+ * @method pause
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ pause: function () {
+ if (isObject(this._running)) {
+ this._running.cancel();
+ }
+
+ this._running = false;
+
+ return this;
+ },
+
+ /**
+ * Stop and clear the queue after the current execution of the
+ * current callback completes.
+ *
+ * @method stop
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ stop : function () {
+ this._q = [];
+
+ return this.pause();
+ },
+
+ /**
+ * Returns the current index of a callback. Pass in either the id or
+ * callback function from getCallback.
+ *
+ * @method indexOf
+ * @param callback {String|Function} the callback or its specified id
+ * @return {Number} index of the callback or -1 if not found
+ */
+ indexOf : function (callback) {
+ var i = 0, len = this._q.length, c;
+
+ for (; i < len; ++i) {
+ c = this._q[i];
+ if (c === callback || c.id === callback) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ /**
+ * Retrieve a callback by its id. Useful to modify the configuration
+ * while the queue is running.
+ *
+ * @method getCallback
+ * @param id {String} the id assigned to the callback
+ * @return {Object} the callback object
+ */
+ getCallback : function (id) {
+ var i = this.indexOf(id);
+
+ return (i > -1) ? this._q[i] : null;
+ },
+
+ /**
+ * Promotes the named callback to the top of the queue. If a callback is
+ * currently executing or looping (via until or iterations), the promotion
+ * is scheduled to occur after the current callback has completed.
+ *
+ * @method promote
+ * @param callback {String|Object} the callback object or a callback's id
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ promote : function (callback) {
+ var payload = { callback : callback },e;
+
+ if (this.isRunning()) {
+ e = this.after(SHIFT, function () {
+ this.fire(PROMOTE, payload);
+ e.detach();
+ }, this);
+ } else {
+ this.fire(PROMOTE, payload);
+ }
+
+ return this;
+ },
+
+ /**
+ * <p>Default functionality for the "promote" event. Promotes the
+ * named callback to the head of the queue.</p>
+ *
+ * <p>The event object will contain a property "callback", which
+ * holds the id of a callback or the callback object itself.</p>
+ *
+ * @method _defPromoteFn
+ * @param e {Event} the custom event
+ * @protected
+ */
+ _defPromoteFn : function (e) {
+ var i = this.indexOf(e.callback),
+ promoted = (i > -1) ? this._q.splice(i,1)[0] : null;
+
+ e.promoted = promoted;
+
+ if (promoted) {
+ this._q.unshift(promoted);
+ }
+ },
+
+ /**
+ * Removes the callback from the queue. If the queue is active, the
+ * removal is scheduled to occur after the current callback has completed.
+ *
+ * @method remove
+ * @param callback {String|Object} the callback object or a callback's id
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ remove : function (callback) {
+ var payload = { callback : callback },e;
+
+ // Can't return the removed callback because of the deferral until
+ // current callback is complete
+ if (this.isRunning()) {
+ e = this.after(SHIFT, function () {
+ this.fire(REMOVE, payload);
+ e.detach();
+ },this);
+ } else {
+ this.fire(REMOVE, payload);
+ }
+
+ return this;
+ },
+
+ /**
+ * <p>Default functionality for the "remove" event. Removes the
+ * callback from the queue.</p>
+ *
+ * <p>The event object will contain a property "callback", which
+ * holds the id of a callback or the callback object itself.</p>
+ *
+ * @method _defRemoveFn
+ * @param e {Event} the custom event
+ * @protected
+ */
+ _defRemoveFn : function (e) {
+ var i = this.indexOf(e.callback);
+
+ e.removed = (i > -1) ? this._q.splice(i,1)[0] : null;
+ },
+
+ /**
+ * Returns the number of callbacks in the queue.
+ *
+ * @method size
+ * @return {Number}
+ */
+ size : function () {
+ // next() flushes callbacks that have met their until() criteria and
+ // therefore shouldn't count since they wouldn't execute anyway.
+ if (!this.isRunning()) {
+ this.next();
+ }
+
+ return this._q.length;
+ }
+});
+
+
+
+}, '3.0.0' ,{requires:['event-custom']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("async-queue",function(G){G.AsyncQueue=function(){this._init();this.add.apply(this,arguments);};var E=G.AsyncQueue,C="execute",B="shift",D="promote",H="remove",A=G.Lang.isObject,F=G.Lang.isFunction;E.defaults=G.mix({autoContinue:true,iterations:1,timeout:10,until:function(){this.iterations|=0;return this.iterations<=0;}},G.config.queueDefaults||{});G.extend(E,G.EventTarget,{_running:false,_init:function(){G.EventTarget.call(this,{emitFacade:true});this._q=[];this.defaults={};this._initEvents();},_initEvents:function(){this.publish("execute",{defaultFn:this._defExecFn,emitFacade:true});this.publish("shift",{defaultFn:this._defShiftFn,emitFacade:true});this.publish("add",{defaultFn:this._defAddFn,emitFacade:true});this.publish("promote",{defaultFn:this._defPromoteFn,emitFacade:true});this.publish("remove",{defaultFn:this._defRemoveFn,emitFacade:true});},next:function(){var I;while(this._q.length){I=this._q[0]=this._prepare(this._q[0]);if(I&&I.until()){this.fire(B,{callback:I});I=null;}else{break;}}return I||null;},_defShiftFn:function(I){if(this.indexOf(I.callback)===0){this._q.shift();}},_prepare:function(K){if(F(K)&&K._prepared){return K;}var I=G.merge(E.defaults,{context:this,args:[],_prepared:true},this.defaults,(F(K)?{fn:K}:K)),J=G.bind(function(){if(!J._running){J.iterations--;}if(F(J.fn)){J.fn.apply(J.context||G,G.Array(J.args));}},this);return G.mix(J,I);},run:function(){var J,I=true;for(J=this.next();I&&J&&!this.isRunning();J=this.next()){I=(J.timeout<0)?this._execute(J):this._schedule(J);}if(!J){this.fire("complete");}return this;},_execute:function(J){this._running=J._running=true;J.iterations--;this.fire(C,{callback:J});var I=this._running&&J.autoContinue;this._running=J._running=false;return I;},_schedule:function(I){this._running=G.later(I.timeout,this,function(){if(this._execute(I)){this.run();}});return false;},isRunning:function(){return !!this._running;},_defExecFn:function(I){I.callback();},add:function(){this.fire("add",{callbacks:G.Array(arguments,0,true)});return this;},_defAddFn:function(J){var K=this._q,I=[];G.Array.each(J.callbacks,function(L){if(A(L)){K.push(L);I.push(L);}});J.added=I;},pause:function(){if(A(this._running)){this._running.cancel();}this._running=false;return this;},stop:function(){this._q=[];return this.pause();},indexOf:function(L){var J=0,I=this._q.length,K;for(;J<I;++J){K=this._q[J];if(K===L||K.id===L){return J;}}return -1;},getCallback:function(J){var I=this.indexOf(J);return(I>-1)?this._q[I]:null;},promote:function(K){var J={callback:K},I;if(this.isRunning()){I=this.after(B,function(){this.fire(D,J);I.detach();},this);}else{this.fire(D,J);}return this;},_defPromoteFn:function(K){var I=this.indexOf(K.callback),J=(I>-1)?this._q.splice(I,1)[0]:null;K.promoted=J;if(J){this._q.unshift(J);}},remove:function(K){var J={callback:K},I;if(this.isRunning()){I=this.after(B,function(){this.fire(H,J);I.detach();},this);}else{this.fire(H,J);}return this;},_defRemoveFn:function(J){var I=this.indexOf(J.callback);J.removed=(I>-1)?this._q.splice(I,1)[0]:null;},size:function(){if(!this.isRunning()){this.next();}return this._q.length;}});},"3.0.0",{requires:["event-custom"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('async-queue', function(Y) {
+
+/**
+ * <p>AsyncQueue allows you create a chain of function callbacks executed
+ * via setTimeout (or synchronously) that are guaranteed to run in order.
+ * Items in the queue can be promoted or removed. Start or resume the
+ * execution chain with run(). pause() to temporarily delay execution, or
+ * stop() to halt and clear the queue.</p>
+ *
+ * @module async-queue
+ */
+
+/**
+ * <p>A specialized queue class that supports scheduling callbacks to execute
+ * sequentially, iteratively, even asynchronously.</p>
+ *
+ * <p>Callbacks can be function refs or objects with the following keys. Only
+ * the <code>fn</code> key is required.</p>
+ *
+ * <ul>
+ * <li><code>fn</code> -- The callback function</li>
+ * <li><code>context</code> -- The execution context for the callbackFn.</li>
+ * <li><code>args</code> -- Arguments to pass to callbackFn.</li>
+ * <li><code>timeout</code> -- Millisecond delay before executing callbackFn.
+ * (Applies to each iterative execution of callback)</li>
+ * <li><code>iterations</code> -- Number of times to repeat the callback.
+ * <li><code>until</code> -- Repeat the callback until this function returns
+ * true. This setting trumps iterations.</li>
+ * <li><code>autoContinue</code> -- Set to false to prevent the AsyncQueue from
+ * executing the next callback in the Queue after
+ * the callback completes.</li>
+ * <li><code>id</code> -- Name that can be used to get, promote, get the
+ * indexOf, or delete this callback.</li>
+ * </ul>
+ *
+ * @class AsyncQueue
+ * @extends EventTarget
+ * @constructor
+ * @param callback* {Function|Object} 0..n callbacks to seed the queue
+ */
+Y.AsyncQueue = function() {
+ this._init();
+ this.add.apply(this, arguments);
+};
+
+var Queue = Y.AsyncQueue,
+ EXECUTE = 'execute',
+ SHIFT = 'shift',
+ PROMOTE = 'promote',
+ REMOVE = 'remove',
+
+ isObject = Y.Lang.isObject,
+ isFunction = Y.Lang.isFunction;
+
+/**
+ * <p>Static default values used to populate callback configuration properties.
+ * Preconfigured defaults include:</p>
+ *
+ * <ul>
+ * <li><code>autoContinue</code>: <code>true</code></li>
+ * <li><code>iterations</code>: 1</li>
+ * <li><code>timeout</code>: 10 (10ms between callbacks)</li>
+ * <li><code>until</code>: (function to run until iterations <= 0)</li>
+ * </ul>
+ *
+ * @property AsyncQueue.defaults
+ * @type {Object}
+ * @static
+ */
+Queue.defaults = Y.mix({
+ autoContinue : true,
+ iterations : 1,
+ timeout : 10,
+ until : function () {
+ this.iterations |= 0;
+ return this.iterations <= 0;
+ }
+}, Y.config.queueDefaults || {});
+
+Y.extend(Queue, Y.EventTarget, {
+ /**
+ * Used to indicate the queue is currently executing a callback.
+ *
+ * @property _running
+ * @type {Boolean|Object} true for synchronous callback execution, the
+ * return handle from Y.later for async callbacks.
+ * Otherwise false.
+ * @protected
+ */
+ _running : false,
+
+ /**
+ * Initializes the AsyncQueue instance properties and events.
+ *
+ * @method _init
+ * @protected
+ */
+ _init : function () {
+ Y.EventTarget.call(this, { emitFacade: true });
+
+ this._q = [];
+
+ /**
+ * Callback defaults for this instance. Static defaults that are not
+ * overridden are also included.
+ *
+ * @property defaults
+ * @type {Object}
+ */
+ this.defaults = {};
+
+ this._initEvents();
+ },
+
+ /**
+ * Initializes the instance events.
+ *
+ * @method _initEvents
+ * @protected
+ */
+ _initEvents : function () {
+ /*
+ this.publish({
+ 'execute' : { defaultFn : this._defExecFn },
+ 'shift' : { defaultFn : this._defShiftFn },
+ 'add' : { defaultFn : this._defAddFn },
+ 'promote' : { defaultFn : this._defPromoteFn },
+ 'remove' : { defaultFn : this._defRemoveFn }
+ });
+ */
+ this.publish('execute' , { defaultFn : this._defExecFn, emitFacade: true });
+ this.publish('shift' , { defaultFn : this._defShiftFn, emitFacade: true });
+ this.publish('add' , { defaultFn : this._defAddFn, emitFacade: true });
+ this.publish('promote' , { defaultFn : this._defPromoteFn, emitFacade: true });
+ this.publish('remove' , { defaultFn : this._defRemoveFn, emitFacade: true });
+ },
+
+ /**
+ * Returns the next callback needing execution. If a callback is
+ * configured to repeat via iterations or until, it will be returned until
+ * the completion criteria is met.
+ *
+ * When the queue is empty, null is returned.
+ *
+ * @method next
+ * @return {Function} the callback to execute
+ */
+ next : function () {
+ var callback;
+
+ while (this._q.length) {
+ callback = this._q[0] = this._prepare(this._q[0]);
+ if (callback && callback.until()) {
+ this.fire(SHIFT, { callback: callback });
+ callback = null;
+ } else {
+ break;
+ }
+ }
+
+ return callback || null;
+ },
+
+ /**
+ * Default functionality for the "shift" event. Shifts the
+ * callback stored in the event object's <em>callback</em> property from
+ * the queue if it is the first item.
+ *
+ * @method _defShiftFn
+ * @param e {Event} The event object
+ * @protected
+ */
+ _defShiftFn : function (e) {
+ if (this.indexOf(e.callback) === 0) {
+ this._q.shift();
+ }
+ },
+
+ /**
+ * Creates a wrapper function to execute the callback using the aggregated
+ * configuration generated by combining the static AsyncQueue.defaults, the
+ * instance defaults, and the specified callback settings.
+ *
+ * The wrapper function is decorated with the callback configuration as
+ * properties for runtime modification.
+ *
+ * @method _prepare
+ * @param callback {Object|Function} the raw callback
+ * @return {Function} a decorated function wrapper to execute the callback
+ * @protected
+ */
+ _prepare: function (callback) {
+ if (isFunction(callback) && callback._prepared) {
+ return callback;
+ }
+
+ var config = Y.merge(
+ Queue.defaults,
+ { context : this, args: [], _prepared: true },
+ this.defaults,
+ (isFunction(callback) ? { fn: callback } : callback)),
+
+ wrapper = Y.bind(function () {
+ if (!wrapper._running) {
+ wrapper.iterations--;
+ }
+ if (isFunction(wrapper.fn)) {
+ wrapper.fn.apply(wrapper.context || Y,
+ Y.Array(wrapper.args));
+ }
+ }, this);
+
+ return Y.mix(wrapper, config);
+ },
+
+ /**
+ * Sets the queue in motion. All queued callbacks will be executed in
+ * order unless pause() or stop() is called or if one of the callbacks is
+ * configured with autoContinue: false.
+ *
+ * @method run
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ run : function () {
+ var callback,
+ cont = true;
+
+ for (callback = this.next();
+ cont && callback && !this.isRunning();
+ callback = this.next())
+ {
+ cont = (callback.timeout < 0) ?
+ this._execute(callback) :
+ this._schedule(callback);
+ }
+
+ if (!callback) {
+ /**
+ * Event fired after the last queued callback is executed.
+ * @event complete
+ */
+ this.fire('complete');
+ }
+
+ return this;
+ },
+
+ /**
+ * Handles the execution of callbacks. Returns a boolean indicating
+ * whether it is appropriate to continue running.
+ *
+ * @method _execute
+ * @param callback {Object} the callback object to execute
+ * @return {Boolean} whether the run loop should continue
+ * @protected
+ */
+ _execute : function (callback) {
+ this._running = callback._running = true;
+
+ callback.iterations--;
+ this.fire(EXECUTE, { callback: callback });
+
+ var cont = this._running && callback.autoContinue;
+
+ this._running = callback._running = false;
+
+ return cont;
+ },
+
+ /**
+ * Schedules the execution of asynchronous callbacks.
+ *
+ * @method _schedule
+ * @param callback {Object} the callback object to execute
+ * @return {Boolean} whether the run loop should continue
+ * @protected
+ */
+ _schedule : function (callback) {
+ this._running = Y.later(callback.timeout, this, function () {
+ if (this._execute(callback)) {
+ this.run();
+ }
+ });
+
+ return false;
+ },
+
+ /**
+ * Determines if the queue is waiting for a callback to complete execution.
+ *
+ * @method isRunning
+ * @return {Boolean} true if queue is waiting for a
+ * from any initiated transactions
+ */
+ isRunning : function () {
+ return !!this._running;
+ },
+
+ /**
+ * Default functionality for the "execute" event. Executes the
+ * callback function
+ *
+ * @method _defExecFn
+ * @param e {Event} the event object
+ * @protected
+ */
+ _defExecFn : function (e) {
+ e.callback();
+ },
+
+ /**
+ * Add any number of callbacks to the end of the queue. Callbacks may be
+ * provided as functions or objects.
+ *
+ * @method add
+ * @param callback* {Function|Object} 0..n callbacks
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ add : function () {
+ this.fire('add', { callbacks: Y.Array(arguments,0,true) });
+
+ return this;
+ },
+
+ /**
+ * Default functionality for the "add" event. Adds the callbacks
+ * in the event facade to the queue. Callbacks successfully added to the
+ * queue are present in the event's <code>added</code> property in the
+ * after phase.
+ *
+ * @method _defAddFn
+ * @param e {Event} the event object
+ * @protected
+ */
+ _defAddFn : function(e) {
+ var _q = this._q,
+ added = [];
+
+ Y.Array.each(e.callbacks, function (c) {
+ if (isObject(c)) {
+ _q.push(c);
+ added.push(c);
+ }
+ });
+
+ e.added = added;
+ },
+
+ /**
+ * Pause the execution of the queue after the execution of the current
+ * callback completes. If called from code outside of a queued callback,
+ * clears the timeout for the pending callback. Paused queue can be
+ * restarted with q.run()
+ *
+ * @method pause
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ pause: function () {
+ if (isObject(this._running)) {
+ this._running.cancel();
+ }
+
+ this._running = false;
+
+ return this;
+ },
+
+ /**
+ * Stop and clear the queue after the current execution of the
+ * current callback completes.
+ *
+ * @method stop
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ stop : function () {
+ this._q = [];
+
+ return this.pause();
+ },
+
+ /**
+ * Returns the current index of a callback. Pass in either the id or
+ * callback function from getCallback.
+ *
+ * @method indexOf
+ * @param callback {String|Function} the callback or its specified id
+ * @return {Number} index of the callback or -1 if not found
+ */
+ indexOf : function (callback) {
+ var i = 0, len = this._q.length, c;
+
+ for (; i < len; ++i) {
+ c = this._q[i];
+ if (c === callback || c.id === callback) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ /**
+ * Retrieve a callback by its id. Useful to modify the configuration
+ * while the queue is running.
+ *
+ * @method getCallback
+ * @param id {String} the id assigned to the callback
+ * @return {Object} the callback object
+ */
+ getCallback : function (id) {
+ var i = this.indexOf(id);
+
+ return (i > -1) ? this._q[i] : null;
+ },
+
+ /**
+ * Promotes the named callback to the top of the queue. If a callback is
+ * currently executing or looping (via until or iterations), the promotion
+ * is scheduled to occur after the current callback has completed.
+ *
+ * @method promote
+ * @param callback {String|Object} the callback object or a callback's id
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ promote : function (callback) {
+ var payload = { callback : callback },e;
+
+ if (this.isRunning()) {
+ e = this.after(SHIFT, function () {
+ this.fire(PROMOTE, payload);
+ e.detach();
+ }, this);
+ } else {
+ this.fire(PROMOTE, payload);
+ }
+
+ return this;
+ },
+
+ /**
+ * <p>Default functionality for the "promote" event. Promotes the
+ * named callback to the head of the queue.</p>
+ *
+ * <p>The event object will contain a property "callback", which
+ * holds the id of a callback or the callback object itself.</p>
+ *
+ * @method _defPromoteFn
+ * @param e {Event} the custom event
+ * @protected
+ */
+ _defPromoteFn : function (e) {
+ var i = this.indexOf(e.callback),
+ promoted = (i > -1) ? this._q.splice(i,1)[0] : null;
+
+ e.promoted = promoted;
+
+ if (promoted) {
+ this._q.unshift(promoted);
+ }
+ },
+
+ /**
+ * Removes the callback from the queue. If the queue is active, the
+ * removal is scheduled to occur after the current callback has completed.
+ *
+ * @method remove
+ * @param callback {String|Object} the callback object or a callback's id
+ * @return {AsyncQueue} the AsyncQueue instance
+ * @chainable
+ */
+ remove : function (callback) {
+ var payload = { callback : callback },e;
+
+ // Can't return the removed callback because of the deferral until
+ // current callback is complete
+ if (this.isRunning()) {
+ e = this.after(SHIFT, function () {
+ this.fire(REMOVE, payload);
+ e.detach();
+ },this);
+ } else {
+ this.fire(REMOVE, payload);
+ }
+
+ return this;
+ },
+
+ /**
+ * <p>Default functionality for the "remove" event. Removes the
+ * callback from the queue.</p>
+ *
+ * <p>The event object will contain a property "callback", which
+ * holds the id of a callback or the callback object itself.</p>
+ *
+ * @method _defRemoveFn
+ * @param e {Event} the custom event
+ * @protected
+ */
+ _defRemoveFn : function (e) {
+ var i = this.indexOf(e.callback);
+
+ e.removed = (i > -1) ? this._q.splice(i,1)[0] : null;
+ },
+
+ /**
+ * Returns the number of callbacks in the queue.
+ *
+ * @method size
+ * @return {Number}
+ */
+ size : function () {
+ // next() flushes callbacks that have met their until() criteria and
+ // therefore shouldn't count since they wouldn't execute anyway.
+ if (!this.isRunning()) {
+ this.next();
+ }
+
+ return this._q.length;
+ }
+});
+
+
+
+}, '3.0.0' ,{requires:['event-custom']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('attribute-base', function(Y) {
+
+ /**
+ * The State class maintains state for a collection of named items, with
+ * a varying number of properties defined.
+ *
+ * It avoids the need to create a separate class for the item, and separate instances
+ * of these classes for each item, by storing the state in a 2 level hash table,
+ * improving performance when the number of items is likely to be large.
+ *
+ * @constructor
+ * @class State
+ */
+ Y.State = function() {
+ /**
+ * Hash of attributes
+ * @property data
+ */
+ this.data = {};
+ };
+
+ Y.State.prototype = {
+
+ /**
+ * Adds a property to an item.
+ *
+ * @method add
+ * @param name {String} The name of the item.
+ * @param key {String} The name of the property.
+ * @param val {Any} The value of the property.
+ */
+ add : function(name, key, val) {
+ var d = this.data;
+ d[key] = d[key] || {};
+ d[key][name] = val;
+ },
+
+ /**
+ * Adds multiple properties to an item.
+ *
+ * @method addAll
+ * @param name {String} The name of the item.
+ * @param o {Object} A hash of property/value pairs.
+ */
+ addAll: function(name, o) {
+ var key;
+ for (key in o) {
+ if (o.hasOwnProperty(key)) {
+ this.add(name, key, o[key]);
+ }
+ }
+ },
+
+ /**
+ * Removes a property from an item.
+ *
+ * @method remove
+ * @param name {String} The name of the item.
+ * @param key {String} The property to remove.
+ */
+ remove: function(name, key) {
+ var d = this.data;
+ if (d[key] && (name in d[key])) {
+ delete d[key][name];
+ }
+ },
+
+ /**
+ * Removes multiple properties from an item, or remove the item completely.
+ *
+ * @method removeAll
+ * @param name {String} The name of the item.
+ * @param o {Object|Array} Collection of properties to delete. If not provided, the entire item is removed.
+ */
+ removeAll: function(name, o) {
+ var d = this.data;
+
+ Y.each(o || d, function(v, k) {
+ if(Y.Lang.isString(k)) {
+ this.remove(name, k);
+ } else {
+ this.remove(name, v);
+ }
+ }, this);
+ },
+
+ /**
+ * For a given item, returns the value of the property requested, or undefined if not found.
+ *
+ * @method get
+ * @param name {String} The name of the item
+ * @param key {String} Optional. The property value to retrieve.
+ * @return {Any} The value of the supplied property.
+ */
+ get: function(name, key) {
+ var d = this.data;
+ return (d[key] && name in d[key]) ? d[key][name] : undefined;
+ },
+
+ /**
+ * For the given item, returns a disposable object with all of the
+ * item's property/value pairs.
+ *
+ * @method getAll
+ * @param name {String} The name of the item
+ * @return {Object} An object with property/value pairs for the item.
+ */
+ getAll : function(name) {
+ var d = this.data, o;
+
+ Y.each(d, function(v, k) {
+ if (name in d[k]) {
+ o = o || {};
+ o[k] = v[name];
+ }
+ }, this);
+
+ return o;
+ }
+ };
+ /**
+ * The attribute module provides an augmentable Attribute implementation, which
+ * adds configurable attributes and attribute change events to the class being
+ * augmented. It also provides a State class, which is used internally by Attribute,
+ * but can also be used independently to provide a name/property/value data structure to
+ * store state.
+ *
+ * @module attribute
+ */
+
+ /**
+ * The attribute-base submodule provides core attribute handling support, with everything
+ * aside from complex attribute handling in the provider's constructor.
+ *
+ * @module attribute
+ * @submodule attribute-base
+ */
+ var O = Y.Object,
+ Lang = Y.Lang,
+ EventTarget = Y.EventTarget,
+
+ DOT = ".",
+ CHANGE = "Change",
+
+ // Externally configurable props
+ GETTER = "getter",
+ SETTER = "setter",
+ READ_ONLY = "readOnly",
+ WRITE_ONCE = "writeOnce",
+ VALIDATOR = "validator",
+ VALUE = "value",
+ VALUE_FN = "valueFn",
+ BROADCAST = "broadcast",
+ LAZY_ADD = "lazyAdd",
+ BYPASS_PROXY = "_bypassProxy",
+
+ // Used for internal state management
+ ADDED = "added",
+ INITIALIZING = "initializing",
+ INIT_VALUE = "initValue",
+ PUBLISHED = "published",
+ DEF_VALUE = "defaultValue",
+ LAZY = "lazy",
+ IS_LAZY_ADD = "isLazyAdd",
+
+ INVALID_VALUE,
+ MODIFIABLE = {};
+
+ // Properties which can be changed after the attribute has been added.
+ MODIFIABLE[READ_ONLY] = 1;
+ MODIFIABLE[WRITE_ONCE] = 1;
+ MODIFIABLE[GETTER] = 1;
+ MODIFIABLE[BROADCAST] = 1;
+
+ /**
+ * <p>
+ * Attribute provides configurable attribute support along with attribute change events. It is designed to be
+ * augmented on to a host class, and provides the host with the ability to configure attributes to store and retrieve state,
+ * along with attribute change events.
+ * </p>
+ * <p>For example, attributes added to the host can be configured:</p>
+ * <ul>
+ * <li>As read only.</li>
+ * <li>As write once.</li>
+ * <li>With a setter function, which can be used to manipulate
+ * values passed to Attribute's <a href="#method_set">set</a> method, before they are stored.</li>
+ * <li>With a getter function, which can be used to manipulate stored values,
+ * before they are returned by Attribute's <a href="#method_get">get</a> method.</li>
+ * <li>With a validator function, to validate values before they are stored.</li>
+ * </ul>
+ *
+ * <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration
+ * options available for attributes</p>.
+ *
+ * <p><strong>NOTE:</strong> Most implementations will be better off extending the <a href="Base.html">Base</a> class,
+ * instead of augmenting Attribute directly. Base augments Attribute and will handle the initial configuration
+ * of attributes for derived classes, accounting for values passed into the constructor.</p>
+ *
+ * @class Attribute
+ * @uses EventTarget
+ */
+ function Attribute() {
+ Y.log('Attribute constructor called', 'info', 'attribute');
+
+ var host = this, // help compression
+ attrs = this.constructor.ATTRS,
+ Base = Y.Base;
+
+ // Perf tweak - avoid creating event literals if not required.
+ host._ATTR_E_FACADE = {};
+
+ EventTarget.call(host, {emitFacade:true});
+
+ // _conf maintained for backwards compat
+ host._conf = host._state = new Y.State();
+
+ host._stateProxy = host._stateProxy || null;
+ host._requireAddAttr = host._requireAddAttr || false;
+
+ // ATTRS support for Node, which is not Base based
+ if ( attrs && !(Base && host instanceof Base)) {
+ host.addAttrs(this._protectAttrs(attrs));
+ }
+ }
+
+ /**
+ * <p>The value to return from an attribute setter in order to prevent the set from going through.</p>
+ *
+ * <p>You can return this value from your setter if you wish to combine validator and setter
+ * functionality into a single setter function, which either returns the massaged value to be stored or
+ * Attribute.INVALID_VALUE to prevent invalid values from being stored.</p>
+ *
+ * @property Attribute.INVALID_VALUE
+ * @type Object
+ * @static
+ * @final
+ */
+ Attribute.INVALID_VALUE = {};
+ INVALID_VALUE = Attribute.INVALID_VALUE;
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce etc.).
+ *
+ * This property is used internally as a whitelist for faster
+ * Y.mix operations.
+ *
+ * @property Attribute._ATTR_CFG
+ * @type Array
+ * @static
+ * @protected
+ */
+ Attribute._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BROADCAST, BYPASS_PROXY];
+
+ Attribute.prototype = {
+ /**
+ * <p>
+ * Adds an attribute with the provided configuration to the host object.
+ * </p>
+ * <p>
+ * The config argument object supports the following properties:
+ * </p>
+ *
+ * <dl>
+ * <dt>value <Any></dt>
+ * <dd>The initial value to set on the attribute</dd>
+ *
+ * <dt>valueFn <Function></dt>
+ * <dd>A function, which will return the initial value to set on the attribute. This is useful
+ * for cases where the attribute configuration is defined statically, but needs to
+ * reference the host instance ("this") to obtain an initial value.
+ * If defined, this precedence over the value property.</dd>
+ *
+ * <dt>readOnly <boolean></dt>
+ * <dd>Whether or not the attribute is read only. Attributes having readOnly set to true
+ * cannot be modified by invoking the set method.</dd>
+ *
+ * <dt>writeOnce <boolean></dt>
+ * <dd>Whether or not the attribute is "write once". Attributes having writeOnce set to true,
+ * can only have their values set once, be it through the default configuration,
+ * constructor configuration arguments, or by invoking set.</dd>
+ *
+ * <dt>setter <Function></dt>
+ * <dd>The setter function used to massage or normalize the value passed to the set method for the attribute.
+ * The value returned by the setter will be the final stored value. Returning
+ * <a href="#property_Attribute.INVALID_VALUE">Attribute.INVALID_VALUE</a>, from the setter will prevent
+ * the value from being stored.</dd>
+ *
+ * <dt>getter <Function></dt>
+ * <dd>The getter function used to massage or normalize the value returned by the get method for the attribute.
+ * The value returned by the getter function is the value which will be returned to the user when they
+ * invoke get.</dd>
+ *
+ * <dt>validator <Function></dt>
+ * <dd>The validator function invoked prior to setting the stored value. Returning
+ * false from the validator function will prevent the value from being stored.</dd>
+ *
+ * <dt>broadcast <int></dt>
+ * <dd>If and how attribute change events for this attribute should be broadcast. See CustomEvent's <a href="CustomEvent.html#property_broadcast">broadcast</a> property for
+ * valid values. By default attribute change events are not broadcast.</dd>
+ *
+ * <dt>lazyAdd <boolean></dt>
+ * <dd>Whether or not to delay initialization of the attribute until the first call to get/set it.
+ * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through
+ * the <a href="#method_addAttrs">addAttrs</a> method.</dd>
+ *
+ * </dl>
+ *
+ * <p>The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with
+ * the context ("this") set to the host object.</p>
+ *
+ * <p>Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.</p>
+ *
+ * @method addAttr
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute.
+ *
+ * <p>
+ * <strong>NOTE:</strong> The configuration object is modified when adding an attribute, so if you need
+ * to protect the original values, you will need to merge the object.
+ * </p>
+ *
+ * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set).
+ *
+ * @return {Object} A reference to the host object.
+ *
+ * @chainable
+ */
+ addAttr: function(name, config, lazy) {
+
+ Y.log('Adding attribute: ' + name, 'info', 'attribute');
+
+ var host = this, // help compression
+ state = host._state,
+ value,
+ hasValue;
+
+ lazy = (LAZY_ADD in config) ? config[LAZY_ADD] : lazy;
+
+ if (lazy && !host.attrAdded(name)) {
+ state.add(name, LAZY, config || {});
+ state.add(name, ADDED, true);
+ } else {
+
+ if (host.attrAdded(name) && !state.get(name, IS_LAZY_ADD)) { Y.log('Attribute: ' + name + ' already exists. Cannot add it again without removing it first', 'warn', 'attribute'); }
+
+ if (!host.attrAdded(name) || state.get(name, IS_LAZY_ADD)) {
+
+ config = config || {};
+
+ hasValue = (VALUE in config);
+
+ if (config.readOnly && !hasValue) { Y.log('readOnly attribute: ' + name + ', added without an initial value. Value will be set on initial call to set', 'warn', 'attribute');}
+
+ if(hasValue) {
+ // We'll go through set, don't want to set value in _state directly
+ value = config.value;
+ delete config.value;
+ }
+
+ config.added = true;
+ config.initializing = true;
+
+ state.addAll(name, config);
+
+ if (hasValue) {
+ // Go through set, so that raw values get normalized/validated
+ host.set(name, value);
+ }
+
+ state.remove(name, INITIALIZING);
+ }
+ }
+
+ return host;
+ },
+
+ /**
+ * Checks if the given attribute has been added to the host
+ *
+ * @method attrAdded
+ * @param {String} name The name of the attribute to check.
+ * @return {boolean} true if an attribute with the given name has been added, false if it hasn't. This method will return true for lazily added attributes.
+ */
+ attrAdded: function(name) {
+ return !!this._state.get(name, ADDED);
+ },
+
+ /**
+ * Updates the configuration of an attribute which has already been added.
+ * <p>
+ * The properties which can be modified through this interface are limited
+ * to the following subset of attributes, which can be safely modified
+ * after a value has already been set on the attribute: readOnly, writeOnce,
+ * broadcast and getter.
+ * </p>
+ * @method modifyAttr
+ * @param {String} name The name of the attribute whose configuration is to be updated.
+ * @param {Object} config An object with configuration property/value pairs, specifying the configuration properties to modify.
+ */
+ modifyAttr: function(name, config) {
+ var host = this, // help compression
+ prop, state;
+
+ if (host.attrAdded(name)) {
+
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ state = host._state;
+ for (prop in config) {
+ if (MODIFIABLE[prop] && config.hasOwnProperty(prop)) {
+ state.add(name, prop, config[prop]);
+
+ // If we reconfigured broadcast, need to republish
+ if (prop === BROADCAST) {
+ state.remove(name, PUBLISHED);
+ }
+ }
+ }
+ }
+
+ if (!host.attrAdded(name)) {Y.log('Attribute modifyAttr:' + name + ' has not been added. Use addAttr to add the attribute', 'warn', 'attribute');}
+ },
+
+ /**
+ * Removes an attribute from the host object
+ *
+ * @method removeAttr
+ * @param {String} name The name of the attribute to be removed.
+ */
+ removeAttr: function(name) {
+ this._state.removeAll(name);
+ },
+
+ /**
+ * Returns the current value of the attribute. If the attribute
+ * has been configured with a 'getter' function, this method will delegate
+ * to the 'getter' to obtain the value of the attribute.
+ *
+ * @method get
+ *
+ * @param {String} name The name of the attribute. If the value of the attribute is an Object,
+ * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
+ *
+ * @return {Any} The value of the attribute
+ */
+ get : function(name) {
+ return this._getAttr(name);
+ },
+
+ /**
+ * Checks whether or not the attribute is one which has been
+ * added lazily and still requires initialization.
+ *
+ * @method _isLazyAttr
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {boolean} true if it's a lazily added attribute, false otherwise.
+ */
+ _isLazyAttr: function(name) {
+ return this._state.get(name, LAZY);
+ },
+
+ /**
+ * Finishes initializing an attribute which has been lazily added.
+ *
+ * @method _addLazyAttr
+ * @private
+ * @param {Object} name The name of the attribute
+ */
+ _addLazyAttr: function(name) {
+ var state = this._state,
+ lazyCfg = state.get(name, LAZY);
+
+ state.add(name, IS_LAZY_ADD, true);
+ state.remove(name, LAZY);
+ this.addAttr(name, lazyCfg);
+ },
+
+ /**
+ * Sets the value of an attribute.
+ *
+ * @method set
+ * @chainable
+ *
+ * @param {String} name The name of the attribute. If the
+ * current value of the attribute is an Object, dot notation can be used
+ * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
+ *
+ * @param {Any} value The value to set the attribute to.
+ *
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event. This
+ * can be used as a flexible way to identify the source of a call to set, allowing
+ * the developer to distinguish between set called internally by the host, vs.
+ * set called externally by the application developer.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ set : function(name, val, opts) {
+ return this._setAttr(name, val, opts);
+ },
+
+ /**
+ * Resets the attribute (or all attributes) to its initial value, as long as
+ * the attribute is not readOnly, or writeOnce.
+ *
+ * @method reset
+ * @param {String} name Optional. The name of the attribute to reset. If omitted, all attributes are reset.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ reset : function(name) {
+ var host = this, // help compression
+ added;
+
+ if (name) {
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+ host.set(name, host._state.get(name, INIT_VALUE));
+ } else {
+ added = host._state.data.added;
+ Y.each(added, function(v, n) {
+ host.reset(n);
+ }, host);
+ }
+ return host;
+ },
+
+ /**
+ * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _set
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} val The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @return {Object} A reference to the host object.
+ */
+ _set : function(name, val, opts) {
+ return this._setAttr(name, val, opts, true);
+ },
+
+ /**
+ * Provides the common implementation for the public get method,
+ * allowing Attribute hosts to over-ride either method.
+ *
+ * See <a href="#method_get">get</a> for argument details.
+ *
+ * @method _getAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @return {Any} The value of the attribute.
+ */
+ _getAttr : function(name) {
+ var host = this, // help compression
+ fullName = name,
+ state = host._state,
+ path,
+ getter,
+ val,
+ cfg;
+
+ if (name.indexOf(DOT) !== -1) {
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ // On Demand - Should be rare - handles out of order valueFn references
+ if (host._tCfgs && host._tCfgs[name]) {
+ cfg = {};
+ cfg[name] = host._tCfgs[name];
+ delete host._tCfgs[name];
+ host._addAttrs(cfg, host._tVals);
+ }
+
+ // Lazy Init
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ val = host._getStateVal(name);
+ getter = state.get(name, GETTER);
+
+ val = (getter) ? getter.call(host, val, fullName) : val;
+ val = (path) ? O.getValue(val, path) : val;
+
+ return val;
+ },
+
+ /**
+ * Provides the common implementation for the public set and protected _set methods.
+ *
+ * See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _setAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} value The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @param {boolean} force If true, allows the caller to set values for
+ * readOnly or writeOnce attributes which have already been set.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ _setAttr : function(name, val, opts, force) {
+ var allowSet = true,
+ state = this._state,
+ stateProxy = this._stateProxy,
+ data = state.data,
+ initialSet,
+ strPath,
+ path,
+ currVal;
+
+ if (name.indexOf(DOT) !== -1) {
+ strPath = name;
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ if (this._isLazyAttr(name)) {
+ this._addLazyAttr(name);
+ }
+
+ initialSet = (!data.value || !(name in data.value));
+
+ if (stateProxy && name in stateProxy && !this._state.get(name, BYPASS_PROXY)) {
+ // TODO: Value is always set for proxy. Can we do any better? Maybe take a snapshot as the initial value for the first call to set?
+ initialSet = false;
+ }
+
+ if (this._requireAddAttr && !this.attrAdded(name)) {
+ Y.log('Set attribute:' + name + ', aborted; Attribute is not configured', 'warn', 'attribute');
+ } else {
+
+ if (!initialSet && !force) {
+
+ if (state.get(name, WRITE_ONCE)) {
+ Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce', 'warn', 'attribute');
+ allowSet = false;
+ }
+
+ if (state.get(name, READ_ONLY)) {
+ Y.log('Set attribute:' + name + ', aborted; Attribute is readOnly', 'warn', 'attribute');
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
+ if (!initialSet) {
+ currVal = this.get(name);
+ }
+
+ if (path) {
+ val = O.setValue(Y.clone(currVal), path, val);
+
+ if (val === undefined) {
+ Y.log('Set attribute path:' + strPath + ', aborted; Path is invalid', 'warn', 'attribute');
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ if (state.get(name, INITIALIZING)) {
+ this._setAttrVal(name, strPath, currVal, val);
+ } else {
+ this._fireAttrChange(name, strPath, currVal, val, opts);
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Utility method to help setup the event payload and fire the attribute change event.
+ *
+ * @method _fireAttrChange
+ * @private
+ * @param {String} attrName The name of the attribute
+ * @param {String} subAttrName The full path of the property being changed,
+ * if this is a sub-attribute value being change. Otherwise null.
+ * @param {Any} currVal The current value of the attribute
+ * @param {Any} newVal The new value of the attribute
+ * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
+ */
+ _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {
+ var host = this,
+ eventName = attrName + CHANGE,
+ state = host._state,
+ facade;
+
+ if (!state.get(attrName, PUBLISHED)) {
+ host.publish(eventName, {
+ queuable:false,
+ defaultFn:host._defAttrChangeFn,
+ silent:true,
+ broadcast : state.get(attrName, BROADCAST)
+ });
+ state.add(attrName, PUBLISHED, true);
+ }
+
+ facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;
+
+ facade.type = eventName;
+ facade.attrName = attrName;
+ facade.subAttrName = subAttrName;
+ facade.prevVal = currVal;
+ facade.newVal = newVal;
+
+ host.fire(facade);
+ },
+
+ /**
+ * Default function for attribute change events.
+ *
+ * @private
+ * @method _defAttrChangeFn
+ * @param {EventFacade} e The event object for attribute change events.
+ */
+ _defAttrChangeFn : function(e) {
+ if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
+ Y.log('State not updated and stopImmediatePropagation called for attribute: ' + e.attrName + ' , value:' + e.newVal, 'warn', 'attribute');
+ // Prevent "after" listeners from being invoked since nothing changed.
+ e.stopImmediatePropagation();
+ } else {
+ e.newVal = this._getStateVal(e.attrName);
+ }
+ },
+
+ /**
+ * Gets the stored value for the attribute, from either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _getStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {Any} The stored value of the attribute
+ */
+ _getStateVal : function(name) {
+ var stateProxy = this._stateProxy;
+ return stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY) ? stateProxy[name] : this._state.get(name, VALUE);
+ },
+
+ /**
+ * Sets the stored value for the attribute, in either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _setStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @param {Any} value The value of the attribute
+ */
+ _setStateVal : function(name, value) {
+ var stateProxy = this._stateProxy;
+ if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) {
+ stateProxy[name] = value;
+ } else {
+ this._state.add(name, VALUE, value);
+ }
+ },
+
+ /**
+ * Updates the stored value of the attribute in the privately held State object,
+ * if validation and setter passes.
+ *
+ * @method _setAttrVal
+ * @private
+ * @param {String} attrName The attribute name.
+ * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z").
+ * @param {Any} prevVal The currently stored value of the attribute.
+ * @param {Any} newVal The value which is going to be stored.
+ *
+ * @return {booolean} true if the new attribute value was stored, false if not.
+ */
+ _setAttrVal : function(attrName, subAttrName, prevVal, newVal) {
+
+ var host = this,
+ allowSet = true,
+ state = host._state,
+
+ validator = state.get(attrName, VALIDATOR),
+ setter = state.get(attrName, SETTER),
+ initializing = state.get(attrName, INITIALIZING),
+ prevValRaw = this._getStateVal(attrName),
+
+ name = subAttrName || attrName,
+ retVal,
+ valid;
+
+ if (validator) {
+ valid = validator.call(host, newVal, name);
+
+ if (!valid && initializing) {
+ newVal = state.get(attrName, DEF_VALUE);
+ valid = true; // Assume it's valid, for perf.
+ }
+ }
+
+ if (!validator || valid) {
+ if (setter) {
+ retVal = setter.call(host, newVal, name);
+
+ if (retVal === INVALID_VALUE) {
+ Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal, 'warn', 'attribute');
+ allowSet = false;
+ } else if (retVal !== undefined){
+ Y.log('Attribute: ' + attrName + ', raw value: ' + newVal + ' modified by setter to:' + retVal, 'info', 'attribute');
+ newVal = retVal;
+ }
+ }
+
+ if (allowSet) {
+ if(!subAttrName && (newVal === prevValRaw) && !Lang.isObject(newVal)) {
+ Y.log('Attribute: ' + attrName + ', value unchanged:' + newVal, 'warn', 'attribute');
+ allowSet = false;
+ } else {
+ // Store value
+ if (state.get(attrName, INIT_VALUE) === undefined) {
+ state.add(attrName, INIT_VALUE, newVal);
+ }
+ host._setStateVal(attrName, newVal);
+ }
+ }
+
+ } else {
+ Y.log('Attribute:' + attrName + ', Validation failed for value:' + newVal, 'warn', 'attribute');
+ allowSet = false;
+ }
+
+ return allowSet;
+ },
+
+ /**
+ * Sets multiple attribute values.
+ *
+ * @method setAttrs
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ setAttrs : function(attrs, opts) {
+ return this._setAttrs(attrs, opts);
+ },
+
+ /**
+ * Implementation behind the public setAttrs method, to set multiple attribute values.
+ *
+ * @method _setAttrs
+ * @protected
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ _setAttrs : function(attrs, opts) {
+ for (var attr in attrs) {
+ if ( attrs.hasOwnProperty(attr) ) {
+ this.set(attr, attrs[attr]);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Gets multiple attribute values.
+ *
+ * @method getAttrs
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs : function(attrs) {
+ return this._getAttrs(attrs);
+ },
+
+ /**
+ * Implementation behind the public getAttrs method, to get multiple attribute values.
+ *
+ * @method _getAttrs
+ * @protected
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ _getAttrs : function(attrs) {
+ var host = this,
+ o = {},
+ i, l, attr, val,
+ modifiedOnly = (attrs === true);
+
+ attrs = (attrs && !modifiedOnly) ? attrs : O.keys(host._state.data.added);
+
+ for (i = 0, l = attrs.length; i < l; i++) {
+ // Go through get, to honor cloning/normalization
+ attr = attrs[i];
+ val = host.get(attr);
+
+ if (!modifiedOnly || host._getStateVal(attr) != host._state.get(attr, INIT_VALUE)) {
+ o[attr] = host.get(attr);
+ }
+ }
+
+ return o;
+ },
+
+ /**
+ * Configures a group of attributes, and sets initial values.
+ *
+ * <p>
+ * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning.
+ * The caller is responsible for merging/cloning the configuration object if required.
+ * </p>
+ *
+ * @method addAttrs
+ * @chainable
+ *
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ addAttrs : function(cfgs, values, lazy) {
+ var host = this; // help compression
+ if (cfgs) {
+ host._tCfgs = cfgs;
+ host._tVals = host._normAttrVals(values);
+ host._addAttrs(cfgs, host._tVals, lazy);
+ host._tCfgs = host._tVals = null;
+ }
+
+ return host;
+ },
+
+ /**
+ * Implementation behind the public addAttrs method.
+ *
+ * This method is invoked directly by get if it encounters a scenario
+ * in which an attribute's valueFn attempts to obtain the
+ * value an attribute in the same group of attributes, which has not yet
+ * been added (on demand initialization).
+ *
+ * @method _addAttrs
+ * @private
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ */
+ _addAttrs : function(cfgs, values, lazy) {
+ var host = this, // help compression
+ attr,
+ attrCfg,
+ value;
+
+ for (attr in cfgs) {
+ if (cfgs.hasOwnProperty(attr)) {
+
+ // Not Merging. Caller is responsible for isolating configs
+ attrCfg = cfgs[attr];
+ attrCfg.defaultValue = attrCfg.value;
+
+ // Handle simple, complex and user values, accounting for read-only
+ value = host._getAttrInitVal(attr, attrCfg, host._tVals);
+
+ if (value !== undefined) {
+ attrCfg.value = value;
+ }
+
+ if (host._tCfgs[attr]) {
+ delete host._tCfgs[attr];
+ }
+
+ host.addAttr(attr, attrCfg, lazy);
+ }
+ }
+ },
+
+ /**
+ * Utility method to protect an attribute configuration
+ * hash, by merging the entire object and the individual
+ * attr config objects.
+ *
+ * @method _protectAttrs
+ * @protected
+ * @param {Object} attrs A hash of attribute to configuration object pairs.
+ * @return {Object} A protected version of the attrs argument.
+ */
+ _protectAttrs : function(attrs) {
+ if (attrs) {
+ attrs = Y.merge(attrs);
+ for (var attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+ attrs[attr] = Y.merge(attrs[attr]);
+ }
+ }
+ }
+ return attrs;
+ },
+
+ /**
+ * Utility method to normalize attribute values. The base implementation
+ * simply merges the hash to protect the original.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object}
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ return (valueHash) ? Y.merge(valueHash) : null;
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ // init value is provided by the user if it exists, else, provided by the config
+ var val = (!cfg[READ_ONLY] && initValues && initValues.hasOwnProperty(attr)) ?
+ val = initValues[attr] :
+ (cfg[VALUE_FN]) ?
+ cfg[VALUE_FN].call(this) :
+ cfg[VALUE];
+
+ Y.log('initValue for ' + attr + ':' + val, 'info', 'attribute');
+
+ return val;
+ }
+ };
+
+ // Basic prototype augment - no lazy constructor invocation.
+ Y.mix(Attribute, EventTarget, false, null, 1);
+
+ Y.Attribute = Attribute;
+
+
+}, '3.0.0' ,{requires:['event-custom']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("attribute-base",function(C){C.State=function(){this.data={};};C.State.prototype={add:function(O,Y,e){var c=this.data;c[Y]=c[Y]||{};c[Y][O]=e;},addAll:function(O,c){var Y;for(Y in c){if(c.hasOwnProperty(Y)){this.add(O,Y,c[Y]);}}},remove:function(O,Y){var c=this.data;if(c[Y]&&(O in c[Y])){delete c[Y][O];}},removeAll:function(O,c){var Y=this.data;C.each(c||Y,function(e,d){if(C.Lang.isString(d)){this.remove(O,d);}else{this.remove(O,e);}},this);},get:function(O,Y){var c=this.data;return(c[Y]&&O in c[Y])?c[Y][O]:undefined;},getAll:function(O){var c=this.data,Y;C.each(c,function(e,d){if(O in c[d]){Y=Y||{};Y[d]=e[O];}},this);return Y;}};var K=C.Object,F=C.Lang,L=C.EventTarget,W=".",U="Change",N="getter",M="setter",P="readOnly",X="writeOnce",b="validator",H="value",Q="valueFn",E="broadcast",S="lazyAdd",J="_bypassProxy",a="added",B="initializing",I="initValue",V="published",T="defaultValue",A="lazy",R="isLazyAdd",G,Z={};Z[P]=1;Z[X]=1;Z[N]=1;Z[E]=1;function D(){var c=this,O=this.constructor.ATTRS,Y=C.Base;c._ATTR_E_FACADE={};L.call(c,{emitFacade:true});c._conf=c._state=new C.State();c._stateProxy=c._stateProxy||null;c._requireAddAttr=c._requireAddAttr||false;if(O&&!(Y&&c instanceof Y)){c.addAttrs(this._protectAttrs(O));}}D.INVALID_VALUE={};G=D.INVALID_VALUE;D._ATTR_CFG=[M,N,b,H,Q,X,P,S,E,J];D.prototype={addAttr:function(Y,O,d){var e=this,g=e._state,f,c;d=(S in O)?O[S]:d;if(d&&!e.attrAdded(Y)){g.add(Y,A,O||{});g.add(Y,a,true);}else{if(!e.attrAdded(Y)||g.get(Y,R)){O=O||{};c=(H in O);if(c){f=O.value;delete O.value;}O.added=true;O.initializing=true;g.addAll(Y,O);if(c){e.set(Y,f);}g.remove(Y,B);}}return e;},attrAdded:function(O){return !!this._state.get(O,a);},modifyAttr:function(Y,O){var c=this,e,d;if(c.attrAdded(Y)){if(c._isLazyAttr(Y)){c._addLazyAttr(Y);}d=c._state;for(e in O){if(Z[e]&&O.hasOwnProperty(e)){d.add(Y,e,O[e]);if(e===E){d.remove(Y,V);}}}}},removeAttr:function(O){this._state.removeAll(O);},get:function(O){return this._getAttr(O);},_isLazyAttr:function(O){return this._state.get(O,A);},_addLazyAttr:function(Y){var c=this._state,O=c.get(Y,A);c.add(Y,R,true);c.remove(Y,A);this.addAttr(Y,O);},set:function(O,c,Y){return this._setAttr(O,c,Y);},reset:function(O){var c=this,Y;if(O){if(c._isLazyAttr(O)){c._addLazyAttr(O);}c.set(O,c._state.get(O,I));}else{Y=c._state.data.added;C.each(Y,function(d,e){c.reset(e);},c);}return c;},_set:function(O,c,Y){return this._setAttr(O,c,Y,true);},_getAttr:function(c){var d=this,h=c,e=d._state,f,O,g,Y;if(c.indexOf(W)!==-1){f=c.split(W);c=f.shift();}if(d._tCfgs&&d._tCfgs[c]){Y={};Y[c]=d._tCfgs[c];delete d._tCfgs[c];d._addAttrs(Y,d._tVals);}if(d._isLazyAttr(c)){d._addLazyAttr(c);}g=d._getStateVal(c);O=e.get(c,N);g=(O)?O.call(d,g,h):g;g=(f)?K.getValue(g,f):g;return g;},_setAttr:function(c,f,O,d){var i=true,Y=this._state,g=this._stateProxy,j=Y.data,h,k,l,e;if(c.indexOf(W)!==-1){k=c;l=c.split(W);c=l.shift();}if(this._isLazyAttr(c)){this._addLazyAttr(c);}h=(!j.value||!(c in j.value));if(g&&c in g&&!this._state.get(c,J)){h=false;}if(this._requireAddAttr&&!this.attrAdded(c)){}else{if(!h&&!d){if(Y.get(c,X)){i=false;}if(Y.get(c,P)){i=false;}}if(i){if(!h){e=this.get(c);}if(l){f=K.setValue(C.clone(e),l,f);if(f===undefined){i=false;}}if(i){if(Y.get(c,B)){this._setAttrVal(c,k,e,f);}else{this._fireAttrChange(c,k,e,f,O);}}}}return this;},_fireAttrChange:function(g,f,d,c,O){var i=this,e=g+U,Y=i._state,h;if(!Y.get(g,V)){i.publish(e,{queuable:false,defaultFn:i._defAttrChangeFn,silent:true,broadcast:Y.get(g,E)});Y.add(g,V,true);}h=(O)?C.merge(O):i._ATTR_E_FACADE;h.type=e;h.attrName=g;h.subAttrName=f;h.prevVal=d;h.newVal=c;i.fire(h);},_defAttrChangeFn:function(O){if(!this._setAttrVal(O.attrName,O.subAttrName,O.prevVal,O.newVal)){O.stopImmediatePropagation();}else{O.newVal=this._getStateVal(O.attrName);}},_getStateVal:function(O){var Y=this._stateProxy;return Y&&(O in Y)&&!this._state.get(O,J)?Y[O]:this._state.get(O,H);},_setStateVal:function(O,c){var Y=this._stateProxy;if(Y&&(O in Y)&&!this._state.get(O,J)){Y[O]=c;}else{this._state.add(O,H,c);}},_setAttrVal:function(l,k,h,f){var n=this,i=true,c=n._state,d=c.get(l,b),g=c.get(l,M),j=c.get(l,B),m=this._getStateVal(l),Y=k||l,e,O;if(d){O=d.call(n,f,Y);if(!O&&j){f=c.get(l,T);O=true;}}if(!d||O){if(g){e=g.call(n,f,Y);if(e===G){i=false;}else{if(e!==undefined){f=e;}}}if(i){if(!k&&(f===m)&&!F.isObject(f)){i=false;}else{if(c.get(l,I)===undefined){c.add(l,I,f);}n._setStateVal(l,f);}}}else{i=false;}return i;},setAttrs:function(O,Y){return this._setAttrs(O,Y);},_setAttrs:function(Y,c){for(var O in Y){if(Y.hasOwnProperty(O)){this.set(O,Y[O]);}}return this;},getAttrs:function(O){return this._getAttrs(O);},_getAttrs:function(d){var f=this,h={},e,Y,O,g,c=(d===true);d=(d&&!c)?d:K.keys(f._state.data.added);for(e=0,Y=d.length;e<Y;e++){O=d[e];g=f.get(O);if(!c||f._getStateVal(O)!=f._state.get(O,I)){h[O]=f.get(O);}}return h;},addAttrs:function(O,Y,c){var d=this;if(O){d._tCfgs=O;d._tVals=d._normAttrVals(Y);d._addAttrs(O,d._tVals,c);d._tCfgs=d._tVals=null;}return d;},_addAttrs:function(Y,c,d){var f=this,O,e,g;for(O in Y){if(Y.hasOwnProperty(O)){e=Y[O];e.defaultValue=e.value;g=f._getAttrInitVal(O,e,f._tVals);if(g!==undefined){e.value=g;}if(f._tCfgs[O]){delete f._tCfgs[O];}f.addAttr(O,e,d);}}},_protectAttrs:function(Y){if(Y){Y=C.merge(Y);for(var O in Y){if(Y.hasOwnProperty(O)){Y[O]=C.merge(Y[O]);}}}return Y;},_normAttrVals:function(O){return(O)?C.merge(O):null;},_getAttrInitVal:function(O,Y,c){var d=(!Y[P]&&c&&c.hasOwnProperty(O))?d=c[O]:(Y[Q])?Y[Q].call(this):Y[H];return d;}};C.mix(D,L,false,null,1);C.Attribute=D;},"3.0.0",{requires:["event-custom"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('attribute-base', function(Y) {
+
+ /**
+ * The State class maintains state for a collection of named items, with
+ * a varying number of properties defined.
+ *
+ * It avoids the need to create a separate class for the item, and separate instances
+ * of these classes for each item, by storing the state in a 2 level hash table,
+ * improving performance when the number of items is likely to be large.
+ *
+ * @constructor
+ * @class State
+ */
+ Y.State = function() {
+ /**
+ * Hash of attributes
+ * @property data
+ */
+ this.data = {};
+ };
+
+ Y.State.prototype = {
+
+ /**
+ * Adds a property to an item.
+ *
+ * @method add
+ * @param name {String} The name of the item.
+ * @param key {String} The name of the property.
+ * @param val {Any} The value of the property.
+ */
+ add : function(name, key, val) {
+ var d = this.data;
+ d[key] = d[key] || {};
+ d[key][name] = val;
+ },
+
+ /**
+ * Adds multiple properties to an item.
+ *
+ * @method addAll
+ * @param name {String} The name of the item.
+ * @param o {Object} A hash of property/value pairs.
+ */
+ addAll: function(name, o) {
+ var key;
+ for (key in o) {
+ if (o.hasOwnProperty(key)) {
+ this.add(name, key, o[key]);
+ }
+ }
+ },
+
+ /**
+ * Removes a property from an item.
+ *
+ * @method remove
+ * @param name {String} The name of the item.
+ * @param key {String} The property to remove.
+ */
+ remove: function(name, key) {
+ var d = this.data;
+ if (d[key] && (name in d[key])) {
+ delete d[key][name];
+ }
+ },
+
+ /**
+ * Removes multiple properties from an item, or remove the item completely.
+ *
+ * @method removeAll
+ * @param name {String} The name of the item.
+ * @param o {Object|Array} Collection of properties to delete. If not provided, the entire item is removed.
+ */
+ removeAll: function(name, o) {
+ var d = this.data;
+
+ Y.each(o || d, function(v, k) {
+ if(Y.Lang.isString(k)) {
+ this.remove(name, k);
+ } else {
+ this.remove(name, v);
+ }
+ }, this);
+ },
+
+ /**
+ * For a given item, returns the value of the property requested, or undefined if not found.
+ *
+ * @method get
+ * @param name {String} The name of the item
+ * @param key {String} Optional. The property value to retrieve.
+ * @return {Any} The value of the supplied property.
+ */
+ get: function(name, key) {
+ var d = this.data;
+ return (d[key] && name in d[key]) ? d[key][name] : undefined;
+ },
+
+ /**
+ * For the given item, returns a disposable object with all of the
+ * item's property/value pairs.
+ *
+ * @method getAll
+ * @param name {String} The name of the item
+ * @return {Object} An object with property/value pairs for the item.
+ */
+ getAll : function(name) {
+ var d = this.data, o;
+
+ Y.each(d, function(v, k) {
+ if (name in d[k]) {
+ o = o || {};
+ o[k] = v[name];
+ }
+ }, this);
+
+ return o;
+ }
+ };
+ /**
+ * The attribute module provides an augmentable Attribute implementation, which
+ * adds configurable attributes and attribute change events to the class being
+ * augmented. It also provides a State class, which is used internally by Attribute,
+ * but can also be used independently to provide a name/property/value data structure to
+ * store state.
+ *
+ * @module attribute
+ */
+
+ /**
+ * The attribute-base submodule provides core attribute handling support, with everything
+ * aside from complex attribute handling in the provider's constructor.
+ *
+ * @module attribute
+ * @submodule attribute-base
+ */
+ var O = Y.Object,
+ Lang = Y.Lang,
+ EventTarget = Y.EventTarget,
+
+ DOT = ".",
+ CHANGE = "Change",
+
+ // Externally configurable props
+ GETTER = "getter",
+ SETTER = "setter",
+ READ_ONLY = "readOnly",
+ WRITE_ONCE = "writeOnce",
+ VALIDATOR = "validator",
+ VALUE = "value",
+ VALUE_FN = "valueFn",
+ BROADCAST = "broadcast",
+ LAZY_ADD = "lazyAdd",
+ BYPASS_PROXY = "_bypassProxy",
+
+ // Used for internal state management
+ ADDED = "added",
+ INITIALIZING = "initializing",
+ INIT_VALUE = "initValue",
+ PUBLISHED = "published",
+ DEF_VALUE = "defaultValue",
+ LAZY = "lazy",
+ IS_LAZY_ADD = "isLazyAdd",
+
+ INVALID_VALUE,
+ MODIFIABLE = {};
+
+ // Properties which can be changed after the attribute has been added.
+ MODIFIABLE[READ_ONLY] = 1;
+ MODIFIABLE[WRITE_ONCE] = 1;
+ MODIFIABLE[GETTER] = 1;
+ MODIFIABLE[BROADCAST] = 1;
+
+ /**
+ * <p>
+ * Attribute provides configurable attribute support along with attribute change events. It is designed to be
+ * augmented on to a host class, and provides the host with the ability to configure attributes to store and retrieve state,
+ * along with attribute change events.
+ * </p>
+ * <p>For example, attributes added to the host can be configured:</p>
+ * <ul>
+ * <li>As read only.</li>
+ * <li>As write once.</li>
+ * <li>With a setter function, which can be used to manipulate
+ * values passed to Attribute's <a href="#method_set">set</a> method, before they are stored.</li>
+ * <li>With a getter function, which can be used to manipulate stored values,
+ * before they are returned by Attribute's <a href="#method_get">get</a> method.</li>
+ * <li>With a validator function, to validate values before they are stored.</li>
+ * </ul>
+ *
+ * <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration
+ * options available for attributes</p>.
+ *
+ * <p><strong>NOTE:</strong> Most implementations will be better off extending the <a href="Base.html">Base</a> class,
+ * instead of augmenting Attribute directly. Base augments Attribute and will handle the initial configuration
+ * of attributes for derived classes, accounting for values passed into the constructor.</p>
+ *
+ * @class Attribute
+ * @uses EventTarget
+ */
+ function Attribute() {
+
+ var host = this, // help compression
+ attrs = this.constructor.ATTRS,
+ Base = Y.Base;
+
+ // Perf tweak - avoid creating event literals if not required.
+ host._ATTR_E_FACADE = {};
+
+ EventTarget.call(host, {emitFacade:true});
+
+ // _conf maintained for backwards compat
+ host._conf = host._state = new Y.State();
+
+ host._stateProxy = host._stateProxy || null;
+ host._requireAddAttr = host._requireAddAttr || false;
+
+ // ATTRS support for Node, which is not Base based
+ if ( attrs && !(Base && host instanceof Base)) {
+ host.addAttrs(this._protectAttrs(attrs));
+ }
+ }
+
+ /**
+ * <p>The value to return from an attribute setter in order to prevent the set from going through.</p>
+ *
+ * <p>You can return this value from your setter if you wish to combine validator and setter
+ * functionality into a single setter function, which either returns the massaged value to be stored or
+ * Attribute.INVALID_VALUE to prevent invalid values from being stored.</p>
+ *
+ * @property Attribute.INVALID_VALUE
+ * @type Object
+ * @static
+ * @final
+ */
+ Attribute.INVALID_VALUE = {};
+ INVALID_VALUE = Attribute.INVALID_VALUE;
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce etc.).
+ *
+ * This property is used internally as a whitelist for faster
+ * Y.mix operations.
+ *
+ * @property Attribute._ATTR_CFG
+ * @type Array
+ * @static
+ * @protected
+ */
+ Attribute._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BROADCAST, BYPASS_PROXY];
+
+ Attribute.prototype = {
+ /**
+ * <p>
+ * Adds an attribute with the provided configuration to the host object.
+ * </p>
+ * <p>
+ * The config argument object supports the following properties:
+ * </p>
+ *
+ * <dl>
+ * <dt>value <Any></dt>
+ * <dd>The initial value to set on the attribute</dd>
+ *
+ * <dt>valueFn <Function></dt>
+ * <dd>A function, which will return the initial value to set on the attribute. This is useful
+ * for cases where the attribute configuration is defined statically, but needs to
+ * reference the host instance ("this") to obtain an initial value.
+ * If defined, this precedence over the value property.</dd>
+ *
+ * <dt>readOnly <boolean></dt>
+ * <dd>Whether or not the attribute is read only. Attributes having readOnly set to true
+ * cannot be modified by invoking the set method.</dd>
+ *
+ * <dt>writeOnce <boolean></dt>
+ * <dd>Whether or not the attribute is "write once". Attributes having writeOnce set to true,
+ * can only have their values set once, be it through the default configuration,
+ * constructor configuration arguments, or by invoking set.</dd>
+ *
+ * <dt>setter <Function></dt>
+ * <dd>The setter function used to massage or normalize the value passed to the set method for the attribute.
+ * The value returned by the setter will be the final stored value. Returning
+ * <a href="#property_Attribute.INVALID_VALUE">Attribute.INVALID_VALUE</a>, from the setter will prevent
+ * the value from being stored.</dd>
+ *
+ * <dt>getter <Function></dt>
+ * <dd>The getter function used to massage or normalize the value returned by the get method for the attribute.
+ * The value returned by the getter function is the value which will be returned to the user when they
+ * invoke get.</dd>
+ *
+ * <dt>validator <Function></dt>
+ * <dd>The validator function invoked prior to setting the stored value. Returning
+ * false from the validator function will prevent the value from being stored.</dd>
+ *
+ * <dt>broadcast <int></dt>
+ * <dd>If and how attribute change events for this attribute should be broadcast. See CustomEvent's <a href="CustomEvent.html#property_broadcast">broadcast</a> property for
+ * valid values. By default attribute change events are not broadcast.</dd>
+ *
+ * <dt>lazyAdd <boolean></dt>
+ * <dd>Whether or not to delay initialization of the attribute until the first call to get/set it.
+ * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through
+ * the <a href="#method_addAttrs">addAttrs</a> method.</dd>
+ *
+ * </dl>
+ *
+ * <p>The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with
+ * the context ("this") set to the host object.</p>
+ *
+ * <p>Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.</p>
+ *
+ * @method addAttr
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute.
+ *
+ * <p>
+ * <strong>NOTE:</strong> The configuration object is modified when adding an attribute, so if you need
+ * to protect the original values, you will need to merge the object.
+ * </p>
+ *
+ * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set).
+ *
+ * @return {Object} A reference to the host object.
+ *
+ * @chainable
+ */
+ addAttr: function(name, config, lazy) {
+
+
+ var host = this, // help compression
+ state = host._state,
+ value,
+ hasValue;
+
+ lazy = (LAZY_ADD in config) ? config[LAZY_ADD] : lazy;
+
+ if (lazy && !host.attrAdded(name)) {
+ state.add(name, LAZY, config || {});
+ state.add(name, ADDED, true);
+ } else {
+
+
+ if (!host.attrAdded(name) || state.get(name, IS_LAZY_ADD)) {
+
+ config = config || {};
+
+ hasValue = (VALUE in config);
+
+
+ if(hasValue) {
+ // We'll go through set, don't want to set value in _state directly
+ value = config.value;
+ delete config.value;
+ }
+
+ config.added = true;
+ config.initializing = true;
+
+ state.addAll(name, config);
+
+ if (hasValue) {
+ // Go through set, so that raw values get normalized/validated
+ host.set(name, value);
+ }
+
+ state.remove(name, INITIALIZING);
+ }
+ }
+
+ return host;
+ },
+
+ /**
+ * Checks if the given attribute has been added to the host
+ *
+ * @method attrAdded
+ * @param {String} name The name of the attribute to check.
+ * @return {boolean} true if an attribute with the given name has been added, false if it hasn't. This method will return true for lazily added attributes.
+ */
+ attrAdded: function(name) {
+ return !!this._state.get(name, ADDED);
+ },
+
+ /**
+ * Updates the configuration of an attribute which has already been added.
+ * <p>
+ * The properties which can be modified through this interface are limited
+ * to the following subset of attributes, which can be safely modified
+ * after a value has already been set on the attribute: readOnly, writeOnce,
+ * broadcast and getter.
+ * </p>
+ * @method modifyAttr
+ * @param {String} name The name of the attribute whose configuration is to be updated.
+ * @param {Object} config An object with configuration property/value pairs, specifying the configuration properties to modify.
+ */
+ modifyAttr: function(name, config) {
+ var host = this, // help compression
+ prop, state;
+
+ if (host.attrAdded(name)) {
+
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ state = host._state;
+ for (prop in config) {
+ if (MODIFIABLE[prop] && config.hasOwnProperty(prop)) {
+ state.add(name, prop, config[prop]);
+
+ // If we reconfigured broadcast, need to republish
+ if (prop === BROADCAST) {
+ state.remove(name, PUBLISHED);
+ }
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Removes an attribute from the host object
+ *
+ * @method removeAttr
+ * @param {String} name The name of the attribute to be removed.
+ */
+ removeAttr: function(name) {
+ this._state.removeAll(name);
+ },
+
+ /**
+ * Returns the current value of the attribute. If the attribute
+ * has been configured with a 'getter' function, this method will delegate
+ * to the 'getter' to obtain the value of the attribute.
+ *
+ * @method get
+ *
+ * @param {String} name The name of the attribute. If the value of the attribute is an Object,
+ * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
+ *
+ * @return {Any} The value of the attribute
+ */
+ get : function(name) {
+ return this._getAttr(name);
+ },
+
+ /**
+ * Checks whether or not the attribute is one which has been
+ * added lazily and still requires initialization.
+ *
+ * @method _isLazyAttr
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {boolean} true if it's a lazily added attribute, false otherwise.
+ */
+ _isLazyAttr: function(name) {
+ return this._state.get(name, LAZY);
+ },
+
+ /**
+ * Finishes initializing an attribute which has been lazily added.
+ *
+ * @method _addLazyAttr
+ * @private
+ * @param {Object} name The name of the attribute
+ */
+ _addLazyAttr: function(name) {
+ var state = this._state,
+ lazyCfg = state.get(name, LAZY);
+
+ state.add(name, IS_LAZY_ADD, true);
+ state.remove(name, LAZY);
+ this.addAttr(name, lazyCfg);
+ },
+
+ /**
+ * Sets the value of an attribute.
+ *
+ * @method set
+ * @chainable
+ *
+ * @param {String} name The name of the attribute. If the
+ * current value of the attribute is an Object, dot notation can be used
+ * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
+ *
+ * @param {Any} value The value to set the attribute to.
+ *
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event. This
+ * can be used as a flexible way to identify the source of a call to set, allowing
+ * the developer to distinguish between set called internally by the host, vs.
+ * set called externally by the application developer.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ set : function(name, val, opts) {
+ return this._setAttr(name, val, opts);
+ },
+
+ /**
+ * Resets the attribute (or all attributes) to its initial value, as long as
+ * the attribute is not readOnly, or writeOnce.
+ *
+ * @method reset
+ * @param {String} name Optional. The name of the attribute to reset. If omitted, all attributes are reset.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ reset : function(name) {
+ var host = this, // help compression
+ added;
+
+ if (name) {
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+ host.set(name, host._state.get(name, INIT_VALUE));
+ } else {
+ added = host._state.data.added;
+ Y.each(added, function(v, n) {
+ host.reset(n);
+ }, host);
+ }
+ return host;
+ },
+
+ /**
+ * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _set
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} val The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @return {Object} A reference to the host object.
+ */
+ _set : function(name, val, opts) {
+ return this._setAttr(name, val, opts, true);
+ },
+
+ /**
+ * Provides the common implementation for the public get method,
+ * allowing Attribute hosts to over-ride either method.
+ *
+ * See <a href="#method_get">get</a> for argument details.
+ *
+ * @method _getAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @return {Any} The value of the attribute.
+ */
+ _getAttr : function(name) {
+ var host = this, // help compression
+ fullName = name,
+ state = host._state,
+ path,
+ getter,
+ val,
+ cfg;
+
+ if (name.indexOf(DOT) !== -1) {
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ // On Demand - Should be rare - handles out of order valueFn references
+ if (host._tCfgs && host._tCfgs[name]) {
+ cfg = {};
+ cfg[name] = host._tCfgs[name];
+ delete host._tCfgs[name];
+ host._addAttrs(cfg, host._tVals);
+ }
+
+ // Lazy Init
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ val = host._getStateVal(name);
+ getter = state.get(name, GETTER);
+
+ val = (getter) ? getter.call(host, val, fullName) : val;
+ val = (path) ? O.getValue(val, path) : val;
+
+ return val;
+ },
+
+ /**
+ * Provides the common implementation for the public set and protected _set methods.
+ *
+ * See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _setAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} value The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @param {boolean} force If true, allows the caller to set values for
+ * readOnly or writeOnce attributes which have already been set.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ _setAttr : function(name, val, opts, force) {
+ var allowSet = true,
+ state = this._state,
+ stateProxy = this._stateProxy,
+ data = state.data,
+ initialSet,
+ strPath,
+ path,
+ currVal;
+
+ if (name.indexOf(DOT) !== -1) {
+ strPath = name;
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ if (this._isLazyAttr(name)) {
+ this._addLazyAttr(name);
+ }
+
+ initialSet = (!data.value || !(name in data.value));
+
+ if (stateProxy && name in stateProxy && !this._state.get(name, BYPASS_PROXY)) {
+ // TODO: Value is always set for proxy. Can we do any better? Maybe take a snapshot as the initial value for the first call to set?
+ initialSet = false;
+ }
+
+ if (this._requireAddAttr && !this.attrAdded(name)) {
+ } else {
+
+ if (!initialSet && !force) {
+
+ if (state.get(name, WRITE_ONCE)) {
+ allowSet = false;
+ }
+
+ if (state.get(name, READ_ONLY)) {
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
+ if (!initialSet) {
+ currVal = this.get(name);
+ }
+
+ if (path) {
+ val = O.setValue(Y.clone(currVal), path, val);
+
+ if (val === undefined) {
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ if (state.get(name, INITIALIZING)) {
+ this._setAttrVal(name, strPath, currVal, val);
+ } else {
+ this._fireAttrChange(name, strPath, currVal, val, opts);
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Utility method to help setup the event payload and fire the attribute change event.
+ *
+ * @method _fireAttrChange
+ * @private
+ * @param {String} attrName The name of the attribute
+ * @param {String} subAttrName The full path of the property being changed,
+ * if this is a sub-attribute value being change. Otherwise null.
+ * @param {Any} currVal The current value of the attribute
+ * @param {Any} newVal The new value of the attribute
+ * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
+ */
+ _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {
+ var host = this,
+ eventName = attrName + CHANGE,
+ state = host._state,
+ facade;
+
+ if (!state.get(attrName, PUBLISHED)) {
+ host.publish(eventName, {
+ queuable:false,
+ defaultFn:host._defAttrChangeFn,
+ silent:true,
+ broadcast : state.get(attrName, BROADCAST)
+ });
+ state.add(attrName, PUBLISHED, true);
+ }
+
+ facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;
+
+ facade.type = eventName;
+ facade.attrName = attrName;
+ facade.subAttrName = subAttrName;
+ facade.prevVal = currVal;
+ facade.newVal = newVal;
+
+ host.fire(facade);
+ },
+
+ /**
+ * Default function for attribute change events.
+ *
+ * @private
+ * @method _defAttrChangeFn
+ * @param {EventFacade} e The event object for attribute change events.
+ */
+ _defAttrChangeFn : function(e) {
+ if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
+ // Prevent "after" listeners from being invoked since nothing changed.
+ e.stopImmediatePropagation();
+ } else {
+ e.newVal = this._getStateVal(e.attrName);
+ }
+ },
+
+ /**
+ * Gets the stored value for the attribute, from either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _getStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {Any} The stored value of the attribute
+ */
+ _getStateVal : function(name) {
+ var stateProxy = this._stateProxy;
+ return stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY) ? stateProxy[name] : this._state.get(name, VALUE);
+ },
+
+ /**
+ * Sets the stored value for the attribute, in either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _setStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @param {Any} value The value of the attribute
+ */
+ _setStateVal : function(name, value) {
+ var stateProxy = this._stateProxy;
+ if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) {
+ stateProxy[name] = value;
+ } else {
+ this._state.add(name, VALUE, value);
+ }
+ },
+
+ /**
+ * Updates the stored value of the attribute in the privately held State object,
+ * if validation and setter passes.
+ *
+ * @method _setAttrVal
+ * @private
+ * @param {String} attrName The attribute name.
+ * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z").
+ * @param {Any} prevVal The currently stored value of the attribute.
+ * @param {Any} newVal The value which is going to be stored.
+ *
+ * @return {booolean} true if the new attribute value was stored, false if not.
+ */
+ _setAttrVal : function(attrName, subAttrName, prevVal, newVal) {
+
+ var host = this,
+ allowSet = true,
+ state = host._state,
+
+ validator = state.get(attrName, VALIDATOR),
+ setter = state.get(attrName, SETTER),
+ initializing = state.get(attrName, INITIALIZING),
+ prevValRaw = this._getStateVal(attrName),
+
+ name = subAttrName || attrName,
+ retVal,
+ valid;
+
+ if (validator) {
+ valid = validator.call(host, newVal, name);
+
+ if (!valid && initializing) {
+ newVal = state.get(attrName, DEF_VALUE);
+ valid = true; // Assume it's valid, for perf.
+ }
+ }
+
+ if (!validator || valid) {
+ if (setter) {
+ retVal = setter.call(host, newVal, name);
+
+ if (retVal === INVALID_VALUE) {
+ allowSet = false;
+ } else if (retVal !== undefined){
+ newVal = retVal;
+ }
+ }
+
+ if (allowSet) {
+ if(!subAttrName && (newVal === prevValRaw) && !Lang.isObject(newVal)) {
+ allowSet = false;
+ } else {
+ // Store value
+ if (state.get(attrName, INIT_VALUE) === undefined) {
+ state.add(attrName, INIT_VALUE, newVal);
+ }
+ host._setStateVal(attrName, newVal);
+ }
+ }
+
+ } else {
+ allowSet = false;
+ }
+
+ return allowSet;
+ },
+
+ /**
+ * Sets multiple attribute values.
+ *
+ * @method setAttrs
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ setAttrs : function(attrs, opts) {
+ return this._setAttrs(attrs, opts);
+ },
+
+ /**
+ * Implementation behind the public setAttrs method, to set multiple attribute values.
+ *
+ * @method _setAttrs
+ * @protected
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ _setAttrs : function(attrs, opts) {
+ for (var attr in attrs) {
+ if ( attrs.hasOwnProperty(attr) ) {
+ this.set(attr, attrs[attr]);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Gets multiple attribute values.
+ *
+ * @method getAttrs
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs : function(attrs) {
+ return this._getAttrs(attrs);
+ },
+
+ /**
+ * Implementation behind the public getAttrs method, to get multiple attribute values.
+ *
+ * @method _getAttrs
+ * @protected
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ _getAttrs : function(attrs) {
+ var host = this,
+ o = {},
+ i, l, attr, val,
+ modifiedOnly = (attrs === true);
+
+ attrs = (attrs && !modifiedOnly) ? attrs : O.keys(host._state.data.added);
+
+ for (i = 0, l = attrs.length; i < l; i++) {
+ // Go through get, to honor cloning/normalization
+ attr = attrs[i];
+ val = host.get(attr);
+
+ if (!modifiedOnly || host._getStateVal(attr) != host._state.get(attr, INIT_VALUE)) {
+ o[attr] = host.get(attr);
+ }
+ }
+
+ return o;
+ },
+
+ /**
+ * Configures a group of attributes, and sets initial values.
+ *
+ * <p>
+ * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning.
+ * The caller is responsible for merging/cloning the configuration object if required.
+ * </p>
+ *
+ * @method addAttrs
+ * @chainable
+ *
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ addAttrs : function(cfgs, values, lazy) {
+ var host = this; // help compression
+ if (cfgs) {
+ host._tCfgs = cfgs;
+ host._tVals = host._normAttrVals(values);
+ host._addAttrs(cfgs, host._tVals, lazy);
+ host._tCfgs = host._tVals = null;
+ }
+
+ return host;
+ },
+
+ /**
+ * Implementation behind the public addAttrs method.
+ *
+ * This method is invoked directly by get if it encounters a scenario
+ * in which an attribute's valueFn attempts to obtain the
+ * value an attribute in the same group of attributes, which has not yet
+ * been added (on demand initialization).
+ *
+ * @method _addAttrs
+ * @private
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ */
+ _addAttrs : function(cfgs, values, lazy) {
+ var host = this, // help compression
+ attr,
+ attrCfg,
+ value;
+
+ for (attr in cfgs) {
+ if (cfgs.hasOwnProperty(attr)) {
+
+ // Not Merging. Caller is responsible for isolating configs
+ attrCfg = cfgs[attr];
+ attrCfg.defaultValue = attrCfg.value;
+
+ // Handle simple, complex and user values, accounting for read-only
+ value = host._getAttrInitVal(attr, attrCfg, host._tVals);
+
+ if (value !== undefined) {
+ attrCfg.value = value;
+ }
+
+ if (host._tCfgs[attr]) {
+ delete host._tCfgs[attr];
+ }
+
+ host.addAttr(attr, attrCfg, lazy);
+ }
+ }
+ },
+
+ /**
+ * Utility method to protect an attribute configuration
+ * hash, by merging the entire object and the individual
+ * attr config objects.
+ *
+ * @method _protectAttrs
+ * @protected
+ * @param {Object} attrs A hash of attribute to configuration object pairs.
+ * @return {Object} A protected version of the attrs argument.
+ */
+ _protectAttrs : function(attrs) {
+ if (attrs) {
+ attrs = Y.merge(attrs);
+ for (var attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+ attrs[attr] = Y.merge(attrs[attr]);
+ }
+ }
+ }
+ return attrs;
+ },
+
+ /**
+ * Utility method to normalize attribute values. The base implementation
+ * simply merges the hash to protect the original.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object}
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ return (valueHash) ? Y.merge(valueHash) : null;
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ // init value is provided by the user if it exists, else, provided by the config
+ var val = (!cfg[READ_ONLY] && initValues && initValues.hasOwnProperty(attr)) ?
+ val = initValues[attr] :
+ (cfg[VALUE_FN]) ?
+ cfg[VALUE_FN].call(this) :
+ cfg[VALUE];
+
+
+ return val;
+ }
+ };
+
+ // Basic prototype augment - no lazy constructor invocation.
+ Y.mix(Attribute, EventTarget, false, null, 1);
+
+ Y.Attribute = Attribute;
+
+
+}, '3.0.0' ,{requires:['event-custom']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('attribute-complex', function(Y) {
+
+ /**
+ * Adds support for attribute providers to handle complex attributes in the constructor
+ *
+ * @module attribute
+ * @submodule attribute-complex
+ * @for Attribute
+ */
+
+ var O = Y.Object,
+ DOT = ".";
+
+ Y.Attribute.Complex = function() {};
+ Y.Attribute.Complex.prototype = {
+
+ /**
+ * Utility method to split out simple attribute name/value pairs ("x")
+ * from complex attribute name/value pairs ("x.y.z"), so that complex
+ * attributes can be keyed by the top level attribute name.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object} An object literal with 2 properties - "simple" and "complex",
+ * containing simple and complex attribute values respectively keyed
+ * by the top level attribute name, or null, if valueHash is falsey.
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ var vals = {},
+ subvals = {},
+ path,
+ attr,
+ v, k;
+
+ if (valueHash) {
+ for (k in valueHash) {
+ if (valueHash.hasOwnProperty(k)) {
+ if (k.indexOf(DOT) !== -1) {
+ path = k.split(DOT);
+ attr = path.shift();
+ v = subvals[attr] = subvals[attr] || [];
+ v[v.length] = {
+ path : path,
+ value: valueHash[k]
+ };
+ } else {
+ vals[k] = valueHash[k];
+ }
+ }
+ }
+ return { simple:vals, complex:subvals };
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ var val = (cfg.valueFn) ? cfg.valueFn.call(this) : cfg.value,
+ simple,
+ complex,
+ i,
+ l,
+ path,
+ subval,
+ subvals;
+
+ if (!cfg.readOnly && initValues) {
+
+ // Simple Attributes
+ simple = initValues.simple;
+ if (simple && simple.hasOwnProperty(attr)) {
+ val = simple[attr];
+ }
+
+ // Complex Attributes (complex values applied, after simple, incase both are set)
+ complex = initValues.complex;
+ if (complex && complex.hasOwnProperty(attr)) {
+ subvals = complex[attr];
+ for (i = 0, l = subvals.length; i < l; ++i) {
+ path = subvals[i].path;
+ subval = subvals[i].value;
+ O.setValue(val, path, subval);
+ }
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.mix(Y.Attribute, Y.Attribute.Complex, true, null, 1);
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("attribute-complex",function(B){var A=B.Object,C=".";B.Attribute.Complex=function(){};B.Attribute.Complex.prototype={_normAttrVals:function(G){var I={},H={},J,D,F,E;if(G){for(E in G){if(G.hasOwnProperty(E)){if(E.indexOf(C)!==-1){J=E.split(C);D=J.shift();F=H[D]=H[D]||[];F[F.length]={path:J,value:G[E]};}else{I[E]=G[E];}}}return{simple:I,complex:H};}else{return null;}},_getAttrInitVal:function(K,I,M){var E=(I.valueFn)?I.valueFn.call(this):I.value,D,F,H,G,N,L,J;if(!I.readOnly&&M){D=M.simple;if(D&&D.hasOwnProperty(K)){E=D[K];}F=M.complex;if(F&&F.hasOwnProperty(K)){J=F[K];for(H=0,G=J.length;H<G;++H){N=J[H].path;L=J[H].value;A.setValue(E,N,L);}}}return E;}};B.mix(B.Attribute,B.Attribute.Complex,true,null,1);},"3.0.0",{requires:["attribute-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('attribute-complex', function(Y) {
+
+ /**
+ * Adds support for attribute providers to handle complex attributes in the constructor
+ *
+ * @module attribute
+ * @submodule attribute-complex
+ * @for Attribute
+ */
+
+ var O = Y.Object,
+ DOT = ".";
+
+ Y.Attribute.Complex = function() {};
+ Y.Attribute.Complex.prototype = {
+
+ /**
+ * Utility method to split out simple attribute name/value pairs ("x")
+ * from complex attribute name/value pairs ("x.y.z"), so that complex
+ * attributes can be keyed by the top level attribute name.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object} An object literal with 2 properties - "simple" and "complex",
+ * containing simple and complex attribute values respectively keyed
+ * by the top level attribute name, or null, if valueHash is falsey.
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ var vals = {},
+ subvals = {},
+ path,
+ attr,
+ v, k;
+
+ if (valueHash) {
+ for (k in valueHash) {
+ if (valueHash.hasOwnProperty(k)) {
+ if (k.indexOf(DOT) !== -1) {
+ path = k.split(DOT);
+ attr = path.shift();
+ v = subvals[attr] = subvals[attr] || [];
+ v[v.length] = {
+ path : path,
+ value: valueHash[k]
+ };
+ } else {
+ vals[k] = valueHash[k];
+ }
+ }
+ }
+ return { simple:vals, complex:subvals };
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ var val = (cfg.valueFn) ? cfg.valueFn.call(this) : cfg.value,
+ simple,
+ complex,
+ i,
+ l,
+ path,
+ subval,
+ subvals;
+
+ if (!cfg.readOnly && initValues) {
+
+ // Simple Attributes
+ simple = initValues.simple;
+ if (simple && simple.hasOwnProperty(attr)) {
+ val = simple[attr];
+ }
+
+ // Complex Attributes (complex values applied, after simple, incase both are set)
+ complex = initValues.complex;
+ if (complex && complex.hasOwnProperty(attr)) {
+ subvals = complex[attr];
+ for (i = 0, l = subvals.length; i < l; ++i) {
+ path = subvals[i].path;
+ subval = subvals[i].value;
+ O.setValue(val, path, subval);
+ }
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.mix(Y.Attribute, Y.Attribute.Complex, true, null, 1);
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('attribute-base', function(Y) {
+
+ /**
+ * The State class maintains state for a collection of named items, with
+ * a varying number of properties defined.
+ *
+ * It avoids the need to create a separate class for the item, and separate instances
+ * of these classes for each item, by storing the state in a 2 level hash table,
+ * improving performance when the number of items is likely to be large.
+ *
+ * @constructor
+ * @class State
+ */
+ Y.State = function() {
+ /**
+ * Hash of attributes
+ * @property data
+ */
+ this.data = {};
+ };
+
+ Y.State.prototype = {
+
+ /**
+ * Adds a property to an item.
+ *
+ * @method add
+ * @param name {String} The name of the item.
+ * @param key {String} The name of the property.
+ * @param val {Any} The value of the property.
+ */
+ add : function(name, key, val) {
+ var d = this.data;
+ d[key] = d[key] || {};
+ d[key][name] = val;
+ },
+
+ /**
+ * Adds multiple properties to an item.
+ *
+ * @method addAll
+ * @param name {String} The name of the item.
+ * @param o {Object} A hash of property/value pairs.
+ */
+ addAll: function(name, o) {
+ var key;
+ for (key in o) {
+ if (o.hasOwnProperty(key)) {
+ this.add(name, key, o[key]);
+ }
+ }
+ },
+
+ /**
+ * Removes a property from an item.
+ *
+ * @method remove
+ * @param name {String} The name of the item.
+ * @param key {String} The property to remove.
+ */
+ remove: function(name, key) {
+ var d = this.data;
+ if (d[key] && (name in d[key])) {
+ delete d[key][name];
+ }
+ },
+
+ /**
+ * Removes multiple properties from an item, or remove the item completely.
+ *
+ * @method removeAll
+ * @param name {String} The name of the item.
+ * @param o {Object|Array} Collection of properties to delete. If not provided, the entire item is removed.
+ */
+ removeAll: function(name, o) {
+ var d = this.data;
+
+ Y.each(o || d, function(v, k) {
+ if(Y.Lang.isString(k)) {
+ this.remove(name, k);
+ } else {
+ this.remove(name, v);
+ }
+ }, this);
+ },
+
+ /**
+ * For a given item, returns the value of the property requested, or undefined if not found.
+ *
+ * @method get
+ * @param name {String} The name of the item
+ * @param key {String} Optional. The property value to retrieve.
+ * @return {Any} The value of the supplied property.
+ */
+ get: function(name, key) {
+ var d = this.data;
+ return (d[key] && name in d[key]) ? d[key][name] : undefined;
+ },
+
+ /**
+ * For the given item, returns a disposable object with all of the
+ * item's property/value pairs.
+ *
+ * @method getAll
+ * @param name {String} The name of the item
+ * @return {Object} An object with property/value pairs for the item.
+ */
+ getAll : function(name) {
+ var d = this.data, o;
+
+ Y.each(d, function(v, k) {
+ if (name in d[k]) {
+ o = o || {};
+ o[k] = v[name];
+ }
+ }, this);
+
+ return o;
+ }
+ };
+ /**
+ * The attribute module provides an augmentable Attribute implementation, which
+ * adds configurable attributes and attribute change events to the class being
+ * augmented. It also provides a State class, which is used internally by Attribute,
+ * but can also be used independently to provide a name/property/value data structure to
+ * store state.
+ *
+ * @module attribute
+ */
+
+ /**
+ * The attribute-base submodule provides core attribute handling support, with everything
+ * aside from complex attribute handling in the provider's constructor.
+ *
+ * @module attribute
+ * @submodule attribute-base
+ */
+ var O = Y.Object,
+ Lang = Y.Lang,
+ EventTarget = Y.EventTarget,
+
+ DOT = ".",
+ CHANGE = "Change",
+
+ // Externally configurable props
+ GETTER = "getter",
+ SETTER = "setter",
+ READ_ONLY = "readOnly",
+ WRITE_ONCE = "writeOnce",
+ VALIDATOR = "validator",
+ VALUE = "value",
+ VALUE_FN = "valueFn",
+ BROADCAST = "broadcast",
+ LAZY_ADD = "lazyAdd",
+ BYPASS_PROXY = "_bypassProxy",
+
+ // Used for internal state management
+ ADDED = "added",
+ INITIALIZING = "initializing",
+ INIT_VALUE = "initValue",
+ PUBLISHED = "published",
+ DEF_VALUE = "defaultValue",
+ LAZY = "lazy",
+ IS_LAZY_ADD = "isLazyAdd",
+
+ INVALID_VALUE,
+ MODIFIABLE = {};
+
+ // Properties which can be changed after the attribute has been added.
+ MODIFIABLE[READ_ONLY] = 1;
+ MODIFIABLE[WRITE_ONCE] = 1;
+ MODIFIABLE[GETTER] = 1;
+ MODIFIABLE[BROADCAST] = 1;
+
+ /**
+ * <p>
+ * Attribute provides configurable attribute support along with attribute change events. It is designed to be
+ * augmented on to a host class, and provides the host with the ability to configure attributes to store and retrieve state,
+ * along with attribute change events.
+ * </p>
+ * <p>For example, attributes added to the host can be configured:</p>
+ * <ul>
+ * <li>As read only.</li>
+ * <li>As write once.</li>
+ * <li>With a setter function, which can be used to manipulate
+ * values passed to Attribute's <a href="#method_set">set</a> method, before they are stored.</li>
+ * <li>With a getter function, which can be used to manipulate stored values,
+ * before they are returned by Attribute's <a href="#method_get">get</a> method.</li>
+ * <li>With a validator function, to validate values before they are stored.</li>
+ * </ul>
+ *
+ * <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration
+ * options available for attributes</p>.
+ *
+ * <p><strong>NOTE:</strong> Most implementations will be better off extending the <a href="Base.html">Base</a> class,
+ * instead of augmenting Attribute directly. Base augments Attribute and will handle the initial configuration
+ * of attributes for derived classes, accounting for values passed into the constructor.</p>
+ *
+ * @class Attribute
+ * @uses EventTarget
+ */
+ function Attribute() {
+ Y.log('Attribute constructor called', 'info', 'attribute');
+
+ var host = this, // help compression
+ attrs = this.constructor.ATTRS,
+ Base = Y.Base;
+
+ // Perf tweak - avoid creating event literals if not required.
+ host._ATTR_E_FACADE = {};
+
+ EventTarget.call(host, {emitFacade:true});
+
+ // _conf maintained for backwards compat
+ host._conf = host._state = new Y.State();
+
+ host._stateProxy = host._stateProxy || null;
+ host._requireAddAttr = host._requireAddAttr || false;
+
+ // ATTRS support for Node, which is not Base based
+ if ( attrs && !(Base && host instanceof Base)) {
+ host.addAttrs(this._protectAttrs(attrs));
+ }
+ }
+
+ /**
+ * <p>The value to return from an attribute setter in order to prevent the set from going through.</p>
+ *
+ * <p>You can return this value from your setter if you wish to combine validator and setter
+ * functionality into a single setter function, which either returns the massaged value to be stored or
+ * Attribute.INVALID_VALUE to prevent invalid values from being stored.</p>
+ *
+ * @property Attribute.INVALID_VALUE
+ * @type Object
+ * @static
+ * @final
+ */
+ Attribute.INVALID_VALUE = {};
+ INVALID_VALUE = Attribute.INVALID_VALUE;
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce etc.).
+ *
+ * This property is used internally as a whitelist for faster
+ * Y.mix operations.
+ *
+ * @property Attribute._ATTR_CFG
+ * @type Array
+ * @static
+ * @protected
+ */
+ Attribute._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BROADCAST, BYPASS_PROXY];
+
+ Attribute.prototype = {
+ /**
+ * <p>
+ * Adds an attribute with the provided configuration to the host object.
+ * </p>
+ * <p>
+ * The config argument object supports the following properties:
+ * </p>
+ *
+ * <dl>
+ * <dt>value <Any></dt>
+ * <dd>The initial value to set on the attribute</dd>
+ *
+ * <dt>valueFn <Function></dt>
+ * <dd>A function, which will return the initial value to set on the attribute. This is useful
+ * for cases where the attribute configuration is defined statically, but needs to
+ * reference the host instance ("this") to obtain an initial value.
+ * If defined, this precedence over the value property.</dd>
+ *
+ * <dt>readOnly <boolean></dt>
+ * <dd>Whether or not the attribute is read only. Attributes having readOnly set to true
+ * cannot be modified by invoking the set method.</dd>
+ *
+ * <dt>writeOnce <boolean></dt>
+ * <dd>Whether or not the attribute is "write once". Attributes having writeOnce set to true,
+ * can only have their values set once, be it through the default configuration,
+ * constructor configuration arguments, or by invoking set.</dd>
+ *
+ * <dt>setter <Function></dt>
+ * <dd>The setter function used to massage or normalize the value passed to the set method for the attribute.
+ * The value returned by the setter will be the final stored value. Returning
+ * <a href="#property_Attribute.INVALID_VALUE">Attribute.INVALID_VALUE</a>, from the setter will prevent
+ * the value from being stored.</dd>
+ *
+ * <dt>getter <Function></dt>
+ * <dd>The getter function used to massage or normalize the value returned by the get method for the attribute.
+ * The value returned by the getter function is the value which will be returned to the user when they
+ * invoke get.</dd>
+ *
+ * <dt>validator <Function></dt>
+ * <dd>The validator function invoked prior to setting the stored value. Returning
+ * false from the validator function will prevent the value from being stored.</dd>
+ *
+ * <dt>broadcast <int></dt>
+ * <dd>If and how attribute change events for this attribute should be broadcast. See CustomEvent's <a href="CustomEvent.html#property_broadcast">broadcast</a> property for
+ * valid values. By default attribute change events are not broadcast.</dd>
+ *
+ * <dt>lazyAdd <boolean></dt>
+ * <dd>Whether or not to delay initialization of the attribute until the first call to get/set it.
+ * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through
+ * the <a href="#method_addAttrs">addAttrs</a> method.</dd>
+ *
+ * </dl>
+ *
+ * <p>The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with
+ * the context ("this") set to the host object.</p>
+ *
+ * <p>Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.</p>
+ *
+ * @method addAttr
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute.
+ *
+ * <p>
+ * <strong>NOTE:</strong> The configuration object is modified when adding an attribute, so if you need
+ * to protect the original values, you will need to merge the object.
+ * </p>
+ *
+ * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set).
+ *
+ * @return {Object} A reference to the host object.
+ *
+ * @chainable
+ */
+ addAttr: function(name, config, lazy) {
+
+ Y.log('Adding attribute: ' + name, 'info', 'attribute');
+
+ var host = this, // help compression
+ state = host._state,
+ value,
+ hasValue;
+
+ lazy = (LAZY_ADD in config) ? config[LAZY_ADD] : lazy;
+
+ if (lazy && !host.attrAdded(name)) {
+ state.add(name, LAZY, config || {});
+ state.add(name, ADDED, true);
+ } else {
+
+ if (host.attrAdded(name) && !state.get(name, IS_LAZY_ADD)) { Y.log('Attribute: ' + name + ' already exists. Cannot add it again without removing it first', 'warn', 'attribute'); }
+
+ if (!host.attrAdded(name) || state.get(name, IS_LAZY_ADD)) {
+
+ config = config || {};
+
+ hasValue = (VALUE in config);
+
+ if (config.readOnly && !hasValue) { Y.log('readOnly attribute: ' + name + ', added without an initial value. Value will be set on initial call to set', 'warn', 'attribute');}
+
+ if(hasValue) {
+ // We'll go through set, don't want to set value in _state directly
+ value = config.value;
+ delete config.value;
+ }
+
+ config.added = true;
+ config.initializing = true;
+
+ state.addAll(name, config);
+
+ if (hasValue) {
+ // Go through set, so that raw values get normalized/validated
+ host.set(name, value);
+ }
+
+ state.remove(name, INITIALIZING);
+ }
+ }
+
+ return host;
+ },
+
+ /**
+ * Checks if the given attribute has been added to the host
+ *
+ * @method attrAdded
+ * @param {String} name The name of the attribute to check.
+ * @return {boolean} true if an attribute with the given name has been added, false if it hasn't. This method will return true for lazily added attributes.
+ */
+ attrAdded: function(name) {
+ return !!this._state.get(name, ADDED);
+ },
+
+ /**
+ * Updates the configuration of an attribute which has already been added.
+ * <p>
+ * The properties which can be modified through this interface are limited
+ * to the following subset of attributes, which can be safely modified
+ * after a value has already been set on the attribute: readOnly, writeOnce,
+ * broadcast and getter.
+ * </p>
+ * @method modifyAttr
+ * @param {String} name The name of the attribute whose configuration is to be updated.
+ * @param {Object} config An object with configuration property/value pairs, specifying the configuration properties to modify.
+ */
+ modifyAttr: function(name, config) {
+ var host = this, // help compression
+ prop, state;
+
+ if (host.attrAdded(name)) {
+
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ state = host._state;
+ for (prop in config) {
+ if (MODIFIABLE[prop] && config.hasOwnProperty(prop)) {
+ state.add(name, prop, config[prop]);
+
+ // If we reconfigured broadcast, need to republish
+ if (prop === BROADCAST) {
+ state.remove(name, PUBLISHED);
+ }
+ }
+ }
+ }
+
+ if (!host.attrAdded(name)) {Y.log('Attribute modifyAttr:' + name + ' has not been added. Use addAttr to add the attribute', 'warn', 'attribute');}
+ },
+
+ /**
+ * Removes an attribute from the host object
+ *
+ * @method removeAttr
+ * @param {String} name The name of the attribute to be removed.
+ */
+ removeAttr: function(name) {
+ this._state.removeAll(name);
+ },
+
+ /**
+ * Returns the current value of the attribute. If the attribute
+ * has been configured with a 'getter' function, this method will delegate
+ * to the 'getter' to obtain the value of the attribute.
+ *
+ * @method get
+ *
+ * @param {String} name The name of the attribute. If the value of the attribute is an Object,
+ * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
+ *
+ * @return {Any} The value of the attribute
+ */
+ get : function(name) {
+ return this._getAttr(name);
+ },
+
+ /**
+ * Checks whether or not the attribute is one which has been
+ * added lazily and still requires initialization.
+ *
+ * @method _isLazyAttr
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {boolean} true if it's a lazily added attribute, false otherwise.
+ */
+ _isLazyAttr: function(name) {
+ return this._state.get(name, LAZY);
+ },
+
+ /**
+ * Finishes initializing an attribute which has been lazily added.
+ *
+ * @method _addLazyAttr
+ * @private
+ * @param {Object} name The name of the attribute
+ */
+ _addLazyAttr: function(name) {
+ var state = this._state,
+ lazyCfg = state.get(name, LAZY);
+
+ state.add(name, IS_LAZY_ADD, true);
+ state.remove(name, LAZY);
+ this.addAttr(name, lazyCfg);
+ },
+
+ /**
+ * Sets the value of an attribute.
+ *
+ * @method set
+ * @chainable
+ *
+ * @param {String} name The name of the attribute. If the
+ * current value of the attribute is an Object, dot notation can be used
+ * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
+ *
+ * @param {Any} value The value to set the attribute to.
+ *
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event. This
+ * can be used as a flexible way to identify the source of a call to set, allowing
+ * the developer to distinguish between set called internally by the host, vs.
+ * set called externally by the application developer.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ set : function(name, val, opts) {
+ return this._setAttr(name, val, opts);
+ },
+
+ /**
+ * Resets the attribute (or all attributes) to its initial value, as long as
+ * the attribute is not readOnly, or writeOnce.
+ *
+ * @method reset
+ * @param {String} name Optional. The name of the attribute to reset. If omitted, all attributes are reset.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ reset : function(name) {
+ var host = this, // help compression
+ added;
+
+ if (name) {
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+ host.set(name, host._state.get(name, INIT_VALUE));
+ } else {
+ added = host._state.data.added;
+ Y.each(added, function(v, n) {
+ host.reset(n);
+ }, host);
+ }
+ return host;
+ },
+
+ /**
+ * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _set
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} val The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @return {Object} A reference to the host object.
+ */
+ _set : function(name, val, opts) {
+ return this._setAttr(name, val, opts, true);
+ },
+
+ /**
+ * Provides the common implementation for the public get method,
+ * allowing Attribute hosts to over-ride either method.
+ *
+ * See <a href="#method_get">get</a> for argument details.
+ *
+ * @method _getAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @return {Any} The value of the attribute.
+ */
+ _getAttr : function(name) {
+ var host = this, // help compression
+ fullName = name,
+ state = host._state,
+ path,
+ getter,
+ val,
+ cfg;
+
+ if (name.indexOf(DOT) !== -1) {
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ // On Demand - Should be rare - handles out of order valueFn references
+ if (host._tCfgs && host._tCfgs[name]) {
+ cfg = {};
+ cfg[name] = host._tCfgs[name];
+ delete host._tCfgs[name];
+ host._addAttrs(cfg, host._tVals);
+ }
+
+ // Lazy Init
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ val = host._getStateVal(name);
+ getter = state.get(name, GETTER);
+
+ val = (getter) ? getter.call(host, val, fullName) : val;
+ val = (path) ? O.getValue(val, path) : val;
+
+ return val;
+ },
+
+ /**
+ * Provides the common implementation for the public set and protected _set methods.
+ *
+ * See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _setAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} value The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @param {boolean} force If true, allows the caller to set values for
+ * readOnly or writeOnce attributes which have already been set.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ _setAttr : function(name, val, opts, force) {
+ var allowSet = true,
+ state = this._state,
+ stateProxy = this._stateProxy,
+ data = state.data,
+ initialSet,
+ strPath,
+ path,
+ currVal;
+
+ if (name.indexOf(DOT) !== -1) {
+ strPath = name;
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ if (this._isLazyAttr(name)) {
+ this._addLazyAttr(name);
+ }
+
+ initialSet = (!data.value || !(name in data.value));
+
+ if (stateProxy && name in stateProxy && !this._state.get(name, BYPASS_PROXY)) {
+ // TODO: Value is always set for proxy. Can we do any better? Maybe take a snapshot as the initial value for the first call to set?
+ initialSet = false;
+ }
+
+ if (this._requireAddAttr && !this.attrAdded(name)) {
+ Y.log('Set attribute:' + name + ', aborted; Attribute is not configured', 'warn', 'attribute');
+ } else {
+
+ if (!initialSet && !force) {
+
+ if (state.get(name, WRITE_ONCE)) {
+ Y.log('Set attribute:' + name + ', aborted; Attribute is writeOnce', 'warn', 'attribute');
+ allowSet = false;
+ }
+
+ if (state.get(name, READ_ONLY)) {
+ Y.log('Set attribute:' + name + ', aborted; Attribute is readOnly', 'warn', 'attribute');
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
+ if (!initialSet) {
+ currVal = this.get(name);
+ }
+
+ if (path) {
+ val = O.setValue(Y.clone(currVal), path, val);
+
+ if (val === undefined) {
+ Y.log('Set attribute path:' + strPath + ', aborted; Path is invalid', 'warn', 'attribute');
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ if (state.get(name, INITIALIZING)) {
+ this._setAttrVal(name, strPath, currVal, val);
+ } else {
+ this._fireAttrChange(name, strPath, currVal, val, opts);
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Utility method to help setup the event payload and fire the attribute change event.
+ *
+ * @method _fireAttrChange
+ * @private
+ * @param {String} attrName The name of the attribute
+ * @param {String} subAttrName The full path of the property being changed,
+ * if this is a sub-attribute value being change. Otherwise null.
+ * @param {Any} currVal The current value of the attribute
+ * @param {Any} newVal The new value of the attribute
+ * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
+ */
+ _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {
+ var host = this,
+ eventName = attrName + CHANGE,
+ state = host._state,
+ facade;
+
+ if (!state.get(attrName, PUBLISHED)) {
+ host.publish(eventName, {
+ queuable:false,
+ defaultFn:host._defAttrChangeFn,
+ silent:true,
+ broadcast : state.get(attrName, BROADCAST)
+ });
+ state.add(attrName, PUBLISHED, true);
+ }
+
+ facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;
+
+ facade.type = eventName;
+ facade.attrName = attrName;
+ facade.subAttrName = subAttrName;
+ facade.prevVal = currVal;
+ facade.newVal = newVal;
+
+ host.fire(facade);
+ },
+
+ /**
+ * Default function for attribute change events.
+ *
+ * @private
+ * @method _defAttrChangeFn
+ * @param {EventFacade} e The event object for attribute change events.
+ */
+ _defAttrChangeFn : function(e) {
+ if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
+ Y.log('State not updated and stopImmediatePropagation called for attribute: ' + e.attrName + ' , value:' + e.newVal, 'warn', 'attribute');
+ // Prevent "after" listeners from being invoked since nothing changed.
+ e.stopImmediatePropagation();
+ } else {
+ e.newVal = this._getStateVal(e.attrName);
+ }
+ },
+
+ /**
+ * Gets the stored value for the attribute, from either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _getStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {Any} The stored value of the attribute
+ */
+ _getStateVal : function(name) {
+ var stateProxy = this._stateProxy;
+ return stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY) ? stateProxy[name] : this._state.get(name, VALUE);
+ },
+
+ /**
+ * Sets the stored value for the attribute, in either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _setStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @param {Any} value The value of the attribute
+ */
+ _setStateVal : function(name, value) {
+ var stateProxy = this._stateProxy;
+ if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) {
+ stateProxy[name] = value;
+ } else {
+ this._state.add(name, VALUE, value);
+ }
+ },
+
+ /**
+ * Updates the stored value of the attribute in the privately held State object,
+ * if validation and setter passes.
+ *
+ * @method _setAttrVal
+ * @private
+ * @param {String} attrName The attribute name.
+ * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z").
+ * @param {Any} prevVal The currently stored value of the attribute.
+ * @param {Any} newVal The value which is going to be stored.
+ *
+ * @return {booolean} true if the new attribute value was stored, false if not.
+ */
+ _setAttrVal : function(attrName, subAttrName, prevVal, newVal) {
+
+ var host = this,
+ allowSet = true,
+ state = host._state,
+
+ validator = state.get(attrName, VALIDATOR),
+ setter = state.get(attrName, SETTER),
+ initializing = state.get(attrName, INITIALIZING),
+ prevValRaw = this._getStateVal(attrName),
+
+ name = subAttrName || attrName,
+ retVal,
+ valid;
+
+ if (validator) {
+ valid = validator.call(host, newVal, name);
+
+ if (!valid && initializing) {
+ newVal = state.get(attrName, DEF_VALUE);
+ valid = true; // Assume it's valid, for perf.
+ }
+ }
+
+ if (!validator || valid) {
+ if (setter) {
+ retVal = setter.call(host, newVal, name);
+
+ if (retVal === INVALID_VALUE) {
+ Y.log('Attribute: ' + attrName + ', setter returned Attribute.INVALID_VALUE for value:' + newVal, 'warn', 'attribute');
+ allowSet = false;
+ } else if (retVal !== undefined){
+ Y.log('Attribute: ' + attrName + ', raw value: ' + newVal + ' modified by setter to:' + retVal, 'info', 'attribute');
+ newVal = retVal;
+ }
+ }
+
+ if (allowSet) {
+ if(!subAttrName && (newVal === prevValRaw) && !Lang.isObject(newVal)) {
+ Y.log('Attribute: ' + attrName + ', value unchanged:' + newVal, 'warn', 'attribute');
+ allowSet = false;
+ } else {
+ // Store value
+ if (state.get(attrName, INIT_VALUE) === undefined) {
+ state.add(attrName, INIT_VALUE, newVal);
+ }
+ host._setStateVal(attrName, newVal);
+ }
+ }
+
+ } else {
+ Y.log('Attribute:' + attrName + ', Validation failed for value:' + newVal, 'warn', 'attribute');
+ allowSet = false;
+ }
+
+ return allowSet;
+ },
+
+ /**
+ * Sets multiple attribute values.
+ *
+ * @method setAttrs
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ setAttrs : function(attrs, opts) {
+ return this._setAttrs(attrs, opts);
+ },
+
+ /**
+ * Implementation behind the public setAttrs method, to set multiple attribute values.
+ *
+ * @method _setAttrs
+ * @protected
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ _setAttrs : function(attrs, opts) {
+ for (var attr in attrs) {
+ if ( attrs.hasOwnProperty(attr) ) {
+ this.set(attr, attrs[attr]);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Gets multiple attribute values.
+ *
+ * @method getAttrs
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs : function(attrs) {
+ return this._getAttrs(attrs);
+ },
+
+ /**
+ * Implementation behind the public getAttrs method, to get multiple attribute values.
+ *
+ * @method _getAttrs
+ * @protected
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ _getAttrs : function(attrs) {
+ var host = this,
+ o = {},
+ i, l, attr, val,
+ modifiedOnly = (attrs === true);
+
+ attrs = (attrs && !modifiedOnly) ? attrs : O.keys(host._state.data.added);
+
+ for (i = 0, l = attrs.length; i < l; i++) {
+ // Go through get, to honor cloning/normalization
+ attr = attrs[i];
+ val = host.get(attr);
+
+ if (!modifiedOnly || host._getStateVal(attr) != host._state.get(attr, INIT_VALUE)) {
+ o[attr] = host.get(attr);
+ }
+ }
+
+ return o;
+ },
+
+ /**
+ * Configures a group of attributes, and sets initial values.
+ *
+ * <p>
+ * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning.
+ * The caller is responsible for merging/cloning the configuration object if required.
+ * </p>
+ *
+ * @method addAttrs
+ * @chainable
+ *
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ addAttrs : function(cfgs, values, lazy) {
+ var host = this; // help compression
+ if (cfgs) {
+ host._tCfgs = cfgs;
+ host._tVals = host._normAttrVals(values);
+ host._addAttrs(cfgs, host._tVals, lazy);
+ host._tCfgs = host._tVals = null;
+ }
+
+ return host;
+ },
+
+ /**
+ * Implementation behind the public addAttrs method.
+ *
+ * This method is invoked directly by get if it encounters a scenario
+ * in which an attribute's valueFn attempts to obtain the
+ * value an attribute in the same group of attributes, which has not yet
+ * been added (on demand initialization).
+ *
+ * @method _addAttrs
+ * @private
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ */
+ _addAttrs : function(cfgs, values, lazy) {
+ var host = this, // help compression
+ attr,
+ attrCfg,
+ value;
+
+ for (attr in cfgs) {
+ if (cfgs.hasOwnProperty(attr)) {
+
+ // Not Merging. Caller is responsible for isolating configs
+ attrCfg = cfgs[attr];
+ attrCfg.defaultValue = attrCfg.value;
+
+ // Handle simple, complex and user values, accounting for read-only
+ value = host._getAttrInitVal(attr, attrCfg, host._tVals);
+
+ if (value !== undefined) {
+ attrCfg.value = value;
+ }
+
+ if (host._tCfgs[attr]) {
+ delete host._tCfgs[attr];
+ }
+
+ host.addAttr(attr, attrCfg, lazy);
+ }
+ }
+ },
+
+ /**
+ * Utility method to protect an attribute configuration
+ * hash, by merging the entire object and the individual
+ * attr config objects.
+ *
+ * @method _protectAttrs
+ * @protected
+ * @param {Object} attrs A hash of attribute to configuration object pairs.
+ * @return {Object} A protected version of the attrs argument.
+ */
+ _protectAttrs : function(attrs) {
+ if (attrs) {
+ attrs = Y.merge(attrs);
+ for (var attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+ attrs[attr] = Y.merge(attrs[attr]);
+ }
+ }
+ }
+ return attrs;
+ },
+
+ /**
+ * Utility method to normalize attribute values. The base implementation
+ * simply merges the hash to protect the original.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object}
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ return (valueHash) ? Y.merge(valueHash) : null;
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ // init value is provided by the user if it exists, else, provided by the config
+ var val = (!cfg[READ_ONLY] && initValues && initValues.hasOwnProperty(attr)) ?
+ val = initValues[attr] :
+ (cfg[VALUE_FN]) ?
+ cfg[VALUE_FN].call(this) :
+ cfg[VALUE];
+
+ Y.log('initValue for ' + attr + ':' + val, 'info', 'attribute');
+
+ return val;
+ }
+ };
+
+ // Basic prototype augment - no lazy constructor invocation.
+ Y.mix(Attribute, EventTarget, false, null, 1);
+
+ Y.Attribute = Attribute;
+
+
+}, '3.0.0' ,{requires:['event-custom']});
+YUI.add('attribute-complex', function(Y) {
+
+ /**
+ * Adds support for attribute providers to handle complex attributes in the constructor
+ *
+ * @module attribute
+ * @submodule attribute-complex
+ * @for Attribute
+ */
+
+ var O = Y.Object,
+ DOT = ".";
+
+ Y.Attribute.Complex = function() {};
+ Y.Attribute.Complex.prototype = {
+
+ /**
+ * Utility method to split out simple attribute name/value pairs ("x")
+ * from complex attribute name/value pairs ("x.y.z"), so that complex
+ * attributes can be keyed by the top level attribute name.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object} An object literal with 2 properties - "simple" and "complex",
+ * containing simple and complex attribute values respectively keyed
+ * by the top level attribute name, or null, if valueHash is falsey.
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ var vals = {},
+ subvals = {},
+ path,
+ attr,
+ v, k;
+
+ if (valueHash) {
+ for (k in valueHash) {
+ if (valueHash.hasOwnProperty(k)) {
+ if (k.indexOf(DOT) !== -1) {
+ path = k.split(DOT);
+ attr = path.shift();
+ v = subvals[attr] = subvals[attr] || [];
+ v[v.length] = {
+ path : path,
+ value: valueHash[k]
+ };
+ } else {
+ vals[k] = valueHash[k];
+ }
+ }
+ }
+ return { simple:vals, complex:subvals };
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ var val = (cfg.valueFn) ? cfg.valueFn.call(this) : cfg.value,
+ simple,
+ complex,
+ i,
+ l,
+ path,
+ subval,
+ subvals;
+
+ if (!cfg.readOnly && initValues) {
+
+ // Simple Attributes
+ simple = initValues.simple;
+ if (simple && simple.hasOwnProperty(attr)) {
+ val = simple[attr];
+ }
+
+ // Complex Attributes (complex values applied, after simple, incase both are set)
+ complex = initValues.complex;
+ if (complex && complex.hasOwnProperty(attr)) {
+ subvals = complex[attr];
+ for (i = 0, l = subvals.length; i < l; ++i) {
+ path = subvals[i].path;
+ subval = subvals[i].value;
+ O.setValue(val, path, subval);
+ }
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.mix(Y.Attribute, Y.Attribute.Complex, true, null, 1);
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
+
+
+YUI.add('attribute', function(Y){}, '3.0.0' ,{use:['attribute-base', 'attribute-complex']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("attribute-base",function(C){C.State=function(){this.data={};};C.State.prototype={add:function(O,Y,e){var c=this.data;c[Y]=c[Y]||{};c[Y][O]=e;},addAll:function(O,c){var Y;for(Y in c){if(c.hasOwnProperty(Y)){this.add(O,Y,c[Y]);}}},remove:function(O,Y){var c=this.data;if(c[Y]&&(O in c[Y])){delete c[Y][O];}},removeAll:function(O,c){var Y=this.data;C.each(c||Y,function(e,d){if(C.Lang.isString(d)){this.remove(O,d);}else{this.remove(O,e);}},this);},get:function(O,Y){var c=this.data;return(c[Y]&&O in c[Y])?c[Y][O]:undefined;},getAll:function(O){var c=this.data,Y;C.each(c,function(e,d){if(O in c[d]){Y=Y||{};Y[d]=e[O];}},this);return Y;}};var K=C.Object,F=C.Lang,L=C.EventTarget,W=".",U="Change",N="getter",M="setter",P="readOnly",X="writeOnce",b="validator",H="value",Q="valueFn",E="broadcast",S="lazyAdd",J="_bypassProxy",a="added",B="initializing",I="initValue",V="published",T="defaultValue",A="lazy",R="isLazyAdd",G,Z={};Z[P]=1;Z[X]=1;Z[N]=1;Z[E]=1;function D(){var c=this,O=this.constructor.ATTRS,Y=C.Base;c._ATTR_E_FACADE={};L.call(c,{emitFacade:true});c._conf=c._state=new C.State();c._stateProxy=c._stateProxy||null;c._requireAddAttr=c._requireAddAttr||false;if(O&&!(Y&&c instanceof Y)){c.addAttrs(this._protectAttrs(O));}}D.INVALID_VALUE={};G=D.INVALID_VALUE;D._ATTR_CFG=[M,N,b,H,Q,X,P,S,E,J];D.prototype={addAttr:function(Y,O,d){var e=this,g=e._state,f,c;d=(S in O)?O[S]:d;if(d&&!e.attrAdded(Y)){g.add(Y,A,O||{});g.add(Y,a,true);}else{if(!e.attrAdded(Y)||g.get(Y,R)){O=O||{};c=(H in O);if(c){f=O.value;delete O.value;}O.added=true;O.initializing=true;g.addAll(Y,O);if(c){e.set(Y,f);}g.remove(Y,B);}}return e;},attrAdded:function(O){return !!this._state.get(O,a);},modifyAttr:function(Y,O){var c=this,e,d;if(c.attrAdded(Y)){if(c._isLazyAttr(Y)){c._addLazyAttr(Y);}d=c._state;for(e in O){if(Z[e]&&O.hasOwnProperty(e)){d.add(Y,e,O[e]);if(e===E){d.remove(Y,V);}}}}},removeAttr:function(O){this._state.removeAll(O);},get:function(O){return this._getAttr(O);},_isLazyAttr:function(O){return this._state.get(O,A);},_addLazyAttr:function(Y){var c=this._state,O=c.get(Y,A);c.add(Y,R,true);c.remove(Y,A);this.addAttr(Y,O);},set:function(O,c,Y){return this._setAttr(O,c,Y);},reset:function(O){var c=this,Y;if(O){if(c._isLazyAttr(O)){c._addLazyAttr(O);}c.set(O,c._state.get(O,I));}else{Y=c._state.data.added;C.each(Y,function(d,e){c.reset(e);},c);}return c;},_set:function(O,c,Y){return this._setAttr(O,c,Y,true);},_getAttr:function(c){var d=this,h=c,e=d._state,f,O,g,Y;if(c.indexOf(W)!==-1){f=c.split(W);c=f.shift();}if(d._tCfgs&&d._tCfgs[c]){Y={};Y[c]=d._tCfgs[c];delete d._tCfgs[c];d._addAttrs(Y,d._tVals);}if(d._isLazyAttr(c)){d._addLazyAttr(c);}g=d._getStateVal(c);O=e.get(c,N);g=(O)?O.call(d,g,h):g;g=(f)?K.getValue(g,f):g;return g;},_setAttr:function(c,f,O,d){var i=true,Y=this._state,g=this._stateProxy,j=Y.data,h,k,l,e;if(c.indexOf(W)!==-1){k=c;l=c.split(W);c=l.shift();}if(this._isLazyAttr(c)){this._addLazyAttr(c);}h=(!j.value||!(c in j.value));if(g&&c in g&&!this._state.get(c,J)){h=false;}if(this._requireAddAttr&&!this.attrAdded(c)){}else{if(!h&&!d){if(Y.get(c,X)){i=false;}if(Y.get(c,P)){i=false;}}if(i){if(!h){e=this.get(c);}if(l){f=K.setValue(C.clone(e),l,f);if(f===undefined){i=false;}}if(i){if(Y.get(c,B)){this._setAttrVal(c,k,e,f);}else{this._fireAttrChange(c,k,e,f,O);}}}}return this;},_fireAttrChange:function(g,f,d,c,O){var i=this,e=g+U,Y=i._state,h;if(!Y.get(g,V)){i.publish(e,{queuable:false,defaultFn:i._defAttrChangeFn,silent:true,broadcast:Y.get(g,E)});Y.add(g,V,true);}h=(O)?C.merge(O):i._ATTR_E_FACADE;h.type=e;h.attrName=g;h.subAttrName=f;h.prevVal=d;h.newVal=c;i.fire(h);},_defAttrChangeFn:function(O){if(!this._setAttrVal(O.attrName,O.subAttrName,O.prevVal,O.newVal)){O.stopImmediatePropagation();}else{O.newVal=this._getStateVal(O.attrName);}},_getStateVal:function(O){var Y=this._stateProxy;return Y&&(O in Y)&&!this._state.get(O,J)?Y[O]:this._state.get(O,H);},_setStateVal:function(O,c){var Y=this._stateProxy;if(Y&&(O in Y)&&!this._state.get(O,J)){Y[O]=c;}else{this._state.add(O,H,c);}},_setAttrVal:function(l,k,h,f){var n=this,i=true,c=n._state,d=c.get(l,b),g=c.get(l,M),j=c.get(l,B),m=this._getStateVal(l),Y=k||l,e,O;if(d){O=d.call(n,f,Y);if(!O&&j){f=c.get(l,T);O=true;}}if(!d||O){if(g){e=g.call(n,f,Y);if(e===G){i=false;}else{if(e!==undefined){f=e;}}}if(i){if(!k&&(f===m)&&!F.isObject(f)){i=false;}else{if(c.get(l,I)===undefined){c.add(l,I,f);}n._setStateVal(l,f);}}}else{i=false;}return i;},setAttrs:function(O,Y){return this._setAttrs(O,Y);},_setAttrs:function(Y,c){for(var O in Y){if(Y.hasOwnProperty(O)){this.set(O,Y[O]);}}return this;},getAttrs:function(O){return this._getAttrs(O);},_getAttrs:function(d){var f=this,h={},e,Y,O,g,c=(d===true);d=(d&&!c)?d:K.keys(f._state.data.added);for(e=0,Y=d.length;e<Y;e++){O=d[e];g=f.get(O);if(!c||f._getStateVal(O)!=f._state.get(O,I)){h[O]=f.get(O);}}return h;},addAttrs:function(O,Y,c){var d=this;if(O){d._tCfgs=O;d._tVals=d._normAttrVals(Y);d._addAttrs(O,d._tVals,c);d._tCfgs=d._tVals=null;}return d;},_addAttrs:function(Y,c,d){var f=this,O,e,g;for(O in Y){if(Y.hasOwnProperty(O)){e=Y[O];e.defaultValue=e.value;g=f._getAttrInitVal(O,e,f._tVals);if(g!==undefined){e.value=g;}if(f._tCfgs[O]){delete f._tCfgs[O];}f.addAttr(O,e,d);}}},_protectAttrs:function(Y){if(Y){Y=C.merge(Y);for(var O in Y){if(Y.hasOwnProperty(O)){Y[O]=C.merge(Y[O]);}}}return Y;},_normAttrVals:function(O){return(O)?C.merge(O):null;},_getAttrInitVal:function(O,Y,c){var d=(!Y[P]&&c&&c.hasOwnProperty(O))?d=c[O]:(Y[Q])?Y[Q].call(this):Y[H];return d;}};C.mix(D,L,false,null,1);C.Attribute=D;},"3.0.0",{requires:["event-custom"]});YUI.add("attribute-complex",function(B){var A=B.Object,C=".";B.Attribute.Complex=function(){};B.Attribute.Complex.prototype={_normAttrVals:function(G){var I={},H={},J,D,F,E;if(G){for(E in G){if(G.hasOwnProperty(E)){if(E.indexOf(C)!==-1){J=E.split(C);D=J.shift();F=H[D]=H[D]||[];F[F.length]={path:J,value:G[E]};}else{I[E]=G[E];}}}return{simple:I,complex:H};}else{return null;}},_getAttrInitVal:function(K,I,M){var E=(I.valueFn)?I.valueFn.call(this):I.value,D,F,H,G,N,L,J;
+if(!I.readOnly&&M){D=M.simple;if(D&&D.hasOwnProperty(K)){E=D[K];}F=M.complex;if(F&&F.hasOwnProperty(K)){J=F[K];for(H=0,G=J.length;H<G;++H){N=J[H].path;L=J[H].value;A.setValue(E,N,L);}}}return E;}};B.mix(B.Attribute,B.Attribute.Complex,true,null,1);},"3.0.0",{requires:["attribute-base"]});YUI.add("attribute",function(A){},"3.0.0",{use:["attribute-base","attribute-complex"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('attribute-base', function(Y) {
+
+ /**
+ * The State class maintains state for a collection of named items, with
+ * a varying number of properties defined.
+ *
+ * It avoids the need to create a separate class for the item, and separate instances
+ * of these classes for each item, by storing the state in a 2 level hash table,
+ * improving performance when the number of items is likely to be large.
+ *
+ * @constructor
+ * @class State
+ */
+ Y.State = function() {
+ /**
+ * Hash of attributes
+ * @property data
+ */
+ this.data = {};
+ };
+
+ Y.State.prototype = {
+
+ /**
+ * Adds a property to an item.
+ *
+ * @method add
+ * @param name {String} The name of the item.
+ * @param key {String} The name of the property.
+ * @param val {Any} The value of the property.
+ */
+ add : function(name, key, val) {
+ var d = this.data;
+ d[key] = d[key] || {};
+ d[key][name] = val;
+ },
+
+ /**
+ * Adds multiple properties to an item.
+ *
+ * @method addAll
+ * @param name {String} The name of the item.
+ * @param o {Object} A hash of property/value pairs.
+ */
+ addAll: function(name, o) {
+ var key;
+ for (key in o) {
+ if (o.hasOwnProperty(key)) {
+ this.add(name, key, o[key]);
+ }
+ }
+ },
+
+ /**
+ * Removes a property from an item.
+ *
+ * @method remove
+ * @param name {String} The name of the item.
+ * @param key {String} The property to remove.
+ */
+ remove: function(name, key) {
+ var d = this.data;
+ if (d[key] && (name in d[key])) {
+ delete d[key][name];
+ }
+ },
+
+ /**
+ * Removes multiple properties from an item, or remove the item completely.
+ *
+ * @method removeAll
+ * @param name {String} The name of the item.
+ * @param o {Object|Array} Collection of properties to delete. If not provided, the entire item is removed.
+ */
+ removeAll: function(name, o) {
+ var d = this.data;
+
+ Y.each(o || d, function(v, k) {
+ if(Y.Lang.isString(k)) {
+ this.remove(name, k);
+ } else {
+ this.remove(name, v);
+ }
+ }, this);
+ },
+
+ /**
+ * For a given item, returns the value of the property requested, or undefined if not found.
+ *
+ * @method get
+ * @param name {String} The name of the item
+ * @param key {String} Optional. The property value to retrieve.
+ * @return {Any} The value of the supplied property.
+ */
+ get: function(name, key) {
+ var d = this.data;
+ return (d[key] && name in d[key]) ? d[key][name] : undefined;
+ },
+
+ /**
+ * For the given item, returns a disposable object with all of the
+ * item's property/value pairs.
+ *
+ * @method getAll
+ * @param name {String} The name of the item
+ * @return {Object} An object with property/value pairs for the item.
+ */
+ getAll : function(name) {
+ var d = this.data, o;
+
+ Y.each(d, function(v, k) {
+ if (name in d[k]) {
+ o = o || {};
+ o[k] = v[name];
+ }
+ }, this);
+
+ return o;
+ }
+ };
+ /**
+ * The attribute module provides an augmentable Attribute implementation, which
+ * adds configurable attributes and attribute change events to the class being
+ * augmented. It also provides a State class, which is used internally by Attribute,
+ * but can also be used independently to provide a name/property/value data structure to
+ * store state.
+ *
+ * @module attribute
+ */
+
+ /**
+ * The attribute-base submodule provides core attribute handling support, with everything
+ * aside from complex attribute handling in the provider's constructor.
+ *
+ * @module attribute
+ * @submodule attribute-base
+ */
+ var O = Y.Object,
+ Lang = Y.Lang,
+ EventTarget = Y.EventTarget,
+
+ DOT = ".",
+ CHANGE = "Change",
+
+ // Externally configurable props
+ GETTER = "getter",
+ SETTER = "setter",
+ READ_ONLY = "readOnly",
+ WRITE_ONCE = "writeOnce",
+ VALIDATOR = "validator",
+ VALUE = "value",
+ VALUE_FN = "valueFn",
+ BROADCAST = "broadcast",
+ LAZY_ADD = "lazyAdd",
+ BYPASS_PROXY = "_bypassProxy",
+
+ // Used for internal state management
+ ADDED = "added",
+ INITIALIZING = "initializing",
+ INIT_VALUE = "initValue",
+ PUBLISHED = "published",
+ DEF_VALUE = "defaultValue",
+ LAZY = "lazy",
+ IS_LAZY_ADD = "isLazyAdd",
+
+ INVALID_VALUE,
+ MODIFIABLE = {};
+
+ // Properties which can be changed after the attribute has been added.
+ MODIFIABLE[READ_ONLY] = 1;
+ MODIFIABLE[WRITE_ONCE] = 1;
+ MODIFIABLE[GETTER] = 1;
+ MODIFIABLE[BROADCAST] = 1;
+
+ /**
+ * <p>
+ * Attribute provides configurable attribute support along with attribute change events. It is designed to be
+ * augmented on to a host class, and provides the host with the ability to configure attributes to store and retrieve state,
+ * along with attribute change events.
+ * </p>
+ * <p>For example, attributes added to the host can be configured:</p>
+ * <ul>
+ * <li>As read only.</li>
+ * <li>As write once.</li>
+ * <li>With a setter function, which can be used to manipulate
+ * values passed to Attribute's <a href="#method_set">set</a> method, before they are stored.</li>
+ * <li>With a getter function, which can be used to manipulate stored values,
+ * before they are returned by Attribute's <a href="#method_get">get</a> method.</li>
+ * <li>With a validator function, to validate values before they are stored.</li>
+ * </ul>
+ *
+ * <p>See the <a href="#method_addAttr">addAttr</a> method, for the complete set of configuration
+ * options available for attributes</p>.
+ *
+ * <p><strong>NOTE:</strong> Most implementations will be better off extending the <a href="Base.html">Base</a> class,
+ * instead of augmenting Attribute directly. Base augments Attribute and will handle the initial configuration
+ * of attributes for derived classes, accounting for values passed into the constructor.</p>
+ *
+ * @class Attribute
+ * @uses EventTarget
+ */
+ function Attribute() {
+
+ var host = this, // help compression
+ attrs = this.constructor.ATTRS,
+ Base = Y.Base;
+
+ // Perf tweak - avoid creating event literals if not required.
+ host._ATTR_E_FACADE = {};
+
+ EventTarget.call(host, {emitFacade:true});
+
+ // _conf maintained for backwards compat
+ host._conf = host._state = new Y.State();
+
+ host._stateProxy = host._stateProxy || null;
+ host._requireAddAttr = host._requireAddAttr || false;
+
+ // ATTRS support for Node, which is not Base based
+ if ( attrs && !(Base && host instanceof Base)) {
+ host.addAttrs(this._protectAttrs(attrs));
+ }
+ }
+
+ /**
+ * <p>The value to return from an attribute setter in order to prevent the set from going through.</p>
+ *
+ * <p>You can return this value from your setter if you wish to combine validator and setter
+ * functionality into a single setter function, which either returns the massaged value to be stored or
+ * Attribute.INVALID_VALUE to prevent invalid values from being stored.</p>
+ *
+ * @property Attribute.INVALID_VALUE
+ * @type Object
+ * @static
+ * @final
+ */
+ Attribute.INVALID_VALUE = {};
+ INVALID_VALUE = Attribute.INVALID_VALUE;
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce etc.).
+ *
+ * This property is used internally as a whitelist for faster
+ * Y.mix operations.
+ *
+ * @property Attribute._ATTR_CFG
+ * @type Array
+ * @static
+ * @protected
+ */
+ Attribute._ATTR_CFG = [SETTER, GETTER, VALIDATOR, VALUE, VALUE_FN, WRITE_ONCE, READ_ONLY, LAZY_ADD, BROADCAST, BYPASS_PROXY];
+
+ Attribute.prototype = {
+ /**
+ * <p>
+ * Adds an attribute with the provided configuration to the host object.
+ * </p>
+ * <p>
+ * The config argument object supports the following properties:
+ * </p>
+ *
+ * <dl>
+ * <dt>value <Any></dt>
+ * <dd>The initial value to set on the attribute</dd>
+ *
+ * <dt>valueFn <Function></dt>
+ * <dd>A function, which will return the initial value to set on the attribute. This is useful
+ * for cases where the attribute configuration is defined statically, but needs to
+ * reference the host instance ("this") to obtain an initial value.
+ * If defined, this precedence over the value property.</dd>
+ *
+ * <dt>readOnly <boolean></dt>
+ * <dd>Whether or not the attribute is read only. Attributes having readOnly set to true
+ * cannot be modified by invoking the set method.</dd>
+ *
+ * <dt>writeOnce <boolean></dt>
+ * <dd>Whether or not the attribute is "write once". Attributes having writeOnce set to true,
+ * can only have their values set once, be it through the default configuration,
+ * constructor configuration arguments, or by invoking set.</dd>
+ *
+ * <dt>setter <Function></dt>
+ * <dd>The setter function used to massage or normalize the value passed to the set method for the attribute.
+ * The value returned by the setter will be the final stored value. Returning
+ * <a href="#property_Attribute.INVALID_VALUE">Attribute.INVALID_VALUE</a>, from the setter will prevent
+ * the value from being stored.</dd>
+ *
+ * <dt>getter <Function></dt>
+ * <dd>The getter function used to massage or normalize the value returned by the get method for the attribute.
+ * The value returned by the getter function is the value which will be returned to the user when they
+ * invoke get.</dd>
+ *
+ * <dt>validator <Function></dt>
+ * <dd>The validator function invoked prior to setting the stored value. Returning
+ * false from the validator function will prevent the value from being stored.</dd>
+ *
+ * <dt>broadcast <int></dt>
+ * <dd>If and how attribute change events for this attribute should be broadcast. See CustomEvent's <a href="CustomEvent.html#property_broadcast">broadcast</a> property for
+ * valid values. By default attribute change events are not broadcast.</dd>
+ *
+ * <dt>lazyAdd <boolean></dt>
+ * <dd>Whether or not to delay initialization of the attribute until the first call to get/set it.
+ * This flag can be used to over-ride lazy initialization on a per attribute basis, when adding multiple attributes through
+ * the <a href="#method_addAttrs">addAttrs</a> method.</dd>
+ *
+ * </dl>
+ *
+ * <p>The setter, getter and validator are invoked with the value and name passed in as the first and second arguments, and with
+ * the context ("this") set to the host object.</p>
+ *
+ * <p>Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.</p>
+ *
+ * @method addAttr
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Object} config An object with attribute configuration property/value pairs, specifying the configuration for the attribute.
+ *
+ * <p>
+ * <strong>NOTE:</strong> The configuration object is modified when adding an attribute, so if you need
+ * to protect the original values, you will need to merge the object.
+ * </p>
+ *
+ * @param {boolean} lazy (optional) Whether or not to add this attribute lazily (on the first call to get/set).
+ *
+ * @return {Object} A reference to the host object.
+ *
+ * @chainable
+ */
+ addAttr: function(name, config, lazy) {
+
+
+ var host = this, // help compression
+ state = host._state,
+ value,
+ hasValue;
+
+ lazy = (LAZY_ADD in config) ? config[LAZY_ADD] : lazy;
+
+ if (lazy && !host.attrAdded(name)) {
+ state.add(name, LAZY, config || {});
+ state.add(name, ADDED, true);
+ } else {
+
+
+ if (!host.attrAdded(name) || state.get(name, IS_LAZY_ADD)) {
+
+ config = config || {};
+
+ hasValue = (VALUE in config);
+
+
+ if(hasValue) {
+ // We'll go through set, don't want to set value in _state directly
+ value = config.value;
+ delete config.value;
+ }
+
+ config.added = true;
+ config.initializing = true;
+
+ state.addAll(name, config);
+
+ if (hasValue) {
+ // Go through set, so that raw values get normalized/validated
+ host.set(name, value);
+ }
+
+ state.remove(name, INITIALIZING);
+ }
+ }
+
+ return host;
+ },
+
+ /**
+ * Checks if the given attribute has been added to the host
+ *
+ * @method attrAdded
+ * @param {String} name The name of the attribute to check.
+ * @return {boolean} true if an attribute with the given name has been added, false if it hasn't. This method will return true for lazily added attributes.
+ */
+ attrAdded: function(name) {
+ return !!this._state.get(name, ADDED);
+ },
+
+ /**
+ * Updates the configuration of an attribute which has already been added.
+ * <p>
+ * The properties which can be modified through this interface are limited
+ * to the following subset of attributes, which can be safely modified
+ * after a value has already been set on the attribute: readOnly, writeOnce,
+ * broadcast and getter.
+ * </p>
+ * @method modifyAttr
+ * @param {String} name The name of the attribute whose configuration is to be updated.
+ * @param {Object} config An object with configuration property/value pairs, specifying the configuration properties to modify.
+ */
+ modifyAttr: function(name, config) {
+ var host = this, // help compression
+ prop, state;
+
+ if (host.attrAdded(name)) {
+
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ state = host._state;
+ for (prop in config) {
+ if (MODIFIABLE[prop] && config.hasOwnProperty(prop)) {
+ state.add(name, prop, config[prop]);
+
+ // If we reconfigured broadcast, need to republish
+ if (prop === BROADCAST) {
+ state.remove(name, PUBLISHED);
+ }
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Removes an attribute from the host object
+ *
+ * @method removeAttr
+ * @param {String} name The name of the attribute to be removed.
+ */
+ removeAttr: function(name) {
+ this._state.removeAll(name);
+ },
+
+ /**
+ * Returns the current value of the attribute. If the attribute
+ * has been configured with a 'getter' function, this method will delegate
+ * to the 'getter' to obtain the value of the attribute.
+ *
+ * @method get
+ *
+ * @param {String} name The name of the attribute. If the value of the attribute is an Object,
+ * dot notation can be used to obtain the value of a property of the object (e.g. <code>get("x.y.z")</code>)
+ *
+ * @return {Any} The value of the attribute
+ */
+ get : function(name) {
+ return this._getAttr(name);
+ },
+
+ /**
+ * Checks whether or not the attribute is one which has been
+ * added lazily and still requires initialization.
+ *
+ * @method _isLazyAttr
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {boolean} true if it's a lazily added attribute, false otherwise.
+ */
+ _isLazyAttr: function(name) {
+ return this._state.get(name, LAZY);
+ },
+
+ /**
+ * Finishes initializing an attribute which has been lazily added.
+ *
+ * @method _addLazyAttr
+ * @private
+ * @param {Object} name The name of the attribute
+ */
+ _addLazyAttr: function(name) {
+ var state = this._state,
+ lazyCfg = state.get(name, LAZY);
+
+ state.add(name, IS_LAZY_ADD, true);
+ state.remove(name, LAZY);
+ this.addAttr(name, lazyCfg);
+ },
+
+ /**
+ * Sets the value of an attribute.
+ *
+ * @method set
+ * @chainable
+ *
+ * @param {String} name The name of the attribute. If the
+ * current value of the attribute is an Object, dot notation can be used
+ * to set the value of a property within the object (e.g. <code>set("x.y.z", 5)</code>).
+ *
+ * @param {Any} value The value to set the attribute to.
+ *
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event. This
+ * can be used as a flexible way to identify the source of a call to set, allowing
+ * the developer to distinguish between set called internally by the host, vs.
+ * set called externally by the application developer.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ set : function(name, val, opts) {
+ return this._setAttr(name, val, opts);
+ },
+
+ /**
+ * Resets the attribute (or all attributes) to its initial value, as long as
+ * the attribute is not readOnly, or writeOnce.
+ *
+ * @method reset
+ * @param {String} name Optional. The name of the attribute to reset. If omitted, all attributes are reset.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ reset : function(name) {
+ var host = this, // help compression
+ added;
+
+ if (name) {
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+ host.set(name, host._state.get(name, INIT_VALUE));
+ } else {
+ added = host._state.data.added;
+ Y.each(added, function(v, n) {
+ host.reset(n);
+ }, host);
+ }
+ return host;
+ },
+
+ /**
+ * Allows setting of readOnly/writeOnce attributes. See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _set
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} val The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @return {Object} A reference to the host object.
+ */
+ _set : function(name, val, opts) {
+ return this._setAttr(name, val, opts, true);
+ },
+
+ /**
+ * Provides the common implementation for the public get method,
+ * allowing Attribute hosts to over-ride either method.
+ *
+ * See <a href="#method_get">get</a> for argument details.
+ *
+ * @method _getAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @return {Any} The value of the attribute.
+ */
+ _getAttr : function(name) {
+ var host = this, // help compression
+ fullName = name,
+ state = host._state,
+ path,
+ getter,
+ val,
+ cfg;
+
+ if (name.indexOf(DOT) !== -1) {
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ // On Demand - Should be rare - handles out of order valueFn references
+ if (host._tCfgs && host._tCfgs[name]) {
+ cfg = {};
+ cfg[name] = host._tCfgs[name];
+ delete host._tCfgs[name];
+ host._addAttrs(cfg, host._tVals);
+ }
+
+ // Lazy Init
+ if (host._isLazyAttr(name)) {
+ host._addLazyAttr(name);
+ }
+
+ val = host._getStateVal(name);
+ getter = state.get(name, GETTER);
+
+ val = (getter) ? getter.call(host, val, fullName) : val;
+ val = (path) ? O.getValue(val, path) : val;
+
+ return val;
+ },
+
+ /**
+ * Provides the common implementation for the public set and protected _set methods.
+ *
+ * See <a href="#method_set">set</a> for argument details.
+ *
+ * @method _setAttr
+ * @protected
+ * @chainable
+ *
+ * @param {String} name The name of the attribute.
+ * @param {Any} value The value to set the attribute to.
+ * @param {Object} opts (Optional) Optional event data to be mixed into
+ * the event facade passed to subscribers of the attribute's change event.
+ * @param {boolean} force If true, allows the caller to set values for
+ * readOnly or writeOnce attributes which have already been set.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ _setAttr : function(name, val, opts, force) {
+ var allowSet = true,
+ state = this._state,
+ stateProxy = this._stateProxy,
+ data = state.data,
+ initialSet,
+ strPath,
+ path,
+ currVal;
+
+ if (name.indexOf(DOT) !== -1) {
+ strPath = name;
+ path = name.split(DOT);
+ name = path.shift();
+ }
+
+ if (this._isLazyAttr(name)) {
+ this._addLazyAttr(name);
+ }
+
+ initialSet = (!data.value || !(name in data.value));
+
+ if (stateProxy && name in stateProxy && !this._state.get(name, BYPASS_PROXY)) {
+ // TODO: Value is always set for proxy. Can we do any better? Maybe take a snapshot as the initial value for the first call to set?
+ initialSet = false;
+ }
+
+ if (this._requireAddAttr && !this.attrAdded(name)) {
+ } else {
+
+ if (!initialSet && !force) {
+
+ if (state.get(name, WRITE_ONCE)) {
+ allowSet = false;
+ }
+
+ if (state.get(name, READ_ONLY)) {
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ // Don't need currVal if initialSet (might fail in custom getter if it always expects a non-undefined/non-null value)
+ if (!initialSet) {
+ currVal = this.get(name);
+ }
+
+ if (path) {
+ val = O.setValue(Y.clone(currVal), path, val);
+
+ if (val === undefined) {
+ allowSet = false;
+ }
+ }
+
+ if (allowSet) {
+ if (state.get(name, INITIALIZING)) {
+ this._setAttrVal(name, strPath, currVal, val);
+ } else {
+ this._fireAttrChange(name, strPath, currVal, val, opts);
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Utility method to help setup the event payload and fire the attribute change event.
+ *
+ * @method _fireAttrChange
+ * @private
+ * @param {String} attrName The name of the attribute
+ * @param {String} subAttrName The full path of the property being changed,
+ * if this is a sub-attribute value being change. Otherwise null.
+ * @param {Any} currVal The current value of the attribute
+ * @param {Any} newVal The new value of the attribute
+ * @param {Object} opts Any additional event data to mix into the attribute change event's event facade.
+ */
+ _fireAttrChange : function(attrName, subAttrName, currVal, newVal, opts) {
+ var host = this,
+ eventName = attrName + CHANGE,
+ state = host._state,
+ facade;
+
+ if (!state.get(attrName, PUBLISHED)) {
+ host.publish(eventName, {
+ queuable:false,
+ defaultFn:host._defAttrChangeFn,
+ silent:true,
+ broadcast : state.get(attrName, BROADCAST)
+ });
+ state.add(attrName, PUBLISHED, true);
+ }
+
+ facade = (opts) ? Y.merge(opts) : host._ATTR_E_FACADE;
+
+ facade.type = eventName;
+ facade.attrName = attrName;
+ facade.subAttrName = subAttrName;
+ facade.prevVal = currVal;
+ facade.newVal = newVal;
+
+ host.fire(facade);
+ },
+
+ /**
+ * Default function for attribute change events.
+ *
+ * @private
+ * @method _defAttrChangeFn
+ * @param {EventFacade} e The event object for attribute change events.
+ */
+ _defAttrChangeFn : function(e) {
+ if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
+ // Prevent "after" listeners from being invoked since nothing changed.
+ e.stopImmediatePropagation();
+ } else {
+ e.newVal = this._getStateVal(e.attrName);
+ }
+ },
+
+ /**
+ * Gets the stored value for the attribute, from either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _getStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @return {Any} The stored value of the attribute
+ */
+ _getStateVal : function(name) {
+ var stateProxy = this._stateProxy;
+ return stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY) ? stateProxy[name] : this._state.get(name, VALUE);
+ },
+
+ /**
+ * Sets the stored value for the attribute, in either the
+ * internal state object, or the state proxy if it exits
+ *
+ * @method _setStateVal
+ * @private
+ * @param {String} name The name of the attribute
+ * @param {Any} value The value of the attribute
+ */
+ _setStateVal : function(name, value) {
+ var stateProxy = this._stateProxy;
+ if (stateProxy && (name in stateProxy) && !this._state.get(name, BYPASS_PROXY)) {
+ stateProxy[name] = value;
+ } else {
+ this._state.add(name, VALUE, value);
+ }
+ },
+
+ /**
+ * Updates the stored value of the attribute in the privately held State object,
+ * if validation and setter passes.
+ *
+ * @method _setAttrVal
+ * @private
+ * @param {String} attrName The attribute name.
+ * @param {String} subAttrName The sub-attribute name, if setting a sub-attribute property ("x.y.z").
+ * @param {Any} prevVal The currently stored value of the attribute.
+ * @param {Any} newVal The value which is going to be stored.
+ *
+ * @return {booolean} true if the new attribute value was stored, false if not.
+ */
+ _setAttrVal : function(attrName, subAttrName, prevVal, newVal) {
+
+ var host = this,
+ allowSet = true,
+ state = host._state,
+
+ validator = state.get(attrName, VALIDATOR),
+ setter = state.get(attrName, SETTER),
+ initializing = state.get(attrName, INITIALIZING),
+ prevValRaw = this._getStateVal(attrName),
+
+ name = subAttrName || attrName,
+ retVal,
+ valid;
+
+ if (validator) {
+ valid = validator.call(host, newVal, name);
+
+ if (!valid && initializing) {
+ newVal = state.get(attrName, DEF_VALUE);
+ valid = true; // Assume it's valid, for perf.
+ }
+ }
+
+ if (!validator || valid) {
+ if (setter) {
+ retVal = setter.call(host, newVal, name);
+
+ if (retVal === INVALID_VALUE) {
+ allowSet = false;
+ } else if (retVal !== undefined){
+ newVal = retVal;
+ }
+ }
+
+ if (allowSet) {
+ if(!subAttrName && (newVal === prevValRaw) && !Lang.isObject(newVal)) {
+ allowSet = false;
+ } else {
+ // Store value
+ if (state.get(attrName, INIT_VALUE) === undefined) {
+ state.add(attrName, INIT_VALUE, newVal);
+ }
+ host._setStateVal(attrName, newVal);
+ }
+ }
+
+ } else {
+ allowSet = false;
+ }
+
+ return allowSet;
+ },
+
+ /**
+ * Sets multiple attribute values.
+ *
+ * @method setAttrs
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ setAttrs : function(attrs, opts) {
+ return this._setAttrs(attrs, opts);
+ },
+
+ /**
+ * Implementation behind the public setAttrs method, to set multiple attribute values.
+ *
+ * @method _setAttrs
+ * @protected
+ * @param {Object} attrs An object with attributes name/value pairs.
+ * @return {Object} A reference to the host object.
+ * @chainable
+ */
+ _setAttrs : function(attrs, opts) {
+ for (var attr in attrs) {
+ if ( attrs.hasOwnProperty(attr) ) {
+ this.set(attr, attrs[attr]);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Gets multiple attribute values.
+ *
+ * @method getAttrs
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs : function(attrs) {
+ return this._getAttrs(attrs);
+ },
+
+ /**
+ * Implementation behind the public getAttrs method, to get multiple attribute values.
+ *
+ * @method _getAttrs
+ * @protected
+ * @param {Array | boolean} attrs Optional. An array of attribute names. If omitted, all attribute values are
+ * returned. If set to true, all attributes modified from their initial values are returned.
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ _getAttrs : function(attrs) {
+ var host = this,
+ o = {},
+ i, l, attr, val,
+ modifiedOnly = (attrs === true);
+
+ attrs = (attrs && !modifiedOnly) ? attrs : O.keys(host._state.data.added);
+
+ for (i = 0, l = attrs.length; i < l; i++) {
+ // Go through get, to honor cloning/normalization
+ attr = attrs[i];
+ val = host.get(attr);
+
+ if (!modifiedOnly || host._getStateVal(attr) != host._state.get(attr, INIT_VALUE)) {
+ o[attr] = host.get(attr);
+ }
+ }
+
+ return o;
+ },
+
+ /**
+ * Configures a group of attributes, and sets initial values.
+ *
+ * <p>
+ * <strong>NOTE:</strong> This method does not isolate the configuration object by merging/cloning.
+ * The caller is responsible for merging/cloning the configuration object if required.
+ * </p>
+ *
+ * @method addAttrs
+ * @chainable
+ *
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ *
+ * @return {Object} A reference to the host object.
+ */
+ addAttrs : function(cfgs, values, lazy) {
+ var host = this; // help compression
+ if (cfgs) {
+ host._tCfgs = cfgs;
+ host._tVals = host._normAttrVals(values);
+ host._addAttrs(cfgs, host._tVals, lazy);
+ host._tCfgs = host._tVals = null;
+ }
+
+ return host;
+ },
+
+ /**
+ * Implementation behind the public addAttrs method.
+ *
+ * This method is invoked directly by get if it encounters a scenario
+ * in which an attribute's valueFn attempts to obtain the
+ * value an attribute in the same group of attributes, which has not yet
+ * been added (on demand initialization).
+ *
+ * @method _addAttrs
+ * @private
+ * @param {Object} cfgs An object with attribute name/configuration pairs.
+ * @param {Object} values An object with attribute name/value pairs, defining the initial values to apply.
+ * Values defined in the cfgs argument will be over-written by values in this argument unless defined as read only.
+ * @param {boolean} lazy Whether or not to delay the intialization of these attributes until the first call to get/set.
+ * Individual attributes can over-ride this behavior by defining a lazyAdd configuration property in their configuration.
+ * See <a href="#method_addAttr">addAttr</a>.
+ */
+ _addAttrs : function(cfgs, values, lazy) {
+ var host = this, // help compression
+ attr,
+ attrCfg,
+ value;
+
+ for (attr in cfgs) {
+ if (cfgs.hasOwnProperty(attr)) {
+
+ // Not Merging. Caller is responsible for isolating configs
+ attrCfg = cfgs[attr];
+ attrCfg.defaultValue = attrCfg.value;
+
+ // Handle simple, complex and user values, accounting for read-only
+ value = host._getAttrInitVal(attr, attrCfg, host._tVals);
+
+ if (value !== undefined) {
+ attrCfg.value = value;
+ }
+
+ if (host._tCfgs[attr]) {
+ delete host._tCfgs[attr];
+ }
+
+ host.addAttr(attr, attrCfg, lazy);
+ }
+ }
+ },
+
+ /**
+ * Utility method to protect an attribute configuration
+ * hash, by merging the entire object and the individual
+ * attr config objects.
+ *
+ * @method _protectAttrs
+ * @protected
+ * @param {Object} attrs A hash of attribute to configuration object pairs.
+ * @return {Object} A protected version of the attrs argument.
+ */
+ _protectAttrs : function(attrs) {
+ if (attrs) {
+ attrs = Y.merge(attrs);
+ for (var attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+ attrs[attr] = Y.merge(attrs[attr]);
+ }
+ }
+ }
+ return attrs;
+ },
+
+ /**
+ * Utility method to normalize attribute values. The base implementation
+ * simply merges the hash to protect the original.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object}
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ return (valueHash) ? Y.merge(valueHash) : null;
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ // init value is provided by the user if it exists, else, provided by the config
+ var val = (!cfg[READ_ONLY] && initValues && initValues.hasOwnProperty(attr)) ?
+ val = initValues[attr] :
+ (cfg[VALUE_FN]) ?
+ cfg[VALUE_FN].call(this) :
+ cfg[VALUE];
+
+
+ return val;
+ }
+ };
+
+ // Basic prototype augment - no lazy constructor invocation.
+ Y.mix(Attribute, EventTarget, false, null, 1);
+
+ Y.Attribute = Attribute;
+
+
+}, '3.0.0' ,{requires:['event-custom']});
+YUI.add('attribute-complex', function(Y) {
+
+ /**
+ * Adds support for attribute providers to handle complex attributes in the constructor
+ *
+ * @module attribute
+ * @submodule attribute-complex
+ * @for Attribute
+ */
+
+ var O = Y.Object,
+ DOT = ".";
+
+ Y.Attribute.Complex = function() {};
+ Y.Attribute.Complex.prototype = {
+
+ /**
+ * Utility method to split out simple attribute name/value pairs ("x")
+ * from complex attribute name/value pairs ("x.y.z"), so that complex
+ * attributes can be keyed by the top level attribute name.
+ *
+ * @method _normAttrVals
+ * @param {Object} valueHash An object with attribute name/value pairs
+ *
+ * @return {Object} An object literal with 2 properties - "simple" and "complex",
+ * containing simple and complex attribute values respectively keyed
+ * by the top level attribute name, or null, if valueHash is falsey.
+ *
+ * @private
+ */
+ _normAttrVals : function(valueHash) {
+ var vals = {},
+ subvals = {},
+ path,
+ attr,
+ v, k;
+
+ if (valueHash) {
+ for (k in valueHash) {
+ if (valueHash.hasOwnProperty(k)) {
+ if (k.indexOf(DOT) !== -1) {
+ path = k.split(DOT);
+ attr = path.shift();
+ v = subvals[attr] = subvals[attr] || [];
+ v[v.length] = {
+ path : path,
+ value: valueHash[k]
+ };
+ } else {
+ vals[k] = valueHash[k];
+ }
+ }
+ }
+ return { simple:vals, complex:subvals };
+ } else {
+ return null;
+ }
+ },
+
+ /**
+ * Returns the initial value of the given attribute from
+ * either the default configuration provided, or the
+ * over-ridden value if it exists in the set of initValues
+ * provided and the attribute is not read-only.
+ *
+ * @param {String} attr The name of the attribute
+ * @param {Object} cfg The attribute configuration object
+ * @param {Object} initValues The object with simple and complex attribute name/value pairs returned from _normAttrVals
+ *
+ * @return {Any} The initial value of the attribute.
+ *
+ * @method _getAttrInitVal
+ * @private
+ */
+ _getAttrInitVal : function(attr, cfg, initValues) {
+
+ var val = (cfg.valueFn) ? cfg.valueFn.call(this) : cfg.value,
+ simple,
+ complex,
+ i,
+ l,
+ path,
+ subval,
+ subvals;
+
+ if (!cfg.readOnly && initValues) {
+
+ // Simple Attributes
+ simple = initValues.simple;
+ if (simple && simple.hasOwnProperty(attr)) {
+ val = simple[attr];
+ }
+
+ // Complex Attributes (complex values applied, after simple, incase both are set)
+ complex = initValues.complex;
+ if (complex && complex.hasOwnProperty(attr)) {
+ subvals = complex[attr];
+ for (i = 0, l = subvals.length; i < l; ++i) {
+ path = subvals[i].path;
+ subval = subvals[i].value;
+ O.setValue(val, path, subval);
+ }
+ }
+ }
+
+ return val;
+ }
+ };
+
+ Y.mix(Y.Attribute, Y.Attribute.Complex, true, null, 1);
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
+
+
+YUI.add('attribute', function(Y){}, '3.0.0' ,{use:['attribute-base', 'attribute-complex']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-base', function(Y) {
+
+ /**
+ * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
+ * The module also provides two ways to reuse code - An augmentable Plugin.Host interface which provides plugin support
+ * (which is augmented to the Base class) and Base.build which provides a way to
+ * build custom classes using extensions.
+ *
+ * @module base
+ */
+
+ /**
+ * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host,
+ * and without the extension support provided by Base.build.
+ *
+ * @module base
+ * @submodule base-base
+ */
+ var O = Y.Object,
+ L = Y.Lang,
+ DOT = ".",
+ DESTROY = "destroy",
+ INIT = "init",
+ INITIALIZED = "initialized",
+ DESTROYED = "destroyed",
+ INITIALIZER = "initializer",
+ OBJECT_CONSTRUCTOR = Object.prototype.constructor,
+ DEEP = "deep",
+ SHALLOW = "shallow",
+ DESTRUCTOR = "destructor",
+
+ Attribute = Y.Attribute;
+
+ /**
+ * <p>
+ * A base class which objects requiring attributes and custom event support can
+ * extend. Base also handles the chaining of initializer and destructor methods across
+ * the hierarchy as part of object construction and destruction. Additionally, attributes configured
+ * through the static <a href="#property_Base.ATTRS">ATTRS</a> property for each class
+ * in the hierarchy will be initialized by Base.
+ * </p>
+ *
+ * <p>
+ * The static <a href="#property_Base.NAME">NAME</a> property of each class extending
+ * from Base will be used as the identifier for the class, and is used by Base to prefix
+ * all events fired by instances of that class.
+ * </p>
+ * @class Base
+ * @constructor
+ * @uses Attribute
+ * @uses Plugin.Host
+ *
+ * @param {Object} config Object with configuration property name/value pairs
+ */
+ function Base() {
+ Y.log('constructor called', 'life', 'base');
+
+ Attribute.call(this);
+
+ // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's
+ // initial state, but don't initialize Plugins yet. That's done after initialization.
+ var PluginHost = Y.Plugin && Y.Plugin.Host;
+ if (this._initPlugins && PluginHost) {
+ PluginHost.call(this);
+ }
+
+ if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; }
+
+ this.init.apply(this, arguments);
+ }
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce, readOnly etc.)
+ *
+ * @property Base._ATTR_CFG
+ * @type Array
+ * @static
+ * @private
+ */
+ Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue");
+
+ /**
+ * <p>
+ * The string to be used to identify instances of
+ * this class, for example in prefixing events.
+ * </p>
+ * <p>
+ * Classes extending Base, should define their own
+ * static NAME property, which should be camelCase by
+ * convention (e.g. MyClass.NAME = "myClass";).
+ * </p>
+ * @property Base.NAME
+ * @type String
+ * @static
+ */
+ Base.NAME = "base";
+
+ /**
+ * The default set of attributes which will be available for instances of this class, and
+ * their configuration. In addition to the configuration properties listed by
+ * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute
+ * can also be configured with a "cloneDefaultValue" property, which defines how the statically
+ * defined value field should be protected ("shallow", "deep" and false are supported values).
+ *
+ * By default if the value is an object literal or an array it will be "shallow" cloned, to
+ * protect the default value.
+ *
+ * @property Base.ATTRS
+ * @type Object
+ * @static
+ */
+ Base.ATTRS = {
+ /**
+ * Flag indicating whether or not this object
+ * has been through the init lifecycle phase.
+ *
+ * @attribute initialized
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ initialized: {
+ readOnly:true,
+ value:false
+ },
+
+ /**
+ * Flag indicating whether or not this object
+ * has been through the destroy lifecycle phase.
+ *
+ * @attribute destroyed
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ destroyed: {
+ readOnly:true,
+ value:false
+ }
+ };
+
+ Base.prototype = {
+
+ /**
+ * Init lifecycle method, invoked during construction.
+ * Fires the init event prior to setting up attributes and
+ * invoking initializers for the class hierarchy.
+ *
+ * @method init
+ * @final
+ * @chainable
+ * @param {Object} config Object with configuration property name/value pairs
+ * @return {Base} A reference to this object
+ */
+ init: function(config) {
+ Y.log('init called', 'life', 'base');
+
+ /**
+ * The string used to identify the class of this object.
+ *
+ * @deprecated Use this.constructor.NAME
+ * @property name
+ * @type String
+ */
+ this._yuievt.config.prefix = this.name = this.constructor.NAME;
+
+ /**
+ * <p>
+ * Lifecycle event for the init phase, fired prior to initialization.
+ * Invoking the preventDefault() method on the event object provided
+ * to subscribers will prevent initialization from occuring.
+ * </p>
+ * <p>
+ * Subscribers to the "after" momemt of this event, will be notified
+ * after initialization of the object is complete (and therefore
+ * cannot prevent initialization).
+ * </p>
+ *
+ * @event init
+ * @preventable _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ */
+ this.publish(INIT, {
+ queuable:false,
+ defaultFn:this._defInitFn
+ });
+
+ if (config) {
+ if (config.on) {
+ this.on(config.on);
+ }
+ if (config.after) {
+ this.after(config.after);
+ }
+ }
+
+ this.fire(INIT, {cfg: config});
+
+ return this;
+ },
+
+ /**
+ * <p>
+ * Destroy lifecycle method. Fires the destroy
+ * event, prior to invoking destructors for the
+ * class hierarchy.
+ * </p>
+ * <p>
+ * Subscribers to the destroy
+ * event can invoke preventDefault on the event object, to prevent destruction
+ * from proceeding.
+ * </p>
+ * @method destroy
+ * @return {Base} A reference to this object
+ * @final
+ * @chainable
+ */
+ destroy: function() {
+ Y.log('destroy called', 'life', 'base');
+
+ /**
+ * <p>
+ * Lifecycle event for the destroy phase,
+ * fired prior to destruction. Invoking the preventDefault
+ * method on the event object provided to subscribers will
+ * prevent destruction from proceeding.
+ * </p>
+ * <p>
+ * Subscribers to the "after" moment of this event, will be notified
+ * after destruction is complete (and as a result cannot prevent
+ * destruction).
+ * </p>
+ * @event destroy
+ * @preventable _defDestroyFn
+ * @param {EventFacade} e Event object
+ */
+ this.publish(DESTROY, {
+ queuable:false,
+ defaultFn: this._defDestroyFn
+ });
+ this.fire(DESTROY);
+ return this;
+ },
+
+ /**
+ * Default init event handler
+ *
+ * @method _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ * @protected
+ */
+ _defInitFn : function(e) {
+ this._initHierarchy(e.cfg);
+ if (this._initPlugins) {
+ // Need to initPlugins manually, to handle constructor parsing, static Plug parsing
+ this._initPlugins(e.cfg);
+ }
+ this._set(INITIALIZED, true);
+ },
+
+ /**
+ * Default destroy event handler
+ *
+ * @method _defDestroyFn
+ * @param {EventFacade} e Event object
+ * @protected
+ */
+ _defDestroyFn : function(e) {
+ this._destroyHierarchy();
+ if (this._destroyPlugins) {
+ this._destroyPlugins();
+ }
+ this._set(DESTROYED, true);
+ },
+
+ /**
+ * Returns the class hierarchy for this object, with Base being the last class in the array.
+ *
+ * @method _getClasses
+ * @protected
+ * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object.
+ * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the
+ * cached value.
+ */
+ _getClasses : function() {
+ if (!this._classes) {
+ this._initHierarchyData();
+ }
+ return this._classes;
+ },
+
+ /**
+ * Returns an aggregated set of attribute configurations, by traversing the class hierarchy.
+ *
+ * @method _getAttrCfgs
+ * @protected
+ * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy
+ * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return
+ * the cached value.
+ */
+ _getAttrCfgs : function() {
+ if (!this._attrs) {
+ this._initHierarchyData();
+ }
+ return this._attrs;
+ },
+
+ /**
+ * A helper method used when processing ATTRS across the class hierarchy during
+ * initialization. Returns a disposable object with the attributes defined for
+ * the provided class, extracted from the set of all attributes passed in .
+ *
+ * @method _filterAttrCfs
+ * @private
+ *
+ * @param {Function} clazz The class for which the desired attributes are required.
+ * @param {Object} allCfgs The set of all attribute configurations for this instance.
+ * Attributes will be removed from this set, if they belong to the filtered class, so
+ * that by the time all classes are processed, allCfgs will be empty.
+ *
+ * @return {Object} The set of attributes belonging to the class passed in, in the form
+ * of an object with attribute name/configuration pairs.
+ */
+ _filterAttrCfgs : function(clazz, allCfgs) {
+ var cfgs = null, attr, attrs = clazz.ATTRS;
+
+ if (attrs) {
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr) && allCfgs[attr]) {
+ cfgs = cfgs || {};
+ cfgs[attr] = allCfgs[attr];
+ delete allCfgs[attr];
+ }
+ }
+ }
+
+ return cfgs;
+ },
+
+ /**
+ * A helper method used by _getClasses and _getAttrCfgs, which determines both
+ * the array of classes and aggregate set of attribute configurations
+ * across the class hierarchy for the instance.
+ *
+ * @method _initHierarchyData
+ * @private
+ */
+ _initHierarchyData : function() {
+ var c = this.constructor,
+ classes = [],
+ attrs = [];
+
+ while (c) {
+ // Add to classes
+ classes[classes.length] = c;
+
+ // Add to attributes
+ if (c.ATTRS) {
+ attrs[attrs.length] = c.ATTRS;
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ this._classes = classes;
+ this._attrs = this._aggregateAttrs(attrs);
+ },
+
+ /**
+ * A helper method, used by _initHierarchyData to aggregate
+ * attribute configuration across the instances class hierarchy.
+ *
+ * The method will potect the attribute configuration value to protect the statically defined
+ * default value in ATTRS if required (if the value is an object literal, array or the
+ * attribute configuration has cloneDefaultValue set to shallow or deep).
+ *
+ * @method _aggregateAttrs
+ * @private
+ * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy
+ * (subclass first, Base last)
+ * @return {Object} The aggregate set of ATTRS definitions for the instance
+ */
+ _aggregateAttrs : function(allAttrs) {
+ var attr,
+ attrs,
+ cfg,
+ val,
+ path,
+ i,
+ clone,
+ cfgProps = Base._ATTR_CFG,
+ aggAttrs = {};
+
+ if (allAttrs) {
+ for (i = allAttrs.length-1; i >= 0; --i) {
+ attrs = allAttrs[i];
+
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+
+ // Protect config passed in
+ cfg = Y.mix({}, attrs[attr], true, cfgProps);
+
+ val = cfg.value;
+ clone = cfg.cloneDefaultValue;
+
+ if (val) {
+ if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) {
+ Y.log('Cloning default value for attribute:' + attr, 'info', 'base');
+ cfg.value = Y.clone(val);
+ } else if (clone === SHALLOW) {
+ Y.log('Merging default value for attribute:' + attr, 'info', 'base');
+ cfg.value = Y.merge(val);
+ }
+ // else if (clone === false), don't clone the static default value.
+ // It's intended to be used by reference.
+ }
+
+ path = null;
+ if (attr.indexOf(DOT) !== -1) {
+ path = attr.split(DOT);
+ attr = path.shift();
+ }
+
+ if (path && aggAttrs[attr] && aggAttrs[attr].value) {
+ O.setValue(aggAttrs[attr].value, path, val);
+ } else if (!path){
+ if (!aggAttrs[attr]) {
+ aggAttrs[attr] = cfg;
+ } else {
+ Y.mix(aggAttrs[attr], cfg, true, cfgProps);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return aggAttrs;
+ },
+
+ /**
+ * Initializes the class hierarchy for the instance, which includes
+ * initializing attributes for each class defined in the class's
+ * static <a href="#property_Base.ATTRS">ATTRS</a> property and
+ * invoking the initializer method on the prototype of each class in the hierarchy.
+ *
+ * @method _initHierarchy
+ * @param {Object} userVals Object with configuration property name/value pairs
+ * @private
+ */
+ _initHierarchy : function(userVals) {
+ var lazy = this._lazyAddAttrs,
+ constr,
+ constrProto,
+ ci,
+ ei,
+ el,
+ classes = this._getClasses(),
+ attrCfgs = this._getAttrCfgs();
+
+ for (ci = classes.length-1; ci >= 0; ci--) {
+
+ constr = classes[ci];
+ constrProto = constr.prototype;
+
+ if (constr._yuibuild && constr._yuibuild.exts && !constr._yuibuild.dynamic) {
+ for (ei = 0, el = constr._yuibuild.exts.length; ei < el; ei++) {
+ constr._yuibuild.exts[ei].apply(this, arguments);
+ }
+ }
+
+ this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy);
+
+ if (constrProto.hasOwnProperty(INITIALIZER)) {
+ constrProto.initializer.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Destroys the class hierarchy for this instance by invoking
+ * the descructor method on the prototype of each class in the hierarchy.
+ *
+ * @method _destroyHierarchy
+ * @private
+ */
+ _destroyHierarchy : function() {
+ var constr,
+ constrProto,
+ ci, cl,
+ classes = this._getClasses();
+
+ for (ci = 0, cl = classes.length; ci < cl; ci++) {
+ constr = classes[ci];
+ constrProto = constr.prototype;
+ if (constrProto.hasOwnProperty(DESTRUCTOR)) {
+ constrProto.destructor.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Default toString implementation. Provides the constructor NAME
+ * and the instance ID.
+ *
+ * @method toString
+ * @return {String} String representation for this object
+ */
+ toString: function() {
+ return this.constructor.NAME + "[" + Y.stamp(this) + "]";
+ }
+ };
+
+ // Straightup augment, no wrapper functions
+ Y.mix(Base, Attribute, false, null, 1);
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+ Y.Base = Base;
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("base-base",function(B){var H=B.Object,J=B.Lang,I=".",F="destroy",P="init",N="initialized",G="destroyed",D="initializer",C=Object.prototype.constructor,K="deep",Q="shallow",M="destructor",A=B.Attribute;function E(){A.call(this);var L=B.Plugin&&B.Plugin.Host;if(this._initPlugins&&L){L.call(this);}if(this._lazyAddAttrs!==false){this._lazyAddAttrs=true;}this.init.apply(this,arguments);}E._ATTR_CFG=A._ATTR_CFG.concat("cloneDefaultValue");E.NAME="base";E.ATTRS={initialized:{readOnly:true,value:false},destroyed:{readOnly:true,value:false}};E.prototype={init:function(L){this._yuievt.config.prefix=this.name=this.constructor.NAME;this.publish(P,{queuable:false,defaultFn:this._defInitFn});if(L){if(L.on){this.on(L.on);}if(L.after){this.after(L.after);}}this.fire(P,{cfg:L});return this;},destroy:function(){this.publish(F,{queuable:false,defaultFn:this._defDestroyFn});this.fire(F);return this;},_defInitFn:function(L){this._initHierarchy(L.cfg);if(this._initPlugins){this._initPlugins(L.cfg);}this._set(N,true);},_defDestroyFn:function(L){this._destroyHierarchy();if(this._destroyPlugins){this._destroyPlugins();}this._set(G,true);},_getClasses:function(){if(!this._classes){this._initHierarchyData();}return this._classes;},_getAttrCfgs:function(){if(!this._attrs){this._initHierarchyData();}return this._attrs;},_filterAttrCfgs:function(T,O){var R=null,L,S=T.ATTRS;if(S){for(L in S){if(S.hasOwnProperty(L)&&O[L]){R=R||{};R[L]=O[L];delete O[L];}}}return R;},_initHierarchyData:function(){var R=this.constructor,O=[],L=[];while(R){O[O.length]=R;if(R.ATTRS){L[L.length]=R.ATTRS;}R=R.superclass?R.superclass.constructor:null;}this._classes=O;this._attrs=this._aggregateAttrs(L);},_aggregateAttrs:function(W){var T,X,S,L,Y,O,V,R=E._ATTR_CFG,U={};if(W){for(O=W.length-1;O>=0;--O){X=W[O];for(T in X){if(X.hasOwnProperty(T)){S=B.mix({},X[T],true,R);L=S.value;V=S.cloneDefaultValue;if(L){if((V===undefined&&(C===L.constructor||J.isArray(L)))||V===K||V===true){S.value=B.clone(L);}else{if(V===Q){S.value=B.merge(L);}}}Y=null;if(T.indexOf(I)!==-1){Y=T.split(I);T=Y.shift();}if(Y&&U[T]&&U[T].value){H.setValue(U[T].value,Y,L);}else{if(!Y){if(!U[T]){U[T]=S;}else{B.mix(U[T],S,true,R);}}}}}}}return U;},_initHierarchy:function(U){var R=this._lazyAddAttrs,V,W,X,S,O,T=this._getClasses(),L=this._getAttrCfgs();for(X=T.length-1;X>=0;X--){V=T[X];W=V.prototype;if(V._yuibuild&&V._yuibuild.exts&&!V._yuibuild.dynamic){for(S=0,O=V._yuibuild.exts.length;S<O;S++){V._yuibuild.exts[S].apply(this,arguments);}}this.addAttrs(this._filterAttrCfgs(V,L),U,R);if(W.hasOwnProperty(D)){W.initializer.apply(this,arguments);}}},_destroyHierarchy:function(){var T,O,S,L,R=this._getClasses();for(S=0,L=R.length;S<L;S++){T=R[S];O=T.prototype;if(O.hasOwnProperty(M)){O.destructor.apply(this,arguments);}}},toString:function(){return this.constructor.NAME+"["+B.stamp(this)+"]";}};B.mix(E,A,false,null,1);E.prototype.constructor=E;B.Base=E;E.prototype.constructor=E;},"3.0.0",{requires:["attribute-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-base', function(Y) {
+
+ /**
+ * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
+ * The module also provides two ways to reuse code - An augmentable Plugin.Host interface which provides plugin support
+ * (which is augmented to the Base class) and Base.build which provides a way to
+ * build custom classes using extensions.
+ *
+ * @module base
+ */
+
+ /**
+ * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host,
+ * and without the extension support provided by Base.build.
+ *
+ * @module base
+ * @submodule base-base
+ */
+ var O = Y.Object,
+ L = Y.Lang,
+ DOT = ".",
+ DESTROY = "destroy",
+ INIT = "init",
+ INITIALIZED = "initialized",
+ DESTROYED = "destroyed",
+ INITIALIZER = "initializer",
+ OBJECT_CONSTRUCTOR = Object.prototype.constructor,
+ DEEP = "deep",
+ SHALLOW = "shallow",
+ DESTRUCTOR = "destructor",
+
+ Attribute = Y.Attribute;
+
+ /**
+ * <p>
+ * A base class which objects requiring attributes and custom event support can
+ * extend. Base also handles the chaining of initializer and destructor methods across
+ * the hierarchy as part of object construction and destruction. Additionally, attributes configured
+ * through the static <a href="#property_Base.ATTRS">ATTRS</a> property for each class
+ * in the hierarchy will be initialized by Base.
+ * </p>
+ *
+ * <p>
+ * The static <a href="#property_Base.NAME">NAME</a> property of each class extending
+ * from Base will be used as the identifier for the class, and is used by Base to prefix
+ * all events fired by instances of that class.
+ * </p>
+ * @class Base
+ * @constructor
+ * @uses Attribute
+ * @uses Plugin.Host
+ *
+ * @param {Object} config Object with configuration property name/value pairs
+ */
+ function Base() {
+
+ Attribute.call(this);
+
+ // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's
+ // initial state, but don't initialize Plugins yet. That's done after initialization.
+ var PluginHost = Y.Plugin && Y.Plugin.Host;
+ if (this._initPlugins && PluginHost) {
+ PluginHost.call(this);
+ }
+
+ if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; }
+
+ this.init.apply(this, arguments);
+ }
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce, readOnly etc.)
+ *
+ * @property Base._ATTR_CFG
+ * @type Array
+ * @static
+ * @private
+ */
+ Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue");
+
+ /**
+ * <p>
+ * The string to be used to identify instances of
+ * this class, for example in prefixing events.
+ * </p>
+ * <p>
+ * Classes extending Base, should define their own
+ * static NAME property, which should be camelCase by
+ * convention (e.g. MyClass.NAME = "myClass";).
+ * </p>
+ * @property Base.NAME
+ * @type String
+ * @static
+ */
+ Base.NAME = "base";
+
+ /**
+ * The default set of attributes which will be available for instances of this class, and
+ * their configuration. In addition to the configuration properties listed by
+ * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute
+ * can also be configured with a "cloneDefaultValue" property, which defines how the statically
+ * defined value field should be protected ("shallow", "deep" and false are supported values).
+ *
+ * By default if the value is an object literal or an array it will be "shallow" cloned, to
+ * protect the default value.
+ *
+ * @property Base.ATTRS
+ * @type Object
+ * @static
+ */
+ Base.ATTRS = {
+ /**
+ * Flag indicating whether or not this object
+ * has been through the init lifecycle phase.
+ *
+ * @attribute initialized
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ initialized: {
+ readOnly:true,
+ value:false
+ },
+
+ /**
+ * Flag indicating whether or not this object
+ * has been through the destroy lifecycle phase.
+ *
+ * @attribute destroyed
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ destroyed: {
+ readOnly:true,
+ value:false
+ }
+ };
+
+ Base.prototype = {
+
+ /**
+ * Init lifecycle method, invoked during construction.
+ * Fires the init event prior to setting up attributes and
+ * invoking initializers for the class hierarchy.
+ *
+ * @method init
+ * @final
+ * @chainable
+ * @param {Object} config Object with configuration property name/value pairs
+ * @return {Base} A reference to this object
+ */
+ init: function(config) {
+
+ /**
+ * The string used to identify the class of this object.
+ *
+ * @deprecated Use this.constructor.NAME
+ * @property name
+ * @type String
+ */
+ this._yuievt.config.prefix = this.name = this.constructor.NAME;
+
+ /**
+ * <p>
+ * Lifecycle event for the init phase, fired prior to initialization.
+ * Invoking the preventDefault() method on the event object provided
+ * to subscribers will prevent initialization from occuring.
+ * </p>
+ * <p>
+ * Subscribers to the "after" momemt of this event, will be notified
+ * after initialization of the object is complete (and therefore
+ * cannot prevent initialization).
+ * </p>
+ *
+ * @event init
+ * @preventable _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ */
+ this.publish(INIT, {
+ queuable:false,
+ defaultFn:this._defInitFn
+ });
+
+ if (config) {
+ if (config.on) {
+ this.on(config.on);
+ }
+ if (config.after) {
+ this.after(config.after);
+ }
+ }
+
+ this.fire(INIT, {cfg: config});
+
+ return this;
+ },
+
+ /**
+ * <p>
+ * Destroy lifecycle method. Fires the destroy
+ * event, prior to invoking destructors for the
+ * class hierarchy.
+ * </p>
+ * <p>
+ * Subscribers to the destroy
+ * event can invoke preventDefault on the event object, to prevent destruction
+ * from proceeding.
+ * </p>
+ * @method destroy
+ * @return {Base} A reference to this object
+ * @final
+ * @chainable
+ */
+ destroy: function() {
+
+ /**
+ * <p>
+ * Lifecycle event for the destroy phase,
+ * fired prior to destruction. Invoking the preventDefault
+ * method on the event object provided to subscribers will
+ * prevent destruction from proceeding.
+ * </p>
+ * <p>
+ * Subscribers to the "after" moment of this event, will be notified
+ * after destruction is complete (and as a result cannot prevent
+ * destruction).
+ * </p>
+ * @event destroy
+ * @preventable _defDestroyFn
+ * @param {EventFacade} e Event object
+ */
+ this.publish(DESTROY, {
+ queuable:false,
+ defaultFn: this._defDestroyFn
+ });
+ this.fire(DESTROY);
+ return this;
+ },
+
+ /**
+ * Default init event handler
+ *
+ * @method _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ * @protected
+ */
+ _defInitFn : function(e) {
+ this._initHierarchy(e.cfg);
+ if (this._initPlugins) {
+ // Need to initPlugins manually, to handle constructor parsing, static Plug parsing
+ this._initPlugins(e.cfg);
+ }
+ this._set(INITIALIZED, true);
+ },
+
+ /**
+ * Default destroy event handler
+ *
+ * @method _defDestroyFn
+ * @param {EventFacade} e Event object
+ * @protected
+ */
+ _defDestroyFn : function(e) {
+ this._destroyHierarchy();
+ if (this._destroyPlugins) {
+ this._destroyPlugins();
+ }
+ this._set(DESTROYED, true);
+ },
+
+ /**
+ * Returns the class hierarchy for this object, with Base being the last class in the array.
+ *
+ * @method _getClasses
+ * @protected
+ * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object.
+ * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the
+ * cached value.
+ */
+ _getClasses : function() {
+ if (!this._classes) {
+ this._initHierarchyData();
+ }
+ return this._classes;
+ },
+
+ /**
+ * Returns an aggregated set of attribute configurations, by traversing the class hierarchy.
+ *
+ * @method _getAttrCfgs
+ * @protected
+ * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy
+ * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return
+ * the cached value.
+ */
+ _getAttrCfgs : function() {
+ if (!this._attrs) {
+ this._initHierarchyData();
+ }
+ return this._attrs;
+ },
+
+ /**
+ * A helper method used when processing ATTRS across the class hierarchy during
+ * initialization. Returns a disposable object with the attributes defined for
+ * the provided class, extracted from the set of all attributes passed in .
+ *
+ * @method _filterAttrCfs
+ * @private
+ *
+ * @param {Function} clazz The class for which the desired attributes are required.
+ * @param {Object} allCfgs The set of all attribute configurations for this instance.
+ * Attributes will be removed from this set, if they belong to the filtered class, so
+ * that by the time all classes are processed, allCfgs will be empty.
+ *
+ * @return {Object} The set of attributes belonging to the class passed in, in the form
+ * of an object with attribute name/configuration pairs.
+ */
+ _filterAttrCfgs : function(clazz, allCfgs) {
+ var cfgs = null, attr, attrs = clazz.ATTRS;
+
+ if (attrs) {
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr) && allCfgs[attr]) {
+ cfgs = cfgs || {};
+ cfgs[attr] = allCfgs[attr];
+ delete allCfgs[attr];
+ }
+ }
+ }
+
+ return cfgs;
+ },
+
+ /**
+ * A helper method used by _getClasses and _getAttrCfgs, which determines both
+ * the array of classes and aggregate set of attribute configurations
+ * across the class hierarchy for the instance.
+ *
+ * @method _initHierarchyData
+ * @private
+ */
+ _initHierarchyData : function() {
+ var c = this.constructor,
+ classes = [],
+ attrs = [];
+
+ while (c) {
+ // Add to classes
+ classes[classes.length] = c;
+
+ // Add to attributes
+ if (c.ATTRS) {
+ attrs[attrs.length] = c.ATTRS;
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ this._classes = classes;
+ this._attrs = this._aggregateAttrs(attrs);
+ },
+
+ /**
+ * A helper method, used by _initHierarchyData to aggregate
+ * attribute configuration across the instances class hierarchy.
+ *
+ * The method will potect the attribute configuration value to protect the statically defined
+ * default value in ATTRS if required (if the value is an object literal, array or the
+ * attribute configuration has cloneDefaultValue set to shallow or deep).
+ *
+ * @method _aggregateAttrs
+ * @private
+ * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy
+ * (subclass first, Base last)
+ * @return {Object} The aggregate set of ATTRS definitions for the instance
+ */
+ _aggregateAttrs : function(allAttrs) {
+ var attr,
+ attrs,
+ cfg,
+ val,
+ path,
+ i,
+ clone,
+ cfgProps = Base._ATTR_CFG,
+ aggAttrs = {};
+
+ if (allAttrs) {
+ for (i = allAttrs.length-1; i >= 0; --i) {
+ attrs = allAttrs[i];
+
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+
+ // Protect config passed in
+ cfg = Y.mix({}, attrs[attr], true, cfgProps);
+
+ val = cfg.value;
+ clone = cfg.cloneDefaultValue;
+
+ if (val) {
+ if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) {
+ cfg.value = Y.clone(val);
+ } else if (clone === SHALLOW) {
+ cfg.value = Y.merge(val);
+ }
+ // else if (clone === false), don't clone the static default value.
+ // It's intended to be used by reference.
+ }
+
+ path = null;
+ if (attr.indexOf(DOT) !== -1) {
+ path = attr.split(DOT);
+ attr = path.shift();
+ }
+
+ if (path && aggAttrs[attr] && aggAttrs[attr].value) {
+ O.setValue(aggAttrs[attr].value, path, val);
+ } else if (!path){
+ if (!aggAttrs[attr]) {
+ aggAttrs[attr] = cfg;
+ } else {
+ Y.mix(aggAttrs[attr], cfg, true, cfgProps);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return aggAttrs;
+ },
+
+ /**
+ * Initializes the class hierarchy for the instance, which includes
+ * initializing attributes for each class defined in the class's
+ * static <a href="#property_Base.ATTRS">ATTRS</a> property and
+ * invoking the initializer method on the prototype of each class in the hierarchy.
+ *
+ * @method _initHierarchy
+ * @param {Object} userVals Object with configuration property name/value pairs
+ * @private
+ */
+ _initHierarchy : function(userVals) {
+ var lazy = this._lazyAddAttrs,
+ constr,
+ constrProto,
+ ci,
+ ei,
+ el,
+ classes = this._getClasses(),
+ attrCfgs = this._getAttrCfgs();
+
+ for (ci = classes.length-1; ci >= 0; ci--) {
+
+ constr = classes[ci];
+ constrProto = constr.prototype;
+
+ if (constr._yuibuild && constr._yuibuild.exts && !constr._yuibuild.dynamic) {
+ for (ei = 0, el = constr._yuibuild.exts.length; ei < el; ei++) {
+ constr._yuibuild.exts[ei].apply(this, arguments);
+ }
+ }
+
+ this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy);
+
+ if (constrProto.hasOwnProperty(INITIALIZER)) {
+ constrProto.initializer.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Destroys the class hierarchy for this instance by invoking
+ * the descructor method on the prototype of each class in the hierarchy.
+ *
+ * @method _destroyHierarchy
+ * @private
+ */
+ _destroyHierarchy : function() {
+ var constr,
+ constrProto,
+ ci, cl,
+ classes = this._getClasses();
+
+ for (ci = 0, cl = classes.length; ci < cl; ci++) {
+ constr = classes[ci];
+ constrProto = constr.prototype;
+ if (constrProto.hasOwnProperty(DESTRUCTOR)) {
+ constrProto.destructor.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Default toString implementation. Provides the constructor NAME
+ * and the instance ID.
+ *
+ * @method toString
+ * @return {String} String representation for this object
+ */
+ toString: function() {
+ return this.constructor.NAME + "[" + Y.stamp(this) + "]";
+ }
+ };
+
+ // Straightup augment, no wrapper functions
+ Y.mix(Base, Attribute, false, null, 1);
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+ Y.Base = Base;
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-build', function(Y) {
+
+ /**
+ * The base-build submodule provides Base.build functionality, which
+ * can be used to create custom classes, by aggregating extensions onto
+ * a main class.
+ *
+ * @module base
+ * @submodule base-build
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ L = Y.Lang;
+
+ /**
+ * The build configuration for the Base class.
+ *
+ * Defines the static fields which need to be aggregated
+ * when the Base class is used as the main class passed to
+ * the <a href="#method_Base.build">Base.build</a> method.
+ *
+ * @property Base._buildCfg
+ * @type Object
+ * @static
+ * @final
+ * @private
+ */
+ Base._buildCfg = {
+ aggregates : ["ATTRS", "_PLUG", "_UNPLUG"]
+ };
+
+ /**
+ * <p>
+ * Builds a custom constructor function (class) from the
+ * main function, and array of extension functions (classes)
+ * provided. The NAME field for the constructor function is
+ * defined by the first argument passed in.
+ * </p>
+ * <p>
+ * The cfg object supports the following properties
+ * </p>
+ * <dl>
+ * <dt>dynamic <boolean></dt>
+ * <dd>
+ * <p>If true (default), a completely new class
+ * is created which extends the main class, and acts as the
+ * host on which the extension classes are augmented.</p>
+ * <p>If false, the extensions classes are augmented directly to
+ * the main class, modifying the main class' prototype.</p>
+ * </dd>
+ * <dt>aggregates <String[]></dt>
+ * <dd>An array of static property names, which will get aggregated
+ * on to the built class, in addition to the default properties build
+ * will always aggregate as defined by the main class' static _buildCfg
+ * property.
+ * </dd>
+ * </dl>
+ *
+ * @method Base.build
+ * @static
+ * @param {Function} name The name of the new class. Used to defined the NAME property for the new class.
+ * @param {Function} main The main class on which to base the built class
+ * @param {Function[]} extensions The set of extension classes which will be
+ * augmented/aggregated to the built class.
+ * @param {Object} cfg Optional. Build configuration for the class (see description).
+ * @return {Function} A custom class, created from the provided main and extension classes
+ */
+ Base.build = function(name, main, extensions, cfg) {
+
+ var build = Base.build,
+ builtClass = build._getClass(main, cfg),
+ aggregates = build._getAggregates(main, cfg),
+ dynamic = builtClass._yuibuild.dynamic,
+ i, l, val, extClass;
+
+ // Shallow isolate aggregates
+ if (dynamic) {
+ if (aggregates) {
+ for (i = 0, l = aggregates.length; i < l; ++i) {
+ val = aggregates[i];
+ if (main.hasOwnProperty(val)) {
+ builtClass[val] = L.isArray(main[val]) ? [] : {};
+ }
+ }
+ Y.aggregate(builtClass, main, true, aggregates);
+ }
+ }
+
+ // Augment/Aggregate
+ for (i = 0, l = extensions.length; i < l; i++) {
+ extClass = extensions[i];
+
+ if (aggregates) {
+ Y.aggregate(builtClass, extClass, true, aggregates);
+ }
+
+ // Old augment
+ Y.mix(builtClass, extClass, true, null, 1);
+
+ builtClass._yuibuild.exts.push(extClass);
+ }
+
+ builtClass.prototype.hasImpl = build._hasImpl;
+
+ if (dynamic) {
+ builtClass.NAME = name;
+ builtClass.prototype.constructor = builtClass;
+ }
+
+ return builtClass;
+ };
+
+ Y.mix(Base.build, {
+
+ _template: function(main) {
+
+ function BuiltClass() {
+
+ BuiltClass.superclass.constructor.apply(this, arguments);
+
+ var f = BuiltClass._yuibuild.exts,
+ l = f.length,
+ i;
+
+ for (i = 0; i < l; i++) {
+ f[i].apply(this, arguments);
+ }
+
+ return this;
+ }
+ Y.extend(BuiltClass, main);
+
+ return BuiltClass;
+ },
+
+ _hasImpl : function(extClass) {
+ var classes = this._getClasses();
+ for (var i = 0, l = classes.length; i < l; i++) {
+ var cls = classes[i];
+
+ if (cls._yuibuild) {
+ var exts = cls._yuibuild.exts,
+ ll = exts.length,
+ j;
+
+ for (j = 0; j < ll; j++) {
+ if (exts[j] === extClass) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ },
+
+ _getClass : function(main, cfg) {
+
+ var dynamic = (cfg && false === cfg.dynamic) ? false : true,
+ builtClass = (dynamic) ? Base.build._template(main) : main;
+
+ builtClass._yuibuild = {
+ id: null,
+ exts : [],
+ dynamic : dynamic
+ };
+
+ return builtClass;
+ },
+
+ _getAggregates : function(main, cfg) {
+ var aggr = [],
+ cfgAggr = (cfg && cfg.aggregates),
+ c = main,
+ classAggr;
+
+ while (c && c.prototype) {
+ classAggr = c._buildCfg && c._buildCfg.aggregates;
+ if (classAggr) {
+ aggr = aggr.concat(classAggr);
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ if (cfgAggr) {
+ aggr = aggr.concat(cfgAggr);
+ }
+
+ return aggr;
+ }
+ });
+
+
+}, '3.0.0' ,{requires:['base-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("base-build",function(C){var B=C.Base,A=C.Lang;B._buildCfg={aggregates:["ATTRS","_PLUG","_UNPLUG"]};B.build=function(D,I,M,L){var O=B.build,E=O._getClass(I,L),K=O._getAggregates(I,L),G=E._yuibuild.dynamic,J,H,F,N;if(G){if(K){for(J=0,H=K.length;J<H;++J){F=K[J];if(I.hasOwnProperty(F)){E[F]=A.isArray(I[F])?[]:{};}}C.aggregate(E,I,true,K);}}for(J=0,H=M.length;J<H;J++){N=M[J];if(K){C.aggregate(E,N,true,K);}C.mix(E,N,true,null,1);E._yuibuild.exts.push(N);}E.prototype.hasImpl=O._hasImpl;if(G){E.NAME=D;E.prototype.constructor=E;}return E;};C.mix(B.build,{_template:function(D){function E(){E.superclass.constructor.apply(this,arguments);var H=E._yuibuild.exts,F=H.length,G;for(G=0;G<F;G++){H[G].apply(this,arguments);}return this;}C.extend(E,D);return E;},_hasImpl:function(G){var J=this._getClasses();for(var I=0,E=J.length;I<E;I++){var D=J[I];if(D._yuibuild){var H=D._yuibuild.exts,K=H.length,F;for(F=0;F<K;F++){if(H[F]===G){return true;}}}}return false;},_getClass:function(D,E){var F=(E&&false===E.dynamic)?false:true,G=(F)?B.build._template(D):D;G._yuibuild={id:null,exts:[],dynamic:F};return G;},_getAggregates:function(D,E){var F=[],H=(E&&E.aggregates),I=D,G;while(I&&I.prototype){G=I._buildCfg&&I._buildCfg.aggregates;if(G){F=F.concat(G);}I=I.superclass?I.superclass.constructor:null;}if(H){F=F.concat(H);}return F;}});},"3.0.0",{requires:["base-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-build', function(Y) {
+
+ /**
+ * The base-build submodule provides Base.build functionality, which
+ * can be used to create custom classes, by aggregating extensions onto
+ * a main class.
+ *
+ * @module base
+ * @submodule base-build
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ L = Y.Lang;
+
+ /**
+ * The build configuration for the Base class.
+ *
+ * Defines the static fields which need to be aggregated
+ * when the Base class is used as the main class passed to
+ * the <a href="#method_Base.build">Base.build</a> method.
+ *
+ * @property Base._buildCfg
+ * @type Object
+ * @static
+ * @final
+ * @private
+ */
+ Base._buildCfg = {
+ aggregates : ["ATTRS", "_PLUG", "_UNPLUG"]
+ };
+
+ /**
+ * <p>
+ * Builds a custom constructor function (class) from the
+ * main function, and array of extension functions (classes)
+ * provided. The NAME field for the constructor function is
+ * defined by the first argument passed in.
+ * </p>
+ * <p>
+ * The cfg object supports the following properties
+ * </p>
+ * <dl>
+ * <dt>dynamic <boolean></dt>
+ * <dd>
+ * <p>If true (default), a completely new class
+ * is created which extends the main class, and acts as the
+ * host on which the extension classes are augmented.</p>
+ * <p>If false, the extensions classes are augmented directly to
+ * the main class, modifying the main class' prototype.</p>
+ * </dd>
+ * <dt>aggregates <String[]></dt>
+ * <dd>An array of static property names, which will get aggregated
+ * on to the built class, in addition to the default properties build
+ * will always aggregate as defined by the main class' static _buildCfg
+ * property.
+ * </dd>
+ * </dl>
+ *
+ * @method Base.build
+ * @static
+ * @param {Function} name The name of the new class. Used to defined the NAME property for the new class.
+ * @param {Function} main The main class on which to base the built class
+ * @param {Function[]} extensions The set of extension classes which will be
+ * augmented/aggregated to the built class.
+ * @param {Object} cfg Optional. Build configuration for the class (see description).
+ * @return {Function} A custom class, created from the provided main and extension classes
+ */
+ Base.build = function(name, main, extensions, cfg) {
+
+ var build = Base.build,
+ builtClass = build._getClass(main, cfg),
+ aggregates = build._getAggregates(main, cfg),
+ dynamic = builtClass._yuibuild.dynamic,
+ i, l, val, extClass;
+
+ // Shallow isolate aggregates
+ if (dynamic) {
+ if (aggregates) {
+ for (i = 0, l = aggregates.length; i < l; ++i) {
+ val = aggregates[i];
+ if (main.hasOwnProperty(val)) {
+ builtClass[val] = L.isArray(main[val]) ? [] : {};
+ }
+ }
+ Y.aggregate(builtClass, main, true, aggregates);
+ }
+ }
+
+ // Augment/Aggregate
+ for (i = 0, l = extensions.length; i < l; i++) {
+ extClass = extensions[i];
+
+ if (aggregates) {
+ Y.aggregate(builtClass, extClass, true, aggregates);
+ }
+
+ // Old augment
+ Y.mix(builtClass, extClass, true, null, 1);
+
+ builtClass._yuibuild.exts.push(extClass);
+ }
+
+ builtClass.prototype.hasImpl = build._hasImpl;
+
+ if (dynamic) {
+ builtClass.NAME = name;
+ builtClass.prototype.constructor = builtClass;
+ }
+
+ return builtClass;
+ };
+
+ Y.mix(Base.build, {
+
+ _template: function(main) {
+
+ function BuiltClass() {
+
+ BuiltClass.superclass.constructor.apply(this, arguments);
+
+ var f = BuiltClass._yuibuild.exts,
+ l = f.length,
+ i;
+
+ for (i = 0; i < l; i++) {
+ f[i].apply(this, arguments);
+ }
+
+ return this;
+ }
+ Y.extend(BuiltClass, main);
+
+ return BuiltClass;
+ },
+
+ _hasImpl : function(extClass) {
+ var classes = this._getClasses();
+ for (var i = 0, l = classes.length; i < l; i++) {
+ var cls = classes[i];
+
+ if (cls._yuibuild) {
+ var exts = cls._yuibuild.exts,
+ ll = exts.length,
+ j;
+
+ for (j = 0; j < ll; j++) {
+ if (exts[j] === extClass) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ },
+
+ _getClass : function(main, cfg) {
+
+ var dynamic = (cfg && false === cfg.dynamic) ? false : true,
+ builtClass = (dynamic) ? Base.build._template(main) : main;
+
+ builtClass._yuibuild = {
+ id: null,
+ exts : [],
+ dynamic : dynamic
+ };
+
+ return builtClass;
+ },
+
+ _getAggregates : function(main, cfg) {
+ var aggr = [],
+ cfgAggr = (cfg && cfg.aggregates),
+ c = main,
+ classAggr;
+
+ while (c && c.prototype) {
+ classAggr = c._buildCfg && c._buildCfg.aggregates;
+ if (classAggr) {
+ aggr = aggr.concat(classAggr);
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ if (cfgAggr) {
+ aggr = aggr.concat(cfgAggr);
+ }
+
+ return aggr;
+ }
+ });
+
+
+}, '3.0.0' ,{requires:['base-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-base', function(Y) {
+
+ /**
+ * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
+ * The module also provides two ways to reuse code - An augmentable Plugin.Host interface which provides plugin support
+ * (which is augmented to the Base class) and Base.build which provides a way to
+ * build custom classes using extensions.
+ *
+ * @module base
+ */
+
+ /**
+ * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host,
+ * and without the extension support provided by Base.build.
+ *
+ * @module base
+ * @submodule base-base
+ */
+ var O = Y.Object,
+ L = Y.Lang,
+ DOT = ".",
+ DESTROY = "destroy",
+ INIT = "init",
+ INITIALIZED = "initialized",
+ DESTROYED = "destroyed",
+ INITIALIZER = "initializer",
+ OBJECT_CONSTRUCTOR = Object.prototype.constructor,
+ DEEP = "deep",
+ SHALLOW = "shallow",
+ DESTRUCTOR = "destructor",
+
+ Attribute = Y.Attribute;
+
+ /**
+ * <p>
+ * A base class which objects requiring attributes and custom event support can
+ * extend. Base also handles the chaining of initializer and destructor methods across
+ * the hierarchy as part of object construction and destruction. Additionally, attributes configured
+ * through the static <a href="#property_Base.ATTRS">ATTRS</a> property for each class
+ * in the hierarchy will be initialized by Base.
+ * </p>
+ *
+ * <p>
+ * The static <a href="#property_Base.NAME">NAME</a> property of each class extending
+ * from Base will be used as the identifier for the class, and is used by Base to prefix
+ * all events fired by instances of that class.
+ * </p>
+ * @class Base
+ * @constructor
+ * @uses Attribute
+ * @uses Plugin.Host
+ *
+ * @param {Object} config Object with configuration property name/value pairs
+ */
+ function Base() {
+ Y.log('constructor called', 'life', 'base');
+
+ Attribute.call(this);
+
+ // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's
+ // initial state, but don't initialize Plugins yet. That's done after initialization.
+ var PluginHost = Y.Plugin && Y.Plugin.Host;
+ if (this._initPlugins && PluginHost) {
+ PluginHost.call(this);
+ }
+
+ if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; }
+
+ this.init.apply(this, arguments);
+ }
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce, readOnly etc.)
+ *
+ * @property Base._ATTR_CFG
+ * @type Array
+ * @static
+ * @private
+ */
+ Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue");
+
+ /**
+ * <p>
+ * The string to be used to identify instances of
+ * this class, for example in prefixing events.
+ * </p>
+ * <p>
+ * Classes extending Base, should define their own
+ * static NAME property, which should be camelCase by
+ * convention (e.g. MyClass.NAME = "myClass";).
+ * </p>
+ * @property Base.NAME
+ * @type String
+ * @static
+ */
+ Base.NAME = "base";
+
+ /**
+ * The default set of attributes which will be available for instances of this class, and
+ * their configuration. In addition to the configuration properties listed by
+ * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute
+ * can also be configured with a "cloneDefaultValue" property, which defines how the statically
+ * defined value field should be protected ("shallow", "deep" and false are supported values).
+ *
+ * By default if the value is an object literal or an array it will be "shallow" cloned, to
+ * protect the default value.
+ *
+ * @property Base.ATTRS
+ * @type Object
+ * @static
+ */
+ Base.ATTRS = {
+ /**
+ * Flag indicating whether or not this object
+ * has been through the init lifecycle phase.
+ *
+ * @attribute initialized
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ initialized: {
+ readOnly:true,
+ value:false
+ },
+
+ /**
+ * Flag indicating whether or not this object
+ * has been through the destroy lifecycle phase.
+ *
+ * @attribute destroyed
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ destroyed: {
+ readOnly:true,
+ value:false
+ }
+ };
+
+ Base.prototype = {
+
+ /**
+ * Init lifecycle method, invoked during construction.
+ * Fires the init event prior to setting up attributes and
+ * invoking initializers for the class hierarchy.
+ *
+ * @method init
+ * @final
+ * @chainable
+ * @param {Object} config Object with configuration property name/value pairs
+ * @return {Base} A reference to this object
+ */
+ init: function(config) {
+ Y.log('init called', 'life', 'base');
+
+ /**
+ * The string used to identify the class of this object.
+ *
+ * @deprecated Use this.constructor.NAME
+ * @property name
+ * @type String
+ */
+ this._yuievt.config.prefix = this.name = this.constructor.NAME;
+
+ /**
+ * <p>
+ * Lifecycle event for the init phase, fired prior to initialization.
+ * Invoking the preventDefault() method on the event object provided
+ * to subscribers will prevent initialization from occuring.
+ * </p>
+ * <p>
+ * Subscribers to the "after" momemt of this event, will be notified
+ * after initialization of the object is complete (and therefore
+ * cannot prevent initialization).
+ * </p>
+ *
+ * @event init
+ * @preventable _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ */
+ this.publish(INIT, {
+ queuable:false,
+ defaultFn:this._defInitFn
+ });
+
+ if (config) {
+ if (config.on) {
+ this.on(config.on);
+ }
+ if (config.after) {
+ this.after(config.after);
+ }
+ }
+
+ this.fire(INIT, {cfg: config});
+
+ return this;
+ },
+
+ /**
+ * <p>
+ * Destroy lifecycle method. Fires the destroy
+ * event, prior to invoking destructors for the
+ * class hierarchy.
+ * </p>
+ * <p>
+ * Subscribers to the destroy
+ * event can invoke preventDefault on the event object, to prevent destruction
+ * from proceeding.
+ * </p>
+ * @method destroy
+ * @return {Base} A reference to this object
+ * @final
+ * @chainable
+ */
+ destroy: function() {
+ Y.log('destroy called', 'life', 'base');
+
+ /**
+ * <p>
+ * Lifecycle event for the destroy phase,
+ * fired prior to destruction. Invoking the preventDefault
+ * method on the event object provided to subscribers will
+ * prevent destruction from proceeding.
+ * </p>
+ * <p>
+ * Subscribers to the "after" moment of this event, will be notified
+ * after destruction is complete (and as a result cannot prevent
+ * destruction).
+ * </p>
+ * @event destroy
+ * @preventable _defDestroyFn
+ * @param {EventFacade} e Event object
+ */
+ this.publish(DESTROY, {
+ queuable:false,
+ defaultFn: this._defDestroyFn
+ });
+ this.fire(DESTROY);
+ return this;
+ },
+
+ /**
+ * Default init event handler
+ *
+ * @method _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ * @protected
+ */
+ _defInitFn : function(e) {
+ this._initHierarchy(e.cfg);
+ if (this._initPlugins) {
+ // Need to initPlugins manually, to handle constructor parsing, static Plug parsing
+ this._initPlugins(e.cfg);
+ }
+ this._set(INITIALIZED, true);
+ },
+
+ /**
+ * Default destroy event handler
+ *
+ * @method _defDestroyFn
+ * @param {EventFacade} e Event object
+ * @protected
+ */
+ _defDestroyFn : function(e) {
+ this._destroyHierarchy();
+ if (this._destroyPlugins) {
+ this._destroyPlugins();
+ }
+ this._set(DESTROYED, true);
+ },
+
+ /**
+ * Returns the class hierarchy for this object, with Base being the last class in the array.
+ *
+ * @method _getClasses
+ * @protected
+ * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object.
+ * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the
+ * cached value.
+ */
+ _getClasses : function() {
+ if (!this._classes) {
+ this._initHierarchyData();
+ }
+ return this._classes;
+ },
+
+ /**
+ * Returns an aggregated set of attribute configurations, by traversing the class hierarchy.
+ *
+ * @method _getAttrCfgs
+ * @protected
+ * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy
+ * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return
+ * the cached value.
+ */
+ _getAttrCfgs : function() {
+ if (!this._attrs) {
+ this._initHierarchyData();
+ }
+ return this._attrs;
+ },
+
+ /**
+ * A helper method used when processing ATTRS across the class hierarchy during
+ * initialization. Returns a disposable object with the attributes defined for
+ * the provided class, extracted from the set of all attributes passed in .
+ *
+ * @method _filterAttrCfs
+ * @private
+ *
+ * @param {Function} clazz The class for which the desired attributes are required.
+ * @param {Object} allCfgs The set of all attribute configurations for this instance.
+ * Attributes will be removed from this set, if they belong to the filtered class, so
+ * that by the time all classes are processed, allCfgs will be empty.
+ *
+ * @return {Object} The set of attributes belonging to the class passed in, in the form
+ * of an object with attribute name/configuration pairs.
+ */
+ _filterAttrCfgs : function(clazz, allCfgs) {
+ var cfgs = null, attr, attrs = clazz.ATTRS;
+
+ if (attrs) {
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr) && allCfgs[attr]) {
+ cfgs = cfgs || {};
+ cfgs[attr] = allCfgs[attr];
+ delete allCfgs[attr];
+ }
+ }
+ }
+
+ return cfgs;
+ },
+
+ /**
+ * A helper method used by _getClasses and _getAttrCfgs, which determines both
+ * the array of classes and aggregate set of attribute configurations
+ * across the class hierarchy for the instance.
+ *
+ * @method _initHierarchyData
+ * @private
+ */
+ _initHierarchyData : function() {
+ var c = this.constructor,
+ classes = [],
+ attrs = [];
+
+ while (c) {
+ // Add to classes
+ classes[classes.length] = c;
+
+ // Add to attributes
+ if (c.ATTRS) {
+ attrs[attrs.length] = c.ATTRS;
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ this._classes = classes;
+ this._attrs = this._aggregateAttrs(attrs);
+ },
+
+ /**
+ * A helper method, used by _initHierarchyData to aggregate
+ * attribute configuration across the instances class hierarchy.
+ *
+ * The method will potect the attribute configuration value to protect the statically defined
+ * default value in ATTRS if required (if the value is an object literal, array or the
+ * attribute configuration has cloneDefaultValue set to shallow or deep).
+ *
+ * @method _aggregateAttrs
+ * @private
+ * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy
+ * (subclass first, Base last)
+ * @return {Object} The aggregate set of ATTRS definitions for the instance
+ */
+ _aggregateAttrs : function(allAttrs) {
+ var attr,
+ attrs,
+ cfg,
+ val,
+ path,
+ i,
+ clone,
+ cfgProps = Base._ATTR_CFG,
+ aggAttrs = {};
+
+ if (allAttrs) {
+ for (i = allAttrs.length-1; i >= 0; --i) {
+ attrs = allAttrs[i];
+
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+
+ // Protect config passed in
+ cfg = Y.mix({}, attrs[attr], true, cfgProps);
+
+ val = cfg.value;
+ clone = cfg.cloneDefaultValue;
+
+ if (val) {
+ if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) {
+ Y.log('Cloning default value for attribute:' + attr, 'info', 'base');
+ cfg.value = Y.clone(val);
+ } else if (clone === SHALLOW) {
+ Y.log('Merging default value for attribute:' + attr, 'info', 'base');
+ cfg.value = Y.merge(val);
+ }
+ // else if (clone === false), don't clone the static default value.
+ // It's intended to be used by reference.
+ }
+
+ path = null;
+ if (attr.indexOf(DOT) !== -1) {
+ path = attr.split(DOT);
+ attr = path.shift();
+ }
+
+ if (path && aggAttrs[attr] && aggAttrs[attr].value) {
+ O.setValue(aggAttrs[attr].value, path, val);
+ } else if (!path){
+ if (!aggAttrs[attr]) {
+ aggAttrs[attr] = cfg;
+ } else {
+ Y.mix(aggAttrs[attr], cfg, true, cfgProps);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return aggAttrs;
+ },
+
+ /**
+ * Initializes the class hierarchy for the instance, which includes
+ * initializing attributes for each class defined in the class's
+ * static <a href="#property_Base.ATTRS">ATTRS</a> property and
+ * invoking the initializer method on the prototype of each class in the hierarchy.
+ *
+ * @method _initHierarchy
+ * @param {Object} userVals Object with configuration property name/value pairs
+ * @private
+ */
+ _initHierarchy : function(userVals) {
+ var lazy = this._lazyAddAttrs,
+ constr,
+ constrProto,
+ ci,
+ ei,
+ el,
+ classes = this._getClasses(),
+ attrCfgs = this._getAttrCfgs();
+
+ for (ci = classes.length-1; ci >= 0; ci--) {
+
+ constr = classes[ci];
+ constrProto = constr.prototype;
+
+ if (constr._yuibuild && constr._yuibuild.exts && !constr._yuibuild.dynamic) {
+ for (ei = 0, el = constr._yuibuild.exts.length; ei < el; ei++) {
+ constr._yuibuild.exts[ei].apply(this, arguments);
+ }
+ }
+
+ this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy);
+
+ if (constrProto.hasOwnProperty(INITIALIZER)) {
+ constrProto.initializer.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Destroys the class hierarchy for this instance by invoking
+ * the descructor method on the prototype of each class in the hierarchy.
+ *
+ * @method _destroyHierarchy
+ * @private
+ */
+ _destroyHierarchy : function() {
+ var constr,
+ constrProto,
+ ci, cl,
+ classes = this._getClasses();
+
+ for (ci = 0, cl = classes.length; ci < cl; ci++) {
+ constr = classes[ci];
+ constrProto = constr.prototype;
+ if (constrProto.hasOwnProperty(DESTRUCTOR)) {
+ constrProto.destructor.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Default toString implementation. Provides the constructor NAME
+ * and the instance ID.
+ *
+ * @method toString
+ * @return {String} String representation for this object
+ */
+ toString: function() {
+ return this.constructor.NAME + "[" + Y.stamp(this) + "]";
+ }
+ };
+
+ // Straightup augment, no wrapper functions
+ Y.mix(Base, Attribute, false, null, 1);
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+ Y.Base = Base;
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
+YUI.add('base-pluginhost', function(Y) {
+
+ /**
+ * The base-pluginhost submodule adds Plugin support to Base, by augmenting Base with
+ * Plugin.Host and setting up static (class level) Base.plug and Base.unplug methods.
+ *
+ * @module base
+ * @submodule base-pluginhost
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ PluginHost = Y.Plugin.Host;
+
+ Y.mix(Base, PluginHost, false, null, 1);
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.plug">Plugin.Host.plug</a>. See aliased
+ * method for argument and return value details.
+ *
+ * @method Base.plug
+ * @static
+ */
+ Base.plug = PluginHost.plug;
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.unplug">Plugin.Host.unplug</a>. See the
+ * aliased method for argument and return value details.
+ *
+ * @method Base.unplug
+ * @static
+ */
+ Base.unplug = PluginHost.unplug;
+
+
+}, '3.0.0' ,{requires:['base-base', 'pluginhost']});
+YUI.add('base-build', function(Y) {
+
+ /**
+ * The base-build submodule provides Base.build functionality, which
+ * can be used to create custom classes, by aggregating extensions onto
+ * a main class.
+ *
+ * @module base
+ * @submodule base-build
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ L = Y.Lang;
+
+ /**
+ * The build configuration for the Base class.
+ *
+ * Defines the static fields which need to be aggregated
+ * when the Base class is used as the main class passed to
+ * the <a href="#method_Base.build">Base.build</a> method.
+ *
+ * @property Base._buildCfg
+ * @type Object
+ * @static
+ * @final
+ * @private
+ */
+ Base._buildCfg = {
+ aggregates : ["ATTRS", "_PLUG", "_UNPLUG"]
+ };
+
+ /**
+ * <p>
+ * Builds a custom constructor function (class) from the
+ * main function, and array of extension functions (classes)
+ * provided. The NAME field for the constructor function is
+ * defined by the first argument passed in.
+ * </p>
+ * <p>
+ * The cfg object supports the following properties
+ * </p>
+ * <dl>
+ * <dt>dynamic <boolean></dt>
+ * <dd>
+ * <p>If true (default), a completely new class
+ * is created which extends the main class, and acts as the
+ * host on which the extension classes are augmented.</p>
+ * <p>If false, the extensions classes are augmented directly to
+ * the main class, modifying the main class' prototype.</p>
+ * </dd>
+ * <dt>aggregates <String[]></dt>
+ * <dd>An array of static property names, which will get aggregated
+ * on to the built class, in addition to the default properties build
+ * will always aggregate as defined by the main class' static _buildCfg
+ * property.
+ * </dd>
+ * </dl>
+ *
+ * @method Base.build
+ * @static
+ * @param {Function} name The name of the new class. Used to defined the NAME property for the new class.
+ * @param {Function} main The main class on which to base the built class
+ * @param {Function[]} extensions The set of extension classes which will be
+ * augmented/aggregated to the built class.
+ * @param {Object} cfg Optional. Build configuration for the class (see description).
+ * @return {Function} A custom class, created from the provided main and extension classes
+ */
+ Base.build = function(name, main, extensions, cfg) {
+
+ var build = Base.build,
+ builtClass = build._getClass(main, cfg),
+ aggregates = build._getAggregates(main, cfg),
+ dynamic = builtClass._yuibuild.dynamic,
+ i, l, val, extClass;
+
+ // Shallow isolate aggregates
+ if (dynamic) {
+ if (aggregates) {
+ for (i = 0, l = aggregates.length; i < l; ++i) {
+ val = aggregates[i];
+ if (main.hasOwnProperty(val)) {
+ builtClass[val] = L.isArray(main[val]) ? [] : {};
+ }
+ }
+ Y.aggregate(builtClass, main, true, aggregates);
+ }
+ }
+
+ // Augment/Aggregate
+ for (i = 0, l = extensions.length; i < l; i++) {
+ extClass = extensions[i];
+
+ if (aggregates) {
+ Y.aggregate(builtClass, extClass, true, aggregates);
+ }
+
+ // Old augment
+ Y.mix(builtClass, extClass, true, null, 1);
+
+ builtClass._yuibuild.exts.push(extClass);
+ }
+
+ builtClass.prototype.hasImpl = build._hasImpl;
+
+ if (dynamic) {
+ builtClass.NAME = name;
+ builtClass.prototype.constructor = builtClass;
+ }
+
+ return builtClass;
+ };
+
+ Y.mix(Base.build, {
+
+ _template: function(main) {
+
+ function BuiltClass() {
+
+ BuiltClass.superclass.constructor.apply(this, arguments);
+
+ var f = BuiltClass._yuibuild.exts,
+ l = f.length,
+ i;
+
+ for (i = 0; i < l; i++) {
+ f[i].apply(this, arguments);
+ }
+
+ return this;
+ }
+ Y.extend(BuiltClass, main);
+
+ return BuiltClass;
+ },
+
+ _hasImpl : function(extClass) {
+ var classes = this._getClasses();
+ for (var i = 0, l = classes.length; i < l; i++) {
+ var cls = classes[i];
+
+ if (cls._yuibuild) {
+ var exts = cls._yuibuild.exts,
+ ll = exts.length,
+ j;
+
+ for (j = 0; j < ll; j++) {
+ if (exts[j] === extClass) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ },
+
+ _getClass : function(main, cfg) {
+
+ var dynamic = (cfg && false === cfg.dynamic) ? false : true,
+ builtClass = (dynamic) ? Base.build._template(main) : main;
+
+ builtClass._yuibuild = {
+ id: null,
+ exts : [],
+ dynamic : dynamic
+ };
+
+ return builtClass;
+ },
+
+ _getAggregates : function(main, cfg) {
+ var aggr = [],
+ cfgAggr = (cfg && cfg.aggregates),
+ c = main,
+ classAggr;
+
+ while (c && c.prototype) {
+ classAggr = c._buildCfg && c._buildCfg.aggregates;
+ if (classAggr) {
+ aggr = aggr.concat(classAggr);
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ if (cfgAggr) {
+ aggr = aggr.concat(cfgAggr);
+ }
+
+ return aggr;
+ }
+ });
+
+
+}, '3.0.0' ,{requires:['base-base']});
+
+
+YUI.add('base', function(Y){}, '3.0.0' ,{use:['base-base', 'base-pluginhost', 'base-build']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("base-base",function(B){var H=B.Object,J=B.Lang,I=".",F="destroy",P="init",N="initialized",G="destroyed",D="initializer",C=Object.prototype.constructor,K="deep",Q="shallow",M="destructor",A=B.Attribute;function E(){A.call(this);var L=B.Plugin&&B.Plugin.Host;if(this._initPlugins&&L){L.call(this);}if(this._lazyAddAttrs!==false){this._lazyAddAttrs=true;}this.init.apply(this,arguments);}E._ATTR_CFG=A._ATTR_CFG.concat("cloneDefaultValue");E.NAME="base";E.ATTRS={initialized:{readOnly:true,value:false},destroyed:{readOnly:true,value:false}};E.prototype={init:function(L){this._yuievt.config.prefix=this.name=this.constructor.NAME;this.publish(P,{queuable:false,defaultFn:this._defInitFn});if(L){if(L.on){this.on(L.on);}if(L.after){this.after(L.after);}}this.fire(P,{cfg:L});return this;},destroy:function(){this.publish(F,{queuable:false,defaultFn:this._defDestroyFn});this.fire(F);return this;},_defInitFn:function(L){this._initHierarchy(L.cfg);if(this._initPlugins){this._initPlugins(L.cfg);}this._set(N,true);},_defDestroyFn:function(L){this._destroyHierarchy();if(this._destroyPlugins){this._destroyPlugins();}this._set(G,true);},_getClasses:function(){if(!this._classes){this._initHierarchyData();}return this._classes;},_getAttrCfgs:function(){if(!this._attrs){this._initHierarchyData();}return this._attrs;},_filterAttrCfgs:function(T,O){var R=null,L,S=T.ATTRS;if(S){for(L in S){if(S.hasOwnProperty(L)&&O[L]){R=R||{};R[L]=O[L];delete O[L];}}}return R;},_initHierarchyData:function(){var R=this.constructor,O=[],L=[];while(R){O[O.length]=R;if(R.ATTRS){L[L.length]=R.ATTRS;}R=R.superclass?R.superclass.constructor:null;}this._classes=O;this._attrs=this._aggregateAttrs(L);},_aggregateAttrs:function(W){var T,X,S,L,Y,O,V,R=E._ATTR_CFG,U={};if(W){for(O=W.length-1;O>=0;--O){X=W[O];for(T in X){if(X.hasOwnProperty(T)){S=B.mix({},X[T],true,R);L=S.value;V=S.cloneDefaultValue;if(L){if((V===undefined&&(C===L.constructor||J.isArray(L)))||V===K||V===true){S.value=B.clone(L);}else{if(V===Q){S.value=B.merge(L);}}}Y=null;if(T.indexOf(I)!==-1){Y=T.split(I);T=Y.shift();}if(Y&&U[T]&&U[T].value){H.setValue(U[T].value,Y,L);}else{if(!Y){if(!U[T]){U[T]=S;}else{B.mix(U[T],S,true,R);}}}}}}}return U;},_initHierarchy:function(U){var R=this._lazyAddAttrs,V,W,X,S,O,T=this._getClasses(),L=this._getAttrCfgs();for(X=T.length-1;X>=0;X--){V=T[X];W=V.prototype;if(V._yuibuild&&V._yuibuild.exts&&!V._yuibuild.dynamic){for(S=0,O=V._yuibuild.exts.length;S<O;S++){V._yuibuild.exts[S].apply(this,arguments);}}this.addAttrs(this._filterAttrCfgs(V,L),U,R);if(W.hasOwnProperty(D)){W.initializer.apply(this,arguments);}}},_destroyHierarchy:function(){var T,O,S,L,R=this._getClasses();for(S=0,L=R.length;S<L;S++){T=R[S];O=T.prototype;if(O.hasOwnProperty(M)){O.destructor.apply(this,arguments);}}},toString:function(){return this.constructor.NAME+"["+B.stamp(this)+"]";}};B.mix(E,A,false,null,1);E.prototype.constructor=E;B.Base=E;E.prototype.constructor=E;},"3.0.0",{requires:["attribute-base"]});YUI.add("base-pluginhost",function(C){var A=C.Base,B=C.Plugin.Host;C.mix(A,B,false,null,1);A.plug=B.plug;A.unplug=B.unplug;},"3.0.0",{requires:["base-base","pluginhost"]});YUI.add("base-build",function(C){var B=C.Base,A=C.Lang;B._buildCfg={aggregates:["ATTRS","_PLUG","_UNPLUG"]};B.build=function(D,I,M,L){var O=B.build,E=O._getClass(I,L),K=O._getAggregates(I,L),G=E._yuibuild.dynamic,J,H,F,N;if(G){if(K){for(J=0,H=K.length;J<H;++J){F=K[J];if(I.hasOwnProperty(F)){E[F]=A.isArray(I[F])?[]:{};}}C.aggregate(E,I,true,K);}}for(J=0,H=M.length;J<H;J++){N=M[J];if(K){C.aggregate(E,N,true,K);}C.mix(E,N,true,null,1);E._yuibuild.exts.push(N);}E.prototype.hasImpl=O._hasImpl;if(G){E.NAME=D;E.prototype.constructor=E;}return E;};C.mix(B.build,{_template:function(D){function E(){E.superclass.constructor.apply(this,arguments);var H=E._yuibuild.exts,F=H.length,G;for(G=0;G<F;G++){H[G].apply(this,arguments);}return this;}C.extend(E,D);return E;},_hasImpl:function(G){var J=this._getClasses();for(var I=0,E=J.length;I<E;I++){var D=J[I];if(D._yuibuild){var H=D._yuibuild.exts,K=H.length,F;for(F=0;F<K;F++){if(H[F]===G){return true;}}}}return false;},_getClass:function(D,E){var F=(E&&false===E.dynamic)?false:true,G=(F)?B.build._template(D):D;G._yuibuild={id:null,exts:[],dynamic:F};return G;},_getAggregates:function(D,E){var F=[],H=(E&&E.aggregates),I=D,G;while(I&&I.prototype){G=I._buildCfg&&I._buildCfg.aggregates;if(G){F=F.concat(G);}I=I.superclass?I.superclass.constructor:null;}if(H){F=F.concat(H);}return F;}});},"3.0.0",{requires:["base-base"]});YUI.add("base",function(A){},"3.0.0",{use:["base-base","base-pluginhost","base-build"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-pluginhost', function(Y) {
+
+ /**
+ * The base-pluginhost submodule adds Plugin support to Base, by augmenting Base with
+ * Plugin.Host and setting up static (class level) Base.plug and Base.unplug methods.
+ *
+ * @module base
+ * @submodule base-pluginhost
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ PluginHost = Y.Plugin.Host;
+
+ Y.mix(Base, PluginHost, false, null, 1);
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.plug">Plugin.Host.plug</a>. See aliased
+ * method for argument and return value details.
+ *
+ * @method Base.plug
+ * @static
+ */
+ Base.plug = PluginHost.plug;
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.unplug">Plugin.Host.unplug</a>. See the
+ * aliased method for argument and return value details.
+ *
+ * @method Base.unplug
+ * @static
+ */
+ Base.unplug = PluginHost.unplug;
+
+
+}, '3.0.0' ,{requires:['base-base', 'pluginhost']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("base-pluginhost",function(C){var A=C.Base,B=C.Plugin.Host;C.mix(A,B,false,null,1);A.plug=B.plug;A.unplug=B.unplug;},"3.0.0",{requires:["base-base","pluginhost"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-pluginhost', function(Y) {
+
+ /**
+ * The base-pluginhost submodule adds Plugin support to Base, by augmenting Base with
+ * Plugin.Host and setting up static (class level) Base.plug and Base.unplug methods.
+ *
+ * @module base
+ * @submodule base-pluginhost
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ PluginHost = Y.Plugin.Host;
+
+ Y.mix(Base, PluginHost, false, null, 1);
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.plug">Plugin.Host.plug</a>. See aliased
+ * method for argument and return value details.
+ *
+ * @method Base.plug
+ * @static
+ */
+ Base.plug = PluginHost.plug;
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.unplug">Plugin.Host.unplug</a>. See the
+ * aliased method for argument and return value details.
+ *
+ * @method Base.unplug
+ * @static
+ */
+ Base.unplug = PluginHost.unplug;
+
+
+}, '3.0.0' ,{requires:['base-base', 'pluginhost']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('base-base', function(Y) {
+
+ /**
+ * The base module provides the Base class, which objects requiring attribute and custom event support can extend.
+ * The module also provides two ways to reuse code - An augmentable Plugin.Host interface which provides plugin support
+ * (which is augmented to the Base class) and Base.build which provides a way to
+ * build custom classes using extensions.
+ *
+ * @module base
+ */
+
+ /**
+ * The base-base submodule provides the Base class without the Plugin support, provided by Plugin.Host,
+ * and without the extension support provided by Base.build.
+ *
+ * @module base
+ * @submodule base-base
+ */
+ var O = Y.Object,
+ L = Y.Lang,
+ DOT = ".",
+ DESTROY = "destroy",
+ INIT = "init",
+ INITIALIZED = "initialized",
+ DESTROYED = "destroyed",
+ INITIALIZER = "initializer",
+ OBJECT_CONSTRUCTOR = Object.prototype.constructor,
+ DEEP = "deep",
+ SHALLOW = "shallow",
+ DESTRUCTOR = "destructor",
+
+ Attribute = Y.Attribute;
+
+ /**
+ * <p>
+ * A base class which objects requiring attributes and custom event support can
+ * extend. Base also handles the chaining of initializer and destructor methods across
+ * the hierarchy as part of object construction and destruction. Additionally, attributes configured
+ * through the static <a href="#property_Base.ATTRS">ATTRS</a> property for each class
+ * in the hierarchy will be initialized by Base.
+ * </p>
+ *
+ * <p>
+ * The static <a href="#property_Base.NAME">NAME</a> property of each class extending
+ * from Base will be used as the identifier for the class, and is used by Base to prefix
+ * all events fired by instances of that class.
+ * </p>
+ * @class Base
+ * @constructor
+ * @uses Attribute
+ * @uses Plugin.Host
+ *
+ * @param {Object} config Object with configuration property name/value pairs
+ */
+ function Base() {
+
+ Attribute.call(this);
+
+ // If Plugin.Host has been augmented [ through base-pluginhost ], setup it's
+ // initial state, but don't initialize Plugins yet. That's done after initialization.
+ var PluginHost = Y.Plugin && Y.Plugin.Host;
+ if (this._initPlugins && PluginHost) {
+ PluginHost.call(this);
+ }
+
+ if (this._lazyAddAttrs !== false) { this._lazyAddAttrs = true; }
+
+ this.init.apply(this, arguments);
+ }
+
+ /**
+ * The list of properties which can be configured for
+ * each attribute (e.g. setter, getter, writeOnce, readOnly etc.)
+ *
+ * @property Base._ATTR_CFG
+ * @type Array
+ * @static
+ * @private
+ */
+ Base._ATTR_CFG = Attribute._ATTR_CFG.concat("cloneDefaultValue");
+
+ /**
+ * <p>
+ * The string to be used to identify instances of
+ * this class, for example in prefixing events.
+ * </p>
+ * <p>
+ * Classes extending Base, should define their own
+ * static NAME property, which should be camelCase by
+ * convention (e.g. MyClass.NAME = "myClass";).
+ * </p>
+ * @property Base.NAME
+ * @type String
+ * @static
+ */
+ Base.NAME = "base";
+
+ /**
+ * The default set of attributes which will be available for instances of this class, and
+ * their configuration. In addition to the configuration properties listed by
+ * Attribute's <a href="Attribute.html#method_addAttr">addAttr</a> method, the attribute
+ * can also be configured with a "cloneDefaultValue" property, which defines how the statically
+ * defined value field should be protected ("shallow", "deep" and false are supported values).
+ *
+ * By default if the value is an object literal or an array it will be "shallow" cloned, to
+ * protect the default value.
+ *
+ * @property Base.ATTRS
+ * @type Object
+ * @static
+ */
+ Base.ATTRS = {
+ /**
+ * Flag indicating whether or not this object
+ * has been through the init lifecycle phase.
+ *
+ * @attribute initialized
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ initialized: {
+ readOnly:true,
+ value:false
+ },
+
+ /**
+ * Flag indicating whether or not this object
+ * has been through the destroy lifecycle phase.
+ *
+ * @attribute destroyed
+ * @readonly
+ * @default false
+ * @type boolean
+ */
+ destroyed: {
+ readOnly:true,
+ value:false
+ }
+ };
+
+ Base.prototype = {
+
+ /**
+ * Init lifecycle method, invoked during construction.
+ * Fires the init event prior to setting up attributes and
+ * invoking initializers for the class hierarchy.
+ *
+ * @method init
+ * @final
+ * @chainable
+ * @param {Object} config Object with configuration property name/value pairs
+ * @return {Base} A reference to this object
+ */
+ init: function(config) {
+
+ /**
+ * The string used to identify the class of this object.
+ *
+ * @deprecated Use this.constructor.NAME
+ * @property name
+ * @type String
+ */
+ this._yuievt.config.prefix = this.name = this.constructor.NAME;
+
+ /**
+ * <p>
+ * Lifecycle event for the init phase, fired prior to initialization.
+ * Invoking the preventDefault() method on the event object provided
+ * to subscribers will prevent initialization from occuring.
+ * </p>
+ * <p>
+ * Subscribers to the "after" momemt of this event, will be notified
+ * after initialization of the object is complete (and therefore
+ * cannot prevent initialization).
+ * </p>
+ *
+ * @event init
+ * @preventable _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ */
+ this.publish(INIT, {
+ queuable:false,
+ defaultFn:this._defInitFn
+ });
+
+ if (config) {
+ if (config.on) {
+ this.on(config.on);
+ }
+ if (config.after) {
+ this.after(config.after);
+ }
+ }
+
+ this.fire(INIT, {cfg: config});
+
+ return this;
+ },
+
+ /**
+ * <p>
+ * Destroy lifecycle method. Fires the destroy
+ * event, prior to invoking destructors for the
+ * class hierarchy.
+ * </p>
+ * <p>
+ * Subscribers to the destroy
+ * event can invoke preventDefault on the event object, to prevent destruction
+ * from proceeding.
+ * </p>
+ * @method destroy
+ * @return {Base} A reference to this object
+ * @final
+ * @chainable
+ */
+ destroy: function() {
+
+ /**
+ * <p>
+ * Lifecycle event for the destroy phase,
+ * fired prior to destruction. Invoking the preventDefault
+ * method on the event object provided to subscribers will
+ * prevent destruction from proceeding.
+ * </p>
+ * <p>
+ * Subscribers to the "after" moment of this event, will be notified
+ * after destruction is complete (and as a result cannot prevent
+ * destruction).
+ * </p>
+ * @event destroy
+ * @preventable _defDestroyFn
+ * @param {EventFacade} e Event object
+ */
+ this.publish(DESTROY, {
+ queuable:false,
+ defaultFn: this._defDestroyFn
+ });
+ this.fire(DESTROY);
+ return this;
+ },
+
+ /**
+ * Default init event handler
+ *
+ * @method _defInitFn
+ * @param {EventFacade} e Event object, with a cfg property which
+ * refers to the configuration object passed to the constructor.
+ * @protected
+ */
+ _defInitFn : function(e) {
+ this._initHierarchy(e.cfg);
+ if (this._initPlugins) {
+ // Need to initPlugins manually, to handle constructor parsing, static Plug parsing
+ this._initPlugins(e.cfg);
+ }
+ this._set(INITIALIZED, true);
+ },
+
+ /**
+ * Default destroy event handler
+ *
+ * @method _defDestroyFn
+ * @param {EventFacade} e Event object
+ * @protected
+ */
+ _defDestroyFn : function(e) {
+ this._destroyHierarchy();
+ if (this._destroyPlugins) {
+ this._destroyPlugins();
+ }
+ this._set(DESTROYED, true);
+ },
+
+ /**
+ * Returns the class hierarchy for this object, with Base being the last class in the array.
+ *
+ * @method _getClasses
+ * @protected
+ * @return {Function[]} An array of classes (constructor functions), making up the class hierarchy for this object.
+ * This value is cached the first time the method, or _getAttrCfgs, is invoked. Subsequent invocations return the
+ * cached value.
+ */
+ _getClasses : function() {
+ if (!this._classes) {
+ this._initHierarchyData();
+ }
+ return this._classes;
+ },
+
+ /**
+ * Returns an aggregated set of attribute configurations, by traversing the class hierarchy.
+ *
+ * @method _getAttrCfgs
+ * @protected
+ * @return {Object} The hash of attribute configurations, aggregated across classes in the hierarchy
+ * This value is cached the first time the method, or _getClasses, is invoked. Subsequent invocations return
+ * the cached value.
+ */
+ _getAttrCfgs : function() {
+ if (!this._attrs) {
+ this._initHierarchyData();
+ }
+ return this._attrs;
+ },
+
+ /**
+ * A helper method used when processing ATTRS across the class hierarchy during
+ * initialization. Returns a disposable object with the attributes defined for
+ * the provided class, extracted from the set of all attributes passed in .
+ *
+ * @method _filterAttrCfs
+ * @private
+ *
+ * @param {Function} clazz The class for which the desired attributes are required.
+ * @param {Object} allCfgs The set of all attribute configurations for this instance.
+ * Attributes will be removed from this set, if they belong to the filtered class, so
+ * that by the time all classes are processed, allCfgs will be empty.
+ *
+ * @return {Object} The set of attributes belonging to the class passed in, in the form
+ * of an object with attribute name/configuration pairs.
+ */
+ _filterAttrCfgs : function(clazz, allCfgs) {
+ var cfgs = null, attr, attrs = clazz.ATTRS;
+
+ if (attrs) {
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr) && allCfgs[attr]) {
+ cfgs = cfgs || {};
+ cfgs[attr] = allCfgs[attr];
+ delete allCfgs[attr];
+ }
+ }
+ }
+
+ return cfgs;
+ },
+
+ /**
+ * A helper method used by _getClasses and _getAttrCfgs, which determines both
+ * the array of classes and aggregate set of attribute configurations
+ * across the class hierarchy for the instance.
+ *
+ * @method _initHierarchyData
+ * @private
+ */
+ _initHierarchyData : function() {
+ var c = this.constructor,
+ classes = [],
+ attrs = [];
+
+ while (c) {
+ // Add to classes
+ classes[classes.length] = c;
+
+ // Add to attributes
+ if (c.ATTRS) {
+ attrs[attrs.length] = c.ATTRS;
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ this._classes = classes;
+ this._attrs = this._aggregateAttrs(attrs);
+ },
+
+ /**
+ * A helper method, used by _initHierarchyData to aggregate
+ * attribute configuration across the instances class hierarchy.
+ *
+ * The method will potect the attribute configuration value to protect the statically defined
+ * default value in ATTRS if required (if the value is an object literal, array or the
+ * attribute configuration has cloneDefaultValue set to shallow or deep).
+ *
+ * @method _aggregateAttrs
+ * @private
+ * @param {Array} allAttrs An array of ATTRS definitions across classes in the hierarchy
+ * (subclass first, Base last)
+ * @return {Object} The aggregate set of ATTRS definitions for the instance
+ */
+ _aggregateAttrs : function(allAttrs) {
+ var attr,
+ attrs,
+ cfg,
+ val,
+ path,
+ i,
+ clone,
+ cfgProps = Base._ATTR_CFG,
+ aggAttrs = {};
+
+ if (allAttrs) {
+ for (i = allAttrs.length-1; i >= 0; --i) {
+ attrs = allAttrs[i];
+
+ for (attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+
+ // Protect config passed in
+ cfg = Y.mix({}, attrs[attr], true, cfgProps);
+
+ val = cfg.value;
+ clone = cfg.cloneDefaultValue;
+
+ if (val) {
+ if ( (clone === undefined && (OBJECT_CONSTRUCTOR === val.constructor || L.isArray(val))) || clone === DEEP || clone === true) {
+ cfg.value = Y.clone(val);
+ } else if (clone === SHALLOW) {
+ cfg.value = Y.merge(val);
+ }
+ // else if (clone === false), don't clone the static default value.
+ // It's intended to be used by reference.
+ }
+
+ path = null;
+ if (attr.indexOf(DOT) !== -1) {
+ path = attr.split(DOT);
+ attr = path.shift();
+ }
+
+ if (path && aggAttrs[attr] && aggAttrs[attr].value) {
+ O.setValue(aggAttrs[attr].value, path, val);
+ } else if (!path){
+ if (!aggAttrs[attr]) {
+ aggAttrs[attr] = cfg;
+ } else {
+ Y.mix(aggAttrs[attr], cfg, true, cfgProps);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return aggAttrs;
+ },
+
+ /**
+ * Initializes the class hierarchy for the instance, which includes
+ * initializing attributes for each class defined in the class's
+ * static <a href="#property_Base.ATTRS">ATTRS</a> property and
+ * invoking the initializer method on the prototype of each class in the hierarchy.
+ *
+ * @method _initHierarchy
+ * @param {Object} userVals Object with configuration property name/value pairs
+ * @private
+ */
+ _initHierarchy : function(userVals) {
+ var lazy = this._lazyAddAttrs,
+ constr,
+ constrProto,
+ ci,
+ ei,
+ el,
+ classes = this._getClasses(),
+ attrCfgs = this._getAttrCfgs();
+
+ for (ci = classes.length-1; ci >= 0; ci--) {
+
+ constr = classes[ci];
+ constrProto = constr.prototype;
+
+ if (constr._yuibuild && constr._yuibuild.exts && !constr._yuibuild.dynamic) {
+ for (ei = 0, el = constr._yuibuild.exts.length; ei < el; ei++) {
+ constr._yuibuild.exts[ei].apply(this, arguments);
+ }
+ }
+
+ this.addAttrs(this._filterAttrCfgs(constr, attrCfgs), userVals, lazy);
+
+ if (constrProto.hasOwnProperty(INITIALIZER)) {
+ constrProto.initializer.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Destroys the class hierarchy for this instance by invoking
+ * the descructor method on the prototype of each class in the hierarchy.
+ *
+ * @method _destroyHierarchy
+ * @private
+ */
+ _destroyHierarchy : function() {
+ var constr,
+ constrProto,
+ ci, cl,
+ classes = this._getClasses();
+
+ for (ci = 0, cl = classes.length; ci < cl; ci++) {
+ constr = classes[ci];
+ constrProto = constr.prototype;
+ if (constrProto.hasOwnProperty(DESTRUCTOR)) {
+ constrProto.destructor.apply(this, arguments);
+ }
+ }
+ },
+
+ /**
+ * Default toString implementation. Provides the constructor NAME
+ * and the instance ID.
+ *
+ * @method toString
+ * @return {String} String representation for this object
+ */
+ toString: function() {
+ return this.constructor.NAME + "[" + Y.stamp(this) + "]";
+ }
+ };
+
+ // Straightup augment, no wrapper functions
+ Y.mix(Base, Attribute, false, null, 1);
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+ Y.Base = Base;
+
+ // Fix constructor
+ Base.prototype.constructor = Base;
+
+
+}, '3.0.0' ,{requires:['attribute-base']});
+YUI.add('base-pluginhost', function(Y) {
+
+ /**
+ * The base-pluginhost submodule adds Plugin support to Base, by augmenting Base with
+ * Plugin.Host and setting up static (class level) Base.plug and Base.unplug methods.
+ *
+ * @module base
+ * @submodule base-pluginhost
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ PluginHost = Y.Plugin.Host;
+
+ Y.mix(Base, PluginHost, false, null, 1);
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.plug">Plugin.Host.plug</a>. See aliased
+ * method for argument and return value details.
+ *
+ * @method Base.plug
+ * @static
+ */
+ Base.plug = PluginHost.plug;
+
+ /**
+ * Alias for <a href="Plugin.Host.html#method_Plugin.Host.unplug">Plugin.Host.unplug</a>. See the
+ * aliased method for argument and return value details.
+ *
+ * @method Base.unplug
+ * @static
+ */
+ Base.unplug = PluginHost.unplug;
+
+
+}, '3.0.0' ,{requires:['base-base', 'pluginhost']});
+YUI.add('base-build', function(Y) {
+
+ /**
+ * The base-build submodule provides Base.build functionality, which
+ * can be used to create custom classes, by aggregating extensions onto
+ * a main class.
+ *
+ * @module base
+ * @submodule base-build
+ * @for Base
+ */
+
+ var Base = Y.Base,
+ L = Y.Lang;
+
+ /**
+ * The build configuration for the Base class.
+ *
+ * Defines the static fields which need to be aggregated
+ * when the Base class is used as the main class passed to
+ * the <a href="#method_Base.build">Base.build</a> method.
+ *
+ * @property Base._buildCfg
+ * @type Object
+ * @static
+ * @final
+ * @private
+ */
+ Base._buildCfg = {
+ aggregates : ["ATTRS", "_PLUG", "_UNPLUG"]
+ };
+
+ /**
+ * <p>
+ * Builds a custom constructor function (class) from the
+ * main function, and array of extension functions (classes)
+ * provided. The NAME field for the constructor function is
+ * defined by the first argument passed in.
+ * </p>
+ * <p>
+ * The cfg object supports the following properties
+ * </p>
+ * <dl>
+ * <dt>dynamic <boolean></dt>
+ * <dd>
+ * <p>If true (default), a completely new class
+ * is created which extends the main class, and acts as the
+ * host on which the extension classes are augmented.</p>
+ * <p>If false, the extensions classes are augmented directly to
+ * the main class, modifying the main class' prototype.</p>
+ * </dd>
+ * <dt>aggregates <String[]></dt>
+ * <dd>An array of static property names, which will get aggregated
+ * on to the built class, in addition to the default properties build
+ * will always aggregate as defined by the main class' static _buildCfg
+ * property.
+ * </dd>
+ * </dl>
+ *
+ * @method Base.build
+ * @static
+ * @param {Function} name The name of the new class. Used to defined the NAME property for the new class.
+ * @param {Function} main The main class on which to base the built class
+ * @param {Function[]} extensions The set of extension classes which will be
+ * augmented/aggregated to the built class.
+ * @param {Object} cfg Optional. Build configuration for the class (see description).
+ * @return {Function} A custom class, created from the provided main and extension classes
+ */
+ Base.build = function(name, main, extensions, cfg) {
+
+ var build = Base.build,
+ builtClass = build._getClass(main, cfg),
+ aggregates = build._getAggregates(main, cfg),
+ dynamic = builtClass._yuibuild.dynamic,
+ i, l, val, extClass;
+
+ // Shallow isolate aggregates
+ if (dynamic) {
+ if (aggregates) {
+ for (i = 0, l = aggregates.length; i < l; ++i) {
+ val = aggregates[i];
+ if (main.hasOwnProperty(val)) {
+ builtClass[val] = L.isArray(main[val]) ? [] : {};
+ }
+ }
+ Y.aggregate(builtClass, main, true, aggregates);
+ }
+ }
+
+ // Augment/Aggregate
+ for (i = 0, l = extensions.length; i < l; i++) {
+ extClass = extensions[i];
+
+ if (aggregates) {
+ Y.aggregate(builtClass, extClass, true, aggregates);
+ }
+
+ // Old augment
+ Y.mix(builtClass, extClass, true, null, 1);
+
+ builtClass._yuibuild.exts.push(extClass);
+ }
+
+ builtClass.prototype.hasImpl = build._hasImpl;
+
+ if (dynamic) {
+ builtClass.NAME = name;
+ builtClass.prototype.constructor = builtClass;
+ }
+
+ return builtClass;
+ };
+
+ Y.mix(Base.build, {
+
+ _template: function(main) {
+
+ function BuiltClass() {
+
+ BuiltClass.superclass.constructor.apply(this, arguments);
+
+ var f = BuiltClass._yuibuild.exts,
+ l = f.length,
+ i;
+
+ for (i = 0; i < l; i++) {
+ f[i].apply(this, arguments);
+ }
+
+ return this;
+ }
+ Y.extend(BuiltClass, main);
+
+ return BuiltClass;
+ },
+
+ _hasImpl : function(extClass) {
+ var classes = this._getClasses();
+ for (var i = 0, l = classes.length; i < l; i++) {
+ var cls = classes[i];
+
+ if (cls._yuibuild) {
+ var exts = cls._yuibuild.exts,
+ ll = exts.length,
+ j;
+
+ for (j = 0; j < ll; j++) {
+ if (exts[j] === extClass) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ },
+
+ _getClass : function(main, cfg) {
+
+ var dynamic = (cfg && false === cfg.dynamic) ? false : true,
+ builtClass = (dynamic) ? Base.build._template(main) : main;
+
+ builtClass._yuibuild = {
+ id: null,
+ exts : [],
+ dynamic : dynamic
+ };
+
+ return builtClass;
+ },
+
+ _getAggregates : function(main, cfg) {
+ var aggr = [],
+ cfgAggr = (cfg && cfg.aggregates),
+ c = main,
+ classAggr;
+
+ while (c && c.prototype) {
+ classAggr = c._buildCfg && c._buildCfg.aggregates;
+ if (classAggr) {
+ aggr = aggr.concat(classAggr);
+ }
+ c = c.superclass ? c.superclass.constructor : null;
+ }
+
+ if (cfgAggr) {
+ aggr = aggr.concat(cfgAggr);
+ }
+
+ return aggr;
+ }
+ });
+
+
+}, '3.0.0' ,{requires:['base-base']});
+
+
+YUI.add('base', function(Y){}, '3.0.0' ,{use:['base-base', 'base-pluginhost', 'base-build']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('cache', function(Y) {
+
+/**
+ * The Cache utility provides a common configurable interface for components to
+ * cache and retrieve data from a local JavaScript struct.
+ *
+ * @module cache
+ */
+var LANG = Y.Lang,
+
+/**
+ * Base class for the YUI Cache utility.
+ * @class Cache
+ * @extends Plugin.Base
+ * @constructor
+ */
+Cache = function() {
+ Cache.superclass.constructor.apply(this, arguments);
+};
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(Cache, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NS: "cache",
+
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NAME: "cache",
+
+
+ ATTRS: {
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * @attribute max
+ * @description Maximum number of entries the Cache can hold.
+ * Set to 0 to turn off caching.
+ * @type Number
+ * @default 0
+ */
+ max: {
+ value: 0,
+ validator: function(value) {
+ return (LANG.isNumber(value));
+ },
+ setter: function(value) {
+ // If the cache is full, make room by removing stalest element (index=0)
+ var entries = this._entries;
+ if(value > 0) {
+ if(entries) {
+ while(entries.length > value) {
+ entries.shift();
+ }
+ }
+ }
+ else {
+ this._entries = [];
+ }
+ return value;
+ }
+ },
+
+ /**
+ * @attribute size
+ * @description Number of entries currently cached.
+ * @type Number
+ */
+ size: {
+ readOnly: true,
+ getter: function() {
+ return this._entries.length;
+ }
+ },
+
+ /**
+ * @attribute uniqueKeys
+ * @description Validate uniqueness of stored keys. Default is false and
+ * is more performant.
+ * @type Number
+ */
+ uniqueKeys: {
+ value: false,
+ validator: function(value) {
+ return (LANG.isBoolean(value));
+ }
+ },
+
+ /**
+ * @attribute entries
+ * @description Cached entries.
+ * @type Array
+ */
+ entries: {
+ readOnly: true,
+ getter: function() {
+ return this._entries;
+ }
+ }
+ }
+});
+
+Y.extend(Cache, Y.Plugin.Base, {
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache private properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Array of request/response objects indexed chronologically.
+ *
+ * @property _entries
+ * @type Object[]
+ * @private
+ */
+ _entries: null,
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache private methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * @method initializer
+ * @description Internal init() handler.
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+
+ /**
+ * @event add
+ * @description Fired when an entry is added.
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
+ * </dl>
+ * @preventable _defAddFn
+ */
+ this.publish("add", {defaultFn: this._defAddFn});
+
+ /**
+ * @event flush
+ * @description Fired when the cache is flushed.
+ * @param e {Event.Facade} Event Facade object.
+ * @preventable _defFlushFn
+ */
+ this.publish("flush", {defaultFn: this._defFlushFn});
+
+ /**
+ * @event request
+ * @description Fired when an entry is requested from the cache.
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>request (Object)</dt> <dd>The request object.</dd>
+ * </dl>
+ */
+
+ /**
+ * @event retrieve
+ * @description Fired when an entry is retrieved from the cache.
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
+ * </dl>
+ */
+
+ // Initialize internal values
+ this._entries = [];
+ Y.log("Cache initialized", "info", "cache");
+ },
+
+ /**
+ * @method destructor
+ * @description Internal destroy() handler.
+ * @private
+ */
+ destructor: function() {
+ this._entries = null;
+ Y.log("Cache destroyed", "info", "cache");
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache protected methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Adds entry to cache.
+ *
+ * @method _defAddFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
+ * </dl>
+ * @protected
+ */
+ _defAddFn: function(e) {
+ var entries = this._entries,
+ max = this.get("max"),
+ entry = e.entry;
+
+ if(this.get("uniqueKeys") && (this.retrieve(e.entry.request))) {
+ entries.shift();
+ }
+
+
+ // If the cache at or over capacity, make room by removing stalest element (index=0)
+ while(entries.length>=max) {
+ entries.shift();
+ }
+
+ // Add entry to cache in the newest position, at the end of the array
+ entries[entries.length] = entry;
+ Y.log("Cached entry: " + Y.dump(entry), "info", "cache");
+ },
+
+ /**
+ * Flushes cache.
+ *
+ * @method _defFlushFn
+ * @param e {Event.Facade} Event Facade object.
+ * @protected
+ */
+ _defFlushFn: function(e) {
+ this._entries = [];
+ Y.log("Cache flushed", "info", "cache");
+ },
+
+ /**
+ * Default overridable method compares current request with given cache entry.
+ * Returns true if current request matches the cached request, otherwise
+ * false. Implementers should override this method to customize the
+ * cache-matching algorithm.
+ *
+ * @method _isMatch
+ * @param request {Object} Request object.
+ * @param entry {Object} Cached entry.
+ * @return {Boolean} True if current request matches given cached request, false otherwise.
+ * @protected
+ */
+ _isMatch: function(request, entry) {
+ return (request === entry.request);
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache public methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Adds a new entry to the cache of the format
+ * {request:request, response:response, payload:payload}.
+ * If cache is full, evicts the stalest entry before adding the new one.
+ *
+ * @method add
+ * @param request {Object} Request value.
+ * @param response {Object} Response value.
+ * @param payload {Object} (optional) Arbitrary data payload.
+ */
+ add: function(request, response, payload) {
+ if(this.get("entries") && (this.get("max")>0) &&
+ (LANG.isValue(request) || LANG.isNull(request) || LANG.isUndefined(request))) {
+ this.fire("add", {entry: {request:request, response:response, payload:payload}});
+ }
+ else {
+ Y.log("Could not add " + Y.dump(response) + " to cache for " + Y.dump(request), "info", "cache");
+ }
+ },
+
+ /**
+ * Flushes cache.
+ *
+ * @method flush
+ */
+ flush: function() {
+ this.fire("flush");
+ },
+
+ /**
+ * Retrieves cached entry for given request, if available, and refreshes
+ * entry in the cache. Returns null if there is no cache match.
+ *
+ * @method retrieve
+ * @param request {Object} Request object.
+ * @return {Object} Cached entry object with the properties request, response, and payload, or null.
+ */
+ retrieve: function(request) {
+ // If cache is enabled...
+ var entries = this._entries,
+ length = entries.length,
+ entry = null,
+ i = length-1;
+
+ if((this.get("max") > 0) && (length > 0)) {
+ this.fire("request", {request: request});
+
+ // Loop through each cached entry starting from the newest
+ for(; i >= 0; i--) {
+ entry = entries[i];
+
+ // Execute matching function
+ if(this._isMatch(request, entry)) {
+ this.fire("retrieve", {entry: entry});
+
+ // Refresh the position of the cache hit
+ if(i < length-1) {
+ // Remove element from its original location
+ entries.splice(i,1);
+ // Add as newest
+ entries[entries.length] = entry;
+ Y.log("Refreshed cache entry: " + Y.dump(entry) +
+ " for request: " + Y.dump(request), "info", "cache");
+ }
+
+ Y.log("Retrieved cached response: " + Y.dump(entry) +
+ " for request: " + Y.dump(request), "info", "cache");
+ return entry;
+ }
+ }
+ }
+ return null;
+ }
+});
+
+Y.Cache = Cache;
+
+
+
+
+}, '3.0.0' ,{requires:['plugin']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("cache",function(C){var A=C.Lang,B=function(){B.superclass.constructor.apply(this,arguments);};C.mix(B,{NS:"cache",NAME:"cache",ATTRS:{max:{value:0,validator:function(D){return(A.isNumber(D));},setter:function(E){var D=this._entries;if(E>0){if(D){while(D.length>E){D.shift();}}}else{this._entries=[];}return E;}},size:{readOnly:true,getter:function(){return this._entries.length;}},uniqueKeys:{value:false,validator:function(D){return(A.isBoolean(D));}},entries:{readOnly:true,getter:function(){return this._entries;}}}});C.extend(B,C.Plugin.Base,{_entries:null,initializer:function(D){this.publish("add",{defaultFn:this._defAddFn});this.publish("flush",{defaultFn:this._defFlushFn});this._entries=[];},destructor:function(){this._entries=null;},_defAddFn:function(G){var E=this._entries,D=this.get("max"),F=G.entry;if(this.get("uniqueKeys")&&(this.retrieve(G.entry.request))){E.shift();}while(E.length>=D){E.shift();}E[E.length]=F;},_defFlushFn:function(D){this._entries=[];},_isMatch:function(E,D){return(E===D.request);},add:function(E,D,F){if(this.get("entries")&&(this.get("max")>0)&&(A.isValue(E)||A.isNull(E)||A.isUndefined(E))){this.fire("add",{entry:{request:E,response:D,payload:F}});}else{}},flush:function(){this.fire("flush");},retrieve:function(H){var D=this._entries,G=D.length,F=null,E=G-1;if((this.get("max")>0)&&(G>0)){this.fire("request",{request:H});for(;E>=0;E--){F=D[E];if(this._isMatch(H,F)){this.fire("retrieve",{entry:F});if(E<G-1){D.splice(E,1);D[D.length]=F;}return F;}}}return null;}});C.Cache=B;},"3.0.0",{requires:["plugin"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('cache', function(Y) {
+
+/**
+ * The Cache utility provides a common configurable interface for components to
+ * cache and retrieve data from a local JavaScript struct.
+ *
+ * @module cache
+ */
+var LANG = Y.Lang,
+
+/**
+ * Base class for the YUI Cache utility.
+ * @class Cache
+ * @extends Plugin.Base
+ * @constructor
+ */
+Cache = function() {
+ Cache.superclass.constructor.apply(this, arguments);
+};
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(Cache, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NS: "cache",
+
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NAME: "cache",
+
+
+ ATTRS: {
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * @attribute max
+ * @description Maximum number of entries the Cache can hold.
+ * Set to 0 to turn off caching.
+ * @type Number
+ * @default 0
+ */
+ max: {
+ value: 0,
+ validator: function(value) {
+ return (LANG.isNumber(value));
+ },
+ setter: function(value) {
+ // If the cache is full, make room by removing stalest element (index=0)
+ var entries = this._entries;
+ if(value > 0) {
+ if(entries) {
+ while(entries.length > value) {
+ entries.shift();
+ }
+ }
+ }
+ else {
+ this._entries = [];
+ }
+ return value;
+ }
+ },
+
+ /**
+ * @attribute size
+ * @description Number of entries currently cached.
+ * @type Number
+ */
+ size: {
+ readOnly: true,
+ getter: function() {
+ return this._entries.length;
+ }
+ },
+
+ /**
+ * @attribute uniqueKeys
+ * @description Validate uniqueness of stored keys. Default is false and
+ * is more performant.
+ * @type Number
+ */
+ uniqueKeys: {
+ value: false,
+ validator: function(value) {
+ return (LANG.isBoolean(value));
+ }
+ },
+
+ /**
+ * @attribute entries
+ * @description Cached entries.
+ * @type Array
+ */
+ entries: {
+ readOnly: true,
+ getter: function() {
+ return this._entries;
+ }
+ }
+ }
+});
+
+Y.extend(Cache, Y.Plugin.Base, {
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache private properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Array of request/response objects indexed chronologically.
+ *
+ * @property _entries
+ * @type Object[]
+ * @private
+ */
+ _entries: null,
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache private methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * @method initializer
+ * @description Internal init() handler.
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+
+ /**
+ * @event add
+ * @description Fired when an entry is added.
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
+ * </dl>
+ * @preventable _defAddFn
+ */
+ this.publish("add", {defaultFn: this._defAddFn});
+
+ /**
+ * @event flush
+ * @description Fired when the cache is flushed.
+ * @param e {Event.Facade} Event Facade object.
+ * @preventable _defFlushFn
+ */
+ this.publish("flush", {defaultFn: this._defFlushFn});
+
+ /**
+ * @event request
+ * @description Fired when an entry is requested from the cache.
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>request (Object)</dt> <dd>The request object.</dd>
+ * </dl>
+ */
+
+ /**
+ * @event retrieve
+ * @description Fired when an entry is retrieved from the cache.
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>entry (Object)</dt> <dd>The retrieved entry.</dd>
+ * </dl>
+ */
+
+ // Initialize internal values
+ this._entries = [];
+ },
+
+ /**
+ * @method destructor
+ * @description Internal destroy() handler.
+ * @private
+ */
+ destructor: function() {
+ this._entries = null;
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache protected methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Adds entry to cache.
+ *
+ * @method _defAddFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>entry (Object)</dt> <dd>The cached entry.</dd>
+ * </dl>
+ * @protected
+ */
+ _defAddFn: function(e) {
+ var entries = this._entries,
+ max = this.get("max"),
+ entry = e.entry;
+
+ if(this.get("uniqueKeys") && (this.retrieve(e.entry.request))) {
+ entries.shift();
+ }
+
+
+ // If the cache at or over capacity, make room by removing stalest element (index=0)
+ while(entries.length>=max) {
+ entries.shift();
+ }
+
+ // Add entry to cache in the newest position, at the end of the array
+ entries[entries.length] = entry;
+ },
+
+ /**
+ * Flushes cache.
+ *
+ * @method _defFlushFn
+ * @param e {Event.Facade} Event Facade object.
+ * @protected
+ */
+ _defFlushFn: function(e) {
+ this._entries = [];
+ },
+
+ /**
+ * Default overridable method compares current request with given cache entry.
+ * Returns true if current request matches the cached request, otherwise
+ * false. Implementers should override this method to customize the
+ * cache-matching algorithm.
+ *
+ * @method _isMatch
+ * @param request {Object} Request object.
+ * @param entry {Object} Cached entry.
+ * @return {Boolean} True if current request matches given cached request, false otherwise.
+ * @protected
+ */
+ _isMatch: function(request, entry) {
+ return (request === entry.request);
+ },
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // Cache public methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Adds a new entry to the cache of the format
+ * {request:request, response:response, payload:payload}.
+ * If cache is full, evicts the stalest entry before adding the new one.
+ *
+ * @method add
+ * @param request {Object} Request value.
+ * @param response {Object} Response value.
+ * @param payload {Object} (optional) Arbitrary data payload.
+ */
+ add: function(request, response, payload) {
+ if(this.get("entries") && (this.get("max")>0) &&
+ (LANG.isValue(request) || LANG.isNull(request) || LANG.isUndefined(request))) {
+ this.fire("add", {entry: {request:request, response:response, payload:payload}});
+ }
+ else {
+ }
+ },
+
+ /**
+ * Flushes cache.
+ *
+ * @method flush
+ */
+ flush: function() {
+ this.fire("flush");
+ },
+
+ /**
+ * Retrieves cached entry for given request, if available, and refreshes
+ * entry in the cache. Returns null if there is no cache match.
+ *
+ * @method retrieve
+ * @param request {Object} Request object.
+ * @return {Object} Cached entry object with the properties request, response, and payload, or null.
+ */
+ retrieve: function(request) {
+ // If cache is enabled...
+ var entries = this._entries,
+ length = entries.length,
+ entry = null,
+ i = length-1;
+
+ if((this.get("max") > 0) && (length > 0)) {
+ this.fire("request", {request: request});
+
+ // Loop through each cached entry starting from the newest
+ for(; i >= 0; i--) {
+ entry = entries[i];
+
+ // Execute matching function
+ if(this._isMatch(request, entry)) {
+ this.fire("retrieve", {entry: entry});
+
+ // Refresh the position of the cache hit
+ if(i < length-1) {
+ // Remove element from its original location
+ entries.splice(i,1);
+ // Add as newest
+ entries[entries.length] = entry;
+ }
+
+ return entry;
+ }
+ }
+ }
+ return null;
+ }
+});
+
+Y.Cache = Cache;
+
+
+
+
+}, '3.0.0' ,{requires:['plugin']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('classnamemanager', function(Y) {
+
+/**
+* Contains a singleton (ClassNameManager) that enables easy creation and caching of
+* prefixed class names.
+* @module classnamemanager
+*/
+
+/**
+ * A singleton class providing:
+ *
+ * <ul>
+ * <li>Easy creation of prefixed class names</li>
+ * <li>Caching of previously created class names for improved performance.</li>
+ * </ul>
+ *
+ * @class ClassNameManager
+ * @static
+ */
+
+// String constants
+var CLASS_NAME_PREFIX = 'classNamePrefix',
+ CLASS_NAME_DELIMITER = 'classNameDelimiter',
+ CONFIG = Y.config;
+
+// Global config
+
+/**
+ * Configuration property indicating the prefix for all CSS class names in this YUI instance.
+ *
+ * @property Y.config.classNamePrefix
+ * @type {String}
+ * @default "yui"
+ * @static
+ */
+CONFIG[CLASS_NAME_PREFIX] = CONFIG[CLASS_NAME_PREFIX] || 'yui';
+
+/**
+ * Configuration property indicating the delimiter used to compose all CSS class names in
+ * this YUI instance.
+ *
+ * @property Y.config.classNameDelimiter
+ * @type {String}
+ * @default "-"
+ * @static
+ */
+CONFIG[CLASS_NAME_DELIMITER] = CONFIG[CLASS_NAME_DELIMITER] || '-';
+
+Y.ClassNameManager = function () {
+
+ var sPrefix = CONFIG[CLASS_NAME_PREFIX],
+ sDelimiter = CONFIG[CLASS_NAME_DELIMITER];
+
+ return {
+
+ /**
+ * Returns a class name prefixed with the the value of the
+ * <code>Y.config.classNamePrefix</code> attribute + the provided strings.
+ * Uses the <code>Y.config.classNameDelimiter</code> attribute to delimit the
+ * provided strings. E.g. Y.ClassNameManager.getClassName('foo','bar'); // yui-foo-bar
+ *
+ * @method getClassName
+ * @param {String}+ one or more classname bits to be joined and prefixed
+ */
+ getClassName: Y.cached(function (c, x) {
+
+ var sClass = sPrefix + sDelimiter +
+ // ((x) ? Y.Array(arguments, 0, true).join(sDelimiter) : c);
+ ((x) ? Array.prototype.join.call(arguments, sDelimiter) : c);
+
+ return sClass.replace(/\s/g, '');
+
+ })
+
+ };
+
+}();
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("classnamemanager",function(C){var B="classNamePrefix",D="classNameDelimiter",A=C.config;A[B]=A[B]||"yui";A[D]=A[D]||"-";C.ClassNameManager=function(){var E=A[B],F=A[D];return{getClassName:C.cached(function(I,G){var H=E+F+((G)?Array.prototype.join.call(arguments,F):I);return H.replace(/\s/g,"");})};}();},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('classnamemanager', function(Y) {
+
+/**
+* Contains a singleton (ClassNameManager) that enables easy creation and caching of
+* prefixed class names.
+* @module classnamemanager
+*/
+
+/**
+ * A singleton class providing:
+ *
+ * <ul>
+ * <li>Easy creation of prefixed class names</li>
+ * <li>Caching of previously created class names for improved performance.</li>
+ * </ul>
+ *
+ * @class ClassNameManager
+ * @static
+ */
+
+// String constants
+var CLASS_NAME_PREFIX = 'classNamePrefix',
+ CLASS_NAME_DELIMITER = 'classNameDelimiter',
+ CONFIG = Y.config;
+
+// Global config
+
+/**
+ * Configuration property indicating the prefix for all CSS class names in this YUI instance.
+ *
+ * @property Y.config.classNamePrefix
+ * @type {String}
+ * @default "yui"
+ * @static
+ */
+CONFIG[CLASS_NAME_PREFIX] = CONFIG[CLASS_NAME_PREFIX] || 'yui';
+
+/**
+ * Configuration property indicating the delimiter used to compose all CSS class names in
+ * this YUI instance.
+ *
+ * @property Y.config.classNameDelimiter
+ * @type {String}
+ * @default "-"
+ * @static
+ */
+CONFIG[CLASS_NAME_DELIMITER] = CONFIG[CLASS_NAME_DELIMITER] || '-';
+
+Y.ClassNameManager = function () {
+
+ var sPrefix = CONFIG[CLASS_NAME_PREFIX],
+ sDelimiter = CONFIG[CLASS_NAME_DELIMITER];
+
+ return {
+
+ /**
+ * Returns a class name prefixed with the the value of the
+ * <code>Y.config.classNamePrefix</code> attribute + the provided strings.
+ * Uses the <code>Y.config.classNameDelimiter</code> attribute to delimit the
+ * provided strings. E.g. Y.ClassNameManager.getClassName('foo','bar'); // yui-foo-bar
+ *
+ * @method getClassName
+ * @param {String}+ one or more classname bits to be joined and prefixed
+ */
+ getClassName: Y.cached(function (c, x) {
+
+ var sClass = sPrefix + sDelimiter +
+ // ((x) ? Y.Array(arguments, 0, true).join(sDelimiter) : c);
+ ((x) ? Array.prototype.join.call(arguments, sDelimiter) : c);
+
+ return sClass.replace(/\s/g, '');
+
+ })
+
+ };
+
+}();
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('collection', function(Y) {
+
+/**
+ * Collection utilities beyond what is provided in the YUI core
+ * @module collection
+ */
+
+var L = Y.Lang, Native = Array.prototype, A = Y.Array;
+
+/**
+ * Adds the following array utilities to the YUI instance
+ * (Y.Array). This is in addition to the methods provided
+ * in the core.
+ * @class YUI~array~extras
+ */
+
+/**
+ * Returns the index of the last item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * method Array.lastIndexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of hte item that contains the value or -1
+ */
+A.lastIndexOf = (Native.lastIndexOf) ?
+ function(a ,val) {
+ return a.lastIndexOf(val);
+ } :
+ function(a, val) {
+ for (var i=a.length-1; i>=0; i=i-1) {
+ if (a[i] === val) {
+ break;
+ }
+ }
+ return i;
+ };
+
+/**
+ * Returns a copy of the array with the duplicate entries removed
+ * @method Array.unique
+ * @static
+ * @param a {Array} the array to find the subset of uniques for
+ * @param sort {bool} flag to denote if the array is sorted or not. Defaults to false, the more general operation
+ * @return {Array} a copy of the array with duplicate entries removed
+ */
+A.unique = function(a, sort) {
+ var b = a.slice(), i = 0, n = -1, item = null;
+
+ while (i < b.length) {
+ item = b[i];
+ while ((n = b.lastIndexOf(item)) !== i) {
+ b.splice(n, 1);
+ }
+ i += 1;
+ }
+
+ // Note: the sort option doesn't really belong here... I think it was added
+ // because there was a way to fast path the two operations together. That
+ // implementation was not working, so I replaced it with the following.
+ // Leaving it in so that the API doesn't get broken.
+ if (sort) {
+ if (L.isNumber(b[0])) {
+ b.sort(A.numericSort);
+ } else {
+ b.sort();
+ }
+ }
+
+ return b;
+};
+
+/**
+* Executes the supplied function on each item in the array.
+* Returns a new array containing the items that the supplied
+* function returned true for.
+* @method Array.filter
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {Array} The items on which the supplied function
+* returned true. If no items matched an empty array is
+* returned.
+*/
+A.filter = (Native.filter) ?
+ function(a, f, o) {
+ return Native.filter.call(a, f, o);
+ } :
+ function(a, f, o) {
+ var results = [];
+ A.each(a, function(item, i, a) {
+ if (f.call(o, item, i, a)) {
+ results.push(item);
+ }
+ });
+
+ return results;
+ };
+
+/**
+* The inverse of filter. Executes the supplied function on each item.
+* Returns a new array containing the items that the supplied
+* function returned *false* for.
+* @method Array.reject
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {Array} The items on which the supplied function
+* returned false.
+*/
+A.reject = function(a, f, o) {
+ return A.filter(a, function(item, i, a) {
+ return !f.call(o, item, i, a);
+ });
+};
+
+/**
+* Executes the supplied function on each item in the array.
+* @method Array.every
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {boolean} true if every item in the array returns true
+* from the supplied function.
+*/
+A.every = (Native.every) ?
+ function(a, f, o) {
+ return Native.every.call(a,f,o);
+ } :
+ function(a, f, o) {
+ var l = a.length;
+ for (var i = 0; i < l; i=i+1) {
+ if (!f.call(o, a[i], i, a)) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+/**
+* Executes the supplied function on each item in the array.
+* @method Array.map
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {Array} A new array containing the return value
+* of the supplied function for each item in the original
+* array.
+*/
+A.map = (Native.map) ?
+ function(a, f, o) {
+ return Native.map.call(a, f, o);
+ } :
+ function(a, f, o) {
+ var results = [];
+ A.each(a, function(item, i, a) {
+ results.push(f.call(o, item, i, a));
+ });
+ return results;
+ };
+
+
+/**
+* Executes the supplied function on each item in the array.
+* Reduce "folds" the array into a single value.
+* @method Array.reduce
+* @param a {Array} the array to iterate
+* @param init The initial value to start from
+* @param f {Function} the function to execute on each item. It
+* is responsible for returning the updated value of the
+* computation.
+* @param o Optional context object
+* @static
+* @return A value that results from iteratively applying the
+* supplied function to each element in the array.
+*/
+A.reduce = (Native.reduce) ?
+ function(a, init, f, o) {
+ //Firefox's Array.reduce does not allow inclusion of a
+ // thisObject, so we need to implement it manually
+ return Native.reduce.call(a, function(init, item, i, a) {
+ return f.call(o, init, item, i, a);
+ }, init);
+ } :
+ function(a, init, f, o) {
+ var r = init;
+ A.each(a, function (item, i, a) {
+ r = f.call(o, r, item, i, a);
+ });
+ return r;
+ };
+
+
+/**
+* Executes the supplied function on each item in the array,
+* searching for the first item that matches the supplied
+* function.
+* @method Array.find
+* @param a {Array} the array to search
+* @param f {Function} the function to execute on each item.
+* Iteration is stopped as soon as this function returns true
+* on an item.
+* @param o Optional context object
+* @static
+* @return {object} the first item that the supplied function
+* returns true for, or null if it never returns true
+*/
+A.find = function(a, f, o) {
+ var l = a.length;
+ for(var i=0; i < l; i++) {
+ if (f.call(o, a[i], i, a)) {
+ return a[i];
+ }
+ }
+ return null;
+};
+
+/**
+* Iterates over an array, returning a new array of all the elements
+* that match the supplied regular expression
+* @method Array.grep
+* @param a {Array} a collection to iterate over
+* @param pattern {RegExp} The regular expression to test against
+* each item
+* @static
+* @return {Array} All the items in the collection that
+* produce a match against the supplied regular expression.
+* If no items match, an empty array is returned.
+*/
+A.grep = function (a, pattern) {
+ return A.filter(a, function (item, index) {
+ return pattern.test(item);
+ });
+};
+
+
+/**
+* Partitions an array into two new arrays, one with the items
+* that match the supplied function, and one with the items that
+* do not.
+* @method Array.partition
+* @param a {Array} a collection to iterate over
+* @paran f {Function} a function that will receive each item
+* in the collection and its index.
+* @param o Optional execution context of f.
+* @static
+* @return An object with two members, 'matches' and 'rejects',
+* that are arrays containing the items that were selected or
+* rejected by the test function (or an empty array).
+*/
+A.partition = function (a, f, o) {
+ var results = {matches: [], rejects: []};
+ A.each(a, function (item, index) {
+ var set = f.call(o, item, index, a) ? results.matches : results.rejects;
+ set.push(item);
+ });
+ return results;
+};
+
+/**
+* Creates an array of arrays by pairing the corresponding
+* elements of two arrays together into a new array.
+* @method Array.zip
+* @param a {Array} a collection to iterate over
+* @param a2 {Array} another collection whose members will be
+* paired with members of the first parameter
+* @static
+* @return An array of arrays formed by pairing each element
+* of the first collection with an item in the second collection
+* having the corresponding index.
+*/
+A.zip = function (a, a2) {
+ var results = [];
+ A.each(a, function (item, index) {
+ results.push([item, a2[index]]);
+ });
+ return results;
+};
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("collection",function(E){var C=E.Lang,D=Array.prototype,B=E.Array;B.lastIndexOf=(D.lastIndexOf)?function(A,F){return A.lastIndexOf(F);}:function(A,G){for(var F=A.length-1;F>=0;F=F-1){if(A[F]===G){break;}}return F;};B.unique=function(F,H){var A=F.slice(),G=0,J=-1,I=null;while(G<A.length){I=A[G];while((J=A.lastIndexOf(I))!==G){A.splice(J,1);}G+=1;}if(H){if(C.isNumber(A[0])){A.sort(B.numericSort);}else{A.sort();}}return A;};B.filter=(D.filter)?function(A,F,G){return D.filter.call(A,F,G);}:function(A,G,H){var F=[];B.each(A,function(K,J,I){if(G.call(H,K,J,I)){F.push(K);}});return F;};B.reject=function(A,F,G){return B.filter(A,function(J,I,H){return !F.call(G,J,I,H);});};B.every=(D.every)?function(A,F,G){return D.every.call(A,F,G);}:function(F,H,I){var A=F.length;for(var G=0;G<A;G=G+1){if(!H.call(I,F[G],G,F)){return false;}}return true;};B.map=(D.map)?function(A,F,G){return D.map.call(A,F,G);}:function(A,G,H){var F=[];B.each(A,function(K,J,I){F.push(G.call(H,K,J,I));});return F;};B.reduce=(D.reduce)?function(A,H,F,G){return D.reduce.call(A,function(L,K,J,I){return F.call(G,L,K,J,I);},H);}:function(A,I,G,H){var F=I;B.each(A,function(L,K,J){F=G.call(H,F,L,K,J);});return F;};B.find=function(F,H,I){var A=F.length;for(var G=0;G<A;G++){if(H.call(I,F[G],G,F)){return F[G];}}return null;};B.grep=function(A,F){return B.filter(A,function(H,G){return F.test(H);});};B.partition=function(A,G,H){var F={matches:[],rejects:[]};B.each(A,function(J,I){var K=G.call(H,J,I,A)?F.matches:F.rejects;K.push(J);});return F;};B.zip=function(F,A){var G=[];B.each(F,function(I,H){G.push([I,A[H]]);});return G;};},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('collection', function(Y) {
+
+/**
+ * Collection utilities beyond what is provided in the YUI core
+ * @module collection
+ */
+
+var L = Y.Lang, Native = Array.prototype, A = Y.Array;
+
+/**
+ * Adds the following array utilities to the YUI instance
+ * (Y.Array). This is in addition to the methods provided
+ * in the core.
+ * @class YUI~array~extras
+ */
+
+/**
+ * Returns the index of the last item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * method Array.lastIndexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of hte item that contains the value or -1
+ */
+A.lastIndexOf = (Native.lastIndexOf) ?
+ function(a ,val) {
+ return a.lastIndexOf(val);
+ } :
+ function(a, val) {
+ for (var i=a.length-1; i>=0; i=i-1) {
+ if (a[i] === val) {
+ break;
+ }
+ }
+ return i;
+ };
+
+/**
+ * Returns a copy of the array with the duplicate entries removed
+ * @method Array.unique
+ * @static
+ * @param a {Array} the array to find the subset of uniques for
+ * @param sort {bool} flag to denote if the array is sorted or not. Defaults to false, the more general operation
+ * @return {Array} a copy of the array with duplicate entries removed
+ */
+A.unique = function(a, sort) {
+ var b = a.slice(), i = 0, n = -1, item = null;
+
+ while (i < b.length) {
+ item = b[i];
+ while ((n = b.lastIndexOf(item)) !== i) {
+ b.splice(n, 1);
+ }
+ i += 1;
+ }
+
+ // Note: the sort option doesn't really belong here... I think it was added
+ // because there was a way to fast path the two operations together. That
+ // implementation was not working, so I replaced it with the following.
+ // Leaving it in so that the API doesn't get broken.
+ if (sort) {
+ if (L.isNumber(b[0])) {
+ b.sort(A.numericSort);
+ } else {
+ b.sort();
+ }
+ }
+
+ return b;
+};
+
+/**
+* Executes the supplied function on each item in the array.
+* Returns a new array containing the items that the supplied
+* function returned true for.
+* @method Array.filter
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {Array} The items on which the supplied function
+* returned true. If no items matched an empty array is
+* returned.
+*/
+A.filter = (Native.filter) ?
+ function(a, f, o) {
+ return Native.filter.call(a, f, o);
+ } :
+ function(a, f, o) {
+ var results = [];
+ A.each(a, function(item, i, a) {
+ if (f.call(o, item, i, a)) {
+ results.push(item);
+ }
+ });
+
+ return results;
+ };
+
+/**
+* The inverse of filter. Executes the supplied function on each item.
+* Returns a new array containing the items that the supplied
+* function returned *false* for.
+* @method Array.reject
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {Array} The items on which the supplied function
+* returned false.
+*/
+A.reject = function(a, f, o) {
+ return A.filter(a, function(item, i, a) {
+ return !f.call(o, item, i, a);
+ });
+};
+
+/**
+* Executes the supplied function on each item in the array.
+* @method Array.every
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {boolean} true if every item in the array returns true
+* from the supplied function.
+*/
+A.every = (Native.every) ?
+ function(a, f, o) {
+ return Native.every.call(a,f,o);
+ } :
+ function(a, f, o) {
+ var l = a.length;
+ for (var i = 0; i < l; i=i+1) {
+ if (!f.call(o, a[i], i, a)) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+/**
+* Executes the supplied function on each item in the array.
+* @method Array.map
+* @param a {Array} the array to iterate
+* @param f {Function} the function to execute on each item
+* @param o Optional context object
+* @static
+* @return {Array} A new array containing the return value
+* of the supplied function for each item in the original
+* array.
+*/
+A.map = (Native.map) ?
+ function(a, f, o) {
+ return Native.map.call(a, f, o);
+ } :
+ function(a, f, o) {
+ var results = [];
+ A.each(a, function(item, i, a) {
+ results.push(f.call(o, item, i, a));
+ });
+ return results;
+ };
+
+
+/**
+* Executes the supplied function on each item in the array.
+* Reduce "folds" the array into a single value.
+* @method Array.reduce
+* @param a {Array} the array to iterate
+* @param init The initial value to start from
+* @param f {Function} the function to execute on each item. It
+* is responsible for returning the updated value of the
+* computation.
+* @param o Optional context object
+* @static
+* @return A value that results from iteratively applying the
+* supplied function to each element in the array.
+*/
+A.reduce = (Native.reduce) ?
+ function(a, init, f, o) {
+ //Firefox's Array.reduce does not allow inclusion of a
+ // thisObject, so we need to implement it manually
+ return Native.reduce.call(a, function(init, item, i, a) {
+ return f.call(o, init, item, i, a);
+ }, init);
+ } :
+ function(a, init, f, o) {
+ var r = init;
+ A.each(a, function (item, i, a) {
+ r = f.call(o, r, item, i, a);
+ });
+ return r;
+ };
+
+
+/**
+* Executes the supplied function on each item in the array,
+* searching for the first item that matches the supplied
+* function.
+* @method Array.find
+* @param a {Array} the array to search
+* @param f {Function} the function to execute on each item.
+* Iteration is stopped as soon as this function returns true
+* on an item.
+* @param o Optional context object
+* @static
+* @return {object} the first item that the supplied function
+* returns true for, or null if it never returns true
+*/
+A.find = function(a, f, o) {
+ var l = a.length;
+ for(var i=0; i < l; i++) {
+ if (f.call(o, a[i], i, a)) {
+ return a[i];
+ }
+ }
+ return null;
+};
+
+/**
+* Iterates over an array, returning a new array of all the elements
+* that match the supplied regular expression
+* @method Array.grep
+* @param a {Array} a collection to iterate over
+* @param pattern {RegExp} The regular expression to test against
+* each item
+* @static
+* @return {Array} All the items in the collection that
+* produce a match against the supplied regular expression.
+* If no items match, an empty array is returned.
+*/
+A.grep = function (a, pattern) {
+ return A.filter(a, function (item, index) {
+ return pattern.test(item);
+ });
+};
+
+
+/**
+* Partitions an array into two new arrays, one with the items
+* that match the supplied function, and one with the items that
+* do not.
+* @method Array.partition
+* @param a {Array} a collection to iterate over
+* @paran f {Function} a function that will receive each item
+* in the collection and its index.
+* @param o Optional execution context of f.
+* @static
+* @return An object with two members, 'matches' and 'rejects',
+* that are arrays containing the items that were selected or
+* rejected by the test function (or an empty array).
+*/
+A.partition = function (a, f, o) {
+ var results = {matches: [], rejects: []};
+ A.each(a, function (item, index) {
+ var set = f.call(o, item, index, a) ? results.matches : results.rejects;
+ set.push(item);
+ });
+ return results;
+};
+
+/**
+* Creates an array of arrays by pairing the corresponding
+* elements of two arrays together into a new array.
+* @method Array.zip
+* @param a {Array} a collection to iterate over
+* @param a2 {Array} another collection whose members will be
+* paired with members of the first parameter
+* @static
+* @return An array of arrays formed by pairing each element
+* of the first collection with an item in the second collection
+* having the corresponding index.
+*/
+A.zip = function (a, a2) {
+ var results = [];
+ A.each(a, function (item, index) {
+ results.push([item, a2[index]]);
+ });
+ return results;
+};
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('compat', function(Y) {
+
+
+var COMPAT_ARG = '~yui|2|compat~';
+
+
+if (window.YAHOO != YUI) {
+
+ // get any existing YAHOO obj props
+ var o = (window.YAHOO) ? YUI.merge(window.YAHOO) : null;
+
+ // Make the YUI global the YAHOO global
+ window.YAHOO = YUI;
+
+ // augment old YAHOO props
+ if (o) {
+ Y.mix(Y, o);
+ }
+}
+
+// add old namespaces
+Y.namespace("util", "widget", "example");
+
+// case/location change
+Y.env = (Y.env) ? Y.mix(Y.env, Y.Env) : Y.Env;
+Y.lang = (Y.lang) ? Y.mix(Y.lang, Y.Lang) : Y.Lang;
+Y.env.ua = Y.UA;
+
+// support Y.register
+Y.mix(Y.env, {
+ modules: [],
+ listeners: [],
+ getVersion: function(name) {
+ return this.Env.modules[name] || null;
+ }
+});
+
+var L = Y.lang;
+
+// add old lang properties
+Y.mix(L, {
+
+ augmentObject: function(r, s) {
+ var a = arguments, wl = (a.length > 2) ? Y.Array(a, 2, true) : null;
+ return Y.mix(r, s, (wl), wl);
+ },
+
+ augmentProto: function(r, s) {
+ var a = arguments, wl = (a.length > 2) ? Y.Array(a, 2, true) : null;
+ return Y.mix(r, s, (wl), wl, 1);
+ },
+
+ // extend: Y.bind(Y.extend, Y),
+ extend: Y.extend,
+ // merge: Y.bind(Y.merge, Y)
+ merge: Y.merge
+}, true);
+
+L.augment = L.augmentProto;
+
+L.hasOwnProperty = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+Y.augmentProto = L.augmentProto;
+
+// add register function
+Y.mix(Y, {
+ register: function(name, mainClass, data) {
+ var mods = Y.Env.modules;
+ if (!mods[name]) {
+ mods[name] = { versions:[], builds:[] };
+ }
+ var m=mods[name],v=data.version,b=data.build,ls=Y.Env.listeners;
+ m.name = name;
+ m.version = v;
+ m.build = b;
+ m.versions.push(v);
+ m.builds.push(b);
+ m.mainClass = mainClass;
+ // fire the module load listeners
+ for (var i=0;i<ls.length;i=i+1) {
+ ls[i](m);
+ }
+ // label the main class
+ if (mainClass) {
+ mainClass.VERSION = v;
+ mainClass.BUILD = b;
+ } else {
+ Y.log("mainClass is undefined for module " + name, "warn");
+ }
+ }
+});
+
+// add old load listeners
+if ("undefined" !== typeof YAHOO_config) {
+ var l=YAHOO_config.listener,ls=Y.Env.listeners,unique=true,i;
+ if (l) {
+ // if YAHOO is loaded multiple times we need to check to see if
+ // this is a new config object. If it is, add the new component
+ // load listener to the stack
+ for (i=0;i<ls.length;i=i+1) {
+ if (ls[i]==l) {
+ unique=false;
+ break;
+ }
+ }
+ if (unique) {
+ ls.push(l);
+ }
+ }
+}
+
+// add old registration for yahoo
+Y.register("yahoo", Y, {version: "3.0.0", build: "1549"});
+
+if (Y.Event) {
+
+ var o = {
+
+ /**
+ * Safari detection
+ * @property isSafari
+ * @private
+ * @static
+ * @deprecated use Y.Env.UA.webkit
+ */
+ isSafari: Y.UA.webkit,
+
+ /**
+ * webkit version
+ * @property webkit
+ * @type string
+ * @private
+ * @static
+ * @deprecated use Y.Env.UA.webkit
+ */
+ webkit: Y.UA.webkit,
+
+ /**
+ * Normalized keycodes for webkit/safari
+ * @property webkitKeymap
+ * @type {int: int}
+ * @private
+ * @static
+ * @final
+ */
+ webkitKeymap: {
+ 63232: 38, // up
+ 63233: 40, // down
+ 63234: 37, // left
+ 63235: 39, // right
+ 63276: 33, // page up
+ 63277: 34, // page down
+ 25: 9 // SHIFT-TAB (Safari provides a different key code in
+ // this case, even though the shiftKey modifier is set)
+ },
+
+ /**
+ * IE detection
+ * @property isIE
+ * @private
+ * @static
+ * @deprecated use Y.Env.UA.ie
+ */
+ isIE: Y.UA.ie,
+
+ /**
+ * Returns scrollLeft
+ * @method _getScrollLeft
+ * @static
+ * @private
+ */
+ _getScrollLeft: function() {
+ return this._getScroll()[1];
+ },
+
+ /**
+ * Returns scrollTop
+ * @method _getScrollTop
+ * @static
+ * @private
+ */
+ _getScrollTop: function() {
+ return this._getScroll()[0];
+ },
+
+ /**
+ * Returns the scrollTop and scrollLeft. Used to calculate the
+ * pageX and pageY in Internet Explorer
+ * @method _getScroll
+ * @static
+ * @private
+ */
+ _getScroll: function() {
+ var d = Y.config.doc, dd = d.documentElement, db = d.body;
+ if (dd && (dd.scrollTop || dd.scrollLeft)) {
+ return [dd.scrollTop, dd.scrollLeft];
+ } else if (db) {
+ return [db.scrollTop, db.scrollLeft];
+ } else {
+ return [0, 0];
+ }
+ },
+
+ /**
+ * Returns the event's pageX
+ * @method getPageX
+ * @param {Event} ev the event
+ * @return {int} the event's pageX
+ * @static
+ */
+ getPageX: function(ev) {
+ var x = ev.pageX;
+ if (!x && 0 !== x) {
+ x = ev.clientX || 0;
+
+ if ( Y.UA.ie ) {
+ x += this._getScrollLeft();
+ }
+ }
+
+ return x;
+ },
+
+ /**
+ * Returns the charcode for an event
+ * @method getCharCode
+ * @param {Event} ev the event
+ * @return {int} the event's charCode
+ * @static
+ */
+ getCharCode: function(ev) {
+ var code = ev.keyCode || ev.charCode || 0;
+
+ // webkit normalization
+ if (Y.UA.webkit && (code in Y.Event.webkitKeymap)) {
+ code = Y.Event.webkitKeymap[code];
+ }
+ return code;
+ },
+
+ /**
+ * Returns the event's pageY
+ * @method getPageY
+ * @param {Event} ev the event
+ * @return {int} the event's pageY
+ * @static
+ */
+ getPageY: function(ev) {
+ var y = ev.pageY;
+ if (!y && 0 !== y) {
+ y = ev.clientY || 0;
+
+ if ( Y.UA.ie ) {
+ y += this._getScrollTop();
+ }
+ }
+
+
+ return y;
+ },
+
+ /**
+ * Returns the pageX and pageY properties as an indexed array.
+ * @method getXY
+ * @param {Event} ev the event
+ * @return {[x, y]} the pageX and pageY properties of the event
+ * @static
+ */
+ getXY: function(ev) {
+ return [this.getPageX(ev), this.getPageY(ev)];
+ },
+
+ /**
+ * Returns the event's related target
+ * @method getRelatedTarget
+ * @param {Event} ev the event
+ * @return {HTMLElement} the event's relatedTarget
+ * @static
+ */
+ getRelatedTarget: function(ev) {
+ var t = ev.relatedTarget;
+ if (!t) {
+ if (ev.type == "mouseout") {
+ t = ev.toElement;
+ } else if (ev.type == "mouseover") {
+ t = ev.fromElement;
+ }
+ }
+
+ return this.resolveTextNode(t);
+ },
+
+ /**
+ * Returns the time of the event. If the time is not included, the
+ * event is modified using the current time.
+ * @method getTime
+ * @param {Event} ev the event
+ * @return {Date} the time of the event
+ * @static
+ */
+ getTime: function(ev) {
+ if (!ev.time) {
+ var t = new Date().getTime();
+ try {
+ ev.time = t;
+ } catch(ex) {
+ this.lastError = ex;
+ return t;
+ }
+ }
+
+ return ev.time;
+ },
+
+ /**
+ * Convenience method for stopPropagation + preventDefault
+ * @method stopEvent
+ * @param {Event} ev the event
+ * @static
+ */
+ stopEvent: function(ev) {
+ this.stopPropagation(ev);
+ this.preventDefault(ev);
+ },
+
+ /**
+ * Stops event propagation
+ * @method stopPropagation
+ * @param {Event} ev the event
+ * @static
+ */
+ stopPropagation: function(ev) {
+ if (ev.stopPropagation) {
+ ev.stopPropagation();
+ } else {
+ ev.cancelBubble = true;
+ }
+ },
+
+ /**
+ * Prevents the default behavior of the event
+ * @method preventDefault
+ * @param {Event} ev the event
+ * @static
+ */
+ preventDefault: function(ev) {
+ if (ev.preventDefault) {
+ ev.preventDefault();
+ } else {
+ ev.returnValue = false;
+ }
+ },
+
+ /**
+ * Returns the event's target element. Safari sometimes provides
+ * a text node, and this is automatically resolved to the text
+ * node's parent so that it behaves like other browsers.
+ * @method getTarget
+ * @param {Event} ev the event
+ * @param {boolean} resolveTextNode when set to true the target's
+ * parent will be returned if the target is a
+ * text node. @deprecated, the text node is
+ * now resolved automatically
+ * @return {HTMLElement} the event's target
+ * @static
+ */
+ getTarget: function(ev, resolveTextNode) {
+ var t = ev.target || ev.srcElement;
+ return this.resolveTextNode(t);
+ },
+
+ /**
+ * In some cases, some browsers will return a text node inside
+ * the actual element that was targeted. This normalizes the
+ * return value for getTarget and getRelatedTarget.
+ * @method resolveTextNode
+ * @param {HTMLElement} node node to resolve
+ * @return {HTMLElement} the normized node
+ * @static
+ */
+ resolveTextNode: function(node) {
+ if (node && 3 == node.nodeType) {
+ return node.parentNode;
+ } else {
+ return node;
+ }
+ },
+
+ /**
+ * We cache elements bound by id because when the unload event
+ * fires, we can no longer use document.getElementById
+ * @method getEl
+ * @static
+ * @private
+ * @deprecated Elements are not cached any longer
+ */
+ getEl: function(id) {
+ return Y.get(id);
+ }
+ };
+
+ Y.mix(Y.Event, o);
+
+ /**
+ * Calls Y.Event.attach with the correct argument order
+ * @method removeListener
+ */
+ Y.Event.removeListener = function(el, type, fn, data, override) {
+
+ var context, a=[type, fn, el];
+
+ if (data) {
+
+ if (override) {
+ context = (override === true) ? data : override;
+ }
+
+ a.push(context);
+ a.push(data);
+ }
+
+ a.push(COMPAT_ARG);
+
+ return Y.Event.detach.apply(Y.Event, a);
+ };
+
+ /**
+ * Calls Y.Event.detach with the correct argument order
+ * @method addListener
+ */
+ Y.Event.addListener = function(el, type, fn, data, override) {
+
+ // Y.log('addListener:');
+ // Y.log(Y.Array(arguments, 0, true), 1);
+
+ // var a = Y.Array(arguments, 0, true), el = a.shift();
+ // a.splice(2, 0, el);
+ // return Y.Event.attach.apply(Y.Event, a);
+ var context, a=[type, fn, el];
+
+ if (data) {
+
+ if (override) {
+ context = (override === true) ? data : override;
+ }
+
+ a.push(context);
+ a.push(data);
+ }
+
+ a.push(COMPAT_ARG);
+
+ return Y.Event.attach.apply(Y.Event, a);
+ };
+
+ Y.Event.on = Y.Event.addListener;
+
+ var newOnavail = Y.Event.onAvailable;
+
+ Y.Event.onAvailable = function(id, fn, p_obj, p_override) {
+ return newOnavail(id, fn, p_obj, p_override, false, true);
+ };
+
+ Y.Event.onContentReady = function(id, fn, p_obj, p_override) {
+ return newOnavail(id, fn, p_obj, p_override, true, true);
+ };
+
+ Y.Event.onDOMReady = function(fn) {
+ var a = Y.Array(arguments, 0, true);
+ a.unshift('event:ready');
+ return Y.on.apply(Y, a);
+ };
+
+ Y.util.Event = Y.Event;
+
+ var CE = function(type, oScope, silent, signature) {
+ //debugger;
+
+ var o = {
+ context: oScope,
+ silent: silent || false
+ // signature: signature || CE.LIST
+ };
+
+ CE.superclass.constructor.call(this, type, o);
+
+ this.signature = signature || CE.LIST;
+ };
+
+ Y.extend(CE, Y.CustomEvent, {
+
+ });
+
+ /**
+ * Subscriber listener sigature constant. The LIST type returns three
+ * parameters: the event type, the array of args passed to fire, and
+ * the optional custom object
+ * @property YAHOO.util.CustomEvent.LIST
+ * @static
+ * @type int
+ */
+ CE.LIST = 0;
+
+ /**
+ * Subscriber listener sigature constant. The FLAT type returns two
+ * parameters: the first argument passed to fire and the optional
+ * custom object
+ * @property YAHOO.util.CustomEvent.FLAT
+ * @static
+ * @type int
+ */
+ CE.FLAT = 1;
+
+ Y.util.CustomEvent = CE;
+
+ var EP = function() {
+ //console.log('Compat CustomEvent constructor executed: ' + this._yuid);
+ if (!this._yuievt) {
+ var sub = this.subscribe;
+ Y.EventTarget.apply(this, arguments);
+ this.subscribe = sub;
+ this.__yuiepinit = function() {};
+ }
+ };
+
+ Y.extend(EP, Y.EventTarget, {
+
+ createEvent: function(type, o) {
+ o = o || {};
+ o.signature = o.signature || CE.FLAT;
+ return this.publish(type, o);
+ },
+
+ subscribe: function(type, fn, obj, override) {
+ var ce = this._yuievt.events[type] || this.createEvent(type),
+ a = Y.Array(arguments);
+
+ if (override && true !== override) {
+ // a[2] = override;
+ // a[1] = obj;
+ }
+
+ Y.EventTarget.prototype.subscribe.apply(this, a);
+ },
+
+ fireEvent: function(type) {
+ return this.fire.apply(this, arguments);
+ },
+
+ hasEvent: function(type) {
+ return this.getEvent(type);
+ }
+ });
+
+ Y.util.EventProvider = EP;
+
+}
+
+
+Y.register("event", Y, {version: "3.0.0", build: "1549"});
+
+
+var propertyCache = {};
+var patterns = {
+ HYPHEN: /(-[a-z])/i, // to normalize get/setStyle
+ ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards,
+ OP_SCROLL:/^(?:inline|table-row)$/i
+};
+
+var hyphenToCamel = function(property) {
+ if ( !patterns.HYPHEN.test(property) ) {
+ return property; // no hyphens
+ }
+
+ if (propertyCache[property]) { // already converted
+ return propertyCache[property];
+ }
+
+ var converted = property;
+
+ while( patterns.HYPHEN.exec(converted) ) {
+ converted = converted.replace(RegExp.$1,
+ RegExp.$1.substr(1).toUpperCase());
+ }
+
+ propertyCache[property] = converted;
+ return converted;
+ //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug
+};
+
+var Dom = {
+ get: function(el) {
+ if (el) {
+ if (el.nodeType || el.item) { // Node, or NodeList
+ return el;
+ }
+
+ if (typeof el === 'string') { // id
+ return document.getElementById(el);
+ }
+
+ if ('length' in el) { // array-like
+ var c = [];
+ for (var i = 0, len = el.length; i < len; ++i) {
+ c[c.length] = Dom.get(el[i]);
+ }
+
+ return c;
+ }
+
+ return el; // some other object, just pass it back
+ }
+
+ return null;
+ },
+
+ isAncestor: function(haystack, needle) {
+ return YUI.DOM.contains(Dom.get(haystack), Dom.get(needle));
+ },
+
+ inDocument: function(el) {
+ return Dom.isAncestor(Y.config.doc.documentElement, el);
+ },
+
+ batch: function(el, method, o, override, args) {
+ el = (el && (el.tagName || el.item)) ? el : Dom.get(el); // skip get() when possible
+
+ if (!el || !method) {
+ return false;
+ }
+ if (args) {
+ args = Y.Array(args);
+ }
+ var scope = (override) ? o : window;
+
+ var apply = function(el) {
+ if (args) {
+ var tmp = slice.call(args);
+ tmp.unshift(el);
+ return method.apply(scope, tmp);
+ } else {
+ return method.call(scope, el, o);
+ }
+ };
+
+ if (el.tagName || el.length === undefined) { // element or not array-like
+ return apply(el);
+ }
+
+ var collection = [];
+
+ for (var i = 0, len = el.length; i < len; ++i) {
+ collection[collection.length] = apply(el[i]);
+ }
+
+ return collection;
+ },
+
+ // 2.x returns false if already present
+ _addClass: function(el, className) {
+ if ( YUI.DOM.hasClass(el, className) ) {
+ return false;
+ }
+
+ YUI.DOM.addClass(el, className);
+ return true;
+ },
+
+ // 2.x returns false if not present
+ _removeClass: function(el, className) {
+ if ( !YUI.DOM.hasClass(el, className) ) {
+ return false;
+ }
+
+ YUI.DOM.removeClass(el, className);
+ return true;
+ },
+
+ // 2.x returns false if no newClass or same as oldClass
+ _replaceClass: function(el, oldClass, newClass) {
+ if (!newClass || oldClass === newClass) {
+ return false;
+ }
+
+ YUI.DOM.replaceClass(el, oldClass, newClass);
+ return true;
+ },
+
+ getElementsByClassName: function(className, tag, root) {
+ tag = tag || '*';
+ root = (root) ? Dom.get(root) : Y.config.doc;
+ var nodes = [];
+ if (root) {
+ nodes = Y.Selector.query(tag + '.' + className, root);
+ }
+ return nodes;
+ },
+
+ getElementsBy: function(method, tag, root) {
+ tag = tag || '*';
+ root = (root) ? Dom.get(root) : null || document;
+
+ var nodes = [];
+ if (root) {
+ nodes = YUI.DOM.byTag(tag, root, method);
+ }
+ return nodes;
+ },
+
+ getViewportWidth: YUI.DOM.winWidth,
+ getViewportHeight: YUI.DOM.winHeight,
+ getDocumentWidth: YUI.DOM.docWidth,
+ getDocumentHeight: YUI.DOM.docHeight,
+ getDocumentScrollTop: YUI.DOM.docScrollY,
+ getDocumentScrollLeft: YUI.DOM.docScrollX,
+ getDocumentHeight: YUI.DOM.docHeight,
+
+ _guid: function(el, prefix) {
+ prefix = prefix || 'yui-gen';
+ Dom._id_counter = Dom._id_counter || 0;
+
+ if (el && el.id) { // do not override existing ID
+ return el.id;
+ }
+
+ var id = prefix + Dom._id_counter++;
+
+ if (el) {
+ el.id = id;
+ }
+
+ return id;
+ },
+
+ _region: function(el) {
+ if ( (el.parentNode === null || el.offsetParent === null ||
+ YUI.DOM.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body) {
+ return false;
+ }
+
+ return YUI.DOM.region(el);
+
+ },
+
+ _ancestorByClass: function(element, className) {
+ return YUI.DOM.ancestor(element, function(el) {
+ return YUI.DOM.hasClass(el, className);
+ });
+ },
+
+ _ancestorByTag: function(element, tag) {
+ tag = tag.toUpperCase();
+ return YUI.DOM.ancestor(element, function(el) {
+ return el.tagName.toUpperCase() === tag;
+ });
+ }
+};
+
+var slice = [].slice;
+
+var wrap = function(fn, name) {
+ Dom[name] = function() {
+ var args = slice.call(arguments);
+ args[0] = Dom.get(args[0]);
+ return fn.apply(Dom, args);
+ };
+};
+
+var wrapped = {
+ getAncestorBy: YUI.DOM.ancestor,
+ getAncestorByClassName: Dom._ancestorByClass,
+ getAncestorByTagName: Dom._ancestorByTag,
+ getPreviousSiblingBy: YUI.DOM.previous,
+ getPreviousSibling: YUI.DOM.previous,
+ getNextSiblingBy: YUI.DOM.next,
+ getNextSibling: YUI.DOM.next,
+ getFirstChildBy: YUI.DOM.firstChild,
+ getFirstChild: YUI.DOM.firstChild,
+ getLastChildBy: YUI.DOM.lastChild,
+ getLastChild: YUI.DOM.lastChild,
+ getChildrenBy: YUI.DOM.children,
+ getChildren: YUI.DOM.children,
+ insertBefore: function(newNode, refNode) {
+ YUI.DOM.insertBefore(Dom.get(newNode), Dom.get(refNode));
+ },
+ insertAfter: function(newNode, refNode) {
+ YUI.DOM.insertAfter(Dom.get(newNode), Dom.get(refNode));
+ }
+};
+
+Y.each(wrapped, wrap);
+
+var batched = {
+ getStyle: YUI.DOM.getStyle,
+ setStyle: YUI.DOM.setStyle,
+ getXY: YUI.DOM.getXY,
+ setXY: YUI.DOM.setXY,
+ getX: YUI.DOM.getX,
+ getY: YUI.DOM.getY,
+ setX: YUI.DOM.setX,
+ setY: YUI.DOM.setY,
+ getRegion: Dom._region,
+ hasClass: YUI.DOM.hasClass,
+ addClass: Dom._addClass,
+ removeClass: Dom._removeClass,
+ replaceClass: Dom._replaceClass,
+ generateId: Dom._guid
+};
+
+Y.each(batched, function(v, n) {
+ Dom[n] = function(el) {
+ var args = slice.call(arguments, 1);
+ return Dom.batch(el, v, null, null, args);
+ };
+});
+
+Y.util.Dom = Dom;
+
+YAHOO.util.Region = function(t, r, b, l) {
+ this.top = t;
+ this[1] = t;
+ this.right = r;
+ this.bottom = b;
+ this.left = l;
+ this[0] = l;
+};
+
+YAHOO.util.Region.prototype.contains = function(region) {
+ return ( region.left >= this.left &&
+ region.right <= this.right &&
+ region.top >= this.top &&
+ region.bottom <= this.bottom );
+
+ // this.logger.debug("does " + this + " contain " + region + " ... " + ret);
+};
+
+YAHOO.util.Region.prototype.getArea = function() {
+ return ( (this.bottom - this.top) * (this.right - this.left) );
+};
+
+YAHOO.util.Region.prototype.intersect = function(region) {
+ var t = Math.max( this.top, region.top );
+ var r = Math.min( this.right, region.right );
+ var b = Math.min( this.bottom, region.bottom );
+ var l = Math.max( this.left, region.left );
+
+ if (b >= t && r >= l) {
+ return new YAHOO.util.Region(t, r, b, l);
+ } else {
+ return null;
+ }
+};
+
+YAHOO.util.Region.prototype.union = function(region) {
+ var t = Math.min( this.top, region.top );
+ var r = Math.max( this.right, region.right );
+ var b = Math.max( this.bottom, region.bottom );
+ var l = Math.min( this.left, region.left );
+
+ return new YAHOO.util.Region(t, r, b, l);
+};
+
+YAHOO.util.Region.prototype.toString = function() {
+ return ( "Region {" +
+ "top: " + this.top +
+ ", right: " + this.right +
+ ", bottom: " + this.bottom +
+ ", left: " + this.left +
+ "}" );
+};
+
+YAHOO.util.Region.getRegion = function(el) {
+ return YUI.DOM.region(el);
+};
+
+YAHOO.util.Point = function(x, y) {
+ if (YAHOO.lang.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc.
+ y = x[1]; // dont blow away x yet
+ x = x[0];
+ }
+
+ this.x = this.right = this.left = this[0] = x;
+ this.y = this.top = this.bottom = this[1] = y;
+};
+
+YAHOO.util.Point.prototype = new YAHOO.util.Region();
+
+
+
+}, '3.0.0' ,{requires:['dom','event']});
+YUI._setup(); YUI.use('dom', 'event', 'compat');
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("compat",function(D){var S="~yui|2|compat~";if(window.YAHOO!=YUI){var M=(window.YAHOO)?YUI.merge(window.YAHOO):null;window.YAHOO=YUI;if(M){D.mix(D,M);}}D.namespace("util","widget","example");D.env=(D.env)?D.mix(D.env,D.Env):D.Env;D.lang=(D.lang)?D.mix(D.lang,D.Lang):D.Lang;D.env.ua=D.UA;D.mix(D.env,{modules:[],listeners:[],getVersion:function(L){return this.Env.modules[L]||null;}});var G=D.lang;D.mix(G,{augmentObject:function(V,U){var L=arguments,W=(L.length>2)?D.Array(L,2,true):null;return D.mix(V,U,(W),W);},augmentProto:function(V,U){var L=arguments,W=(L.length>2)?D.Array(L,2,true):null;return D.mix(V,U,(W),W,1);},extend:D.extend,merge:D.merge},true);G.augment=G.augmentProto;G.hasOwnProperty=function(U,L){return(U.hasOwnProperty(L));};D.augmentProto=G.augmentProto;D.mix(D,{register:function(L,X,W){var c=D.Env.modules;if(!c[L]){c[L]={versions:[],builds:[]};}var U=c[L],a=W.version,Z=W.build,Y=D.Env.listeners;U.name=L;U.version=a;U.build=Z;U.versions.push(a);U.builds.push(Z);U.mainClass=X;for(var V=0;V<Y.length;V=V+1){Y[V](U);}if(X){X.VERSION=a;X.BUILD=Z;}else{}}});if("undefined"!==typeof YAHOO_config){var O=YAHOO_config.listener,E=D.Env.listeners,B=true,Q;if(O){for(Q=0;Q<E.length;Q=Q+1){if(E[Q]==O){B=false;break;}}if(B){E.push(O);}}}D.register("yahoo",D,{version:"3.0.0",build:"1549"});if(D.Event){var M={isSafari:D.UA.webkit,webkit:D.UA.webkit,webkitKeymap:{63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9},isIE:D.UA.ie,_getScrollLeft:function(){return this._getScroll()[1];},_getScrollTop:function(){return this._getScroll()[0];},_getScroll:function(){var V=D.config.doc,L=V.documentElement,U=V.body;if(L&&(L.scrollTop||L.scrollLeft)){return[L.scrollTop,L.scrollLeft];}else{if(U){return[U.scrollTop,U.scrollLeft];}else{return[0,0];}}},getPageX:function(U){var L=U.pageX;if(!L&&0!==L){L=U.clientX||0;if(D.UA.ie){L+=this._getScrollLeft();}}return L;},getCharCode:function(U){var L=U.keyCode||U.charCode||0;if(D.UA.webkit&&(L in D.Event.webkitKeymap)){L=D.Event.webkitKeymap[L];}return L;},getPageY:function(L){var U=L.pageY;if(!U&&0!==U){U=L.clientY||0;if(D.UA.ie){U+=this._getScrollTop();}}return U;},getXY:function(L){return[this.getPageX(L),this.getPageY(L)];},getRelatedTarget:function(U){var L=U.relatedTarget;if(!L){if(U.type=="mouseout"){L=U.toElement;}else{if(U.type=="mouseover"){L=U.fromElement;}}}return this.resolveTextNode(L);},getTime:function(V){if(!V.time){var U=new Date().getTime();try{V.time=U;}catch(L){this.lastError=L;return U;}}return V.time;},stopEvent:function(L){this.stopPropagation(L);this.preventDefault(L);},stopPropagation:function(L){if(L.stopPropagation){L.stopPropagation();}else{L.cancelBubble=true;}},preventDefault:function(L){if(L.preventDefault){L.preventDefault();}else{L.returnValue=false;}},getTarget:function(V,U){var L=V.target||V.srcElement;return this.resolveTextNode(L);},resolveTextNode:function(L){if(L&&3==L.nodeType){return L.parentNode;}else{return L;}},getEl:function(L){return D.get(L);}};D.mix(D.Event,M);D.Event.removeListener=function(Y,X,W,Z,V){var U,L=[X,W,Y];if(Z){if(V){U=(V===true)?Z:V;}L.push(U);L.push(Z);}L.push(S);return D.Event.detach.apply(D.Event,L);};D.Event.addListener=function(Y,X,W,Z,V){var U,L=[X,W,Y];if(Z){if(V){U=(V===true)?Z:V;}L.push(U);L.push(Z);}L.push(S);return D.Event.attach.apply(D.Event,L);};D.Event.on=D.Event.addListener;var T=D.Event.onAvailable;D.Event.onAvailable=function(W,L,V,U){return T(W,L,V,U,false,true);};D.Event.onContentReady=function(W,L,V,U){return T(W,L,V,U,true,true);};D.Event.onDOMReady=function(U){var L=D.Array(arguments,0,true);L.unshift("event:ready");return D.on.apply(D,L);};D.util.Event=D.Event;var F=function(W,U,V,L){var X={context:U,silent:V||false};F.superclass.constructor.call(this,W,X);this.signature=L||F.LIST;};D.extend(F,D.CustomEvent,{});F.LIST=0;F.FLAT=1;D.util.CustomEvent=F;var R=function(){if(!this._yuievt){var L=this.subscribe;D.EventTarget.apply(this,arguments);this.subscribe=L;this.__yuiepinit=function(){};}};D.extend(R,D.EventTarget,{createEvent:function(L,U){U=U||{};U.signature=U.signature||F.FLAT;return this.publish(L,U);},subscribe:function(W,V,Y,U){var X=this._yuievt.events[W]||this.createEvent(W),L=D.Array(arguments);if(U&&true!==U){}D.EventTarget.prototype.subscribe.apply(this,L);},fireEvent:function(L){return this.fire.apply(this,arguments);},hasEvent:function(L){return this.getEvent(L);}});D.util.EventProvider=R;}D.register("event",D,{version:"3.0.0",build:"1549"});var P={};var C={HYPHEN:/(-[a-z])/i,ROOT_TAG:/^body|html$/i,OP_SCROLL:/^(?:inline|table-row)$/i};var I=function(L){if(!C.HYPHEN.test(L)){return L;}if(P[L]){return P[L];}var U=L;while(C.HYPHEN.exec(U)){U=U.replace(RegExp.$1,RegExp.$1.substr(1).toUpperCase());}P[L]=U;return U;};var A={get:function(V){if(V){if(V.nodeType||V.item){return V;}if(typeof V==="string"){return document.getElementById(V);}if("length" in V){var W=[];for(var U=0,L=V.length;U<L;++U){W[W.length]=A.get(V[U]);}return W;}return V;}return null;},isAncestor:function(L,U){return YUI.DOM.contains(A.get(L),A.get(U));},inDocument:function(L){return A.isAncestor(D.config.doc.documentElement,L);},batch:function(W,L,V,U,a){W=(W&&(W.tagName||W.item))?W:A.get(W);if(!W||!L){return false;}if(a){a=D.Array(a);}var c=(U)?V:window;var b=function(e){if(a){var d=N.call(a);d.unshift(e);return L.apply(c,d);}else{return L.call(c,e,V);}};if(W.tagName||W.length===undefined){return b(W);}var Y=[];for(var X=0,Z=W.length;X<Z;++X){Y[Y.length]=b(W[X]);}return Y;},_addClass:function(U,L){if(YUI.DOM.hasClass(U,L)){return false;}YUI.DOM.addClass(U,L);return true;},_removeClass:function(U,L){if(!YUI.DOM.hasClass(U,L)){return false;}YUI.DOM.removeClass(U,L);return true;},_replaceClass:function(U,L,V){if(!V||L===V){return false;}YUI.DOM.replaceClass(U,L,V);return true;},getElementsByClassName:function(W,L,U){L=L||"*";U=(U)?A.get(U):D.config.doc;var V=[];if(U){V=D.Selector.query(L+"."+W,U);}return V;},getElementsBy:function(W,L,U){L=L||"*";U=(U)?A.get(U):null||document;var V=[];
+if(U){V=YUI.DOM.byTag(L,U,W);}return V;},getViewportWidth:YUI.DOM.winWidth,getViewportHeight:YUI.DOM.winHeight,getDocumentWidth:YUI.DOM.docWidth,getDocumentHeight:YUI.DOM.docHeight,getDocumentScrollTop:YUI.DOM.docScrollY,getDocumentScrollLeft:YUI.DOM.docScrollX,getDocumentHeight:YUI.DOM.docHeight,_guid:function(L,U){U=U||"yui-gen";A._id_counter=A._id_counter||0;if(L&&L.id){return L.id;}var V=U+A._id_counter++;if(L){L.id=V;}return V;},_region:function(L){if((L.parentNode===null||L.offsetParent===null||YUI.DOM.getStyle(L,"display")=="none")&&L!=L.ownerDocument.body){return false;}return YUI.DOM.region(L);},_ancestorByClass:function(L,U){return YUI.DOM.ancestor(L,function(V){return YUI.DOM.hasClass(V,U);});},_ancestorByTag:function(U,L){L=L.toUpperCase();return YUI.DOM.ancestor(U,function(V){return V.tagName.toUpperCase()===L;});}};var N=[].slice;var H=function(U,L){A[L]=function(){var V=N.call(arguments);V[0]=A.get(V[0]);return U.apply(A,V);};};var J={getAncestorBy:YUI.DOM.ancestor,getAncestorByClassName:A._ancestorByClass,getAncestorByTagName:A._ancestorByTag,getPreviousSiblingBy:YUI.DOM.previous,getPreviousSibling:YUI.DOM.previous,getNextSiblingBy:YUI.DOM.next,getNextSibling:YUI.DOM.next,getFirstChildBy:YUI.DOM.firstChild,getFirstChild:YUI.DOM.firstChild,getLastChildBy:YUI.DOM.lastChild,getLastChild:YUI.DOM.lastChild,getChildrenBy:YUI.DOM.children,getChildren:YUI.DOM.children,insertBefore:function(U,L){YUI.DOM.insertBefore(A.get(U),A.get(L));},insertAfter:function(U,L){YUI.DOM.insertAfter(A.get(U),A.get(L));}};D.each(J,H);var K={getStyle:YUI.DOM.getStyle,setStyle:YUI.DOM.setStyle,getXY:YUI.DOM.getXY,setXY:YUI.DOM.setXY,getX:YUI.DOM.getX,getY:YUI.DOM.getY,setX:YUI.DOM.setX,setY:YUI.DOM.setY,getRegion:A._region,hasClass:YUI.DOM.hasClass,addClass:A._addClass,removeClass:A._removeClass,replaceClass:A._replaceClass,generateId:A._guid};D.each(K,function(L,U){A[U]=function(W){var V=N.call(arguments,1);return A.batch(W,L,null,null,V);};});D.util.Dom=A;YAHOO.util.Region=function(V,W,L,U){this.top=V;this[1]=V;this.right=W;this.bottom=L;this.left=U;this[0]=U;};YAHOO.util.Region.prototype.contains=function(L){return(L.left>=this.left&&L.right<=this.right&&L.top>=this.top&&L.bottom<=this.bottom);};YAHOO.util.Region.prototype.getArea=function(){return((this.bottom-this.top)*(this.right-this.left));};YAHOO.util.Region.prototype.intersect=function(X){var V=Math.max(this.top,X.top);var W=Math.min(this.right,X.right);var L=Math.min(this.bottom,X.bottom);var U=Math.max(this.left,X.left);if(L>=V&&W>=U){return new YAHOO.util.Region(V,W,L,U);}else{return null;}};YAHOO.util.Region.prototype.union=function(X){var V=Math.min(this.top,X.top);var W=Math.max(this.right,X.right);var L=Math.max(this.bottom,X.bottom);var U=Math.min(this.left,X.left);return new YAHOO.util.Region(V,W,L,U);};YAHOO.util.Region.prototype.toString=function(){return("Region {"+"top: "+this.top+", right: "+this.right+", bottom: "+this.bottom+", left: "+this.left+"}");};YAHOO.util.Region.getRegion=function(L){return YUI.DOM.region(L);};YAHOO.util.Point=function(L,U){if(YAHOO.lang.isArray(L)){U=L[1];L=L[0];}this.x=this.right=this.left=this[0]=L;this.y=this.top=this.bottom=this[1]=U;};YAHOO.util.Point.prototype=new YAHOO.util.Region();},"3.0.0",{requires:["dom","event"]});YUI._setup();YUI.use("dom","event","compat");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('compat', function(Y) {
+
+
+var COMPAT_ARG = '~yui|2|compat~';
+
+
+if (window.YAHOO != YUI) {
+
+ // get any existing YAHOO obj props
+ var o = (window.YAHOO) ? YUI.merge(window.YAHOO) : null;
+
+ // Make the YUI global the YAHOO global
+ window.YAHOO = YUI;
+
+ // augment old YAHOO props
+ if (o) {
+ Y.mix(Y, o);
+ }
+}
+
+// add old namespaces
+Y.namespace("util", "widget", "example");
+
+// case/location change
+Y.env = (Y.env) ? Y.mix(Y.env, Y.Env) : Y.Env;
+Y.lang = (Y.lang) ? Y.mix(Y.lang, Y.Lang) : Y.Lang;
+Y.env.ua = Y.UA;
+
+// support Y.register
+Y.mix(Y.env, {
+ modules: [],
+ listeners: [],
+ getVersion: function(name) {
+ return this.Env.modules[name] || null;
+ }
+});
+
+var L = Y.lang;
+
+// add old lang properties
+Y.mix(L, {
+
+ augmentObject: function(r, s) {
+ var a = arguments, wl = (a.length > 2) ? Y.Array(a, 2, true) : null;
+ return Y.mix(r, s, (wl), wl);
+ },
+
+ augmentProto: function(r, s) {
+ var a = arguments, wl = (a.length > 2) ? Y.Array(a, 2, true) : null;
+ return Y.mix(r, s, (wl), wl, 1);
+ },
+
+ // extend: Y.bind(Y.extend, Y),
+ extend: Y.extend,
+ // merge: Y.bind(Y.merge, Y)
+ merge: Y.merge
+}, true);
+
+L.augment = L.augmentProto;
+
+L.hasOwnProperty = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+Y.augmentProto = L.augmentProto;
+
+// add register function
+Y.mix(Y, {
+ register: function(name, mainClass, data) {
+ var mods = Y.Env.modules;
+ if (!mods[name]) {
+ mods[name] = { versions:[], builds:[] };
+ }
+ var m=mods[name],v=data.version,b=data.build,ls=Y.Env.listeners;
+ m.name = name;
+ m.version = v;
+ m.build = b;
+ m.versions.push(v);
+ m.builds.push(b);
+ m.mainClass = mainClass;
+ // fire the module load listeners
+ for (var i=0;i<ls.length;i=i+1) {
+ ls[i](m);
+ }
+ // label the main class
+ if (mainClass) {
+ mainClass.VERSION = v;
+ mainClass.BUILD = b;
+ } else {
+ }
+ }
+});
+
+// add old load listeners
+if ("undefined" !== typeof YAHOO_config) {
+ var l=YAHOO_config.listener,ls=Y.Env.listeners,unique=true,i;
+ if (l) {
+ // if YAHOO is loaded multiple times we need to check to see if
+ // this is a new config object. If it is, add the new component
+ // load listener to the stack
+ for (i=0;i<ls.length;i=i+1) {
+ if (ls[i]==l) {
+ unique=false;
+ break;
+ }
+ }
+ if (unique) {
+ ls.push(l);
+ }
+ }
+}
+
+// add old registration for yahoo
+Y.register("yahoo", Y, {version: "3.0.0", build: "1549"});
+
+if (Y.Event) {
+
+ var o = {
+
+ /**
+ * Safari detection
+ * @property isSafari
+ * @private
+ * @static
+ * @deprecated use Y.Env.UA.webkit
+ */
+ isSafari: Y.UA.webkit,
+
+ /**
+ * webkit version
+ * @property webkit
+ * @type string
+ * @private
+ * @static
+ * @deprecated use Y.Env.UA.webkit
+ */
+ webkit: Y.UA.webkit,
+
+ /**
+ * Normalized keycodes for webkit/safari
+ * @property webkitKeymap
+ * @type {int: int}
+ * @private
+ * @static
+ * @final
+ */
+ webkitKeymap: {
+ 63232: 38, // up
+ 63233: 40, // down
+ 63234: 37, // left
+ 63235: 39, // right
+ 63276: 33, // page up
+ 63277: 34, // page down
+ 25: 9 // SHIFT-TAB (Safari provides a different key code in
+ // this case, even though the shiftKey modifier is set)
+ },
+
+ /**
+ * IE detection
+ * @property isIE
+ * @private
+ * @static
+ * @deprecated use Y.Env.UA.ie
+ */
+ isIE: Y.UA.ie,
+
+ /**
+ * Returns scrollLeft
+ * @method _getScrollLeft
+ * @static
+ * @private
+ */
+ _getScrollLeft: function() {
+ return this._getScroll()[1];
+ },
+
+ /**
+ * Returns scrollTop
+ * @method _getScrollTop
+ * @static
+ * @private
+ */
+ _getScrollTop: function() {
+ return this._getScroll()[0];
+ },
+
+ /**
+ * Returns the scrollTop and scrollLeft. Used to calculate the
+ * pageX and pageY in Internet Explorer
+ * @method _getScroll
+ * @static
+ * @private
+ */
+ _getScroll: function() {
+ var d = Y.config.doc, dd = d.documentElement, db = d.body;
+ if (dd && (dd.scrollTop || dd.scrollLeft)) {
+ return [dd.scrollTop, dd.scrollLeft];
+ } else if (db) {
+ return [db.scrollTop, db.scrollLeft];
+ } else {
+ return [0, 0];
+ }
+ },
+
+ /**
+ * Returns the event's pageX
+ * @method getPageX
+ * @param {Event} ev the event
+ * @return {int} the event's pageX
+ * @static
+ */
+ getPageX: function(ev) {
+ var x = ev.pageX;
+ if (!x && 0 !== x) {
+ x = ev.clientX || 0;
+
+ if ( Y.UA.ie ) {
+ x += this._getScrollLeft();
+ }
+ }
+
+ return x;
+ },
+
+ /**
+ * Returns the charcode for an event
+ * @method getCharCode
+ * @param {Event} ev the event
+ * @return {int} the event's charCode
+ * @static
+ */
+ getCharCode: function(ev) {
+ var code = ev.keyCode || ev.charCode || 0;
+
+ // webkit normalization
+ if (Y.UA.webkit && (code in Y.Event.webkitKeymap)) {
+ code = Y.Event.webkitKeymap[code];
+ }
+ return code;
+ },
+
+ /**
+ * Returns the event's pageY
+ * @method getPageY
+ * @param {Event} ev the event
+ * @return {int} the event's pageY
+ * @static
+ */
+ getPageY: function(ev) {
+ var y = ev.pageY;
+ if (!y && 0 !== y) {
+ y = ev.clientY || 0;
+
+ if ( Y.UA.ie ) {
+ y += this._getScrollTop();
+ }
+ }
+
+
+ return y;
+ },
+
+ /**
+ * Returns the pageX and pageY properties as an indexed array.
+ * @method getXY
+ * @param {Event} ev the event
+ * @return {[x, y]} the pageX and pageY properties of the event
+ * @static
+ */
+ getXY: function(ev) {
+ return [this.getPageX(ev), this.getPageY(ev)];
+ },
+
+ /**
+ * Returns the event's related target
+ * @method getRelatedTarget
+ * @param {Event} ev the event
+ * @return {HTMLElement} the event's relatedTarget
+ * @static
+ */
+ getRelatedTarget: function(ev) {
+ var t = ev.relatedTarget;
+ if (!t) {
+ if (ev.type == "mouseout") {
+ t = ev.toElement;
+ } else if (ev.type == "mouseover") {
+ t = ev.fromElement;
+ }
+ }
+
+ return this.resolveTextNode(t);
+ },
+
+ /**
+ * Returns the time of the event. If the time is not included, the
+ * event is modified using the current time.
+ * @method getTime
+ * @param {Event} ev the event
+ * @return {Date} the time of the event
+ * @static
+ */
+ getTime: function(ev) {
+ if (!ev.time) {
+ var t = new Date().getTime();
+ try {
+ ev.time = t;
+ } catch(ex) {
+ this.lastError = ex;
+ return t;
+ }
+ }
+
+ return ev.time;
+ },
+
+ /**
+ * Convenience method for stopPropagation + preventDefault
+ * @method stopEvent
+ * @param {Event} ev the event
+ * @static
+ */
+ stopEvent: function(ev) {
+ this.stopPropagation(ev);
+ this.preventDefault(ev);
+ },
+
+ /**
+ * Stops event propagation
+ * @method stopPropagation
+ * @param {Event} ev the event
+ * @static
+ */
+ stopPropagation: function(ev) {
+ if (ev.stopPropagation) {
+ ev.stopPropagation();
+ } else {
+ ev.cancelBubble = true;
+ }
+ },
+
+ /**
+ * Prevents the default behavior of the event
+ * @method preventDefault
+ * @param {Event} ev the event
+ * @static
+ */
+ preventDefault: function(ev) {
+ if (ev.preventDefault) {
+ ev.preventDefault();
+ } else {
+ ev.returnValue = false;
+ }
+ },
+
+ /**
+ * Returns the event's target element. Safari sometimes provides
+ * a text node, and this is automatically resolved to the text
+ * node's parent so that it behaves like other browsers.
+ * @method getTarget
+ * @param {Event} ev the event
+ * @param {boolean} resolveTextNode when set to true the target's
+ * parent will be returned if the target is a
+ * text node. @deprecated, the text node is
+ * now resolved automatically
+ * @return {HTMLElement} the event's target
+ * @static
+ */
+ getTarget: function(ev, resolveTextNode) {
+ var t = ev.target || ev.srcElement;
+ return this.resolveTextNode(t);
+ },
+
+ /**
+ * In some cases, some browsers will return a text node inside
+ * the actual element that was targeted. This normalizes the
+ * return value for getTarget and getRelatedTarget.
+ * @method resolveTextNode
+ * @param {HTMLElement} node node to resolve
+ * @return {HTMLElement} the normized node
+ * @static
+ */
+ resolveTextNode: function(node) {
+ if (node && 3 == node.nodeType) {
+ return node.parentNode;
+ } else {
+ return node;
+ }
+ },
+
+ /**
+ * We cache elements bound by id because when the unload event
+ * fires, we can no longer use document.getElementById
+ * @method getEl
+ * @static
+ * @private
+ * @deprecated Elements are not cached any longer
+ */
+ getEl: function(id) {
+ return Y.get(id);
+ }
+ };
+
+ Y.mix(Y.Event, o);
+
+ /**
+ * Calls Y.Event.attach with the correct argument order
+ * @method removeListener
+ */
+ Y.Event.removeListener = function(el, type, fn, data, override) {
+
+ var context, a=[type, fn, el];
+
+ if (data) {
+
+ if (override) {
+ context = (override === true) ? data : override;
+ }
+
+ a.push(context);
+ a.push(data);
+ }
+
+ a.push(COMPAT_ARG);
+
+ return Y.Event.detach.apply(Y.Event, a);
+ };
+
+ /**
+ * Calls Y.Event.detach with the correct argument order
+ * @method addListener
+ */
+ Y.Event.addListener = function(el, type, fn, data, override) {
+
+
+ // var a = Y.Array(arguments, 0, true), el = a.shift();
+ // a.splice(2, 0, el);
+ // return Y.Event.attach.apply(Y.Event, a);
+ var context, a=[type, fn, el];
+
+ if (data) {
+
+ if (override) {
+ context = (override === true) ? data : override;
+ }
+
+ a.push(context);
+ a.push(data);
+ }
+
+ a.push(COMPAT_ARG);
+
+ return Y.Event.attach.apply(Y.Event, a);
+ };
+
+ Y.Event.on = Y.Event.addListener;
+
+ var newOnavail = Y.Event.onAvailable;
+
+ Y.Event.onAvailable = function(id, fn, p_obj, p_override) {
+ return newOnavail(id, fn, p_obj, p_override, false, true);
+ };
+
+ Y.Event.onContentReady = function(id, fn, p_obj, p_override) {
+ return newOnavail(id, fn, p_obj, p_override, true, true);
+ };
+
+ Y.Event.onDOMReady = function(fn) {
+ var a = Y.Array(arguments, 0, true);
+ a.unshift('event:ready');
+ return Y.on.apply(Y, a);
+ };
+
+ Y.util.Event = Y.Event;
+
+ var CE = function(type, oScope, silent, signature) {
+ //debugger;
+
+ var o = {
+ context: oScope,
+ silent: silent || false
+ // signature: signature || CE.LIST
+ };
+
+ CE.superclass.constructor.call(this, type, o);
+
+ this.signature = signature || CE.LIST;
+ };
+
+ Y.extend(CE, Y.CustomEvent, {
+
+ });
+
+ /**
+ * Subscriber listener sigature constant. The LIST type returns three
+ * parameters: the event type, the array of args passed to fire, and
+ * the optional custom object
+ * @property YAHOO.util.CustomEvent.LIST
+ * @static
+ * @type int
+ */
+ CE.LIST = 0;
+
+ /**
+ * Subscriber listener sigature constant. The FLAT type returns two
+ * parameters: the first argument passed to fire and the optional
+ * custom object
+ * @property YAHOO.util.CustomEvent.FLAT
+ * @static
+ * @type int
+ */
+ CE.FLAT = 1;
+
+ Y.util.CustomEvent = CE;
+
+ var EP = function() {
+ //console.log('Compat CustomEvent constructor executed: ' + this._yuid);
+ if (!this._yuievt) {
+ var sub = this.subscribe;
+ Y.EventTarget.apply(this, arguments);
+ this.subscribe = sub;
+ this.__yuiepinit = function() {};
+ }
+ };
+
+ Y.extend(EP, Y.EventTarget, {
+
+ createEvent: function(type, o) {
+ o = o || {};
+ o.signature = o.signature || CE.FLAT;
+ return this.publish(type, o);
+ },
+
+ subscribe: function(type, fn, obj, override) {
+ var ce = this._yuievt.events[type] || this.createEvent(type),
+ a = Y.Array(arguments);
+
+ if (override && true !== override) {
+ // a[2] = override;
+ // a[1] = obj;
+ }
+
+ Y.EventTarget.prototype.subscribe.apply(this, a);
+ },
+
+ fireEvent: function(type) {
+ return this.fire.apply(this, arguments);
+ },
+
+ hasEvent: function(type) {
+ return this.getEvent(type);
+ }
+ });
+
+ Y.util.EventProvider = EP;
+
+}
+
+
+Y.register("event", Y, {version: "3.0.0", build: "1549"});
+
+
+var propertyCache = {};
+var patterns = {
+ HYPHEN: /(-[a-z])/i, // to normalize get/setStyle
+ ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards,
+ OP_SCROLL:/^(?:inline|table-row)$/i
+};
+
+var hyphenToCamel = function(property) {
+ if ( !patterns.HYPHEN.test(property) ) {
+ return property; // no hyphens
+ }
+
+ if (propertyCache[property]) { // already converted
+ return propertyCache[property];
+ }
+
+ var converted = property;
+
+ while( patterns.HYPHEN.exec(converted) ) {
+ converted = converted.replace(RegExp.$1,
+ RegExp.$1.substr(1).toUpperCase());
+ }
+
+ propertyCache[property] = converted;
+ return converted;
+ //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug
+};
+
+var Dom = {
+ get: function(el) {
+ if (el) {
+ if (el.nodeType || el.item) { // Node, or NodeList
+ return el;
+ }
+
+ if (typeof el === 'string') { // id
+ return document.getElementById(el);
+ }
+
+ if ('length' in el) { // array-like
+ var c = [];
+ for (var i = 0, len = el.length; i < len; ++i) {
+ c[c.length] = Dom.get(el[i]);
+ }
+
+ return c;
+ }
+
+ return el; // some other object, just pass it back
+ }
+
+ return null;
+ },
+
+ isAncestor: function(haystack, needle) {
+ return YUI.DOM.contains(Dom.get(haystack), Dom.get(needle));
+ },
+
+ inDocument: function(el) {
+ return Dom.isAncestor(Y.config.doc.documentElement, el);
+ },
+
+ batch: function(el, method, o, override, args) {
+ el = (el && (el.tagName || el.item)) ? el : Dom.get(el); // skip get() when possible
+
+ if (!el || !method) {
+ return false;
+ }
+ if (args) {
+ args = Y.Array(args);
+ }
+ var scope = (override) ? o : window;
+
+ var apply = function(el) {
+ if (args) {
+ var tmp = slice.call(args);
+ tmp.unshift(el);
+ return method.apply(scope, tmp);
+ } else {
+ return method.call(scope, el, o);
+ }
+ };
+
+ if (el.tagName || el.length === undefined) { // element or not array-like
+ return apply(el);
+ }
+
+ var collection = [];
+
+ for (var i = 0, len = el.length; i < len; ++i) {
+ collection[collection.length] = apply(el[i]);
+ }
+
+ return collection;
+ },
+
+ // 2.x returns false if already present
+ _addClass: function(el, className) {
+ if ( YUI.DOM.hasClass(el, className) ) {
+ return false;
+ }
+
+ YUI.DOM.addClass(el, className);
+ return true;
+ },
+
+ // 2.x returns false if not present
+ _removeClass: function(el, className) {
+ if ( !YUI.DOM.hasClass(el, className) ) {
+ return false;
+ }
+
+ YUI.DOM.removeClass(el, className);
+ return true;
+ },
+
+ // 2.x returns false if no newClass or same as oldClass
+ _replaceClass: function(el, oldClass, newClass) {
+ if (!newClass || oldClass === newClass) {
+ return false;
+ }
+
+ YUI.DOM.replaceClass(el, oldClass, newClass);
+ return true;
+ },
+
+ getElementsByClassName: function(className, tag, root) {
+ tag = tag || '*';
+ root = (root) ? Dom.get(root) : Y.config.doc;
+ var nodes = [];
+ if (root) {
+ nodes = Y.Selector.query(tag + '.' + className, root);
+ }
+ return nodes;
+ },
+
+ getElementsBy: function(method, tag, root) {
+ tag = tag || '*';
+ root = (root) ? Dom.get(root) : null || document;
+
+ var nodes = [];
+ if (root) {
+ nodes = YUI.DOM.byTag(tag, root, method);
+ }
+ return nodes;
+ },
+
+ getViewportWidth: YUI.DOM.winWidth,
+ getViewportHeight: YUI.DOM.winHeight,
+ getDocumentWidth: YUI.DOM.docWidth,
+ getDocumentHeight: YUI.DOM.docHeight,
+ getDocumentScrollTop: YUI.DOM.docScrollY,
+ getDocumentScrollLeft: YUI.DOM.docScrollX,
+ getDocumentHeight: YUI.DOM.docHeight,
+
+ _guid: function(el, prefix) {
+ prefix = prefix || 'yui-gen';
+ Dom._id_counter = Dom._id_counter || 0;
+
+ if (el && el.id) { // do not override existing ID
+ return el.id;
+ }
+
+ var id = prefix + Dom._id_counter++;
+
+ if (el) {
+ el.id = id;
+ }
+
+ return id;
+ },
+
+ _region: function(el) {
+ if ( (el.parentNode === null || el.offsetParent === null ||
+ YUI.DOM.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body) {
+ return false;
+ }
+
+ return YUI.DOM.region(el);
+
+ },
+
+ _ancestorByClass: function(element, className) {
+ return YUI.DOM.ancestor(element, function(el) {
+ return YUI.DOM.hasClass(el, className);
+ });
+ },
+
+ _ancestorByTag: function(element, tag) {
+ tag = tag.toUpperCase();
+ return YUI.DOM.ancestor(element, function(el) {
+ return el.tagName.toUpperCase() === tag;
+ });
+ }
+};
+
+var slice = [].slice;
+
+var wrap = function(fn, name) {
+ Dom[name] = function() {
+ var args = slice.call(arguments);
+ args[0] = Dom.get(args[0]);
+ return fn.apply(Dom, args);
+ };
+};
+
+var wrapped = {
+ getAncestorBy: YUI.DOM.ancestor,
+ getAncestorByClassName: Dom._ancestorByClass,
+ getAncestorByTagName: Dom._ancestorByTag,
+ getPreviousSiblingBy: YUI.DOM.previous,
+ getPreviousSibling: YUI.DOM.previous,
+ getNextSiblingBy: YUI.DOM.next,
+ getNextSibling: YUI.DOM.next,
+ getFirstChildBy: YUI.DOM.firstChild,
+ getFirstChild: YUI.DOM.firstChild,
+ getLastChildBy: YUI.DOM.lastChild,
+ getLastChild: YUI.DOM.lastChild,
+ getChildrenBy: YUI.DOM.children,
+ getChildren: YUI.DOM.children,
+ insertBefore: function(newNode, refNode) {
+ YUI.DOM.insertBefore(Dom.get(newNode), Dom.get(refNode));
+ },
+ insertAfter: function(newNode, refNode) {
+ YUI.DOM.insertAfter(Dom.get(newNode), Dom.get(refNode));
+ }
+};
+
+Y.each(wrapped, wrap);
+
+var batched = {
+ getStyle: YUI.DOM.getStyle,
+ setStyle: YUI.DOM.setStyle,
+ getXY: YUI.DOM.getXY,
+ setXY: YUI.DOM.setXY,
+ getX: YUI.DOM.getX,
+ getY: YUI.DOM.getY,
+ setX: YUI.DOM.setX,
+ setY: YUI.DOM.setY,
+ getRegion: Dom._region,
+ hasClass: YUI.DOM.hasClass,
+ addClass: Dom._addClass,
+ removeClass: Dom._removeClass,
+ replaceClass: Dom._replaceClass,
+ generateId: Dom._guid
+};
+
+Y.each(batched, function(v, n) {
+ Dom[n] = function(el) {
+ var args = slice.call(arguments, 1);
+ return Dom.batch(el, v, null, null, args);
+ };
+});
+
+Y.util.Dom = Dom;
+
+YAHOO.util.Region = function(t, r, b, l) {
+ this.top = t;
+ this[1] = t;
+ this.right = r;
+ this.bottom = b;
+ this.left = l;
+ this[0] = l;
+};
+
+YAHOO.util.Region.prototype.contains = function(region) {
+ return ( region.left >= this.left &&
+ region.right <= this.right &&
+ region.top >= this.top &&
+ region.bottom <= this.bottom );
+
+};
+
+YAHOO.util.Region.prototype.getArea = function() {
+ return ( (this.bottom - this.top) * (this.right - this.left) );
+};
+
+YAHOO.util.Region.prototype.intersect = function(region) {
+ var t = Math.max( this.top, region.top );
+ var r = Math.min( this.right, region.right );
+ var b = Math.min( this.bottom, region.bottom );
+ var l = Math.max( this.left, region.left );
+
+ if (b >= t && r >= l) {
+ return new YAHOO.util.Region(t, r, b, l);
+ } else {
+ return null;
+ }
+};
+
+YAHOO.util.Region.prototype.union = function(region) {
+ var t = Math.min( this.top, region.top );
+ var r = Math.max( this.right, region.right );
+ var b = Math.max( this.bottom, region.bottom );
+ var l = Math.min( this.left, region.left );
+
+ return new YAHOO.util.Region(t, r, b, l);
+};
+
+YAHOO.util.Region.prototype.toString = function() {
+ return ( "Region {" +
+ "top: " + this.top +
+ ", right: " + this.right +
+ ", bottom: " + this.bottom +
+ ", left: " + this.left +
+ "}" );
+};
+
+YAHOO.util.Region.getRegion = function(el) {
+ return YUI.DOM.region(el);
+};
+
+YAHOO.util.Point = function(x, y) {
+ if (YAHOO.lang.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc.
+ y = x[1]; // dont blow away x yet
+ x = x[0];
+ }
+
+ this.x = this.right = this.left = this[0] = x;
+ this.y = this.top = this.bottom = this[1] = y;
+};
+
+YAHOO.util.Point.prototype = new YAHOO.util.Region();
+
+
+
+}, '3.0.0' ,{requires:['dom','event']});
+YUI._setup(); YUI.use('dom', 'event', 'compat');
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-console-ft .yui-console-filters-categories,
+.yui-skin-sam .yui-console-ft .yui-console-filters-sources {
+ text-align: left;
+ padding: 5px 0;
+ border: 1px inset;
+ margin: 0 2px;
+}
+.yui-skin-sam .yui-console-ft .yui-console-filters-categories {
+ background: #fff;
+ border-bottom: 2px ridge;
+}
+.yui-skin-sam .yui-console-ft .yui-console-filters-sources {
+ background: #fff;
+ margin-bottom: 2px;
+
+ border-top: 0 none;
+ border-bottom-right-radius: 10px;
+ border-bottom-left-radius: 10px;
+ -moz-border-radius-bottomright: 10px;
+ -moz-border-radius-bottomleft: 10px;
+ -webkit-border-bottom-right-radius: 10px;
+ -webkit-border-bottom-left-radius: 10px;
+}
+.yui-skin-sam .yui-console-filter-label {
+ white-space: nowrap;
+ margin-left: 1ex;
+}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-console-ft .yui-console-filters-categories,.yui-skin-sam .yui-console-ft .yui-console-filters-sources{text-align:left;padding:5px 0;border:1px inset;margin:0 2px;}.yui-skin-sam .yui-console-ft .yui-console-filters-categories{background:#fff;border-bottom:2px ridge;}.yui-skin-sam .yui-console-ft .yui-console-filters-sources{background:#fff;margin-bottom:2px;border-top:0 none;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-moz-border-radius-bottomright:10px;-moz-border-radius-bottomleft:10px;-webkit-border-bottom-right-radius:10px;-webkit-border-bottom-left-radius:10px;}.yui-skin-sam .yui-console-filter-label{white-space:nowrap;margin-left:1ex;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-separate-console {
+ position:absolute;
+ right:1em;
+ top:1em;
+ z-index:999;
+}
+
+.yui-skin-sam .yui-inline-console {
+ /* xbrowser inline-block styles */
+ display: -moz-inline-stack; /* FF2 */
+ display: inline-block;
+ *display: inline; /* IE 7- (with zoom) */
+ zoom: 1;
+ vertical-align: top;
+}
+.yui-skin-sam .yui-inline-console .yui-console-content {
+ position: relative;
+}
+
+.yui-skin-sam .yui-console-content {
+ background: #777;
+ _background: #D8D8DA url(bg.png) repeat-x 0 0;
+ font: normal 13px/1.3 Arial, sans-serif;
+ text-align: left;
+
+ border: 1px solid #777;
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+}
+
+.yui-skin-sam .yui-console-hd,
+.yui-skin-sam .yui-console-bd,
+.yui-skin-sam .yui-console-ft {
+ position: relative;
+}
+
+.yui-skin-sam .yui-console-hd,
+.yui-skin-sam .yui-console-ft .yui-console-controls {
+ text-align: right;
+}
+
+.yui-skin-sam .yui-console-hd {
+ background: #D8D8DA url(bg.png) repeat-x 0 0;
+ padding: 1ex;
+
+ border: 1px solid transparent;
+ _border: 0 none;
+ border-top-right-radius: 10px;
+ border-top-left-radius: 10px;
+ -moz-border-radius-topright: 10px;
+ -moz-border-radius-topleft: 10px;
+ -webkit-border-top-right-radius: 10px;
+ -webkit-border-top-left-radius: 10px;
+}
+
+.yui-skin-sam .yui-console-bd {
+ background: #fff;
+ border-top: 1px solid #777;
+ border-bottom: 1px solid #777;
+ color: #000;
+ font-size: 11px;
+ overflow: auto;
+ overflow-x: auto;
+ overflow-y: scroll;
+ _width: 100%;
+}
+
+.yui-skin-sam .yui-console-ft {
+ background: #D8D8DA url(bg.png) repeat-x 0 0;
+
+ border: 1px solid transparent;
+ _border: 0 none;
+ border-bottom-right-radius: 10px;
+ border-bottom-left-radius: 10px;
+ -moz-border-radius-bottomright: 10px;
+ -moz-border-radius-bottomleft: 10px;
+ -webkit-border-bottom-right-radius: 10px;
+ -webkit-border-bottom-left-radius: 10px;
+}
+
+.yui-skin-sam .yui-console-controls {
+ padding: 4px 1ex;
+ zoom: 1;
+}
+
+.yui-skin-sam .yui-console-title {
+ color: #000;
+ display: inline;
+ float: left;
+ font-weight: bold;
+ font-size: 13px;
+ height: 24px;
+ line-height: 24px;
+ margin: 0;
+ padding-left: 1ex;
+}
+
+.yui-skin-sam .yui-console-pause-label {
+ float: left;
+}
+.yui-skin-sam .yui-console-button {
+ line-height: 1.3;
+}
+
+.yui-skin-sam .yui-console-collapsed .yui-console-bd,
+.yui-skin-sam .yui-console-collapsed .yui-console-ft {
+ display: none;
+}
+.yui-skin-sam .yui-console-content.yui-console-collapsed {
+ -webkit-border-radius: 0;
+}
+.yui-skin-sam .yui-console-collapsed .yui-console-hd {
+ border-radius: 10px;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 0;
+}
+
+/* Log entries */
+.yui-skin-sam .yui-console-entry {
+ border-bottom: 1px solid #aaa;
+ min-height: 32px;
+ _height: 32px;
+}
+
+.yui-skin-sam .yui-console-entry-meta {
+ margin: 0;
+ overflow: hidden;
+}
+
+.yui-skin-sam .yui-console-entry-content {
+ margin: 0;
+ padding: 0 1ex;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+.yui-skin-sam .yui-console-entry-meta .yui-console-entry-src {
+ color: #000;
+ font-style: italic;
+ font-weight: bold;
+ float: right;
+ margin: 2px 5px 0 0;
+}
+.yui-skin-sam .yui-console-entry-meta .yui-console-entry-time {
+ color: #777;
+ padding-left: 1ex;
+}
+.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-time {
+ color: #555;
+}
+
+.yui-skin-sam .yui-console-entry-info .yui-console-entry-meta .yui-console-entry-cat,
+.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-cat,
+.yui-skin-sam .yui-console-entry-error .yui-console-entry-meta .yui-console-entry-cat {
+ display: none;
+}
+.yui-skin-sam .yui-console-entry-warn {
+ background: #aee url(warn_error.png) no-repeat -15px 15px;
+}
+.yui-skin-sam .yui-console-entry-error {
+ background: #ffa url(warn_error.png) no-repeat 5px -24px;
+ color: #900;
+}
+.yui-skin-sam .yui-console-entry-warn .yui-console-entry-content,
+.yui-skin-sam .yui-console-entry-error .yui-console-entry-content {
+ padding-left: 24px;
+}
+.yui-skin-sam .yui-console-entry-cat {
+ text-transform: uppercase;
+ padding: 1px 4px;
+ background-color: #ccc;
+}
+.yui-skin-sam .yui-console-entry-info .yui-console-entry-cat {
+ background-color: #ac2;
+}
+.yui-skin-sam .yui-console-entry-warn .yui-console-entry-cat {
+ background-color: #e81;
+}
+.yui-skin-sam .yui-console-entry-error .yui-console-entry-cat {
+ background-color: #b00;
+ color: #fff;
+}
+
+.yui-skin-sam .yui-console-hidden { display: none; }
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-separate-console{position:absolute;right:1em;top:1em;z-index:999;}.yui-skin-sam .yui-inline-console{display:-moz-inline-stack;display:inline-block;*display:inline;zoom:1;vertical-align:top;}.yui-skin-sam .yui-inline-console .yui-console-content{position:relative;}.yui-skin-sam .yui-console-content{background:#777;_background:#D8D8DA url(bg.png) repeat-x 0 0;font:normal 13px/1.3 Arial,sans-serif;text-align:left;border:1px solid #777;border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;}.yui-skin-sam .yui-console-hd,.yui-skin-sam .yui-console-bd,.yui-skin-sam .yui-console-ft{position:relative;}.yui-skin-sam .yui-console-hd,.yui-skin-sam .yui-console-ft .yui-console-controls{text-align:right;}.yui-skin-sam .yui-console-hd{background:#D8D8DA url(bg.png) repeat-x 0 0;padding:1ex;border:1px solid transparent;_border:0 none;border-top-right-radius:10px;border-top-left-radius:10px;-moz-border-radius-topright:10px;-moz-border-radius-topleft:10px;-webkit-border-top-right-radius:10px;-webkit-border-top-left-radius:10px;}.yui-skin-sam .yui-console-bd{background:#fff;border-top:1px solid #777;border-bottom:1px solid #777;color:#000;font-size:11px;overflow:auto;overflow-x:auto;overflow-y:scroll;_width:100%;}.yui-skin-sam .yui-console-ft{background:#D8D8DA url(bg.png) repeat-x 0 0;border:1px solid transparent;_border:0 none;border-bottom-right-radius:10px;border-bottom-left-radius:10px;-moz-border-radius-bottomright:10px;-moz-border-radius-bottomleft:10px;-webkit-border-bottom-right-radius:10px;-webkit-border-bottom-left-radius:10px;}.yui-skin-sam .yui-console-controls{padding:4px 1ex;zoom:1;}.yui-skin-sam .yui-console-title{color:#000;display:inline;float:left;font-weight:bold;font-size:13px;height:24px;line-height:24px;margin:0;padding-left:1ex;}.yui-skin-sam .yui-console-pause-label{float:left;}.yui-skin-sam .yui-console-button{line-height:1.3;}.yui-skin-sam .yui-console-collapsed .yui-console-bd,.yui-skin-sam .yui-console-collapsed .yui-console-ft{display:none;}.yui-skin-sam .yui-console-content.yui-console-collapsed{-webkit-border-radius:0;}.yui-skin-sam .yui-console-collapsed .yui-console-hd{border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:0;}.yui-skin-sam .yui-console-entry{border-bottom:1px solid #aaa;min-height:32px;_height:32px;}.yui-skin-sam .yui-console-entry-meta{margin:0;overflow:hidden;}.yui-skin-sam .yui-console-entry-content{margin:0;padding:0 1ex;white-space:pre-wrap;word-wrap:break-word;}.yui-skin-sam .yui-console-entry-meta .yui-console-entry-src{color:#000;font-style:italic;font-weight:bold;float:right;margin:2px 5px 0 0;}.yui-skin-sam .yui-console-entry-meta .yui-console-entry-time{color:#777;padding-left:1ex;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-time{color:#555;}.yui-skin-sam .yui-console-entry-info .yui-console-entry-meta .yui-console-entry-cat,.yui-skin-sam .yui-console-entry-warn .yui-console-entry-meta .yui-console-entry-cat,.yui-skin-sam .yui-console-entry-error .yui-console-entry-meta .yui-console-entry-cat{display:none;}.yui-skin-sam .yui-console-entry-warn{background:#aee url(warn_error.png) no-repeat -15px 15px;}.yui-skin-sam .yui-console-entry-error{background:#ffa url(warn_error.png) no-repeat 5px -24px;color:#900;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-content,.yui-skin-sam .yui-console-entry-error .yui-console-entry-content{padding-left:24px;}.yui-skin-sam .yui-console-entry-cat{text-transform:uppercase;padding:1px 4px;background-color:#ccc;}.yui-skin-sam .yui-console-entry-info .yui-console-entry-cat{background-color:#ac2;}.yui-skin-sam .yui-console-entry-warn .yui-console-entry-cat{background-color:#e81;}.yui-skin-sam .yui-console-entry-error .yui-console-entry-cat{background-color:#b00;color:#fff;}.yui-skin-sam .yui-console-hidden{display:none;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('console', function(Y) {
+
+/**
+ * Console creates a visualization for messages logged through calls to a YUI
+ * instance's <code>Y.log( message, category, source )</code> method. The
+ * debug versions of YUI modules will include logging statements to offer some
+ * insight into the steps executed during that module's operation. Including
+ * log statements in your code will cause those messages to also appear in the
+ * Console. Use Console to aid in developing your page or application.
+ *
+ * Entry categories "info", "warn", and "error"
+ * are also referred to as the log level, and entries are filtered against the
+ * configured logLevel.
+ *
+ * @module console
+ * @class Console
+ * @extends Widget
+ * @param conf {Object} Configuration object (see Configuration attributes)
+ * @constructor
+ */
+function Console() {
+ Console.superclass.constructor.apply(this,arguments);
+}
+
+var getCN = Y.ClassNameManager.getClassName,
+ CHECKED = 'checked',
+ CLEAR = 'clear',
+ CLICK = 'click',
+ COLLAPSED = 'collapsed',
+ CONSOLE = 'console',
+ CONTENT_BOX = 'contentBox',
+ DISABLED = 'disabled',
+ ENTRY = 'entry',
+ ERROR = 'error',
+ HEIGHT = 'height',
+ INFO = 'info',
+ INNER_HTML = 'innerHTML',
+ LAST_TIME = 'lastTime',
+ PAUSE = 'pause',
+ PAUSED = 'paused',
+ RESET = 'reset',
+ START_TIME = 'startTime',
+ TITLE = 'title',
+ WARN = 'warn',
+
+ DOT = '.',
+
+ C_BUTTON = getCN(CONSOLE,'button'),
+ C_CHECKBOX = getCN(CONSOLE,'checkbox'),
+ C_CLEAR = getCN(CONSOLE,CLEAR),
+ C_COLLAPSE = getCN(CONSOLE,'collapse'),
+ C_COLLAPSED = getCN(CONSOLE,COLLAPSED),
+ C_CONSOLE_CONTROLS = getCN(CONSOLE,'controls'),
+ C_CONSOLE_HD = getCN(CONSOLE,'hd'),
+ C_CONSOLE_BD = getCN(CONSOLE,'bd'),
+ C_CONSOLE_FT = getCN(CONSOLE,'ft'),
+ C_CONSOLE_TITLE = getCN(CONSOLE,TITLE),
+ C_ENTRY = getCN(CONSOLE,ENTRY),
+ C_ENTRY_CAT = getCN(CONSOLE,ENTRY,'cat'),
+ C_ENTRY_CONTENT = getCN(CONSOLE,ENTRY,'content'),
+ C_ENTRY_META = getCN(CONSOLE,ENTRY,'meta'),
+ C_ENTRY_SRC = getCN(CONSOLE,ENTRY,'src'),
+ C_ENTRY_TIME = getCN(CONSOLE,ENTRY,'time'),
+ C_PAUSE = getCN(CONSOLE,PAUSE),
+ C_PAUSE_LABEL = getCN(CONSOLE,PAUSE,'label'),
+
+ RE_INLINE_SOURCE = /^(\S+)\s/,
+ RE_AMP = /&/g,
+ RE_GT = />/g,
+ RE_LT = /</g,
+
+ ESC_AMP = '&',
+ ESC_GT = '>',
+ ESC_LT = '<',
+
+ ENTRY_TEMPLATE_STR =
+ '<div class="{entry_class} {cat_class} {src_class}">'+
+ '<p class="{entry_meta_class}">'+
+ '<span class="{entry_src_class}">'+
+ '{sourceAndDetail}'+
+ '</span>'+
+ '<span class="{entry_cat_class}">'+
+ '{category}</span>'+
+ '<span class="{entry_time_class}">'+
+ ' {totalTime}ms (+{elapsedTime}) {localTime}'+
+ '</span>'+
+ '</p>'+
+ '<pre class="{entry_content_class}">{message}</pre>'+
+ '</div>',
+
+ L = Y.Lang,
+ create = Y.Node.create,
+ isNumber = L.isNumber,
+ isString = L.isString,
+ merge = Y.merge,
+ substitute = Y.substitute;
+
+
+Y.mix(Console, {
+
+ /**
+ * The identity of the widget.
+ *
+ * @property Console.NAME
+ * @type String
+ * @static
+ */
+ NAME : CONSOLE,
+
+ /**
+ * Static identifier for logLevel configuration setting to allow all
+ * incoming messages to generate Console entries.
+ *
+ * @property Console.LOG_LEVEL_INFO
+ * @type String
+ * @static
+ */
+ LOG_LEVEL_INFO : INFO,
+
+ /**
+ * Static identifier for logLevel configuration setting to allow only
+ * incoming messages of logLevel "warn" or "error"
+ * to generate Console entries.
+ *
+ * @property Console.LOG_LEVEL_WARN
+ * @type String
+ * @static
+ */
+ LOG_LEVEL_WARN : WARN,
+
+ /**
+ * Static identifier for logLevel configuration setting to allow only
+ * incoming messages of logLevel "error" to generate
+ * Console entries.
+ *
+ * @property Console.LOG_LEVEL_ERROR
+ * @type String
+ * @static
+ */
+ LOG_LEVEL_ERROR : ERROR,
+
+ /**
+ * Map (object) of classNames used to populate the placeholders in the
+ * Console.ENTRY_TEMPLATE markup when rendering a new Console entry.
+ *
+ * <p>By default, the keys contained in the object are:</p>
+ * <ul>
+ * <li>entry_class</li>
+ * <li>entry_meta_class</li>
+ * <li>entry_cat_class</li>
+ * <li>entry_src_class</li>
+ * <li>entry_time_class</li>
+ * <li>entry_content_class</li>
+ * </ul>
+ *
+ * @property Console.ENTRY_CLASSES
+ * @type Object
+ * @static
+ */
+ ENTRY_CLASSES : {
+ entry_class : C_ENTRY,
+ entry_meta_class : C_ENTRY_META,
+ entry_cat_class : C_ENTRY_CAT,
+ entry_src_class : C_ENTRY_SRC,
+ entry_time_class : C_ENTRY_TIME,
+ entry_content_class : C_ENTRY_CONTENT
+ },
+
+ /**
+ * Map (object) of classNames used to populate the placeholders in the
+ * Console.HEADER_TEMPLATE, Console.BODY_TEMPLATE, and
+ * Console.FOOTER_TEMPLATE markup when rendering the Console UI.
+ *
+ * <p>By default, the keys contained in the object are:</p>
+ * <ul>
+ * <li>console_hd_class</li>
+ * <li>console_bd_class</li>
+ * <li>console_ft_class</li>
+ * <li>console_controls_class</li>
+ * <li>console_checkbox_class</li>
+ * <li>console_pause_class</li>
+ * <li>console_pause_label_class</li>
+ * <li>console_button_class</li>
+ * <li>console_clear_class</li>
+ * <li>console_collapse_class</li>
+ * <li>console_title_class</li>
+ * </ul>
+ *
+ * @property Console.CHROME_CLASSES
+ * @type Object
+ * @static
+ */
+ CHROME_CLASSES : {
+ console_hd_class : C_CONSOLE_HD,
+ console_bd_class : C_CONSOLE_BD,
+ console_ft_class : C_CONSOLE_FT,
+ console_controls_class : C_CONSOLE_CONTROLS,
+ console_checkbox_class : C_CHECKBOX,
+ console_pause_class : C_PAUSE,
+ console_pause_label_class : C_PAUSE_LABEL,
+ console_button_class : C_BUTTON,
+ console_clear_class : C_CLEAR,
+ console_collapse_class : C_COLLAPSE,
+ console_title_class : C_CONSOLE_TITLE
+ },
+
+ /**
+ * Markup template used to generate the DOM structure for the header
+ * section of the Console when it is rendered. The template includes
+ * these {placeholder}s:
+ *
+ * <ul>
+ * <li>console_button_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>console_collapse_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>console_hd_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>console_title_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>str_collapse - pulled from attribute strings.collapse</li>
+ * <li>str_title - pulled from attribute strings.title</li>
+ * </ul>
+ *
+ * @property Console.HEADER_TEMPLATE
+ * @type String
+ * @static
+ */
+ HEADER_TEMPLATE :
+ '<div class="{console_hd_class}">'+
+ '<h4 class="{console_title_class}">{str_title}</h4>'+
+ '<button type="button" class="'+
+ '{console_button_class} {console_collapse_class}">{str_collapse}'+
+ '</button>'+
+ '</div>',
+
+ /**
+ * Markup template used to generate the DOM structure for the Console body
+ * (where the messages are inserted) when it is rendered. The template
+ * includes only the {placeholder} "console_bd_class", which is
+ * constributed by Console.CHROME_CLASSES.
+ *
+ * @property Console.BODY_TEMPLATE
+ * @type String
+ * @static
+ */
+ BODY_TEMPLATE : '<div class="{console_bd_class}"></div>',
+
+ /**
+ * Markup template used to generate the DOM structure for the footer
+ * section of the Console when it is rendered. The template includes
+ * many of the {placeholder}s from Console.CHROME_CLASSES as well as:
+ *
+ * <ul>
+ * <li>id_guid - generated unique id, relates the label and checkbox</li>
+ * <li>str_pause - pulled from attribute strings.pause</li>
+ * <li>str_clear - pulled from attribute strings.clear</li>
+ * </ul>
+ *
+ * @property Console.FOOTER_TEMPLATE
+ * @type String
+ * @static
+ */
+ FOOTER_TEMPLATE :
+ '<div class="{console_ft_class}">'+
+ '<div class="{console_controls_class}">'+
+ '<label for="{id_guid}" class="{console_pause_label_class}">'+
+ '<input type="checkbox" class="{console_checkbox_class} '+
+ '{console_pause_class}" value="1" id="{id_guid}"> '+
+ '{str_pause}</label>' +
+ '<button type="button" class="'+
+ '{console_button_class} {console_clear_class}">{str_clear}'+
+ '</button>'+
+ '</div>'+
+ '</div>',
+
+ /**
+ * Default markup template used to create the DOM structure for Console
+ * entries. The markup contains {placeholder}s for content and classes
+ * that are replaced via Y.substitute. The default template contains
+ * the {placeholder}s identified in Console.ENTRY_CLASSES as well as the
+ * following placeholders that will be populated by the log entry data:
+ *
+ * <ul>
+ * <li>cat_class</li>
+ * <li>src_class</li>
+ * <li>totalTime</li>
+ * <li>elapsedTime</li>
+ * <li>localTime</li>
+ * <li>sourceAndDetail</li>
+ * <li>message</li>
+ * </ul>
+ *
+ * @property Console.ENTRY_TEMPLATE
+ * @type String
+ * @static
+ */
+ ENTRY_TEMPLATE : ENTRY_TEMPLATE_STR,
+
+ /**
+ * Static property used to define the default attribute configuration of
+ * the Widget.
+ *
+ * @property Console.ATTRS
+ * @Type Object
+ * @static
+ */
+ ATTRS : {
+
+ /**
+ * Name of the custom event that will communicate log messages.
+ *
+ * @attribute logEvent
+ * @type String
+ * @default "yui:log"
+ */
+ logEvent : {
+ value : 'yui:log',
+ writeOnce : true,
+ validator : isString
+ },
+
+ /**
+ * Object that will emit the log events. By default the YUI instance.
+ * To have a single Console capture events from all YUI instances, set
+ * this to the Y.Global object.
+ *
+ * @attribute logSource
+ * @type EventTarget
+ * @default Y
+ */
+ logSource : {
+ value : Y,
+ writeOnce : true,
+ validator : function (v) {
+ return v && Y.Lang.isFunction(v.on);
+ }
+ },
+
+ /**
+ * Collection of strings used to label elements in the Console UI.
+ * Default collection contains the following name:value pairs:
+ *
+ * <ul>
+ * <li>title : "Log Console"</li>
+ * <li>pause : "Pause"</li>
+ * <li>clear : "Clear"</li>
+ * <li>collapse : "Collapse"</li>
+ * <li>expand : "Expand"</li>
+ * </ul>
+ *
+ * @attribute strings
+ * @type Object
+ */
+ strings : {
+ value : {
+ title : "Log Console",
+ pause : "Pause",
+ clear : "Clear",
+ collapse : "Collapse",
+ expand : "Expand"
+ }
+ },
+
+ /**
+ * Boolean to pause the outputting of new messages to the console.
+ * When paused, messages will accumulate in the buffer.
+ *
+ * @attribute paused
+ * @type boolean
+ * @default false
+ */
+ paused : {
+ value : false,
+ validator : L.isBoolean
+ },
+
+ /**
+ * If a category is not specified in the Y.log(..) statement, this
+ * category will be used. Categories "info",
+ * "warn", and "error" are also called log level.
+ *
+ * @attribute defaultCategory
+ * @type String
+ * @default "info"
+ */
+ defaultCategory : {
+ value : INFO,
+ validator : isString
+ },
+
+ /**
+ * If a source is not specified in the Y.log(..) statement, this
+ * source will be used.
+ *
+ * @attribute defaultSource
+ * @type String
+ * @default "global"
+ */
+ defaultSource : {
+ value : 'global',
+ validator : isString
+ },
+
+ /**
+ * Markup template used to create the DOM structure for Console entries.
+ *
+ * @attribute entryTemplate
+ * @type String
+ * @default Console.ENTRY_TEMPLATE
+ */
+ entryTemplate : {
+ value : ENTRY_TEMPLATE_STR,
+ validator : isString
+ },
+
+ /**
+ * Minimum entry log level to render into the Console. The initial
+ * logLevel value for all Console instances defaults from the
+ * Y.config.logLevel YUI configuration, or Console.LOG_LEVEL_INFO if
+ * that configuration is not set.
+ *
+ * Possible values are "info", "warn",
+ * "error" (case insensitive), or their corresponding statics
+ * Console.LOG_LEVEL_INFO and so on.
+ *
+ * @attribute logLevel
+ * @type String
+ * @default Y.config.logLevel or Console.LOG_LEVEL_INFO
+ */
+ logLevel : {
+ value : Y.config.logLevel || INFO,
+ setter : function (v) {
+ return this._setLogLevel(v);
+ }
+ },
+
+ /**
+ * Millisecond timeout between iterations of the print loop, moving
+ * entries from the buffer to the UI.
+ *
+ * @attribute printTimeout
+ * @type Number
+ * @default 100
+ */
+ printTimeout : {
+ value : 100,
+ validator : isNumber
+ },
+
+ /**
+ * Maximum number of entries printed in each iteration of the print
+ * loop. This is used to prevent excessive logging locking the page UI.
+ *
+ * @attribute printLimit
+ * @type Number
+ * @default 50
+ */
+ printLimit : {
+ value : 50,
+ validator : isNumber
+ },
+
+ /**
+ * Maximum number of Console entries allowed in the Console body at one
+ * time. This is used to keep acquired messages from exploding the
+ * DOM tree and impacting page performance.
+ *
+ * @attribute consoleLimit
+ * @type Number
+ * @default 300
+ */
+ consoleLimit : {
+ value : 300,
+ validator : isNumber
+ },
+
+ /**
+ * New entries should display at the top of the Console or the bottom?
+ *
+ * @attribute newestOnTop
+ * @type Boolean
+ * @default true
+ */
+ newestOnTop : {
+ value : true
+ },
+
+ /**
+ * When new entries are added to the Console UI, should they be
+ * scrolled into view?
+ *
+ * @attribute scrollIntoView
+ * @type Boolean
+ * @default true
+ */
+ scrollIntoView : {
+ value : true
+ },
+
+ /**
+ * The baseline time for this Console instance, used to measure elapsed
+ * time from the moment the console module is <code>use</code>d to the
+ * moment each new entry is logged (not rendered).
+ *
+ * This value is reset by the instance method myConsole.reset().
+ *
+ * @attribute startTime
+ * @type Date
+ * @default The moment the console module is <code>use</code>d
+ */
+ startTime : {
+ value : new Date()
+ },
+
+ /**
+ * The precise time the last entry was logged. Used to measure elapsed
+ * time between log messages.
+ *
+ * @attribute lastTime
+ * @type Date
+ * @default The moment the console module is <code>use</code>d
+ */
+ lastTime : {
+ value : new Date(),
+ readOnly: true
+ },
+
+ /**
+ * Controls the collapsed state of the Console
+ *
+ * @attribute collapsed
+ * @type Boolean
+ * @default false
+ */
+ collapsed : {
+ value : false
+ },
+
+ /**
+ * String with units, or number, representing the height of the Console,
+ * inclusive of header and footer. If a number is provided, the default
+ * unit, defined by Widget's DEF_UNIT, property is used.
+ *
+ * @attribute height
+ * @default "300px"
+ * @type {String | Number}
+ */
+ height: {
+ value: "300px"
+ },
+
+ /**
+ * String with units, or number, representing the width of the Console.
+ * If a number is provided, the default unit, defined by Widget's
+ * DEF_UNIT, property is used.
+ *
+ * @attribute width
+ * @default "300px"
+ * @type {String | Number}
+ */
+ width: {
+ value: "300px"
+ },
+
+ /**
+ * Pass through to the YUI instance useBrowserConsole configuration.
+ * By default this is set to false, which will disable logging to the
+ * browser console when a Console instance is created. If the
+ * logSource is not a YUI instance, this has no effect.
+ *
+ * @attribute useBrowserConsole
+ * @type {Boolean}
+ * @default false
+ */
+ useBrowserConsole : {
+ lazyAdd: false,
+ value: false,
+ getter : function () {
+ var logSource = this.get('logSource');
+ return logSource instanceof YUI ?
+ logSource.config.useBrowserConsole : null;
+ },
+ setter : function (v) {
+ var logSource = this.get('logSource');
+ if (logSource instanceof YUI) {
+ v = !!v;
+ logSource.config.useBrowserConsole = !!v;
+ return v;
+ } else {
+ return Y.Attribute.INVALID_VALUE;
+ }
+ }
+ },
+
+ /**
+ * Allows the Console to flow in the document. Available values are
+ * 'inline', 'block', and 'separate' (the default).
+ *
+ * @attribute style
+ * @type {String}
+ * @default 'separate'
+ */
+ style : {
+ value : 'separate',
+ writeOnce : true,
+ validator : function (v) {
+ return this._validateStyle(v);
+ }
+ }
+ }
+
+});
+
+Y.extend(Console,Y.Widget,{
+
+ /**
+ * Category to prefix all event subscriptions to allow for ease of detach
+ * during destroy.
+ *
+ * @property _evtCat
+ * @type string
+ * @protected
+ */
+ _evtCat : null,
+
+ /**
+ * Reference to the Node instance containing the header contents.
+ *
+ * @property _head
+ * @type Node
+ * @default null
+ * @protected
+ */
+ _head : null,
+
+ /**
+ * Reference to the Node instance that will house the console messages.
+ *
+ * @property _body
+ * @type Node
+ * @default null
+ * @protected
+ */
+ _body : null,
+
+ /**
+ * Reference to the Node instance containing the footer contents.
+ *
+ * @property _foot
+ * @type Node
+ * @default null
+ * @protected
+ */
+ _foot : null,
+
+ /**
+ * Holds the object API returned from <code>Y.later</code> for the print
+ * loop interval.
+ *
+ * @property _printLoop
+ * @type Object
+ * @default null
+ * @protected
+ */
+ _printLoop : null,
+
+ /**
+ * Array of normalized message objects awaiting printing.
+ *
+ * @property buffer
+ * @type Array
+ * @default null
+ * @protected
+ */
+ buffer : null,
+
+ /**
+ * Wrapper for <code>Y.log</code>.
+ *
+ * @method log
+ * @param arg* {MIXED} (all arguments passed through to <code>Y.log</code>)
+ * @chainable
+ */
+ log : function () {
+ Y.log.apply(Y,arguments);
+
+ return this;
+ },
+
+ /**
+ * Clear the console of messages and flush the buffer of pending messages.
+ *
+ * @method clearConsole
+ * @chainable
+ */
+ clearConsole : function () {
+ // TODO: clear event listeners from console contents
+ this._body.set(INNER_HTML,'');
+
+ this._cancelPrintLoop();
+
+ this.buffer = [];
+
+ return this;
+ },
+
+ /**
+ * Clears the console and resets internal timers.
+ *
+ * @method reset
+ * @chainable
+ */
+ reset : function () {
+ this.fire(RESET);
+
+ return this;
+ },
+
+ /**
+ * Collapses the body and footer.
+ *
+ * @method collapse
+ * @chainable
+ */
+ collapse : function () {
+ this.set(COLLAPSED, true);
+
+ return this;
+ },
+
+ /**
+ * Expands the body and footer if collapsed.
+ *
+ * @method expand
+ * @chainable
+ */
+ expand : function () {
+ this.set(COLLAPSED, false);
+
+ return this;
+ },
+
+ /**
+ * Outputs buffered messages to the console UI. This is typically called
+ * from a scheduled interval until the buffer is empty (referred to as the
+ * print loop). The number of buffered messages output to the Console is
+ * limited to the number provided as an argument. If no limit is passed,
+ * all buffered messages are rendered.
+ *
+ * @method printBuffer
+ * @param limit {Number} (optional) max number of buffered entries to write
+ * @chainable
+ */
+ printBuffer: function (limit) {
+ var messages = this.buffer,
+ debug = Y.config.debug,
+ entries = [],
+ consoleLimit= this.get('consoleLimit'),
+ newestOnTop = this.get('newestOnTop'),
+ anchor = newestOnTop ? this._body.get('firstChild') : null,
+ i;
+
+ if (messages.length > consoleLimit) {
+ messages.splice(0, messages.length - consoleLimit);
+ }
+
+ limit = Math.min(messages.length, (limit || messages.length));
+
+ // turn off logging system
+ Y.config.debug = false;
+
+ if (!this.get(PAUSED) && this.get('rendered')) {
+
+ for (i = 0; i < limit && messages.length; ++i) {
+ entries[i] = this._createEntryHTML(messages.shift());
+ }
+
+ if (!messages.length) {
+ this._cancelPrintLoop();
+ }
+
+ if (entries.length) {
+ if (newestOnTop) {
+ entries.reverse();
+ }
+
+ this._body.insertBefore(create(entries.join('')), anchor);
+
+ if (this.get('scrollIntoView')) {
+ this.scrollToLatest();
+ }
+
+ this._trimOldEntries();
+ }
+ }
+
+ // restore logging system
+ Y.config.debug = debug;
+
+ return this;
+ },
+
+
+ /**
+ * Constructor code. Set up the buffer and entry template, publish
+ * internal events, and subscribe to the configured logEvent.
+ *
+ * @method initializer
+ * @protected
+ */
+ initializer : function () {
+ this._evtCat = Y.stamp(this) + '|';
+
+ this.buffer = [];
+
+ this.get('logSource').on(this._evtCat +
+ this.get('logEvent'),Y.bind("_onLogEvent",this));
+
+ /**
+ * Transfers a received message to the print loop buffer. Default
+ * behavior defined in _defEntryFn.
+ *
+ * @event entry
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>message</dt>
+ * <dd>The message data normalized into an object literal (see _normalizeMessage)</dd>
+ * </dl>
+ * @preventable _defEntryFn
+ */
+ this.publish(ENTRY, { defaultFn: this._defEntryFn });
+
+ /**
+ * Triggers the reset behavior via the default logic in _defResetFn.
+ *
+ * @event reset
+ * @param event {Event.Facade} Event Facade object
+ * @preventable _defResetFn
+ */
+ this.publish(RESET, { defaultFn: this._defResetFn });
+
+ this.after('rendered', this._schedulePrint);
+ },
+
+ /**
+ * Tears down the instance, flushing event subscriptions and purging the UI.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor : function () {
+ var bb = this.get('boundingBox');
+
+ this._cancelPrintLoop();
+
+ this.get('logSource').detach(this._evtCat + '*');
+
+ Y.Event.purgeElement(bb, true);
+
+ bb.set('innerHTML','');
+ },
+
+ /**
+ * Generate the Console UI.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI : function () {
+ this._initHead();
+ this._initBody();
+ this._initFoot();
+
+ // Apply positioning to the bounding box if appropriate
+ var style = this.get('style');
+ if (style !== 'block') {
+ this.get('boundingBox').addClass('yui-'+style+'-console');
+ }
+ },
+
+ /**
+ * Sync the UI state to the current attribute state.
+ *
+ * @method syncUI
+ */
+ syncUI : function () {
+ this._uiUpdatePaused(this.get(PAUSED));
+ this._uiUpdateCollapsed(this.get(COLLAPSED));
+ this._uiSetHeight(this.get(HEIGHT));
+ },
+
+ /**
+ * Set up event listeners to wire up the UI to the internal state.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI : function () {
+ this.get(CONTENT_BOX).query('button.'+C_COLLAPSE).
+ on(CLICK,this._onCollapseClick,this);
+
+ this.get(CONTENT_BOX).query('input[type=checkbox].'+C_PAUSE).
+ on(CLICK,this._onPauseClick,this);
+
+ this.get(CONTENT_BOX).query('button.'+C_CLEAR).
+ on(CLICK,this._onClearClick,this);
+
+ // Attribute changes
+ this.after(this._evtCat + 'stringsChange',
+ this._afterStringsChange);
+ this.after(this._evtCat + 'pausedChange',
+ this._afterPausedChange);
+ this.after(this._evtCat + 'consoleLimitChange',
+ this._afterConsoleLimitChange);
+ this.after(this._evtCat + 'collapsedChange',
+ this._afterCollapsedChange);
+ },
+
+
+ /**
+ * Create the DOM structure for the header elements.
+ *
+ * @method _initHead
+ * @protected
+ */
+ _initHead : function () {
+ var cb = this.get(CONTENT_BOX),
+ info = merge(Console.CHROME_CLASSES, {
+ str_collapse : this.get('strings.collapse'),
+ str_title : this.get('strings.title')
+ });
+
+ this._head = create(substitute(Console.HEADER_TEMPLATE,info));
+
+ cb.insertBefore(this._head,cb.get('firstChild'));
+ },
+
+ /**
+ * Create the DOM structure for the console body—where messages are
+ * rendered.
+ *
+ * @method _initBody
+ * @protected
+ */
+ _initBody : function () {
+ this._body = create(substitute(
+ Console.BODY_TEMPLATE,
+ Console.CHROME_CLASSES));
+
+ this.get(CONTENT_BOX).appendChild(this._body);
+ },
+
+ /**
+ * Create the DOM structure for the footer elements.
+ *
+ * @method _initFoot
+ * @protected
+ */
+ _initFoot : function () {
+ var info = merge(Console.CHROME_CLASSES, {
+ id_guid : Y.guid(),
+ str_pause : this.get('strings.pause'),
+ str_clear : this.get('strings.clear')
+ });
+
+ this._foot = create(substitute(Console.FOOTER_TEMPLATE,info));
+
+ this.get(CONTENT_BOX).appendChild(this._foot);
+ },
+
+ /**
+ * Determine if incoming log messages are within the configured logLevel
+ * to be buffered for printing.
+ *
+ * @method _isInLogLevel
+ * @protected
+ */
+ _isInLogLevel : function (e) {
+ var cat = e.cat, lvl = this.get('logLevel');
+
+ if (lvl !== INFO) {
+ cat = cat || INFO;
+
+ if (isString(cat)) {
+ cat = cat.toLowerCase();
+ }
+
+ if ((cat === WARN && lvl === ERROR) ||
+ (cat === INFO && lvl !== INFO)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Create a log entry message from the inputs including the following keys:
+ * <ul>
+ * <li>time - this moment</li>
+ * <li>message - leg message</li>
+ * <li>category - logLevel or custom category for the message</li>
+ * <li>source - when provided, the widget or util calling Y.log</li>
+ * <li>sourceAndDetail - same as source but can include instance info</li>
+ * <li>localTime - readable version of time</li>
+ * <li>elapsedTime - ms since last entry</li>
+ * <li>totalTime - ms since Console was instantiated or reset</li>
+ * </ul>
+ *
+ * @method _normalizeMessage
+ * @param e {Event} custom event containing the log message
+ * @return Object the message object
+ * @protected
+ */
+ _normalizeMessage : function (e) {
+
+ var msg = e.msg,
+ cat = e.cat,
+ src = e.src,
+
+ m = {
+ time : new Date(),
+ message : msg,
+ category : cat || this.get('defaultCategory'),
+ sourceAndDetail : src || this.get('defaultSource'),
+ source : null,
+ localTime : null,
+ elapsedTime : null,
+ totalTime : null
+ };
+
+ // Extract m.source "Foo" from m.sourceAndDetail "Foo bar baz"
+ m.source = RE_INLINE_SOURCE.test(m.sourceAndDetail) ?
+ RegExp.$1 : m.sourceAndDetail;
+ m.localTime = m.time.toLocaleTimeString ?
+ m.time.toLocaleTimeString() : (m.time + '');
+ m.elapsedTime = m.time - this.get(LAST_TIME);
+ m.totalTime = m.time - this.get(START_TIME);
+
+ this._set(LAST_TIME,m.time);
+
+ return m;
+ },
+
+ /**
+ * Sets an interval for buffered messages to be output to the console.
+ *
+ * @method _schedulePrint
+ * @protected
+ */
+ _schedulePrint : function () {
+ if (!this._printLoop && !this.get(PAUSED) && this.get('rendered')) {
+ this._printLoop = Y.later(
+ this.get('printTimeout'),
+ this, this.printBuffer,
+ this.get('printLimit'), true);
+ }
+ },
+
+ /**
+ * Translates message meta into the markup for a console entry.
+ *
+ * @method _createEntryHTML
+ * @param m {Object} object literal containing normalized message metadata
+ * @return String
+ * @protected
+ */
+ _createEntryHTML : function (m) {
+ m = merge(
+ this._htmlEscapeMessage(m),
+ Console.ENTRY_CLASSES,
+ {
+ cat_class : this.getClassName(ENTRY,m.category),
+ src_class : this.getClassName(ENTRY,m.source)
+ });
+
+ return this.get('entryTemplate').replace(/\{(\w+)\}/g,
+ function (_,token) {
+ return token in m ? m[token] : '';
+ });
+ },
+
+ /**
+ * Scrolls to the most recent entry
+ *
+ * @method scrollToLatest
+ * @chainable
+ */
+ scrollToLatest : function () {
+ var scrollTop = this.get('newestOnTop') ?
+ 0 :
+ this._body.get('scrollHeight');
+
+ this._body.set('scrollTop', scrollTop);
+ },
+
+ /**
+ * Performs HTML escaping on strings in the message object.
+ *
+ * @method _htmlEscapeMessage
+ * @param m {Object} the normalized message object
+ * @return Object the message object with proper escapement
+ * @protected
+ */
+ _htmlEscapeMessage : function (m) {
+ m.message = this._encodeHTML(m.message);
+ m.source = this._encodeHTML(m.source);
+ m.sourceAndDetail = this._encodeHTML(m.sourceAndDetail);
+ m.category = this._encodeHTML(m.category);
+
+ return m;
+ },
+
+ /**
+ * Removes the oldest message entries from the UI to maintain the limit
+ * specified in the consoleLimit configuration.
+ *
+ * @method _trimOldEntries
+ * @protected
+ */
+ _trimOldEntries : function () {
+ // Turn off the logging system for the duration of this operation
+ // to prevent an infinite loop
+ Y.config.debug = false;
+
+ var bd = this._body,
+ limit = this.get('consoleLimit'),
+ debug = Y.config.debug,
+ entries,e,i,l;
+
+ if (bd) {
+ entries = bd.queryAll(DOT+C_ENTRY);
+ l = entries.size() - limit;
+
+ if (l > 0) {
+ if (this.get('newestOnTop')) {
+ i = limit;
+ l = entries.size();
+ } else {
+ i = 0;
+ }
+
+ this._body.setStyle('display','none');
+
+ for (;i < l; ++i) {
+ e = entries.item(i);
+ if (e) {
+ e.remove();
+ }
+ }
+
+ this._body.setStyle('display','');
+ }
+
+ }
+
+ Y.config.debug = debug;
+ },
+
+ /**
+ * Returns the input string with ampersands (&), <, and > encoded
+ * as HTML entities.
+ *
+ * @method _encodeHTML
+ * @param s {String} the raw string
+ * @return String the encoded string
+ * @protected
+ */
+ _encodeHTML : function (s) {
+ return isString(s) ?
+ s.replace(RE_AMP,ESC_AMP).
+ replace(RE_LT, ESC_LT).
+ replace(RE_GT, ESC_GT) :
+ s;
+ },
+
+ /**
+ * Clears the timeout for printing buffered messages.
+ *
+ * @method _cancelPrintLoop
+ * @protected
+ */
+ _cancelPrintLoop : function () {
+ if (this._printLoop) {
+ this._printLoop.cancel();
+ this._printLoop = null;
+ }
+ },
+
+ /**
+ * Validates input value for style attribute. Accepts only values 'inline',
+ * 'block', and 'separate'.
+ *
+ * @method _validateStyle
+ * @param style {String} the proposed value
+ * @return {Boolean} pass/fail
+ * @protected
+ */
+ _validateStyle : function (style) {
+ return style === 'inline' || style === 'block' || style === 'separate';
+ },
+
+ /**
+ * Event handler for clicking on the Pause checkbox to update the paused
+ * attribute.
+ *
+ * @method _onPauseClick
+ * @param e {Event} DOM event facade for the click event
+ * @protected
+ */
+ _onPauseClick : function (e) {
+ this.set(PAUSED,e.target.get(CHECKED));
+ },
+
+ /**
+ * Event handler for clicking on the Clear button. Pass-through to
+ * <code>this.clearConsole()</code>.
+ *
+ * @method _onClearClick
+ * @param e {Event} DOM event facade for the click event
+ * @protected
+ */
+ _onClearClick : function (e) {
+ this.clearConsole();
+ },
+
+ /**
+ * Event handler for clicking on the Collapse/Expand button. Sets the
+ * "collapsed" attribute accordingly.
+ *
+ * @method _onCollapseClick
+ * @param e {Event} DOM event facade for the click event
+ * @protected
+ */
+ _onCollapseClick : function (e) {
+ this.set(COLLAPSED, !this.get(COLLAPSED));
+ },
+
+
+ /**
+ * Setter method for logLevel attribute. Acceptable values are
+ * "error", "warn", and "info" (case
+ * insensitive). Other values are treated as "info".
+ *
+ * @method _setLogLevel
+ * @param v {String} the desired log level
+ * @return String One of Console.LOG_LEVEL_INFO, _WARN, or _ERROR
+ * @protected
+ */
+ _setLogLevel : function (v) {
+ if (isString(v)) {
+ v = v.toLowerCase();
+ }
+
+ return (v === WARN || v === ERROR) ? v : INFO;
+ },
+
+ /**
+ * Set the height of the Console container. Set the body height to the difference between the configured height and the calculated heights of the header and footer.
+ * Overrides Widget.prototype._uiSetHeight.
+ *
+ * @method _uiSetHeight
+ * @param v {String|Number} the new height
+ * @protected
+ */
+ _uiSetHeight : function (v) {
+ Console.superclass._uiSetHeight.apply(this,arguments);
+
+ if (this._head && this._foot) {
+ var h = this.get('boundingBox').get('offsetHeight') -
+ this._head.get('offsetHeight') -
+ this._foot.get('offsetHeight');
+
+ this._body.setStyle(HEIGHT,h+'px');
+ }
+ },
+
+ /**
+ * Updates the UI if changes are made to any of the strings in the strings
+ * attribute.
+ *
+ * @method _afterStringsChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterStringsChange : function (e) {
+ var prop = e.subAttrName ? e.subAttrName.split(DOT)[1] : null,
+ cb = this.get(CONTENT_BOX),
+ before = e.prevVal,
+ after = e.newVal;
+
+ if ((!prop || prop === TITLE) && before.title !== after.title) {
+ cb.queryAll(DOT+C_CONSOLE_TITLE).set(INNER_HTML, after.title);
+ }
+
+ if ((!prop || prop === PAUSE) && before.pause !== after.pause) {
+ cb.queryAll(DOT+C_PAUSE_LABEL).set(INNER_HTML, after.pause);
+ }
+
+ if ((!prop || prop === CLEAR) && before.clear !== after.clear) {
+ cb.queryAll(DOT+C_CLEAR).set('value',after.clear);
+ }
+ },
+
+ /**
+ * Updates the UI and schedules or cancels the print loop.
+ *
+ * @method _afterPausedChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterPausedChange : function (e) {
+ var paused = e.newVal;
+
+ if (e.src !== Y.Widget.SRC_UI) {
+ this._uiUpdatePaused(paused);
+ }
+
+ if (!paused) {
+ this._schedulePrint();
+ } else if (this._printLoop) {
+ this._cancelPrintLoop();
+ }
+ },
+
+ /**
+ * Checks or unchecks the paused checkbox
+ *
+ * @method _uiUpdatePaused
+ * @param on {Boolean} the new checked state
+ * @protected
+ */
+ _uiUpdatePaused : function (on) {
+ var node = this._foot.queryAll('input[type=checkbox].'+C_PAUSE);
+
+ if (node) {
+ node.set(CHECKED,on);
+ }
+ },
+
+ /**
+ * Calls this._trimOldEntries() in response to changes in the configured
+ * consoleLimit attribute.
+ *
+ * @method _afterConsoleLimitChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterConsoleLimitChange : function () {
+ this._trimOldEntries();
+ },
+
+
+ /**
+ * Updates the className of the contentBox, which should trigger CSS to
+ * hide or show the body and footer sections depending on the new value.
+ *
+ * @method _afterCollapsedChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterCollapsedChange : function (e) {
+ this._uiUpdateCollapsed(e.newVal);
+ },
+
+ /**
+ * Updates the UI to reflect the new Collapsed state
+ *
+ * @method _uiUpdateCollapsed
+ * @param v {Boolean} true for collapsed, false for expanded
+ * @protected
+ */
+ _uiUpdateCollapsed : function (v) {
+ var bb = this.get('boundingBox'),
+ button = bb.queryAll('button.'+C_COLLAPSE),
+ method = v ? 'addClass' : 'removeClass',
+ str = this.get('strings.'+(v ? 'expand' : 'collapse'));
+
+ bb[method](C_COLLAPSED);
+
+ if (button) {
+ button.set('innerHTML',str);
+ }
+
+ this._uiSetHeight(v ? this._head.get('offsetHeight'): this.get(HEIGHT));
+ },
+
+ /**
+ * Makes adjustments to the UI if needed when the Console is hidden or shown
+ *
+ * @method _afterVisibleChange
+ * @param e {Event} the visibleChange event
+ * @protected
+ */
+ _afterVisibleChange : function (e) {
+ Console.superclass._afterVisibleChange.apply(this,arguments);
+
+ this._uiUpdateFromHideShow(e.newVal);
+ },
+
+ /**
+ * Recalculates dimensions and updates appropriately when shown
+ *
+ * @method _uiUpdateFromHideShow
+ * @param v {Boolean} true for visible, false for hidden
+ * @protected
+ */
+ _uiUpdateFromHideShow : function (v) {
+ if (v) {
+ this._uiSetHeight(this.get(HEIGHT));
+ }
+ },
+
+ /**
+ * Responds to log events by normalizing qualifying messages and passing
+ * them along through the entry event for buffering etc.
+ *
+ * @method _onLogEvent
+ * @param msg {String} the log message
+ * @param cat {String} OPTIONAL the category or logLevel of the message
+ * @param src {String} OPTIONAL the source of the message (e.g. widget name)
+ * @protected
+ */
+ _onLogEvent : function (e) {
+
+ if (!this.get(DISABLED) && this._isInLogLevel(e)) {
+
+ var debug = Y.config.debug;
+
+ /* TODO: needed? */
+ Y.config.debug = false;
+
+ this.fire(ENTRY, {
+ message : this._normalizeMessage(e)
+ });
+
+ Y.config.debug = debug;
+ }
+ },
+
+ /**
+ * Clears the console, resets the startTime attribute, enables and
+ * unpauses the widget.
+ *
+ * @method _defResetFn
+ * @protected
+ */
+ _defResetFn : function () {
+ this.clearConsole();
+ this.set(START_TIME,new Date());
+ this.set(DISABLED,false);
+ this.set(PAUSED,false);
+ },
+
+ /**
+ * Buffers incoming message objects and schedules the printing.
+ *
+ * @method _defEntryFn
+ * @param e {Event} The Custom event carrying the message in its payload
+ * @protected
+ */
+ _defEntryFn : function (e) {
+ if (e.message) {
+ this.buffer.push(e.message);
+ this._schedulePrint();
+ }
+ }
+
+});
+
+Y.Console = Console;
+
+
+}, '3.0.0' ,{requires:['substitute','widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('console-filters', function(Y) {
+
+/**
+ * <p>Provides Plugin.ConsoleFilters plugin class.</p>
+ *
+ * <p>This plugin adds the ability to control which Console entries display by filtering on category and source. Two groups of checkboxes are added to the Console footer, one for categories and the other for sources. Only those messages that match a checked category or source are displayed.</p>
+ *
+ * @module console-filters
+ * @namespace Plugin
+ * @class ConsoleFilters
+ */
+
+// Some common strings and functions
+var getCN = Y.ClassNameManager.getClassName,
+ CONSOLE = 'console',
+ FILTERS = 'filters',
+ FILTER = 'filter',
+ CATEGORY = 'category',
+ SOURCE = 'source',
+ CATEGORY_DOT = 'category.',
+ SOURCE_DOT = 'source.',
+
+ HOST = 'host',
+ PARENT_NODE = 'parentNode',
+ CHECKED = 'checked',
+ DEF_VISIBILITY = 'defaultVisibility',
+
+ DOT = '.',
+ EMPTY = '',
+
+ C_BODY = DOT + Y.Console.CHROME_CLASSES.console_bd_class,
+ C_FOOT = DOT + Y.Console.CHROME_CLASSES.console_ft_class,
+
+ SEL_CHECK = 'input[type=checkbox].',
+
+ isString = Y.Lang.isString;
+
+function ConsoleFilters() {
+ ConsoleFilters.superclass.constructor.apply(this,arguments);
+}
+
+Y.mix(ConsoleFilters,{
+ /**
+ * Plugin name.
+ *
+ * @property ConsoleFilters.NAME
+ * @type String
+ * @static
+ * @default 'consoleFilters'
+ */
+ NAME : 'consoleFilters',
+
+ /**
+ * The namespace hung off the host object that this plugin will inhabit.
+ *
+ * @property ConsoleFilters.NS
+ * @type String
+ * @static
+ * @default 'filter'
+ */
+ NS : FILTER,
+
+ /**
+ * Markup template used to create the container for the category filters.
+ *
+ * @property ConsoleFilters.CATEGORIES_TEMPLATE
+ * @type String
+ * @static
+ */
+ CATEGORIES_TEMPLATE :
+ '<div class="{categories}"></div>',
+
+ /**
+ * Markup template used to create the container for the source filters.
+ *
+ * @property ConsoleFilters.SOURCES_TEMPLATE
+ * @type String
+ * @static
+ */
+ SOURCES_TEMPLATE :
+ '<div class="{sources}"></div>',
+
+ /**
+ * Markup template used to create the category and source filter checkboxes.
+ *
+ * @property ConsoleFilters.FILTER_TEMPLATE
+ * @type String
+ * @static
+ */
+ FILTER_TEMPLATE :
+ // IE8 and FF3 don't permit breaking _between_ nowrap elements. IE8
+ // doesn't understand (non spec) wbr tag, nor does it create text nodes
+ // for spaces in innerHTML strings. The thin-space entity suffices to
+ // create a breakable point.
+ '<label class="{filter_label}">'+
+ '<input type="checkbox" value="{filter_name}" '+
+ 'class="{filter} {filter_class}"> {filter_name}'+
+ '</label> ',
+
+ /**
+ * Classnames used by the templates when creating nodes.
+ *
+ * @property ConsoleFilters.CHROME_CLASSES
+ * @type Object
+ * @static
+ * @protected
+ */
+ CHROME_CLASSES : {
+ categories : getCN(CONSOLE,FILTERS,'categories'),
+ sources : getCN(CONSOLE,FILTERS,'sources'),
+ category : getCN(CONSOLE,FILTER,CATEGORY),
+ source : getCN(CONSOLE,FILTER,SOURCE),
+ filter : getCN(CONSOLE,FILTER),
+ filter_label : getCN(CONSOLE,FILTER,'label')
+ },
+
+ ATTRS : {
+ /**
+ * Default visibility applied to new categories and sources.
+ *
+ * @attribute defaultVisibility
+ * @type {Boolean}
+ * @default true
+ */
+ defaultVisibility : {
+ value : true,
+ validator : Y.Lang.isBoolean
+ },
+
+ /**
+ * <p>Map of entry categories to their visibility status. Update a
+ * particular category's visibility by setting the subattribute to true
+ * (visible) or false (hidden).</p>
+ *
+ * <p>For example, yconsole.filter.set('category.info', false) to hide
+ * log entries with the category/logLevel of 'info'.</p>
+ *
+ * <p>Similarly, yconsole.filter.get('category.warn') will return a
+ * boolean indicating whether that category is currently being included
+ * in the UI.</p>
+ *
+ * <p>Unlike the YUI instance configuration's logInclude and logExclude
+ * properties, filtered entries are only hidden from the UI, but
+ * can be made visible again.</p>
+ *
+ * @attribute category
+ * @type Object
+ */
+ category : {
+ value : {},
+ validator : function (v,k) {
+ return this._validateCategory(k,v);
+ }
+ },
+
+ /**
+ * <p>Map of entry sources to their visibility status. Update a
+ * particular sources's visibility by setting the subattribute to true
+ * (visible) or false (hidden).</p>
+ *
+ * <p>For example, yconsole.filter.set('sources.slider', false) to hide
+ * log entries originating from Y.Slider.</p>
+ *
+ * @attribute source
+ * @type Object
+ */
+ source : {
+ value : {},
+ validator : function (v,k) {
+ return this._validateSource(k,v);
+ }
+ },
+
+ /**
+ * Maximum number of entries to store in the message cache. Use this to
+ * limit the memory footprint in environments with heavy log usage.
+ * By default, there is no limit (Number.POSITIVE_INFINITY).
+ *
+ * @attribute cacheLimit
+ * @type {Number}
+ * @default Number.POSITIVE_INFINITY
+ */
+ cacheLimit : {
+ value : Number.POSITIVE_INFINITY,
+ setter : function (v) {
+ if (Y.Lang.isNumber(v)) {
+ this._cacheLimit = v;
+ return v;
+ } else {
+ return Y.Attribute.INVALID_VALUE;
+ }
+ }
+ }
+ }
+});
+
+Y.extend(ConsoleFilters, Y.Plugin.Base, {
+
+ /**
+ * Collection of all log messages passed through since the plugin's
+ * instantiation. This holds all messages regardless of filter status.
+ * Used as a single source of truth for repopulating the Console body when
+ * filters are changed.
+ *
+ * @property _entries
+ * @type Array
+ * @protected
+ */
+ _entries : null,
+
+ _cacheLimit : Number.POSITIVE_INFINITY,
+
+ /**
+ * The container node created to house the category filters.
+ *
+ * @property _categories
+ * @type Node
+ * @protected
+ */
+ _categories : null,
+
+ /**
+ * The container node created to house the source filters.
+ *
+ * @property _sources
+ * @type Node
+ * @protected
+ */
+ _sources : null,
+
+ /**
+ * Initialize entries collection and attach listeners to host events and
+ * methods.
+ *
+ * @method initializer
+ * @protected
+ */
+ initializer : function () {
+ this._entries = [];
+
+ this.get(HOST).on("entry", this._onEntry, this);
+
+ this.doAfter("renderUI", this.renderUI);
+ this.doAfter("syncUI", this.syncUI);
+ this.doAfter("bindUI", this.bindUI);
+
+ this.doAfter("clearConsole", this._afterClearConsole);
+
+ if (this.get(HOST).get('rendered')) {
+ this.renderUI();
+ this.syncUI();
+ this.bindUI();
+ }
+
+ this.after("cacheLimitChange", this._afterCacheLimitChange);
+ },
+
+ /**
+ * Removes the plugin UI and unwires events.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor : function () {
+ //TODO: grab last {consoleLimit} entries and update the console with
+ //them (no filtering)
+ this._entries = [];
+
+ if (this._categories) {
+ this._categories.get(PARENT_NODE).removeChild(this._categories);
+ }
+ if (this._sources) {
+ this._sources.get(PARENT_NODE).removeChild(this._sources);
+ }
+ },
+
+ /**
+ * Adds the category and source filter sections to the Console footer.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI : function () {
+ var foot = this.get(HOST).get('contentBox').query(C_FOOT),
+ html;
+
+ if (foot) {
+ html = Y.substitute(
+ ConsoleFilters.CATEGORIES_TEMPLATE,
+ ConsoleFilters.CHROME_CLASSES);
+
+ this._categories = foot.appendChild(Y.Node.create(html));
+
+ html = Y.substitute(
+ ConsoleFilters.SOURCES_TEMPLATE,
+ ConsoleFilters.CHROME_CLASSES);
+
+ this._sources = foot.appendChild(Y.Node.create(html));
+ }
+ },
+
+ /**
+ * Binds to checkbox click events and internal attribute change events to
+ * maintain the UI state.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI : function () {
+ this._categories.on('click', Y.bind(this._onCategoryCheckboxClick, this));
+
+ this._sources.on('click', Y.bind(this._onSourceCheckboxClick, this));
+
+ this.after('categoryChange',this._afterCategoryChange);
+ this.after('sourceChange', this._afterSourceChange);
+ },
+
+ /**
+ * Updates the UI to be in accordance with the current state of the plugin.
+ *
+ * @method syncUI
+ */
+ syncUI : function () {
+ Y.each(this.get(CATEGORY), function (v, k) {
+ this._uiSetCheckbox(CATEGORY, k, v);
+ }, this);
+
+ Y.each(this.get(SOURCE), function (v, k) {
+ this._uiSetCheckbox(SOURCE, k, v);
+ }, this);
+
+ this.refreshConsole();
+ },
+
+ /**
+ * Ensures a filter is set up for any new categories or sources and
+ * collects the messages in _entries. If the message is stamped with a
+ * category or source that is currently being filtered out, the message
+ * will not pass to the Console's print buffer.
+ *
+ * @method _onEntry
+ * @param e {Event} the custom event object
+ * @protected
+ */
+ _onEntry : function (e) {
+ this._entries.push(e.message);
+
+ var cat = CATEGORY_DOT + e.message.category,
+ src = SOURCE_DOT + e.message.source,
+ cat_filter = this.get(cat),
+ src_filter = this.get(src),
+ overLimit = this._entries.length - this._cacheLimit,
+ visible;
+
+ if (overLimit > 0) {
+ this._entries.splice(0, overLimit);
+ }
+
+ if (cat_filter === undefined) {
+ visible = this.get(DEF_VISIBILITY);
+ this.set(cat, visible);
+ cat_filter = visible;
+ }
+
+ if (src_filter === undefined) {
+ visible = this.get(DEF_VISIBILITY);
+ this.set(src, visible);
+ src_filter = visible;
+ }
+
+ if (!cat_filter || !src_filter) {
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Flushes the cached entries after a call to the Console's clearConsole().
+ *
+ * @method _afterClearConsole
+ * @protected
+ */
+ _afterClearConsole : function () {
+ this._entries = [];
+ },
+
+ /**
+ * Triggers the Console to update if a known category filter
+ * changes value (e.g. visible => hidden). Updates the appropriate
+ * checkbox's checked state if necessary.
+ *
+ * @method _afterCategoryChange
+ * @param e {Event} the attribute change event object
+ * @protected
+ */
+ _afterCategoryChange : function (e) {
+ var cat = e.subAttrName.replace(/category\./, EMPTY),
+ before = e.prevVal,
+ after = e.newVal;
+
+ // Don't update the console for new categories
+ if (!cat || before[cat] !== undefined) {
+ this.refreshConsole();
+
+ this._filterBuffer();
+ }
+
+ if (cat && !e.fromUI) {
+ this._uiSetCheckbox(CATEGORY, cat, after[cat]);
+ }
+ },
+
+ /**
+ * Triggers the Console to update if a known source filter
+ * changes value (e.g. visible => hidden). Updates the appropriate
+ * checkbox's checked state if necessary.
+ *
+ * @method _afterSourceChange
+ * @param e {Event} the attribute change event object
+ * @protected
+ */
+ _afterSourceChange : function (e) {
+ var src = e.subAttrName.replace(/source\./, EMPTY),
+ before = e.prevVal,
+ after = e.newVal;
+
+ // Don't update the console for new sources
+ if (!src || before[src] !== undefined) {
+ this.refreshConsole();
+
+ this._filterBuffer();
+ }
+
+ if (src && !e.fromUI) {
+ this._uiSetCheckbox(SOURCE, src, after[src]);
+ }
+ },
+
+ /**
+ * Flushes the Console's print buffer of any entries that have a category
+ * or source that is currently being excluded.
+ *
+ * @method _filterBuffer
+ * @protected
+ */
+ _filterBuffer : function () {
+ var cats = this.get(CATEGORY),
+ srcs = this.get(SOURCE),
+ buffer = this.get(HOST).buffer,
+ start = null,
+ i;
+
+ for (i = buffer.length - 1; i >= 0; --i) {
+ if (!cats[buffer[i].category] || !srcs[buffer[i].source]) {
+ start = start || i;
+ } else if (start) {
+ buffer.splice(i,(start - i));
+ start = null;
+ }
+ }
+ if (start) {
+ buffer.splice(0,start + 1);
+ }
+ },
+
+ /**
+ * Trims the cache of entries to the appropriate new length.
+ *
+ * @method _afterCacheLimitChange
+ * @param e {Event} the attribute change event object
+ * @protected
+ */
+ _afterCacheLimitChange : function (e) {
+ if (isFinite(e.newVal)) {
+ var delta = this._entries.length - e.newVal;
+
+ if (delta > 0) {
+ this._entries.splice(0,delta);
+ }
+ }
+ },
+
+ /**
+ * Repopulates the Console with entries appropriate to the current filter
+ * settings.
+ *
+ * @method refreshConsole
+ */
+ refreshConsole : function () {
+ var entries = this._entries,
+ host = this.get(HOST),
+ body = host.get('contentBox').query(C_BODY),
+ remaining = host.get('consoleLimit'),
+ cats = this.get(CATEGORY),
+ srcs = this.get(SOURCE),
+ buffer = [],
+ i,e;
+
+ if (body) {
+ host._cancelPrintLoop();
+
+ // Evaluate all entries from latest to oldest
+ for (i = entries.length - 1; i >= 0 && remaining >= 0; --i) {
+ e = entries[i];
+ if (cats[e.category] && srcs[e.source]) {
+ buffer.unshift(e);
+ --remaining;
+ }
+ }
+
+ body.set('innerHTML',EMPTY);
+ host.buffer = buffer;
+ host.printBuffer();
+ }
+ },
+
+ /**
+ * Updates the checked property of a filter checkbox of the specified type.
+ * If no checkbox is found for the input params, one is created.
+ *
+ * @method _uiSetCheckbox
+ * @param type {String} 'category' or 'source'
+ * @param item {String} the name of the filter (e.g. 'info', 'event')
+ * @param checked {Boolean} value to set the checkbox's checked property
+ * @protected
+ */
+ _uiSetCheckbox : function (type, item, checked) {
+ if (type && item) {
+ var container = type === CATEGORY ?
+ this._categories :
+ this._sources,
+ sel = SEL_CHECK + getCN(CONSOLE,FILTER,item),
+ checkbox = container.query(sel),
+ host;
+
+ if (!checkbox) {
+ host = this.get(HOST);
+
+ this._createCheckbox(container, item);
+
+ checkbox = container.query(sel);
+
+ host._uiSetHeight(host.get('height'));
+ }
+
+ checkbox.set(CHECKED, checked);
+ }
+ },
+
+ /**
+ * Passes checkbox clicks on to the category attribute.
+ *
+ * @method _onCategoryCheckboxClick
+ * @param e {Event} the DOM event
+ * @protected
+ */
+ _onCategoryCheckboxClick : function (e) {
+ var t = e.target, cat;
+
+ if (t.hasClass(ConsoleFilters.CHROME_CLASSES.filter)) {
+ cat = t.get('value');
+ if (cat && cat in this.get(CATEGORY)) {
+ this.set(CATEGORY_DOT + cat, t.get(CHECKED), { fromUI: true });
+ }
+ }
+ },
+
+ /**
+ * Passes checkbox clicks on to the source attribute.
+ *
+ * @method _onSourceCheckboxClick
+ * @param e {Event} the DOM event
+ * @protected
+ */
+ _onSourceCheckboxClick : function (e) {
+ var t = e.target, src;
+
+ if (t.hasClass(ConsoleFilters.CHROME_CLASSES.filter)) {
+ src = t.get('value');
+ if (src && src in this.get(SOURCE)) {
+ this.set(SOURCE_DOT + src, t.get(CHECKED), { fromUI: true });
+ }
+ }
+ },
+
+ /**
+ * Hides any number of categories from the UI. Convenience method for
+ * myConsole.filter.set('category.foo', false); set('category.bar', false);
+ * and so on.
+ *
+ * @method hideCategory
+ * @param cat* {String} 1..n categories to filter out of the UI
+ */
+ hideCategory : function (cat, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(CATEGORY_DOT + cat, false);
+ }
+ },
+
+ /**
+ * Shows any number of categories in the UI. Convenience method for
+ * myConsole.filter.set('category.foo', true); set('category.bar', true);
+ * and so on.
+ *
+ * @method showCategory
+ * @param cat* {String} 1..n categories to allow to display in the UI
+ */
+ showCategory : function (cat, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(CATEGORY_DOT + cat, true);
+ }
+ },
+
+ /**
+ * Hides any number of sources from the UI. Convenience method for
+ * myConsole.filter.set('source.foo', false); set('source.bar', false);
+ * and so on.
+ *
+ * @method hideSource
+ * @param src* {String} 1..n sources to filter out of the UI
+ */
+ hideSource : function (src, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(SOURCE_DOT + src, false);
+ }
+ },
+
+ /**
+ * Shows any number of sources in the UI. Convenience method for
+ * myConsole.filter.set('source.foo', true); set('source.bar', true);
+ * and so on.
+ *
+ * @method showSource
+ * @param src* {String} 1..n sources to allow to display in the UI
+ */
+ showSource : function (src, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(SOURCE_DOT + src, true);
+ }
+ },
+
+ /**
+ * Creates a checkbox and label from the ConsoleFilters.FILTER_TEMPLATE for
+ * the provided type and name. The checkbox and label are appended to the
+ * container node passes as the first arg.
+ *
+ * @method _createCheckbox
+ * @param container {Node} the parentNode of the new checkbox and label
+ * @param name {String} the identifier of the filter
+ * @protected
+ */
+ _createCheckbox : function (container, name) {
+ var info = Y.merge(ConsoleFilters.CHROME_CLASSES, {
+ filter_name : name,
+ filter_class : getCN(CONSOLE, FILTER, name)
+ }),
+ node = Y.Node.create(
+ Y.substitute(ConsoleFilters.FILTER_TEMPLATE, info));
+
+ container.appendChild(node);
+ },
+
+ /**
+ * Validates category updates are objects and the subattribute is not too
+ * deep.
+ *
+ * @method _validateCategory
+ * @param cat {String} the new category:visibility map
+ * @param v {String} the subattribute path updated
+ * @return Boolean
+ * @protected
+ */
+ _validateCategory : function (cat, v) {
+ return Y.Lang.isObject(v,true) && cat.split(/\./).length < 3;
+ },
+
+ /**
+ * Validates source updates are objects and the subattribute is not too
+ * deep.
+ *
+ * @method _validateSource
+ * @param cat {String} the new source:visibility map
+ * @param v {String} the subattribute path updated
+ * @return Boolean
+ * @protected
+ */
+ _validateSource : function (src, v) {
+ return Y.Lang.isObject(v,true) && src.split(/\./).length < 3;
+ }
+
+});
+
+Y.namespace('Plugin').ConsoleFilters = ConsoleFilters;
+
+
+}, '3.0.0' ,{requires:['console','plugin']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("console-filters",function(C){var Q=C.ClassNameManager.getClassName,G="console",B="filters",L="filter",F="category",D="source",E="category.",M="source.",P="host",H="parentNode",R="checked",O="defaultVisibility",N=".",T="",S=N+C.Console.CHROME_CLASSES.console_bd_class,I=N+C.Console.CHROME_CLASSES.console_ft_class,A="input[type=checkbox].",J=C.Lang.isString;function K(){K.superclass.constructor.apply(this,arguments);}C.mix(K,{NAME:"consoleFilters",NS:L,CATEGORIES_TEMPLATE:'<div class="{categories}"></div>',SOURCES_TEMPLATE:'<div class="{sources}"></div>',FILTER_TEMPLATE:'<label class="{filter_label}">'+'<input type="checkbox" value="{filter_name}" '+'class="{filter} {filter_class}"> {filter_name}'+"</label> ",CHROME_CLASSES:{categories:Q(G,B,"categories"),sources:Q(G,B,"sources"),category:Q(G,L,F),source:Q(G,L,D),filter:Q(G,L),filter_label:Q(G,L,"label")},ATTRS:{defaultVisibility:{value:true,validator:C.Lang.isBoolean},category:{value:{},validator:function(V,U){return this._validateCategory(U,V);}},source:{value:{},validator:function(V,U){return this._validateSource(U,V);}},cacheLimit:{value:Number.POSITIVE_INFINITY,setter:function(U){if(C.Lang.isNumber(U)){this._cacheLimit=U;return U;}else{return C.Attribute.INVALID_VALUE;}}}}});C.extend(K,C.Plugin.Base,{_entries:null,_cacheLimit:Number.POSITIVE_INFINITY,_categories:null,_sources:null,initializer:function(){this._entries=[];this.get(P).on("entry",this._onEntry,this);this.doAfter("renderUI",this.renderUI);this.doAfter("syncUI",this.syncUI);this.doAfter("bindUI",this.bindUI);this.doAfter("clearConsole",this._afterClearConsole);if(this.get(P).get("rendered")){this.renderUI();this.syncUI();this.bindUI();}this.after("cacheLimitChange",this._afterCacheLimitChange);},destructor:function(){this._entries=[];if(this._categories){this._categories.get(H).removeChild(this._categories);}if(this._sources){this._sources.get(H).removeChild(this._sources);}},renderUI:function(){var V=this.get(P).get("contentBox").query(I),U;if(V){U=C.substitute(K.CATEGORIES_TEMPLATE,K.CHROME_CLASSES);this._categories=V.appendChild(C.Node.create(U));U=C.substitute(K.SOURCES_TEMPLATE,K.CHROME_CLASSES);this._sources=V.appendChild(C.Node.create(U));}},bindUI:function(){this._categories.on("click",C.bind(this._onCategoryCheckboxClick,this));this._sources.on("click",C.bind(this._onSourceCheckboxClick,this));this.after("categoryChange",this._afterCategoryChange);this.after("sourceChange",this._afterSourceChange);},syncUI:function(){C.each(this.get(F),function(V,U){this._uiSetCheckbox(F,U,V);},this);C.each(this.get(D),function(V,U){this._uiSetCheckbox(D,U,V);},this);this.refreshConsole();},_onEntry:function(X){this._entries.push(X.message);var U=E+X.message.category,Z=M+X.message.source,V=this.get(U),a=this.get(Z),W=this._entries.length-this._cacheLimit,Y;if(W>0){this._entries.splice(0,W);}if(V===undefined){Y=this.get(O);this.set(U,Y);V=Y;}if(a===undefined){Y=this.get(O);this.set(Z,Y);a=Y;}if(!V||!a){X.preventDefault();}},_afterClearConsole:function(){this._entries=[];},_afterCategoryChange:function(W){var U=W.subAttrName.replace(/category\./,T),V=W.prevVal,X=W.newVal;if(!U||V[U]!==undefined){this.refreshConsole();this._filterBuffer();}if(U&&!W.fromUI){this._uiSetCheckbox(F,U,X[U]);}},_afterSourceChange:function(V){var X=V.subAttrName.replace(/source\./,T),U=V.prevVal,W=V.newVal;if(!X||U[X]!==undefined){this.refreshConsole();this._filterBuffer();}if(X&&!V.fromUI){this._uiSetCheckbox(D,X,W[X]);}},_filterBuffer:function(){var V=this.get(F),X=this.get(D),U=this.get(P).buffer,Y=null,W;for(W=U.length-1;W>=0;--W){if(!V[U[W].category]||!X[U[W].source]){Y=Y||W;}else{if(Y){U.splice(W,(Y-W));Y=null;}}}if(Y){U.splice(0,Y+1);}},_afterCacheLimitChange:function(U){if(isFinite(U.newVal)){var V=this._entries.length-U.newVal;if(V>0){this._entries.splice(0,V);}}},refreshConsole:function(){var Y=this._entries,c=this.get(P),Z=c.get("contentBox").query(S),V=c.get("consoleLimit"),b=this.get(F),U=this.get(D),W=[],X,a;if(Z){c._cancelPrintLoop();for(X=Y.length-1;X>=0&&V>=0;--X){a=Y[X];if(b[a.category]&&U[a.source]){W.unshift(a);--V;}}Z.set("innerHTML",T);c.buffer=W;c.printBuffer();}},_uiSetCheckbox:function(V,Y,X){if(V&&Y){var U=V===F?this._categories:this._sources,a=A+Q(G,L,Y),Z=U.query(a),W;if(!Z){W=this.get(P);this._createCheckbox(U,Y);Z=U.query(a);W._uiSetHeight(W.get("height"));}Z.set(R,X);}},_onCategoryCheckboxClick:function(W){var V=W.target,U;if(V.hasClass(K.CHROME_CLASSES.filter)){U=V.get("value");if(U&&U in this.get(F)){this.set(E+U,V.get(R),{fromUI:true});}}},_onSourceCheckboxClick:function(V){var U=V.target,W;if(U.hasClass(K.CHROME_CLASSES.filter)){W=U.get("value");if(W&&W in this.get(D)){this.set(M+W,U.get(R),{fromUI:true});}}},hideCategory:function(V,U){if(J(U)){C.Array.each(arguments,arguments.callee,this);}else{this.set(E+V,false);}},showCategory:function(V,U){if(J(U)){C.Array.each(arguments,arguments.callee,this);}else{this.set(E+V,true);}},hideSource:function(V,U){if(J(U)){C.Array.each(arguments,arguments.callee,this);}else{this.set(M+V,false);}},showSource:function(V,U){if(J(U)){C.Array.each(arguments,arguments.callee,this);}else{this.set(M+V,true);}},_createCheckbox:function(U,V){var X=C.merge(K.CHROME_CLASSES,{filter_name:V,filter_class:Q(G,L,V)}),W=C.Node.create(C.substitute(K.FILTER_TEMPLATE,X));U.appendChild(W);},_validateCategory:function(U,V){return C.Lang.isObject(V,true)&&U.split(/\./).length<3;},_validateSource:function(V,U){return C.Lang.isObject(U,true)&&V.split(/\./).length<3;}});C.namespace("Plugin").ConsoleFilters=K;},"3.0.0",{requires:["console","plugin"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('console-filters', function(Y) {
+
+/**
+ * <p>Provides Plugin.ConsoleFilters plugin class.</p>
+ *
+ * <p>This plugin adds the ability to control which Console entries display by filtering on category and source. Two groups of checkboxes are added to the Console footer, one for categories and the other for sources. Only those messages that match a checked category or source are displayed.</p>
+ *
+ * @module console-filters
+ * @namespace Plugin
+ * @class ConsoleFilters
+ */
+
+// Some common strings and functions
+var getCN = Y.ClassNameManager.getClassName,
+ CONSOLE = 'console',
+ FILTERS = 'filters',
+ FILTER = 'filter',
+ CATEGORY = 'category',
+ SOURCE = 'source',
+ CATEGORY_DOT = 'category.',
+ SOURCE_DOT = 'source.',
+
+ HOST = 'host',
+ PARENT_NODE = 'parentNode',
+ CHECKED = 'checked',
+ DEF_VISIBILITY = 'defaultVisibility',
+
+ DOT = '.',
+ EMPTY = '',
+
+ C_BODY = DOT + Y.Console.CHROME_CLASSES.console_bd_class,
+ C_FOOT = DOT + Y.Console.CHROME_CLASSES.console_ft_class,
+
+ SEL_CHECK = 'input[type=checkbox].',
+
+ isString = Y.Lang.isString;
+
+function ConsoleFilters() {
+ ConsoleFilters.superclass.constructor.apply(this,arguments);
+}
+
+Y.mix(ConsoleFilters,{
+ /**
+ * Plugin name.
+ *
+ * @property ConsoleFilters.NAME
+ * @type String
+ * @static
+ * @default 'consoleFilters'
+ */
+ NAME : 'consoleFilters',
+
+ /**
+ * The namespace hung off the host object that this plugin will inhabit.
+ *
+ * @property ConsoleFilters.NS
+ * @type String
+ * @static
+ * @default 'filter'
+ */
+ NS : FILTER,
+
+ /**
+ * Markup template used to create the container for the category filters.
+ *
+ * @property ConsoleFilters.CATEGORIES_TEMPLATE
+ * @type String
+ * @static
+ */
+ CATEGORIES_TEMPLATE :
+ '<div class="{categories}"></div>',
+
+ /**
+ * Markup template used to create the container for the source filters.
+ *
+ * @property ConsoleFilters.SOURCES_TEMPLATE
+ * @type String
+ * @static
+ */
+ SOURCES_TEMPLATE :
+ '<div class="{sources}"></div>',
+
+ /**
+ * Markup template used to create the category and source filter checkboxes.
+ *
+ * @property ConsoleFilters.FILTER_TEMPLATE
+ * @type String
+ * @static
+ */
+ FILTER_TEMPLATE :
+ // IE8 and FF3 don't permit breaking _between_ nowrap elements. IE8
+ // doesn't understand (non spec) wbr tag, nor does it create text nodes
+ // for spaces in innerHTML strings. The thin-space entity suffices to
+ // create a breakable point.
+ '<label class="{filter_label}">'+
+ '<input type="checkbox" value="{filter_name}" '+
+ 'class="{filter} {filter_class}"> {filter_name}'+
+ '</label> ',
+
+ /**
+ * Classnames used by the templates when creating nodes.
+ *
+ * @property ConsoleFilters.CHROME_CLASSES
+ * @type Object
+ * @static
+ * @protected
+ */
+ CHROME_CLASSES : {
+ categories : getCN(CONSOLE,FILTERS,'categories'),
+ sources : getCN(CONSOLE,FILTERS,'sources'),
+ category : getCN(CONSOLE,FILTER,CATEGORY),
+ source : getCN(CONSOLE,FILTER,SOURCE),
+ filter : getCN(CONSOLE,FILTER),
+ filter_label : getCN(CONSOLE,FILTER,'label')
+ },
+
+ ATTRS : {
+ /**
+ * Default visibility applied to new categories and sources.
+ *
+ * @attribute defaultVisibility
+ * @type {Boolean}
+ * @default true
+ */
+ defaultVisibility : {
+ value : true,
+ validator : Y.Lang.isBoolean
+ },
+
+ /**
+ * <p>Map of entry categories to their visibility status. Update a
+ * particular category's visibility by setting the subattribute to true
+ * (visible) or false (hidden).</p>
+ *
+ * <p>For example, yconsole.filter.set('category.info', false) to hide
+ * log entries with the category/logLevel of 'info'.</p>
+ *
+ * <p>Similarly, yconsole.filter.get('category.warn') will return a
+ * boolean indicating whether that category is currently being included
+ * in the UI.</p>
+ *
+ * <p>Unlike the YUI instance configuration's logInclude and logExclude
+ * properties, filtered entries are only hidden from the UI, but
+ * can be made visible again.</p>
+ *
+ * @attribute category
+ * @type Object
+ */
+ category : {
+ value : {},
+ validator : function (v,k) {
+ return this._validateCategory(k,v);
+ }
+ },
+
+ /**
+ * <p>Map of entry sources to their visibility status. Update a
+ * particular sources's visibility by setting the subattribute to true
+ * (visible) or false (hidden).</p>
+ *
+ * <p>For example, yconsole.filter.set('sources.slider', false) to hide
+ * log entries originating from Y.Slider.</p>
+ *
+ * @attribute source
+ * @type Object
+ */
+ source : {
+ value : {},
+ validator : function (v,k) {
+ return this._validateSource(k,v);
+ }
+ },
+
+ /**
+ * Maximum number of entries to store in the message cache. Use this to
+ * limit the memory footprint in environments with heavy log usage.
+ * By default, there is no limit (Number.POSITIVE_INFINITY).
+ *
+ * @attribute cacheLimit
+ * @type {Number}
+ * @default Number.POSITIVE_INFINITY
+ */
+ cacheLimit : {
+ value : Number.POSITIVE_INFINITY,
+ setter : function (v) {
+ if (Y.Lang.isNumber(v)) {
+ this._cacheLimit = v;
+ return v;
+ } else {
+ return Y.Attribute.INVALID_VALUE;
+ }
+ }
+ }
+ }
+});
+
+Y.extend(ConsoleFilters, Y.Plugin.Base, {
+
+ /**
+ * Collection of all log messages passed through since the plugin's
+ * instantiation. This holds all messages regardless of filter status.
+ * Used as a single source of truth for repopulating the Console body when
+ * filters are changed.
+ *
+ * @property _entries
+ * @type Array
+ * @protected
+ */
+ _entries : null,
+
+ _cacheLimit : Number.POSITIVE_INFINITY,
+
+ /**
+ * The container node created to house the category filters.
+ *
+ * @property _categories
+ * @type Node
+ * @protected
+ */
+ _categories : null,
+
+ /**
+ * The container node created to house the source filters.
+ *
+ * @property _sources
+ * @type Node
+ * @protected
+ */
+ _sources : null,
+
+ /**
+ * Initialize entries collection and attach listeners to host events and
+ * methods.
+ *
+ * @method initializer
+ * @protected
+ */
+ initializer : function () {
+ this._entries = [];
+
+ this.get(HOST).on("entry", this._onEntry, this);
+
+ this.doAfter("renderUI", this.renderUI);
+ this.doAfter("syncUI", this.syncUI);
+ this.doAfter("bindUI", this.bindUI);
+
+ this.doAfter("clearConsole", this._afterClearConsole);
+
+ if (this.get(HOST).get('rendered')) {
+ this.renderUI();
+ this.syncUI();
+ this.bindUI();
+ }
+
+ this.after("cacheLimitChange", this._afterCacheLimitChange);
+ },
+
+ /**
+ * Removes the plugin UI and unwires events.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor : function () {
+ //TODO: grab last {consoleLimit} entries and update the console with
+ //them (no filtering)
+ this._entries = [];
+
+ if (this._categories) {
+ this._categories.get(PARENT_NODE).removeChild(this._categories);
+ }
+ if (this._sources) {
+ this._sources.get(PARENT_NODE).removeChild(this._sources);
+ }
+ },
+
+ /**
+ * Adds the category and source filter sections to the Console footer.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI : function () {
+ var foot = this.get(HOST).get('contentBox').query(C_FOOT),
+ html;
+
+ if (foot) {
+ html = Y.substitute(
+ ConsoleFilters.CATEGORIES_TEMPLATE,
+ ConsoleFilters.CHROME_CLASSES);
+
+ this._categories = foot.appendChild(Y.Node.create(html));
+
+ html = Y.substitute(
+ ConsoleFilters.SOURCES_TEMPLATE,
+ ConsoleFilters.CHROME_CLASSES);
+
+ this._sources = foot.appendChild(Y.Node.create(html));
+ }
+ },
+
+ /**
+ * Binds to checkbox click events and internal attribute change events to
+ * maintain the UI state.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI : function () {
+ this._categories.on('click', Y.bind(this._onCategoryCheckboxClick, this));
+
+ this._sources.on('click', Y.bind(this._onSourceCheckboxClick, this));
+
+ this.after('categoryChange',this._afterCategoryChange);
+ this.after('sourceChange', this._afterSourceChange);
+ },
+
+ /**
+ * Updates the UI to be in accordance with the current state of the plugin.
+ *
+ * @method syncUI
+ */
+ syncUI : function () {
+ Y.each(this.get(CATEGORY), function (v, k) {
+ this._uiSetCheckbox(CATEGORY, k, v);
+ }, this);
+
+ Y.each(this.get(SOURCE), function (v, k) {
+ this._uiSetCheckbox(SOURCE, k, v);
+ }, this);
+
+ this.refreshConsole();
+ },
+
+ /**
+ * Ensures a filter is set up for any new categories or sources and
+ * collects the messages in _entries. If the message is stamped with a
+ * category or source that is currently being filtered out, the message
+ * will not pass to the Console's print buffer.
+ *
+ * @method _onEntry
+ * @param e {Event} the custom event object
+ * @protected
+ */
+ _onEntry : function (e) {
+ this._entries.push(e.message);
+
+ var cat = CATEGORY_DOT + e.message.category,
+ src = SOURCE_DOT + e.message.source,
+ cat_filter = this.get(cat),
+ src_filter = this.get(src),
+ overLimit = this._entries.length - this._cacheLimit,
+ visible;
+
+ if (overLimit > 0) {
+ this._entries.splice(0, overLimit);
+ }
+
+ if (cat_filter === undefined) {
+ visible = this.get(DEF_VISIBILITY);
+ this.set(cat, visible);
+ cat_filter = visible;
+ }
+
+ if (src_filter === undefined) {
+ visible = this.get(DEF_VISIBILITY);
+ this.set(src, visible);
+ src_filter = visible;
+ }
+
+ if (!cat_filter || !src_filter) {
+ e.preventDefault();
+ }
+ },
+
+ /**
+ * Flushes the cached entries after a call to the Console's clearConsole().
+ *
+ * @method _afterClearConsole
+ * @protected
+ */
+ _afterClearConsole : function () {
+ this._entries = [];
+ },
+
+ /**
+ * Triggers the Console to update if a known category filter
+ * changes value (e.g. visible => hidden). Updates the appropriate
+ * checkbox's checked state if necessary.
+ *
+ * @method _afterCategoryChange
+ * @param e {Event} the attribute change event object
+ * @protected
+ */
+ _afterCategoryChange : function (e) {
+ var cat = e.subAttrName.replace(/category\./, EMPTY),
+ before = e.prevVal,
+ after = e.newVal;
+
+ // Don't update the console for new categories
+ if (!cat || before[cat] !== undefined) {
+ this.refreshConsole();
+
+ this._filterBuffer();
+ }
+
+ if (cat && !e.fromUI) {
+ this._uiSetCheckbox(CATEGORY, cat, after[cat]);
+ }
+ },
+
+ /**
+ * Triggers the Console to update if a known source filter
+ * changes value (e.g. visible => hidden). Updates the appropriate
+ * checkbox's checked state if necessary.
+ *
+ * @method _afterSourceChange
+ * @param e {Event} the attribute change event object
+ * @protected
+ */
+ _afterSourceChange : function (e) {
+ var src = e.subAttrName.replace(/source\./, EMPTY),
+ before = e.prevVal,
+ after = e.newVal;
+
+ // Don't update the console for new sources
+ if (!src || before[src] !== undefined) {
+ this.refreshConsole();
+
+ this._filterBuffer();
+ }
+
+ if (src && !e.fromUI) {
+ this._uiSetCheckbox(SOURCE, src, after[src]);
+ }
+ },
+
+ /**
+ * Flushes the Console's print buffer of any entries that have a category
+ * or source that is currently being excluded.
+ *
+ * @method _filterBuffer
+ * @protected
+ */
+ _filterBuffer : function () {
+ var cats = this.get(CATEGORY),
+ srcs = this.get(SOURCE),
+ buffer = this.get(HOST).buffer,
+ start = null,
+ i;
+
+ for (i = buffer.length - 1; i >= 0; --i) {
+ if (!cats[buffer[i].category] || !srcs[buffer[i].source]) {
+ start = start || i;
+ } else if (start) {
+ buffer.splice(i,(start - i));
+ start = null;
+ }
+ }
+ if (start) {
+ buffer.splice(0,start + 1);
+ }
+ },
+
+ /**
+ * Trims the cache of entries to the appropriate new length.
+ *
+ * @method _afterCacheLimitChange
+ * @param e {Event} the attribute change event object
+ * @protected
+ */
+ _afterCacheLimitChange : function (e) {
+ if (isFinite(e.newVal)) {
+ var delta = this._entries.length - e.newVal;
+
+ if (delta > 0) {
+ this._entries.splice(0,delta);
+ }
+ }
+ },
+
+ /**
+ * Repopulates the Console with entries appropriate to the current filter
+ * settings.
+ *
+ * @method refreshConsole
+ */
+ refreshConsole : function () {
+ var entries = this._entries,
+ host = this.get(HOST),
+ body = host.get('contentBox').query(C_BODY),
+ remaining = host.get('consoleLimit'),
+ cats = this.get(CATEGORY),
+ srcs = this.get(SOURCE),
+ buffer = [],
+ i,e;
+
+ if (body) {
+ host._cancelPrintLoop();
+
+ // Evaluate all entries from latest to oldest
+ for (i = entries.length - 1; i >= 0 && remaining >= 0; --i) {
+ e = entries[i];
+ if (cats[e.category] && srcs[e.source]) {
+ buffer.unshift(e);
+ --remaining;
+ }
+ }
+
+ body.set('innerHTML',EMPTY);
+ host.buffer = buffer;
+ host.printBuffer();
+ }
+ },
+
+ /**
+ * Updates the checked property of a filter checkbox of the specified type.
+ * If no checkbox is found for the input params, one is created.
+ *
+ * @method _uiSetCheckbox
+ * @param type {String} 'category' or 'source'
+ * @param item {String} the name of the filter (e.g. 'info', 'event')
+ * @param checked {Boolean} value to set the checkbox's checked property
+ * @protected
+ */
+ _uiSetCheckbox : function (type, item, checked) {
+ if (type && item) {
+ var container = type === CATEGORY ?
+ this._categories :
+ this._sources,
+ sel = SEL_CHECK + getCN(CONSOLE,FILTER,item),
+ checkbox = container.query(sel),
+ host;
+
+ if (!checkbox) {
+ host = this.get(HOST);
+
+ this._createCheckbox(container, item);
+
+ checkbox = container.query(sel);
+
+ host._uiSetHeight(host.get('height'));
+ }
+
+ checkbox.set(CHECKED, checked);
+ }
+ },
+
+ /**
+ * Passes checkbox clicks on to the category attribute.
+ *
+ * @method _onCategoryCheckboxClick
+ * @param e {Event} the DOM event
+ * @protected
+ */
+ _onCategoryCheckboxClick : function (e) {
+ var t = e.target, cat;
+
+ if (t.hasClass(ConsoleFilters.CHROME_CLASSES.filter)) {
+ cat = t.get('value');
+ if (cat && cat in this.get(CATEGORY)) {
+ this.set(CATEGORY_DOT + cat, t.get(CHECKED), { fromUI: true });
+ }
+ }
+ },
+
+ /**
+ * Passes checkbox clicks on to the source attribute.
+ *
+ * @method _onSourceCheckboxClick
+ * @param e {Event} the DOM event
+ * @protected
+ */
+ _onSourceCheckboxClick : function (e) {
+ var t = e.target, src;
+
+ if (t.hasClass(ConsoleFilters.CHROME_CLASSES.filter)) {
+ src = t.get('value');
+ if (src && src in this.get(SOURCE)) {
+ this.set(SOURCE_DOT + src, t.get(CHECKED), { fromUI: true });
+ }
+ }
+ },
+
+ /**
+ * Hides any number of categories from the UI. Convenience method for
+ * myConsole.filter.set('category.foo', false); set('category.bar', false);
+ * and so on.
+ *
+ * @method hideCategory
+ * @param cat* {String} 1..n categories to filter out of the UI
+ */
+ hideCategory : function (cat, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(CATEGORY_DOT + cat, false);
+ }
+ },
+
+ /**
+ * Shows any number of categories in the UI. Convenience method for
+ * myConsole.filter.set('category.foo', true); set('category.bar', true);
+ * and so on.
+ *
+ * @method showCategory
+ * @param cat* {String} 1..n categories to allow to display in the UI
+ */
+ showCategory : function (cat, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(CATEGORY_DOT + cat, true);
+ }
+ },
+
+ /**
+ * Hides any number of sources from the UI. Convenience method for
+ * myConsole.filter.set('source.foo', false); set('source.bar', false);
+ * and so on.
+ *
+ * @method hideSource
+ * @param src* {String} 1..n sources to filter out of the UI
+ */
+ hideSource : function (src, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(SOURCE_DOT + src, false);
+ }
+ },
+
+ /**
+ * Shows any number of sources in the UI. Convenience method for
+ * myConsole.filter.set('source.foo', true); set('source.bar', true);
+ * and so on.
+ *
+ * @method showSource
+ * @param src* {String} 1..n sources to allow to display in the UI
+ */
+ showSource : function (src, multiple) {
+ if (isString(multiple)) {
+ Y.Array.each(arguments, arguments.callee, this);
+ } else {
+ this.set(SOURCE_DOT + src, true);
+ }
+ },
+
+ /**
+ * Creates a checkbox and label from the ConsoleFilters.FILTER_TEMPLATE for
+ * the provided type and name. The checkbox and label are appended to the
+ * container node passes as the first arg.
+ *
+ * @method _createCheckbox
+ * @param container {Node} the parentNode of the new checkbox and label
+ * @param name {String} the identifier of the filter
+ * @protected
+ */
+ _createCheckbox : function (container, name) {
+ var info = Y.merge(ConsoleFilters.CHROME_CLASSES, {
+ filter_name : name,
+ filter_class : getCN(CONSOLE, FILTER, name)
+ }),
+ node = Y.Node.create(
+ Y.substitute(ConsoleFilters.FILTER_TEMPLATE, info));
+
+ container.appendChild(node);
+ },
+
+ /**
+ * Validates category updates are objects and the subattribute is not too
+ * deep.
+ *
+ * @method _validateCategory
+ * @param cat {String} the new category:visibility map
+ * @param v {String} the subattribute path updated
+ * @return Boolean
+ * @protected
+ */
+ _validateCategory : function (cat, v) {
+ return Y.Lang.isObject(v,true) && cat.split(/\./).length < 3;
+ },
+
+ /**
+ * Validates source updates are objects and the subattribute is not too
+ * deep.
+ *
+ * @method _validateSource
+ * @param cat {String} the new source:visibility map
+ * @param v {String} the subattribute path updated
+ * @return Boolean
+ * @protected
+ */
+ _validateSource : function (src, v) {
+ return Y.Lang.isObject(v,true) && src.split(/\./).length < 3;
+ }
+
+});
+
+Y.namespace('Plugin').ConsoleFilters = ConsoleFilters;
+
+
+}, '3.0.0' ,{requires:['console','plugin']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("console",function(D){function V(){V.superclass.constructor.apply(this,arguments);}var G=D.ClassNameManager.getClassName,B="checked",s="clear",r="click",T="collapsed",AE="console",d="contentBox",h="disabled",o="entry",l="error",j="height",P="info",y="innerHTML",M="lastTime",F="pause",f="paused",x="reset",v="startTime",p="title",i="warn",Z=".",X=G(AE,"button"),b=G(AE,"checkbox"),AD=G(AE,s),w=G(AE,"collapse"),S=G(AE,T),E=G(AE,"controls"),e=G(AE,"hd"),c=G(AE,"bd"),C=G(AE,"ft"),k=G(AE,p),z=G(AE,o),t=G(AE,o,"cat"),a=G(AE,o,"content"),U=G(AE,o,"meta"),g=G(AE,o,"src"),A=G(AE,o,"time"),Q=G(AE,F),W=G(AE,F,"label"),n=/^(\S+)\s/,AA=/&/g,u=/>/g,J=/</g,H="&",R=">",m="<",O='<div class="{entry_class} {cat_class} {src_class}">'+'<p class="{entry_meta_class}">'+'<span class="{entry_src_class}">'+"{sourceAndDetail}"+"</span>"+'<span class="{entry_cat_class}">'+"{category}</span>"+'<span class="{entry_time_class}">'+" {totalTime}ms (+{elapsedTime}) {localTime}"+"</span>"+"</p>"+'<pre class="{entry_content_class}">{message}</pre>'+"</div>",I=D.Lang,K=D.Node.create,AC=I.isNumber,N=I.isString,q=D.merge,AB=D.substitute;D.mix(V,{NAME:AE,LOG_LEVEL_INFO:P,LOG_LEVEL_WARN:i,LOG_LEVEL_ERROR:l,ENTRY_CLASSES:{entry_class:z,entry_meta_class:U,entry_cat_class:t,entry_src_class:g,entry_time_class:A,entry_content_class:a},CHROME_CLASSES:{console_hd_class:e,console_bd_class:c,console_ft_class:C,console_controls_class:E,console_checkbox_class:b,console_pause_class:Q,console_pause_label_class:W,console_button_class:X,console_clear_class:AD,console_collapse_class:w,console_title_class:k},HEADER_TEMPLATE:'<div class="{console_hd_class}">'+'<h4 class="{console_title_class}">{str_title}</h4>'+'<button type="button" class="'+'{console_button_class} {console_collapse_class}">{str_collapse}'+"</button>"+"</div>",BODY_TEMPLATE:'<div class="{console_bd_class}"></div>',FOOTER_TEMPLATE:'<div class="{console_ft_class}">'+'<div class="{console_controls_class}">'+'<label for="{id_guid}" class="{console_pause_label_class}">'+'<input type="checkbox" class="{console_checkbox_class} '+'{console_pause_class}" value="1" id="{id_guid}"> '+"{str_pause}</label>"+'<button type="button" class="'+'{console_button_class} {console_clear_class}">{str_clear}'+"</button>"+"</div>"+"</div>",ENTRY_TEMPLATE:O,ATTRS:{logEvent:{value:"yui:log",writeOnce:true,validator:N},logSource:{value:D,writeOnce:true,validator:function(L){return L&&D.Lang.isFunction(L.on);}},strings:{value:{title:"Log Console",pause:"Pause",clear:"Clear",collapse:"Collapse",expand:"Expand"}},paused:{value:false,validator:I.isBoolean},defaultCategory:{value:P,validator:N},defaultSource:{value:"global",validator:N},entryTemplate:{value:O,validator:N},logLevel:{value:D.config.logLevel||P,setter:function(L){return this._setLogLevel(L);}},printTimeout:{value:100,validator:AC},printLimit:{value:50,validator:AC},consoleLimit:{value:300,validator:AC},newestOnTop:{value:true},scrollIntoView:{value:true},startTime:{value:new Date()},lastTime:{value:new Date(),readOnly:true},collapsed:{value:false},height:{value:"300px"},width:{value:"300px"},useBrowserConsole:{lazyAdd:false,value:false,getter:function(){var L=this.get("logSource");return L instanceof YUI?L.config.useBrowserConsole:null;},setter:function(L){var Y=this.get("logSource");if(Y instanceof YUI){L=!!L;Y.config.useBrowserConsole=!!L;return L;}else{return D.Attribute.INVALID_VALUE;}}},style:{value:"separate",writeOnce:true,validator:function(L){return this._validateStyle(L);}}}});D.extend(V,D.Widget,{_evtCat:null,_head:null,_body:null,_foot:null,_printLoop:null,buffer:null,log:function(){D.log.apply(D,arguments);return this;},clearConsole:function(){this._body.set(y,"");this._cancelPrintLoop();this.buffer=[];return this;},reset:function(){this.fire(x);return this;},collapse:function(){this.set(T,true);return this;},expand:function(){this.set(T,false);return this;},printBuffer:function(Y){var AK=this.buffer,AF=D.config.debug,L=[],AH=this.get("consoleLimit"),AJ=this.get("newestOnTop"),AG=AJ?this._body.get("firstChild"):null,AI;if(AK.length>AH){AK.splice(0,AK.length-AH);}Y=Math.min(AK.length,(Y||AK.length));D.config.debug=false;if(!this.get(f)&&this.get("rendered")){for(AI=0;AI<Y&&AK.length;++AI){L[AI]=this._createEntryHTML(AK.shift());}if(!AK.length){this._cancelPrintLoop();}if(L.length){if(AJ){L.reverse();}this._body.insertBefore(K(L.join("")),AG);if(this.get("scrollIntoView")){this.scrollToLatest();}this._trimOldEntries();}}D.config.debug=AF;return this;},initializer:function(){this._evtCat=D.stamp(this)+"|";this.buffer=[];this.get("logSource").on(this._evtCat+this.get("logEvent"),D.bind("_onLogEvent",this));this.publish(o,{defaultFn:this._defEntryFn});this.publish(x,{defaultFn:this._defResetFn});this.after("rendered",this._schedulePrint);},destructor:function(){var L=this.get("boundingBox");this._cancelPrintLoop();this.get("logSource").detach(this._evtCat+"*");D.Event.purgeElement(L,true);L.set("innerHTML","");},renderUI:function(){this._initHead();this._initBody();this._initFoot();var L=this.get("style");if(L!=="block"){this.get("boundingBox").addClass("yui-"+L+"-console");}},syncUI:function(){this._uiUpdatePaused(this.get(f));this._uiUpdateCollapsed(this.get(T));this._uiSetHeight(this.get(j));},bindUI:function(){this.get(d).query("button."+w).on(r,this._onCollapseClick,this);this.get(d).query("input[type=checkbox]."+Q).on(r,this._onPauseClick,this);this.get(d).query("button."+AD).on(r,this._onClearClick,this);this.after(this._evtCat+"stringsChange",this._afterStringsChange);this.after(this._evtCat+"pausedChange",this._afterPausedChange);this.after(this._evtCat+"consoleLimitChange",this._afterConsoleLimitChange);this.after(this._evtCat+"collapsedChange",this._afterCollapsedChange);},_initHead:function(){var L=this.get(d),Y=q(V.CHROME_CLASSES,{str_collapse:this.get("strings.collapse"),str_title:this.get("strings.title")});this._head=K(AB(V.HEADER_TEMPLATE,Y));L.insertBefore(this._head,L.get("firstChild"));},_initBody:function(){this._body=K(AB(V.BODY_TEMPLATE,V.CHROME_CLASSES));
+this.get(d).appendChild(this._body);},_initFoot:function(){var L=q(V.CHROME_CLASSES,{id_guid:D.guid(),str_pause:this.get("strings.pause"),str_clear:this.get("strings.clear")});this._foot=K(AB(V.FOOTER_TEMPLATE,L));this.get(d).appendChild(this._foot);},_isInLogLevel:function(AF){var L=AF.cat,Y=this.get("logLevel");if(Y!==P){L=L||P;if(N(L)){L=L.toLowerCase();}if((L===i&&Y===l)||(L===P&&Y!==P)){return false;}}return true;},_normalizeMessage:function(AF){var AH=AF.msg,Y=AF.cat,AG=AF.src,L={time:new Date(),message:AH,category:Y||this.get("defaultCategory"),sourceAndDetail:AG||this.get("defaultSource"),source:null,localTime:null,elapsedTime:null,totalTime:null};L.source=n.test(L.sourceAndDetail)?RegExp.$1:L.sourceAndDetail;L.localTime=L.time.toLocaleTimeString?L.time.toLocaleTimeString():(L.time+"");L.elapsedTime=L.time-this.get(M);L.totalTime=L.time-this.get(v);this._set(M,L.time);return L;},_schedulePrint:function(){if(!this._printLoop&&!this.get(f)&&this.get("rendered")){this._printLoop=D.later(this.get("printTimeout"),this,this.printBuffer,this.get("printLimit"),true);}},_createEntryHTML:function(L){L=q(this._htmlEscapeMessage(L),V.ENTRY_CLASSES,{cat_class:this.getClassName(o,L.category),src_class:this.getClassName(o,L.source)});return this.get("entryTemplate").replace(/\{(\w+)\}/g,function(Y,AF){return AF in L?L[AF]:"";});},scrollToLatest:function(){var L=this.get("newestOnTop")?0:this._body.get("scrollHeight");this._body.set("scrollTop",L);},_htmlEscapeMessage:function(L){L.message=this._encodeHTML(L.message);L.source=this._encodeHTML(L.source);L.sourceAndDetail=this._encodeHTML(L.sourceAndDetail);L.category=this._encodeHTML(L.category);return L;},_trimOldEntries:function(){D.config.debug=false;var AI=this._body,AF=this.get("consoleLimit"),AG=D.config.debug,L,AJ,AH,Y;if(AI){L=AI.queryAll(Z+z);Y=L.size()-AF;if(Y>0){if(this.get("newestOnTop")){AH=AF;Y=L.size();}else{AH=0;}this._body.setStyle("display","none");for(;AH<Y;++AH){AJ=L.item(AH);if(AJ){AJ.remove();}}this._body.setStyle("display","");}}D.config.debug=AG;},_encodeHTML:function(L){return N(L)?L.replace(AA,H).replace(J,m).replace(u,R):L;},_cancelPrintLoop:function(){if(this._printLoop){this._printLoop.cancel();this._printLoop=null;}},_validateStyle:function(L){return L==="inline"||L==="block"||L==="separate";},_onPauseClick:function(L){this.set(f,L.target.get(B));},_onClearClick:function(L){this.clearConsole();},_onCollapseClick:function(L){this.set(T,!this.get(T));},_setLogLevel:function(L){if(N(L)){L=L.toLowerCase();}return(L===i||L===l)?L:P;},_uiSetHeight:function(L){V.superclass._uiSetHeight.apply(this,arguments);if(this._head&&this._foot){var Y=this.get("boundingBox").get("offsetHeight")-this._head.get("offsetHeight")-this._foot.get("offsetHeight");this._body.setStyle(j,Y+"px");}},_afterStringsChange:function(AF){var AH=AF.subAttrName?AF.subAttrName.split(Z)[1]:null,L=this.get(d),Y=AF.prevVal,AG=AF.newVal;if((!AH||AH===p)&&Y.title!==AG.title){L.queryAll(Z+k).set(y,AG.title);}if((!AH||AH===F)&&Y.pause!==AG.pause){L.queryAll(Z+W).set(y,AG.pause);}if((!AH||AH===s)&&Y.clear!==AG.clear){L.queryAll(Z+AD).set("value",AG.clear);}},_afterPausedChange:function(Y){var L=Y.newVal;if(Y.src!==D.Widget.SRC_UI){this._uiUpdatePaused(L);}if(!L){this._schedulePrint();}else{if(this._printLoop){this._cancelPrintLoop();}}},_uiUpdatePaused:function(L){var Y=this._foot.queryAll("input[type=checkbox]."+Q);if(Y){Y.set(B,L);}},_afterConsoleLimitChange:function(){this._trimOldEntries();},_afterCollapsedChange:function(L){this._uiUpdateCollapsed(L.newVal);},_uiUpdateCollapsed:function(L){var AG=this.get("boundingBox"),Y=AG.queryAll("button."+w),AH=L?"addClass":"removeClass",AF=this.get("strings."+(L?"expand":"collapse"));AG[AH](S);if(Y){Y.set("innerHTML",AF);}this._uiSetHeight(L?this._head.get("offsetHeight"):this.get(j));},_afterVisibleChange:function(L){V.superclass._afterVisibleChange.apply(this,arguments);this._uiUpdateFromHideShow(L.newVal);},_uiUpdateFromHideShow:function(L){if(L){this._uiSetHeight(this.get(j));}},_onLogEvent:function(Y){if(!this.get(h)&&this._isInLogLevel(Y)){var L=D.config.debug;D.config.debug=false;this.fire(o,{message:this._normalizeMessage(Y)});D.config.debug=L;}},_defResetFn:function(){this.clearConsole();this.set(v,new Date());this.set(h,false);this.set(f,false);},_defEntryFn:function(L){if(L.message){this.buffer.push(L.message);this._schedulePrint();}}});D.Console=V;},"3.0.0",{requires:["substitute","widget"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('console', function(Y) {
+
+/**
+ * Console creates a visualization for messages logged through calls to a YUI
+ * instance's <code>Y.log( message, category, source )</code> method. The
+ * debug versions of YUI modules will include logging statements to offer some
+ * insight into the steps executed during that module's operation. Including
+ * log statements in your code will cause those messages to also appear in the
+ * Console. Use Console to aid in developing your page or application.
+ *
+ * Entry categories "info", "warn", and "error"
+ * are also referred to as the log level, and entries are filtered against the
+ * configured logLevel.
+ *
+ * @module console
+ * @class Console
+ * @extends Widget
+ * @param conf {Object} Configuration object (see Configuration attributes)
+ * @constructor
+ */
+function Console() {
+ Console.superclass.constructor.apply(this,arguments);
+}
+
+var getCN = Y.ClassNameManager.getClassName,
+ CHECKED = 'checked',
+ CLEAR = 'clear',
+ CLICK = 'click',
+ COLLAPSED = 'collapsed',
+ CONSOLE = 'console',
+ CONTENT_BOX = 'contentBox',
+ DISABLED = 'disabled',
+ ENTRY = 'entry',
+ ERROR = 'error',
+ HEIGHT = 'height',
+ INFO = 'info',
+ INNER_HTML = 'innerHTML',
+ LAST_TIME = 'lastTime',
+ PAUSE = 'pause',
+ PAUSED = 'paused',
+ RESET = 'reset',
+ START_TIME = 'startTime',
+ TITLE = 'title',
+ WARN = 'warn',
+
+ DOT = '.',
+
+ C_BUTTON = getCN(CONSOLE,'button'),
+ C_CHECKBOX = getCN(CONSOLE,'checkbox'),
+ C_CLEAR = getCN(CONSOLE,CLEAR),
+ C_COLLAPSE = getCN(CONSOLE,'collapse'),
+ C_COLLAPSED = getCN(CONSOLE,COLLAPSED),
+ C_CONSOLE_CONTROLS = getCN(CONSOLE,'controls'),
+ C_CONSOLE_HD = getCN(CONSOLE,'hd'),
+ C_CONSOLE_BD = getCN(CONSOLE,'bd'),
+ C_CONSOLE_FT = getCN(CONSOLE,'ft'),
+ C_CONSOLE_TITLE = getCN(CONSOLE,TITLE),
+ C_ENTRY = getCN(CONSOLE,ENTRY),
+ C_ENTRY_CAT = getCN(CONSOLE,ENTRY,'cat'),
+ C_ENTRY_CONTENT = getCN(CONSOLE,ENTRY,'content'),
+ C_ENTRY_META = getCN(CONSOLE,ENTRY,'meta'),
+ C_ENTRY_SRC = getCN(CONSOLE,ENTRY,'src'),
+ C_ENTRY_TIME = getCN(CONSOLE,ENTRY,'time'),
+ C_PAUSE = getCN(CONSOLE,PAUSE),
+ C_PAUSE_LABEL = getCN(CONSOLE,PAUSE,'label'),
+
+ RE_INLINE_SOURCE = /^(\S+)\s/,
+ RE_AMP = /&/g,
+ RE_GT = />/g,
+ RE_LT = /</g,
+
+ ESC_AMP = '&',
+ ESC_GT = '>',
+ ESC_LT = '<',
+
+ ENTRY_TEMPLATE_STR =
+ '<div class="{entry_class} {cat_class} {src_class}">'+
+ '<p class="{entry_meta_class}">'+
+ '<span class="{entry_src_class}">'+
+ '{sourceAndDetail}'+
+ '</span>'+
+ '<span class="{entry_cat_class}">'+
+ '{category}</span>'+
+ '<span class="{entry_time_class}">'+
+ ' {totalTime}ms (+{elapsedTime}) {localTime}'+
+ '</span>'+
+ '</p>'+
+ '<pre class="{entry_content_class}">{message}</pre>'+
+ '</div>',
+
+ L = Y.Lang,
+ create = Y.Node.create,
+ isNumber = L.isNumber,
+ isString = L.isString,
+ merge = Y.merge,
+ substitute = Y.substitute;
+
+
+Y.mix(Console, {
+
+ /**
+ * The identity of the widget.
+ *
+ * @property Console.NAME
+ * @type String
+ * @static
+ */
+ NAME : CONSOLE,
+
+ /**
+ * Static identifier for logLevel configuration setting to allow all
+ * incoming messages to generate Console entries.
+ *
+ * @property Console.LOG_LEVEL_INFO
+ * @type String
+ * @static
+ */
+ LOG_LEVEL_INFO : INFO,
+
+ /**
+ * Static identifier for logLevel configuration setting to allow only
+ * incoming messages of logLevel "warn" or "error"
+ * to generate Console entries.
+ *
+ * @property Console.LOG_LEVEL_WARN
+ * @type String
+ * @static
+ */
+ LOG_LEVEL_WARN : WARN,
+
+ /**
+ * Static identifier for logLevel configuration setting to allow only
+ * incoming messages of logLevel "error" to generate
+ * Console entries.
+ *
+ * @property Console.LOG_LEVEL_ERROR
+ * @type String
+ * @static
+ */
+ LOG_LEVEL_ERROR : ERROR,
+
+ /**
+ * Map (object) of classNames used to populate the placeholders in the
+ * Console.ENTRY_TEMPLATE markup when rendering a new Console entry.
+ *
+ * <p>By default, the keys contained in the object are:</p>
+ * <ul>
+ * <li>entry_class</li>
+ * <li>entry_meta_class</li>
+ * <li>entry_cat_class</li>
+ * <li>entry_src_class</li>
+ * <li>entry_time_class</li>
+ * <li>entry_content_class</li>
+ * </ul>
+ *
+ * @property Console.ENTRY_CLASSES
+ * @type Object
+ * @static
+ */
+ ENTRY_CLASSES : {
+ entry_class : C_ENTRY,
+ entry_meta_class : C_ENTRY_META,
+ entry_cat_class : C_ENTRY_CAT,
+ entry_src_class : C_ENTRY_SRC,
+ entry_time_class : C_ENTRY_TIME,
+ entry_content_class : C_ENTRY_CONTENT
+ },
+
+ /**
+ * Map (object) of classNames used to populate the placeholders in the
+ * Console.HEADER_TEMPLATE, Console.BODY_TEMPLATE, and
+ * Console.FOOTER_TEMPLATE markup when rendering the Console UI.
+ *
+ * <p>By default, the keys contained in the object are:</p>
+ * <ul>
+ * <li>console_hd_class</li>
+ * <li>console_bd_class</li>
+ * <li>console_ft_class</li>
+ * <li>console_controls_class</li>
+ * <li>console_checkbox_class</li>
+ * <li>console_pause_class</li>
+ * <li>console_pause_label_class</li>
+ * <li>console_button_class</li>
+ * <li>console_clear_class</li>
+ * <li>console_collapse_class</li>
+ * <li>console_title_class</li>
+ * </ul>
+ *
+ * @property Console.CHROME_CLASSES
+ * @type Object
+ * @static
+ */
+ CHROME_CLASSES : {
+ console_hd_class : C_CONSOLE_HD,
+ console_bd_class : C_CONSOLE_BD,
+ console_ft_class : C_CONSOLE_FT,
+ console_controls_class : C_CONSOLE_CONTROLS,
+ console_checkbox_class : C_CHECKBOX,
+ console_pause_class : C_PAUSE,
+ console_pause_label_class : C_PAUSE_LABEL,
+ console_button_class : C_BUTTON,
+ console_clear_class : C_CLEAR,
+ console_collapse_class : C_COLLAPSE,
+ console_title_class : C_CONSOLE_TITLE
+ },
+
+ /**
+ * Markup template used to generate the DOM structure for the header
+ * section of the Console when it is rendered. The template includes
+ * these {placeholder}s:
+ *
+ * <ul>
+ * <li>console_button_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>console_collapse_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>console_hd_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>console_title_class - contributed by Console.CHROME_CLASSES</li>
+ * <li>str_collapse - pulled from attribute strings.collapse</li>
+ * <li>str_title - pulled from attribute strings.title</li>
+ * </ul>
+ *
+ * @property Console.HEADER_TEMPLATE
+ * @type String
+ * @static
+ */
+ HEADER_TEMPLATE :
+ '<div class="{console_hd_class}">'+
+ '<h4 class="{console_title_class}">{str_title}</h4>'+
+ '<button type="button" class="'+
+ '{console_button_class} {console_collapse_class}">{str_collapse}'+
+ '</button>'+
+ '</div>',
+
+ /**
+ * Markup template used to generate the DOM structure for the Console body
+ * (where the messages are inserted) when it is rendered. The template
+ * includes only the {placeholder} "console_bd_class", which is
+ * constributed by Console.CHROME_CLASSES.
+ *
+ * @property Console.BODY_TEMPLATE
+ * @type String
+ * @static
+ */
+ BODY_TEMPLATE : '<div class="{console_bd_class}"></div>',
+
+ /**
+ * Markup template used to generate the DOM structure for the footer
+ * section of the Console when it is rendered. The template includes
+ * many of the {placeholder}s from Console.CHROME_CLASSES as well as:
+ *
+ * <ul>
+ * <li>id_guid - generated unique id, relates the label and checkbox</li>
+ * <li>str_pause - pulled from attribute strings.pause</li>
+ * <li>str_clear - pulled from attribute strings.clear</li>
+ * </ul>
+ *
+ * @property Console.FOOTER_TEMPLATE
+ * @type String
+ * @static
+ */
+ FOOTER_TEMPLATE :
+ '<div class="{console_ft_class}">'+
+ '<div class="{console_controls_class}">'+
+ '<label for="{id_guid}" class="{console_pause_label_class}">'+
+ '<input type="checkbox" class="{console_checkbox_class} '+
+ '{console_pause_class}" value="1" id="{id_guid}"> '+
+ '{str_pause}</label>' +
+ '<button type="button" class="'+
+ '{console_button_class} {console_clear_class}">{str_clear}'+
+ '</button>'+
+ '</div>'+
+ '</div>',
+
+ /**
+ * Default markup template used to create the DOM structure for Console
+ * entries. The markup contains {placeholder}s for content and classes
+ * that are replaced via Y.substitute. The default template contains
+ * the {placeholder}s identified in Console.ENTRY_CLASSES as well as the
+ * following placeholders that will be populated by the log entry data:
+ *
+ * <ul>
+ * <li>cat_class</li>
+ * <li>src_class</li>
+ * <li>totalTime</li>
+ * <li>elapsedTime</li>
+ * <li>localTime</li>
+ * <li>sourceAndDetail</li>
+ * <li>message</li>
+ * </ul>
+ *
+ * @property Console.ENTRY_TEMPLATE
+ * @type String
+ * @static
+ */
+ ENTRY_TEMPLATE : ENTRY_TEMPLATE_STR,
+
+ /**
+ * Static property used to define the default attribute configuration of
+ * the Widget.
+ *
+ * @property Console.ATTRS
+ * @Type Object
+ * @static
+ */
+ ATTRS : {
+
+ /**
+ * Name of the custom event that will communicate log messages.
+ *
+ * @attribute logEvent
+ * @type String
+ * @default "yui:log"
+ */
+ logEvent : {
+ value : 'yui:log',
+ writeOnce : true,
+ validator : isString
+ },
+
+ /**
+ * Object that will emit the log events. By default the YUI instance.
+ * To have a single Console capture events from all YUI instances, set
+ * this to the Y.Global object.
+ *
+ * @attribute logSource
+ * @type EventTarget
+ * @default Y
+ */
+ logSource : {
+ value : Y,
+ writeOnce : true,
+ validator : function (v) {
+ return v && Y.Lang.isFunction(v.on);
+ }
+ },
+
+ /**
+ * Collection of strings used to label elements in the Console UI.
+ * Default collection contains the following name:value pairs:
+ *
+ * <ul>
+ * <li>title : "Log Console"</li>
+ * <li>pause : "Pause"</li>
+ * <li>clear : "Clear"</li>
+ * <li>collapse : "Collapse"</li>
+ * <li>expand : "Expand"</li>
+ * </ul>
+ *
+ * @attribute strings
+ * @type Object
+ */
+ strings : {
+ value : {
+ title : "Log Console",
+ pause : "Pause",
+ clear : "Clear",
+ collapse : "Collapse",
+ expand : "Expand"
+ }
+ },
+
+ /**
+ * Boolean to pause the outputting of new messages to the console.
+ * When paused, messages will accumulate in the buffer.
+ *
+ * @attribute paused
+ * @type boolean
+ * @default false
+ */
+ paused : {
+ value : false,
+ validator : L.isBoolean
+ },
+
+ /**
+ * If a category is not specified in the Y.log(..) statement, this
+ * category will be used. Categories "info",
+ * "warn", and "error" are also called log level.
+ *
+ * @attribute defaultCategory
+ * @type String
+ * @default "info"
+ */
+ defaultCategory : {
+ value : INFO,
+ validator : isString
+ },
+
+ /**
+ * If a source is not specified in the Y.log(..) statement, this
+ * source will be used.
+ *
+ * @attribute defaultSource
+ * @type String
+ * @default "global"
+ */
+ defaultSource : {
+ value : 'global',
+ validator : isString
+ },
+
+ /**
+ * Markup template used to create the DOM structure for Console entries.
+ *
+ * @attribute entryTemplate
+ * @type String
+ * @default Console.ENTRY_TEMPLATE
+ */
+ entryTemplate : {
+ value : ENTRY_TEMPLATE_STR,
+ validator : isString
+ },
+
+ /**
+ * Minimum entry log level to render into the Console. The initial
+ * logLevel value for all Console instances defaults from the
+ * Y.config.logLevel YUI configuration, or Console.LOG_LEVEL_INFO if
+ * that configuration is not set.
+ *
+ * Possible values are "info", "warn",
+ * "error" (case insensitive), or their corresponding statics
+ * Console.LOG_LEVEL_INFO and so on.
+ *
+ * @attribute logLevel
+ * @type String
+ * @default Y.config.logLevel or Console.LOG_LEVEL_INFO
+ */
+ logLevel : {
+ value : Y.config.logLevel || INFO,
+ setter : function (v) {
+ return this._setLogLevel(v);
+ }
+ },
+
+ /**
+ * Millisecond timeout between iterations of the print loop, moving
+ * entries from the buffer to the UI.
+ *
+ * @attribute printTimeout
+ * @type Number
+ * @default 100
+ */
+ printTimeout : {
+ value : 100,
+ validator : isNumber
+ },
+
+ /**
+ * Maximum number of entries printed in each iteration of the print
+ * loop. This is used to prevent excessive logging locking the page UI.
+ *
+ * @attribute printLimit
+ * @type Number
+ * @default 50
+ */
+ printLimit : {
+ value : 50,
+ validator : isNumber
+ },
+
+ /**
+ * Maximum number of Console entries allowed in the Console body at one
+ * time. This is used to keep acquired messages from exploding the
+ * DOM tree and impacting page performance.
+ *
+ * @attribute consoleLimit
+ * @type Number
+ * @default 300
+ */
+ consoleLimit : {
+ value : 300,
+ validator : isNumber
+ },
+
+ /**
+ * New entries should display at the top of the Console or the bottom?
+ *
+ * @attribute newestOnTop
+ * @type Boolean
+ * @default true
+ */
+ newestOnTop : {
+ value : true
+ },
+
+ /**
+ * When new entries are added to the Console UI, should they be
+ * scrolled into view?
+ *
+ * @attribute scrollIntoView
+ * @type Boolean
+ * @default true
+ */
+ scrollIntoView : {
+ value : true
+ },
+
+ /**
+ * The baseline time for this Console instance, used to measure elapsed
+ * time from the moment the console module is <code>use</code>d to the
+ * moment each new entry is logged (not rendered).
+ *
+ * This value is reset by the instance method myConsole.reset().
+ *
+ * @attribute startTime
+ * @type Date
+ * @default The moment the console module is <code>use</code>d
+ */
+ startTime : {
+ value : new Date()
+ },
+
+ /**
+ * The precise time the last entry was logged. Used to measure elapsed
+ * time between log messages.
+ *
+ * @attribute lastTime
+ * @type Date
+ * @default The moment the console module is <code>use</code>d
+ */
+ lastTime : {
+ value : new Date(),
+ readOnly: true
+ },
+
+ /**
+ * Controls the collapsed state of the Console
+ *
+ * @attribute collapsed
+ * @type Boolean
+ * @default false
+ */
+ collapsed : {
+ value : false
+ },
+
+ /**
+ * String with units, or number, representing the height of the Console,
+ * inclusive of header and footer. If a number is provided, the default
+ * unit, defined by Widget's DEF_UNIT, property is used.
+ *
+ * @attribute height
+ * @default "300px"
+ * @type {String | Number}
+ */
+ height: {
+ value: "300px"
+ },
+
+ /**
+ * String with units, or number, representing the width of the Console.
+ * If a number is provided, the default unit, defined by Widget's
+ * DEF_UNIT, property is used.
+ *
+ * @attribute width
+ * @default "300px"
+ * @type {String | Number}
+ */
+ width: {
+ value: "300px"
+ },
+
+ /**
+ * Pass through to the YUI instance useBrowserConsole configuration.
+ * By default this is set to false, which will disable logging to the
+ * browser console when a Console instance is created. If the
+ * logSource is not a YUI instance, this has no effect.
+ *
+ * @attribute useBrowserConsole
+ * @type {Boolean}
+ * @default false
+ */
+ useBrowserConsole : {
+ lazyAdd: false,
+ value: false,
+ getter : function () {
+ var logSource = this.get('logSource');
+ return logSource instanceof YUI ?
+ logSource.config.useBrowserConsole : null;
+ },
+ setter : function (v) {
+ var logSource = this.get('logSource');
+ if (logSource instanceof YUI) {
+ v = !!v;
+ logSource.config.useBrowserConsole = !!v;
+ return v;
+ } else {
+ return Y.Attribute.INVALID_VALUE;
+ }
+ }
+ },
+
+ /**
+ * Allows the Console to flow in the document. Available values are
+ * 'inline', 'block', and 'separate' (the default).
+ *
+ * @attribute style
+ * @type {String}
+ * @default 'separate'
+ */
+ style : {
+ value : 'separate',
+ writeOnce : true,
+ validator : function (v) {
+ return this._validateStyle(v);
+ }
+ }
+ }
+
+});
+
+Y.extend(Console,Y.Widget,{
+
+ /**
+ * Category to prefix all event subscriptions to allow for ease of detach
+ * during destroy.
+ *
+ * @property _evtCat
+ * @type string
+ * @protected
+ */
+ _evtCat : null,
+
+ /**
+ * Reference to the Node instance containing the header contents.
+ *
+ * @property _head
+ * @type Node
+ * @default null
+ * @protected
+ */
+ _head : null,
+
+ /**
+ * Reference to the Node instance that will house the console messages.
+ *
+ * @property _body
+ * @type Node
+ * @default null
+ * @protected
+ */
+ _body : null,
+
+ /**
+ * Reference to the Node instance containing the footer contents.
+ *
+ * @property _foot
+ * @type Node
+ * @default null
+ * @protected
+ */
+ _foot : null,
+
+ /**
+ * Holds the object API returned from <code>Y.later</code> for the print
+ * loop interval.
+ *
+ * @property _printLoop
+ * @type Object
+ * @default null
+ * @protected
+ */
+ _printLoop : null,
+
+ /**
+ * Array of normalized message objects awaiting printing.
+ *
+ * @property buffer
+ * @type Array
+ * @default null
+ * @protected
+ */
+ buffer : null,
+
+ /**
+ * Wrapper for <code>Y.log</code>.
+ *
+ * @method log
+ * @param arg* {MIXED} (all arguments passed through to <code>Y.log</code>)
+ * @chainable
+ */
+ log : function () {
+ Y.log.apply(Y,arguments);
+
+ return this;
+ },
+
+ /**
+ * Clear the console of messages and flush the buffer of pending messages.
+ *
+ * @method clearConsole
+ * @chainable
+ */
+ clearConsole : function () {
+ // TODO: clear event listeners from console contents
+ this._body.set(INNER_HTML,'');
+
+ this._cancelPrintLoop();
+
+ this.buffer = [];
+
+ return this;
+ },
+
+ /**
+ * Clears the console and resets internal timers.
+ *
+ * @method reset
+ * @chainable
+ */
+ reset : function () {
+ this.fire(RESET);
+
+ return this;
+ },
+
+ /**
+ * Collapses the body and footer.
+ *
+ * @method collapse
+ * @chainable
+ */
+ collapse : function () {
+ this.set(COLLAPSED, true);
+
+ return this;
+ },
+
+ /**
+ * Expands the body and footer if collapsed.
+ *
+ * @method expand
+ * @chainable
+ */
+ expand : function () {
+ this.set(COLLAPSED, false);
+
+ return this;
+ },
+
+ /**
+ * Outputs buffered messages to the console UI. This is typically called
+ * from a scheduled interval until the buffer is empty (referred to as the
+ * print loop). The number of buffered messages output to the Console is
+ * limited to the number provided as an argument. If no limit is passed,
+ * all buffered messages are rendered.
+ *
+ * @method printBuffer
+ * @param limit {Number} (optional) max number of buffered entries to write
+ * @chainable
+ */
+ printBuffer: function (limit) {
+ var messages = this.buffer,
+ debug = Y.config.debug,
+ entries = [],
+ consoleLimit= this.get('consoleLimit'),
+ newestOnTop = this.get('newestOnTop'),
+ anchor = newestOnTop ? this._body.get('firstChild') : null,
+ i;
+
+ if (messages.length > consoleLimit) {
+ messages.splice(0, messages.length - consoleLimit);
+ }
+
+ limit = Math.min(messages.length, (limit || messages.length));
+
+ // turn off logging system
+ Y.config.debug = false;
+
+ if (!this.get(PAUSED) && this.get('rendered')) {
+
+ for (i = 0; i < limit && messages.length; ++i) {
+ entries[i] = this._createEntryHTML(messages.shift());
+ }
+
+ if (!messages.length) {
+ this._cancelPrintLoop();
+ }
+
+ if (entries.length) {
+ if (newestOnTop) {
+ entries.reverse();
+ }
+
+ this._body.insertBefore(create(entries.join('')), anchor);
+
+ if (this.get('scrollIntoView')) {
+ this.scrollToLatest();
+ }
+
+ this._trimOldEntries();
+ }
+ }
+
+ // restore logging system
+ Y.config.debug = debug;
+
+ return this;
+ },
+
+
+ /**
+ * Constructor code. Set up the buffer and entry template, publish
+ * internal events, and subscribe to the configured logEvent.
+ *
+ * @method initializer
+ * @protected
+ */
+ initializer : function () {
+ this._evtCat = Y.stamp(this) + '|';
+
+ this.buffer = [];
+
+ this.get('logSource').on(this._evtCat +
+ this.get('logEvent'),Y.bind("_onLogEvent",this));
+
+ /**
+ * Transfers a received message to the print loop buffer. Default
+ * behavior defined in _defEntryFn.
+ *
+ * @event entry
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>message</dt>
+ * <dd>The message data normalized into an object literal (see _normalizeMessage)</dd>
+ * </dl>
+ * @preventable _defEntryFn
+ */
+ this.publish(ENTRY, { defaultFn: this._defEntryFn });
+
+ /**
+ * Triggers the reset behavior via the default logic in _defResetFn.
+ *
+ * @event reset
+ * @param event {Event.Facade} Event Facade object
+ * @preventable _defResetFn
+ */
+ this.publish(RESET, { defaultFn: this._defResetFn });
+
+ this.after('rendered', this._schedulePrint);
+ },
+
+ /**
+ * Tears down the instance, flushing event subscriptions and purging the UI.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor : function () {
+ var bb = this.get('boundingBox');
+
+ this._cancelPrintLoop();
+
+ this.get('logSource').detach(this._evtCat + '*');
+
+ Y.Event.purgeElement(bb, true);
+
+ bb.set('innerHTML','');
+ },
+
+ /**
+ * Generate the Console UI.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI : function () {
+ this._initHead();
+ this._initBody();
+ this._initFoot();
+
+ // Apply positioning to the bounding box if appropriate
+ var style = this.get('style');
+ if (style !== 'block') {
+ this.get('boundingBox').addClass('yui-'+style+'-console');
+ }
+ },
+
+ /**
+ * Sync the UI state to the current attribute state.
+ *
+ * @method syncUI
+ */
+ syncUI : function () {
+ this._uiUpdatePaused(this.get(PAUSED));
+ this._uiUpdateCollapsed(this.get(COLLAPSED));
+ this._uiSetHeight(this.get(HEIGHT));
+ },
+
+ /**
+ * Set up event listeners to wire up the UI to the internal state.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI : function () {
+ this.get(CONTENT_BOX).query('button.'+C_COLLAPSE).
+ on(CLICK,this._onCollapseClick,this);
+
+ this.get(CONTENT_BOX).query('input[type=checkbox].'+C_PAUSE).
+ on(CLICK,this._onPauseClick,this);
+
+ this.get(CONTENT_BOX).query('button.'+C_CLEAR).
+ on(CLICK,this._onClearClick,this);
+
+ // Attribute changes
+ this.after(this._evtCat + 'stringsChange',
+ this._afterStringsChange);
+ this.after(this._evtCat + 'pausedChange',
+ this._afterPausedChange);
+ this.after(this._evtCat + 'consoleLimitChange',
+ this._afterConsoleLimitChange);
+ this.after(this._evtCat + 'collapsedChange',
+ this._afterCollapsedChange);
+ },
+
+
+ /**
+ * Create the DOM structure for the header elements.
+ *
+ * @method _initHead
+ * @protected
+ */
+ _initHead : function () {
+ var cb = this.get(CONTENT_BOX),
+ info = merge(Console.CHROME_CLASSES, {
+ str_collapse : this.get('strings.collapse'),
+ str_title : this.get('strings.title')
+ });
+
+ this._head = create(substitute(Console.HEADER_TEMPLATE,info));
+
+ cb.insertBefore(this._head,cb.get('firstChild'));
+ },
+
+ /**
+ * Create the DOM structure for the console body—where messages are
+ * rendered.
+ *
+ * @method _initBody
+ * @protected
+ */
+ _initBody : function () {
+ this._body = create(substitute(
+ Console.BODY_TEMPLATE,
+ Console.CHROME_CLASSES));
+
+ this.get(CONTENT_BOX).appendChild(this._body);
+ },
+
+ /**
+ * Create the DOM structure for the footer elements.
+ *
+ * @method _initFoot
+ * @protected
+ */
+ _initFoot : function () {
+ var info = merge(Console.CHROME_CLASSES, {
+ id_guid : Y.guid(),
+ str_pause : this.get('strings.pause'),
+ str_clear : this.get('strings.clear')
+ });
+
+ this._foot = create(substitute(Console.FOOTER_TEMPLATE,info));
+
+ this.get(CONTENT_BOX).appendChild(this._foot);
+ },
+
+ /**
+ * Determine if incoming log messages are within the configured logLevel
+ * to be buffered for printing.
+ *
+ * @method _isInLogLevel
+ * @protected
+ */
+ _isInLogLevel : function (e) {
+ var cat = e.cat, lvl = this.get('logLevel');
+
+ if (lvl !== INFO) {
+ cat = cat || INFO;
+
+ if (isString(cat)) {
+ cat = cat.toLowerCase();
+ }
+
+ if ((cat === WARN && lvl === ERROR) ||
+ (cat === INFO && lvl !== INFO)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ /**
+ * Create a log entry message from the inputs including the following keys:
+ * <ul>
+ * <li>time - this moment</li>
+ * <li>message - leg message</li>
+ * <li>category - logLevel or custom category for the message</li>
+ * <li>source - when provided, the widget or util calling Y.log</li>
+ * <li>sourceAndDetail - same as source but can include instance info</li>
+ * <li>localTime - readable version of time</li>
+ * <li>elapsedTime - ms since last entry</li>
+ * <li>totalTime - ms since Console was instantiated or reset</li>
+ * </ul>
+ *
+ * @method _normalizeMessage
+ * @param e {Event} custom event containing the log message
+ * @return Object the message object
+ * @protected
+ */
+ _normalizeMessage : function (e) {
+
+ var msg = e.msg,
+ cat = e.cat,
+ src = e.src,
+
+ m = {
+ time : new Date(),
+ message : msg,
+ category : cat || this.get('defaultCategory'),
+ sourceAndDetail : src || this.get('defaultSource'),
+ source : null,
+ localTime : null,
+ elapsedTime : null,
+ totalTime : null
+ };
+
+ // Extract m.source "Foo" from m.sourceAndDetail "Foo bar baz"
+ m.source = RE_INLINE_SOURCE.test(m.sourceAndDetail) ?
+ RegExp.$1 : m.sourceAndDetail;
+ m.localTime = m.time.toLocaleTimeString ?
+ m.time.toLocaleTimeString() : (m.time + '');
+ m.elapsedTime = m.time - this.get(LAST_TIME);
+ m.totalTime = m.time - this.get(START_TIME);
+
+ this._set(LAST_TIME,m.time);
+
+ return m;
+ },
+
+ /**
+ * Sets an interval for buffered messages to be output to the console.
+ *
+ * @method _schedulePrint
+ * @protected
+ */
+ _schedulePrint : function () {
+ if (!this._printLoop && !this.get(PAUSED) && this.get('rendered')) {
+ this._printLoop = Y.later(
+ this.get('printTimeout'),
+ this, this.printBuffer,
+ this.get('printLimit'), true);
+ }
+ },
+
+ /**
+ * Translates message meta into the markup for a console entry.
+ *
+ * @method _createEntryHTML
+ * @param m {Object} object literal containing normalized message metadata
+ * @return String
+ * @protected
+ */
+ _createEntryHTML : function (m) {
+ m = merge(
+ this._htmlEscapeMessage(m),
+ Console.ENTRY_CLASSES,
+ {
+ cat_class : this.getClassName(ENTRY,m.category),
+ src_class : this.getClassName(ENTRY,m.source)
+ });
+
+ return this.get('entryTemplate').replace(/\{(\w+)\}/g,
+ function (_,token) {
+ return token in m ? m[token] : '';
+ });
+ },
+
+ /**
+ * Scrolls to the most recent entry
+ *
+ * @method scrollToLatest
+ * @chainable
+ */
+ scrollToLatest : function () {
+ var scrollTop = this.get('newestOnTop') ?
+ 0 :
+ this._body.get('scrollHeight');
+
+ this._body.set('scrollTop', scrollTop);
+ },
+
+ /**
+ * Performs HTML escaping on strings in the message object.
+ *
+ * @method _htmlEscapeMessage
+ * @param m {Object} the normalized message object
+ * @return Object the message object with proper escapement
+ * @protected
+ */
+ _htmlEscapeMessage : function (m) {
+ m.message = this._encodeHTML(m.message);
+ m.source = this._encodeHTML(m.source);
+ m.sourceAndDetail = this._encodeHTML(m.sourceAndDetail);
+ m.category = this._encodeHTML(m.category);
+
+ return m;
+ },
+
+ /**
+ * Removes the oldest message entries from the UI to maintain the limit
+ * specified in the consoleLimit configuration.
+ *
+ * @method _trimOldEntries
+ * @protected
+ */
+ _trimOldEntries : function () {
+ // Turn off the logging system for the duration of this operation
+ // to prevent an infinite loop
+ Y.config.debug = false;
+
+ var bd = this._body,
+ limit = this.get('consoleLimit'),
+ debug = Y.config.debug,
+ entries,e,i,l;
+
+ if (bd) {
+ entries = bd.queryAll(DOT+C_ENTRY);
+ l = entries.size() - limit;
+
+ if (l > 0) {
+ if (this.get('newestOnTop')) {
+ i = limit;
+ l = entries.size();
+ } else {
+ i = 0;
+ }
+
+ this._body.setStyle('display','none');
+
+ for (;i < l; ++i) {
+ e = entries.item(i);
+ if (e) {
+ e.remove();
+ }
+ }
+
+ this._body.setStyle('display','');
+ }
+
+ }
+
+ Y.config.debug = debug;
+ },
+
+ /**
+ * Returns the input string with ampersands (&), <, and > encoded
+ * as HTML entities.
+ *
+ * @method _encodeHTML
+ * @param s {String} the raw string
+ * @return String the encoded string
+ * @protected
+ */
+ _encodeHTML : function (s) {
+ return isString(s) ?
+ s.replace(RE_AMP,ESC_AMP).
+ replace(RE_LT, ESC_LT).
+ replace(RE_GT, ESC_GT) :
+ s;
+ },
+
+ /**
+ * Clears the timeout for printing buffered messages.
+ *
+ * @method _cancelPrintLoop
+ * @protected
+ */
+ _cancelPrintLoop : function () {
+ if (this._printLoop) {
+ this._printLoop.cancel();
+ this._printLoop = null;
+ }
+ },
+
+ /**
+ * Validates input value for style attribute. Accepts only values 'inline',
+ * 'block', and 'separate'.
+ *
+ * @method _validateStyle
+ * @param style {String} the proposed value
+ * @return {Boolean} pass/fail
+ * @protected
+ */
+ _validateStyle : function (style) {
+ return style === 'inline' || style === 'block' || style === 'separate';
+ },
+
+ /**
+ * Event handler for clicking on the Pause checkbox to update the paused
+ * attribute.
+ *
+ * @method _onPauseClick
+ * @param e {Event} DOM event facade for the click event
+ * @protected
+ */
+ _onPauseClick : function (e) {
+ this.set(PAUSED,e.target.get(CHECKED));
+ },
+
+ /**
+ * Event handler for clicking on the Clear button. Pass-through to
+ * <code>this.clearConsole()</code>.
+ *
+ * @method _onClearClick
+ * @param e {Event} DOM event facade for the click event
+ * @protected
+ */
+ _onClearClick : function (e) {
+ this.clearConsole();
+ },
+
+ /**
+ * Event handler for clicking on the Collapse/Expand button. Sets the
+ * "collapsed" attribute accordingly.
+ *
+ * @method _onCollapseClick
+ * @param e {Event} DOM event facade for the click event
+ * @protected
+ */
+ _onCollapseClick : function (e) {
+ this.set(COLLAPSED, !this.get(COLLAPSED));
+ },
+
+
+ /**
+ * Setter method for logLevel attribute. Acceptable values are
+ * "error", "warn", and "info" (case
+ * insensitive). Other values are treated as "info".
+ *
+ * @method _setLogLevel
+ * @param v {String} the desired log level
+ * @return String One of Console.LOG_LEVEL_INFO, _WARN, or _ERROR
+ * @protected
+ */
+ _setLogLevel : function (v) {
+ if (isString(v)) {
+ v = v.toLowerCase();
+ }
+
+ return (v === WARN || v === ERROR) ? v : INFO;
+ },
+
+ /**
+ * Set the height of the Console container. Set the body height to the difference between the configured height and the calculated heights of the header and footer.
+ * Overrides Widget.prototype._uiSetHeight.
+ *
+ * @method _uiSetHeight
+ * @param v {String|Number} the new height
+ * @protected
+ */
+ _uiSetHeight : function (v) {
+ Console.superclass._uiSetHeight.apply(this,arguments);
+
+ if (this._head && this._foot) {
+ var h = this.get('boundingBox').get('offsetHeight') -
+ this._head.get('offsetHeight') -
+ this._foot.get('offsetHeight');
+
+ this._body.setStyle(HEIGHT,h+'px');
+ }
+ },
+
+ /**
+ * Updates the UI if changes are made to any of the strings in the strings
+ * attribute.
+ *
+ * @method _afterStringsChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterStringsChange : function (e) {
+ var prop = e.subAttrName ? e.subAttrName.split(DOT)[1] : null,
+ cb = this.get(CONTENT_BOX),
+ before = e.prevVal,
+ after = e.newVal;
+
+ if ((!prop || prop === TITLE) && before.title !== after.title) {
+ cb.queryAll(DOT+C_CONSOLE_TITLE).set(INNER_HTML, after.title);
+ }
+
+ if ((!prop || prop === PAUSE) && before.pause !== after.pause) {
+ cb.queryAll(DOT+C_PAUSE_LABEL).set(INNER_HTML, after.pause);
+ }
+
+ if ((!prop || prop === CLEAR) && before.clear !== after.clear) {
+ cb.queryAll(DOT+C_CLEAR).set('value',after.clear);
+ }
+ },
+
+ /**
+ * Updates the UI and schedules or cancels the print loop.
+ *
+ * @method _afterPausedChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterPausedChange : function (e) {
+ var paused = e.newVal;
+
+ if (e.src !== Y.Widget.SRC_UI) {
+ this._uiUpdatePaused(paused);
+ }
+
+ if (!paused) {
+ this._schedulePrint();
+ } else if (this._printLoop) {
+ this._cancelPrintLoop();
+ }
+ },
+
+ /**
+ * Checks or unchecks the paused checkbox
+ *
+ * @method _uiUpdatePaused
+ * @param on {Boolean} the new checked state
+ * @protected
+ */
+ _uiUpdatePaused : function (on) {
+ var node = this._foot.queryAll('input[type=checkbox].'+C_PAUSE);
+
+ if (node) {
+ node.set(CHECKED,on);
+ }
+ },
+
+ /**
+ * Calls this._trimOldEntries() in response to changes in the configured
+ * consoleLimit attribute.
+ *
+ * @method _afterConsoleLimitChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterConsoleLimitChange : function () {
+ this._trimOldEntries();
+ },
+
+
+ /**
+ * Updates the className of the contentBox, which should trigger CSS to
+ * hide or show the body and footer sections depending on the new value.
+ *
+ * @method _afterCollapsedChange
+ * @param e {Event} Custom event for the attribute change
+ * @protected
+ */
+ _afterCollapsedChange : function (e) {
+ this._uiUpdateCollapsed(e.newVal);
+ },
+
+ /**
+ * Updates the UI to reflect the new Collapsed state
+ *
+ * @method _uiUpdateCollapsed
+ * @param v {Boolean} true for collapsed, false for expanded
+ * @protected
+ */
+ _uiUpdateCollapsed : function (v) {
+ var bb = this.get('boundingBox'),
+ button = bb.queryAll('button.'+C_COLLAPSE),
+ method = v ? 'addClass' : 'removeClass',
+ str = this.get('strings.'+(v ? 'expand' : 'collapse'));
+
+ bb[method](C_COLLAPSED);
+
+ if (button) {
+ button.set('innerHTML',str);
+ }
+
+ this._uiSetHeight(v ? this._head.get('offsetHeight'): this.get(HEIGHT));
+ },
+
+ /**
+ * Makes adjustments to the UI if needed when the Console is hidden or shown
+ *
+ * @method _afterVisibleChange
+ * @param e {Event} the visibleChange event
+ * @protected
+ */
+ _afterVisibleChange : function (e) {
+ Console.superclass._afterVisibleChange.apply(this,arguments);
+
+ this._uiUpdateFromHideShow(e.newVal);
+ },
+
+ /**
+ * Recalculates dimensions and updates appropriately when shown
+ *
+ * @method _uiUpdateFromHideShow
+ * @param v {Boolean} true for visible, false for hidden
+ * @protected
+ */
+ _uiUpdateFromHideShow : function (v) {
+ if (v) {
+ this._uiSetHeight(this.get(HEIGHT));
+ }
+ },
+
+ /**
+ * Responds to log events by normalizing qualifying messages and passing
+ * them along through the entry event for buffering etc.
+ *
+ * @method _onLogEvent
+ * @param msg {String} the log message
+ * @param cat {String} OPTIONAL the category or logLevel of the message
+ * @param src {String} OPTIONAL the source of the message (e.g. widget name)
+ * @protected
+ */
+ _onLogEvent : function (e) {
+
+ if (!this.get(DISABLED) && this._isInLogLevel(e)) {
+
+ var debug = Y.config.debug;
+
+ /* TODO: needed? */
+ Y.config.debug = false;
+
+ this.fire(ENTRY, {
+ message : this._normalizeMessage(e)
+ });
+
+ Y.config.debug = debug;
+ }
+ },
+
+ /**
+ * Clears the console, resets the startTime attribute, enables and
+ * unpauses the widget.
+ *
+ * @method _defResetFn
+ * @protected
+ */
+ _defResetFn : function () {
+ this.clearConsole();
+ this.set(START_TIME,new Date());
+ this.set(DISABLED,false);
+ this.set(PAUSED,false);
+ },
+
+ /**
+ * Buffers incoming message objects and schedules the printing.
+ *
+ * @method _defEntryFn
+ * @param e {Event} The Custom event carrying the message in its payload
+ * @protected
+ */
+ _defEntryFn : function (e) {
+ if (e.message) {
+ this.buffer.push(e.message);
+ this._schedulePrint();
+ }
+ }
+
+});
+
+Y.Console = Console;
+
+
+}, '3.0.0' ,{requires:['substitute','widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('cookie', function(Y) {
+
+/**
+ * Utilities for cookie management
+ * @module cookie
+ */
+
+ //shortcuts
+ var L = Y.Lang,
+ O = Y.Object,
+ NULL = null,
+
+ //shortcuts to functions
+ isString = L.isString,
+ isObject = L.isObject,
+ isUndefined = L.isUndefined,
+ isFunction = L.isFunction,
+ encode = encodeURIComponent,
+ decode = decodeURIComponent,
+
+ //shortcut to document
+ doc = Y.config.doc;
+
+ /*
+ * Throws an error message.
+ */
+ function error(message){
+ throw new TypeError(message);
+ }
+
+ /*
+ * Checks the validity of a cookie name.
+ */
+ function validateCookieName(name){
+ if (!isString(name) || name === ""){
+ error("Cookie name must be a non-empty string.");
+ }
+ }
+
+ /*
+ * Checks the validity of a subcookie name.
+ */
+ function validateSubcookieName(subName){
+ if (!isString(subName) || subName === ""){
+ error("Subcookie name must be a non-empty string.");
+ }
+ }
+
+ /**
+ * Cookie utility.
+ * @class Cookie
+ * @static
+ */
+ Y.Cookie = {
+
+ //-------------------------------------------------------------------------
+ // Private Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Creates a cookie string that can be assigned into document.cookie.
+ * @param {String} name The name of the cookie.
+ * @param {String} value The value of the cookie.
+ * @param {Boolean} encodeValue True to encode the value, false to leave as-is.
+ * @param {Object} options (Optional) Options for the cookie.
+ * @return {String} The formatted cookie string.
+ * @method _createCookieString
+ * @private
+ * @static
+ */
+ _createCookieString : function (name /*:String*/, value /*:Variant*/, encodeValue /*:Boolean*/, options /*:Object*/) /*:String*/ {
+
+ options = options || {};
+
+ var text /*:String*/ = encode(name) + "=" + (encodeValue ? encode(value) : value),
+ expires = options.expires,
+ path = options.path,
+ domain = options.domain;
+
+
+ if (isObject(options)){
+ //expiration date
+ if (expires instanceof Date){
+ text += "; expires=" + expires.toUTCString();
+ }
+
+ //path
+ if (isString(path) && path !== ""){
+ text += "; path=" + path;
+ }
+
+ //domain
+ if (isString(domain) && domain !== ""){
+ text += "; domain=" + domain;
+ }
+
+ //secure
+ if (options.secure === true){
+ text += "; secure";
+ }
+ }
+
+ return text;
+ },
+
+ /**
+ * Formats a cookie value for an object containing multiple values.
+ * @param {Object} hash An object of key-value pairs to create a string for.
+ * @return {String} A string suitable for use as a cookie value.
+ * @method _createCookieHashString
+ * @private
+ * @static
+ */
+ _createCookieHashString : function (hash /*:Object*/) /*:String*/ {
+ if (!isObject(hash)){
+ error("Cookie._createCookieHashString(): Argument must be an object.");
+ }
+
+ var text /*:Array*/ = [];
+
+ O.each(hash, function(value, key){
+ if (!isFunction(value) && !isUndefined(value)){
+ text.push(encode(key) + "=" + encode(String(value)));
+ }
+ });
+
+ return text.join("&");
+ },
+
+ /**
+ * Parses a cookie hash string into an object.
+ * @param {String} text The cookie hash string to parse (format: n1=v1&n2=v2).
+ * @return {Object} An object containing entries for each cookie value.
+ * @method _parseCookieHash
+ * @private
+ * @static
+ */
+ _parseCookieHash : function (text) {
+
+ var hashParts = text.split("&"),
+ hashPart = NULL,
+ hash = {};
+
+ if (text.length){
+ for (var i=0, len=hashParts.length; i < len; i++){
+ hashPart = hashParts[i].split("=");
+ hash[decode(hashPart[0])] = decode(hashPart[1]);
+ }
+ }
+
+ return hash;
+ },
+
+ /**
+ * Parses a cookie string into an object representing all accessible cookies.
+ * @param {String} text The cookie string to parse.
+ * @param {Boolean} shouldDecode (Optional) Indicates if the cookie values should be decoded or not. Default is true.
+ * @return {Object} An object containing entries for each accessible cookie.
+ * @method _parseCookieString
+ * @private
+ * @static
+ */
+ _parseCookieString : function (text /*:String*/, shouldDecode /*:Boolean*/) /*:Object*/ {
+
+ var cookies /*:Object*/ = {};
+
+ if (isString(text) && text.length > 0) {
+
+ var decodeValue = (shouldDecode === false ? function(s){return s;} : decode),
+ cookieParts = text.split(/;\s/g),
+ cookieName = NULL,
+ cookieValue = NULL,
+ cookieNameValue = NULL;
+
+ for (var i=0, len=cookieParts.length; i < len; i++){
+
+ //check for normally-formatted cookie (name-value)
+ cookieNameValue = cookieParts[i].match(/([^=]+)=/i);
+ if (cookieNameValue instanceof Array){
+ try {
+ cookieName = decode(cookieNameValue[1]);
+ cookieValue = decodeValue(cookieParts[i].substring(cookieNameValue[1].length+1));
+ } catch (ex){
+ //intentionally ignore the cookie - the encoding is wrong
+ }
+ } else {
+ //means the cookie does not have an "=", so treat it as a boolean flag
+ cookieName = decode(cookieParts[i]);
+ cookieValue = "";
+ }
+ cookies[cookieName] = cookieValue;
+ }
+
+ }
+
+ return cookies;
+ },
+
+ //-------------------------------------------------------------------------
+ // Public Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Determines if the cookie with the given name exists. This is useful for
+ * Boolean cookies (those that do not follow the name=value convention).
+ * @param {String} name The name of the cookie to check.
+ * @return {Boolean} True if the cookie exists, false if not.
+ * @method exists
+ * @static
+ */
+ exists: function(name) {
+
+ validateCookieName(name); //throws error
+
+ var cookies = this._parseCookieString(doc.cookie, true);
+
+ return cookies.hasOwnProperty(name);
+ },
+
+ /**
+ * Returns the cookie value for the given name.
+ * @param {String} name The name of the cookie to retrieve.
+ * @param {Function|Object} options (Optional) An object containing one or more
+ * cookie options: raw (true/false) and converter (a function).
+ * The converter function is run on the value before returning it. The
+ * function is not used if the cookie doesn't exist. The function can be
+ * passed instead of the options object for backwards compatibility. When
+ * raw is set to true, the cookie value is not URI decoded.
+ * @return {Variant} If no converter is specified, returns a string or null if
+ * the cookie doesn't exist. If the converter is specified, returns the value
+ * returned from the converter or null if the cookie doesn't exist.
+ * @method get
+ * @static
+ */
+ get : function (name, options) {
+
+ validateCookieName(name); //throws error
+
+ var cookies,
+ cookie,
+ converter;
+
+ //if options is a function, then it's the converter
+ if (isFunction(options)) {
+ converter = options;
+ options = {};
+ } else if (isObject(options)) {
+ converter = options.converter;
+ } else {
+ options = {};
+ }
+
+ cookies = this._parseCookieString(doc.cookie, !options.raw);
+ cookie = cookies[name];
+
+ //should return null, not undefined if the cookie doesn't exist
+ if (isUndefined(cookie)) {
+ return NULL;
+ }
+
+ if (!isFunction(converter)){
+ return cookie;
+ } else {
+ return converter(cookie);
+ }
+ },
+
+ /**
+ * Returns the value of a subcookie.
+ * @param {String} name The name of the cookie to retrieve.
+ * @param {String} subName The name of the subcookie to retrieve.
+ * @param {Function} converter (Optional) A function to run on the value before returning
+ * it. The function is not used if the cookie doesn't exist.
+ * @return {Variant} If the cookie doesn't exist, null is returned. If the subcookie
+ * doesn't exist, null if also returned. If no converter is specified and the
+ * subcookie exists, a string is returned. If a converter is specified and the
+ * subcookie exists, the value returned from the converter is returned.
+ * @method getSub
+ * @static
+ */
+ getSub : function (name /*:String*/, subName /*:String*/, converter /*:Function*/) /*:Variant*/ {
+
+ var hash /*:Variant*/ = this.getSubs(name);
+
+ if (hash !== NULL) {
+
+ validateSubcookieName(subName); //throws error
+
+ if (isUndefined(hash[subName])){
+ return NULL;
+ }
+
+ if (!isFunction(converter)){
+ return hash[subName];
+ } else {
+ return converter(hash[subName]);
+ }
+ } else {
+ return NULL;
+ }
+
+ },
+
+ /**
+ * Returns an object containing name-value pairs stored in the cookie with the given name.
+ * @param {String} name The name of the cookie to retrieve.
+ * @return {Object} An object of name-value pairs if the cookie with the given name
+ * exists, null if it does not.
+ * @method getSubs
+ * @static
+ */
+ getSubs : function (name) {
+
+ validateCookieName(name); //throws error
+
+ var cookies = this._parseCookieString(doc.cookie, false);
+ if (isString(cookies[name])){
+ return this._parseCookieHash(cookies[name]);
+ }
+ return NULL;
+ },
+
+ /**
+ * Removes a cookie from the machine by setting its expiration date to
+ * sometime in the past.
+ * @param {String} name The name of the cookie to remove.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string),
+ * and secure (true/false). The expires option will be overwritten
+ * by the method.
+ * @return {String} The created cookie string.
+ * @method remove
+ * @static
+ */
+ remove : function (name, options) {
+
+ validateCookieName(name); //throws error
+
+ //set options
+ options = Y.merge(options || {}, {
+ expires: new Date(0)
+ });
+
+ //set cookie
+ return this.set(name, "", options);
+ },
+
+ /**
+ * Removes a sub cookie with a given name.
+ * @param {String} name The name of the cookie in which the subcookie exists.
+ * @param {String} subName The name of the subcookie to remove.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * removeIfEmpty (true/false), and secure (true/false). This must be the same
+ * settings as the original subcookie.
+ * @return {String} The created cookie string.
+ * @method removeSub
+ * @static
+ */
+ removeSub : function(name, subName, options) {
+
+ validateCookieName(name); //throws error
+
+ validateSubcookieName(subName); //throws error
+
+ options = options || {};
+
+ //get all subcookies for this cookie
+ var subs = this.getSubs(name);
+
+ //delete the indicated subcookie
+ if (isObject(subs) && subs.hasOwnProperty(subName)){
+ delete subs[subName];
+
+ if (!options.removeIfEmpty) {
+ //reset the cookie
+
+ return this.setSubs(name, subs, options);
+ } else {
+ //reset the cookie if there are subcookies left, else remove
+ for (var key in subs){
+ if (subs.hasOwnProperty(key) && !isFunction(subs[key]) && !isUndefined(subs[key])){
+ return this.setSubs(name, subs, options);
+ }
+ }
+
+ return this.remove(name, options);
+ }
+ } else {
+ return "";
+ }
+
+ },
+
+ /**
+ * Sets a cookie with a given name and value.
+ * @param {String} name The name of the cookie to set.
+ * @param {Variant} value The value to set for the cookie.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * secure (true/false), and raw (true/false). Setting raw to true indicates
+ * that the cookie should not be URI encoded before being set.
+ * @return {String} The created cookie string.
+ * @method set
+ * @static
+ */
+ set : function (name, value, options) {
+
+ validateCookieName(name); //throws error
+
+ if (isUndefined(value)){
+ error("Cookie.set(): Value cannot be undefined.");
+ }
+
+ options = options || {};
+
+ var text = this._createCookieString(name, value, !options.raw, options);
+ doc.cookie = text;
+ return text;
+ },
+
+ /**
+ * Sets a sub cookie with a given name to a particular value.
+ * @param {String} name The name of the cookie to set.
+ * @param {String} subName The name of the subcookie to set.
+ * @param {Variant} value The value to set.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * and secure (true/false).
+ * @return {String} The created cookie string.
+ * @method setSub
+ * @static
+ */
+ setSub : function (name, subName, value, options) {
+
+ validateCookieName(name); //throws error
+
+ validateSubcookieName(subName); //throws error
+
+ if (isUndefined(value)){
+ error("Cookie.setSub(): Subcookie value cannot be undefined.");
+ }
+
+ var hash = this.getSubs(name);
+
+ if (!isObject(hash)){
+ hash = {};
+ }
+
+ hash[subName] = value;
+
+ return this.setSubs(name, hash, options);
+
+ },
+
+ /**
+ * Sets a cookie with a given name to contain a hash of name-value pairs.
+ * @param {String} name The name of the cookie to set.
+ * @param {Object} value An object containing name-value pairs.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * and secure (true/false).
+ * @return {String} The created cookie string.
+ * @method setSubs
+ * @static
+ */
+ setSubs : function (name, value, options) {
+
+ validateCookieName(name); //throws error
+
+ if (!isObject(value)){
+ error("Cookie.setSubs(): Cookie value must be an object.");
+ }
+
+ var text /*:String*/ = this._createCookieString(name, this._createCookieHashString(value), false, options);
+ doc.cookie = text;
+ return text;
+ }
+
+ };
+
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("cookie",function(C){var K=C.Lang,I=C.Object,G=null,D=K.isString,P=K.isObject,F=K.isUndefined,E=K.isFunction,H=encodeURIComponent,B=decodeURIComponent,N=C.config.doc;function J(L){throw new TypeError(L);}function M(L){if(!D(L)||L===""){J("Cookie name must be a non-empty string.");}}function A(L){if(!D(L)||L===""){J("Subcookie name must be a non-empty string.");}}C.Cookie={_createCookieString:function(Q,T,R,O){O=O||{};var V=H(Q)+"="+(R?H(T):T),L=O.expires,U=O.path,S=O.domain;if(P(O)){if(L instanceof Date){V+="; expires="+L.toUTCString();}if(D(U)&&U!==""){V+="; path="+U;}if(D(S)&&S!==""){V+="; domain="+S;}if(O.secure===true){V+="; secure";}}return V;},_createCookieHashString:function(L){if(!P(L)){J("Cookie._createCookieHashString(): Argument must be an object.");}var O=[];I.each(L,function(R,Q){if(!E(R)&&!F(R)){O.push(H(Q)+"="+H(String(R)));}});return O.join("&");},_parseCookieHash:function(S){var R=S.split("&"),T=G,Q={};if(S.length){for(var O=0,L=R.length;O<L;O++){T=R[O].split("=");Q[B(T[0])]=B(T[1]);}}return Q;},_parseCookieString:function(W,Y){var X={};if(D(W)&&W.length>0){var L=(Y===false?function(Z){return Z;}:B),U=W.split(/;\s/g),V=G,O=G,R=G;for(var Q=0,S=U.length;Q<S;Q++){R=U[Q].match(/([^=]+)=/i);if(R instanceof Array){try{V=B(R[1]);O=L(U[Q].substring(R[1].length+1));}catch(T){}}else{V=B(U[Q]);O="";}X[V]=O;}}return X;},exists:function(L){M(L);var O=this._parseCookieString(N.cookie,true);return O.hasOwnProperty(L);},get:function(O,L){M(O);var S,Q,R;if(E(L)){R=L;L={};}else{if(P(L)){R=L.converter;}else{L={};}}S=this._parseCookieString(N.cookie,!L.raw);Q=S[O];if(F(Q)){return G;}if(!E(R)){return Q;}else{return R(Q);}},getSub:function(L,Q,O){var R=this.getSubs(L);if(R!==G){A(Q);if(F(R[Q])){return G;}if(!E(O)){return R[Q];}else{return O(R[Q]);}}else{return G;}},getSubs:function(L){M(L);var O=this._parseCookieString(N.cookie,false);if(D(O[L])){return this._parseCookieHash(O[L]);}return G;},remove:function(O,L){M(O);L=C.merge(L||{},{expires:new Date(0)});return this.set(O,"",L);},removeSub:function(O,S,L){M(O);A(S);L=L||{};var R=this.getSubs(O);if(P(R)&&R.hasOwnProperty(S)){delete R[S];if(!L.removeIfEmpty){return this.setSubs(O,R,L);}else{for(var Q in R){if(R.hasOwnProperty(Q)&&!E(R[Q])&&!F(R[Q])){return this.setSubs(O,R,L);}}return this.remove(O,L);}}else{return"";}},set:function(O,Q,L){M(O);if(F(Q)){J("Cookie.set(): Value cannot be undefined.");}L=L||{};var R=this._createCookieString(O,Q,!L.raw,L);N.cookie=R;return R;},setSub:function(O,R,Q,L){M(O);A(R);if(F(Q)){J("Cookie.setSub(): Subcookie value cannot be undefined.");}var S=this.getSubs(O);if(!P(S)){S={};}S[R]=Q;return this.setSubs(O,S,L);},setSubs:function(O,Q,L){M(O);if(!P(Q)){J("Cookie.setSubs(): Cookie value must be an object.");}var R=this._createCookieString(O,this._createCookieHashString(Q),false,L);N.cookie=R;return R;}};},"3.0.0",{requires:["yui-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('cookie', function(Y) {
+
+/**
+ * Utilities for cookie management
+ * @module cookie
+ */
+
+ //shortcuts
+ var L = Y.Lang,
+ O = Y.Object,
+ NULL = null,
+
+ //shortcuts to functions
+ isString = L.isString,
+ isObject = L.isObject,
+ isUndefined = L.isUndefined,
+ isFunction = L.isFunction,
+ encode = encodeURIComponent,
+ decode = decodeURIComponent,
+
+ //shortcut to document
+ doc = Y.config.doc;
+
+ /*
+ * Throws an error message.
+ */
+ function error(message){
+ throw new TypeError(message);
+ }
+
+ /*
+ * Checks the validity of a cookie name.
+ */
+ function validateCookieName(name){
+ if (!isString(name) || name === ""){
+ error("Cookie name must be a non-empty string.");
+ }
+ }
+
+ /*
+ * Checks the validity of a subcookie name.
+ */
+ function validateSubcookieName(subName){
+ if (!isString(subName) || subName === ""){
+ error("Subcookie name must be a non-empty string.");
+ }
+ }
+
+ /**
+ * Cookie utility.
+ * @class Cookie
+ * @static
+ */
+ Y.Cookie = {
+
+ //-------------------------------------------------------------------------
+ // Private Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Creates a cookie string that can be assigned into document.cookie.
+ * @param {String} name The name of the cookie.
+ * @param {String} value The value of the cookie.
+ * @param {Boolean} encodeValue True to encode the value, false to leave as-is.
+ * @param {Object} options (Optional) Options for the cookie.
+ * @return {String} The formatted cookie string.
+ * @method _createCookieString
+ * @private
+ * @static
+ */
+ _createCookieString : function (name /*:String*/, value /*:Variant*/, encodeValue /*:Boolean*/, options /*:Object*/) /*:String*/ {
+
+ options = options || {};
+
+ var text /*:String*/ = encode(name) + "=" + (encodeValue ? encode(value) : value),
+ expires = options.expires,
+ path = options.path,
+ domain = options.domain;
+
+
+ if (isObject(options)){
+ //expiration date
+ if (expires instanceof Date){
+ text += "; expires=" + expires.toUTCString();
+ }
+
+ //path
+ if (isString(path) && path !== ""){
+ text += "; path=" + path;
+ }
+
+ //domain
+ if (isString(domain) && domain !== ""){
+ text += "; domain=" + domain;
+ }
+
+ //secure
+ if (options.secure === true){
+ text += "; secure";
+ }
+ }
+
+ return text;
+ },
+
+ /**
+ * Formats a cookie value for an object containing multiple values.
+ * @param {Object} hash An object of key-value pairs to create a string for.
+ * @return {String} A string suitable for use as a cookie value.
+ * @method _createCookieHashString
+ * @private
+ * @static
+ */
+ _createCookieHashString : function (hash /*:Object*/) /*:String*/ {
+ if (!isObject(hash)){
+ error("Cookie._createCookieHashString(): Argument must be an object.");
+ }
+
+ var text /*:Array*/ = [];
+
+ O.each(hash, function(value, key){
+ if (!isFunction(value) && !isUndefined(value)){
+ text.push(encode(key) + "=" + encode(String(value)));
+ }
+ });
+
+ return text.join("&");
+ },
+
+ /**
+ * Parses a cookie hash string into an object.
+ * @param {String} text The cookie hash string to parse (format: n1=v1&n2=v2).
+ * @return {Object} An object containing entries for each cookie value.
+ * @method _parseCookieHash
+ * @private
+ * @static
+ */
+ _parseCookieHash : function (text) {
+
+ var hashParts = text.split("&"),
+ hashPart = NULL,
+ hash = {};
+
+ if (text.length){
+ for (var i=0, len=hashParts.length; i < len; i++){
+ hashPart = hashParts[i].split("=");
+ hash[decode(hashPart[0])] = decode(hashPart[1]);
+ }
+ }
+
+ return hash;
+ },
+
+ /**
+ * Parses a cookie string into an object representing all accessible cookies.
+ * @param {String} text The cookie string to parse.
+ * @param {Boolean} shouldDecode (Optional) Indicates if the cookie values should be decoded or not. Default is true.
+ * @return {Object} An object containing entries for each accessible cookie.
+ * @method _parseCookieString
+ * @private
+ * @static
+ */
+ _parseCookieString : function (text /*:String*/, shouldDecode /*:Boolean*/) /*:Object*/ {
+
+ var cookies /*:Object*/ = {};
+
+ if (isString(text) && text.length > 0) {
+
+ var decodeValue = (shouldDecode === false ? function(s){return s;} : decode),
+ cookieParts = text.split(/;\s/g),
+ cookieName = NULL,
+ cookieValue = NULL,
+ cookieNameValue = NULL;
+
+ for (var i=0, len=cookieParts.length; i < len; i++){
+
+ //check for normally-formatted cookie (name-value)
+ cookieNameValue = cookieParts[i].match(/([^=]+)=/i);
+ if (cookieNameValue instanceof Array){
+ try {
+ cookieName = decode(cookieNameValue[1]);
+ cookieValue = decodeValue(cookieParts[i].substring(cookieNameValue[1].length+1));
+ } catch (ex){
+ //intentionally ignore the cookie - the encoding is wrong
+ }
+ } else {
+ //means the cookie does not have an "=", so treat it as a boolean flag
+ cookieName = decode(cookieParts[i]);
+ cookieValue = "";
+ }
+ cookies[cookieName] = cookieValue;
+ }
+
+ }
+
+ return cookies;
+ },
+
+ //-------------------------------------------------------------------------
+ // Public Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Determines if the cookie with the given name exists. This is useful for
+ * Boolean cookies (those that do not follow the name=value convention).
+ * @param {String} name The name of the cookie to check.
+ * @return {Boolean} True if the cookie exists, false if not.
+ * @method exists
+ * @static
+ */
+ exists: function(name) {
+
+ validateCookieName(name); //throws error
+
+ var cookies = this._parseCookieString(doc.cookie, true);
+
+ return cookies.hasOwnProperty(name);
+ },
+
+ /**
+ * Returns the cookie value for the given name.
+ * @param {String} name The name of the cookie to retrieve.
+ * @param {Function|Object} options (Optional) An object containing one or more
+ * cookie options: raw (true/false) and converter (a function).
+ * The converter function is run on the value before returning it. The
+ * function is not used if the cookie doesn't exist. The function can be
+ * passed instead of the options object for backwards compatibility. When
+ * raw is set to true, the cookie value is not URI decoded.
+ * @return {Variant} If no converter is specified, returns a string or null if
+ * the cookie doesn't exist. If the converter is specified, returns the value
+ * returned from the converter or null if the cookie doesn't exist.
+ * @method get
+ * @static
+ */
+ get : function (name, options) {
+
+ validateCookieName(name); //throws error
+
+ var cookies,
+ cookie,
+ converter;
+
+ //if options is a function, then it's the converter
+ if (isFunction(options)) {
+ converter = options;
+ options = {};
+ } else if (isObject(options)) {
+ converter = options.converter;
+ } else {
+ options = {};
+ }
+
+ cookies = this._parseCookieString(doc.cookie, !options.raw);
+ cookie = cookies[name];
+
+ //should return null, not undefined if the cookie doesn't exist
+ if (isUndefined(cookie)) {
+ return NULL;
+ }
+
+ if (!isFunction(converter)){
+ return cookie;
+ } else {
+ return converter(cookie);
+ }
+ },
+
+ /**
+ * Returns the value of a subcookie.
+ * @param {String} name The name of the cookie to retrieve.
+ * @param {String} subName The name of the subcookie to retrieve.
+ * @param {Function} converter (Optional) A function to run on the value before returning
+ * it. The function is not used if the cookie doesn't exist.
+ * @return {Variant} If the cookie doesn't exist, null is returned. If the subcookie
+ * doesn't exist, null if also returned. If no converter is specified and the
+ * subcookie exists, a string is returned. If a converter is specified and the
+ * subcookie exists, the value returned from the converter is returned.
+ * @method getSub
+ * @static
+ */
+ getSub : function (name /*:String*/, subName /*:String*/, converter /*:Function*/) /*:Variant*/ {
+
+ var hash /*:Variant*/ = this.getSubs(name);
+
+ if (hash !== NULL) {
+
+ validateSubcookieName(subName); //throws error
+
+ if (isUndefined(hash[subName])){
+ return NULL;
+ }
+
+ if (!isFunction(converter)){
+ return hash[subName];
+ } else {
+ return converter(hash[subName]);
+ }
+ } else {
+ return NULL;
+ }
+
+ },
+
+ /**
+ * Returns an object containing name-value pairs stored in the cookie with the given name.
+ * @param {String} name The name of the cookie to retrieve.
+ * @return {Object} An object of name-value pairs if the cookie with the given name
+ * exists, null if it does not.
+ * @method getSubs
+ * @static
+ */
+ getSubs : function (name) {
+
+ validateCookieName(name); //throws error
+
+ var cookies = this._parseCookieString(doc.cookie, false);
+ if (isString(cookies[name])){
+ return this._parseCookieHash(cookies[name]);
+ }
+ return NULL;
+ },
+
+ /**
+ * Removes a cookie from the machine by setting its expiration date to
+ * sometime in the past.
+ * @param {String} name The name of the cookie to remove.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string),
+ * and secure (true/false). The expires option will be overwritten
+ * by the method.
+ * @return {String} The created cookie string.
+ * @method remove
+ * @static
+ */
+ remove : function (name, options) {
+
+ validateCookieName(name); //throws error
+
+ //set options
+ options = Y.merge(options || {}, {
+ expires: new Date(0)
+ });
+
+ //set cookie
+ return this.set(name, "", options);
+ },
+
+ /**
+ * Removes a sub cookie with a given name.
+ * @param {String} name The name of the cookie in which the subcookie exists.
+ * @param {String} subName The name of the subcookie to remove.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * removeIfEmpty (true/false), and secure (true/false). This must be the same
+ * settings as the original subcookie.
+ * @return {String} The created cookie string.
+ * @method removeSub
+ * @static
+ */
+ removeSub : function(name, subName, options) {
+
+ validateCookieName(name); //throws error
+
+ validateSubcookieName(subName); //throws error
+
+ options = options || {};
+
+ //get all subcookies for this cookie
+ var subs = this.getSubs(name);
+
+ //delete the indicated subcookie
+ if (isObject(subs) && subs.hasOwnProperty(subName)){
+ delete subs[subName];
+
+ if (!options.removeIfEmpty) {
+ //reset the cookie
+
+ return this.setSubs(name, subs, options);
+ } else {
+ //reset the cookie if there are subcookies left, else remove
+ for (var key in subs){
+ if (subs.hasOwnProperty(key) && !isFunction(subs[key]) && !isUndefined(subs[key])){
+ return this.setSubs(name, subs, options);
+ }
+ }
+
+ return this.remove(name, options);
+ }
+ } else {
+ return "";
+ }
+
+ },
+
+ /**
+ * Sets a cookie with a given name and value.
+ * @param {String} name The name of the cookie to set.
+ * @param {Variant} value The value to set for the cookie.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * secure (true/false), and raw (true/false). Setting raw to true indicates
+ * that the cookie should not be URI encoded before being set.
+ * @return {String} The created cookie string.
+ * @method set
+ * @static
+ */
+ set : function (name, value, options) {
+
+ validateCookieName(name); //throws error
+
+ if (isUndefined(value)){
+ error("Cookie.set(): Value cannot be undefined.");
+ }
+
+ options = options || {};
+
+ var text = this._createCookieString(name, value, !options.raw, options);
+ doc.cookie = text;
+ return text;
+ },
+
+ /**
+ * Sets a sub cookie with a given name to a particular value.
+ * @param {String} name The name of the cookie to set.
+ * @param {String} subName The name of the subcookie to set.
+ * @param {Variant} value The value to set.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * and secure (true/false).
+ * @return {String} The created cookie string.
+ * @method setSub
+ * @static
+ */
+ setSub : function (name, subName, value, options) {
+
+ validateCookieName(name); //throws error
+
+ validateSubcookieName(subName); //throws error
+
+ if (isUndefined(value)){
+ error("Cookie.setSub(): Subcookie value cannot be undefined.");
+ }
+
+ var hash = this.getSubs(name);
+
+ if (!isObject(hash)){
+ hash = {};
+ }
+
+ hash[subName] = value;
+
+ return this.setSubs(name, hash, options);
+
+ },
+
+ /**
+ * Sets a cookie with a given name to contain a hash of name-value pairs.
+ * @param {String} name The name of the cookie to set.
+ * @param {Object} value An object containing name-value pairs.
+ * @param {Object} options (Optional) An object containing one or more
+ * cookie options: path (a string), domain (a string), expires (a Date object),
+ * and secure (true/false).
+ * @return {String} The created cookie string.
+ * @method setSubs
+ * @static
+ */
+ setSubs : function (name, value, options) {
+
+ validateCookieName(name); //throws error
+
+ if (!isObject(value)){
+ error("Cookie.setSubs(): Cookie value must be an object.");
+ }
+
+ var text /*:String*/ = this._createCookieString(name, this._createCookieHashString(value), false, options);
+ doc.cookie = text;
+ return text;
+ }
+
+ };
+
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-cssbase h1{font-size:138.5%;}.yui-cssbase h2{font-size:123.1%;}.yui-cssbase h3{font-size:108%;}.yui-cssbase h1,.yui-cssbase h2,.yui-cssbase h3{margin:1em 0;}.yui-cssbase h1,.yui-cssbase h2,.yui-cssbase h3,.yui-cssbase h4,.yui-cssbase h5,.yui-cssbase h6,.yui-cssbase strong{font-weight:bold;}.yui-cssbase abbr,.yui-cssbase acronym{border-bottom:1px dotted #000;cursor:help;}.yui-cssbase em{font-style:italic;}.yui-cssbase blockquote,.yui-cssbase ul,.yui-cssbase ol,.yui-cssbase dl{margin:1em;}.yui-cssbase ol,.yui-cssbase ul,.yui-cssbase dl{margin-left:2em;}.yui-cssbase ol li{list-style:decimal outside;}.yui-cssbase ul li{list-style:disc outside;}.yui-cssbase dl dd{margin-left:1em;}.yui-cssbase th,.yui-cssbase td{border:1px solid #000;padding:.5em;}.yui-cssbase th{font-weight:bold;text-align:center;}.yui-cssbase caption{margin-bottom:.5em;text-align:center;}.yui-cssbase p,.yui-cssbase fieldset,.yui-cssbase table,.yui-cssbase pre{margin-bottom:1em;}.yui-cssbase input[type=text],.yui-cssbase input[type=password],.yui-cssbase textarea{width:12.25em;*width:11.9em;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/* base.css, part of YUI's CSS Foundation */
+.yui-cssbase h1 {
+ /*18px via YUI Fonts CSS foundation*/
+ font-size:138.5%;
+}
+.yui-cssbase h2 {
+ /*16px via YUI Fonts CSS foundation*/
+ font-size:123.1%;
+}
+.yui-cssbase h3 {
+ /*14px via YUI Fonts CSS foundation*/
+ font-size:108%;
+}
+.yui-cssbase h1,.yui-cssbase h2,.yui-cssbase h3 {
+ /* top & bottom margin based on font size */
+ margin:1em 0;
+}
+.yui-cssbase h1,.yui-cssbase h2,.yui-cssbase h3,.yui-cssbase h4,.yui-cssbase h5,.yui-cssbase h6,.yui-cssbase strong {
+ /*bringing boldness back to headers and the strong element*/
+ font-weight:bold;
+}
+.yui-cssbase abbr,.yui-cssbase acronym {
+ /*indicating to users that more info is available */
+ border-bottom:1px dotted #000;
+ cursor:help;
+}
+.yui-cssbase em {
+ /*bringing italics back to the em element*/
+ font-style:italic;
+}
+.yui-cssbase blockquote,.yui-cssbase ul,.yui-cssbase ol,.yui-cssbase dl {
+ /*giving blockquotes and lists room to breath*/
+ margin:1em;
+}
+.yui-cssbase ol,.yui-cssbase ul,.yui-cssbase dl {
+ /*bringing lists on to the page with breathing room */
+ margin-left:2em;
+}
+.yui-cssbase ol li {
+ /*giving OL's LIs generated numbers*/
+ list-style: decimal outside;
+}
+.yui-cssbase ul li {
+ /*giving UL's LIs generated disc markers*/
+ list-style: disc outside;
+}
+.yui-cssbase dl dd {
+ /*providing spacing for definition terms*/
+ margin-left:1em;
+}
+.yui-cssbase th,.yui-cssbase td {
+ /*borders and padding to make the table readable*/
+ border:1px solid #000;
+ padding:.5em;
+}
+.yui-cssbase th {
+ /*distinguishing table headers from data cells*/
+ font-weight:bold;
+ text-align:center;
+}
+.yui-cssbase caption {
+ /*coordinated margin to match cell's padding*/
+ margin-bottom:.5em;
+ /*centered so it doesn't blend in to other content*/
+ text-align:center;
+}
+.yui-cssbase p,.yui-cssbase fieldset,.yui-cssbase table,.yui-cssbase pre {
+ /*so things don't run into each other*/
+ margin-bottom:1em;
+}
+/* setting a consistent width, 160px;
+ control of type=file still not possible */
+.yui-cssbase input[type=text],.yui-cssbase input[type=password],.yui-cssbase textarea{width:12.25em;*width:11.9em;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+h1{font-size:138.5%;}h2{font-size:123.1%;}h3{font-size:108%;}h1,h2,h3{margin:1em 0;}h1,h2,h3,h4,h5,h6,strong{font-weight:bold;}abbr,acronym{border-bottom:1px dotted #000;cursor:help;}em{font-style:italic;}blockquote,ul,ol,dl{margin:1em;}ol,ul,dl{margin-left:2em;}ol li{list-style:decimal outside;}ul li{list-style:disc outside;}dl dd{margin-left:1em;}th,td{border:1px solid #000;padding:.5em;}th{font-weight:bold;text-align:center;}caption{margin-bottom:.5em;text-align:center;}p,fieldset,table,pre{margin-bottom:1em;}input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/* base.css, part of YUI's CSS Foundation */
+h1 {
+ /*18px via YUI Fonts CSS foundation*/
+ font-size:138.5%;
+}
+h2 {
+ /*16px via YUI Fonts CSS foundation*/
+ font-size:123.1%;
+}
+h3 {
+ /*14px via YUI Fonts CSS foundation*/
+ font-size:108%;
+}
+h1,h2,h3 {
+ /* top & bottom margin based on font size */
+ margin:1em 0;
+}
+h1,h2,h3,h4,h5,h6,strong {
+ /*bringing boldness back to headers and the strong element*/
+ font-weight:bold;
+}
+abbr,acronym {
+ /*indicating to users that more info is available */
+ border-bottom:1px dotted #000;
+ cursor:help;
+}
+em {
+ /*bringing italics back to the em element*/
+ font-style:italic;
+}
+blockquote,ul,ol,dl {
+ /*giving blockquotes and lists room to breath*/
+ margin:1em;
+}
+ol,ul,dl {
+ /*bringing lists on to the page with breathing room */
+ margin-left:2em;
+}
+ol li {
+ /*giving OL's LIs generated numbers*/
+ list-style: decimal outside;
+}
+ul li {
+ /*giving UL's LIs generated disc markers*/
+ list-style: disc outside;
+}
+dl dd {
+ /*providing spacing for definition terms*/
+ margin-left:1em;
+}
+th,td {
+ /*borders and padding to make the table readable*/
+ border:1px solid #000;
+ padding:.5em;
+}
+th {
+ /*distinguishing table headers from data cells*/
+ font-weight:bold;
+ text-align:center;
+}
+caption {
+ /*coordinated margin to match cell's padding*/
+ margin-bottom:.5em;
+ /*centered so it doesn't blend in to other content*/
+ text-align:center;
+}
+p,fieldset,table,pre {
+ /*so things don't run into each other*/
+ margin-bottom:1em;
+}
+/* setting a consistent width, 160px;
+ control of type=file still not possible */
+input[type=text],input[type=password],textarea{width:12.25em;*width:11.9em;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-cssfonts body,.yui-cssfonts{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}.yui-cssfonts select,.yui-cssfonts input,.yui-cssfonts button,.yui-cssfonts textarea{font:99% arial,helvetica,clean,sans-serif;}.yui-cssfonts table{font-size:inherit;font:100%;}.yui-cssfonts pre,.yui-cssfonts code,.yui-cssfonts kbd,.yui-cssfonts samp,.yui-cssfonts tt{font-family:monospace;*font-size:108%;line-height:100%;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/**
+ * Percents could work for IE, but for backCompat purposes, we are using keywords.
+ * x-small is for IE6/7 quirks mode.
+ */
+.yui-cssfonts body, .yui-cssfonts {
+ font:13px/1.231 arial,helvetica,clean,sans-serif;
+ *font-size:small; /* for IE */
+ *font:x-small; /* for IE in quirks mode */
+}
+
+/**
+ * Nudge down to get to 13px equivalent for these form elements
+ */
+.yui-cssfonts select,
+.yui-cssfonts input,
+.yui-cssfonts button,
+.yui-cssfonts textarea {
+ font:99% arial,helvetica,clean,sans-serif;
+}
+
+/**
+ * To help tables remember to inherit
+ */
+.yui-cssfonts table {
+ font-size:inherit;
+ font:100%;
+}
+
+/**
+ * Bump up IE to get to 13px equivalent for these fixed-width elements
+ */
+.yui-cssfonts pre,
+.yui-cssfonts code,
+.yui-cssfonts kbd,
+.yui-cssfonts samp,
+.yui-cssfonts tt {
+ font-family:monospace;
+ *font-size:108%;
+ line-height:100%;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}select,input,button,textarea{font:99% arial,helvetica,clean,sans-serif;}table{font-size:inherit;font:100%;}pre,code,kbd,samp,tt{font-family:monospace;*font-size:108%;line-height:100%;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/**
+ * Percents could work for IE, but for backCompat purposes, we are using keywords.
+ * x-small is for IE6/7 quirks mode.
+ */
+body {
+ font:13px/1.231 arial,helvetica,clean,sans-serif;
+ *font-size:small; /* for IE */
+ *font:x-small; /* for IE in quirks mode */
+}
+
+/**
+ * Nudge down to get to 13px equivalent for these form elements
+ */
+select,
+input,
+button,
+textarea {
+ font:99% arial,helvetica,clean,sans-serif;
+}
+
+/**
+ * To help tables remember to inherit
+ */
+table {
+ font-size:inherit;
+ font:100%;
+}
+
+/**
+ * Bump up IE to get to 13px equivalent for these fixed-width elements
+ */
+pre,
+code,
+kbd,
+samp,
+tt {
+ font-family:monospace;
+ *font-size:108%;
+ line-height:100%;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-cssgrids body{text-align:center;margin-left:auto;margin-right:auto;}.yui-cssgrids .yui-d0,.yui-cssgrids .yui-d1,.yui-cssgrids .yui-d1f,.yui-cssgrids .yui-d2,.yui-cssgrids .yui-d2f,.yui-cssgrids .yui-d3,.yui-cssgrids .yui-d3f{margin:auto;text-align:left;width:57.69em;*width:56.25em;}.yui-cssgrids .yui-t1,.yui-cssgrids .yui-t2,.yui-cssgrids .yui-t3,.yui-cssgrids .yui-t4,.yui-cssgrids .yui-t5,.yui-cssgrids .yui-t6{margin:auto;text-align:left;width:100%;}.yui-cssgrids .yui-d0{margin:auto 10px;width:auto;}.yui-cssgrids .yui-d0f{width:100%;}.yui-cssgrids .yui-d2{width:73.076em;*width:71.25em;}.yui-cssgrids .yui-d2f{width:950px;}.yui-cssgrids .yui-d3{width:74.923em;*width:73.05em;}.yui-cssgrids .yui-d3f{width:974px;}.yui-cssgrids .yui-b{position:relative;}.yui-cssgrids .yui-b{_position:static;}.yui-cssgrids .yui-main .yui-b{position:static;}.yui-cssgrids .yui-main{width:100%;}.yui-cssgrids .yui-t1 .yui-main,.yui-cssgrids .yui-t2 .yui-main,.yui-cssgrids .yui-t3 .yui-main{float:right;margin-left:-25em;}.yui-cssgrids .yui-t4 .yui-main,.yui-cssgrids .yui-t5 .yui-main,.yui-cssgrids .yui-t6 .yui-main{float:left;margin-right:-25em;}.yui-cssgrids .yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-cssgrids .yui-t1 .yui-main .yui-b{margin-left:13.30769em;*margin-left:12.975em;}.yui-cssgrids .yui-t2 .yui-b{float:left;width:13.84615em;*width:13.50em;}.yui-cssgrids .yui-t2 .yui-main .yui-b{margin-left:14.84615em;*margin-left:14.475em;}.yui-cssgrids .yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-cssgrids .yui-t3 .yui-main .yui-b{margin-left:24.0769em;*margin-left:23.475em;}.yui-cssgrids .yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-cssgrids .yui-t4 .yui-main .yui-b{margin-right:14.8456em;*margin-right:14.475em;}.yui-cssgrids .yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-cssgrids .yui-t5 .yui-main .yui-b{margin-right:19.4615em;*margin-right:18.975em;}.yui-cssgrids .yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-cssgrids .yui-t6 .yui-main .yui-b{margin-right:24.0769em;*margin-right:23.475em;}.yui-cssgrids .yui-main .yui-b{float:none;width:auto;}.yui-cssgrids .yui-gb .yui-u,.yui-cssgrids .yui-g .yui-gb .yui-u,.yui-cssgrids .yui-gb .yui-g,.yui-cssgrids .yui-gb .yui-gb,.yui-cssgrids .yui-gb .yui-gc,.yui-cssgrids .yui-gb .yui-gd,.yui-cssgrids .yui-gb .yui-ge,.yui-cssgrids .yui-gb .yui-gf,.yui-cssgrids .yui-gc .yui-u,.yui-cssgrids .yui-gc .yui-g,.yui-cssgrids .yui-gd .yui-u{float:left;}.yui-cssgrids .yui-g .yui-u,.yui-cssgrids .yui-g .yui-g,.yui-cssgrids .yui-g .yui-gb,.yui-cssgrids .yui-g .yui-gc,.yui-cssgrids .yui-g .yui-gd,.yui-cssgrids .yui-g .yui-ge,.yui-cssgrids .yui-g .yui-gf,.yui-cssgrids .yui-gc .yui-u,.yui-cssgrids .yui-gd .yui-g,.yui-cssgrids .yui-g .yui-gc .yui-u,.yui-cssgrids .yui-ge .yui-u,.yui-cssgrids .yui-ge .yui-g,.yui-cssgrids .yui-gf .yui-g,.yui-cssgrids .yui-gf .yui-u{float:right;}.yui-cssgrids .yui-g div.first,.yui-cssgrids .yui-gb div.first,.yui-cssgrids .yui-gc div.first,.yui-cssgrids .yui-gd div.first,.yui-cssgrids .yui-ge div.first,.yui-cssgrids .yui-gf div.first,.yui-cssgrids .yui-g .yui-gc div.first,.yui-cssgrids .yui-g .yui-ge div.first,.yui-cssgrids .yui-gc div.first div.first{float:left;}.yui-cssgrids .yui-g .yui-u,.yui-cssgrids .yui-g .yui-g,.yui-cssgrids .yui-g .yui-gb,.yui-cssgrids .yui-g .yui-gc,.yui-cssgrids .yui-g .yui-gd,.yui-cssgrids .yui-g .yui-ge,.yui-cssgrids .yui-g .yui-gf{width:49.1%;}.yui-cssgrids .yui-gb .yui-u,.yui-cssgrids .yui-g .yui-gb .yui-u,.yui-cssgrids .yui-gb .yui-g,.yui-cssgrids .yui-gb .yui-gb,.yui-cssgrids .yui-gb .yui-gc,.yui-cssgrids .yui-gb .yui-gd,.yui-cssgrids .yui-gb .yui-ge,.yui-cssgrids .yui-gb .yui-gf,.yui-cssgrids .yui-gc .yui-u,.yui-cssgrids .yui-gc .yui-g,.yui-cssgrids .yui-gd .yui-u{width:32%;margin-left:2.0%;}.yui-cssgrids .yui-gb .yui-u{*width:31.8%;*margin-left:1.9%;}.yui-cssgrids .yui-gc div.first,.yui-cssgrids .yui-gd .yui-u{width:66%;_width:65.7%;}.yui-cssgrids .yui-gd div.first{width:32%;_width:31.5%;}.yui-cssgrids .yui-ge div.first,.yui-cssgrids .yui-gf .yui-u{width:74.2%;_width:74%;}.yui-cssgrids .yui-ge .yui-u,.yui-cssgrids .yui-gf div.first{width:24%;_width:23.8%;}.yui-cssgrids .yui-g .yui-gb div.first,.yui-cssgrids .yui-gb div.first,.yui-cssgrids .yui-gc div.first,.yui-cssgrids .yui-gd div.first{margin-left:0;}.yui-cssgrids .yui-g .yui-g .yui-u,.yui-cssgrids .yui-gb .yui-g .yui-u,.yui-cssgrids .yui-gc .yui-g .yui-u,.yui-cssgrids .yui-gd .yui-g .yui-u,.yui-cssgrids .yui-ge .yui-g .yui-u,.yui-cssgrids .yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-cssgrids .yui-g .yui-gb div.first,.yui-cssgrids .yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-cssgrids .yui-g .yui-gc div.first,.yui-cssgrids .yui-gd .yui-g{width:66%;}.yui-cssgrids .yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-cssgrids .yui-gb .yui-gc div.first,.yui-cssgrids .yui-gb .yui-gd div.first{*margin-right:0;}.yui-cssgrids .yui-gb .yui-gb .yui-u,.yui-cssgrids .yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-cssgrids .yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-cssgrids .yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-cssgrids .yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-cssgrids .yui-g .yui-gc .yui-u,.yui-cssgrids .yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-cssgrids .yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-cssgrids .yui-gb .yui-ge .yui-u,.yui-cssgrids .yui-gb .yui-gf .yui-u{margin:0;}.yui-cssgrids .yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-cssgrids .yui-gb .yui-g div.first,.yui-cssgrids .yui-gb .yui-gb div.first{*margin-left:0;}.yui-cssgrids .yui-gc .yui-g .yui-u,.yui-cssgrids .yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-cssgrids .yui-gb .yui-gd div.first{width:32%;}.yui-cssgrids .yui-g .yui-gd div.first{_width:29.9%;}.yui-cssgrids .yui-ge .yui-g{width:24%;}.yui-cssgrids .yui-gf .yui-g{width:74.2%;}.yui-cssgrids .yui-gb .yui-ge div.yui-u,.yui-cssgrids .yui-gb .yui-gf div.yui-u{float:right;}.yui-cssgrids .yui-gb .yui-ge div.first,.yui-cssgrids .yui-gb .yui-gf div.first{float:left;}.yui-cssgrids .yui-gb .yui-ge .yui-u,.yui-cssgrids .yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-cssgrids .yui-gc .yui-gf .yui-u{width:74%;_width:73%;}.yui-cssgrids .yui-gc .yui-gf div.first{width:24%;}.yui-cssgrids .yui-gb .yui-ge div.first,.yui-cssgrids .yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-cssgrids .yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-cssgrids .yui-ge div.first .yui-gd div.first{width:32%;}.yui-cssgrids #bd:after,.yui-cssgrids .yui-g:after,.yui-cssgrids .yui-gb:after,.yui-cssgrids .yui-gc:after,.yui-cssgrids .yui-gd:after,.yui-cssgrids .yui-ge:after,.yui-cssgrids .yui-gf:after,.yui-cssgrids .yui-t1:after,.yui-cssgrids .yui-t2:after,.yui-cssgrids .yui-t3:after,.yui-cssgrids .yui-t4:after,.yui-cssgrids .yui-t5:after,.yui-cssgrids .yui-t6:after{content:".";display:block;height:0;clear:both;visibility:hidden;}.yui-cssgrids #bd,.yui-cssgrids .yui-g,.yui-cssgrids .yui-gb,.yui-cssgrids .yui-gc,.yui-cssgrids .yui-gd,.yui-cssgrids .yui-ge,.yui-cssgrids .yui-gf,.yui-cssgrids .yui-t1,.yui-cssgrids .yui-t2,.yui-cssgrids .yui-t3,.yui-cssgrids .yui-t4,.yui-cssgrids .yui-t5,.yui-cssgrids .yui-t6{zoom:1;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*
+*
+* The YUI CSS Foundation uses the *property and _property CSS filter
+* techniques to shield a value from A-grade browsers [1] other than
+* IE6 & IE7 (*property) and IE6 (_property)
+*
+/
+Section: General Rules
+*/
+.yui-cssgrids body {
+ /* center the page */
+ text-align: center;
+ margin-left: auto;
+ margin-right: auto;
+}
+/*
+Section: Page Width Rules (#doc, #doc2, #doc3, #doc4)
+*/
+/*
+Subsection: General
+*/
+.yui-cssgrids .yui-d0, /* 100% */
+.yui-cssgrids .yui-d1, /* 750px */
+.yui-cssgrids .yui-d1f, /* 750px fixed */
+.yui-cssgrids .yui-d2, /* 950px */
+.yui-cssgrids .yui-d2f, /* 950px fixed */
+.yui-cssgrids .yui-d3, /* 974px */
+.yui-cssgrids .yui-d3f { /* 974px fixed */
+ margin: auto;
+ text-align: left;
+ width: 57.69em;
+ *width: 56.25em; /* doc1*/
+}
+
+.yui-cssgrids .yui-t1,
+.yui-cssgrids .yui-t2,
+.yui-cssgrids .yui-t3,
+.yui-cssgrids .yui-t4,
+.yui-cssgrids .yui-t5,
+.yui-cssgrids .yui-t6 {
+ margin: auto;
+ text-align: left;
+ width: 100%;
+}
+
+/*
+Subsection: 100% (doc)
+*/
+.yui-cssgrids .yui-d0 {
+ /* Left and Right margins are not a structural part of Grids. Without them Grids
+ works fine, but content bleeds to the very edge of the document, which often
+ impairs readability and usability. They are
+ provided because they prevent the content from "bleeding" into the browser's chrome.*/
+ margin: auto 10px;
+ width: auto;
+}
+.yui-cssgrids .yui-d0f {
+ width: 100%;
+}
+
+/*
+Subsection: 950 Centered (doc2)
+*/
+.yui-cssgrids .yui-d2 {
+ width: 73.076em;
+ *width: 71.25em;
+}
+.yui-cssgrids .yui-d2f {
+ width: 950px;
+}
+/*
+Subsection: 974 Centered (doc3)
+*/
+.yui-cssgrids .yui-d3 {
+ width: 74.923em;
+ *width: 73.05em;
+}
+.yui-cssgrids .yui-d3f {
+ width: 974px;
+}
+/*
+Section: Preset Template Rules (.yui-t[1-6])
+*/
+/*
+Subsection: General
+*/
+
+/* to preserve source-order independence for Gecko without breaking IE */
+.yui-cssgrids .yui-b {
+ position: relative;
+}
+.yui-cssgrids .yui-b {
+ _position: static;
+}
+.yui-cssgrids .yui-main .yui-b {
+ position: static;
+}
+.yui-cssgrids .yui-main {
+ width: 100%;
+}
+.yui-cssgrids .yui-t1 .yui-main,
+.yui-cssgrids .yui-t2 .yui-main,
+.yui-cssgrids .yui-t3 .yui-main {
+ float: right;
+ /* IE: preserve layout at narrow widths */
+ margin-left: -25em;
+}
+.yui-cssgrids .yui-t4 .yui-main,
+.yui-cssgrids .yui-t5 .yui-main,
+.yui-cssgrids .yui-t6 .yui-main {
+ float: left;
+ /* IE: preserve layout at narrow widths */
+ margin-right: -25em;
+}
+
+/* Subsection: For Specific Template Presets */
+
+/**
+* Nudge down to get to 13px equivalent for these form elements
+*/
+
+/*
+TODO Create t1-6's that are based on fixed widths
+*/
+/* t1 narrow block = left, equivalent of 160px */
+.yui-cssgrids .yui-t1 .yui-b {
+ float: left;
+ width: 12.30769em;
+ *width: 12.00em;
+}
+.yui-cssgrids .yui-t1 .yui-main .yui-b {
+ margin-left: 13.30769em;
+ *margin-left:12.975em;
+}
+/* t2 narrow block = left, equivalent of 180px */
+.yui-cssgrids .yui-t2 .yui-b {
+ float: left;
+ width: 13.84615em;
+ *width: 13.50em;
+}
+.yui-cssgrids .yui-t2 .yui-main .yui-b {
+ margin-left: 14.84615em;
+ *margin-left: 14.475em;
+}
+/* t3 narrow block = left, equivalent of 300px */
+.yui-cssgrids .yui-t3 .yui-b {
+ float: left;
+ width: 23.0769em;
+ *width: 22.50em;
+}
+.yui-cssgrids .yui-t3 .yui-main .yui-b {
+ margin-left: 24.0769em;
+ *margin-left: 23.475em;
+}
+/* t4 narrow block = right, equivalent of 180px */
+.yui-cssgrids .yui-t4 .yui-b {
+ float: right;
+ width: 13.8456em;
+ *width: 13.50em;
+}
+.yui-cssgrids .yui-t4 .yui-main .yui-b {
+ margin-right: 14.8456em;
+ *margin-right: 14.475em;
+}
+/* t5 narrow block = right, equivalent of 240px */
+.yui-cssgrids .yui-t5 .yui-b {
+ float: right;
+ width: 18.4615em;
+ *width: 18.00em;
+}
+.yui-cssgrids .yui-t5 .yui-main .yui-b {
+ margin-right: 19.4615em;
+ *margin-right: 18.975em;
+}
+/* t6 narrow block = equivalent of 300px */
+.yui-cssgrids .yui-t6 .yui-b {
+ float: right;
+ width: 23.0769em;
+ *width: 22.50em;
+}
+.yui-cssgrids .yui-t6 .yui-main .yui-b {
+ margin-right: 24.0769em;
+ *margin-right: 23.475em;
+}
+
+.yui-cssgrids .yui-main .yui-b {
+ float: none;
+ width: auto;
+}
+
+/*
+Section: Grids and Nesting Grids
+*/
+
+/*
+Subsection: Children generally take half the available space
+*/
+
+.yui-cssgrids .yui-gb .yui-u,
+.yui-cssgrids .yui-g .yui-gb .yui-u,
+.yui-cssgrids .yui-gb .yui-g,
+.yui-cssgrids .yui-gb .yui-gb,
+.yui-cssgrids .yui-gb .yui-gc,
+.yui-cssgrids .yui-gb .yui-gd,
+.yui-cssgrids .yui-gb .yui-ge,
+.yui-cssgrids .yui-gb .yui-gf,
+.yui-cssgrids .yui-gc .yui-u,
+.yui-cssgrids .yui-gc .yui-g,
+.yui-cssgrids .yui-gd .yui-u {
+ float: left;
+}
+
+/*Float units (and sub grids) to the right */
+.yui-cssgrids .yui-g .yui-u,
+.yui-cssgrids .yui-g .yui-g,
+.yui-cssgrids .yui-g .yui-gb,
+.yui-cssgrids .yui-g .yui-gc,
+.yui-cssgrids .yui-g .yui-gd,
+.yui-cssgrids .yui-g .yui-ge,
+.yui-cssgrids .yui-g .yui-gf,
+.yui-cssgrids .yui-gc .yui-u,
+.yui-cssgrids .yui-gd .yui-g,
+.yui-cssgrids .yui-g .yui-gc .yui-u,
+.yui-cssgrids .yui-ge .yui-u,
+.yui-cssgrids .yui-ge .yui-g,
+.yui-cssgrids .yui-gf .yui-g,
+.yui-cssgrids .yui-gf .yui-u {
+ float: right;
+}
+
+/*Float units (and sub grids) to the left */
+.yui-cssgrids .yui-g div.first,
+.yui-cssgrids .yui-gb div.first,
+.yui-cssgrids .yui-gc div.first,
+.yui-cssgrids .yui-gd div.first,
+.yui-cssgrids .yui-ge div.first,
+.yui-cssgrids .yui-gf div.first,
+.yui-cssgrids .yui-g .yui-gc div.first,
+.yui-cssgrids .yui-g .yui-ge div.first,
+.yui-cssgrids .yui-gc div.first div.first {
+ float: left;
+}
+
+.yui-cssgrids .yui-g .yui-u,
+.yui-cssgrids .yui-g .yui-g,
+.yui-cssgrids .yui-g .yui-gb,
+.yui-cssgrids .yui-g .yui-gc,
+.yui-cssgrids .yui-g .yui-gd,
+.yui-cssgrids .yui-g .yui-ge,
+.yui-cssgrids .yui-g .yui-gf {
+ width: 49.1%;
+}
+
+.yui-cssgrids .yui-gb .yui-u,
+.yui-cssgrids .yui-g .yui-gb .yui-u,
+.yui-cssgrids .yui-gb .yui-g,
+.yui-cssgrids .yui-gb .yui-gb,
+.yui-cssgrids .yui-gb .yui-gc,
+.yui-cssgrids .yui-gb .yui-gd,
+.yui-cssgrids .yui-gb .yui-ge,
+.yui-cssgrids .yui-gb .yui-gf,
+.yui-cssgrids .yui-gc .yui-u,
+.yui-cssgrids .yui-gc .yui-g,
+.yui-cssgrids .yui-gd .yui-u {
+ width: 32%;
+ margin-left: 2.0%;
+}
+
+/* Give IE some extra breathing room for 1/3-based rounding issues */
+.yui-cssgrids .yui-gb .yui-u {
+ *width: 31.8%;
+ *margin-left: 1.9%;
+}
+
+.yui-cssgrids .yui-gc div.first,
+.yui-cssgrids .yui-gd .yui-u {
+ width: 66%;
+ _width: 65.7%;
+}
+.yui-cssgrids .yui-gd div.first {
+ width: 32%;
+ _width: 31.5%;
+}
+
+.yui-cssgrids .yui-ge div.first,
+.yui-cssgrids .yui-gf .yui-u {
+ width: 74.2%;
+ _width: 74%;
+}
+
+.yui-cssgrids .yui-ge .yui-u,
+.yui-cssgrids .yui-gf div.first {
+ width: 24%;
+ _width: 23.8%;
+}
+
+.yui-cssgrids .yui-g .yui-gb div.first,
+.yui-cssgrids .yui-gb div.first,
+.yui-cssgrids .yui-gc div.first,
+.yui-cssgrids .yui-gd div.first {
+ margin-left: 0;
+}
+
+/*
+Section: Deep Nesting
+*/
+.yui-cssgrids .yui-g .yui-g .yui-u,
+.yui-cssgrids .yui-gb .yui-g .yui-u,
+.yui-cssgrids .yui-gc .yui-g .yui-u,
+.yui-cssgrids .yui-gd .yui-g .yui-u,
+.yui-cssgrids .yui-ge .yui-g .yui-u,
+.yui-cssgrids .yui-gf .yui-g .yui-u {
+ width: 49%;
+ *width: 48.1%;
+ *margin-left: 0;
+}
+
+.yui-cssgrids .yui-g .yui-gb div.first,
+.yui-cssgrids .yui-gb .yui-gb div.first {
+ *margin-right: 0;
+ *width: 32%;
+ _width: 31.7%;
+}
+
+.yui-cssgrids .yui-g .yui-gc div.first,
+.yui-cssgrids .yui-gd .yui-g {
+ width: 66%;
+}
+
+.yui-cssgrids .yui-gb .yui-g div.first {
+ *margin-right: 4%;
+ _margin-right: 1.3%;
+}
+
+.yui-cssgrids .yui-gb .yui-gc div.first,
+.yui-cssgrids .yui-gb .yui-gd div.first {
+ *margin-right: 0;
+}
+
+.yui-cssgrids .yui-gb .yui-gb .yui-u,
+.yui-cssgrids .yui-gb .yui-gc .yui-u {
+ *margin-left: 1.8%;
+ _margin-left: 4%;
+}
+
+.yui-cssgrids .yui-g .yui-gb .yui-u {
+ _margin-left: 1.0%;
+}
+
+.yui-cssgrids .yui-gb .yui-gd .yui-u {
+ *width: 66%;
+ _width: 61.2%;
+}
+.yui-cssgrids .yui-gb .yui-gd div.first {
+ *width: 31%;
+ _width: 29.5%;
+}
+
+.yui-cssgrids .yui-g .yui-gc .yui-u,
+.yui-cssgrids .yui-gb .yui-gc .yui-u {
+ width: 32%;
+ _float: right;
+ margin-right: 0;
+ _margin-left: 0;
+}
+.yui-cssgrids .yui-gb .yui-gc div.first {
+ width: 66%;
+ *float: left;
+ *margin-left: 0;
+}
+
+.yui-cssgrids .yui-gb .yui-ge .yui-u,
+.yui-cssgrids .yui-gb .yui-gf .yui-u {
+ margin: 0;
+}
+
+.yui-cssgrids .yui-gb .yui-gb .yui-u {
+ _margin-left: .7%;
+}
+
+.yui-cssgrids .yui-gb .yui-g div.first,
+.yui-cssgrids .yui-gb .yui-gb div.first {
+ *margin-left:0;
+}
+
+.yui-cssgrids .yui-gc .yui-g .yui-u,
+.yui-cssgrids .yui-gd .yui-g .yui-u {
+ *width: 48.1%;
+ *margin-left: 0;
+}
+
+.yui-cssgrids .yui-gb .yui-gd div.first {
+ width: 32%;
+}
+.yui-cssgrids .yui-g .yui-gd div.first {
+ _width: 29.9%;
+}
+
+.yui-cssgrids .yui-ge .yui-g {
+ width: 24%;
+}
+.yui-cssgrids .yui-gf .yui-g {
+ width: 74.2%;
+}
+
+.yui-cssgrids .yui-gb .yui-ge div.yui-u,
+.yui-cssgrids .yui-gb .yui-gf div.yui-u {
+ float: right;
+}
+.yui-cssgrids .yui-gb .yui-ge div.first,
+.yui-cssgrids .yui-gb .yui-gf div.first {
+ float: left;
+}
+
+/* Width Accommodation for Nested Contexts */
+.yui-cssgrids .yui-gb .yui-ge .yui-u,
+.yui-cssgrids .yui-gb .yui-gf div.first {
+ *width: 24%;
+ _width: 20%;
+}
+
+/* Width Accommodation for Nested Contexts */
+
+.yui-cssgrids .yui-gc .yui-gf .yui-u {
+ width: 74%;
+ _width: 73%;
+}
+
+.yui-cssgrids .yui-gc .yui-gf div.first {
+ width: 24%;
+}
+
+.yui-cssgrids .yui-gb .yui-ge div.first,
+.yui-cssgrids .yui-gb .yui-gf .yui-u {
+ *width: 73.5%;
+ _width: 65.5%;
+}
+
+/* Patch for GD within GE */
+.yui-cssgrids .yui-ge div.first .yui-gd .yui-u {
+ width: 65%;
+}
+.yui-cssgrids .yui-ge div.first .yui-gd div.first {
+ width: 32%;
+}
+
+/*
+Section: Clearing. zoom for IE, :after for others
+*/
+
+.yui-cssgrids #bd:after,
+.yui-cssgrids .yui-g:after,
+.yui-cssgrids .yui-gb:after,
+.yui-cssgrids .yui-gc:after,
+.yui-cssgrids .yui-gd:after,
+.yui-cssgrids .yui-ge:after,
+.yui-cssgrids .yui-gf:after,
+.yui-cssgrids .yui-t1:after,
+.yui-cssgrids .yui-t2:after,
+.yui-cssgrids .yui-t3:after,
+.yui-cssgrids .yui-t4:after,
+.yui-cssgrids .yui-t5:after,
+.yui-cssgrids .yui-t6:after {
+ content: ".";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+.yui-cssgrids #bd,
+.yui-cssgrids .yui-g,
+.yui-cssgrids .yui-gb,
+.yui-cssgrids .yui-gc,
+.yui-cssgrids .yui-gd,
+.yui-cssgrids .yui-ge,
+.yui-cssgrids .yui-gf,
+.yui-cssgrids .yui-t1,
+.yui-cssgrids .yui-t2,
+.yui-cssgrids .yui-t3,
+.yui-cssgrids .yui-t4,
+.yui-cssgrids .yui-t5,
+.yui-cssgrids .yui-t6 {
+ zoom: 1;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+body{text-align:center;margin-left:auto;margin-right:auto;}.yui-d0,.yui-d1,.yui-d1f,.yui-d2,.yui-d2f,.yui-d3,.yui-d3f{margin:auto;text-align:left;width:57.69em;*width:56.25em;}.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6{margin:auto;text-align:left;width:100%;}.yui-d0{margin:auto 10px;width:auto;}.yui-d0f{width:100%;}.yui-d2{width:73.076em;*width:71.25em;}.yui-d2f{width:950px;}.yui-d3{width:74.923em;*width:73.05em;}.yui-d3f{width:974px;}.yui-b{position:relative;}.yui-b{_position:static;}.yui-main .yui-b{position:static;}.yui-main{width:100%;}.yui-t1 .yui-main,.yui-t2 .yui-main,.yui-t3 .yui-main{float:right;margin-left:-25em;}.yui-t4 .yui-main,.yui-t5 .yui-main,.yui-t6 .yui-main{float:left;margin-right:-25em;}.yui-t1 .yui-b{float:left;width:12.30769em;*width:12.00em;}.yui-t1 .yui-main .yui-b{margin-left:13.30769em;*margin-left:12.975em;}.yui-t2 .yui-b{float:left;width:13.84615em;*width:13.50em;}.yui-t2 .yui-main .yui-b{margin-left:14.84615em;*margin-left:14.475em;}.yui-t3 .yui-b{float:left;width:23.0769em;*width:22.50em;}.yui-t3 .yui-main .yui-b{margin-left:24.0769em;*margin-left:23.475em;}.yui-t4 .yui-b{float:right;width:13.8456em;*width:13.50em;}.yui-t4 .yui-main .yui-b{margin-right:14.8456em;*margin-right:14.475em;}.yui-t5 .yui-b{float:right;width:18.4615em;*width:18.00em;}.yui-t5 .yui-main .yui-b{margin-right:19.4615em;*margin-right:18.975em;}.yui-t6 .yui-b{float:right;width:23.0769em;*width:22.50em;}.yui-t6 .yui-main .yui-b{margin-right:24.0769em;*margin-right:23.475em;}.yui-main .yui-b{float:none;width:auto;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf,.yui-gc .yui-u,.yui-gd .yui-g,.yui-g .yui-gc .yui-u,.yui-ge .yui-u,.yui-ge .yui-g,.yui-gf .yui-g,.yui-gf .yui-u{float:right;}.yui-g div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first,.yui-ge div.first,.yui-gf div.first,.yui-g .yui-gc div.first,.yui-g .yui-ge div.first,.yui-gc div.first div.first{float:left;}.yui-g .yui-u,.yui-g .yui-g,.yui-g .yui-gb,.yui-g .yui-gc,.yui-g .yui-gd,.yui-g .yui-ge,.yui-g .yui-gf{width:49.1%;}.yui-gb .yui-u,.yui-g .yui-gb .yui-u,.yui-gb .yui-g,.yui-gb .yui-gb,.yui-gb .yui-gc,.yui-gb .yui-gd,.yui-gb .yui-ge,.yui-gb .yui-gf,.yui-gc .yui-u,.yui-gc .yui-g,.yui-gd .yui-u{width:32%;margin-left:2.0%;}.yui-gb .yui-u{*width:31.8%;*margin-left:1.9%;}.yui-gc div.first,.yui-gd .yui-u{width:66%;_width:65.7%;}.yui-gd div.first{width:32%;_width:31.5%;}.yui-ge div.first,.yui-gf .yui-u{width:74.2%;_width:74%;}.yui-ge .yui-u,.yui-gf div.first{width:24%;_width:23.8%;}.yui-g .yui-gb div.first,.yui-gb div.first,.yui-gc div.first,.yui-gd div.first{margin-left:0;}.yui-g .yui-g .yui-u,.yui-gb .yui-g .yui-u,.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u,.yui-ge .yui-g .yui-u,.yui-gf .yui-g .yui-u{width:49%;*width:48.1%;*margin-left:0;}.yui-g .yui-gb div.first,.yui-gb .yui-gb div.first{*margin-right:0;*width:32%;_width:31.7%;}.yui-g .yui-gc div.first,.yui-gd .yui-g{width:66%;}.yui-gb .yui-g div.first{*margin-right:4%;_margin-right:1.3%;}.yui-gb .yui-gc div.first,.yui-gb .yui-gd div.first{*margin-right:0;}.yui-gb .yui-gb .yui-u,.yui-gb .yui-gc .yui-u{*margin-left:1.8%;_margin-left:4%;}.yui-g .yui-gb .yui-u{_margin-left:1.0%;}.yui-gb .yui-gd .yui-u{*width:66%;_width:61.2%;}.yui-gb .yui-gd div.first{*width:31%;_width:29.5%;}.yui-g .yui-gc .yui-u,.yui-gb .yui-gc .yui-u{width:32%;_float:right;margin-right:0;_margin-left:0;}.yui-gb .yui-gc div.first{width:66%;*float:left;*margin-left:0;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf .yui-u{margin:0;}.yui-gb .yui-gb .yui-u{_margin-left:.7%;}.yui-gb .yui-g div.first,.yui-gb .yui-gb div.first{*margin-left:0;}.yui-gc .yui-g .yui-u,.yui-gd .yui-g .yui-u{*width:48.1%;*margin-left:0;}.yui-gb .yui-gd div.first{width:32%;}.yui-g .yui-gd div.first{_width:29.9%;}.yui-ge .yui-g{width:24%;}.yui-gf .yui-g{width:74.2%;}.yui-gb .yui-ge div.yui-u,.yui-gb .yui-gf div.yui-u{float:right;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf div.first{float:left;}.yui-gb .yui-ge .yui-u,.yui-gb .yui-gf div.first{*width:24%;_width:20%;}.yui-gb .yui-ge div.first,.yui-gb .yui-gf .yui-u{*width:73.5%;_width:65.5%;}.yui-ge div.first .yui-gd .yui-u{width:65%;}.yui-ge div.first .yui-gd div.first{width:32%;}#bd:after,.yui-g:after,.yui-gb:after,.yui-gc:after,.yui-gd:after,.yui-ge:after,.yui-gf:after,.yui-t1:after,.yui-t2:after,.yui-t3:after,.yui-t4:after,.yui-t5:after,.yui-t6:after{content:".";display:block;height:0;clear:both;visibility:hidden;}#bd,.yui-g,.yui-gb,.yui-gc,.yui-gd,.yui-ge,.yui-gf,.yui-t1,.yui-t2,.yui-t3,.yui-t4,.yui-t5,.yui-t6{zoom:1;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*
+*
+* The YUI CSS Foundation uses the *property and _property CSS filter
+* techniques to shield a value from A-grade browsers [1] other than
+* IE6 & IE7 (*property) and IE6 (_property)
+*
+/
+Section: General Rules
+*/
+body {
+ /* center the page */
+ text-align: center;
+ margin-left: auto;
+ margin-right: auto;
+}
+/*
+Section: Page Width Rules (#doc, #doc2, #doc3, #doc4)
+*/
+/*
+Subsection: General
+*/
+.yui-d0, /* 100% */
+.yui-d1, /* 750px */
+.yui-d1f, /* 750px fixed */
+.yui-d2, /* 950px */
+.yui-d2f, /* 950px fixed */
+.yui-d3, /* 974px */
+.yui-d3f { /* 974px fixed */
+ margin: auto;
+ text-align: left;
+ width: 57.69em;
+ *width: 56.25em; /* doc1*/
+}
+
+.yui-t1,
+.yui-t2,
+.yui-t3,
+.yui-t4,
+.yui-t5,
+.yui-t6 {
+ margin: auto;
+ text-align: left;
+ width: 100%;
+}
+
+/*
+Subsection: 100% (doc)
+*/
+.yui-d0 {
+ /* Left and Right margins are not a structural part of Grids. Without them Grids
+ works fine, but content bleeds to the very edge of the document, which often
+ impairs readability and usability. They are
+ provided because they prevent the content from "bleeding" into the browser's chrome.*/
+ margin: auto 10px;
+ width: auto;
+}
+.yui-d0f {
+ width: 100%;
+}
+
+/*
+Subsection: 950 Centered (doc2)
+*/
+.yui-d2 {
+ width: 73.076em;
+ *width: 71.25em;
+}
+.yui-d2f {
+ width: 950px;
+}
+/*
+Subsection: 974 Centered (doc3)
+*/
+.yui-d3 {
+ width: 74.923em;
+ *width: 73.05em;
+}
+.yui-d3f {
+ width: 974px;
+}
+/*
+Section: Preset Template Rules (.yui-t[1-6])
+*/
+/*
+Subsection: General
+*/
+
+/* to preserve source-order independence for Gecko without breaking IE */
+.yui-b {
+ position: relative;
+}
+.yui-b {
+ _position: static;
+}
+.yui-main .yui-b {
+ position: static;
+}
+.yui-main {
+ width: 100%;
+}
+.yui-t1 .yui-main,
+.yui-t2 .yui-main,
+.yui-t3 .yui-main {
+ float: right;
+ /* IE: preserve layout at narrow widths */
+ margin-left: -25em;
+}
+.yui-t4 .yui-main,
+.yui-t5 .yui-main,
+.yui-t6 .yui-main {
+ float: left;
+ /* IE: preserve layout at narrow widths */
+ margin-right: -25em;
+}
+
+/* Subsection: For Specific Template Presets */
+
+/**
+* Nudge down to get to 13px equivalent for these form elements
+*/
+
+/*
+TODO Create t1-6's that are based on fixed widths
+*/
+/* t1 narrow block = left, equivalent of 160px */
+.yui-t1 .yui-b {
+ float: left;
+ width: 12.30769em;
+ *width: 12.00em;
+}
+.yui-t1 .yui-main .yui-b {
+ margin-left: 13.30769em;
+ *margin-left:12.975em;
+}
+/* t2 narrow block = left, equivalent of 180px */
+.yui-t2 .yui-b {
+ float: left;
+ width: 13.84615em;
+ *width: 13.50em;
+}
+.yui-t2 .yui-main .yui-b {
+ margin-left: 14.84615em;
+ *margin-left: 14.475em;
+}
+/* t3 narrow block = left, equivalent of 300px */
+.yui-t3 .yui-b {
+ float: left;
+ width: 23.0769em;
+ *width: 22.50em;
+}
+.yui-t3 .yui-main .yui-b {
+ margin-left: 24.0769em;
+ *margin-left: 23.475em;
+}
+/* t4 narrow block = right, equivalent of 180px */
+.yui-t4 .yui-b {
+ float: right;
+ width: 13.8456em;
+ *width: 13.50em;
+}
+.yui-t4 .yui-main .yui-b {
+ margin-right: 14.8456em;
+ *margin-right: 14.475em;
+}
+/* t5 narrow block = right, equivalent of 240px */
+.yui-t5 .yui-b {
+ float: right;
+ width: 18.4615em;
+ *width: 18.00em;
+}
+.yui-t5 .yui-main .yui-b {
+ margin-right: 19.4615em;
+ *margin-right: 18.975em;
+}
+/* t6 narrow block = equivalent of 300px */
+.yui-t6 .yui-b {
+ float: right;
+ width: 23.0769em;
+ *width: 22.50em;
+}
+.yui-t6 .yui-main .yui-b {
+ margin-right: 24.0769em;
+ *margin-right: 23.475em;
+}
+
+.yui-main .yui-b {
+ float: none;
+ width: auto;
+}
+
+/*
+Section: Grids and Nesting Grids
+*/
+
+/*
+Subsection: Children generally take half the available space
+*/
+
+.yui-gb .yui-u,
+.yui-g .yui-gb .yui-u,
+.yui-gb .yui-g,
+.yui-gb .yui-gb,
+.yui-gb .yui-gc,
+.yui-gb .yui-gd,
+.yui-gb .yui-ge,
+.yui-gb .yui-gf,
+.yui-gc .yui-u,
+.yui-gc .yui-g,
+.yui-gd .yui-u {
+ float: left;
+}
+
+/*Float units (and sub grids) to the right */
+.yui-g .yui-u,
+.yui-g .yui-g,
+.yui-g .yui-gb,
+.yui-g .yui-gc,
+.yui-g .yui-gd,
+.yui-g .yui-ge,
+.yui-g .yui-gf,
+.yui-gc .yui-u,
+.yui-gd .yui-g,
+.yui-g .yui-gc .yui-u,
+.yui-ge .yui-u,
+.yui-ge .yui-g,
+.yui-gf .yui-g,
+.yui-gf .yui-u {
+ float: right;
+}
+
+/*Float units (and sub grids) to the left */
+.yui-g div.first,
+.yui-gb div.first,
+.yui-gc div.first,
+.yui-gd div.first,
+.yui-ge div.first,
+.yui-gf div.first,
+.yui-g .yui-gc div.first,
+.yui-g .yui-ge div.first,
+.yui-gc div.first div.first {
+ float: left;
+}
+
+.yui-g .yui-u,
+.yui-g .yui-g,
+.yui-g .yui-gb,
+.yui-g .yui-gc,
+.yui-g .yui-gd,
+.yui-g .yui-ge,
+.yui-g .yui-gf {
+ width: 49.1%;
+}
+
+.yui-gb .yui-u,
+.yui-g .yui-gb .yui-u,
+.yui-gb .yui-g,
+.yui-gb .yui-gb,
+.yui-gb .yui-gc,
+.yui-gb .yui-gd,
+.yui-gb .yui-ge,
+.yui-gb .yui-gf,
+.yui-gc .yui-u,
+.yui-gc .yui-g,
+.yui-gd .yui-u {
+ width: 32%;
+ margin-left: 2.0%;
+}
+
+/* Give IE some extra breathing room for 1/3-based rounding issues */
+.yui-gb .yui-u {
+ *width: 31.8%;
+ *margin-left: 1.9%;
+}
+
+.yui-gc div.first,
+.yui-gd .yui-u {
+ width: 66%;
+ _width: 65.7%;
+}
+.yui-gd div.first {
+ width: 32%;
+ _width: 31.5%;
+}
+
+.yui-ge div.first,
+.yui-gf .yui-u {
+ width: 74.2%;
+ _width: 74%;
+}
+
+.yui-ge .yui-u,
+.yui-gf div.first {
+ width: 24%;
+ _width: 23.8%;
+}
+
+.yui-g .yui-gb div.first,
+.yui-gb div.first,
+.yui-gc div.first,
+.yui-gd div.first {
+ margin-left: 0;
+}
+
+/*
+Section: Deep Nesting
+*/
+.yui-g .yui-g .yui-u,
+.yui-gb .yui-g .yui-u,
+.yui-gc .yui-g .yui-u,
+.yui-gd .yui-g .yui-u,
+.yui-ge .yui-g .yui-u,
+.yui-gf .yui-g .yui-u {
+ width: 49%;
+ *width: 48.1%;
+ *margin-left: 0;
+}
+
+.yui-g .yui-gb div.first,
+.yui-gb .yui-gb div.first {
+ *margin-right: 0;
+ *width: 32%;
+ _width: 31.7%;
+}
+
+.yui-g .yui-gc div.first,
+.yui-gd .yui-g {
+ width: 66%;
+}
+
+.yui-gb .yui-g div.first {
+ *margin-right: 4%;
+ _margin-right: 1.3%;
+}
+
+.yui-gb .yui-gc div.first,
+.yui-gb .yui-gd div.first {
+ *margin-right: 0;
+}
+
+.yui-gb .yui-gb .yui-u,
+.yui-gb .yui-gc .yui-u {
+ *margin-left: 1.8%;
+ _margin-left: 4%;
+}
+
+.yui-g .yui-gb .yui-u {
+ _margin-left: 1.0%;
+}
+
+.yui-gb .yui-gd .yui-u {
+ *width: 66%;
+ _width: 61.2%;
+}
+.yui-gb .yui-gd div.first {
+ *width: 31%;
+ _width: 29.5%;
+}
+
+.yui-g .yui-gc .yui-u,
+.yui-gb .yui-gc .yui-u {
+ width: 32%;
+ _float: right;
+ margin-right: 0;
+ _margin-left: 0;
+}
+.yui-gb .yui-gc div.first {
+ width: 66%;
+ *float: left;
+ *margin-left: 0;
+}
+
+.yui-gb .yui-ge .yui-u,
+.yui-gb .yui-gf .yui-u {
+ margin: 0;
+}
+
+.yui-gb .yui-gb .yui-u {
+ _margin-left: .7%;
+}
+
+.yui-gb .yui-g div.first,
+.yui-gb .yui-gb div.first {
+ *margin-left:0;
+}
+
+.yui-gc .yui-g .yui-u,
+.yui-gd .yui-g .yui-u {
+ *width: 48.1%;
+ *margin-left: 0;
+}
+
+.yui-gb .yui-gd div.first {
+ width: 32%;
+}
+.yui-g .yui-gd div.first {
+ _width: 29.9%;
+}
+
+.yui-ge .yui-g {
+ width: 24%;
+}
+.yui-gf .yui-g {
+ width: 74.2%;
+}
+
+.yui-gb .yui-ge div.yui-u,
+.yui-gb .yui-gf div.yui-u {
+ float: right;
+}
+.yui-gb .yui-ge div.first,
+.yui-gb .yui-gf div.first {
+ float: left;
+}
+
+/* Width Accommodation for Nested Contexts */
+.yui-gb .yui-ge .yui-u,
+.yui-gb .yui-gf div.first {
+ *width: 24%;
+ _width: 20%;
+}
+
+/* Width Accommodation for Nested Contexts */
+.yui-gb .yui-ge div.first,
+.yui-gb .yui-gf .yui-u {
+ *width: 73.5%;
+ _width: 65.5%;
+}
+
+/* Patch for GD within GE */
+.yui-ge div.first .yui-gd .yui-u {
+ width: 65%;
+}
+.yui-ge div.first .yui-gd div.first {
+ width: 32%;
+}
+
+/*
+Section: Clearing. zoom for IE, :after for others
+*/
+
+#bd:after,
+.yui-g:after,
+.yui-gb:after,
+.yui-gc:after,
+.yui-gd:after,
+.yui-ge:after,
+.yui-gf:after,
+.yui-t1:after,
+.yui-t2:after,
+.yui-t3:after,
+.yui-t4:after,
+.yui-t5:after,
+.yui-t6:after {
+ content: ".";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+#bd,
+.yui-g,
+.yui-gb,
+.yui-gc,
+.yui-gd,
+.yui-ge,
+.yui-gf,
+.yui-t1,
+.yui-t2,
+.yui-t3,
+.yui-t4,
+.yui-t5,
+.yui-t6 {
+ zoom: 1;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-cssreset html{color:#000;background:#FFF;}.yui-cssreset body,.yui-cssreset div,.yui-cssreset dl,.yui-cssreset dt,.yui-cssreset dd,.yui-cssreset ul,.yui-cssreset ol,.yui-cssreset li,.yui-cssreset h1,.yui-cssreset h2,.yui-cssreset h3,.yui-cssreset h4,.yui-cssreset h5,.yui-cssreset h6,.yui-cssreset pre,.yui-cssreset code,.yui-cssreset form,.yui-cssreset fieldset,.yui-cssreset legend,.yui-cssreset input,.yui-cssreset textarea,.yui-cssreset p,.yui-cssreset blockquote,.yui-cssreset th,.yui-cssreset td{margin:0;padding:0;}.yui-cssreset table{border-collapse:collapse;border-spacing:0;}.yui-cssreset fieldset,.yui-cssreset img{border:0;}.yui-cssreset address,.yui-cssreset caption,.yui-cssreset cite,.yui-cssreset code,.yui-cssreset dfn,.yui-cssreset em,.yui-cssreset strong,.yui-cssreset th,.yui-cssreset var{font-style:normal;font-weight:normal;}.yui-cssreset li{list-style:none;}.yui-cssreset caption,.yui-cssreset th{text-align:left;}.yui-cssreset h1,.yui-cssreset h2,.yui-cssreset h3,.yui-cssreset h4,.yui-cssreset h5,.yui-cssreset h6{font-size:100%;font-weight:normal;}.yui-cssreset q:before,.yui-cssreset q:after{content:'';}.yui-cssreset abbr,.yui-cssreset acronym{border:0;font-variant:normal;}.yui-cssreset sup{vertical-align:text-top;}.yui-cssreset sub{vertical-align:text-bottom;}.yui-cssreset input,.yui-cssreset textarea,.yui-cssreset select{font-family:inherit;font-size:inherit;font-weight:inherit;}.yui-cssreset input,.yui-cssreset textarea,.yui-cssreset select{*font-size:100%;}.yui-cssreset legend{color:#000;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*e
+ TODO will need to remove settings on HTML since we can't namespace it.
+ TODO with the prefix, should I group by selector or property for weight savings?
+*/
+.yui-cssreset html{
+ color:#000;
+ background:#FFF;
+}
+/*
+ TODO remove settings on BODY since we can't namespace it.
+*/
+/*
+ TODO test putting a class on HEAD.
+ - Fails on FF.
+*/
+.yui-cssreset body,
+.yui-cssreset div,
+.yui-cssreset dl,
+.yui-cssreset dt,
+.yui-cssreset dd,
+.yui-cssreset ul,
+.yui-cssreset ol,
+.yui-cssreset li,
+.yui-cssreset h1,
+.yui-cssreset h2,
+.yui-cssreset h3,
+.yui-cssreset h4,
+.yui-cssreset h5,
+.yui-cssreset h6,
+.yui-cssreset pre,
+.yui-cssreset code,
+.yui-cssreset form,
+.yui-cssreset fieldset,
+.yui-cssreset legend,
+.yui-cssreset input,
+.yui-cssreset textarea,
+.yui-cssreset p,
+.yui-cssreset blockquote,
+.yui-cssreset th,
+.yui-cssreset td {
+ margin:0;
+ padding:0;
+}
+.yui-cssreset table {
+ border-collapse:collapse;
+ border-spacing:0;
+}
+.yui-cssreset fieldset,
+.yui-cssreset img {
+ border:0;
+}
+/*
+ TODO think about hanlding inheritence differently, maybe letting IE6 fail a bit...
+*/
+.yui-cssreset address,
+.yui-cssreset caption,
+.yui-cssreset cite,
+.yui-cssreset code,
+.yui-cssreset dfn,
+.yui-cssreset em,
+.yui-cssreset strong,
+.yui-cssreset th,
+.yui-cssreset var {
+ font-style:normal;
+ font-weight:normal;
+}
+/*
+ TODO Figure out where this list-style rule is best set. Hedger has a request to investigate.
+*/
+.yui-cssreset li {
+ list-style:none;
+}
+
+.yui-cssreset caption,
+.yui-cssreset th {
+ text-align:left;
+}
+.yui-cssreset h1,
+.yui-cssreset h2,
+.yui-cssreset h3,
+.yui-cssreset h4,
+.yui-cssreset h5,
+.yui-cssreset h6 {
+ font-size:100%;
+ font-weight:normal;
+}
+.yui-cssreset q:before,
+.yui-cssreset q:after {
+ content:'';
+}
+.yui-cssreset abbr,
+.yui-cssreset acronym {
+ border:0;
+ font-variant:normal;
+}
+/* to preserve line-height and selector appearance */
+.yui-cssreset sup {
+ vertical-align:text-top;
+}
+.yui-cssreset sub {
+ vertical-align:text-bottom;
+}
+.yui-cssreset input,
+.yui-cssreset textarea,
+.yui-cssreset select {
+ font-family:inherit;
+ font-size:inherit;
+ font-weight:inherit;
+}
+/*to enable resizing for IE*/
+.yui-cssreset input,
+.yui-cssreset textarea,
+.yui-cssreset select {
+ *font-size:100%;
+}
+/*because legend doesn't inherit in IE */
+.yui-cssreset legend {
+ color:#000;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*
+ TODO will need to remove settings on HTML since we can't namespace it.
+ TODO with the prefix, should I group by selector or property for weight savings?
+*/
+html{
+ color:#000;
+ background:#FFF;
+}
+/*
+ TODO remove settings on BODY since we can't namespace it.
+*/
+/*
+ TODO test putting a class on HEAD.
+ - Fails on FF.
+*/
+body,
+div,
+dl,
+dt,
+dd,
+ul,
+ol,
+li,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+pre,
+code,
+form,
+fieldset,
+legend,
+input,
+textarea,
+p,
+blockquote,
+th,
+td {
+ margin:0;
+ padding:0;
+}
+table {
+ border-collapse:collapse;
+ border-spacing:0;
+}
+fieldset,
+img {
+ border:0;
+}
+/*
+ TODO think about hanlding inheritence differently, maybe letting IE6 fail a bit...
+*/
+address,
+caption,
+cite,
+code,
+dfn,
+em,
+strong,
+th,
+var {
+ font-style:normal;
+ font-weight:normal;
+}
+/*
+ TODO Figure out where this list-style rule is best set. Hedger has a request to investigate.
+*/
+li {
+ list-style:none;
+}
+
+caption,
+th {
+ text-align:left;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-size:100%;
+ font-weight:normal;
+}
+q:before,
+q:after {
+ content:'';
+}
+abbr,
+acronym {
+ border:0;
+ font-variant:normal;
+}
+/* to preserve line-height and selector appearance */
+sup {
+ vertical-align:text-top;
+}
+sub {
+ vertical-align:text-bottom;
+}
+input,
+textarea,
+select {
+ font-family:inherit;
+ font-size:inherit;
+ font-weight:inherit;
+}
+/*to enable resizing for IE*/
+input,
+textarea,
+select {
+ *font-size:100%;
+}
+/*because legend doesn't inherit in IE */
+legend {
+ color:#000;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-array', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with data stored in arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-array
+ */
+
+/**
+ * Array subclass for the DataSchema Utility.
+ * @class DataSchema.Array
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaArray = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Array static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given Array data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Array data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isArray(data_in)) {
+ if(LANG.isArray(schema.resultFields)) {
+ // Parse results data
+ data_out = SchemaArray._parseResults(schema.resultFields, data_in, data_out);
+ }
+ else {
+ data_out.results = data_in;
+ Y.log("Schema resultFields property not found: " + Y.dump(schema), "warn", "dataschema-array");
+ }
+ }
+ else {
+ Y.log("Array data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-array");
+ data_out.error = new Error("Array schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param fields {Array} Schema to parse against.
+ * @param array_in {Array} Array to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(fields, array_in, data_out) {
+ var results = [],
+ result, item, type, field, key, value, i, j;
+
+ for(i=array_in.length-1; i>-1; i--) {
+ result = {};
+ item = array_in[i];
+ type = (LANG.isObject(item) && !LANG.isFunction(item)) ? 2 : (LANG.isArray(item)) ? 1 : (LANG.isString(item)) ? 0 : -1;
+ if(type > 0) {
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(item[key])) ? item[key] : item[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+ else if(type === 0) {
+ result = item;
+ }
+ else {
+ //TODO: null or {}?
+ result = null;
+ Y.log("Unexpected type while parsing array: " + Y.dump(item), "warn", "dataschema-array");
+ }
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Array = Y.mix(SchemaArray, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dataschema-array",function(C){var A=C.Lang,B={apply:function(F,G){var D=G,E={results:[],meta:{}};if(A.isArray(D)){if(A.isArray(F.resultFields)){E=B._parseResults(F.resultFields,D,E);}else{E.results=D;}}else{E.error=new Error("Array schema parse failure");}return E;},_parseResults:function(H,K,D){var G=[],O,N,I,J,M,L,F,E;for(F=K.length-1;F>-1;F--){O={};N=K[F];I=(A.isObject(N)&&!A.isFunction(N))?2:(A.isArray(N))?1:(A.isString(N))?0:-1;if(I>0){for(E=H.length-1;E>-1;E--){J=H[E];M=(!A.isUndefined(J.key))?J.key:J;L=(!A.isUndefined(N[M]))?N[M]:N[E];O[M]=C.DataSchema.Base.parse(L,J);}}else{if(I===0){O=N;}else{O=null;}}G[F]=O;}D.results=G;return D;}};C.DataSchema.Array=C.mix(B,C.DataSchema.Base);},"3.0.0",{requires:["dataschema-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-array', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with data stored in arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-array
+ */
+
+/**
+ * Array subclass for the DataSchema Utility.
+ * @class DataSchema.Array
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaArray = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Array static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given Array data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Array data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isArray(data_in)) {
+ if(LANG.isArray(schema.resultFields)) {
+ // Parse results data
+ data_out = SchemaArray._parseResults(schema.resultFields, data_in, data_out);
+ }
+ else {
+ data_out.results = data_in;
+ }
+ }
+ else {
+ data_out.error = new Error("Array schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param fields {Array} Schema to parse against.
+ * @param array_in {Array} Array to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(fields, array_in, data_out) {
+ var results = [],
+ result, item, type, field, key, value, i, j;
+
+ for(i=array_in.length-1; i>-1; i--) {
+ result = {};
+ item = array_in[i];
+ type = (LANG.isObject(item) && !LANG.isFunction(item)) ? 2 : (LANG.isArray(item)) ? 1 : (LANG.isString(item)) ? 0 : -1;
+ if(type > 0) {
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(item[key])) ? item[key] : item[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+ else if(type === 0) {
+ result = item;
+ }
+ else {
+ //TODO: null or {}?
+ result = null;
+ }
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Array = Y.mix(SchemaArray, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-base', function(Y) {
+
+/**
+ * The DataSchema utility provides a common configurable interface for widgets to
+ * apply a given schema to a variety of data.
+ *
+ * @module dataschema
+ */
+
+/**
+ * Provides the base DataSchema implementation, which can be extended to
+ * create DataSchemas for specific data formats, such XML, JSON, text and
+ * arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-base
+ */
+
+var LANG = Y.Lang,
+/**
+ * Base class for the YUI DataSchema Utility.
+ * @class DataSchema.Base
+ * @static
+ */
+ SchemaBase = {
+ /**
+ * Overridable method returns data as-is.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ return data;
+ },
+
+ /**
+ * Applies field parser, if defined
+ *
+ * @method parse
+ * @param value {Object} Original value.
+ * @param field {Object} Field.
+ * @return {Object} Type-converted value.
+ */
+ parse: function(value, field) {
+ if(field.parser) {
+ var parser = (LANG.isFunction(field.parser)) ?
+ field.parser : Y.Parsers[field.parser+''];
+ if(parser) {
+ value = parser.call(this, value);
+ }
+ else {
+ Y.log("Could not find parser for field " + Y.dump(field), "warn", "dataschema-json");
+ }
+ }
+ return value;
+ }
+};
+
+Y.namespace("DataSchema").Base = SchemaBase;
+Y.namespace("Parsers");
+
+
+
+}, '3.0.0' ,{requires:['base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dataschema-base",function(B){var A=B.Lang,C={apply:function(D,E){return E;},parse:function(D,E){if(E.parser){var F=(A.isFunction(E.parser))?E.parser:B.Parsers[E.parser+""];if(F){D=F.call(this,D);}else{}}return D;}};B.namespace("DataSchema").Base=C;B.namespace("Parsers");},"3.0.0",{requires:["base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-base', function(Y) {
+
+/**
+ * The DataSchema utility provides a common configurable interface for widgets to
+ * apply a given schema to a variety of data.
+ *
+ * @module dataschema
+ */
+
+/**
+ * Provides the base DataSchema implementation, which can be extended to
+ * create DataSchemas for specific data formats, such XML, JSON, text and
+ * arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-base
+ */
+
+var LANG = Y.Lang,
+/**
+ * Base class for the YUI DataSchema Utility.
+ * @class DataSchema.Base
+ * @static
+ */
+ SchemaBase = {
+ /**
+ * Overridable method returns data as-is.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ return data;
+ },
+
+ /**
+ * Applies field parser, if defined
+ *
+ * @method parse
+ * @param value {Object} Original value.
+ * @param field {Object} Field.
+ * @return {Object} Type-converted value.
+ */
+ parse: function(value, field) {
+ if(field.parser) {
+ var parser = (LANG.isFunction(field.parser)) ?
+ field.parser : Y.Parsers[field.parser+''];
+ if(parser) {
+ value = parser.call(this, value);
+ }
+ else {
+ }
+ }
+ return value;
+ }
+};
+
+Y.namespace("DataSchema").Base = SchemaBase;
+Y.namespace("Parsers");
+
+
+
+}, '3.0.0' ,{requires:['base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-base', function(Y) {
+
+/**
+ * The DataSchema utility provides a common configurable interface for widgets to
+ * apply a given schema to a variety of data.
+ *
+ * @module dataschema
+ */
+
+/**
+ * Provides the base DataSchema implementation, which can be extended to
+ * create DataSchemas for specific data formats, such XML, JSON, text and
+ * arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-base
+ */
+
+var LANG = Y.Lang,
+/**
+ * Base class for the YUI DataSchema Utility.
+ * @class DataSchema.Base
+ * @static
+ */
+ SchemaBase = {
+ /**
+ * Overridable method returns data as-is.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ return data;
+ },
+
+ /**
+ * Applies field parser, if defined
+ *
+ * @method parse
+ * @param value {Object} Original value.
+ * @param field {Object} Field.
+ * @return {Object} Type-converted value.
+ */
+ parse: function(value, field) {
+ if(field.parser) {
+ var parser = (LANG.isFunction(field.parser)) ?
+ field.parser : Y.Parsers[field.parser+''];
+ if(parser) {
+ value = parser.call(this, value);
+ }
+ else {
+ Y.log("Could not find parser for field " + Y.dump(field), "warn", "dataschema-json");
+ }
+ }
+ return value;
+ }
+};
+
+Y.namespace("DataSchema").Base = SchemaBase;
+Y.namespace("Parsers");
+
+
+
+}, '3.0.0' ,{requires:['base']});
+
+YUI.add('dataschema-json', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with JSON data.
+ *
+ * @module dataschema
+ * @submodule dataschema-json
+ */
+
+/**
+ * JSON subclass for the DataSchema Utility.
+ * @class DataSchema.JSON
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaJSON = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.JSON static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Utility function converts JSON locator strings into walkable paths
+ *
+ * @method DataSchema.JSON.getPath
+ * @param locator {String} JSON value locator.
+ * @return {String[]} Walkable path to data value.
+ * @static
+ */
+ getPath: function(locator) {
+ var path = null,
+ keys = [],
+ i = 0;
+
+ if (locator) {
+ // Strip the ["string keys"] and [1] array indexes
+ locator = locator.
+ replace(/\[(['"])(.*?)\1\]/g,
+ function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
+ replace(/\[(\d+)\]/g,
+ function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
+ replace(/^\./,''); // remove leading dot
+
+ // Validate against problematic characters.
+ if (!/[^\w\.\$@]/.test(locator)) {
+ path = locator.split('.');
+ for (i=path.length-1; i >= 0; --i) {
+ if (path[i].charAt(0) === '@') {
+ path[i] = keys[parseInt(path[i].substr(1),10)];
+ }
+ }
+ }
+ else {
+ Y.log("Invalid locator: " + locator, "error", "dataschema-json");
+ }
+ }
+ return path;
+ },
+
+ /**
+ * Utility function to walk a path and return the value located there.
+ *
+ * @method DataSchema.JSON.getLocationValue
+ * @param path {String[]} Locator path.
+ * @param data {String} Data to traverse.
+ * @return {Object} Data value at location.
+ * @static
+ */
+ getLocationValue: function (path, data) {
+ var i = 0,
+ len = path.length;
+ for (;i<len;i++) {
+ if(!LANG.isUndefined(data[path[i]])) {
+ data = data[path[i]];
+ }
+ else {
+ data = undefined;
+ break;
+ }
+ }
+ return data;
+ },
+
+ /**
+ * Applies a given schema to given JSON data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} JSON data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ // Convert incoming JSON strings
+ if(!LANG.isObject(data)) {
+ try {
+ data_in = Y.JSON.parse(data);
+ }
+ catch(e) {
+ data_out.error = e;
+ return data_out;
+ }
+ }
+
+ if(LANG.isObject(data_in) && schema) {
+ // Parse results data
+ if(!LANG.isUndefined(schema.resultListLocator)) {
+ data_out = SchemaJSON._parseResults(schema, data_in, data_out);
+ }
+
+ // Parse meta data
+ if(!LANG.isUndefined(schema.metaFields)) {
+ data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
+ }
+ }
+ else {
+ Y.log("JSON data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-json");
+ data_out.error = new Error("JSON schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, json_in, data_out) {
+ var results = [],
+ path,
+ error;
+
+ if(schema.resultListLocator) {
+ path = SchemaJSON.getPath(schema.resultListLocator);
+ if(path) {
+ results = SchemaJSON.getLocationValue(path, json_in);
+ if (results === undefined) {
+ data_out.results = [];
+ error = new Error("JSON results retrieval failure");
+ }
+ else {
+ if(LANG.isArray(schema.resultFields) && LANG.isArray(results)) {
+ data_out = SchemaJSON._getFieldValues(schema.resultFields, results, data_out);
+ }
+ else {
+ data_out.results = [];
+ error = new Error("JSON Schema fields retrieval failure");
+ }
+ }
+ }
+ else {
+ error = new Error("JSON Schema results locator failure");
+ }
+
+ if (error) {
+ Y.log("JSON data could not be parsed: " + Y.dump(json_in), "error", "dataschema-json");
+ data_out.error = error;
+ }
+
+ }
+ return data_out;
+ },
+
+ /**
+ * Get field data values out of list of full results
+ *
+ * @method _getFieldValues
+ * @param fields {Array} Fields to find.
+ * @param array_in {Array} Results to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _getFieldValues: function(fields, array_in, data_out) {
+ var results = [],
+ len = fields.length,
+ i, j,
+ field, key, path, parser,
+ simplePaths = [], complexPaths = [], fieldParsers = [],
+ result, record;
+
+ // First collect hashes of simple paths, complex paths, and parsers
+ for (i=0; i<len; i++) {
+ field = fields[i]; // A field can be a simple string or a hash
+ key = field.key || field; // Find the key
+
+ // Validate and store locators for later
+ path = SchemaJSON.getPath(key);
+ if (path) {
+ if (path.length === 1) {
+ simplePaths[simplePaths.length] = {key:key, path:path[0]};
+ } else {
+ complexPaths[complexPaths.length] = {key:key, path:path};
+ }
+ } else {
+ Y.log("Invalid key syntax: " + key, "warn", "dataschema-json");
+ }
+
+ // Validate and store parsers for later
+ //TODO: use Y.DataSchema.parse?
+ parser = (LANG.isFunction(field.parser)) ? field.parser : Y.Parsers[field.parser+''];
+ if (parser) {
+ fieldParsers[fieldParsers.length] = {key:key, parser:parser};
+ }
+ }
+
+ // Traverse list of array_in, creating records of simple fields,
+ // complex fields, and applying parsers as necessary
+ for (i=array_in.length-1; i>=0; --i) {
+ record = {};
+ result = array_in[i];
+ if(result) {
+ // Cycle through simpleLocators
+ for (j=simplePaths.length-1; j>=0; --j) {
+ // Bug 1777850: The result might be an array instead of object
+ record[simplePaths[j].key] = Y.DataSchema.Base.parse(
+ (LANG.isUndefined(result[simplePaths[j].path]) ?
+ result[j] : result[simplePaths[j].path]), simplePaths[j]);
+ }
+
+ // Cycle through complexLocators
+ for (j=complexPaths.length - 1; j>=0; --j) {
+ record[complexPaths[j].key] = Y.DataSchema.Base.parse(
+ (SchemaJSON.getLocationValue(complexPaths[j].path, result)), complexPaths[j] );
+ }
+
+ // Cycle through fieldParsers
+ for (j=fieldParsers.length-1; j>=0; --j) {
+ key = fieldParsers[j].key;
+ record[key] = fieldParsers[j].parser(record[key]);
+ // Safety net
+ if (LANG.isUndefined(record[key])) {
+ record[key] = null;
+ }
+ }
+ results[i] = record;
+ }
+ }
+ data_out.results = results;
+ return data_out;
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param metaFields {Object} Metafields definitions.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Schema-parsed meta data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, json_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key, path;
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ path = SchemaJSON.getPath(metaFields[key]);
+ if (path && json_in) {
+ data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
+ }
+ }
+ }
+ }
+ else {
+ data_out.error = new Error("JSON meta data retrieval failure");
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['json', 'dataschema-base']});
+
+YUI.add('dataschema-xml', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with XML data.
+ *
+ * @module dataschema
+ * @submodule dataschema-xml
+ */
+var LANG = Y.Lang,
+
+ /**
+ * XML subclass for the DataSchema Utility.
+ * @class DataSchema.XML
+ * @extends DataSchema.Base
+ * @static
+ */
+ SchemaXML = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.XML static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given XML data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {XMLDoc} XML document.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var xmldoc = data,
+ data_out = {results:[],meta:{}};
+
+ if(xmldoc && xmldoc.nodeType && (xmldoc.nodeType === 9 || xmldoc.nodeType === 1 || xmldoc.nodeType === 11) && schema) {
+ // Parse results data
+ data_out = SchemaXML._parseResults(schema, xmldoc, data_out);
+
+ // Parse meta data
+ data_out = SchemaXML._parseMeta(schema.metaFields, xmldoc, data_out);
+ }
+ else {
+ Y.log("XML data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-xml");
+ data_out.error = new Error("XML schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Get an XPath-specified value for a given field from an XML node or document.
+ *
+ * @method _getLocationValue
+ * @param field {String | Object} Field definition.
+ * @param context {Object} XML node or document to search within.
+ * @return {Object} Data value or null.
+ * @static
+ * @protected
+ */
+ _getLocationValue: function(field, context) {
+ var locator = field.locator || field.key || field,
+ xmldoc = context.ownerDocument || context,
+ result, res, value = null;
+
+ try {
+ // Standards mode
+ if(!LANG.isUndefined(xmldoc.evaluate)) {
+ result = xmldoc.evaluate(locator, context, xmldoc.createNSResolver(!context.ownerDocument ? context.documentElement : context.ownerDocument.documentElement), 0, null);
+ while(res = result.iterateNext()) {
+ value = res.textContent;
+ }
+ }
+ // IE mode
+ else {
+ xmldoc.setProperty("SelectionLanguage", "XPath");
+ result = context.selectNodes(locator)[0];
+ value = result.value || result.text || null;
+ }
+ return Y.DataSchema.Base.parse(value, field);
+
+ }
+ catch(e) {
+ }
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, xmldoc_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key,
+ xmldoc = xmldoc_in.ownerDocument || xmldoc_in;
+
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ data_out.meta[key] = SchemaXML._getLocationValue(metaFields[key], xmldoc);
+ }
+ }
+ }
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, xmldoc_in, data_out) {
+ if(schema.resultListLocator && LANG.isArray(schema.resultFields)) {
+ var nodeList = xmldoc_in.getElementsByTagName(schema.resultListLocator),
+ fields = schema.resultFields,
+ results = [],
+ node, field, result, i, j;
+
+ if(nodeList.length) {
+ // Loop through each result node
+ for(i=nodeList.length-1; i>= 0; i--) {
+ result = {};
+ node = nodeList[i];
+
+ // Find each field value
+ for(j=fields.length-1; j>= 0; j--) {
+ field = fields[j];
+ result[field.key || field] = SchemaXML._getLocationValue(field, node);
+ }
+ results[i] = result;
+ }
+
+ data_out.results = results;
+ }
+ else {
+ data_out.error = new Error("XML schema result nodes retrieval failure");
+ }
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.XML = Y.mix(SchemaXML, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
+
+YUI.add('dataschema-array', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with data stored in arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-array
+ */
+
+/**
+ * Array subclass for the DataSchema Utility.
+ * @class DataSchema.Array
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaArray = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Array static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given Array data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Array data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isArray(data_in)) {
+ if(LANG.isArray(schema.resultFields)) {
+ // Parse results data
+ data_out = SchemaArray._parseResults(schema.resultFields, data_in, data_out);
+ }
+ else {
+ data_out.results = data_in;
+ Y.log("Schema resultFields property not found: " + Y.dump(schema), "warn", "dataschema-array");
+ }
+ }
+ else {
+ Y.log("Array data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-array");
+ data_out.error = new Error("Array schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param fields {Array} Schema to parse against.
+ * @param array_in {Array} Array to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(fields, array_in, data_out) {
+ var results = [],
+ result, item, type, field, key, value, i, j;
+
+ for(i=array_in.length-1; i>-1; i--) {
+ result = {};
+ item = array_in[i];
+ type = (LANG.isObject(item) && !LANG.isFunction(item)) ? 2 : (LANG.isArray(item)) ? 1 : (LANG.isString(item)) ? 0 : -1;
+ if(type > 0) {
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(item[key])) ? item[key] : item[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+ else if(type === 0) {
+ result = item;
+ }
+ else {
+ //TODO: null or {}?
+ result = null;
+ Y.log("Unexpected type while parsing array: " + Y.dump(item), "warn", "dataschema-array");
+ }
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Array = Y.mix(SchemaArray, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
+
+YUI.add('dataschema-text', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with delimited text data.
+ *
+ * @module dataschema
+ * @submodule dataschema-text
+ */
+
+/**
+ * Text subclass for the DataSchema Utility.
+ * @class DataSchema.Text
+ * @extends DataSchema.Base
+ * @static
+ */
+
+var LANG = Y.Lang,
+
+ SchemaText = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Text static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given delimited text data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Text data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isString(data_in) && LANG.isString(schema.resultDelimiter)) {
+ // Parse results data
+ data_out = SchemaText._parseResults(schema, data_in, data_out);
+ }
+ else {
+ Y.log("Text data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-text");
+ data_out.error = new Error("Text schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Array} Schema to parse against.
+ * @param text_in {String} Text to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, text_in, data_out) {
+ var resultDelim = schema.resultDelimiter,
+ results = [],
+ results_in, fields_in, result, item, fields, field, key, value, i, j,
+
+ // Delete final delimiter at end of string if there
+ tmpLength = text_in.length-resultDelim.length;
+ if(text_in.substr(tmpLength) == resultDelim) {
+ text_in = text_in.substr(0, tmpLength);
+ }
+
+ // Split into results
+ results_in = text_in.split(schema.resultDelimiter);
+
+ for(i=results_in.length-1; i>-1; i--) {
+ result = {};
+ item = results_in[i];
+
+ if(LANG.isString(schema.fieldDelimiter)) {
+ fields_in = item.split(schema.fieldDelimiter);
+
+ if(LANG.isArray(schema.resultFields)) {
+ fields = schema.resultFields;
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(fields_in[key])) ? fields_in[key] : fields_in[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+
+ }
+ else {
+ result = item;
+ }
+
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Text = Y.mix(SchemaText, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
+
+
+
+YUI.add('dataschema', function(Y){}, '3.0.0' ,{use:['dataschema-base','dataschema-json','dataschema-xml','dataschema-array','dataschema-text']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-json', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with JSON data.
+ *
+ * @module dataschema
+ * @submodule dataschema-json
+ */
+
+/**
+ * JSON subclass for the DataSchema Utility.
+ * @class DataSchema.JSON
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaJSON = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.JSON static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Utility function converts JSON locator strings into walkable paths
+ *
+ * @method DataSchema.JSON.getPath
+ * @param locator {String} JSON value locator.
+ * @return {String[]} Walkable path to data value.
+ * @static
+ */
+ getPath: function(locator) {
+ var path = null,
+ keys = [],
+ i = 0;
+
+ if (locator) {
+ // Strip the ["string keys"] and [1] array indexes
+ locator = locator.
+ replace(/\[(['"])(.*?)\1\]/g,
+ function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
+ replace(/\[(\d+)\]/g,
+ function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
+ replace(/^\./,''); // remove leading dot
+
+ // Validate against problematic characters.
+ if (!/[^\w\.\$@]/.test(locator)) {
+ path = locator.split('.');
+ for (i=path.length-1; i >= 0; --i) {
+ if (path[i].charAt(0) === '@') {
+ path[i] = keys[parseInt(path[i].substr(1),10)];
+ }
+ }
+ }
+ else {
+ Y.log("Invalid locator: " + locator, "error", "dataschema-json");
+ }
+ }
+ return path;
+ },
+
+ /**
+ * Utility function to walk a path and return the value located there.
+ *
+ * @method DataSchema.JSON.getLocationValue
+ * @param path {String[]} Locator path.
+ * @param data {String} Data to traverse.
+ * @return {Object} Data value at location.
+ * @static
+ */
+ getLocationValue: function (path, data) {
+ var i = 0,
+ len = path.length;
+ for (;i<len;i++) {
+ if(!LANG.isUndefined(data[path[i]])) {
+ data = data[path[i]];
+ }
+ else {
+ data = undefined;
+ break;
+ }
+ }
+ return data;
+ },
+
+ /**
+ * Applies a given schema to given JSON data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} JSON data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ // Convert incoming JSON strings
+ if(!LANG.isObject(data)) {
+ try {
+ data_in = Y.JSON.parse(data);
+ }
+ catch(e) {
+ data_out.error = e;
+ return data_out;
+ }
+ }
+
+ if(LANG.isObject(data_in) && schema) {
+ // Parse results data
+ if(!LANG.isUndefined(schema.resultListLocator)) {
+ data_out = SchemaJSON._parseResults(schema, data_in, data_out);
+ }
+
+ // Parse meta data
+ if(!LANG.isUndefined(schema.metaFields)) {
+ data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
+ }
+ }
+ else {
+ Y.log("JSON data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-json");
+ data_out.error = new Error("JSON schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, json_in, data_out) {
+ var results = [],
+ path,
+ error;
+
+ if(schema.resultListLocator) {
+ path = SchemaJSON.getPath(schema.resultListLocator);
+ if(path) {
+ results = SchemaJSON.getLocationValue(path, json_in);
+ if (results === undefined) {
+ data_out.results = [];
+ error = new Error("JSON results retrieval failure");
+ }
+ else {
+ if(LANG.isArray(schema.resultFields) && LANG.isArray(results)) {
+ data_out = SchemaJSON._getFieldValues(schema.resultFields, results, data_out);
+ }
+ else {
+ data_out.results = [];
+ error = new Error("JSON Schema fields retrieval failure");
+ }
+ }
+ }
+ else {
+ error = new Error("JSON Schema results locator failure");
+ }
+
+ if (error) {
+ Y.log("JSON data could not be parsed: " + Y.dump(json_in), "error", "dataschema-json");
+ data_out.error = error;
+ }
+
+ }
+ return data_out;
+ },
+
+ /**
+ * Get field data values out of list of full results
+ *
+ * @method _getFieldValues
+ * @param fields {Array} Fields to find.
+ * @param array_in {Array} Results to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _getFieldValues: function(fields, array_in, data_out) {
+ var results = [],
+ len = fields.length,
+ i, j,
+ field, key, path, parser,
+ simplePaths = [], complexPaths = [], fieldParsers = [],
+ result, record;
+
+ // First collect hashes of simple paths, complex paths, and parsers
+ for (i=0; i<len; i++) {
+ field = fields[i]; // A field can be a simple string or a hash
+ key = field.key || field; // Find the key
+
+ // Validate and store locators for later
+ path = SchemaJSON.getPath(key);
+ if (path) {
+ if (path.length === 1) {
+ simplePaths[simplePaths.length] = {key:key, path:path[0]};
+ } else {
+ complexPaths[complexPaths.length] = {key:key, path:path};
+ }
+ } else {
+ Y.log("Invalid key syntax: " + key, "warn", "dataschema-json");
+ }
+
+ // Validate and store parsers for later
+ //TODO: use Y.DataSchema.parse?
+ parser = (LANG.isFunction(field.parser)) ? field.parser : Y.Parsers[field.parser+''];
+ if (parser) {
+ fieldParsers[fieldParsers.length] = {key:key, parser:parser};
+ }
+ }
+
+ // Traverse list of array_in, creating records of simple fields,
+ // complex fields, and applying parsers as necessary
+ for (i=array_in.length-1; i>=0; --i) {
+ record = {};
+ result = array_in[i];
+ if(result) {
+ // Cycle through simpleLocators
+ for (j=simplePaths.length-1; j>=0; --j) {
+ // Bug 1777850: The result might be an array instead of object
+ record[simplePaths[j].key] = Y.DataSchema.Base.parse(
+ (LANG.isUndefined(result[simplePaths[j].path]) ?
+ result[j] : result[simplePaths[j].path]), simplePaths[j]);
+ }
+
+ // Cycle through complexLocators
+ for (j=complexPaths.length - 1; j>=0; --j) {
+ record[complexPaths[j].key] = Y.DataSchema.Base.parse(
+ (SchemaJSON.getLocationValue(complexPaths[j].path, result)), complexPaths[j] );
+ }
+
+ // Cycle through fieldParsers
+ for (j=fieldParsers.length-1; j>=0; --j) {
+ key = fieldParsers[j].key;
+ record[key] = fieldParsers[j].parser(record[key]);
+ // Safety net
+ if (LANG.isUndefined(record[key])) {
+ record[key] = null;
+ }
+ }
+ results[i] = record;
+ }
+ }
+ data_out.results = results;
+ return data_out;
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param metaFields {Object} Metafields definitions.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Schema-parsed meta data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, json_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key, path;
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ path = SchemaJSON.getPath(metaFields[key]);
+ if (path && json_in) {
+ data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
+ }
+ }
+ }
+ }
+ else {
+ data_out.error = new Error("JSON meta data retrieval failure");
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['json', 'dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dataschema-json",function(C){var A=C.Lang,B={getPath:function(D){var G=null,F=[],E=0;if(D){D=D.replace(/\[(['"])(.*?)\1\]/g,function(I,H,J){F[E]=J;return".@"+(E++);}).replace(/\[(\d+)\]/g,function(I,H){F[E]=parseInt(H,10)|0;return".@"+(E++);}).replace(/^\./,"");if(!/[^\w\.\$@]/.test(D)){G=D.split(".");for(E=G.length-1;E>=0;--E){if(G[E].charAt(0)==="@"){G[E]=F[parseInt(G[E].substr(1),10)];}}}else{}}return G;},getLocationValue:function(G,F){var E=0,D=G.length;for(;E<D;E++){if(!A.isUndefined(F[G[E]])){F=F[G[E]];}else{F=undefined;break;}}return F;},apply:function(F,G){var D=G,E={results:[],meta:{}};if(!A.isObject(G)){try{D=C.JSON.parse(G);}catch(H){E.error=H;return E;}}if(A.isObject(D)&&F){if(!A.isUndefined(F.resultListLocator)){E=B._parseResults(F,D,E);}if(!A.isUndefined(F.metaFields)){E=B._parseMeta(F.metaFields,D,E);}}else{E.error=new Error("JSON schema parse failure");}return E;},_parseResults:function(H,D,G){var F=[],I,E;if(H.resultListLocator){I=B.getPath(H.resultListLocator);if(I){F=B.getLocationValue(I,D);if(F===undefined){G.results=[];E=new Error("JSON results retrieval failure");}else{if(A.isArray(H.resultFields)&&A.isArray(F)){G=B._getFieldValues(H.resultFields,F,G);}else{G.results=[];E=new Error("JSON Schema fields retrieval failure");}}}else{E=new Error("JSON Schema results locator failure");}if(E){G.error=E;}}return G;},_getFieldValues:function(K,P,E){var G=[],M=K.length,H,F,O,Q,S,D,J=[],N=[],L=[],R,I;for(H=0;H<M;H++){O=K[H];Q=O.key||O;S=B.getPath(Q);if(S){if(S.length===1){J[J.length]={key:Q,path:S[0]};}else{N[N.length]={key:Q,path:S};}}else{}D=(A.isFunction(O.parser))?O.parser:C.Parsers[O.parser+""];if(D){L[L.length]={key:Q,parser:D};}}for(H=P.length-1;H>=0;--H){I={};R=P[H];if(R){for(F=J.length-1;F>=0;--F){I[J[F].key]=C.DataSchema.Base.parse((A.isUndefined(R[J[F].path])?R[F]:R[J[F].path]),J[F]);}for(F=N.length-1;F>=0;--F){I[N[F].key]=C.DataSchema.Base.parse((B.getLocationValue(N[F].path,R)),N[F]);}for(F=L.length-1;F>=0;--F){Q=L[F].key;I[Q]=L[F].parser(I[Q]);if(A.isUndefined(I[Q])){I[Q]=null;}}G[H]=I;}}E.results=G;return E;},_parseMeta:function(G,D,F){if(A.isObject(G)){var E,H;for(E in G){if(G.hasOwnProperty(E)){H=B.getPath(G[E]);if(H&&D){F.meta[E]=B.getLocationValue(H,D);}}}}else{F.error=new Error("JSON meta data retrieval failure");}return F;}};C.DataSchema.JSON=C.mix(B,C.DataSchema.Base);},"3.0.0",{requires:["json","dataschema-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-json', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with JSON data.
+ *
+ * @module dataschema
+ * @submodule dataschema-json
+ */
+
+/**
+ * JSON subclass for the DataSchema Utility.
+ * @class DataSchema.JSON
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaJSON = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.JSON static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Utility function converts JSON locator strings into walkable paths
+ *
+ * @method DataSchema.JSON.getPath
+ * @param locator {String} JSON value locator.
+ * @return {String[]} Walkable path to data value.
+ * @static
+ */
+ getPath: function(locator) {
+ var path = null,
+ keys = [],
+ i = 0;
+
+ if (locator) {
+ // Strip the ["string keys"] and [1] array indexes
+ locator = locator.
+ replace(/\[(['"])(.*?)\1\]/g,
+ function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
+ replace(/\[(\d+)\]/g,
+ function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
+ replace(/^\./,''); // remove leading dot
+
+ // Validate against problematic characters.
+ if (!/[^\w\.\$@]/.test(locator)) {
+ path = locator.split('.');
+ for (i=path.length-1; i >= 0; --i) {
+ if (path[i].charAt(0) === '@') {
+ path[i] = keys[parseInt(path[i].substr(1),10)];
+ }
+ }
+ }
+ else {
+ }
+ }
+ return path;
+ },
+
+ /**
+ * Utility function to walk a path and return the value located there.
+ *
+ * @method DataSchema.JSON.getLocationValue
+ * @param path {String[]} Locator path.
+ * @param data {String} Data to traverse.
+ * @return {Object} Data value at location.
+ * @static
+ */
+ getLocationValue: function (path, data) {
+ var i = 0,
+ len = path.length;
+ for (;i<len;i++) {
+ if(!LANG.isUndefined(data[path[i]])) {
+ data = data[path[i]];
+ }
+ else {
+ data = undefined;
+ break;
+ }
+ }
+ return data;
+ },
+
+ /**
+ * Applies a given schema to given JSON data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} JSON data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ // Convert incoming JSON strings
+ if(!LANG.isObject(data)) {
+ try {
+ data_in = Y.JSON.parse(data);
+ }
+ catch(e) {
+ data_out.error = e;
+ return data_out;
+ }
+ }
+
+ if(LANG.isObject(data_in) && schema) {
+ // Parse results data
+ if(!LANG.isUndefined(schema.resultListLocator)) {
+ data_out = SchemaJSON._parseResults(schema, data_in, data_out);
+ }
+
+ // Parse meta data
+ if(!LANG.isUndefined(schema.metaFields)) {
+ data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
+ }
+ }
+ else {
+ data_out.error = new Error("JSON schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, json_in, data_out) {
+ var results = [],
+ path,
+ error;
+
+ if(schema.resultListLocator) {
+ path = SchemaJSON.getPath(schema.resultListLocator);
+ if(path) {
+ results = SchemaJSON.getLocationValue(path, json_in);
+ if (results === undefined) {
+ data_out.results = [];
+ error = new Error("JSON results retrieval failure");
+ }
+ else {
+ if(LANG.isArray(schema.resultFields) && LANG.isArray(results)) {
+ data_out = SchemaJSON._getFieldValues(schema.resultFields, results, data_out);
+ }
+ else {
+ data_out.results = [];
+ error = new Error("JSON Schema fields retrieval failure");
+ }
+ }
+ }
+ else {
+ error = new Error("JSON Schema results locator failure");
+ }
+
+ if (error) {
+ data_out.error = error;
+ }
+
+ }
+ return data_out;
+ },
+
+ /**
+ * Get field data values out of list of full results
+ *
+ * @method _getFieldValues
+ * @param fields {Array} Fields to find.
+ * @param array_in {Array} Results to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _getFieldValues: function(fields, array_in, data_out) {
+ var results = [],
+ len = fields.length,
+ i, j,
+ field, key, path, parser,
+ simplePaths = [], complexPaths = [], fieldParsers = [],
+ result, record;
+
+ // First collect hashes of simple paths, complex paths, and parsers
+ for (i=0; i<len; i++) {
+ field = fields[i]; // A field can be a simple string or a hash
+ key = field.key || field; // Find the key
+
+ // Validate and store locators for later
+ path = SchemaJSON.getPath(key);
+ if (path) {
+ if (path.length === 1) {
+ simplePaths[simplePaths.length] = {key:key, path:path[0]};
+ } else {
+ complexPaths[complexPaths.length] = {key:key, path:path};
+ }
+ } else {
+ }
+
+ // Validate and store parsers for later
+ //TODO: use Y.DataSchema.parse?
+ parser = (LANG.isFunction(field.parser)) ? field.parser : Y.Parsers[field.parser+''];
+ if (parser) {
+ fieldParsers[fieldParsers.length] = {key:key, parser:parser};
+ }
+ }
+
+ // Traverse list of array_in, creating records of simple fields,
+ // complex fields, and applying parsers as necessary
+ for (i=array_in.length-1; i>=0; --i) {
+ record = {};
+ result = array_in[i];
+ if(result) {
+ // Cycle through simpleLocators
+ for (j=simplePaths.length-1; j>=0; --j) {
+ // Bug 1777850: The result might be an array instead of object
+ record[simplePaths[j].key] = Y.DataSchema.Base.parse(
+ (LANG.isUndefined(result[simplePaths[j].path]) ?
+ result[j] : result[simplePaths[j].path]), simplePaths[j]);
+ }
+
+ // Cycle through complexLocators
+ for (j=complexPaths.length - 1; j>=0; --j) {
+ record[complexPaths[j].key] = Y.DataSchema.Base.parse(
+ (SchemaJSON.getLocationValue(complexPaths[j].path, result)), complexPaths[j] );
+ }
+
+ // Cycle through fieldParsers
+ for (j=fieldParsers.length-1; j>=0; --j) {
+ key = fieldParsers[j].key;
+ record[key] = fieldParsers[j].parser(record[key]);
+ // Safety net
+ if (LANG.isUndefined(record[key])) {
+ record[key] = null;
+ }
+ }
+ results[i] = record;
+ }
+ }
+ data_out.results = results;
+ return data_out;
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param metaFields {Object} Metafields definitions.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Schema-parsed meta data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, json_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key, path;
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ path = SchemaJSON.getPath(metaFields[key]);
+ if (path && json_in) {
+ data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
+ }
+ }
+ }
+ }
+ else {
+ data_out.error = new Error("JSON meta data retrieval failure");
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['json', 'dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dataschema-base",function(B){var A=B.Lang,C={apply:function(D,E){return E;},parse:function(D,E){if(E.parser){var F=(A.isFunction(E.parser))?E.parser:B.Parsers[E.parser+""];if(F){D=F.call(this,D);}else{}}return D;}};B.namespace("DataSchema").Base=C;B.namespace("Parsers");},"3.0.0",{requires:["base"]});YUI.add("dataschema-json",function(C){var A=C.Lang,B={getPath:function(D){var G=null,F=[],E=0;if(D){D=D.replace(/\[(['"])(.*?)\1\]/g,function(I,H,J){F[E]=J;return".@"+(E++);}).replace(/\[(\d+)\]/g,function(I,H){F[E]=parseInt(H,10)|0;return".@"+(E++);}).replace(/^\./,"");if(!/[^\w\.\$@]/.test(D)){G=D.split(".");for(E=G.length-1;E>=0;--E){if(G[E].charAt(0)==="@"){G[E]=F[parseInt(G[E].substr(1),10)];}}}else{}}return G;},getLocationValue:function(G,F){var E=0,D=G.length;for(;E<D;E++){if(!A.isUndefined(F[G[E]])){F=F[G[E]];}else{F=undefined;break;}}return F;},apply:function(F,G){var D=G,E={results:[],meta:{}};if(!A.isObject(G)){try{D=C.JSON.parse(G);}catch(H){E.error=H;return E;}}if(A.isObject(D)&&F){if(!A.isUndefined(F.resultListLocator)){E=B._parseResults(F,D,E);}if(!A.isUndefined(F.metaFields)){E=B._parseMeta(F.metaFields,D,E);}}else{E.error=new Error("JSON schema parse failure");}return E;},_parseResults:function(H,D,G){var F=[],I,E;if(H.resultListLocator){I=B.getPath(H.resultListLocator);if(I){F=B.getLocationValue(I,D);if(F===undefined){G.results=[];E=new Error("JSON results retrieval failure");}else{if(A.isArray(H.resultFields)&&A.isArray(F)){G=B._getFieldValues(H.resultFields,F,G);}else{G.results=[];E=new Error("JSON Schema fields retrieval failure");}}}else{E=new Error("JSON Schema results locator failure");}if(E){G.error=E;}}return G;},_getFieldValues:function(K,P,E){var G=[],M=K.length,H,F,O,Q,S,D,J=[],N=[],L=[],R,I;for(H=0;H<M;H++){O=K[H];Q=O.key||O;S=B.getPath(Q);if(S){if(S.length===1){J[J.length]={key:Q,path:S[0]};}else{N[N.length]={key:Q,path:S};}}else{}D=(A.isFunction(O.parser))?O.parser:C.Parsers[O.parser+""];if(D){L[L.length]={key:Q,parser:D};}}for(H=P.length-1;H>=0;--H){I={};R=P[H];if(R){for(F=J.length-1;F>=0;--F){I[J[F].key]=C.DataSchema.Base.parse((A.isUndefined(R[J[F].path])?R[F]:R[J[F].path]),J[F]);}for(F=N.length-1;F>=0;--F){I[N[F].key]=C.DataSchema.Base.parse((B.getLocationValue(N[F].path,R)),N[F]);}for(F=L.length-1;F>=0;--F){Q=L[F].key;I[Q]=L[F].parser(I[Q]);if(A.isUndefined(I[Q])){I[Q]=null;}}G[H]=I;}}E.results=G;return E;},_parseMeta:function(G,D,F){if(A.isObject(G)){var E,H;for(E in G){if(G.hasOwnProperty(E)){H=B.getPath(G[E]);if(H&&D){F.meta[E]=B.getLocationValue(H,D);}}}}else{F.error=new Error("JSON meta data retrieval failure");}return F;}};C.DataSchema.JSON=C.mix(B,C.DataSchema.Base);},"3.0.0",{requires:["json","dataschema-base"]});YUI.add("dataschema-xml",function(C){var B=C.Lang,A={apply:function(F,G){var D=G,E={results:[],meta:{}};if(D&&D.nodeType&&(D.nodeType===9||D.nodeType===1||D.nodeType===11)&&F){E=A._parseResults(F,D,E);E=A._parseMeta(F.metaFields,D,E);}else{E.error=new Error("XML schema parse failure");}return E;},_getLocationValue:function(K,H){var F=K.locator||K.key||K,E=H.ownerDocument||H,D,G,I=null;try{if(!B.isUndefined(E.evaluate)){D=E.evaluate(F,H,E.createNSResolver(!H.ownerDocument?H.documentElement:H.ownerDocument.documentElement),0,null);while(G=D.iterateNext()){I=G.textContent;}}else{E.setProperty("SelectionLanguage","XPath");D=H.selectNodes(F)[0];I=D.value||D.text||null;}return C.DataSchema.Base.parse(I,K);}catch(J){}},_parseMeta:function(H,G,F){if(B.isObject(H)){var E,D=G.ownerDocument||G;for(E in H){if(H.hasOwnProperty(E)){F.meta[E]=A._getLocationValue(H[E],D);}}}return F;},_parseResults:function(F,K,G){if(F.resultListLocator&&B.isArray(F.resultFields)){var E=K.getElementsByTagName(F.resultListLocator),L=F.resultFields,J=[],D,M,N,I,H;if(E.length){for(I=E.length-1;I>=0;I--){N={};D=E[I];for(H=L.length-1;H>=0;H--){M=L[H];N[M.key||M]=A._getLocationValue(M,D);}J[I]=N;}G.results=J;}else{G.error=new Error("XML schema result nodes retrieval failure");}}return G;}};C.DataSchema.XML=C.mix(A,C.DataSchema.Base);},"3.0.0",{requires:["dataschema-base"]});YUI.add("dataschema-array",function(C){var A=C.Lang,B={apply:function(F,G){var D=G,E={results:[],meta:{}};if(A.isArray(D)){if(A.isArray(F.resultFields)){E=B._parseResults(F.resultFields,D,E);}else{E.results=D;}}else{E.error=new Error("Array schema parse failure");}return E;},_parseResults:function(H,K,D){var G=[],O,N,I,J,M,L,F,E;for(F=K.length-1;F>-1;F--){O={};N=K[F];I=(A.isObject(N)&&!A.isFunction(N))?2:(A.isArray(N))?1:(A.isString(N))?0:-1;if(I>0){for(E=H.length-1;E>-1;E--){J=H[E];M=(!A.isUndefined(J.key))?J.key:J;L=(!A.isUndefined(N[M]))?N[M]:N[E];O[M]=C.DataSchema.Base.parse(L,J);}}else{if(I===0){O=N;}else{O=null;}}G[F]=O;}D.results=G;return D;}};C.DataSchema.Array=C.mix(B,C.DataSchema.Base);},"3.0.0",{requires:["dataschema-base"]});YUI.add("dataschema-text",function(C){var B=C.Lang,A={apply:function(F,G){var D=G,E={results:[],meta:{}};if(B.isString(D)&&B.isString(F.resultDelimiter)){E=A._parseResults(F,D,E);}else{E.error=new Error("Text schema parse failure");}return E;},_parseResults:function(D,K,E){var I=D.resultDelimiter,H=[],L,P,S,R,J,N,Q,O,G,F,M=K.length-I.length;if(K.substr(M)==I){K=K.substr(0,M);}L=K.split(D.resultDelimiter);for(G=L.length-1;G>-1;G--){S={};R=L[G];if(B.isString(D.fieldDelimiter)){P=R.split(D.fieldDelimiter);if(B.isArray(D.resultFields)){J=D.resultFields;for(F=J.length-1;F>-1;F--){N=J[F];Q=(!B.isUndefined(N.key))?N.key:N;O=(!B.isUndefined(P[Q]))?P[Q]:P[F];S[Q]=C.DataSchema.Base.parse(O,N);}}}else{S=R;}H[G]=S;}E.results=H;return E;}};C.DataSchema.Text=C.mix(A,C.DataSchema.Base);},"3.0.0",{requires:["dataschema-base"]});YUI.add("dataschema",function(A){},"3.0.0",{use:["dataschema-base","dataschema-json","dataschema-xml","dataschema-array","dataschema-text"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-text', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with delimited text data.
+ *
+ * @module dataschema
+ * @submodule dataschema-text
+ */
+
+/**
+ * Text subclass for the DataSchema Utility.
+ * @class DataSchema.Text
+ * @extends DataSchema.Base
+ * @static
+ */
+
+var LANG = Y.Lang,
+
+ SchemaText = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Text static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given delimited text data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Text data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isString(data_in) && LANG.isString(schema.resultDelimiter)) {
+ // Parse results data
+ data_out = SchemaText._parseResults(schema, data_in, data_out);
+ }
+ else {
+ Y.log("Text data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-text");
+ data_out.error = new Error("Text schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Array} Schema to parse against.
+ * @param text_in {String} Text to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, text_in, data_out) {
+ var resultDelim = schema.resultDelimiter,
+ results = [],
+ results_in, fields_in, result, item, fields, field, key, value, i, j,
+
+ // Delete final delimiter at end of string if there
+ tmpLength = text_in.length-resultDelim.length;
+ if(text_in.substr(tmpLength) == resultDelim) {
+ text_in = text_in.substr(0, tmpLength);
+ }
+
+ // Split into results
+ results_in = text_in.split(schema.resultDelimiter);
+
+ for(i=results_in.length-1; i>-1; i--) {
+ result = {};
+ item = results_in[i];
+
+ if(LANG.isString(schema.fieldDelimiter)) {
+ fields_in = item.split(schema.fieldDelimiter);
+
+ if(LANG.isArray(schema.resultFields)) {
+ fields = schema.resultFields;
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(fields_in[key])) ? fields_in[key] : fields_in[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+
+ }
+ else {
+ result = item;
+ }
+
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Text = Y.mix(SchemaText, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dataschema-text",function(C){var B=C.Lang,A={apply:function(F,G){var D=G,E={results:[],meta:{}};if(B.isString(D)&&B.isString(F.resultDelimiter)){E=A._parseResults(F,D,E);}else{E.error=new Error("Text schema parse failure");}return E;},_parseResults:function(D,K,E){var I=D.resultDelimiter,H=[],L,P,S,R,J,N,Q,O,G,F,M=K.length-I.length;if(K.substr(M)==I){K=K.substr(0,M);}L=K.split(D.resultDelimiter);for(G=L.length-1;G>-1;G--){S={};R=L[G];if(B.isString(D.fieldDelimiter)){P=R.split(D.fieldDelimiter);if(B.isArray(D.resultFields)){J=D.resultFields;for(F=J.length-1;F>-1;F--){N=J[F];Q=(!B.isUndefined(N.key))?N.key:N;O=(!B.isUndefined(P[Q]))?P[Q]:P[F];S[Q]=C.DataSchema.Base.parse(O,N);}}}else{S=R;}H[G]=S;}E.results=H;return E;}};C.DataSchema.Text=C.mix(A,C.DataSchema.Base);},"3.0.0",{requires:["dataschema-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-text', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with delimited text data.
+ *
+ * @module dataschema
+ * @submodule dataschema-text
+ */
+
+/**
+ * Text subclass for the DataSchema Utility.
+ * @class DataSchema.Text
+ * @extends DataSchema.Base
+ * @static
+ */
+
+var LANG = Y.Lang,
+
+ SchemaText = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Text static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given delimited text data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Text data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isString(data_in) && LANG.isString(schema.resultDelimiter)) {
+ // Parse results data
+ data_out = SchemaText._parseResults(schema, data_in, data_out);
+ }
+ else {
+ data_out.error = new Error("Text schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Array} Schema to parse against.
+ * @param text_in {String} Text to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, text_in, data_out) {
+ var resultDelim = schema.resultDelimiter,
+ results = [],
+ results_in, fields_in, result, item, fields, field, key, value, i, j,
+
+ // Delete final delimiter at end of string if there
+ tmpLength = text_in.length-resultDelim.length;
+ if(text_in.substr(tmpLength) == resultDelim) {
+ text_in = text_in.substr(0, tmpLength);
+ }
+
+ // Split into results
+ results_in = text_in.split(schema.resultDelimiter);
+
+ for(i=results_in.length-1; i>-1; i--) {
+ result = {};
+ item = results_in[i];
+
+ if(LANG.isString(schema.fieldDelimiter)) {
+ fields_in = item.split(schema.fieldDelimiter);
+
+ if(LANG.isArray(schema.resultFields)) {
+ fields = schema.resultFields;
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(fields_in[key])) ? fields_in[key] : fields_in[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+
+ }
+ else {
+ result = item;
+ }
+
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Text = Y.mix(SchemaText, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-xml', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with XML data.
+ *
+ * @module dataschema
+ * @submodule dataschema-xml
+ */
+var LANG = Y.Lang,
+
+ /**
+ * XML subclass for the DataSchema Utility.
+ * @class DataSchema.XML
+ * @extends DataSchema.Base
+ * @static
+ */
+ SchemaXML = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.XML static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given XML data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {XMLDoc} XML document.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var xmldoc = data,
+ data_out = {results:[],meta:{}};
+
+ if(xmldoc && xmldoc.nodeType && (xmldoc.nodeType === 9 || xmldoc.nodeType === 1 || xmldoc.nodeType === 11) && schema) {
+ // Parse results data
+ data_out = SchemaXML._parseResults(schema, xmldoc, data_out);
+
+ // Parse meta data
+ data_out = SchemaXML._parseMeta(schema.metaFields, xmldoc, data_out);
+ }
+ else {
+ Y.log("XML data could not be schema-parsed: " + Y.dump(data) + " " + Y.dump(data), "error", "dataschema-xml");
+ data_out.error = new Error("XML schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Get an XPath-specified value for a given field from an XML node or document.
+ *
+ * @method _getLocationValue
+ * @param field {String | Object} Field definition.
+ * @param context {Object} XML node or document to search within.
+ * @return {Object} Data value or null.
+ * @static
+ * @protected
+ */
+ _getLocationValue: function(field, context) {
+ var locator = field.locator || field.key || field,
+ xmldoc = context.ownerDocument || context,
+ result, res, value = null;
+
+ try {
+ // Standards mode
+ if(!LANG.isUndefined(xmldoc.evaluate)) {
+ result = xmldoc.evaluate(locator, context, xmldoc.createNSResolver(!context.ownerDocument ? context.documentElement : context.ownerDocument.documentElement), 0, null);
+ while(res = result.iterateNext()) {
+ value = res.textContent;
+ }
+ }
+ // IE mode
+ else {
+ xmldoc.setProperty("SelectionLanguage", "XPath");
+ result = context.selectNodes(locator)[0];
+ value = result.value || result.text || null;
+ }
+ return Y.DataSchema.Base.parse(value, field);
+
+ }
+ catch(e) {
+ }
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, xmldoc_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key,
+ xmldoc = xmldoc_in.ownerDocument || xmldoc_in;
+
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ data_out.meta[key] = SchemaXML._getLocationValue(metaFields[key], xmldoc);
+ }
+ }
+ }
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, xmldoc_in, data_out) {
+ if(schema.resultListLocator && LANG.isArray(schema.resultFields)) {
+ var nodeList = xmldoc_in.getElementsByTagName(schema.resultListLocator),
+ fields = schema.resultFields,
+ results = [],
+ node, field, result, i, j;
+
+ if(nodeList.length) {
+ // Loop through each result node
+ for(i=nodeList.length-1; i>= 0; i--) {
+ result = {};
+ node = nodeList[i];
+
+ // Find each field value
+ for(j=fields.length-1; j>= 0; j--) {
+ field = fields[j];
+ result[field.key || field] = SchemaXML._getLocationValue(field, node);
+ }
+ results[i] = result;
+ }
+
+ data_out.results = results;
+ }
+ else {
+ data_out.error = new Error("XML schema result nodes retrieval failure");
+ }
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.XML = Y.mix(SchemaXML, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dataschema-xml",function(C){var B=C.Lang,A={apply:function(F,G){var D=G,E={results:[],meta:{}};if(D&&D.nodeType&&(D.nodeType===9||D.nodeType===1||D.nodeType===11)&&F){E=A._parseResults(F,D,E);E=A._parseMeta(F.metaFields,D,E);}else{E.error=new Error("XML schema parse failure");}return E;},_getLocationValue:function(K,H){var F=K.locator||K.key||K,E=H.ownerDocument||H,D,G,I=null;try{if(!B.isUndefined(E.evaluate)){D=E.evaluate(F,H,E.createNSResolver(!H.ownerDocument?H.documentElement:H.ownerDocument.documentElement),0,null);while(G=D.iterateNext()){I=G.textContent;}}else{E.setProperty("SelectionLanguage","XPath");D=H.selectNodes(F)[0];I=D.value||D.text||null;}return C.DataSchema.Base.parse(I,K);}catch(J){}},_parseMeta:function(H,G,F){if(B.isObject(H)){var E,D=G.ownerDocument||G;for(E in H){if(H.hasOwnProperty(E)){F.meta[E]=A._getLocationValue(H[E],D);}}}return F;},_parseResults:function(F,K,G){if(F.resultListLocator&&B.isArray(F.resultFields)){var E=K.getElementsByTagName(F.resultListLocator),L=F.resultFields,J=[],D,M,N,I,H;if(E.length){for(I=E.length-1;I>=0;I--){N={};D=E[I];for(H=L.length-1;H>=0;H--){M=L[H];N[M.key||M]=A._getLocationValue(M,D);}J[I]=N;}G.results=J;}else{G.error=new Error("XML schema result nodes retrieval failure");}}return G;}};C.DataSchema.XML=C.mix(A,C.DataSchema.Base);},"3.0.0",{requires:["dataschema-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-xml', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with XML data.
+ *
+ * @module dataschema
+ * @submodule dataschema-xml
+ */
+var LANG = Y.Lang,
+
+ /**
+ * XML subclass for the DataSchema Utility.
+ * @class DataSchema.XML
+ * @extends DataSchema.Base
+ * @static
+ */
+ SchemaXML = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.XML static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given XML data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {XMLDoc} XML document.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var xmldoc = data,
+ data_out = {results:[],meta:{}};
+
+ if(xmldoc && xmldoc.nodeType && (xmldoc.nodeType === 9 || xmldoc.nodeType === 1 || xmldoc.nodeType === 11) && schema) {
+ // Parse results data
+ data_out = SchemaXML._parseResults(schema, xmldoc, data_out);
+
+ // Parse meta data
+ data_out = SchemaXML._parseMeta(schema.metaFields, xmldoc, data_out);
+ }
+ else {
+ data_out.error = new Error("XML schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Get an XPath-specified value for a given field from an XML node or document.
+ *
+ * @method _getLocationValue
+ * @param field {String | Object} Field definition.
+ * @param context {Object} XML node or document to search within.
+ * @return {Object} Data value or null.
+ * @static
+ * @protected
+ */
+ _getLocationValue: function(field, context) {
+ var locator = field.locator || field.key || field,
+ xmldoc = context.ownerDocument || context,
+ result, res, value = null;
+
+ try {
+ // Standards mode
+ if(!LANG.isUndefined(xmldoc.evaluate)) {
+ result = xmldoc.evaluate(locator, context, xmldoc.createNSResolver(!context.ownerDocument ? context.documentElement : context.ownerDocument.documentElement), 0, null);
+ while(res = result.iterateNext()) {
+ value = res.textContent;
+ }
+ }
+ // IE mode
+ else {
+ xmldoc.setProperty("SelectionLanguage", "XPath");
+ result = context.selectNodes(locator)[0];
+ value = result.value || result.text || null;
+ }
+ return Y.DataSchema.Base.parse(value, field);
+
+ }
+ catch(e) {
+ }
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, xmldoc_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key,
+ xmldoc = xmldoc_in.ownerDocument || xmldoc_in;
+
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ data_out.meta[key] = SchemaXML._getLocationValue(metaFields[key], xmldoc);
+ }
+ }
+ }
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, xmldoc_in, data_out) {
+ if(schema.resultListLocator && LANG.isArray(schema.resultFields)) {
+ var nodeList = xmldoc_in.getElementsByTagName(schema.resultListLocator),
+ fields = schema.resultFields,
+ results = [],
+ node, field, result, i, j;
+
+ if(nodeList.length) {
+ // Loop through each result node
+ for(i=nodeList.length-1; i>= 0; i--) {
+ result = {};
+ node = nodeList[i];
+
+ // Find each field value
+ for(j=fields.length-1; j>= 0; j--) {
+ field = fields[j];
+ result[field.key || field] = SchemaXML._getLocationValue(field, node);
+ }
+ results[i] = result;
+ }
+
+ data_out.results = results;
+ }
+ else {
+ data_out.error = new Error("XML schema result nodes retrieval failure");
+ }
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.XML = Y.mix(SchemaXML, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dataschema-base', function(Y) {
+
+/**
+ * The DataSchema utility provides a common configurable interface for widgets to
+ * apply a given schema to a variety of data.
+ *
+ * @module dataschema
+ */
+
+/**
+ * Provides the base DataSchema implementation, which can be extended to
+ * create DataSchemas for specific data formats, such XML, JSON, text and
+ * arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-base
+ */
+
+var LANG = Y.Lang,
+/**
+ * Base class for the YUI DataSchema Utility.
+ * @class DataSchema.Base
+ * @static
+ */
+ SchemaBase = {
+ /**
+ * Overridable method returns data as-is.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ return data;
+ },
+
+ /**
+ * Applies field parser, if defined
+ *
+ * @method parse
+ * @param value {Object} Original value.
+ * @param field {Object} Field.
+ * @return {Object} Type-converted value.
+ */
+ parse: function(value, field) {
+ if(field.parser) {
+ var parser = (LANG.isFunction(field.parser)) ?
+ field.parser : Y.Parsers[field.parser+''];
+ if(parser) {
+ value = parser.call(this, value);
+ }
+ else {
+ }
+ }
+ return value;
+ }
+};
+
+Y.namespace("DataSchema").Base = SchemaBase;
+Y.namespace("Parsers");
+
+
+
+}, '3.0.0' ,{requires:['base']});
+
+YUI.add('dataschema-json', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with JSON data.
+ *
+ * @module dataschema
+ * @submodule dataschema-json
+ */
+
+/**
+ * JSON subclass for the DataSchema Utility.
+ * @class DataSchema.JSON
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaJSON = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.JSON static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Utility function converts JSON locator strings into walkable paths
+ *
+ * @method DataSchema.JSON.getPath
+ * @param locator {String} JSON value locator.
+ * @return {String[]} Walkable path to data value.
+ * @static
+ */
+ getPath: function(locator) {
+ var path = null,
+ keys = [],
+ i = 0;
+
+ if (locator) {
+ // Strip the ["string keys"] and [1] array indexes
+ locator = locator.
+ replace(/\[(['"])(.*?)\1\]/g,
+ function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
+ replace(/\[(\d+)\]/g,
+ function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
+ replace(/^\./,''); // remove leading dot
+
+ // Validate against problematic characters.
+ if (!/[^\w\.\$@]/.test(locator)) {
+ path = locator.split('.');
+ for (i=path.length-1; i >= 0; --i) {
+ if (path[i].charAt(0) === '@') {
+ path[i] = keys[parseInt(path[i].substr(1),10)];
+ }
+ }
+ }
+ else {
+ }
+ }
+ return path;
+ },
+
+ /**
+ * Utility function to walk a path and return the value located there.
+ *
+ * @method DataSchema.JSON.getLocationValue
+ * @param path {String[]} Locator path.
+ * @param data {String} Data to traverse.
+ * @return {Object} Data value at location.
+ * @static
+ */
+ getLocationValue: function (path, data) {
+ var i = 0,
+ len = path.length;
+ for (;i<len;i++) {
+ if(!LANG.isUndefined(data[path[i]])) {
+ data = data[path[i]];
+ }
+ else {
+ data = undefined;
+ break;
+ }
+ }
+ return data;
+ },
+
+ /**
+ * Applies a given schema to given JSON data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} JSON data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ // Convert incoming JSON strings
+ if(!LANG.isObject(data)) {
+ try {
+ data_in = Y.JSON.parse(data);
+ }
+ catch(e) {
+ data_out.error = e;
+ return data_out;
+ }
+ }
+
+ if(LANG.isObject(data_in) && schema) {
+ // Parse results data
+ if(!LANG.isUndefined(schema.resultListLocator)) {
+ data_out = SchemaJSON._parseResults(schema, data_in, data_out);
+ }
+
+ // Parse meta data
+ if(!LANG.isUndefined(schema.metaFields)) {
+ data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
+ }
+ }
+ else {
+ data_out.error = new Error("JSON schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, json_in, data_out) {
+ var results = [],
+ path,
+ error;
+
+ if(schema.resultListLocator) {
+ path = SchemaJSON.getPath(schema.resultListLocator);
+ if(path) {
+ results = SchemaJSON.getLocationValue(path, json_in);
+ if (results === undefined) {
+ data_out.results = [];
+ error = new Error("JSON results retrieval failure");
+ }
+ else {
+ if(LANG.isArray(schema.resultFields) && LANG.isArray(results)) {
+ data_out = SchemaJSON._getFieldValues(schema.resultFields, results, data_out);
+ }
+ else {
+ data_out.results = [];
+ error = new Error("JSON Schema fields retrieval failure");
+ }
+ }
+ }
+ else {
+ error = new Error("JSON Schema results locator failure");
+ }
+
+ if (error) {
+ data_out.error = error;
+ }
+
+ }
+ return data_out;
+ },
+
+ /**
+ * Get field data values out of list of full results
+ *
+ * @method _getFieldValues
+ * @param fields {Array} Fields to find.
+ * @param array_in {Array} Results to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _getFieldValues: function(fields, array_in, data_out) {
+ var results = [],
+ len = fields.length,
+ i, j,
+ field, key, path, parser,
+ simplePaths = [], complexPaths = [], fieldParsers = [],
+ result, record;
+
+ // First collect hashes of simple paths, complex paths, and parsers
+ for (i=0; i<len; i++) {
+ field = fields[i]; // A field can be a simple string or a hash
+ key = field.key || field; // Find the key
+
+ // Validate and store locators for later
+ path = SchemaJSON.getPath(key);
+ if (path) {
+ if (path.length === 1) {
+ simplePaths[simplePaths.length] = {key:key, path:path[0]};
+ } else {
+ complexPaths[complexPaths.length] = {key:key, path:path};
+ }
+ } else {
+ }
+
+ // Validate and store parsers for later
+ //TODO: use Y.DataSchema.parse?
+ parser = (LANG.isFunction(field.parser)) ? field.parser : Y.Parsers[field.parser+''];
+ if (parser) {
+ fieldParsers[fieldParsers.length] = {key:key, parser:parser};
+ }
+ }
+
+ // Traverse list of array_in, creating records of simple fields,
+ // complex fields, and applying parsers as necessary
+ for (i=array_in.length-1; i>=0; --i) {
+ record = {};
+ result = array_in[i];
+ if(result) {
+ // Cycle through simpleLocators
+ for (j=simplePaths.length-1; j>=0; --j) {
+ // Bug 1777850: The result might be an array instead of object
+ record[simplePaths[j].key] = Y.DataSchema.Base.parse(
+ (LANG.isUndefined(result[simplePaths[j].path]) ?
+ result[j] : result[simplePaths[j].path]), simplePaths[j]);
+ }
+
+ // Cycle through complexLocators
+ for (j=complexPaths.length - 1; j>=0; --j) {
+ record[complexPaths[j].key] = Y.DataSchema.Base.parse(
+ (SchemaJSON.getLocationValue(complexPaths[j].path, result)), complexPaths[j] );
+ }
+
+ // Cycle through fieldParsers
+ for (j=fieldParsers.length-1; j>=0; --j) {
+ key = fieldParsers[j].key;
+ record[key] = fieldParsers[j].parser(record[key]);
+ // Safety net
+ if (LANG.isUndefined(record[key])) {
+ record[key] = null;
+ }
+ }
+ results[i] = record;
+ }
+ }
+ data_out.results = results;
+ return data_out;
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param metaFields {Object} Metafields definitions.
+ * @param json_in {Object} JSON to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Schema-parsed meta data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, json_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key, path;
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ path = SchemaJSON.getPath(metaFields[key]);
+ if (path && json_in) {
+ data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
+ }
+ }
+ }
+ }
+ else {
+ data_out.error = new Error("JSON meta data retrieval failure");
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['json', 'dataschema-base']});
+
+YUI.add('dataschema-xml', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with XML data.
+ *
+ * @module dataschema
+ * @submodule dataschema-xml
+ */
+var LANG = Y.Lang,
+
+ /**
+ * XML subclass for the DataSchema Utility.
+ * @class DataSchema.XML
+ * @extends DataSchema.Base
+ * @static
+ */
+ SchemaXML = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.XML static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given XML data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {XMLDoc} XML document.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var xmldoc = data,
+ data_out = {results:[],meta:{}};
+
+ if(xmldoc && xmldoc.nodeType && (xmldoc.nodeType === 9 || xmldoc.nodeType === 1 || xmldoc.nodeType === 11) && schema) {
+ // Parse results data
+ data_out = SchemaXML._parseResults(schema, xmldoc, data_out);
+
+ // Parse meta data
+ data_out = SchemaXML._parseMeta(schema.metaFields, xmldoc, data_out);
+ }
+ else {
+ data_out.error = new Error("XML schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Get an XPath-specified value for a given field from an XML node or document.
+ *
+ * @method _getLocationValue
+ * @param field {String | Object} Field definition.
+ * @param context {Object} XML node or document to search within.
+ * @return {Object} Data value or null.
+ * @static
+ * @protected
+ */
+ _getLocationValue: function(field, context) {
+ var locator = field.locator || field.key || field,
+ xmldoc = context.ownerDocument || context,
+ result, res, value = null;
+
+ try {
+ // Standards mode
+ if(!LANG.isUndefined(xmldoc.evaluate)) {
+ result = xmldoc.evaluate(locator, context, xmldoc.createNSResolver(!context.ownerDocument ? context.documentElement : context.ownerDocument.documentElement), 0, null);
+ while(res = result.iterateNext()) {
+ value = res.textContent;
+ }
+ }
+ // IE mode
+ else {
+ xmldoc.setProperty("SelectionLanguage", "XPath");
+ result = context.selectNodes(locator)[0];
+ value = result.value || result.text || null;
+ }
+ return Y.DataSchema.Base.parse(value, field);
+
+ }
+ catch(e) {
+ }
+ },
+
+ /**
+ * Parses results data according to schema
+ *
+ * @method _parseMeta
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseMeta: function(metaFields, xmldoc_in, data_out) {
+ if(LANG.isObject(metaFields)) {
+ var key,
+ xmldoc = xmldoc_in.ownerDocument || xmldoc_in;
+
+ for(key in metaFields) {
+ if (metaFields.hasOwnProperty(key)) {
+ data_out.meta[key] = SchemaXML._getLocationValue(metaFields[key], xmldoc);
+ }
+ }
+ }
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Object} Schema to parse against.
+ * @param xmldoc_in {Object} XML document parse.
+ * @param data_out {Object} In-progress schema-parsed data to update.
+ * @return {Object} Schema-parsed data.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, xmldoc_in, data_out) {
+ if(schema.resultListLocator && LANG.isArray(schema.resultFields)) {
+ var nodeList = xmldoc_in.getElementsByTagName(schema.resultListLocator),
+ fields = schema.resultFields,
+ results = [],
+ node, field, result, i, j;
+
+ if(nodeList.length) {
+ // Loop through each result node
+ for(i=nodeList.length-1; i>= 0; i--) {
+ result = {};
+ node = nodeList[i];
+
+ // Find each field value
+ for(j=fields.length-1; j>= 0; j--) {
+ field = fields[j];
+ result[field.key || field] = SchemaXML._getLocationValue(field, node);
+ }
+ results[i] = result;
+ }
+
+ data_out.results = results;
+ }
+ else {
+ data_out.error = new Error("XML schema result nodes retrieval failure");
+ }
+ }
+ return data_out;
+ }
+ };
+
+Y.DataSchema.XML = Y.mix(SchemaXML, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
+
+YUI.add('dataschema-array', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with data stored in arrays.
+ *
+ * @module dataschema
+ * @submodule dataschema-array
+ */
+
+/**
+ * Array subclass for the DataSchema Utility.
+ * @class DataSchema.Array
+ * @extends DataSchema.Base
+ * @static
+ */
+var LANG = Y.Lang,
+
+ SchemaArray = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Array static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given Array data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Array data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isArray(data_in)) {
+ if(LANG.isArray(schema.resultFields)) {
+ // Parse results data
+ data_out = SchemaArray._parseResults(schema.resultFields, data_in, data_out);
+ }
+ else {
+ data_out.results = data_in;
+ }
+ }
+ else {
+ data_out.error = new Error("Array schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param fields {Array} Schema to parse against.
+ * @param array_in {Array} Array to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(fields, array_in, data_out) {
+ var results = [],
+ result, item, type, field, key, value, i, j;
+
+ for(i=array_in.length-1; i>-1; i--) {
+ result = {};
+ item = array_in[i];
+ type = (LANG.isObject(item) && !LANG.isFunction(item)) ? 2 : (LANG.isArray(item)) ? 1 : (LANG.isString(item)) ? 0 : -1;
+ if(type > 0) {
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(item[key])) ? item[key] : item[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+ else if(type === 0) {
+ result = item;
+ }
+ else {
+ //TODO: null or {}?
+ result = null;
+ }
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Array = Y.mix(SchemaArray, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
+
+YUI.add('dataschema-text', function(Y) {
+
+/**
+ * Provides a DataSchema implementation which can be used to work with delimited text data.
+ *
+ * @module dataschema
+ * @submodule dataschema-text
+ */
+
+/**
+ * Text subclass for the DataSchema Utility.
+ * @class DataSchema.Text
+ * @extends DataSchema.Base
+ * @static
+ */
+
+var LANG = Y.Lang,
+
+ SchemaText = {
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSchema.Text static methods
+ //
+ /////////////////////////////////////////////////////////////////////////////
+ /**
+ * Applies a given schema to given delimited text data.
+ *
+ * @method apply
+ * @param schema {Object} Schema to apply.
+ * @param data {Object} Text data.
+ * @return {Object} Schema-parsed data.
+ * @static
+ */
+ apply: function(schema, data) {
+ var data_in = data,
+ data_out = {results:[],meta:{}};
+
+ if(LANG.isString(data_in) && LANG.isString(schema.resultDelimiter)) {
+ // Parse results data
+ data_out = SchemaText._parseResults(schema, data_in, data_out);
+ }
+ else {
+ data_out.error = new Error("Text schema parse failure");
+ }
+
+ return data_out;
+ },
+
+ /**
+ * Schema-parsed list of results from full data
+ *
+ * @method _parseResults
+ * @param schema {Array} Schema to parse against.
+ * @param text_in {String} Text to parse.
+ * @param data_out {Object} In-progress parsed data to update.
+ * @return {Object} Parsed data object.
+ * @static
+ * @protected
+ */
+ _parseResults: function(schema, text_in, data_out) {
+ var resultDelim = schema.resultDelimiter,
+ results = [],
+ results_in, fields_in, result, item, fields, field, key, value, i, j,
+
+ // Delete final delimiter at end of string if there
+ tmpLength = text_in.length-resultDelim.length;
+ if(text_in.substr(tmpLength) == resultDelim) {
+ text_in = text_in.substr(0, tmpLength);
+ }
+
+ // Split into results
+ results_in = text_in.split(schema.resultDelimiter);
+
+ for(i=results_in.length-1; i>-1; i--) {
+ result = {};
+ item = results_in[i];
+
+ if(LANG.isString(schema.fieldDelimiter)) {
+ fields_in = item.split(schema.fieldDelimiter);
+
+ if(LANG.isArray(schema.resultFields)) {
+ fields = schema.resultFields;
+ for(j=fields.length-1; j>-1; j--) {
+ field = fields[j];
+ key = (!LANG.isUndefined(field.key)) ? field.key : field;
+ value = (!LANG.isUndefined(fields_in[key])) ? fields_in[key] : fields_in[j];
+ result[key] = Y.DataSchema.Base.parse(value, field);
+ }
+ }
+
+ }
+ else {
+ result = item;
+ }
+
+ results[i] = result;
+ }
+ data_out.results = results;
+
+ return data_out;
+ }
+ };
+
+Y.DataSchema.Text = Y.mix(SchemaText, Y.DataSchema.Base);
+
+
+
+}, '3.0.0' ,{requires:['dataschema-base']});
+
+
+
+YUI.add('dataschema', function(Y){}, '3.0.0' ,{use:['dataschema-base','dataschema-json','dataschema-xml','dataschema-array','dataschema-text']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-arrayschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on array data.
+ *
+ * @module datasource
+ * @submodule datasource-arrayschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceArraySchema
+ * @extends Plugin.Base
+ */
+var DataSourceArraySchema = function() {
+ DataSourceArraySchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceArraySchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceArraySchema"
+ */
+ NAME: "dataSourceArraySchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceArraySchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceArraySchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Array.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceArraySchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceArraySchema = DataSourceArraySchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-array']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-arrayschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"schema",NAME:"dataSourceArraySchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&B.Lang.isString(E.data.responseText))?E.data.responseText:E.data,C=B.DataSchema.Array.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceArraySchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceArraySchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-array"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-arrayschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on array data.
+ *
+ * @module datasource
+ * @submodule datasource-arrayschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceArraySchema
+ * @extends Plugin.Base
+ */
+var DataSourceArraySchema = function() {
+ DataSourceArraySchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceArraySchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceArraySchema"
+ */
+ NAME: "dataSourceArraySchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceArraySchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceArraySchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Array.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceArraySchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceArraySchema = DataSourceArraySchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-array']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-cache', function(Y) {
+
+/**
+ * Extends DataSource with caching functionality.
+ *
+ * @module datasource
+ * @submodule datasource-cache
+ */
+
+/**
+ * Adds cacheability to the DataSource Utility.
+ * @class DataSourceCache
+ * @extends Cache
+ */
+var DataSourceCache = function() {
+ DataSourceCache.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceCache, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NS: "cache",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceCache"
+ */
+ NAME: "dataSourceCache",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceCache Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+
+ }
+});
+
+Y.extend(DataSourceCache, Y.Cache, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defRequestFn", this._beforeDefRequestFn);
+ this.doBefore("_defResponseFn", this._beforeDefResponseFn);
+ },
+
+ /**
+ * First look for cached response, then send request to live data.
+ *
+ * @method _beforeDefRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefRequestFn: function(e) {
+ // Is response already in the Cache?
+ var entry = (this.retrieve(e.request)) || null;
+ if(entry && entry.response) {
+ this.get("host").fire("response", Y.mix({response: entry.response}, e));
+ return new Y.Do.Halt("DataSourceCache plugin halted _defRequestFn");
+ }
+ },
+
+ /**
+ * Adds data to cache before returning data.
+ *
+ * @method _beforeDefResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>cached (Object)</dt> <dd>True when response is cached.</dd>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefResponseFn: function(e) {
+ // Add to Cache before returning
+ if(e.response && !e.response.cached) {
+ e.response.cached = true;
+ this.add(e.request, e.response, (e.callback && e.callback.argument));
+ }
+ }
+});
+
+Y.namespace('Plugin').DataSourceCache = DataSourceCache;
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'cache']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-cache",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"cache",NAME:"dataSourceCache",ATTRS:{}});B.extend(A,B.Cache,{initializer:function(C){this.doBefore("_defRequestFn",this._beforeDefRequestFn);this.doBefore("_defResponseFn",this._beforeDefResponseFn);},_beforeDefRequestFn:function(D){var C=(this.retrieve(D.request))||null;if(C&&C.response){this.get("host").fire("response",B.mix({response:C.response},D));return new B.Do.Halt("DataSourceCache plugin halted _defRequestFn");}},_beforeDefResponseFn:function(C){if(C.response&&!C.response.cached){C.response.cached=true;this.add(C.request,C.response,(C.callback&&C.callback.argument));}}});B.namespace("Plugin").DataSourceCache=A;},"3.0.0",{requires:["datasource-local","cache"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-cache', function(Y) {
+
+/**
+ * Extends DataSource with caching functionality.
+ *
+ * @module datasource
+ * @submodule datasource-cache
+ */
+
+/**
+ * Adds cacheability to the DataSource Utility.
+ * @class DataSourceCache
+ * @extends Cache
+ */
+var DataSourceCache = function() {
+ DataSourceCache.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceCache, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NS: "cache",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceCache"
+ */
+ NAME: "dataSourceCache",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceCache Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+
+ }
+});
+
+Y.extend(DataSourceCache, Y.Cache, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defRequestFn", this._beforeDefRequestFn);
+ this.doBefore("_defResponseFn", this._beforeDefResponseFn);
+ },
+
+ /**
+ * First look for cached response, then send request to live data.
+ *
+ * @method _beforeDefRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefRequestFn: function(e) {
+ // Is response already in the Cache?
+ var entry = (this.retrieve(e.request)) || null;
+ if(entry && entry.response) {
+ this.get("host").fire("response", Y.mix({response: entry.response}, e));
+ return new Y.Do.Halt("DataSourceCache plugin halted _defRequestFn");
+ }
+ },
+
+ /**
+ * Adds data to cache before returning data.
+ *
+ * @method _beforeDefResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>cached (Object)</dt> <dd>True when response is cached.</dd>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefResponseFn: function(e) {
+ // Add to Cache before returning
+ if(e.response && !e.response.cached) {
+ e.response.cached = true;
+ this.add(e.request, e.response, (e.callback && e.callback.argument));
+ }
+ }
+});
+
+Y.namespace('Plugin').DataSourceCache = DataSourceCache;
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'cache']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-local', function(Y) {
+
+/**
+ * The DataSource utility provides a common configurable interface for widgets to
+ * access a variety of data, from JavaScript arrays to online database servers.
+ *
+ * @module datasource
+ */
+
+/**
+ * Provides the base DataSource implementation, which can be extended to
+ * create DataSources for specific data protocols, such as the IO Utility, the
+ * Get Utility, or custom functions.
+ *
+ * @module datasource
+ * @submodule datasource-local
+ */
+
+/**
+ * Base class for the DataSource Utility.
+ * @class DataSource.Local
+ * @extends Base
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+DSLocal = function() {
+ DSLocal.superclass.constructor.apply(this, arguments);
+};
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSLocal, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceLocal"
+ */
+ NAME: "dataSourceLocal",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ value: null
+ }
+ },
+
+ /**
+ * Global transaction counter.
+ *
+ * @property DataSource._tId
+ * @type Number
+ * @static
+ * @private
+ * @default 0
+ */
+ _tId: 0,
+
+ /**
+ * Executes a given callback. The third param determines whether to execute
+ *
+ * @method DataSource.issueCallback
+ * @param callback {Object} The callback object.
+ * @param params {Array} params to be passed to the callback method
+ * @param error {Boolean} whether an error occurred
+ * @static
+ */
+ issueCallback: function (e) {
+ if(e.callback) {
+ var callbackFunc = (e.error && e.callback.failure) || e.callback.success;
+ if (callbackFunc) {
+ callbackFunc(e);
+ }
+ }
+ }
+});
+
+Y.extend(DSLocal, Y.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._initEvents();
+ },
+
+ /**
+ * This method creates all the events for this module.
+ * @method _initEvents
+ * @private
+ */
+ _initEvents: function() {
+ /**
+ * Fired when a data request is received.
+ *
+ * @event request
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @preventable _defRequestFn
+ */
+ this.publish("request", {defaultFn: Y.bind("_defRequestFn", this), queuable:true});
+
+ /**
+ * Fired when raw data is received.
+ *
+ * @event data
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @preventable _defDataFn
+ */
+ this.publish("data", {defaultFn: Y.bind("_defDataFn", this), queuable:true});
+
+ /**
+ * Fired when response is returned.
+ *
+ * @event response
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @preventable _defResponseFn
+ */
+ this.publish("response", {defaultFn: Y.bind("_defResponseFn", this), queuable:true});
+
+ /**
+ * Fired when an error is encountered.
+ *
+ * @event error
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ */
+
+ },
+
+ /**
+ * Manages request/response transaction. Must fire <code>response</code>
+ * event when response is received. This method should be implemented by
+ * subclasses to achieve more complex behavior such as accessing remote data.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facadewith the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var data = this.get("source");
+
+ // Problematic data
+ if(LANG.isUndefined(data)) {
+ e.error = new Error("Local source undefined");
+ }
+ if(e.error) {
+ this.fire("error", e);
+ Y.log("Error in response", "error", "datasource-local");
+ }
+
+ this.fire("data", Y.mix({data:data}, e));
+ Y.log("Transaction " + e.tId + " complete. Request: " +
+ Y.dump(e.request) + " . Response: " + Y.dump(e.response), "info", "datasource-local");
+ },
+
+ /**
+ * Normalizes raw data into a response that includes results and meta properties.
+ *
+ * @method _defDataFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _defDataFn: function(e) {
+ var data = e.data,
+ meta = e.meta,
+ response = {
+ results: (LANG.isArray(data)) ? data : [data],
+ meta: (meta) ? meta : {}
+ };
+
+ this.fire("response", Y.mix({response: response}, e));
+ },
+
+ /**
+ * Sends data as a normalized response to callback.
+ *
+ * @method _defResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @protected
+ */
+ _defResponseFn: function(e) {
+ // Send the response back to the callback
+ DSLocal.issueCallback(e);
+ },
+
+ /**
+ * Generates a unique transaction ID and fires <code>request</code> event.
+ *
+ * @method sendRequest
+ * @param request {Object} Request.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data payload that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @param cfg {Object} Configuration object
+ * @return {Number} Transaction ID.
+ */
+ sendRequest: function(request, callback, cfg) {
+ var tId = DSLocal._tId++;
+ this.fire("request", {tId:tId, request:request, callback:callback, cfg:cfg || {}});
+ Y.log("Transaction " + tId + " sent request: " + Y.dump(request), "info", "datasource-local");
+ return tId;
+ }
+});
+
+Y.namespace("DataSource").Local = DSLocal;
+
+
+
+}, '3.0.0' ,{requires:['base']});
+
+YUI.add('datasource-io', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the IO Utility.
+ *
+ * @module datasource
+ * @submodule datasource-io
+ */
+
+/**
+ * IO subclass for the DataSource Utility.
+ * @class DataSource.IO
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSIO = function() {
+ DSIO.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSIO, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceIO"
+ */
+ NAME: "dataSourceIO",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to IO Utility.
+ *
+ * @attribute io
+ * @type Y.io
+ * @default Y.io
+ */
+ io: {
+ value: Y.io,
+ cloneDefaultValue: false
+ }
+ }
+});
+
+Y.extend(DSIO, Y.DataSource.Local, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._queue = {interval:null, conn:null, requests:[]};
+ },
+
+ /**
+ * @property _queue
+ * @description Object literal to manage asynchronous request/response
+ * cycles enabled if queue needs to be managed (asyncMode/ioConnMode):
+ * <dl>
+ * <dt>interval {Number}</dt>
+ * <dd>Interval ID of in-progress queue.</dd>
+ * <dt>conn</dt>
+ * <dd>In-progress connection identifier (if applicable).</dd>
+ * <dt>requests {Object[]}</dt>
+ * <dd>Array of queued request objects: {request:request, callback:callback}.</dd>
+ * </dl>
+ * @type Object
+ * @default {interval:null, conn:null, requests:[]}
+ * @private
+ */
+ _queue: null,
+
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ io = this.get("io"),
+ request = e.request,
+ cfg = Y.mix(e.cfg, {
+ on: {
+ success: function (id, response, e) {
+ this.fire("data", Y.mix({data:response}, e));
+ Y.log("Received IO data response for \"" + request + "\"", "info", "datasource-io");
+ },
+ failure: function (id, response, e) {
+ e.error = new Error("IO data failure");
+ this.fire("error", Y.mix({data:response}, e));
+ this.fire("data", Y.mix({data:response}, e));
+ Y.log("Received IO data failure for \"" + request + "\"", "info", "datasource-io");
+ }
+ },
+ context: this,
+ arguments: e
+ });
+
+ // Support for POST transactions
+ if(Y.Lang.isString(request)) {
+ if(cfg.method && (cfg.method.toUpperCase() === "POST")) {
+ cfg.data = cfg.data ? cfg.data+request : request;
+ }
+ else {
+ uri += request;
+ }
+ }
+ io(uri, cfg);
+ return e.tId;
+ }
+});
+
+Y.DataSource.IO = DSIO;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'io']});
+
+YUI.add('datasource-get', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the Get Utility.
+ *
+ * @module datasource
+ * @submodule datasource-get
+ */
+
+/**
+ * Get Utility subclass for the DataSource Utility.
+ * @class DataSource.Get
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSGet = function() {
+ DSGet.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSGet, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceGet"
+ */
+ NAME: "dataSourceGet",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to Get Utility.
+ *
+ * @attribute get
+ * @type Y.Get
+ * @default Y.Get
+ */
+ get: {
+ value: Y.Get,
+ cloneDefaultValue: false
+ },
+
+/**
+ * Defines request/response management in the following manner:
+ * <dl>
+ * <!--<dt>queueRequests</dt>
+ * <dd>If a request is already in progress, wait until response is returned before sending the next request.</dd>
+ * <dt>cancelStaleRequests</dt>
+ * <dd>If a request is already in progress, cancel it before sending the next request.</dd>-->
+ * <dt>ignoreStaleResponses</dt>
+ * <dd>Send all requests, but handle only the response for the most recently sent request.</dd>
+ * <dt>allowAll</dt>
+ * <dd>Send all requests and handle all responses.</dd>
+ * </dl>
+ *
+ * @attribute asyncMode
+ * @type String
+ * @default "allowAll"
+ */
+asyncMode: {
+ value: "allowAll"
+},
+
+/**
+ * Callback string parameter name sent to the remote script. By default,
+ * requests are sent to
+ * <URI>?<scriptCallbackParam>=callbackFunction
+ *
+ * @attribute scriptCallbackParam
+ * @type String
+ * @default "callback"
+ */
+scriptCallbackParam : {
+ value: "callback"
+},
+
+/**
+ * Accepts the DataSource instance and a callback ID, and returns a callback
+ * param/value string that gets appended to the script URI. Implementers
+ * can customize this string to match their server's query syntax.
+ *
+ * @attribute generateRequestCallback
+ * @type Function
+ */
+generateRequestCallback : {
+ value: function(self, id) {
+ return "&" + self.get("scriptCallbackParam") + "=YUI.Env.DataSource.callbacks["+id+"]" ;
+ }
+}
+
+
+
+
+
+ },
+
+ /**
+ * Global array of callback functions, one for each request sent.
+ *
+ * @property callbacks
+ * @type Function[]
+ * @static
+ */
+ callbacks : [],
+
+ /**
+ * Unique ID to track requests.
+ *
+ * @property _tId
+ * @type Number
+ * @private
+ * @static
+ */
+ _tId : 0
+});
+
+Y.extend(DSGet, Y.DataSource.Local, {
+ /**
+ * Passes query string to Get Utility. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ get = this.get("get"),
+ id = DSGet._tId++,
+ self = this;
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // Dynamically add handler function with a closure to the callback stack
+ YUI.Env.DataSource.callbacks[id] = Y.rbind(function(response) {
+ if((self.get("asyncMode") !== "ignoreStaleResponses")||
+ (id === DSGet.callbacks.length-1)) { // Must ignore stale responses
+
+ self.fire("data", Y.mix({data:response}, e));
+ }
+ else {
+ Y.log("DataSource ignored stale response for id " + e.tId + "(" + e.request + ")", "info", "datasource-get");
+ }
+
+ delete DSGet.callbacks[id];
+ }, this, id);
+
+ // We are now creating a request
+ uri += e.request + this.get("generateRequestCallback")(this, id);
+ //uri = this.doBefore(sUri);
+ Y.log("DataSource is querying URL " + uri, "info", "datasource-get");
+ get.script(uri, {
+ autopurge: true,
+ // Works in Firefox only....
+ onFailure: Y.bind(function(e) {
+ e.error = new Error("Script node data failure");
+ this.fire("error", e);
+ }, this, e)
+ });
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Get = DSGet;
+YUI.namespace("Env.DataSource.callbacks");
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'get']});
+
+YUI.add('datasource-function', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data from a custom function.
+ *
+ * @module datasource
+ * @submodule datasource-function
+ */
+
+/**
+ * Function subclass for the DataSource Utility.
+ * @class DataSource.Function
+ * @extends DataSource.Local
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+ DSFn = function() {
+ DSFn.superclass.constructor.apply(this, arguments);
+ };
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSFn, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceFunction"
+ */
+ NAME: "dataSourceFunction",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ validator: LANG.isFunction
+ }
+ }
+});
+
+Y.extend(DSFn, Y.DataSource.Local, {
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var fn = this.get("source"),
+ response;
+
+ if(fn) {
+ try {
+ response = fn(e.request, this, e);
+ this.fire("data", Y.mix({data:response}, e));
+ }
+ catch(error) {
+ e.error = error;
+ this.fire("error", e);
+ }
+ }
+ else {
+ e.error = new Error("Function data failure");
+ this.fire("error", e);
+ }
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Function = DSFn;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
+
+YUI.add('datasource-cache', function(Y) {
+
+/**
+ * Extends DataSource with caching functionality.
+ *
+ * @module datasource
+ * @submodule datasource-cache
+ */
+
+/**
+ * Adds cacheability to the DataSource Utility.
+ * @class DataSourceCache
+ * @extends Cache
+ */
+var DataSourceCache = function() {
+ DataSourceCache.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceCache, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NS: "cache",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceCache"
+ */
+ NAME: "dataSourceCache",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceCache Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+
+ }
+});
+
+Y.extend(DataSourceCache, Y.Cache, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defRequestFn", this._beforeDefRequestFn);
+ this.doBefore("_defResponseFn", this._beforeDefResponseFn);
+ },
+
+ /**
+ * First look for cached response, then send request to live data.
+ *
+ * @method _beforeDefRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefRequestFn: function(e) {
+ // Is response already in the Cache?
+ var entry = (this.retrieve(e.request)) || null;
+ if(entry && entry.response) {
+ this.get("host").fire("response", Y.mix({response: entry.response}, e));
+ return new Y.Do.Halt("DataSourceCache plugin halted _defRequestFn");
+ }
+ },
+
+ /**
+ * Adds data to cache before returning data.
+ *
+ * @method _beforeDefResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>cached (Object)</dt> <dd>True when response is cached.</dd>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefResponseFn: function(e) {
+ // Add to Cache before returning
+ if(e.response && !e.response.cached) {
+ e.response.cached = true;
+ this.add(e.request, e.response, (e.callback && e.callback.argument));
+ }
+ }
+});
+
+Y.namespace('Plugin').DataSourceCache = DataSourceCache;
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'cache']});
+
+YUI.add('datasource-jsonschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on JSON data.
+ *
+ * @module datasource
+ * @submodule datasource-jsonschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceJSONSchema
+ * @extends Plugin.Base
+ */
+var DataSourceJSONSchema = function() {
+ DataSourceJSONSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceJSONSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceJSONSchema"
+ */
+ NAME: "dataSourceJSONSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceJSONSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceJSONSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.JSON.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceJSONSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceJSONSchema = DataSourceJSONSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-json']});
+
+YUI.add('datasource-xmlschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on XML data.
+ *
+ * @module datasource
+ * @submodule datasource-xmlschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceXMLSchema
+ * @extends Plugin.Base
+ */
+var DataSourceXMLSchema = function() {
+ DataSourceXMLSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceXMLSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceXMLSchema"
+ */
+ NAME: "dataSourceXMLSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceXMLSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceXMLSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && e.data.responseXML && (e.data.responseXML.nodeType === 9)) ? e.data.responseXML : e.data,
+ response = Y.DataSchema.XML.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceXMLSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceXMLSchema = DataSourceXMLSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-xml']});
+
+YUI.add('datasource-arrayschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on array data.
+ *
+ * @module datasource
+ * @submodule datasource-arrayschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceArraySchema
+ * @extends Plugin.Base
+ */
+var DataSourceArraySchema = function() {
+ DataSourceArraySchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceArraySchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceArraySchema"
+ */
+ NAME: "dataSourceArraySchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceArraySchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceArraySchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Array.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceArraySchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceArraySchema = DataSourceArraySchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-array']});
+
+YUI.add('datasource-textschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on text data.
+ *
+ * @module datasource
+ * @submodule datasource-textschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceTextSchema
+ * @extends Plugin.Base
+ */
+var DataSourceTextSchema = function() {
+ DataSourceTextSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceTextSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceTextSchema"
+ */
+ NAME: "dataSourceTextSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceTextSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceTextSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Text.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceTextSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceTextSchema = DataSourceTextSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-text']});
+
+YUI.add('datasource-polling', function(Y) {
+
+/**
+ * Extends DataSource with polling functionality.
+ *
+ * @module datasource
+ * @submodule datasource-polling
+ */
+
+/**
+ * Adds polling to the DataSource Utility.
+ * @class Pollable
+ * @extends DataSource.Local
+ */
+var LANG = Y.Lang,
+
+ Pollable = function() {
+ this._intervals = {};
+ };
+
+Pollable.prototype = {
+
+ /**
+ * @property _intervals
+ * @description Hash of polling interval IDs that have been enabled,
+ * stored here to be able to clear all intervals.
+ * @private
+ */
+ _intervals: null,
+
+ /**
+ * Sets up a polling mechanism to send requests at set intervals and forward
+ * responses to given callback.
+ *
+ * @method setInterval
+ * @param msec {Number} Length of interval in milliseconds.
+ * @param request {Object} Request object.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @return {Number} Interval ID.
+ */
+ setInterval: function(msec, request, callback) {
+ var x = Y.later(msec, this, this.sendRequest, [request, callback], true);
+ this._intervals[x.id] = x;
+ return x.id;
+ },
+
+ /**
+ * Disables polling mechanism associated with the given interval ID.
+ *
+ * @method clearInterval
+ * @param id {Number} Interval ID.
+ */
+ clearInterval: function(id, key) {
+ // In case of being called by clearAllIntervals()
+ id = key || id;
+ if(this._intervals[id]) {
+ // Clear the interval
+ this._intervals[id].cancel();
+ // Clear from tracker
+ delete this._intervals[id];
+ }
+ },
+
+ /**
+ * Clears all intervals.
+ *
+ * @method clearAllIntervals
+ */
+ clearAllIntervals: function() {
+ Y.each(this._intervals, this.clearInterval, this);
+ }
+};
+
+Y.augment(Y.DataSource.Local, Pollable);
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
+
+
+
+YUI.add('datasource', function(Y){}, '3.0.0' ,{use:['datasource-local','datasource-io','datasource-get','datasource-function','datasource-cache','datasource-jsonschema','datasource-xmlschema','datasource-arrayschema','datasource-textschema','datasource-polling']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-function', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data from a custom function.
+ *
+ * @module datasource
+ * @submodule datasource-function
+ */
+
+/**
+ * Function subclass for the DataSource Utility.
+ * @class DataSource.Function
+ * @extends DataSource.Local
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+ DSFn = function() {
+ DSFn.superclass.constructor.apply(this, arguments);
+ };
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSFn, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceFunction"
+ */
+ NAME: "dataSourceFunction",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ validator: LANG.isFunction
+ }
+ }
+});
+
+Y.extend(DSFn, Y.DataSource.Local, {
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var fn = this.get("source"),
+ response;
+
+ if(fn) {
+ try {
+ response = fn(e.request, this, e);
+ this.fire("data", Y.mix({data:response}, e));
+ }
+ catch(error) {
+ e.error = error;
+ this.fire("error", e);
+ }
+ }
+ else {
+ e.error = new Error("Function data failure");
+ this.fire("error", e);
+ }
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Function = DSFn;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-function",function(B){var A=B.Lang,C=function(){C.superclass.constructor.apply(this,arguments);};B.mix(C,{NAME:"dataSourceFunction",ATTRS:{source:{validator:A.isFunction}}});B.extend(C,B.DataSource.Local,{_defRequestFn:function(G){var F=this.get("source"),D;if(F){try{D=F(G.request,this,G);this.fire("data",B.mix({data:D},G));}catch(E){G.error=E;this.fire("error",G);}}else{G.error=new Error("Function data failure");this.fire("error",G);}return G.tId;}});B.DataSource.Function=C;},"3.0.0",{requires:["datasource-local"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-function', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data from a custom function.
+ *
+ * @module datasource
+ * @submodule datasource-function
+ */
+
+/**
+ * Function subclass for the DataSource Utility.
+ * @class DataSource.Function
+ * @extends DataSource.Local
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+ DSFn = function() {
+ DSFn.superclass.constructor.apply(this, arguments);
+ };
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSFn, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceFunction"
+ */
+ NAME: "dataSourceFunction",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ validator: LANG.isFunction
+ }
+ }
+});
+
+Y.extend(DSFn, Y.DataSource.Local, {
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var fn = this.get("source"),
+ response;
+
+ if(fn) {
+ try {
+ response = fn(e.request, this, e);
+ this.fire("data", Y.mix({data:response}, e));
+ }
+ catch(error) {
+ e.error = error;
+ this.fire("error", e);
+ }
+ }
+ else {
+ e.error = new Error("Function data failure");
+ this.fire("error", e);
+ }
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Function = DSFn;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-get', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the Get Utility.
+ *
+ * @module datasource
+ * @submodule datasource-get
+ */
+
+/**
+ * Get Utility subclass for the DataSource Utility.
+ * @class DataSource.Get
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSGet = function() {
+ DSGet.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSGet, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceGet"
+ */
+ NAME: "dataSourceGet",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to Get Utility.
+ *
+ * @attribute get
+ * @type Y.Get
+ * @default Y.Get
+ */
+ get: {
+ value: Y.Get,
+ cloneDefaultValue: false
+ },
+
+/**
+ * Defines request/response management in the following manner:
+ * <dl>
+ * <!--<dt>queueRequests</dt>
+ * <dd>If a request is already in progress, wait until response is returned before sending the next request.</dd>
+ * <dt>cancelStaleRequests</dt>
+ * <dd>If a request is already in progress, cancel it before sending the next request.</dd>-->
+ * <dt>ignoreStaleResponses</dt>
+ * <dd>Send all requests, but handle only the response for the most recently sent request.</dd>
+ * <dt>allowAll</dt>
+ * <dd>Send all requests and handle all responses.</dd>
+ * </dl>
+ *
+ * @attribute asyncMode
+ * @type String
+ * @default "allowAll"
+ */
+asyncMode: {
+ value: "allowAll"
+},
+
+/**
+ * Callback string parameter name sent to the remote script. By default,
+ * requests are sent to
+ * <URI>?<scriptCallbackParam>=callbackFunction
+ *
+ * @attribute scriptCallbackParam
+ * @type String
+ * @default "callback"
+ */
+scriptCallbackParam : {
+ value: "callback"
+},
+
+/**
+ * Accepts the DataSource instance and a callback ID, and returns a callback
+ * param/value string that gets appended to the script URI. Implementers
+ * can customize this string to match their server's query syntax.
+ *
+ * @attribute generateRequestCallback
+ * @type Function
+ */
+generateRequestCallback : {
+ value: function(self, id) {
+ return "&" + self.get("scriptCallbackParam") + "=YUI.Env.DataSource.callbacks["+id+"]" ;
+ }
+}
+
+
+
+
+
+ },
+
+ /**
+ * Global array of callback functions, one for each request sent.
+ *
+ * @property callbacks
+ * @type Function[]
+ * @static
+ */
+ callbacks : [],
+
+ /**
+ * Unique ID to track requests.
+ *
+ * @property _tId
+ * @type Number
+ * @private
+ * @static
+ */
+ _tId : 0
+});
+
+Y.extend(DSGet, Y.DataSource.Local, {
+ /**
+ * Passes query string to Get Utility. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ get = this.get("get"),
+ id = DSGet._tId++,
+ self = this;
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // Dynamically add handler function with a closure to the callback stack
+ YUI.Env.DataSource.callbacks[id] = Y.rbind(function(response) {
+ if((self.get("asyncMode") !== "ignoreStaleResponses")||
+ (id === DSGet.callbacks.length-1)) { // Must ignore stale responses
+
+ self.fire("data", Y.mix({data:response}, e));
+ }
+ else {
+ Y.log("DataSource ignored stale response for id " + e.tId + "(" + e.request + ")", "info", "datasource-get");
+ }
+
+ delete DSGet.callbacks[id];
+ }, this, id);
+
+ // We are now creating a request
+ uri += e.request + this.get("generateRequestCallback")(this, id);
+ //uri = this.doBefore(sUri);
+ Y.log("DataSource is querying URL " + uri, "info", "datasource-get");
+ get.script(uri, {
+ autopurge: true,
+ // Works in Firefox only....
+ onFailure: Y.bind(function(e) {
+ e.error = new Error("Script node data failure");
+ this.fire("error", e);
+ }, this, e)
+ });
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Get = DSGet;
+YUI.namespace("Env.DataSource.callbacks");
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'get']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-get",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NAME:"dataSourceGet",ATTRS:{get:{value:B.Get,cloneDefaultValue:false},asyncMode:{value:"allowAll"},scriptCallbackParam:{value:"callback"},generateRequestCallback:{value:function(C,D){return"&"+C.get("scriptCallbackParam")+"=YUI.Env.DataSource.callbacks["+D+"]";}}},callbacks:[],_tId:0});B.extend(A,B.DataSource.Local,{_defRequestFn:function(F){var E=this.get("source"),D=this.get("get"),G=A._tId++,C=this;YUI.Env.DataSource.callbacks[G]=B.rbind(function(H){if((C.get("asyncMode")!=="ignoreStaleResponses")||(G===A.callbacks.length-1)){C.fire("data",B.mix({data:H},F));}else{}delete A.callbacks[G];},this,G);E+=F.request+this.get("generateRequestCallback")(this,G);D.script(E,{autopurge:true,onFailure:B.bind(function(H){H.error=new Error("Script node data failure");this.fire("error",H);},this,F)});return F.tId;}});B.DataSource.Get=A;YUI.namespace("Env.DataSource.callbacks");},"3.0.0",{requires:["datasource-local","get"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-get', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the Get Utility.
+ *
+ * @module datasource
+ * @submodule datasource-get
+ */
+
+/**
+ * Get Utility subclass for the DataSource Utility.
+ * @class DataSource.Get
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSGet = function() {
+ DSGet.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSGet, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceGet"
+ */
+ NAME: "dataSourceGet",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to Get Utility.
+ *
+ * @attribute get
+ * @type Y.Get
+ * @default Y.Get
+ */
+ get: {
+ value: Y.Get,
+ cloneDefaultValue: false
+ },
+
+/**
+ * Defines request/response management in the following manner:
+ * <dl>
+ * <!--<dt>queueRequests</dt>
+ * <dd>If a request is already in progress, wait until response is returned before sending the next request.</dd>
+ * <dt>cancelStaleRequests</dt>
+ * <dd>If a request is already in progress, cancel it before sending the next request.</dd>-->
+ * <dt>ignoreStaleResponses</dt>
+ * <dd>Send all requests, but handle only the response for the most recently sent request.</dd>
+ * <dt>allowAll</dt>
+ * <dd>Send all requests and handle all responses.</dd>
+ * </dl>
+ *
+ * @attribute asyncMode
+ * @type String
+ * @default "allowAll"
+ */
+asyncMode: {
+ value: "allowAll"
+},
+
+/**
+ * Callback string parameter name sent to the remote script. By default,
+ * requests are sent to
+ * <URI>?<scriptCallbackParam>=callbackFunction
+ *
+ * @attribute scriptCallbackParam
+ * @type String
+ * @default "callback"
+ */
+scriptCallbackParam : {
+ value: "callback"
+},
+
+/**
+ * Accepts the DataSource instance and a callback ID, and returns a callback
+ * param/value string that gets appended to the script URI. Implementers
+ * can customize this string to match their server's query syntax.
+ *
+ * @attribute generateRequestCallback
+ * @type Function
+ */
+generateRequestCallback : {
+ value: function(self, id) {
+ return "&" + self.get("scriptCallbackParam") + "=YUI.Env.DataSource.callbacks["+id+"]" ;
+ }
+}
+
+
+
+
+
+ },
+
+ /**
+ * Global array of callback functions, one for each request sent.
+ *
+ * @property callbacks
+ * @type Function[]
+ * @static
+ */
+ callbacks : [],
+
+ /**
+ * Unique ID to track requests.
+ *
+ * @property _tId
+ * @type Number
+ * @private
+ * @static
+ */
+ _tId : 0
+});
+
+Y.extend(DSGet, Y.DataSource.Local, {
+ /**
+ * Passes query string to Get Utility. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ get = this.get("get"),
+ id = DSGet._tId++,
+ self = this;
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // Dynamically add handler function with a closure to the callback stack
+ YUI.Env.DataSource.callbacks[id] = Y.rbind(function(response) {
+ if((self.get("asyncMode") !== "ignoreStaleResponses")||
+ (id === DSGet.callbacks.length-1)) { // Must ignore stale responses
+
+ self.fire("data", Y.mix({data:response}, e));
+ }
+ else {
+ }
+
+ delete DSGet.callbacks[id];
+ }, this, id);
+
+ // We are now creating a request
+ uri += e.request + this.get("generateRequestCallback")(this, id);
+ //uri = this.doBefore(sUri);
+ get.script(uri, {
+ autopurge: true,
+ // Works in Firefox only....
+ onFailure: Y.bind(function(e) {
+ e.error = new Error("Script node data failure");
+ this.fire("error", e);
+ }, this, e)
+ });
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Get = DSGet;
+YUI.namespace("Env.DataSource.callbacks");
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'get']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-io', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the IO Utility.
+ *
+ * @module datasource
+ * @submodule datasource-io
+ */
+
+/**
+ * IO subclass for the DataSource Utility.
+ * @class DataSource.IO
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSIO = function() {
+ DSIO.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSIO, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceIO"
+ */
+ NAME: "dataSourceIO",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to IO Utility.
+ *
+ * @attribute io
+ * @type Y.io
+ * @default Y.io
+ */
+ io: {
+ value: Y.io,
+ cloneDefaultValue: false
+ }
+ }
+});
+
+Y.extend(DSIO, Y.DataSource.Local, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._queue = {interval:null, conn:null, requests:[]};
+ },
+
+ /**
+ * @property _queue
+ * @description Object literal to manage asynchronous request/response
+ * cycles enabled if queue needs to be managed (asyncMode/ioConnMode):
+ * <dl>
+ * <dt>interval {Number}</dt>
+ * <dd>Interval ID of in-progress queue.</dd>
+ * <dt>conn</dt>
+ * <dd>In-progress connection identifier (if applicable).</dd>
+ * <dt>requests {Object[]}</dt>
+ * <dd>Array of queued request objects: {request:request, callback:callback}.</dd>
+ * </dl>
+ * @type Object
+ * @default {interval:null, conn:null, requests:[]}
+ * @private
+ */
+ _queue: null,
+
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ io = this.get("io"),
+ request = e.request,
+ cfg = Y.mix(e.cfg, {
+ on: {
+ success: function (id, response, e) {
+ this.fire("data", Y.mix({data:response}, e));
+ Y.log("Received IO data response for \"" + request + "\"", "info", "datasource-io");
+ },
+ failure: function (id, response, e) {
+ e.error = new Error("IO data failure");
+ this.fire("error", Y.mix({data:response}, e));
+ this.fire("data", Y.mix({data:response}, e));
+ Y.log("Received IO data failure for \"" + request + "\"", "info", "datasource-io");
+ }
+ },
+ context: this,
+ arguments: e
+ });
+
+ // Support for POST transactions
+ if(Y.Lang.isString(request)) {
+ if(cfg.method && (cfg.method.toUpperCase() === "POST")) {
+ cfg.data = cfg.data ? cfg.data+request : request;
+ }
+ else {
+ uri += request;
+ }
+ }
+ io(uri, cfg);
+ return e.tId;
+ }
+});
+
+Y.DataSource.IO = DSIO;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'io']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-io",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NAME:"dataSourceIO",ATTRS:{io:{value:B.io,cloneDefaultValue:false}}});B.extend(A,B.DataSource.Local,{initializer:function(C){this._queue={interval:null,conn:null,requests:[]};},_queue:null,_defRequestFn:function(F){var E=this.get("source"),G=this.get("io"),D=F.request,C=B.mix(F.cfg,{on:{success:function(J,H,I){this.fire("data",B.mix({data:H},I));},failure:function(J,H,I){I.error=new Error("IO data failure");this.fire("error",B.mix({data:H},I));this.fire("data",B.mix({data:H},I));}},context:this,arguments:F});if(B.Lang.isString(D)){if(C.method&&(C.method.toUpperCase()==="POST")){C.data=C.data?C.data+D:D;}else{E+=D;}}G(E,C);return F.tId;}});B.DataSource.IO=A;},"3.0.0",{requires:["datasource-local","io"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-io', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the IO Utility.
+ *
+ * @module datasource
+ * @submodule datasource-io
+ */
+
+/**
+ * IO subclass for the DataSource Utility.
+ * @class DataSource.IO
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSIO = function() {
+ DSIO.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSIO, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceIO"
+ */
+ NAME: "dataSourceIO",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to IO Utility.
+ *
+ * @attribute io
+ * @type Y.io
+ * @default Y.io
+ */
+ io: {
+ value: Y.io,
+ cloneDefaultValue: false
+ }
+ }
+});
+
+Y.extend(DSIO, Y.DataSource.Local, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._queue = {interval:null, conn:null, requests:[]};
+ },
+
+ /**
+ * @property _queue
+ * @description Object literal to manage asynchronous request/response
+ * cycles enabled if queue needs to be managed (asyncMode/ioConnMode):
+ * <dl>
+ * <dt>interval {Number}</dt>
+ * <dd>Interval ID of in-progress queue.</dd>
+ * <dt>conn</dt>
+ * <dd>In-progress connection identifier (if applicable).</dd>
+ * <dt>requests {Object[]}</dt>
+ * <dd>Array of queued request objects: {request:request, callback:callback}.</dd>
+ * </dl>
+ * @type Object
+ * @default {interval:null, conn:null, requests:[]}
+ * @private
+ */
+ _queue: null,
+
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ io = this.get("io"),
+ request = e.request,
+ cfg = Y.mix(e.cfg, {
+ on: {
+ success: function (id, response, e) {
+ this.fire("data", Y.mix({data:response}, e));
+ },
+ failure: function (id, response, e) {
+ e.error = new Error("IO data failure");
+ this.fire("error", Y.mix({data:response}, e));
+ this.fire("data", Y.mix({data:response}, e));
+ }
+ },
+ context: this,
+ arguments: e
+ });
+
+ // Support for POST transactions
+ if(Y.Lang.isString(request)) {
+ if(cfg.method && (cfg.method.toUpperCase() === "POST")) {
+ cfg.data = cfg.data ? cfg.data+request : request;
+ }
+ else {
+ uri += request;
+ }
+ }
+ io(uri, cfg);
+ return e.tId;
+ }
+});
+
+Y.DataSource.IO = DSIO;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'io']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-jsonschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on JSON data.
+ *
+ * @module datasource
+ * @submodule datasource-jsonschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceJSONSchema
+ * @extends Plugin.Base
+ */
+var DataSourceJSONSchema = function() {
+ DataSourceJSONSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceJSONSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceJSONSchema"
+ */
+ NAME: "dataSourceJSONSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceJSONSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceJSONSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.JSON.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceJSONSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceJSONSchema = DataSourceJSONSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-json']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-jsonschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"schema",NAME:"dataSourceJSONSchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&B.Lang.isString(E.data.responseText))?E.data.responseText:E.data,C=B.DataSchema.JSON.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceJSONSchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceJSONSchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-json"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-jsonschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on JSON data.
+ *
+ * @module datasource
+ * @submodule datasource-jsonschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceJSONSchema
+ * @extends Plugin.Base
+ */
+var DataSourceJSONSchema = function() {
+ DataSourceJSONSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceJSONSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceJSONSchema"
+ */
+ NAME: "dataSourceJSONSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceJSONSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceJSONSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.JSON.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceJSONSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceJSONSchema = DataSourceJSONSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-json']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-local', function(Y) {
+
+/**
+ * The DataSource utility provides a common configurable interface for widgets to
+ * access a variety of data, from JavaScript arrays to online database servers.
+ *
+ * @module datasource
+ */
+
+/**
+ * Provides the base DataSource implementation, which can be extended to
+ * create DataSources for specific data protocols, such as the IO Utility, the
+ * Get Utility, or custom functions.
+ *
+ * @module datasource
+ * @submodule datasource-local
+ */
+
+/**
+ * Base class for the DataSource Utility.
+ * @class DataSource.Local
+ * @extends Base
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+DSLocal = function() {
+ DSLocal.superclass.constructor.apply(this, arguments);
+};
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSLocal, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceLocal"
+ */
+ NAME: "dataSourceLocal",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ value: null
+ }
+ },
+
+ /**
+ * Global transaction counter.
+ *
+ * @property DataSource._tId
+ * @type Number
+ * @static
+ * @private
+ * @default 0
+ */
+ _tId: 0,
+
+ /**
+ * Executes a given callback. The third param determines whether to execute
+ *
+ * @method DataSource.issueCallback
+ * @param callback {Object} The callback object.
+ * @param params {Array} params to be passed to the callback method
+ * @param error {Boolean} whether an error occurred
+ * @static
+ */
+ issueCallback: function (e) {
+ if(e.callback) {
+ var callbackFunc = (e.error && e.callback.failure) || e.callback.success;
+ if (callbackFunc) {
+ callbackFunc(e);
+ }
+ }
+ }
+});
+
+Y.extend(DSLocal, Y.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._initEvents();
+ },
+
+ /**
+ * This method creates all the events for this module.
+ * @method _initEvents
+ * @private
+ */
+ _initEvents: function() {
+ /**
+ * Fired when a data request is received.
+ *
+ * @event request
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @preventable _defRequestFn
+ */
+ this.publish("request", {defaultFn: Y.bind("_defRequestFn", this), queuable:true});
+
+ /**
+ * Fired when raw data is received.
+ *
+ * @event data
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @preventable _defDataFn
+ */
+ this.publish("data", {defaultFn: Y.bind("_defDataFn", this), queuable:true});
+
+ /**
+ * Fired when response is returned.
+ *
+ * @event response
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @preventable _defResponseFn
+ */
+ this.publish("response", {defaultFn: Y.bind("_defResponseFn", this), queuable:true});
+
+ /**
+ * Fired when an error is encountered.
+ *
+ * @event error
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ */
+
+ },
+
+ /**
+ * Manages request/response transaction. Must fire <code>response</code>
+ * event when response is received. This method should be implemented by
+ * subclasses to achieve more complex behavior such as accessing remote data.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facadewith the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var data = this.get("source");
+
+ // Problematic data
+ if(LANG.isUndefined(data)) {
+ e.error = new Error("Local source undefined");
+ }
+ if(e.error) {
+ this.fire("error", e);
+ Y.log("Error in response", "error", "datasource-local");
+ }
+
+ this.fire("data", Y.mix({data:data}, e));
+ Y.log("Transaction " + e.tId + " complete. Request: " +
+ Y.dump(e.request) + " . Response: " + Y.dump(e.response), "info", "datasource-local");
+ },
+
+ /**
+ * Normalizes raw data into a response that includes results and meta properties.
+ *
+ * @method _defDataFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _defDataFn: function(e) {
+ var data = e.data,
+ meta = e.meta,
+ response = {
+ results: (LANG.isArray(data)) ? data : [data],
+ meta: (meta) ? meta : {}
+ };
+
+ this.fire("response", Y.mix({response: response}, e));
+ },
+
+ /**
+ * Sends data as a normalized response to callback.
+ *
+ * @method _defResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @protected
+ */
+ _defResponseFn: function(e) {
+ // Send the response back to the callback
+ DSLocal.issueCallback(e);
+ },
+
+ /**
+ * Generates a unique transaction ID and fires <code>request</code> event.
+ *
+ * @method sendRequest
+ * @param request {Object} Request.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data payload that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @param cfg {Object} Configuration object
+ * @return {Number} Transaction ID.
+ */
+ sendRequest: function(request, callback, cfg) {
+ var tId = DSLocal._tId++;
+ this.fire("request", {tId:tId, request:request, callback:callback, cfg:cfg || {}});
+ Y.log("Transaction " + tId + " sent request: " + Y.dump(request), "info", "datasource-local");
+ return tId;
+ }
+});
+
+Y.namespace("DataSource").Local = DSLocal;
+
+
+
+}, '3.0.0' ,{requires:['base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-local",function(C){var B=C.Lang,A=function(){A.superclass.constructor.apply(this,arguments);};C.mix(A,{NAME:"dataSourceLocal",ATTRS:{source:{value:null}},_tId:0,issueCallback:function(E){if(E.callback){var D=(E.error&&E.callback.failure)||E.callback.success;if(D){D(E);}}}});C.extend(A,C.Base,{initializer:function(D){this._initEvents();},_initEvents:function(){this.publish("request",{defaultFn:C.bind("_defRequestFn",this),queuable:true});this.publish("data",{defaultFn:C.bind("_defDataFn",this),queuable:true});this.publish("response",{defaultFn:C.bind("_defResponseFn",this),queuable:true});},_defRequestFn:function(E){var D=this.get("source");if(B.isUndefined(D)){E.error=new Error("Local source undefined");}if(E.error){this.fire("error",E);}this.fire("data",C.mix({data:D},E));},_defDataFn:function(G){var E=G.data,F=G.meta,D={results:(B.isArray(E))?E:[E],meta:(F)?F:{}};this.fire("response",C.mix({response:D},G));},_defResponseFn:function(D){A.issueCallback(D);},sendRequest:function(E,G,D){var F=A._tId++;this.fire("request",{tId:F,request:E,callback:G,cfg:D||{}});return F;}});C.namespace("DataSource").Local=A;},"3.0.0",{requires:["base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-local', function(Y) {
+
+/**
+ * The DataSource utility provides a common configurable interface for widgets to
+ * access a variety of data, from JavaScript arrays to online database servers.
+ *
+ * @module datasource
+ */
+
+/**
+ * Provides the base DataSource implementation, which can be extended to
+ * create DataSources for specific data protocols, such as the IO Utility, the
+ * Get Utility, or custom functions.
+ *
+ * @module datasource
+ * @submodule datasource-local
+ */
+
+/**
+ * Base class for the DataSource Utility.
+ * @class DataSource.Local
+ * @extends Base
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+DSLocal = function() {
+ DSLocal.superclass.constructor.apply(this, arguments);
+};
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSLocal, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceLocal"
+ */
+ NAME: "dataSourceLocal",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ value: null
+ }
+ },
+
+ /**
+ * Global transaction counter.
+ *
+ * @property DataSource._tId
+ * @type Number
+ * @static
+ * @private
+ * @default 0
+ */
+ _tId: 0,
+
+ /**
+ * Executes a given callback. The third param determines whether to execute
+ *
+ * @method DataSource.issueCallback
+ * @param callback {Object} The callback object.
+ * @param params {Array} params to be passed to the callback method
+ * @param error {Boolean} whether an error occurred
+ * @static
+ */
+ issueCallback: function (e) {
+ if(e.callback) {
+ var callbackFunc = (e.error && e.callback.failure) || e.callback.success;
+ if (callbackFunc) {
+ callbackFunc(e);
+ }
+ }
+ }
+});
+
+Y.extend(DSLocal, Y.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._initEvents();
+ },
+
+ /**
+ * This method creates all the events for this module.
+ * @method _initEvents
+ * @private
+ */
+ _initEvents: function() {
+ /**
+ * Fired when a data request is received.
+ *
+ * @event request
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @preventable _defRequestFn
+ */
+ this.publish("request", {defaultFn: Y.bind("_defRequestFn", this), queuable:true});
+
+ /**
+ * Fired when raw data is received.
+ *
+ * @event data
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @preventable _defDataFn
+ */
+ this.publish("data", {defaultFn: Y.bind("_defDataFn", this), queuable:true});
+
+ /**
+ * Fired when response is returned.
+ *
+ * @event response
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @preventable _defResponseFn
+ */
+ this.publish("response", {defaultFn: Y.bind("_defResponseFn", this), queuable:true});
+
+ /**
+ * Fired when an error is encountered.
+ *
+ * @event error
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ */
+
+ },
+
+ /**
+ * Manages request/response transaction. Must fire <code>response</code>
+ * event when response is received. This method should be implemented by
+ * subclasses to achieve more complex behavior such as accessing remote data.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facadewith the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var data = this.get("source");
+
+ // Problematic data
+ if(LANG.isUndefined(data)) {
+ e.error = new Error("Local source undefined");
+ }
+ if(e.error) {
+ this.fire("error", e);
+ }
+
+ this.fire("data", Y.mix({data:data}, e));
+ },
+
+ /**
+ * Normalizes raw data into a response that includes results and meta properties.
+ *
+ * @method _defDataFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _defDataFn: function(e) {
+ var data = e.data,
+ meta = e.meta,
+ response = {
+ results: (LANG.isArray(data)) ? data : [data],
+ meta: (meta) ? meta : {}
+ };
+
+ this.fire("response", Y.mix({response: response}, e));
+ },
+
+ /**
+ * Sends data as a normalized response to callback.
+ *
+ * @method _defResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @protected
+ */
+ _defResponseFn: function(e) {
+ // Send the response back to the callback
+ DSLocal.issueCallback(e);
+ },
+
+ /**
+ * Generates a unique transaction ID and fires <code>request</code> event.
+ *
+ * @method sendRequest
+ * @param request {Object} Request.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data payload that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @param cfg {Object} Configuration object
+ * @return {Number} Transaction ID.
+ */
+ sendRequest: function(request, callback, cfg) {
+ var tId = DSLocal._tId++;
+ this.fire("request", {tId:tId, request:request, callback:callback, cfg:cfg || {}});
+ return tId;
+ }
+});
+
+Y.namespace("DataSource").Local = DSLocal;
+
+
+
+}, '3.0.0' ,{requires:['base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-local",function(C){var B=C.Lang,A=function(){A.superclass.constructor.apply(this,arguments);};C.mix(A,{NAME:"dataSourceLocal",ATTRS:{source:{value:null}},_tId:0,issueCallback:function(E){if(E.callback){var D=(E.error&&E.callback.failure)||E.callback.success;if(D){D(E);}}}});C.extend(A,C.Base,{initializer:function(D){this._initEvents();},_initEvents:function(){this.publish("request",{defaultFn:C.bind("_defRequestFn",this),queuable:true});this.publish("data",{defaultFn:C.bind("_defDataFn",this),queuable:true});this.publish("response",{defaultFn:C.bind("_defResponseFn",this),queuable:true});},_defRequestFn:function(E){var D=this.get("source");if(B.isUndefined(D)){E.error=new Error("Local source undefined");}if(E.error){this.fire("error",E);}this.fire("data",C.mix({data:D},E));},_defDataFn:function(G){var E=G.data,F=G.meta,D={results:(B.isArray(E))?E:[E],meta:(F)?F:{}};this.fire("response",C.mix({response:D},G));},_defResponseFn:function(D){A.issueCallback(D);},sendRequest:function(E,G,D){var F=A._tId++;this.fire("request",{tId:F,request:E,callback:G,cfg:D||{}});return F;}});C.namespace("DataSource").Local=A;},"3.0.0",{requires:["base"]});YUI.add("datasource-io",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NAME:"dataSourceIO",ATTRS:{io:{value:B.io,cloneDefaultValue:false}}});B.extend(A,B.DataSource.Local,{initializer:function(C){this._queue={interval:null,conn:null,requests:[]};},_queue:null,_defRequestFn:function(F){var E=this.get("source"),G=this.get("io"),D=F.request,C=B.mix(F.cfg,{on:{success:function(J,H,I){this.fire("data",B.mix({data:H},I));},failure:function(J,H,I){I.error=new Error("IO data failure");this.fire("error",B.mix({data:H},I));this.fire("data",B.mix({data:H},I));}},context:this,arguments:F});if(B.Lang.isString(D)){if(C.method&&(C.method.toUpperCase()==="POST")){C.data=C.data?C.data+D:D;}else{E+=D;}}G(E,C);return F.tId;}});B.DataSource.IO=A;},"3.0.0",{requires:["datasource-local","io"]});YUI.add("datasource-get",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NAME:"dataSourceGet",ATTRS:{get:{value:B.Get,cloneDefaultValue:false},asyncMode:{value:"allowAll"},scriptCallbackParam:{value:"callback"},generateRequestCallback:{value:function(C,D){return"&"+C.get("scriptCallbackParam")+"=YUI.Env.DataSource.callbacks["+D+"]";}}},callbacks:[],_tId:0});B.extend(A,B.DataSource.Local,{_defRequestFn:function(F){var E=this.get("source"),D=this.get("get"),G=A._tId++,C=this;YUI.Env.DataSource.callbacks[G]=B.rbind(function(H){if((C.get("asyncMode")!=="ignoreStaleResponses")||(G===A.callbacks.length-1)){C.fire("data",B.mix({data:H},F));}else{}delete A.callbacks[G];},this,G);E+=F.request+this.get("generateRequestCallback")(this,G);D.script(E,{autopurge:true,onFailure:B.bind(function(H){H.error=new Error("Script node data failure");this.fire("error",H);},this,F)});return F.tId;}});B.DataSource.Get=A;YUI.namespace("Env.DataSource.callbacks");},"3.0.0",{requires:["datasource-local","get"]});YUI.add("datasource-function",function(B){var A=B.Lang,C=function(){C.superclass.constructor.apply(this,arguments);};B.mix(C,{NAME:"dataSourceFunction",ATTRS:{source:{validator:A.isFunction}}});B.extend(C,B.DataSource.Local,{_defRequestFn:function(G){var F=this.get("source"),D;if(F){try{D=F(G.request,this,G);this.fire("data",B.mix({data:D},G));}catch(E){G.error=E;this.fire("error",G);}}else{G.error=new Error("Function data failure");this.fire("error",G);}return G.tId;}});B.DataSource.Function=C;},"3.0.0",{requires:["datasource-local"]});YUI.add("datasource-cache",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"cache",NAME:"dataSourceCache",ATTRS:{}});B.extend(A,B.Cache,{initializer:function(C){this.doBefore("_defRequestFn",this._beforeDefRequestFn);this.doBefore("_defResponseFn",this._beforeDefResponseFn);},_beforeDefRequestFn:function(D){var C=(this.retrieve(D.request))||null;if(C&&C.response){this.get("host").fire("response",B.mix({response:C.response},D));return new B.Do.Halt("DataSourceCache plugin halted _defRequestFn");}},_beforeDefResponseFn:function(C){if(C.response&&!C.response.cached){C.response.cached=true;this.add(C.request,C.response,(C.callback&&C.callback.argument));}}});B.namespace("Plugin").DataSourceCache=A;},"3.0.0",{requires:["datasource-local","cache"]});YUI.add("datasource-jsonschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"schema",NAME:"dataSourceJSONSchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&B.Lang.isString(E.data.responseText))?E.data.responseText:E.data,C=B.DataSchema.JSON.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceJSONSchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceJSONSchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-json"]});YUI.add("datasource-xmlschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"schema",NAME:"dataSourceXMLSchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&E.data.responseXML&&(E.data.responseXML.nodeType===9))?E.data.responseXML:E.data,C=B.DataSchema.XML.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceXMLSchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceXMLSchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-xml"]});YUI.add("datasource-arrayschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);
+};B.mix(A,{NS:"schema",NAME:"dataSourceArraySchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&B.Lang.isString(E.data.responseText))?E.data.responseText:E.data,C=B.DataSchema.Array.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceArraySchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceArraySchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-array"]});YUI.add("datasource-textschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"schema",NAME:"dataSourceTextSchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&B.Lang.isString(E.data.responseText))?E.data.responseText:E.data,C=B.DataSchema.Text.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceTextSchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceTextSchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-text"]});YUI.add("datasource-polling",function(C){var A=C.Lang,B=function(){this._intervals={};};B.prototype={_intervals:null,setInterval:function(F,E,G){var D=C.later(F,this,this.sendRequest,[E,G],true);this._intervals[D.id]=D;return D.id;},clearInterval:function(E,D){E=D||E;if(this._intervals[E]){this._intervals[E].cancel();delete this._intervals[E];}},clearAllIntervals:function(){C.each(this._intervals,this.clearInterval,this);}};C.augment(C.DataSource.Local,B);},"3.0.0",{requires:["datasource-local"]});YUI.add("datasource",function(A){},"3.0.0",{use:["datasource-local","datasource-io","datasource-get","datasource-function","datasource-cache","datasource-jsonschema","datasource-xmlschema","datasource-arrayschema","datasource-textschema","datasource-polling"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-polling', function(Y) {
+
+/**
+ * Extends DataSource with polling functionality.
+ *
+ * @module datasource
+ * @submodule datasource-polling
+ */
+
+/**
+ * Adds polling to the DataSource Utility.
+ * @class Pollable
+ * @extends DataSource.Local
+ */
+var LANG = Y.Lang,
+
+ Pollable = function() {
+ this._intervals = {};
+ };
+
+Pollable.prototype = {
+
+ /**
+ * @property _intervals
+ * @description Hash of polling interval IDs that have been enabled,
+ * stored here to be able to clear all intervals.
+ * @private
+ */
+ _intervals: null,
+
+ /**
+ * Sets up a polling mechanism to send requests at set intervals and forward
+ * responses to given callback.
+ *
+ * @method setInterval
+ * @param msec {Number} Length of interval in milliseconds.
+ * @param request {Object} Request object.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @return {Number} Interval ID.
+ */
+ setInterval: function(msec, request, callback) {
+ var x = Y.later(msec, this, this.sendRequest, [request, callback], true);
+ this._intervals[x.id] = x;
+ return x.id;
+ },
+
+ /**
+ * Disables polling mechanism associated with the given interval ID.
+ *
+ * @method clearInterval
+ * @param id {Number} Interval ID.
+ */
+ clearInterval: function(id, key) {
+ // In case of being called by clearAllIntervals()
+ id = key || id;
+ if(this._intervals[id]) {
+ // Clear the interval
+ this._intervals[id].cancel();
+ // Clear from tracker
+ delete this._intervals[id];
+ }
+ },
+
+ /**
+ * Clears all intervals.
+ *
+ * @method clearAllIntervals
+ */
+ clearAllIntervals: function() {
+ Y.each(this._intervals, this.clearInterval, this);
+ }
+};
+
+Y.augment(Y.DataSource.Local, Pollable);
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-polling",function(C){var A=C.Lang,B=function(){this._intervals={};};B.prototype={_intervals:null,setInterval:function(F,E,G){var D=C.later(F,this,this.sendRequest,[E,G],true);this._intervals[D.id]=D;return D.id;},clearInterval:function(E,D){E=D||E;if(this._intervals[E]){this._intervals[E].cancel();delete this._intervals[E];}},clearAllIntervals:function(){C.each(this._intervals,this.clearInterval,this);}};C.augment(C.DataSource.Local,B);},"3.0.0",{requires:["datasource-local"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-polling', function(Y) {
+
+/**
+ * Extends DataSource with polling functionality.
+ *
+ * @module datasource
+ * @submodule datasource-polling
+ */
+
+/**
+ * Adds polling to the DataSource Utility.
+ * @class Pollable
+ * @extends DataSource.Local
+ */
+var LANG = Y.Lang,
+
+ Pollable = function() {
+ this._intervals = {};
+ };
+
+Pollable.prototype = {
+
+ /**
+ * @property _intervals
+ * @description Hash of polling interval IDs that have been enabled,
+ * stored here to be able to clear all intervals.
+ * @private
+ */
+ _intervals: null,
+
+ /**
+ * Sets up a polling mechanism to send requests at set intervals and forward
+ * responses to given callback.
+ *
+ * @method setInterval
+ * @param msec {Number} Length of interval in milliseconds.
+ * @param request {Object} Request object.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @return {Number} Interval ID.
+ */
+ setInterval: function(msec, request, callback) {
+ var x = Y.later(msec, this, this.sendRequest, [request, callback], true);
+ this._intervals[x.id] = x;
+ return x.id;
+ },
+
+ /**
+ * Disables polling mechanism associated with the given interval ID.
+ *
+ * @method clearInterval
+ * @param id {Number} Interval ID.
+ */
+ clearInterval: function(id, key) {
+ // In case of being called by clearAllIntervals()
+ id = key || id;
+ if(this._intervals[id]) {
+ // Clear the interval
+ this._intervals[id].cancel();
+ // Clear from tracker
+ delete this._intervals[id];
+ }
+ },
+
+ /**
+ * Clears all intervals.
+ *
+ * @method clearAllIntervals
+ */
+ clearAllIntervals: function() {
+ Y.each(this._intervals, this.clearInterval, this);
+ }
+};
+
+Y.augment(Y.DataSource.Local, Pollable);
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-textschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on text data.
+ *
+ * @module datasource
+ * @submodule datasource-textschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceTextSchema
+ * @extends Plugin.Base
+ */
+var DataSourceTextSchema = function() {
+ DataSourceTextSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceTextSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceTextSchema"
+ */
+ NAME: "dataSourceTextSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceTextSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceTextSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Text.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceTextSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceTextSchema = DataSourceTextSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-text']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-textschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"schema",NAME:"dataSourceTextSchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&B.Lang.isString(E.data.responseText))?E.data.responseText:E.data,C=B.DataSchema.Text.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceTextSchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceTextSchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-text"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-textschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on text data.
+ *
+ * @module datasource
+ * @submodule datasource-textschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceTextSchema
+ * @extends Plugin.Base
+ */
+var DataSourceTextSchema = function() {
+ DataSourceTextSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceTextSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceTextSchema"
+ */
+ NAME: "dataSourceTextSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceTextSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceTextSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Text.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceTextSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceTextSchema = DataSourceTextSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-text']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-xmlschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on XML data.
+ *
+ * @module datasource
+ * @submodule datasource-xmlschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceXMLSchema
+ * @extends Plugin.Base
+ */
+var DataSourceXMLSchema = function() {
+ DataSourceXMLSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceXMLSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceXMLSchema"
+ */
+ NAME: "dataSourceXMLSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceXMLSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceXMLSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && e.data.responseXML && (e.data.responseXML.nodeType === 9)) ? e.data.responseXML : e.data,
+ response = Y.DataSchema.XML.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceXMLSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceXMLSchema = DataSourceXMLSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-xml']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datasource-xmlschema",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};B.mix(A,{NS:"schema",NAME:"dataSourceXMLSchema",ATTRS:{schema:{}}});B.extend(A,B.Plugin.Base,{initializer:function(C){this.doBefore("_defDataFn",this._beforeDefDataFn);},_beforeDefDataFn:function(E){var D=(B.DataSource.IO&&(this.get("host") instanceof B.DataSource.IO)&&E.data.responseXML&&(E.data.responseXML.nodeType===9))?E.data.responseXML:E.data,C=B.DataSchema.XML.apply(this.get("schema"),D);if(!C){C={meta:{},results:D};}this.get("host").fire("response",B.mix({response:C},E));return new B.Do.Halt("DataSourceXMLSchema plugin halted _defDataFn");}});B.namespace("Plugin").DataSourceXMLSchema=A;},"3.0.0",{requires:["plugin","datasource-local","dataschema-xml"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-xmlschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on XML data.
+ *
+ * @module datasource
+ * @submodule datasource-xmlschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceXMLSchema
+ * @extends Plugin.Base
+ */
+var DataSourceXMLSchema = function() {
+ DataSourceXMLSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceXMLSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceXMLSchema"
+ */
+ NAME: "dataSourceXMLSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceXMLSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceXMLSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && e.data.responseXML && (e.data.responseXML.nodeType === 9)) ? e.data.responseXML : e.data,
+ response = Y.DataSchema.XML.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceXMLSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceXMLSchema = DataSourceXMLSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-xml']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datasource-local', function(Y) {
+
+/**
+ * The DataSource utility provides a common configurable interface for widgets to
+ * access a variety of data, from JavaScript arrays to online database servers.
+ *
+ * @module datasource
+ */
+
+/**
+ * Provides the base DataSource implementation, which can be extended to
+ * create DataSources for specific data protocols, such as the IO Utility, the
+ * Get Utility, or custom functions.
+ *
+ * @module datasource
+ * @submodule datasource-local
+ */
+
+/**
+ * Base class for the DataSource Utility.
+ * @class DataSource.Local
+ * @extends Base
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+DSLocal = function() {
+ DSLocal.superclass.constructor.apply(this, arguments);
+};
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSLocal, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceLocal"
+ */
+ NAME: "dataSourceLocal",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ value: null
+ }
+ },
+
+ /**
+ * Global transaction counter.
+ *
+ * @property DataSource._tId
+ * @type Number
+ * @static
+ * @private
+ * @default 0
+ */
+ _tId: 0,
+
+ /**
+ * Executes a given callback. The third param determines whether to execute
+ *
+ * @method DataSource.issueCallback
+ * @param callback {Object} The callback object.
+ * @param params {Array} params to be passed to the callback method
+ * @param error {Boolean} whether an error occurred
+ * @static
+ */
+ issueCallback: function (e) {
+ if(e.callback) {
+ var callbackFunc = (e.error && e.callback.failure) || e.callback.success;
+ if (callbackFunc) {
+ callbackFunc(e);
+ }
+ }
+ }
+});
+
+Y.extend(DSLocal, Y.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._initEvents();
+ },
+
+ /**
+ * This method creates all the events for this module.
+ * @method _initEvents
+ * @private
+ */
+ _initEvents: function() {
+ /**
+ * Fired when a data request is received.
+ *
+ * @event request
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @preventable _defRequestFn
+ */
+ this.publish("request", {defaultFn: Y.bind("_defRequestFn", this), queuable:true});
+
+ /**
+ * Fired when raw data is received.
+ *
+ * @event data
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @preventable _defDataFn
+ */
+ this.publish("data", {defaultFn: Y.bind("_defDataFn", this), queuable:true});
+
+ /**
+ * Fired when response is returned.
+ *
+ * @event response
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @preventable _defResponseFn
+ */
+ this.publish("response", {defaultFn: Y.bind("_defResponseFn", this), queuable:true});
+
+ /**
+ * Fired when an error is encountered.
+ *
+ * @event error
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ */
+
+ },
+
+ /**
+ * Manages request/response transaction. Must fire <code>response</code>
+ * event when response is received. This method should be implemented by
+ * subclasses to achieve more complex behavior such as accessing remote data.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facadewith the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var data = this.get("source");
+
+ // Problematic data
+ if(LANG.isUndefined(data)) {
+ e.error = new Error("Local source undefined");
+ }
+ if(e.error) {
+ this.fire("error", e);
+ }
+
+ this.fire("data", Y.mix({data:data}, e));
+ },
+
+ /**
+ * Normalizes raw data into a response that includes results and meta properties.
+ *
+ * @method _defDataFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _defDataFn: function(e) {
+ var data = e.data,
+ meta = e.meta,
+ response = {
+ results: (LANG.isArray(data)) ? data : [data],
+ meta: (meta) ? meta : {}
+ };
+
+ this.fire("response", Y.mix({response: response}, e));
+ },
+
+ /**
+ * Sends data as a normalized response to callback.
+ *
+ * @method _defResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Boolean)</dt> <dd>Error flag.</dd>
+ * </dl>
+ * </dd>
+ * </dl>
+ * @protected
+ */
+ _defResponseFn: function(e) {
+ // Send the response back to the callback
+ DSLocal.issueCallback(e);
+ },
+
+ /**
+ * Generates a unique transaction ID and fires <code>request</code> event.
+ *
+ * @method sendRequest
+ * @param request {Object} Request.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data payload that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @param cfg {Object} Configuration object
+ * @return {Number} Transaction ID.
+ */
+ sendRequest: function(request, callback, cfg) {
+ var tId = DSLocal._tId++;
+ this.fire("request", {tId:tId, request:request, callback:callback, cfg:cfg || {}});
+ return tId;
+ }
+});
+
+Y.namespace("DataSource").Local = DSLocal;
+
+
+
+}, '3.0.0' ,{requires:['base']});
+
+YUI.add('datasource-io', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the IO Utility.
+ *
+ * @module datasource
+ * @submodule datasource-io
+ */
+
+/**
+ * IO subclass for the DataSource Utility.
+ * @class DataSource.IO
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSIO = function() {
+ DSIO.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSIO, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceIO"
+ */
+ NAME: "dataSourceIO",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.IO Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to IO Utility.
+ *
+ * @attribute io
+ * @type Y.io
+ * @default Y.io
+ */
+ io: {
+ value: Y.io,
+ cloneDefaultValue: false
+ }
+ }
+});
+
+Y.extend(DSIO, Y.DataSource.Local, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this._queue = {interval:null, conn:null, requests:[]};
+ },
+
+ /**
+ * @property _queue
+ * @description Object literal to manage asynchronous request/response
+ * cycles enabled if queue needs to be managed (asyncMode/ioConnMode):
+ * <dl>
+ * <dt>interval {Number}</dt>
+ * <dd>Interval ID of in-progress queue.</dd>
+ * <dt>conn</dt>
+ * <dd>In-progress connection identifier (if applicable).</dd>
+ * <dt>requests {Object[]}</dt>
+ * <dd>Array of queued request objects: {request:request, callback:callback}.</dd>
+ * </dl>
+ * @type Object
+ * @default {interval:null, conn:null, requests:[]}
+ * @private
+ */
+ _queue: null,
+
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ io = this.get("io"),
+ request = e.request,
+ cfg = Y.mix(e.cfg, {
+ on: {
+ success: function (id, response, e) {
+ this.fire("data", Y.mix({data:response}, e));
+ },
+ failure: function (id, response, e) {
+ e.error = new Error("IO data failure");
+ this.fire("error", Y.mix({data:response}, e));
+ this.fire("data", Y.mix({data:response}, e));
+ }
+ },
+ context: this,
+ arguments: e
+ });
+
+ // Support for POST transactions
+ if(Y.Lang.isString(request)) {
+ if(cfg.method && (cfg.method.toUpperCase() === "POST")) {
+ cfg.data = cfg.data ? cfg.data+request : request;
+ }
+ else {
+ uri += request;
+ }
+ }
+ io(uri, cfg);
+ return e.tId;
+ }
+});
+
+Y.DataSource.IO = DSIO;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'io']});
+
+YUI.add('datasource-get', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data via the Get Utility.
+ *
+ * @module datasource
+ * @submodule datasource-get
+ */
+
+/**
+ * Get Utility subclass for the DataSource Utility.
+ * @class DataSource.Get
+ * @extends DataSource.Local
+ * @constructor
+ */
+var DSGet = function() {
+ DSGet.superclass.constructor.apply(this, arguments);
+};
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSGet, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceGet"
+ */
+ NAME: "dataSourceGet",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Get Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * Pointer to Get Utility.
+ *
+ * @attribute get
+ * @type Y.Get
+ * @default Y.Get
+ */
+ get: {
+ value: Y.Get,
+ cloneDefaultValue: false
+ },
+
+/**
+ * Defines request/response management in the following manner:
+ * <dl>
+ * <!--<dt>queueRequests</dt>
+ * <dd>If a request is already in progress, wait until response is returned before sending the next request.</dd>
+ * <dt>cancelStaleRequests</dt>
+ * <dd>If a request is already in progress, cancel it before sending the next request.</dd>-->
+ * <dt>ignoreStaleResponses</dt>
+ * <dd>Send all requests, but handle only the response for the most recently sent request.</dd>
+ * <dt>allowAll</dt>
+ * <dd>Send all requests and handle all responses.</dd>
+ * </dl>
+ *
+ * @attribute asyncMode
+ * @type String
+ * @default "allowAll"
+ */
+asyncMode: {
+ value: "allowAll"
+},
+
+/**
+ * Callback string parameter name sent to the remote script. By default,
+ * requests are sent to
+ * <URI>?<scriptCallbackParam>=callbackFunction
+ *
+ * @attribute scriptCallbackParam
+ * @type String
+ * @default "callback"
+ */
+scriptCallbackParam : {
+ value: "callback"
+},
+
+/**
+ * Accepts the DataSource instance and a callback ID, and returns a callback
+ * param/value string that gets appended to the script URI. Implementers
+ * can customize this string to match their server's query syntax.
+ *
+ * @attribute generateRequestCallback
+ * @type Function
+ */
+generateRequestCallback : {
+ value: function(self, id) {
+ return "&" + self.get("scriptCallbackParam") + "=YUI.Env.DataSource.callbacks["+id+"]" ;
+ }
+}
+
+
+
+
+
+ },
+
+ /**
+ * Global array of callback functions, one for each request sent.
+ *
+ * @property callbacks
+ * @type Function[]
+ * @static
+ */
+ callbacks : [],
+
+ /**
+ * Unique ID to track requests.
+ *
+ * @property _tId
+ * @type Number
+ * @private
+ * @static
+ */
+ _tId : 0
+});
+
+Y.extend(DSGet, Y.DataSource.Local, {
+ /**
+ * Passes query string to Get Utility. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var uri = this.get("source"),
+ get = this.get("get"),
+ id = DSGet._tId++,
+ self = this;
+
+
+
+
+
+
+
+
+
+
+
+
+
+ // Dynamically add handler function with a closure to the callback stack
+ YUI.Env.DataSource.callbacks[id] = Y.rbind(function(response) {
+ if((self.get("asyncMode") !== "ignoreStaleResponses")||
+ (id === DSGet.callbacks.length-1)) { // Must ignore stale responses
+
+ self.fire("data", Y.mix({data:response}, e));
+ }
+ else {
+ }
+
+ delete DSGet.callbacks[id];
+ }, this, id);
+
+ // We are now creating a request
+ uri += e.request + this.get("generateRequestCallback")(this, id);
+ //uri = this.doBefore(sUri);
+ get.script(uri, {
+ autopurge: true,
+ // Works in Firefox only....
+ onFailure: Y.bind(function(e) {
+ e.error = new Error("Script node data failure");
+ this.fire("error", e);
+ }, this, e)
+ });
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Get = DSGet;
+YUI.namespace("Env.DataSource.callbacks");
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'get']});
+
+YUI.add('datasource-function', function(Y) {
+
+/**
+ * Provides a DataSource implementation which can be used to retrieve data from a custom function.
+ *
+ * @module datasource
+ * @submodule datasource-function
+ */
+
+/**
+ * Function subclass for the DataSource Utility.
+ * @class DataSource.Function
+ * @extends DataSource.Local
+ * @constructor
+ */
+var LANG = Y.Lang,
+
+ DSFn = function() {
+ DSFn.superclass.constructor.apply(this, arguments);
+ };
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function static properties
+ //
+ /////////////////////////////////////////////////////////////////////////////
+Y.mix(DSFn, {
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceFunction"
+ */
+ NAME: "dataSourceFunction",
+
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSource.Function Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ /**
+ * @attribute source
+ * @description Pointer to live data.
+ * @type MIXED
+ * @default null
+ */
+ source: {
+ validator: LANG.isFunction
+ }
+ }
+});
+
+Y.extend(DSFn, Y.DataSource.Local, {
+ /**
+ * Passes query string to IO. Fires <code>response</code> event when
+ * response is received asynchronously.
+ *
+ * @method _defRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _defRequestFn: function(e) {
+ var fn = this.get("source"),
+ response;
+
+ if(fn) {
+ try {
+ response = fn(e.request, this, e);
+ this.fire("data", Y.mix({data:response}, e));
+ }
+ catch(error) {
+ e.error = error;
+ this.fire("error", e);
+ }
+ }
+ else {
+ e.error = new Error("Function data failure");
+ this.fire("error", e);
+ }
+
+ return e.tId;
+ }
+});
+
+Y.DataSource.Function = DSFn;
+
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
+
+YUI.add('datasource-cache', function(Y) {
+
+/**
+ * Extends DataSource with caching functionality.
+ *
+ * @module datasource
+ * @submodule datasource-cache
+ */
+
+/**
+ * Adds cacheability to the DataSource Utility.
+ * @class DataSourceCache
+ * @extends Cache
+ */
+var DataSourceCache = function() {
+ DataSourceCache.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceCache, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "cache"
+ */
+ NS: "cache",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceCache"
+ */
+ NAME: "dataSourceCache",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceCache Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+
+ }
+});
+
+Y.extend(DataSourceCache, Y.Cache, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defRequestFn", this._beforeDefRequestFn);
+ this.doBefore("_defResponseFn", this._beforeDefResponseFn);
+ },
+
+ /**
+ * First look for cached response, then send request to live data.
+ *
+ * @method _beforeDefRequestFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object.</dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefRequestFn: function(e) {
+ // Is response already in the Cache?
+ var entry = (this.retrieve(e.request)) || null;
+ if(entry && entry.response) {
+ this.get("host").fire("response", Y.mix({response: entry.response}, e));
+ return new Y.Do.Halt("DataSourceCache plugin halted _defRequestFn");
+ }
+ },
+
+ /**
+ * Adds data to cache before returning data.
+ *
+ * @method _beforeDefResponseFn
+ * @param e {Event.Facade} Event Facade with the following properties:
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * <dt>response (Object)</dt> <dd>Normalized response object with the following properties:
+ * <dl>
+ * <dt>cached (Object)</dt> <dd>True when response is cached.</dd>
+ * <dt>results (Object)</dt> <dd>Parsed results.</dd>
+ * <dt>meta (Object)</dt> <dd>Parsed meta data.</dd>
+ * <dt>error (Object)</dt> <dd>Error object.</dd>
+ * </dl>
+ * </dd>
+ * <dt>cfg (Object)</dt> <dd>Configuration object.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefResponseFn: function(e) {
+ // Add to Cache before returning
+ if(e.response && !e.response.cached) {
+ e.response.cached = true;
+ this.add(e.request, e.response, (e.callback && e.callback.argument));
+ }
+ }
+});
+
+Y.namespace('Plugin').DataSourceCache = DataSourceCache;
+
+
+
+}, '3.0.0' ,{requires:['datasource-local', 'cache']});
+
+YUI.add('datasource-jsonschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on JSON data.
+ *
+ * @module datasource
+ * @submodule datasource-jsonschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceJSONSchema
+ * @extends Plugin.Base
+ */
+var DataSourceJSONSchema = function() {
+ DataSourceJSONSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceJSONSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceJSONSchema"
+ */
+ NAME: "dataSourceJSONSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceJSONSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceJSONSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.JSON.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceJSONSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceJSONSchema = DataSourceJSONSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-json']});
+
+YUI.add('datasource-xmlschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on XML data.
+ *
+ * @module datasource
+ * @submodule datasource-xmlschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceXMLSchema
+ * @extends Plugin.Base
+ */
+var DataSourceXMLSchema = function() {
+ DataSourceXMLSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceXMLSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceXMLSchema"
+ */
+ NAME: "dataSourceXMLSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceXMLSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceXMLSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && e.data.responseXML && (e.data.responseXML.nodeType === 9)) ? e.data.responseXML : e.data,
+ response = Y.DataSchema.XML.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceXMLSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceXMLSchema = DataSourceXMLSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-xml']});
+
+YUI.add('datasource-arrayschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on array data.
+ *
+ * @module datasource
+ * @submodule datasource-arrayschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceArraySchema
+ * @extends Plugin.Base
+ */
+var DataSourceArraySchema = function() {
+ DataSourceArraySchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceArraySchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceArraySchema"
+ */
+ NAME: "dataSourceArraySchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceArraySchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceArraySchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Array.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceArraySchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceArraySchema = DataSourceArraySchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-array']});
+
+YUI.add('datasource-textschema', function(Y) {
+
+/**
+ * Extends DataSource with schema-parsing on text data.
+ *
+ * @module datasource
+ * @submodule datasource-textschema
+ */
+
+/**
+ * Adds schema-parsing to the DataSource Utility.
+ * @class DataSourceTextSchema
+ * @extends Plugin.Base
+ */
+var DataSourceTextSchema = function() {
+ DataSourceTextSchema.superclass.constructor.apply(this, arguments);
+};
+
+Y.mix(DataSourceTextSchema, {
+ /**
+ * The namespace for the plugin. This will be the property on the host which
+ * references the plugin instance.
+ *
+ * @property NS
+ * @type String
+ * @static
+ * @final
+ * @value "schema"
+ */
+ NS: "schema",
+
+ /**
+ * Class name.
+ *
+ * @property NAME
+ * @type String
+ * @static
+ * @final
+ * @value "dataSourceTextSchema"
+ */
+ NAME: "dataSourceTextSchema",
+
+ /////////////////////////////////////////////////////////////////////////////
+ //
+ // DataSourceTextSchema Attributes
+ //
+ /////////////////////////////////////////////////////////////////////////////
+
+ ATTRS: {
+ schema: {
+ //value: {}
+ }
+ }
+});
+
+Y.extend(DataSourceTextSchema, Y.Plugin.Base, {
+ /**
+ * Internal init() handler.
+ *
+ * @method initializer
+ * @param config {Object} Config object.
+ * @private
+ */
+ initializer: function(config) {
+ this.doBefore("_defDataFn", this._beforeDefDataFn);
+ },
+
+ /**
+ * Parses raw data into a normalized response.
+ *
+ * @method _beforeDefDataFn
+ * <dl>
+ * <dt>tId (Number)</dt> <dd>Unique transaction ID.</dd>
+ * <dt>request (Object)</dt> <dd>The request.</dd>
+ * <dt>callback (Object)</dt> <dd>The callback object with the following properties:
+ * <dl>
+ * <dt>success (Function)</dt> <dd>Success handler.</dd>
+ * <dt>failure (Function)</dt> <dd>Failure handler.</dd>
+ * </dl>
+ * </dd>
+ * <dt>data (Object)</dt> <dd>Raw data.</dd>
+ * </dl>
+ * @protected
+ */
+ _beforeDefDataFn: function(e) {
+ var data = (Y.DataSource.IO && (this.get("host") instanceof Y.DataSource.IO) && Y.Lang.isString(e.data.responseText)) ? e.data.responseText : e.data,
+ response = Y.DataSchema.Text.apply(this.get("schema"), data);
+
+ // Default
+ if(!response) {
+ response = {
+ meta: {},
+ results: data
+ };
+ }
+
+ this.get("host").fire("response", Y.mix({response:response}, e));
+ return new Y.Do.Halt("DataSourceTextSchema plugin halted _defDataFn");
+ }
+});
+
+Y.namespace('Plugin').DataSourceTextSchema = DataSourceTextSchema;
+
+
+
+}, '3.0.0' ,{requires:['plugin', 'datasource-local', 'dataschema-text']});
+
+YUI.add('datasource-polling', function(Y) {
+
+/**
+ * Extends DataSource with polling functionality.
+ *
+ * @module datasource
+ * @submodule datasource-polling
+ */
+
+/**
+ * Adds polling to the DataSource Utility.
+ * @class Pollable
+ * @extends DataSource.Local
+ */
+var LANG = Y.Lang,
+
+ Pollable = function() {
+ this._intervals = {};
+ };
+
+Pollable.prototype = {
+
+ /**
+ * @property _intervals
+ * @description Hash of polling interval IDs that have been enabled,
+ * stored here to be able to clear all intervals.
+ * @private
+ */
+ _intervals: null,
+
+ /**
+ * Sets up a polling mechanism to send requests at set intervals and forward
+ * responses to given callback.
+ *
+ * @method setInterval
+ * @param msec {Number} Length of interval in milliseconds.
+ * @param request {Object} Request object.
+ * @param callback {Object} An object literal with the following properties:
+ * <dl>
+ * <dt><code>success</code></dt>
+ * <dd>The function to call when the data is ready.</dd>
+ * <dt><code>failure</code></dt>
+ * <dd>The function to call upon a response failure condition.</dd>
+ * <dt><code>argument</code></dt>
+ * <dd>Arbitrary data that will be passed back to the success and failure handlers.</dd>
+ * </dl>
+ * @return {Number} Interval ID.
+ */
+ setInterval: function(msec, request, callback) {
+ var x = Y.later(msec, this, this.sendRequest, [request, callback], true);
+ this._intervals[x.id] = x;
+ return x.id;
+ },
+
+ /**
+ * Disables polling mechanism associated with the given interval ID.
+ *
+ * @method clearInterval
+ * @param id {Number} Interval ID.
+ */
+ clearInterval: function(id, key) {
+ // In case of being called by clearAllIntervals()
+ id = key || id;
+ if(this._intervals[id]) {
+ // Clear the interval
+ this._intervals[id].cancel();
+ // Clear from tracker
+ delete this._intervals[id];
+ }
+ },
+
+ /**
+ * Clears all intervals.
+ *
+ * @method clearAllIntervals
+ */
+ clearAllIntervals: function() {
+ Y.each(this._intervals, this.clearInterval, this);
+ }
+};
+
+Y.augment(Y.DataSource.Local, Pollable);
+
+
+
+}, '3.0.0' ,{requires:['datasource-local']});
+
+
+
+YUI.add('datasource', function(Y){}, '3.0.0' ,{use:['datasource-local','datasource-io','datasource-get','datasource-function','datasource-cache','datasource-jsonschema','datasource-xmlschema','datasource-arrayschema','datasource-textschema','datasource-polling']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-date-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date-parse
+ * @for DataType.Date
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Date"), {
+ /**
+ * Converts data to type Date.
+ *
+ * @method parse
+ * @param data {String | Number} Data to convert. Values supported by the Date constructor are supported.
+ * @return {Date} A Date, or null.
+ */
+ parse: function(data) {
+ var date = null;
+
+ //Convert to date
+ if(!(LANG.isDate(data))) {
+ date = new Date(data);
+ }
+ else {
+ return date;
+ }
+
+ // Validate
+ if(LANG.isDate(date) && (date != "Invalid Date") && !isNaN(date)) { // Workaround for bug 2527965
+ return date;
+ }
+ else {
+ Y.log("Could not convert data " + LANG.dump(date) + " to type Date", "warn", "date");
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").date = Y.DataType.Date.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-date-format', function(Y) {
+
+/**
+ * The DataType Utility provides type-conversion and string-formatting
+ * convenience methods for various JavaScript object types.
+ *
+ * @module datatype
+ */
+
+/**
+ * Date submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date
+ */
+
+/**
+ * Format date submodule implements strftime formatters for javascript based on the
+ * Open Group specification defined at
+ * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+ * This implementation does not include modified conversion specifiers (i.e., Ex and Ox)
+ *
+ * @module datatype
+ * @submodule datatype-date-format
+ */
+
+/**
+ * DataType.Date provides a set of utility functions to operate against Date objects.
+ *
+ * @class DataType.Date
+ * @static
+ */
+
+/**
+ * Pad a number with leading spaces, zeroes or something else
+ * @method xPad
+ * @param x {Number} The number to be padded
+ * @param pad {String} The character to pad the number with
+ * @param r {Number} (optional) The base of the pad, eg, 10 implies to two digits, 100 implies to 3 digits.
+ * @private
+ */
+var xPad=function (x, pad, r)
+{
+ if(typeof r === "undefined")
+ {
+ r=10;
+ }
+ pad = pad.toString();
+ for( ; parseInt(x, 10)<r && r>1; r/=10) {
+ x = pad + x;
+ }
+ return x.toString();
+};
+
+/**
+ * Default date format.
+ *
+ * @for config
+ * @property dateFormat
+ * @type String
+ * @value "%Y-%m-%d"
+ */
+Y.config.dateFormat = Y.config.dateFormat || "%Y-%m-%d";
+
+/**
+ * Default locale for the YUI instance.
+ *
+ * @property locale
+ * @type String
+ * @value "en"
+ */
+Y.config.locale = Y.config.locale || "en";
+
+var Dt = {
+ formats: {
+ a: function (d, l) { return l.a[d.getDay()]; },
+ A: function (d, l) { return l.A[d.getDay()]; },
+ b: function (d, l) { return l.b[d.getMonth()]; },
+ B: function (d, l) { return l.B[d.getMonth()]; },
+ C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); },
+ d: ["getDate", "0"],
+ e: ["getDate", " "],
+ g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); },
+ G: function (d) {
+ var y = d.getFullYear();
+ var V = parseInt(Dt.formats.V(d), 10);
+ var W = parseInt(Dt.formats.W(d), 10);
+
+ if(W > V) {
+ y++;
+ } else if(W===0 && V>=52) {
+ y--;
+ }
+
+ return y;
+ },
+ H: ["getHours", "0"],
+ I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); },
+ j: function (d) {
+ var gmd_1 = new Date("" + d.getFullYear() + "/1/1 GMT");
+ var gmdate = new Date("" + d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDate() + " GMT");
+ var ms = gmdate - gmd_1;
+ var doy = parseInt(ms/60000/60/24, 10)+1;
+ return xPad(doy, 0, 100);
+ },
+ k: ["getHours", " "],
+ l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, " "); },
+ m: function (d) { return xPad(d.getMonth()+1, 0); },
+ M: ["getMinutes", "0"],
+ p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; },
+ P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; },
+ s: function (d, l) { return parseInt(d.getTime()/1000, 10); },
+ S: ["getSeconds", "0"],
+ u: function (d) { var dow = d.getDay(); return dow===0?7:dow; },
+ U: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 6-d.getDay();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0);
+ },
+ V: function (d) {
+ var woy = parseInt(Dt.formats.W(d), 10);
+ var dow1_1 = (new Date("" + d.getFullYear() + "/1/1")).getDay();
+ // First week is 01 and not 00 as in the case of %U and %W,
+ // so we add 1 to the final result except if day 1 of the year
+ // is a Monday (then %W returns 01).
+ // We also need to subtract 1 if the day 1 of the year is
+ // Friday-Sunday, so the resulting equation becomes:
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+ if(idow === 53 && (new Date("" + d.getFullYear() + "/12/31")).getDay() < 4)
+ {
+ idow = 1;
+ }
+ else if(idow === 0)
+ {
+ idow = Dt.formats.V(new Date("" + (d.getFullYear()-1) + "/12/31"));
+ }
+
+ return xPad(idow, 0);
+ },
+ w: "getDay",
+ W: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 7-Dt.formats.u(d);
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0, 10);
+ },
+ y: function (d) { return xPad(d.getFullYear()%100, 0); },
+ Y: "getFullYear",
+ z: function (d) {
+ var o = d.getTimezoneOffset();
+ var H = xPad(parseInt(Math.abs(o/60), 10), 0);
+ var M = xPad(Math.abs(o%60), 0);
+ return (o>0?"-":"+") + H + M;
+ },
+ Z: function (d) {
+ var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, "$2").replace(/[a-z ]/g, "");
+ if(tz.length > 4) {
+ tz = Dt.formats.z(d);
+ }
+ return tz;
+ },
+ "%": function (d) { return "%"; }
+ },
+
+ aggregates: {
+ c: "locale",
+ D: "%m/%d/%y",
+ F: "%Y-%m-%d",
+ h: "%b",
+ n: "\n",
+ r: "locale",
+ R: "%H:%M",
+ t: "\t",
+ T: "%H:%M:%S",
+ x: "locale",
+ X: "locale"
+ //"+": "%a %b %e %T %Z %Y"
+ },
+
+ /**
+ * Takes a native JavaScript Date and formats it as a string for display to user.
+ *
+ * @for DataType.Date
+ * @method format
+ * @param oDate {Date} Date.
+ * @param oConfig {Object} (Optional) Object literal of configuration values:
+ * <dl>
+ * <dt>format {String} (Optional)</dt>
+ * <dd>
+ * <p>
+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at
+ * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html</a>
+ * PHP added a few of its own, defined at <a href="http://www.php.net/strftime">http://www.php.net/strftime</a>
+ * </p>
+ * <p>
+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below.
+ * </p>
+ * <p>
+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat
+ * </p>
+ * <dl>
+ * <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd>
+ * <dt>%A</dt> <dd>full weekday name according to the current locale</dd>
+ * <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd>
+ * <dt>%B</dt> <dd>full month name according to the current locale</dd>
+ * <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd>
+ * <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd>
+ * <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd>
+ * <dt>%D</dt> <dd>same as %m/%d/%y</dd>
+ * <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")</dd>
+ * <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd>
+ * <dt>%g</dt> <dd>like %G, but without the century</dd>
+ * <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd>
+ * <dt>%h</dt> <dd>same as %b</dd>
+ * <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd>
+ * <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd>
+ * <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd>
+ * <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd>
+ * <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd>
+ * <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd>
+ * <dt>%M</dt> <dd>minute as a decimal number</dd>
+ * <dt>%n</dt> <dd>newline character</dd>
+ * <dt>%p</dt> <dd>either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale</dd>
+ * <dt>%P</dt> <dd>like %p, but lower case</dd>
+ * <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd>
+ * <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd>
+ * <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd>
+ * <dt>%S</dt> <dd>second as a decimal number</dd>
+ * <dt>%t</dt> <dd>tab character</dd>
+ * <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd>
+ * <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd>
+ * <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Sunday as the first day of the first week</dd>
+ * <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number,
+ * range 01 to 53, where week 1 is the first week that has at least 4 days
+ * in the current year, and with Monday as the first day of the week.</dd>
+ * <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd>
+ * <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Monday as the first day of the first week</dd>
+ * <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd>
+ * <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd>
+ * <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd>
+ * <dt>%Y</dt> <dd>year as a decimal number including the century</dd>
+ * <dt>%z</dt> <dd>numerical time zone representation</dd>
+ * <dt>%Z</dt> <dd>time zone name or abbreviation</dd>
+ * <dt>%%</dt> <dd>a literal "%" character</dd>
+ * </dl>
+ * </dd>
+ * <dt>locale {String} (Optional)</dt>
+ * <dd>
+ * The locale to use when displaying days of week, months of the year, and other locale specific
+ * strings. If not specified, this defaults to "en" (though this may be overridden by changing Y.config.locale).
+ * The following locales are built in:
+ * <dl>
+ * <dt>en</dt>
+ * <dd>English</dd>
+ * <dt>en-US</dt>
+ * <dd>US English</dd>
+ * <dt>en-GB</dt>
+ * <dd>British English</dd>
+ * <dt>en-AU</dt>
+ * <dd>Australian English (identical to British English)</dd>
+ * </dl>
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"].
+ * See Y.DataType.Date.Locale for more information.
+ * </dd>
+ * </dl>
+ * @return {String} Formatted date for display.
+ */
+ format : function (oDate, oConfig) {
+ oConfig = oConfig || {};
+
+ if(!Y.Lang.isDate(oDate)) {
+ Y.log("format called without a date", "WARN", "datatype-date");
+ return Y.Lang.isValue(oDate) ? oDate : "";
+ }
+
+ var format = oConfig.format || Y.config.dateFormat,
+ sLocale = oConfig.locale || Y.config.locale,
+ LOCALE = Y.DataType.Date.Locale;
+
+ sLocale = sLocale.replace(/_/g, "-");
+
+ // Make sure we have a definition for the requested locale, or default to en.
+ if(!LOCALE[sLocale]) {
+ Y.log("selected locale " + sLocale + " not found, trying alternatives", "WARN", "datatype-date");
+ var tmpLocale = sLocale.replace(/-[a-zA-Z]+$/, "");
+ if(tmpLocale in LOCALE) {
+ sLocale = tmpLocale;
+ } else if(Y.config.locale in LOCALE) {
+ sLocale = Y.config.locale;
+ } else {
+ sLocale = "en";
+ }
+ Y.log("falling back to " + sLocale, "INFO", "datatype-date");
+ }
+
+ var aLocale = LOCALE[sLocale];
+
+ var replace_aggs = function (m0, m1) {
+ var f = Dt.aggregates[m1];
+ return (f === "locale" ? aLocale[m1] : f);
+ };
+
+ var replace_formats = function (m0, m1) {
+ var f = Dt.formats[m1];
+ switch(Y.Lang.type(f)) {
+ case "string": // string => built in date function
+ return oDate[f]();
+ case "function": // function => our own function
+ return f.call(oDate, oDate, aLocale);
+ case "array": // built in function with padding
+ if(Y.Lang.type(f[0]) === "string") {
+ return xPad(oDate[f[0]](), f[1]);
+ } // no break; (fall through to default:)
+ default:
+ Y.log("unrecognised replacement type, please file a bug (format: " + oConfig.format || Y.config.dateFormat + ")", "WARN", "datatype-date");
+ return m1;
+ }
+ };
+
+ // First replace aggregates (run in a loop because an agg may be made up of other aggs)
+ while(format.match(/%[cDFhnrRtTxX]/)) {
+ format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs);
+ }
+
+ // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a)
+ var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats);
+
+ replace_aggs = replace_formats = undefined;
+
+ return str;
+ }
+};
+
+Y.mix(Y.namespace("DataType.Date"), Dt);
+
+/**
+ * @module datatype
+*/
+
+/**
+ * The Date.Locale class is a container for all localised date strings
+ * used by Y.DataType.Date. It is used internally, but may be extended
+ * to provide new date localisations.
+ *
+ * To create your own Locale, follow these steps:
+ * <ol>
+ * <li>Find an existing locale that matches closely with your needs</li>
+ * <li>Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing
+ * matches.</li>
+ * <li>Create your own class as an extension of the base class using
+ * Y.merge, and add your own localisations where needed.</li>
+ * </ol>
+ * See the Y.DataType.Date.Locale["en-US"] and Y.DataType.Date.Locale["en-GB"]
+ * classes which extend Y.DataType.Date.Locale["en"].
+ *
+ * For example, to implement locales for French french and Canadian french,
+ * we would do the following:
+ * <ol>
+ * <li>For French french, we have no existing similar locale, so use
+ * Y.DataType.Date.Locale["en"] as the base, and extend it:
+ * <pre>
+ * Y.DataType.Date.Locale["fr"] = Y.merge(Y.DataType.Date.Locale, {
+ * a: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
+ * A: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ * b: ["jan", "fév", "mar", "avr", "mai", "jun", "jui", "aoû", "sep", "oct", "nov", "déc"],
+ * B: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ * c: "%a %d %b %Y %T %Z",
+ * p: ["", ""],
+ * P: ["", ""],
+ * x: "%d.%m.%Y",
+ * X: "%T"
+ * });
+ * </pre>
+ * </li>
+ * <li>For Canadian french, we start with French french and change the meaning of \%x:
+ * <pre>
+ * Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
+ * x: "%Y-%m-%d"
+ * });
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * With that, you can use your new locales:
+ * <pre>
+ * var d = new Date("2008/04/22");
+ * Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * </pre>
+ * will return:
+ * <pre>
+ * mardi, 22 avril == 22.04.2008
+ * </pre>
+ * And
+ * <pre>
+ * Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * </pre>
+ * Will return:
+ * <pre>
+ * mardi, 22 avril == 2008-04-22
+ * </pre>
+ * @requires oop
+ * @class DataType.Date.Locale
+ * @static
+ */
+var YDateEn = {
+ a: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ A: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ b: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ B: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ c: "%a %d %b %Y %T %Z",
+ p: ["AM", "PM"],
+ P: ["am", "pm"],
+ r: "%I:%M:%S %p",
+ x: "%d/%m/%y",
+ X: "%T"
+};
+
+Y.namespace("DataType.Date.Locale");
+
+Y.DataType.Date.Locale["en"] = YDateEn;
+
+Y.DataType.Date.Locale["en-US"] = Y.merge(YDateEn, {
+ c: "%a %d %b %Y %I:%M:%S %p %Z",
+ x: "%m/%d/%Y",
+ X: "%I:%M:%S %p"
+});
+
+Y.DataType.Date.Locale["en-GB"] = Y.merge(YDateEn, {
+ r: "%l:%M:%S %P %Z"
+});
+Y.DataType.Date.Locale["en-AU"] = Y.merge(YDateEn);
+
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-date', function(Y){}, '3.0.0' ,{use:['datatype-date-parse', 'datatype-date-format']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-date-format', function(Y) {
+
+/**
+ * The DataType Utility provides type-conversion and string-formatting
+ * convenience methods for various JavaScript object types.
+ *
+ * @module datatype
+ */
+
+/**
+ * Date submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date
+ */
+
+/**
+ * Format date submodule implements strftime formatters for javascript based on the
+ * Open Group specification defined at
+ * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+ * This implementation does not include modified conversion specifiers (i.e., Ex and Ox)
+ *
+ * @module datatype
+ * @submodule datatype-date-format
+ */
+
+/**
+ * DataType.Date provides a set of utility functions to operate against Date objects.
+ *
+ * @class DataType.Date
+ * @static
+ */
+
+/**
+ * Pad a number with leading spaces, zeroes or something else
+ * @method xPad
+ * @param x {Number} The number to be padded
+ * @param pad {String} The character to pad the number with
+ * @param r {Number} (optional) The base of the pad, eg, 10 implies to two digits, 100 implies to 3 digits.
+ * @private
+ */
+var xPad=function (x, pad, r)
+{
+ if(typeof r === "undefined")
+ {
+ r=10;
+ }
+ pad = pad.toString();
+ for( ; parseInt(x, 10)<r && r>1; r/=10) {
+ x = pad + x;
+ }
+ return x.toString();
+};
+
+/**
+ * Default date format.
+ *
+ * @for config
+ * @property dateFormat
+ * @type String
+ * @value "%Y-%m-%d"
+ */
+Y.config.dateFormat = Y.config.dateFormat || "%Y-%m-%d";
+
+/**
+ * Default locale for the YUI instance.
+ *
+ * @property locale
+ * @type String
+ * @value "en"
+ */
+Y.config.locale = Y.config.locale || "en";
+
+var Dt = {
+ formats: {
+ a: function (d, l) { return l.a[d.getDay()]; },
+ A: function (d, l) { return l.A[d.getDay()]; },
+ b: function (d, l) { return l.b[d.getMonth()]; },
+ B: function (d, l) { return l.B[d.getMonth()]; },
+ C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); },
+ d: ["getDate", "0"],
+ e: ["getDate", " "],
+ g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); },
+ G: function (d) {
+ var y = d.getFullYear();
+ var V = parseInt(Dt.formats.V(d), 10);
+ var W = parseInt(Dt.formats.W(d), 10);
+
+ if(W > V) {
+ y++;
+ } else if(W===0 && V>=52) {
+ y--;
+ }
+
+ return y;
+ },
+ H: ["getHours", "0"],
+ I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); },
+ j: function (d) {
+ var gmd_1 = new Date("" + d.getFullYear() + "/1/1 GMT");
+ var gmdate = new Date("" + d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDate() + " GMT");
+ var ms = gmdate - gmd_1;
+ var doy = parseInt(ms/60000/60/24, 10)+1;
+ return xPad(doy, 0, 100);
+ },
+ k: ["getHours", " "],
+ l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, " "); },
+ m: function (d) { return xPad(d.getMonth()+1, 0); },
+ M: ["getMinutes", "0"],
+ p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; },
+ P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; },
+ s: function (d, l) { return parseInt(d.getTime()/1000, 10); },
+ S: ["getSeconds", "0"],
+ u: function (d) { var dow = d.getDay(); return dow===0?7:dow; },
+ U: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 6-d.getDay();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0);
+ },
+ V: function (d) {
+ var woy = parseInt(Dt.formats.W(d), 10);
+ var dow1_1 = (new Date("" + d.getFullYear() + "/1/1")).getDay();
+ // First week is 01 and not 00 as in the case of %U and %W,
+ // so we add 1 to the final result except if day 1 of the year
+ // is a Monday (then %W returns 01).
+ // We also need to subtract 1 if the day 1 of the year is
+ // Friday-Sunday, so the resulting equation becomes:
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+ if(idow === 53 && (new Date("" + d.getFullYear() + "/12/31")).getDay() < 4)
+ {
+ idow = 1;
+ }
+ else if(idow === 0)
+ {
+ idow = Dt.formats.V(new Date("" + (d.getFullYear()-1) + "/12/31"));
+ }
+
+ return xPad(idow, 0);
+ },
+ w: "getDay",
+ W: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 7-Dt.formats.u(d);
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0, 10);
+ },
+ y: function (d) { return xPad(d.getFullYear()%100, 0); },
+ Y: "getFullYear",
+ z: function (d) {
+ var o = d.getTimezoneOffset();
+ var H = xPad(parseInt(Math.abs(o/60), 10), 0);
+ var M = xPad(Math.abs(o%60), 0);
+ return (o>0?"-":"+") + H + M;
+ },
+ Z: function (d) {
+ var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, "$2").replace(/[a-z ]/g, "");
+ if(tz.length > 4) {
+ tz = Dt.formats.z(d);
+ }
+ return tz;
+ },
+ "%": function (d) { return "%"; }
+ },
+
+ aggregates: {
+ c: "locale",
+ D: "%m/%d/%y",
+ F: "%Y-%m-%d",
+ h: "%b",
+ n: "\n",
+ r: "locale",
+ R: "%H:%M",
+ t: "\t",
+ T: "%H:%M:%S",
+ x: "locale",
+ X: "locale"
+ //"+": "%a %b %e %T %Z %Y"
+ },
+
+ /**
+ * Takes a native JavaScript Date and formats it as a string for display to user.
+ *
+ * @for DataType.Date
+ * @method format
+ * @param oDate {Date} Date.
+ * @param oConfig {Object} (Optional) Object literal of configuration values:
+ * <dl>
+ * <dt>format {String} (Optional)</dt>
+ * <dd>
+ * <p>
+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at
+ * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html</a>
+ * PHP added a few of its own, defined at <a href="http://www.php.net/strftime">http://www.php.net/strftime</a>
+ * </p>
+ * <p>
+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below.
+ * </p>
+ * <p>
+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat
+ * </p>
+ * <dl>
+ * <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd>
+ * <dt>%A</dt> <dd>full weekday name according to the current locale</dd>
+ * <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd>
+ * <dt>%B</dt> <dd>full month name according to the current locale</dd>
+ * <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd>
+ * <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd>
+ * <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd>
+ * <dt>%D</dt> <dd>same as %m/%d/%y</dd>
+ * <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")</dd>
+ * <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd>
+ * <dt>%g</dt> <dd>like %G, but without the century</dd>
+ * <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd>
+ * <dt>%h</dt> <dd>same as %b</dd>
+ * <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd>
+ * <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd>
+ * <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd>
+ * <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd>
+ * <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd>
+ * <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd>
+ * <dt>%M</dt> <dd>minute as a decimal number</dd>
+ * <dt>%n</dt> <dd>newline character</dd>
+ * <dt>%p</dt> <dd>either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale</dd>
+ * <dt>%P</dt> <dd>like %p, but lower case</dd>
+ * <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd>
+ * <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd>
+ * <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd>
+ * <dt>%S</dt> <dd>second as a decimal number</dd>
+ * <dt>%t</dt> <dd>tab character</dd>
+ * <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd>
+ * <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd>
+ * <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Sunday as the first day of the first week</dd>
+ * <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number,
+ * range 01 to 53, where week 1 is the first week that has at least 4 days
+ * in the current year, and with Monday as the first day of the week.</dd>
+ * <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd>
+ * <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Monday as the first day of the first week</dd>
+ * <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd>
+ * <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd>
+ * <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd>
+ * <dt>%Y</dt> <dd>year as a decimal number including the century</dd>
+ * <dt>%z</dt> <dd>numerical time zone representation</dd>
+ * <dt>%Z</dt> <dd>time zone name or abbreviation</dd>
+ * <dt>%%</dt> <dd>a literal "%" character</dd>
+ * </dl>
+ * </dd>
+ * <dt>locale {String} (Optional)</dt>
+ * <dd>
+ * The locale to use when displaying days of week, months of the year, and other locale specific
+ * strings. If not specified, this defaults to "en" (though this may be overridden by changing Y.config.locale).
+ * The following locales are built in:
+ * <dl>
+ * <dt>en</dt>
+ * <dd>English</dd>
+ * <dt>en-US</dt>
+ * <dd>US English</dd>
+ * <dt>en-GB</dt>
+ * <dd>British English</dd>
+ * <dt>en-AU</dt>
+ * <dd>Australian English (identical to British English)</dd>
+ * </dl>
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"].
+ * See Y.DataType.Date.Locale for more information.
+ * </dd>
+ * </dl>
+ * @return {String} Formatted date for display.
+ */
+ format : function (oDate, oConfig) {
+ oConfig = oConfig || {};
+
+ if(!Y.Lang.isDate(oDate)) {
+ Y.log("format called without a date", "WARN", "datatype-date");
+ return Y.Lang.isValue(oDate) ? oDate : "";
+ }
+
+ var format = oConfig.format || Y.config.dateFormat,
+ sLocale = oConfig.locale || Y.config.locale,
+ LOCALE = Y.DataType.Date.Locale;
+
+ sLocale = sLocale.replace(/_/g, "-");
+
+ // Make sure we have a definition for the requested locale, or default to en.
+ if(!LOCALE[sLocale]) {
+ Y.log("selected locale " + sLocale + " not found, trying alternatives", "WARN", "datatype-date");
+ var tmpLocale = sLocale.replace(/-[a-zA-Z]+$/, "");
+ if(tmpLocale in LOCALE) {
+ sLocale = tmpLocale;
+ } else if(Y.config.locale in LOCALE) {
+ sLocale = Y.config.locale;
+ } else {
+ sLocale = "en";
+ }
+ Y.log("falling back to " + sLocale, "INFO", "datatype-date");
+ }
+
+ var aLocale = LOCALE[sLocale];
+
+ var replace_aggs = function (m0, m1) {
+ var f = Dt.aggregates[m1];
+ return (f === "locale" ? aLocale[m1] : f);
+ };
+
+ var replace_formats = function (m0, m1) {
+ var f = Dt.formats[m1];
+ switch(Y.Lang.type(f)) {
+ case "string": // string => built in date function
+ return oDate[f]();
+ case "function": // function => our own function
+ return f.call(oDate, oDate, aLocale);
+ case "array": // built in function with padding
+ if(Y.Lang.type(f[0]) === "string") {
+ return xPad(oDate[f[0]](), f[1]);
+ } // no break; (fall through to default:)
+ default:
+ Y.log("unrecognised replacement type, please file a bug (format: " + oConfig.format || Y.config.dateFormat + ")", "WARN", "datatype-date");
+ return m1;
+ }
+ };
+
+ // First replace aggregates (run in a loop because an agg may be made up of other aggs)
+ while(format.match(/%[cDFhnrRtTxX]/)) {
+ format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs);
+ }
+
+ // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a)
+ var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats);
+
+ replace_aggs = replace_formats = undefined;
+
+ return str;
+ }
+};
+
+Y.mix(Y.namespace("DataType.Date"), Dt);
+
+/**
+ * @module datatype
+*/
+
+/**
+ * The Date.Locale class is a container for all localised date strings
+ * used by Y.DataType.Date. It is used internally, but may be extended
+ * to provide new date localisations.
+ *
+ * To create your own Locale, follow these steps:
+ * <ol>
+ * <li>Find an existing locale that matches closely with your needs</li>
+ * <li>Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing
+ * matches.</li>
+ * <li>Create your own class as an extension of the base class using
+ * Y.merge, and add your own localisations where needed.</li>
+ * </ol>
+ * See the Y.DataType.Date.Locale["en-US"] and Y.DataType.Date.Locale["en-GB"]
+ * classes which extend Y.DataType.Date.Locale["en"].
+ *
+ * For example, to implement locales for French french and Canadian french,
+ * we would do the following:
+ * <ol>
+ * <li>For French french, we have no existing similar locale, so use
+ * Y.DataType.Date.Locale["en"] as the base, and extend it:
+ * <pre>
+ * Y.DataType.Date.Locale["fr"] = Y.merge(Y.DataType.Date.Locale, {
+ * a: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
+ * A: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ * b: ["jan", "fév", "mar", "avr", "mai", "jun", "jui", "aoû", "sep", "oct", "nov", "déc"],
+ * B: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ * c: "%a %d %b %Y %T %Z",
+ * p: ["", ""],
+ * P: ["", ""],
+ * x: "%d.%m.%Y",
+ * X: "%T"
+ * });
+ * </pre>
+ * </li>
+ * <li>For Canadian french, we start with French french and change the meaning of \%x:
+ * <pre>
+ * Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
+ * x: "%Y-%m-%d"
+ * });
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * With that, you can use your new locales:
+ * <pre>
+ * var d = new Date("2008/04/22");
+ * Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * </pre>
+ * will return:
+ * <pre>
+ * mardi, 22 avril == 22.04.2008
+ * </pre>
+ * And
+ * <pre>
+ * Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * </pre>
+ * Will return:
+ * <pre>
+ * mardi, 22 avril == 2008-04-22
+ * </pre>
+ * @requires oop
+ * @class DataType.Date.Locale
+ * @static
+ */
+var YDateEn = {
+ a: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ A: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ b: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ B: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ c: "%a %d %b %Y %T %Z",
+ p: ["AM", "PM"],
+ P: ["am", "pm"],
+ r: "%I:%M:%S %p",
+ x: "%d/%m/%y",
+ X: "%T"
+};
+
+Y.namespace("DataType.Date.Locale");
+
+Y.DataType.Date.Locale["en"] = YDateEn;
+
+Y.DataType.Date.Locale["en-US"] = Y.merge(YDateEn, {
+ c: "%a %d %b %Y %I:%M:%S %p %Z",
+ x: "%m/%d/%Y",
+ X: "%I:%M:%S %p"
+});
+
+Y.DataType.Date.Locale["en-GB"] = Y.merge(YDateEn, {
+ r: "%l:%M:%S %P %Z"
+});
+Y.DataType.Date.Locale["en-AU"] = Y.merge(YDateEn);
+
+
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-date-format",function(D){var A=function(E,G,F){if(typeof F==="undefined"){F=10;}G=G.toString();for(;parseInt(E,10)<F&&F>1;F/=10){E=G+E;}return E.toString();};D.config.dateFormat=D.config.dateFormat||"%Y-%m-%d";D.config.locale=D.config.locale||"en";var C={formats:{a:function(F,E){return E.a[F.getDay()];},A:function(F,E){return E.A[F.getDay()];},b:function(F,E){return E.b[F.getMonth()];},B:function(F,E){return E.B[F.getMonth()];},C:function(E){return A(parseInt(E.getFullYear()/100,10),0);},d:["getDate","0"],e:["getDate"," "],g:function(E){return A(parseInt(C.formats.G(E)%100,10),0);},G:function(G){var H=G.getFullYear();var F=parseInt(C.formats.V(G),10);var E=parseInt(C.formats.W(G),10);if(E>F){H++;}else{if(E===0&&F>=52){H--;}}return H;},H:["getHours","0"],I:function(F){var E=F.getHours()%12;return A(E===0?12:E,0);},j:function(I){var H=new Date(""+I.getFullYear()+"/1/1 GMT");var F=new Date(""+I.getFullYear()+"/"+(I.getMonth()+1)+"/"+I.getDate()+" GMT");var E=F-H;var G=parseInt(E/60000/60/24,10)+1;return A(G,0,100);},k:["getHours"," "],l:function(F){var E=F.getHours()%12;return A(E===0?12:E," ");},m:function(E){return A(E.getMonth()+1,0);},M:["getMinutes","0"],p:function(F,E){return E.p[F.getHours()>=12?1:0];},P:function(F,E){return E.P[F.getHours()>=12?1:0];},s:function(F,E){return parseInt(F.getTime()/1000,10);},S:["getSeconds","0"],u:function(E){var F=E.getDay();return F===0?7:F;},U:function(H){var E=parseInt(C.formats.j(H),10);var G=6-H.getDay();var F=parseInt((E+G)/7,10);return A(F,0);},V:function(H){var G=parseInt(C.formats.W(H),10);var E=(new Date(""+H.getFullYear()+"/1/1")).getDay();var F=G+(E>4||E<=1?0:1);if(F===53&&(new Date(""+H.getFullYear()+"/12/31")).getDay()<4){F=1;}else{if(F===0){F=C.formats.V(new Date(""+(H.getFullYear()-1)+"/12/31"));}}return A(F,0);},w:"getDay",W:function(H){var E=parseInt(C.formats.j(H),10);var G=7-C.formats.u(H);var F=parseInt((E+G)/7,10);return A(F,0,10);},y:function(E){return A(E.getFullYear()%100,0);},Y:"getFullYear",z:function(G){var F=G.getTimezoneOffset();var E=A(parseInt(Math.abs(F/60),10),0);var I=A(Math.abs(F%60),0);return(F>0?"-":"+")+E+I;},Z:function(E){var F=E.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/,"$2").replace(/[a-z ]/g,"");if(F.length>4){F=C.formats.z(E);}return F;},"%":function(E){return"%";}},aggregates:{c:"locale",D:"%m/%d/%y",F:"%Y-%m-%d",h:"%b",n:"\n",r:"locale",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"},format:function(N,H){H=H||{};if(!D.Lang.isDate(N)){return D.Lang.isValue(N)?N:"";}var M=H.format||D.config.dateFormat,F=H.locale||D.config.locale,L=D.DataType.Date.Locale;F=F.replace(/_/g,"-");if(!L[F]){var G=F.replace(/-[a-zA-Z]+$/,"");if(G in L){F=G;}else{if(D.config.locale in L){F=D.config.locale;}else{F="en";}}}var J=L[F];var I=function(P,O){var Q=C.aggregates[O];return(Q==="locale"?J[O]:Q);};var E=function(P,O){var Q=C.formats[O];switch(D.Lang.type(Q)){case"string":return N[Q]();case"function":return Q.call(N,N,J);case"array":if(D.Lang.type(Q[0])==="string"){return A(N[Q[0]](),Q[1]);}default:return O;}};while(M.match(/%[cDFhnrRtTxX]/)){M=M.replace(/%([cDFhnrRtTxX])/g,I);}var K=M.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g,E);I=E=undefined;return K;}};D.mix(D.namespace("DataType.Date"),C);var B={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],r:"%I:%M:%S %p",x:"%d/%m/%y",X:"%T"};D.namespace("DataType.Date.Locale");D.DataType.Date.Locale["en"]=B;D.DataType.Date.Locale["en-US"]=D.merge(B,{c:"%a %d %b %Y %I:%M:%S %p %Z",x:"%m/%d/%Y",X:"%I:%M:%S %p"});D.DataType.Date.Locale["en-GB"]=D.merge(B,{r:"%l:%M:%S %P %Z"});D.DataType.Date.Locale["en-AU"]=D.merge(B);},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-date-format', function(Y) {
+
+/**
+ * The DataType Utility provides type-conversion and string-formatting
+ * convenience methods for various JavaScript object types.
+ *
+ * @module datatype
+ */
+
+/**
+ * Date submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date
+ */
+
+/**
+ * Format date submodule implements strftime formatters for javascript based on the
+ * Open Group specification defined at
+ * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+ * This implementation does not include modified conversion specifiers (i.e., Ex and Ox)
+ *
+ * @module datatype
+ * @submodule datatype-date-format
+ */
+
+/**
+ * DataType.Date provides a set of utility functions to operate against Date objects.
+ *
+ * @class DataType.Date
+ * @static
+ */
+
+/**
+ * Pad a number with leading spaces, zeroes or something else
+ * @method xPad
+ * @param x {Number} The number to be padded
+ * @param pad {String} The character to pad the number with
+ * @param r {Number} (optional) The base of the pad, eg, 10 implies to two digits, 100 implies to 3 digits.
+ * @private
+ */
+var xPad=function (x, pad, r)
+{
+ if(typeof r === "undefined")
+ {
+ r=10;
+ }
+ pad = pad.toString();
+ for( ; parseInt(x, 10)<r && r>1; r/=10) {
+ x = pad + x;
+ }
+ return x.toString();
+};
+
+/**
+ * Default date format.
+ *
+ * @for config
+ * @property dateFormat
+ * @type String
+ * @value "%Y-%m-%d"
+ */
+Y.config.dateFormat = Y.config.dateFormat || "%Y-%m-%d";
+
+/**
+ * Default locale for the YUI instance.
+ *
+ * @property locale
+ * @type String
+ * @value "en"
+ */
+Y.config.locale = Y.config.locale || "en";
+
+var Dt = {
+ formats: {
+ a: function (d, l) { return l.a[d.getDay()]; },
+ A: function (d, l) { return l.A[d.getDay()]; },
+ b: function (d, l) { return l.b[d.getMonth()]; },
+ B: function (d, l) { return l.B[d.getMonth()]; },
+ C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); },
+ d: ["getDate", "0"],
+ e: ["getDate", " "],
+ g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); },
+ G: function (d) {
+ var y = d.getFullYear();
+ var V = parseInt(Dt.formats.V(d), 10);
+ var W = parseInt(Dt.formats.W(d), 10);
+
+ if(W > V) {
+ y++;
+ } else if(W===0 && V>=52) {
+ y--;
+ }
+
+ return y;
+ },
+ H: ["getHours", "0"],
+ I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); },
+ j: function (d) {
+ var gmd_1 = new Date("" + d.getFullYear() + "/1/1 GMT");
+ var gmdate = new Date("" + d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDate() + " GMT");
+ var ms = gmdate - gmd_1;
+ var doy = parseInt(ms/60000/60/24, 10)+1;
+ return xPad(doy, 0, 100);
+ },
+ k: ["getHours", " "],
+ l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, " "); },
+ m: function (d) { return xPad(d.getMonth()+1, 0); },
+ M: ["getMinutes", "0"],
+ p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; },
+ P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; },
+ s: function (d, l) { return parseInt(d.getTime()/1000, 10); },
+ S: ["getSeconds", "0"],
+ u: function (d) { var dow = d.getDay(); return dow===0?7:dow; },
+ U: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 6-d.getDay();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0);
+ },
+ V: function (d) {
+ var woy = parseInt(Dt.formats.W(d), 10);
+ var dow1_1 = (new Date("" + d.getFullYear() + "/1/1")).getDay();
+ // First week is 01 and not 00 as in the case of %U and %W,
+ // so we add 1 to the final result except if day 1 of the year
+ // is a Monday (then %W returns 01).
+ // We also need to subtract 1 if the day 1 of the year is
+ // Friday-Sunday, so the resulting equation becomes:
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+ if(idow === 53 && (new Date("" + d.getFullYear() + "/12/31")).getDay() < 4)
+ {
+ idow = 1;
+ }
+ else if(idow === 0)
+ {
+ idow = Dt.formats.V(new Date("" + (d.getFullYear()-1) + "/12/31"));
+ }
+
+ return xPad(idow, 0);
+ },
+ w: "getDay",
+ W: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 7-Dt.formats.u(d);
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0, 10);
+ },
+ y: function (d) { return xPad(d.getFullYear()%100, 0); },
+ Y: "getFullYear",
+ z: function (d) {
+ var o = d.getTimezoneOffset();
+ var H = xPad(parseInt(Math.abs(o/60), 10), 0);
+ var M = xPad(Math.abs(o%60), 0);
+ return (o>0?"-":"+") + H + M;
+ },
+ Z: function (d) {
+ var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, "$2").replace(/[a-z ]/g, "");
+ if(tz.length > 4) {
+ tz = Dt.formats.z(d);
+ }
+ return tz;
+ },
+ "%": function (d) { return "%"; }
+ },
+
+ aggregates: {
+ c: "locale",
+ D: "%m/%d/%y",
+ F: "%Y-%m-%d",
+ h: "%b",
+ n: "\n",
+ r: "locale",
+ R: "%H:%M",
+ t: "\t",
+ T: "%H:%M:%S",
+ x: "locale",
+ X: "locale"
+ //"+": "%a %b %e %T %Z %Y"
+ },
+
+ /**
+ * Takes a native JavaScript Date and formats it as a string for display to user.
+ *
+ * @for DataType.Date
+ * @method format
+ * @param oDate {Date} Date.
+ * @param oConfig {Object} (Optional) Object literal of configuration values:
+ * <dl>
+ * <dt>format {String} (Optional)</dt>
+ * <dd>
+ * <p>
+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at
+ * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html</a>
+ * PHP added a few of its own, defined at <a href="http://www.php.net/strftime">http://www.php.net/strftime</a>
+ * </p>
+ * <p>
+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below.
+ * </p>
+ * <p>
+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat
+ * </p>
+ * <dl>
+ * <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd>
+ * <dt>%A</dt> <dd>full weekday name according to the current locale</dd>
+ * <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd>
+ * <dt>%B</dt> <dd>full month name according to the current locale</dd>
+ * <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd>
+ * <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd>
+ * <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd>
+ * <dt>%D</dt> <dd>same as %m/%d/%y</dd>
+ * <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")</dd>
+ * <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd>
+ * <dt>%g</dt> <dd>like %G, but without the century</dd>
+ * <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd>
+ * <dt>%h</dt> <dd>same as %b</dd>
+ * <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd>
+ * <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd>
+ * <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd>
+ * <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd>
+ * <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd>
+ * <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd>
+ * <dt>%M</dt> <dd>minute as a decimal number</dd>
+ * <dt>%n</dt> <dd>newline character</dd>
+ * <dt>%p</dt> <dd>either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale</dd>
+ * <dt>%P</dt> <dd>like %p, but lower case</dd>
+ * <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd>
+ * <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd>
+ * <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd>
+ * <dt>%S</dt> <dd>second as a decimal number</dd>
+ * <dt>%t</dt> <dd>tab character</dd>
+ * <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd>
+ * <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd>
+ * <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Sunday as the first day of the first week</dd>
+ * <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number,
+ * range 01 to 53, where week 1 is the first week that has at least 4 days
+ * in the current year, and with Monday as the first day of the week.</dd>
+ * <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd>
+ * <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Monday as the first day of the first week</dd>
+ * <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd>
+ * <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd>
+ * <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd>
+ * <dt>%Y</dt> <dd>year as a decimal number including the century</dd>
+ * <dt>%z</dt> <dd>numerical time zone representation</dd>
+ * <dt>%Z</dt> <dd>time zone name or abbreviation</dd>
+ * <dt>%%</dt> <dd>a literal "%" character</dd>
+ * </dl>
+ * </dd>
+ * <dt>locale {String} (Optional)</dt>
+ * <dd>
+ * The locale to use when displaying days of week, months of the year, and other locale specific
+ * strings. If not specified, this defaults to "en" (though this may be overridden by changing Y.config.locale).
+ * The following locales are built in:
+ * <dl>
+ * <dt>en</dt>
+ * <dd>English</dd>
+ * <dt>en-US</dt>
+ * <dd>US English</dd>
+ * <dt>en-GB</dt>
+ * <dd>British English</dd>
+ * <dt>en-AU</dt>
+ * <dd>Australian English (identical to British English)</dd>
+ * </dl>
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"].
+ * See Y.DataType.Date.Locale for more information.
+ * </dd>
+ * </dl>
+ * @return {String} Formatted date for display.
+ */
+ format : function (oDate, oConfig) {
+ oConfig = oConfig || {};
+
+ if(!Y.Lang.isDate(oDate)) {
+ return Y.Lang.isValue(oDate) ? oDate : "";
+ }
+
+ var format = oConfig.format || Y.config.dateFormat,
+ sLocale = oConfig.locale || Y.config.locale,
+ LOCALE = Y.DataType.Date.Locale;
+
+ sLocale = sLocale.replace(/_/g, "-");
+
+ // Make sure we have a definition for the requested locale, or default to en.
+ if(!LOCALE[sLocale]) {
+ var tmpLocale = sLocale.replace(/-[a-zA-Z]+$/, "");
+ if(tmpLocale in LOCALE) {
+ sLocale = tmpLocale;
+ } else if(Y.config.locale in LOCALE) {
+ sLocale = Y.config.locale;
+ } else {
+ sLocale = "en";
+ }
+ }
+
+ var aLocale = LOCALE[sLocale];
+
+ var replace_aggs = function (m0, m1) {
+ var f = Dt.aggregates[m1];
+ return (f === "locale" ? aLocale[m1] : f);
+ };
+
+ var replace_formats = function (m0, m1) {
+ var f = Dt.formats[m1];
+ switch(Y.Lang.type(f)) {
+ case "string": // string => built in date function
+ return oDate[f]();
+ case "function": // function => our own function
+ return f.call(oDate, oDate, aLocale);
+ case "array": // built in function with padding
+ if(Y.Lang.type(f[0]) === "string") {
+ return xPad(oDate[f[0]](), f[1]);
+ } // no break; (fall through to default:)
+ default:
+ return m1;
+ }
+ };
+
+ // First replace aggregates (run in a loop because an agg may be made up of other aggs)
+ while(format.match(/%[cDFhnrRtTxX]/)) {
+ format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs);
+ }
+
+ // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a)
+ var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats);
+
+ replace_aggs = replace_formats = undefined;
+
+ return str;
+ }
+};
+
+Y.mix(Y.namespace("DataType.Date"), Dt);
+
+/**
+ * @module datatype
+*/
+
+/**
+ * The Date.Locale class is a container for all localised date strings
+ * used by Y.DataType.Date. It is used internally, but may be extended
+ * to provide new date localisations.
+ *
+ * To create your own Locale, follow these steps:
+ * <ol>
+ * <li>Find an existing locale that matches closely with your needs</li>
+ * <li>Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing
+ * matches.</li>
+ * <li>Create your own class as an extension of the base class using
+ * Y.merge, and add your own localisations where needed.</li>
+ * </ol>
+ * See the Y.DataType.Date.Locale["en-US"] and Y.DataType.Date.Locale["en-GB"]
+ * classes which extend Y.DataType.Date.Locale["en"].
+ *
+ * For example, to implement locales for French french and Canadian french,
+ * we would do the following:
+ * <ol>
+ * <li>For French french, we have no existing similar locale, so use
+ * Y.DataType.Date.Locale["en"] as the base, and extend it:
+ * <pre>
+ * Y.DataType.Date.Locale["fr"] = Y.merge(Y.DataType.Date.Locale, {
+ * a: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
+ * A: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ * b: ["jan", "fév", "mar", "avr", "mai", "jun", "jui", "aoû", "sep", "oct", "nov", "déc"],
+ * B: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ * c: "%a %d %b %Y %T %Z",
+ * p: ["", ""],
+ * P: ["", ""],
+ * x: "%d.%m.%Y",
+ * X: "%T"
+ * });
+ * </pre>
+ * </li>
+ * <li>For Canadian french, we start with French french and change the meaning of \%x:
+ * <pre>
+ * Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
+ * x: "%Y-%m-%d"
+ * });
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * With that, you can use your new locales:
+ * <pre>
+ * var d = new Date("2008/04/22");
+ * Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * </pre>
+ * will return:
+ * <pre>
+ * mardi, 22 avril == 22.04.2008
+ * </pre>
+ * And
+ * <pre>
+ * Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * </pre>
+ * Will return:
+ * <pre>
+ * mardi, 22 avril == 2008-04-22
+ * </pre>
+ * @requires oop
+ * @class DataType.Date.Locale
+ * @static
+ */
+var YDateEn = {
+ a: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ A: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ b: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ B: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ c: "%a %d %b %Y %T %Z",
+ p: ["AM", "PM"],
+ P: ["am", "pm"],
+ r: "%I:%M:%S %p",
+ x: "%d/%m/%y",
+ X: "%T"
+};
+
+Y.namespace("DataType.Date.Locale");
+
+Y.DataType.Date.Locale["en"] = YDateEn;
+
+Y.DataType.Date.Locale["en-US"] = Y.merge(YDateEn, {
+ c: "%a %d %b %Y %I:%M:%S %p %Z",
+ x: "%m/%d/%Y",
+ X: "%I:%M:%S %p"
+});
+
+Y.DataType.Date.Locale["en-GB"] = Y.merge(YDateEn, {
+ r: "%l:%M:%S %P %Z"
+});
+Y.DataType.Date.Locale["en-AU"] = Y.merge(YDateEn);
+
+
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-date-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Date"),{parse:function(D){var C=null;if(!(A.isDate(D))){C=new Date(D);}else{return C;}if(A.isDate(C)&&(C!="Invalid Date")&&!isNaN(C)){return C;}else{return null;}}});B.namespace("Parsers").date=B.DataType.Date.parse;},"3.0.0");YUI.add("datatype-date-format",function(D){var A=function(E,G,F){if(typeof F==="undefined"){F=10;}G=G.toString();for(;parseInt(E,10)<F&&F>1;F/=10){E=G+E;}return E.toString();};D.config.dateFormat=D.config.dateFormat||"%Y-%m-%d";D.config.locale=D.config.locale||"en";var C={formats:{a:function(F,E){return E.a[F.getDay()];},A:function(F,E){return E.A[F.getDay()];},b:function(F,E){return E.b[F.getMonth()];},B:function(F,E){return E.B[F.getMonth()];},C:function(E){return A(parseInt(E.getFullYear()/100,10),0);},d:["getDate","0"],e:["getDate"," "],g:function(E){return A(parseInt(C.formats.G(E)%100,10),0);},G:function(G){var H=G.getFullYear();var F=parseInt(C.formats.V(G),10);var E=parseInt(C.formats.W(G),10);if(E>F){H++;}else{if(E===0&&F>=52){H--;}}return H;},H:["getHours","0"],I:function(F){var E=F.getHours()%12;return A(E===0?12:E,0);},j:function(I){var H=new Date(""+I.getFullYear()+"/1/1 GMT");var F=new Date(""+I.getFullYear()+"/"+(I.getMonth()+1)+"/"+I.getDate()+" GMT");var E=F-H;var G=parseInt(E/60000/60/24,10)+1;return A(G,0,100);},k:["getHours"," "],l:function(F){var E=F.getHours()%12;return A(E===0?12:E," ");},m:function(E){return A(E.getMonth()+1,0);},M:["getMinutes","0"],p:function(F,E){return E.p[F.getHours()>=12?1:0];},P:function(F,E){return E.P[F.getHours()>=12?1:0];},s:function(F,E){return parseInt(F.getTime()/1000,10);},S:["getSeconds","0"],u:function(E){var F=E.getDay();return F===0?7:F;},U:function(H){var E=parseInt(C.formats.j(H),10);var G=6-H.getDay();var F=parseInt((E+G)/7,10);return A(F,0);},V:function(H){var G=parseInt(C.formats.W(H),10);var E=(new Date(""+H.getFullYear()+"/1/1")).getDay();var F=G+(E>4||E<=1?0:1);if(F===53&&(new Date(""+H.getFullYear()+"/12/31")).getDay()<4){F=1;}else{if(F===0){F=C.formats.V(new Date(""+(H.getFullYear()-1)+"/12/31"));}}return A(F,0);},w:"getDay",W:function(H){var E=parseInt(C.formats.j(H),10);var G=7-C.formats.u(H);var F=parseInt((E+G)/7,10);return A(F,0,10);},y:function(E){return A(E.getFullYear()%100,0);},Y:"getFullYear",z:function(G){var F=G.getTimezoneOffset();var E=A(parseInt(Math.abs(F/60),10),0);var I=A(Math.abs(F%60),0);return(F>0?"-":"+")+E+I;},Z:function(E){var F=E.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/,"$2").replace(/[a-z ]/g,"");if(F.length>4){F=C.formats.z(E);}return F;},"%":function(E){return"%";}},aggregates:{c:"locale",D:"%m/%d/%y",F:"%Y-%m-%d",h:"%b",n:"\n",r:"locale",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"},format:function(N,H){H=H||{};if(!D.Lang.isDate(N)){return D.Lang.isValue(N)?N:"";}var M=H.format||D.config.dateFormat,F=H.locale||D.config.locale,L=D.DataType.Date.Locale;F=F.replace(/_/g,"-");if(!L[F]){var G=F.replace(/-[a-zA-Z]+$/,"");if(G in L){F=G;}else{if(D.config.locale in L){F=D.config.locale;}else{F="en";}}}var J=L[F];var I=function(P,O){var Q=C.aggregates[O];return(Q==="locale"?J[O]:Q);};var E=function(P,O){var Q=C.formats[O];switch(D.Lang.type(Q)){case"string":return N[Q]();case"function":return Q.call(N,N,J);case"array":if(D.Lang.type(Q[0])==="string"){return A(N[Q[0]](),Q[1]);}default:return O;}};while(M.match(/%[cDFhnrRtTxX]/)){M=M.replace(/%([cDFhnrRtTxX])/g,I);}var K=M.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g,E);I=E=undefined;return K;}};D.mix(D.namespace("DataType.Date"),C);var B={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],r:"%I:%M:%S %p",x:"%d/%m/%y",X:"%T"};D.namespace("DataType.Date.Locale");D.DataType.Date.Locale["en"]=B;D.DataType.Date.Locale["en-US"]=D.merge(B,{c:"%a %d %b %Y %I:%M:%S %p %Z",x:"%m/%d/%Y",X:"%I:%M:%S %p"});D.DataType.Date.Locale["en-GB"]=D.merge(B,{r:"%l:%M:%S %P %Z"});D.DataType.Date.Locale["en-AU"]=D.merge(B);},"3.0.0");YUI.add("datatype-date",function(A){},"3.0.0",{use:["datatype-date-parse","datatype-date-format"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-date-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date-parse
+ * @for DataType.Date
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Date"), {
+ /**
+ * Converts data to type Date.
+ *
+ * @method parse
+ * @param data {String | Number} Data to convert. Values supported by the Date constructor are supported.
+ * @return {Date} A Date, or null.
+ */
+ parse: function(data) {
+ var date = null;
+
+ //Convert to date
+ if(!(LANG.isDate(data))) {
+ date = new Date(data);
+ }
+ else {
+ return date;
+ }
+
+ // Validate
+ if(LANG.isDate(date) && (date != "Invalid Date") && !isNaN(date)) { // Workaround for bug 2527965
+ return date;
+ }
+ else {
+ Y.log("Could not convert data " + LANG.dump(date) + " to type Date", "warn", "date");
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").date = Y.DataType.Date.parse;
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-date-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Date"),{parse:function(D){var C=null;if(!(A.isDate(D))){C=new Date(D);}else{return C;}if(A.isDate(C)&&(C!="Invalid Date")&&!isNaN(C)){return C;}else{return null;}}});B.namespace("Parsers").date=B.DataType.Date.parse;},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-date-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date-parse
+ * @for DataType.Date
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Date"), {
+ /**
+ * Converts data to type Date.
+ *
+ * @method parse
+ * @param data {String | Number} Data to convert. Values supported by the Date constructor are supported.
+ * @return {Date} A Date, or null.
+ */
+ parse: function(data) {
+ var date = null;
+
+ //Convert to date
+ if(!(LANG.isDate(data))) {
+ date = new Date(data);
+ }
+ else {
+ return date;
+ }
+
+ // Validate
+ if(LANG.isDate(date) && (date != "Invalid Date") && !isNaN(date)) { // Workaround for bug 2527965
+ return date;
+ }
+ else {
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").date = Y.DataType.Date.parse;
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-date-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date-parse
+ * @for DataType.Date
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Date"), {
+ /**
+ * Converts data to type Date.
+ *
+ * @method parse
+ * @param data {String | Number} Data to convert. Values supported by the Date constructor are supported.
+ * @return {Date} A Date, or null.
+ */
+ parse: function(data) {
+ var date = null;
+
+ //Convert to date
+ if(!(LANG.isDate(data))) {
+ date = new Date(data);
+ }
+ else {
+ return date;
+ }
+
+ // Validate
+ if(LANG.isDate(date) && (date != "Invalid Date") && !isNaN(date)) { // Workaround for bug 2527965
+ return date;
+ }
+ else {
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").date = Y.DataType.Date.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-date-format', function(Y) {
+
+/**
+ * The DataType Utility provides type-conversion and string-formatting
+ * convenience methods for various JavaScript object types.
+ *
+ * @module datatype
+ */
+
+/**
+ * Date submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date
+ */
+
+/**
+ * Format date submodule implements strftime formatters for javascript based on the
+ * Open Group specification defined at
+ * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+ * This implementation does not include modified conversion specifiers (i.e., Ex and Ox)
+ *
+ * @module datatype
+ * @submodule datatype-date-format
+ */
+
+/**
+ * DataType.Date provides a set of utility functions to operate against Date objects.
+ *
+ * @class DataType.Date
+ * @static
+ */
+
+/**
+ * Pad a number with leading spaces, zeroes or something else
+ * @method xPad
+ * @param x {Number} The number to be padded
+ * @param pad {String} The character to pad the number with
+ * @param r {Number} (optional) The base of the pad, eg, 10 implies to two digits, 100 implies to 3 digits.
+ * @private
+ */
+var xPad=function (x, pad, r)
+{
+ if(typeof r === "undefined")
+ {
+ r=10;
+ }
+ pad = pad.toString();
+ for( ; parseInt(x, 10)<r && r>1; r/=10) {
+ x = pad + x;
+ }
+ return x.toString();
+};
+
+/**
+ * Default date format.
+ *
+ * @for config
+ * @property dateFormat
+ * @type String
+ * @value "%Y-%m-%d"
+ */
+Y.config.dateFormat = Y.config.dateFormat || "%Y-%m-%d";
+
+/**
+ * Default locale for the YUI instance.
+ *
+ * @property locale
+ * @type String
+ * @value "en"
+ */
+Y.config.locale = Y.config.locale || "en";
+
+var Dt = {
+ formats: {
+ a: function (d, l) { return l.a[d.getDay()]; },
+ A: function (d, l) { return l.A[d.getDay()]; },
+ b: function (d, l) { return l.b[d.getMonth()]; },
+ B: function (d, l) { return l.B[d.getMonth()]; },
+ C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); },
+ d: ["getDate", "0"],
+ e: ["getDate", " "],
+ g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); },
+ G: function (d) {
+ var y = d.getFullYear();
+ var V = parseInt(Dt.formats.V(d), 10);
+ var W = parseInt(Dt.formats.W(d), 10);
+
+ if(W > V) {
+ y++;
+ } else if(W===0 && V>=52) {
+ y--;
+ }
+
+ return y;
+ },
+ H: ["getHours", "0"],
+ I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); },
+ j: function (d) {
+ var gmd_1 = new Date("" + d.getFullYear() + "/1/1 GMT");
+ var gmdate = new Date("" + d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDate() + " GMT");
+ var ms = gmdate - gmd_1;
+ var doy = parseInt(ms/60000/60/24, 10)+1;
+ return xPad(doy, 0, 100);
+ },
+ k: ["getHours", " "],
+ l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, " "); },
+ m: function (d) { return xPad(d.getMonth()+1, 0); },
+ M: ["getMinutes", "0"],
+ p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; },
+ P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; },
+ s: function (d, l) { return parseInt(d.getTime()/1000, 10); },
+ S: ["getSeconds", "0"],
+ u: function (d) { var dow = d.getDay(); return dow===0?7:dow; },
+ U: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 6-d.getDay();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0);
+ },
+ V: function (d) {
+ var woy = parseInt(Dt.formats.W(d), 10);
+ var dow1_1 = (new Date("" + d.getFullYear() + "/1/1")).getDay();
+ // First week is 01 and not 00 as in the case of %U and %W,
+ // so we add 1 to the final result except if day 1 of the year
+ // is a Monday (then %W returns 01).
+ // We also need to subtract 1 if the day 1 of the year is
+ // Friday-Sunday, so the resulting equation becomes:
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+ if(idow === 53 && (new Date("" + d.getFullYear() + "/12/31")).getDay() < 4)
+ {
+ idow = 1;
+ }
+ else if(idow === 0)
+ {
+ idow = Dt.formats.V(new Date("" + (d.getFullYear()-1) + "/12/31"));
+ }
+
+ return xPad(idow, 0);
+ },
+ w: "getDay",
+ W: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 7-Dt.formats.u(d);
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0, 10);
+ },
+ y: function (d) { return xPad(d.getFullYear()%100, 0); },
+ Y: "getFullYear",
+ z: function (d) {
+ var o = d.getTimezoneOffset();
+ var H = xPad(parseInt(Math.abs(o/60), 10), 0);
+ var M = xPad(Math.abs(o%60), 0);
+ return (o>0?"-":"+") + H + M;
+ },
+ Z: function (d) {
+ var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, "$2").replace(/[a-z ]/g, "");
+ if(tz.length > 4) {
+ tz = Dt.formats.z(d);
+ }
+ return tz;
+ },
+ "%": function (d) { return "%"; }
+ },
+
+ aggregates: {
+ c: "locale",
+ D: "%m/%d/%y",
+ F: "%Y-%m-%d",
+ h: "%b",
+ n: "\n",
+ r: "locale",
+ R: "%H:%M",
+ t: "\t",
+ T: "%H:%M:%S",
+ x: "locale",
+ X: "locale"
+ //"+": "%a %b %e %T %Z %Y"
+ },
+
+ /**
+ * Takes a native JavaScript Date and formats it as a string for display to user.
+ *
+ * @for DataType.Date
+ * @method format
+ * @param oDate {Date} Date.
+ * @param oConfig {Object} (Optional) Object literal of configuration values:
+ * <dl>
+ * <dt>format {String} (Optional)</dt>
+ * <dd>
+ * <p>
+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at
+ * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html</a>
+ * PHP added a few of its own, defined at <a href="http://www.php.net/strftime">http://www.php.net/strftime</a>
+ * </p>
+ * <p>
+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below.
+ * </p>
+ * <p>
+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat
+ * </p>
+ * <dl>
+ * <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd>
+ * <dt>%A</dt> <dd>full weekday name according to the current locale</dd>
+ * <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd>
+ * <dt>%B</dt> <dd>full month name according to the current locale</dd>
+ * <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd>
+ * <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd>
+ * <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd>
+ * <dt>%D</dt> <dd>same as %m/%d/%y</dd>
+ * <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")</dd>
+ * <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd>
+ * <dt>%g</dt> <dd>like %G, but without the century</dd>
+ * <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd>
+ * <dt>%h</dt> <dd>same as %b</dd>
+ * <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd>
+ * <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd>
+ * <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd>
+ * <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd>
+ * <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd>
+ * <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd>
+ * <dt>%M</dt> <dd>minute as a decimal number</dd>
+ * <dt>%n</dt> <dd>newline character</dd>
+ * <dt>%p</dt> <dd>either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale</dd>
+ * <dt>%P</dt> <dd>like %p, but lower case</dd>
+ * <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd>
+ * <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd>
+ * <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd>
+ * <dt>%S</dt> <dd>second as a decimal number</dd>
+ * <dt>%t</dt> <dd>tab character</dd>
+ * <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd>
+ * <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd>
+ * <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Sunday as the first day of the first week</dd>
+ * <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number,
+ * range 01 to 53, where week 1 is the first week that has at least 4 days
+ * in the current year, and with Monday as the first day of the week.</dd>
+ * <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd>
+ * <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Monday as the first day of the first week</dd>
+ * <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd>
+ * <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd>
+ * <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd>
+ * <dt>%Y</dt> <dd>year as a decimal number including the century</dd>
+ * <dt>%z</dt> <dd>numerical time zone representation</dd>
+ * <dt>%Z</dt> <dd>time zone name or abbreviation</dd>
+ * <dt>%%</dt> <dd>a literal "%" character</dd>
+ * </dl>
+ * </dd>
+ * <dt>locale {String} (Optional)</dt>
+ * <dd>
+ * The locale to use when displaying days of week, months of the year, and other locale specific
+ * strings. If not specified, this defaults to "en" (though this may be overridden by changing Y.config.locale).
+ * The following locales are built in:
+ * <dl>
+ * <dt>en</dt>
+ * <dd>English</dd>
+ * <dt>en-US</dt>
+ * <dd>US English</dd>
+ * <dt>en-GB</dt>
+ * <dd>British English</dd>
+ * <dt>en-AU</dt>
+ * <dd>Australian English (identical to British English)</dd>
+ * </dl>
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"].
+ * See Y.DataType.Date.Locale for more information.
+ * </dd>
+ * </dl>
+ * @return {String} Formatted date for display.
+ */
+ format : function (oDate, oConfig) {
+ oConfig = oConfig || {};
+
+ if(!Y.Lang.isDate(oDate)) {
+ return Y.Lang.isValue(oDate) ? oDate : "";
+ }
+
+ var format = oConfig.format || Y.config.dateFormat,
+ sLocale = oConfig.locale || Y.config.locale,
+ LOCALE = Y.DataType.Date.Locale;
+
+ sLocale = sLocale.replace(/_/g, "-");
+
+ // Make sure we have a definition for the requested locale, or default to en.
+ if(!LOCALE[sLocale]) {
+ var tmpLocale = sLocale.replace(/-[a-zA-Z]+$/, "");
+ if(tmpLocale in LOCALE) {
+ sLocale = tmpLocale;
+ } else if(Y.config.locale in LOCALE) {
+ sLocale = Y.config.locale;
+ } else {
+ sLocale = "en";
+ }
+ }
+
+ var aLocale = LOCALE[sLocale];
+
+ var replace_aggs = function (m0, m1) {
+ var f = Dt.aggregates[m1];
+ return (f === "locale" ? aLocale[m1] : f);
+ };
+
+ var replace_formats = function (m0, m1) {
+ var f = Dt.formats[m1];
+ switch(Y.Lang.type(f)) {
+ case "string": // string => built in date function
+ return oDate[f]();
+ case "function": // function => our own function
+ return f.call(oDate, oDate, aLocale);
+ case "array": // built in function with padding
+ if(Y.Lang.type(f[0]) === "string") {
+ return xPad(oDate[f[0]](), f[1]);
+ } // no break; (fall through to default:)
+ default:
+ return m1;
+ }
+ };
+
+ // First replace aggregates (run in a loop because an agg may be made up of other aggs)
+ while(format.match(/%[cDFhnrRtTxX]/)) {
+ format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs);
+ }
+
+ // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a)
+ var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats);
+
+ replace_aggs = replace_formats = undefined;
+
+ return str;
+ }
+};
+
+Y.mix(Y.namespace("DataType.Date"), Dt);
+
+/**
+ * @module datatype
+*/
+
+/**
+ * The Date.Locale class is a container for all localised date strings
+ * used by Y.DataType.Date. It is used internally, but may be extended
+ * to provide new date localisations.
+ *
+ * To create your own Locale, follow these steps:
+ * <ol>
+ * <li>Find an existing locale that matches closely with your needs</li>
+ * <li>Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing
+ * matches.</li>
+ * <li>Create your own class as an extension of the base class using
+ * Y.merge, and add your own localisations where needed.</li>
+ * </ol>
+ * See the Y.DataType.Date.Locale["en-US"] and Y.DataType.Date.Locale["en-GB"]
+ * classes which extend Y.DataType.Date.Locale["en"].
+ *
+ * For example, to implement locales for French french and Canadian french,
+ * we would do the following:
+ * <ol>
+ * <li>For French french, we have no existing similar locale, so use
+ * Y.DataType.Date.Locale["en"] as the base, and extend it:
+ * <pre>
+ * Y.DataType.Date.Locale["fr"] = Y.merge(Y.DataType.Date.Locale, {
+ * a: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
+ * A: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ * b: ["jan", "fév", "mar", "avr", "mai", "jun", "jui", "aoû", "sep", "oct", "nov", "déc"],
+ * B: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ * c: "%a %d %b %Y %T %Z",
+ * p: ["", ""],
+ * P: ["", ""],
+ * x: "%d.%m.%Y",
+ * X: "%T"
+ * });
+ * </pre>
+ * </li>
+ * <li>For Canadian french, we start with French french and change the meaning of \%x:
+ * <pre>
+ * Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
+ * x: "%Y-%m-%d"
+ * });
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * With that, you can use your new locales:
+ * <pre>
+ * var d = new Date("2008/04/22");
+ * Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * </pre>
+ * will return:
+ * <pre>
+ * mardi, 22 avril == 22.04.2008
+ * </pre>
+ * And
+ * <pre>
+ * Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * </pre>
+ * Will return:
+ * <pre>
+ * mardi, 22 avril == 2008-04-22
+ * </pre>
+ * @requires oop
+ * @class DataType.Date.Locale
+ * @static
+ */
+var YDateEn = {
+ a: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ A: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ b: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ B: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ c: "%a %d %b %Y %T %Z",
+ p: ["AM", "PM"],
+ P: ["am", "pm"],
+ r: "%I:%M:%S %p",
+ x: "%d/%m/%y",
+ X: "%T"
+};
+
+Y.namespace("DataType.Date.Locale");
+
+Y.DataType.Date.Locale["en"] = YDateEn;
+
+Y.DataType.Date.Locale["en-US"] = Y.merge(YDateEn, {
+ c: "%a %d %b %Y %I:%M:%S %p %Z",
+ x: "%m/%d/%Y",
+ X: "%I:%M:%S %p"
+});
+
+Y.DataType.Date.Locale["en-GB"] = Y.merge(YDateEn, {
+ r: "%l:%M:%S %P %Z"
+});
+Y.DataType.Date.Locale["en-AU"] = Y.merge(YDateEn);
+
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-date', function(Y){}, '3.0.0' ,{use:['datatype-date-parse', 'datatype-date-format']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-parse
+ * @for DataType.Number
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Converts data to type Number.
+ *
+ * @method parse
+ * @param data {String | Number | Boolean} Data to convert. The following
+ * values return as null: null, undefined, NaN, "".
+ * @return {Number} A number, or null.
+ */
+ parse: function(data) {
+ var number = (data === null) ? data : +data;
+ if(LANG.isNumber(number)) {
+ return number;
+ }
+ else {
+ Y.log("Could not parse data " + Y.dump(data) + " to type Number", "warn", "datatype-number");
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").number = Y.DataType.Number.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-number-format', function(Y) {
+
+/**
+ * Number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number
+ */
+
+/**
+ * Format number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-format
+ */
+
+/**
+ * DataType.Number provides a set of utility functions to operate against Number objects.
+ *
+ * @class DataType.Number
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Takes a Number and formats to string for display to user.
+ *
+ * @method format
+ * @param data {Number} Number.
+ * @param config {Object} (Optional) Optional configuration values:
+ * <dl>
+ * <dt>prefix {String}</dd>
+ * <dd>String prepended before each number, like a currency designator "$"</dd>
+ * <dt>decimalPlaces {Number}</dd>
+ * <dd>Number of decimal places to round. Must be a number 0 to 20.</dd>
+ * <dt>decimalSeparator {String}</dd>
+ * <dd>Decimal separator</dd>
+ * <dt>thousandsSeparator {String}</dd>
+ * <dd>Thousands separator</dd>
+ * <dt>suffix {String}</dd>
+ * <dd>String appended after each number, like " items" (note the space)</dd>
+ * </dl>
+ * @return {String} Formatted number for display. Note, the following values
+ * return as "": null, undefined, NaN, "".
+ */
+ format: function(data, config) {
+ if(LANG.isNumber(data)) {
+ config = config || {};
+
+ var isNeg = (data < 0),
+ output = data + "",
+ decPlaces = config.decimalPlaces,
+ decSep = config.decimalSeparator || ".",
+ thouSep = config.thousandsSeparator,
+ decIndex,
+ newOutput, count, i;
+
+ // Decimal precision
+ if(LANG.isNumber(decPlaces) && (decPlaces >= 0) && (decPlaces <= 20)) {
+ // Round to the correct decimal place
+ output = data.toFixed(decPlaces);
+ }
+
+ // Decimal separator
+ if(decSep !== "."){
+ output = output.replace(".", decSep);
+ }
+
+ // Add the thousands separator
+ if(thouSep) {
+ // Find the dot or where it would be
+ decIndex = output.lastIndexOf(decSep);
+ decIndex = (decIndex > -1) ? decIndex : output.length;
+ // Start with the dot and everything to the right
+ newOutput = output.substring(decIndex);
+ // Working left, every third time add a separator, every time add a digit
+ for (count = 0, i=decIndex; i>0; i--) {
+ if ((count%3 === 0) && (i !== decIndex) && (!isNeg || (i > 1))) {
+ newOutput = thouSep + newOutput;
+ }
+ newOutput = output.charAt(i-1) + newOutput;
+ count++;
+ }
+ output = newOutput;
+ }
+
+ // Prepend prefix
+ output = (config.prefix) ? config.prefix + output : output;
+
+ // Append suffix
+ output = (config.suffix) ? output + config.suffix : output;
+
+ return output;
+ }
+ // Not a Number, just return as string
+ else {
+ Y.log("Could not format data " + Y.dump(data) + " from type Number", "warn", "datatype-number");
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+});
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-number', function(Y){}, '3.0.0' ,{use:['datatype-number-parse', 'datatype-number-format']});
+
+
+YUI.add('datatype-date-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date-parse
+ * @for DataType.Date
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Date"), {
+ /**
+ * Converts data to type Date.
+ *
+ * @method parse
+ * @param data {String | Number} Data to convert. Values supported by the Date constructor are supported.
+ * @return {Date} A Date, or null.
+ */
+ parse: function(data) {
+ var date = null;
+
+ //Convert to date
+ if(!(LANG.isDate(data))) {
+ date = new Date(data);
+ }
+ else {
+ return date;
+ }
+
+ // Validate
+ if(LANG.isDate(date) && (date != "Invalid Date") && !isNaN(date)) { // Workaround for bug 2527965
+ return date;
+ }
+ else {
+ Y.log("Could not convert data " + LANG.dump(date) + " to type Date", "warn", "date");
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").date = Y.DataType.Date.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-date-format', function(Y) {
+
+/**
+ * The DataType Utility provides type-conversion and string-formatting
+ * convenience methods for various JavaScript object types.
+ *
+ * @module datatype
+ */
+
+/**
+ * Date submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date
+ */
+
+/**
+ * Format date submodule implements strftime formatters for javascript based on the
+ * Open Group specification defined at
+ * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+ * This implementation does not include modified conversion specifiers (i.e., Ex and Ox)
+ *
+ * @module datatype
+ * @submodule datatype-date-format
+ */
+
+/**
+ * DataType.Date provides a set of utility functions to operate against Date objects.
+ *
+ * @class DataType.Date
+ * @static
+ */
+
+/**
+ * Pad a number with leading spaces, zeroes or something else
+ * @method xPad
+ * @param x {Number} The number to be padded
+ * @param pad {String} The character to pad the number with
+ * @param r {Number} (optional) The base of the pad, eg, 10 implies to two digits, 100 implies to 3 digits.
+ * @private
+ */
+var xPad=function (x, pad, r)
+{
+ if(typeof r === "undefined")
+ {
+ r=10;
+ }
+ pad = pad.toString();
+ for( ; parseInt(x, 10)<r && r>1; r/=10) {
+ x = pad + x;
+ }
+ return x.toString();
+};
+
+/**
+ * Default date format.
+ *
+ * @for config
+ * @property dateFormat
+ * @type String
+ * @value "%Y-%m-%d"
+ */
+Y.config.dateFormat = Y.config.dateFormat || "%Y-%m-%d";
+
+/**
+ * Default locale for the YUI instance.
+ *
+ * @property locale
+ * @type String
+ * @value "en"
+ */
+Y.config.locale = Y.config.locale || "en";
+
+var Dt = {
+ formats: {
+ a: function (d, l) { return l.a[d.getDay()]; },
+ A: function (d, l) { return l.A[d.getDay()]; },
+ b: function (d, l) { return l.b[d.getMonth()]; },
+ B: function (d, l) { return l.B[d.getMonth()]; },
+ C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); },
+ d: ["getDate", "0"],
+ e: ["getDate", " "],
+ g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); },
+ G: function (d) {
+ var y = d.getFullYear();
+ var V = parseInt(Dt.formats.V(d), 10);
+ var W = parseInt(Dt.formats.W(d), 10);
+
+ if(W > V) {
+ y++;
+ } else if(W===0 && V>=52) {
+ y--;
+ }
+
+ return y;
+ },
+ H: ["getHours", "0"],
+ I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); },
+ j: function (d) {
+ var gmd_1 = new Date("" + d.getFullYear() + "/1/1 GMT");
+ var gmdate = new Date("" + d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDate() + " GMT");
+ var ms = gmdate - gmd_1;
+ var doy = parseInt(ms/60000/60/24, 10)+1;
+ return xPad(doy, 0, 100);
+ },
+ k: ["getHours", " "],
+ l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, " "); },
+ m: function (d) { return xPad(d.getMonth()+1, 0); },
+ M: ["getMinutes", "0"],
+ p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; },
+ P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; },
+ s: function (d, l) { return parseInt(d.getTime()/1000, 10); },
+ S: ["getSeconds", "0"],
+ u: function (d) { var dow = d.getDay(); return dow===0?7:dow; },
+ U: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 6-d.getDay();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0);
+ },
+ V: function (d) {
+ var woy = parseInt(Dt.formats.W(d), 10);
+ var dow1_1 = (new Date("" + d.getFullYear() + "/1/1")).getDay();
+ // First week is 01 and not 00 as in the case of %U and %W,
+ // so we add 1 to the final result except if day 1 of the year
+ // is a Monday (then %W returns 01).
+ // We also need to subtract 1 if the day 1 of the year is
+ // Friday-Sunday, so the resulting equation becomes:
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+ if(idow === 53 && (new Date("" + d.getFullYear() + "/12/31")).getDay() < 4)
+ {
+ idow = 1;
+ }
+ else if(idow === 0)
+ {
+ idow = Dt.formats.V(new Date("" + (d.getFullYear()-1) + "/12/31"));
+ }
+
+ return xPad(idow, 0);
+ },
+ w: "getDay",
+ W: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 7-Dt.formats.u(d);
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0, 10);
+ },
+ y: function (d) { return xPad(d.getFullYear()%100, 0); },
+ Y: "getFullYear",
+ z: function (d) {
+ var o = d.getTimezoneOffset();
+ var H = xPad(parseInt(Math.abs(o/60), 10), 0);
+ var M = xPad(Math.abs(o%60), 0);
+ return (o>0?"-":"+") + H + M;
+ },
+ Z: function (d) {
+ var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, "$2").replace(/[a-z ]/g, "");
+ if(tz.length > 4) {
+ tz = Dt.formats.z(d);
+ }
+ return tz;
+ },
+ "%": function (d) { return "%"; }
+ },
+
+ aggregates: {
+ c: "locale",
+ D: "%m/%d/%y",
+ F: "%Y-%m-%d",
+ h: "%b",
+ n: "\n",
+ r: "locale",
+ R: "%H:%M",
+ t: "\t",
+ T: "%H:%M:%S",
+ x: "locale",
+ X: "locale"
+ //"+": "%a %b %e %T %Z %Y"
+ },
+
+ /**
+ * Takes a native JavaScript Date and formats it as a string for display to user.
+ *
+ * @for DataType.Date
+ * @method format
+ * @param oDate {Date} Date.
+ * @param oConfig {Object} (Optional) Object literal of configuration values:
+ * <dl>
+ * <dt>format {String} (Optional)</dt>
+ * <dd>
+ * <p>
+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at
+ * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html</a>
+ * PHP added a few of its own, defined at <a href="http://www.php.net/strftime">http://www.php.net/strftime</a>
+ * </p>
+ * <p>
+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below.
+ * </p>
+ * <p>
+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat
+ * </p>
+ * <dl>
+ * <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd>
+ * <dt>%A</dt> <dd>full weekday name according to the current locale</dd>
+ * <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd>
+ * <dt>%B</dt> <dd>full month name according to the current locale</dd>
+ * <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd>
+ * <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd>
+ * <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd>
+ * <dt>%D</dt> <dd>same as %m/%d/%y</dd>
+ * <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")</dd>
+ * <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd>
+ * <dt>%g</dt> <dd>like %G, but without the century</dd>
+ * <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd>
+ * <dt>%h</dt> <dd>same as %b</dd>
+ * <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd>
+ * <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd>
+ * <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd>
+ * <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd>
+ * <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd>
+ * <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd>
+ * <dt>%M</dt> <dd>minute as a decimal number</dd>
+ * <dt>%n</dt> <dd>newline character</dd>
+ * <dt>%p</dt> <dd>either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale</dd>
+ * <dt>%P</dt> <dd>like %p, but lower case</dd>
+ * <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd>
+ * <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd>
+ * <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd>
+ * <dt>%S</dt> <dd>second as a decimal number</dd>
+ * <dt>%t</dt> <dd>tab character</dd>
+ * <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd>
+ * <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd>
+ * <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Sunday as the first day of the first week</dd>
+ * <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number,
+ * range 01 to 53, where week 1 is the first week that has at least 4 days
+ * in the current year, and with Monday as the first day of the week.</dd>
+ * <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd>
+ * <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Monday as the first day of the first week</dd>
+ * <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd>
+ * <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd>
+ * <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd>
+ * <dt>%Y</dt> <dd>year as a decimal number including the century</dd>
+ * <dt>%z</dt> <dd>numerical time zone representation</dd>
+ * <dt>%Z</dt> <dd>time zone name or abbreviation</dd>
+ * <dt>%%</dt> <dd>a literal "%" character</dd>
+ * </dl>
+ * </dd>
+ * <dt>locale {String} (Optional)</dt>
+ * <dd>
+ * The locale to use when displaying days of week, months of the year, and other locale specific
+ * strings. If not specified, this defaults to "en" (though this may be overridden by changing Y.config.locale).
+ * The following locales are built in:
+ * <dl>
+ * <dt>en</dt>
+ * <dd>English</dd>
+ * <dt>en-US</dt>
+ * <dd>US English</dd>
+ * <dt>en-GB</dt>
+ * <dd>British English</dd>
+ * <dt>en-AU</dt>
+ * <dd>Australian English (identical to British English)</dd>
+ * </dl>
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"].
+ * See Y.DataType.Date.Locale for more information.
+ * </dd>
+ * </dl>
+ * @return {String} Formatted date for display.
+ */
+ format : function (oDate, oConfig) {
+ oConfig = oConfig || {};
+
+ if(!Y.Lang.isDate(oDate)) {
+ Y.log("format called without a date", "WARN", "datatype-date");
+ return Y.Lang.isValue(oDate) ? oDate : "";
+ }
+
+ var format = oConfig.format || Y.config.dateFormat,
+ sLocale = oConfig.locale || Y.config.locale,
+ LOCALE = Y.DataType.Date.Locale;
+
+ sLocale = sLocale.replace(/_/g, "-");
+
+ // Make sure we have a definition for the requested locale, or default to en.
+ if(!LOCALE[sLocale]) {
+ Y.log("selected locale " + sLocale + " not found, trying alternatives", "WARN", "datatype-date");
+ var tmpLocale = sLocale.replace(/-[a-zA-Z]+$/, "");
+ if(tmpLocale in LOCALE) {
+ sLocale = tmpLocale;
+ } else if(Y.config.locale in LOCALE) {
+ sLocale = Y.config.locale;
+ } else {
+ sLocale = "en";
+ }
+ Y.log("falling back to " + sLocale, "INFO", "datatype-date");
+ }
+
+ var aLocale = LOCALE[sLocale];
+
+ var replace_aggs = function (m0, m1) {
+ var f = Dt.aggregates[m1];
+ return (f === "locale" ? aLocale[m1] : f);
+ };
+
+ var replace_formats = function (m0, m1) {
+ var f = Dt.formats[m1];
+ switch(Y.Lang.type(f)) {
+ case "string": // string => built in date function
+ return oDate[f]();
+ case "function": // function => our own function
+ return f.call(oDate, oDate, aLocale);
+ case "array": // built in function with padding
+ if(Y.Lang.type(f[0]) === "string") {
+ return xPad(oDate[f[0]](), f[1]);
+ } // no break; (fall through to default:)
+ default:
+ Y.log("unrecognised replacement type, please file a bug (format: " + oConfig.format || Y.config.dateFormat + ")", "WARN", "datatype-date");
+ return m1;
+ }
+ };
+
+ // First replace aggregates (run in a loop because an agg may be made up of other aggs)
+ while(format.match(/%[cDFhnrRtTxX]/)) {
+ format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs);
+ }
+
+ // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a)
+ var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats);
+
+ replace_aggs = replace_formats = undefined;
+
+ return str;
+ }
+};
+
+Y.mix(Y.namespace("DataType.Date"), Dt);
+
+/**
+ * @module datatype
+*/
+
+/**
+ * The Date.Locale class is a container for all localised date strings
+ * used by Y.DataType.Date. It is used internally, but may be extended
+ * to provide new date localisations.
+ *
+ * To create your own Locale, follow these steps:
+ * <ol>
+ * <li>Find an existing locale that matches closely with your needs</li>
+ * <li>Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing
+ * matches.</li>
+ * <li>Create your own class as an extension of the base class using
+ * Y.merge, and add your own localisations where needed.</li>
+ * </ol>
+ * See the Y.DataType.Date.Locale["en-US"] and Y.DataType.Date.Locale["en-GB"]
+ * classes which extend Y.DataType.Date.Locale["en"].
+ *
+ * For example, to implement locales for French french and Canadian french,
+ * we would do the following:
+ * <ol>
+ * <li>For French french, we have no existing similar locale, so use
+ * Y.DataType.Date.Locale["en"] as the base, and extend it:
+ * <pre>
+ * Y.DataType.Date.Locale["fr"] = Y.merge(Y.DataType.Date.Locale, {
+ * a: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
+ * A: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ * b: ["jan", "fév", "mar", "avr", "mai", "jun", "jui", "aoû", "sep", "oct", "nov", "déc"],
+ * B: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ * c: "%a %d %b %Y %T %Z",
+ * p: ["", ""],
+ * P: ["", ""],
+ * x: "%d.%m.%Y",
+ * X: "%T"
+ * });
+ * </pre>
+ * </li>
+ * <li>For Canadian french, we start with French french and change the meaning of \%x:
+ * <pre>
+ * Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
+ * x: "%Y-%m-%d"
+ * });
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * With that, you can use your new locales:
+ * <pre>
+ * var d = new Date("2008/04/22");
+ * Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * </pre>
+ * will return:
+ * <pre>
+ * mardi, 22 avril == 22.04.2008
+ * </pre>
+ * And
+ * <pre>
+ * Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * </pre>
+ * Will return:
+ * <pre>
+ * mardi, 22 avril == 2008-04-22
+ * </pre>
+ * @requires oop
+ * @class DataType.Date.Locale
+ * @static
+ */
+var YDateEn = {
+ a: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ A: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ b: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ B: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ c: "%a %d %b %Y %T %Z",
+ p: ["AM", "PM"],
+ P: ["am", "pm"],
+ r: "%I:%M:%S %p",
+ x: "%d/%m/%y",
+ X: "%T"
+};
+
+Y.namespace("DataType.Date.Locale");
+
+Y.DataType.Date.Locale["en"] = YDateEn;
+
+Y.DataType.Date.Locale["en-US"] = Y.merge(YDateEn, {
+ c: "%a %d %b %Y %I:%M:%S %p %Z",
+ x: "%m/%d/%Y",
+ X: "%I:%M:%S %p"
+});
+
+Y.DataType.Date.Locale["en-GB"] = Y.merge(YDateEn, {
+ r: "%l:%M:%S %P %Z"
+});
+Y.DataType.Date.Locale["en-AU"] = Y.merge(YDateEn);
+
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-date', function(Y){}, '3.0.0' ,{use:['datatype-date-parse', 'datatype-date-format']});
+
+
+YUI.add('datatype-xml-parse', function(Y) {
+
+/**
+ * Parse XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-parse
+ * @for DataType.XML
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method parse
+ * @param data {String} Data to convert.
+ * @return {XMLDoc} XML Document.
+ */
+ parse: function(data) {
+ var xmlDoc = null;
+ if(LANG.isString(data)) {
+ try {
+ if(!LANG.isUndefined(DOMParser)) {
+ xmlDoc = new DOMParser().parseFromString(data, "text/xml");
+ }
+ }
+ catch(e) {
+ try {
+ if(!LANG.isUndefined(ActiveXObject)) {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = false;
+ xmlDoc.loadXML(data);
+ }
+ }
+ catch(ee) {
+ Y.log(ee.message + " (Could not parse data " + Y.dump(data) + " to type XML Document)", "warn", "datatype-xml");
+ }
+ }
+ }
+
+ if( (LANG.isNull(xmlDoc)) || (LANG.isNull(xmlDoc.documentElement)) || (xmlDoc.documentElement.nodeName === "parsererror") ) {
+ Y.log("Could not parse data " + Y.dump(data) + " to type XML Document", "warn", "datatype-xml");
+ }
+
+ return xmlDoc;
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").xml = Y.DataType.XML.parse;
+
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-xml-format', function(Y) {
+
+/**
+ * Format XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-format
+ */
+
+/**
+ * XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml
+ */
+
+/**
+ * DataType.XML provides a set of utility functions to operate against XML documents.
+ *
+ * @class DataType.XML
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method format
+ * @param data {XMLDoc} Data to convert.
+ * @return {String} String.
+ */
+ format: function(data) {
+ try {
+ if(!LANG.isUndefined(XMLSerializer)) {
+ return (new XMLSerializer()).serializeToString(data);
+ }
+ }
+ catch(e) {
+ if(data && data.xml) {
+ return data.xml;
+ }
+ else {
+ Y.log("Could not format data " + Y.dump(data) + " from type XML", "warn", "datatype-xml");
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+ }
+});
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-xml', function(Y){}, '3.0.0' ,{use:['datatype-xml-parse', 'datatype-xml-format']});
+
+
+
+
+YUI.add('datatype', function(Y){}, '3.0.0' ,{use:['datatype-number', 'datatype-date', 'datatype-xml']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-number-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Number"),{parse:function(D){var C=(D===null)?D:+D;if(A.isNumber(C)){return C;}else{return null;}}});B.namespace("Parsers").number=B.DataType.Number.parse;},"3.0.0");YUI.add("datatype-number-format",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Number"),{format:function(I,E){if(A.isNumber(I)){E=E||{};var D=(I<0),F=I+"",M=E.decimalPlaces,C=E.decimalSeparator||".",L=E.thousandsSeparator,K,G,J,H;if(A.isNumber(M)&&(M>=0)&&(M<=20)){F=I.toFixed(M);}if(C!=="."){F=F.replace(".",C);}if(L){K=F.lastIndexOf(C);K=(K>-1)?K:F.length;G=F.substring(K);for(J=0,H=K;H>0;H--){if((J%3===0)&&(H!==K)&&(!D||(H>1))){G=L+G;}G=F.charAt(H-1)+G;J++;}F=G;}F=(E.prefix)?E.prefix+F:F;F=(E.suffix)?F+E.suffix:F;return F;}else{return(A.isValue(I)&&I.toString)?I.toString():"";}}});},"3.0.0");YUI.add("datatype-number",function(A){},"3.0.0",{use:["datatype-number-parse","datatype-number-format"]});YUI.add("datatype-date-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Date"),{parse:function(D){var C=null;if(!(A.isDate(D))){C=new Date(D);}else{return C;}if(A.isDate(C)&&(C!="Invalid Date")&&!isNaN(C)){return C;}else{return null;}}});B.namespace("Parsers").date=B.DataType.Date.parse;},"3.0.0");YUI.add("datatype-date-format",function(D){var A=function(E,G,F){if(typeof F==="undefined"){F=10;}G=G.toString();for(;parseInt(E,10)<F&&F>1;F/=10){E=G+E;}return E.toString();};D.config.dateFormat=D.config.dateFormat||"%Y-%m-%d";D.config.locale=D.config.locale||"en";var C={formats:{a:function(F,E){return E.a[F.getDay()];},A:function(F,E){return E.A[F.getDay()];},b:function(F,E){return E.b[F.getMonth()];},B:function(F,E){return E.B[F.getMonth()];},C:function(E){return A(parseInt(E.getFullYear()/100,10),0);},d:["getDate","0"],e:["getDate"," "],g:function(E){return A(parseInt(C.formats.G(E)%100,10),0);},G:function(G){var H=G.getFullYear();var F=parseInt(C.formats.V(G),10);var E=parseInt(C.formats.W(G),10);if(E>F){H++;}else{if(E===0&&F>=52){H--;}}return H;},H:["getHours","0"],I:function(F){var E=F.getHours()%12;return A(E===0?12:E,0);},j:function(I){var H=new Date(""+I.getFullYear()+"/1/1 GMT");var F=new Date(""+I.getFullYear()+"/"+(I.getMonth()+1)+"/"+I.getDate()+" GMT");var E=F-H;var G=parseInt(E/60000/60/24,10)+1;return A(G,0,100);},k:["getHours"," "],l:function(F){var E=F.getHours()%12;return A(E===0?12:E," ");},m:function(E){return A(E.getMonth()+1,0);},M:["getMinutes","0"],p:function(F,E){return E.p[F.getHours()>=12?1:0];},P:function(F,E){return E.P[F.getHours()>=12?1:0];},s:function(F,E){return parseInt(F.getTime()/1000,10);},S:["getSeconds","0"],u:function(E){var F=E.getDay();return F===0?7:F;},U:function(H){var E=parseInt(C.formats.j(H),10);var G=6-H.getDay();var F=parseInt((E+G)/7,10);return A(F,0);},V:function(H){var G=parseInt(C.formats.W(H),10);var E=(new Date(""+H.getFullYear()+"/1/1")).getDay();var F=G+(E>4||E<=1?0:1);if(F===53&&(new Date(""+H.getFullYear()+"/12/31")).getDay()<4){F=1;}else{if(F===0){F=C.formats.V(new Date(""+(H.getFullYear()-1)+"/12/31"));}}return A(F,0);},w:"getDay",W:function(H){var E=parseInt(C.formats.j(H),10);var G=7-C.formats.u(H);var F=parseInt((E+G)/7,10);return A(F,0,10);},y:function(E){return A(E.getFullYear()%100,0);},Y:"getFullYear",z:function(G){var F=G.getTimezoneOffset();var E=A(parseInt(Math.abs(F/60),10),0);var I=A(Math.abs(F%60),0);return(F>0?"-":"+")+E+I;},Z:function(E){var F=E.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/,"$2").replace(/[a-z ]/g,"");if(F.length>4){F=C.formats.z(E);}return F;},"%":function(E){return"%";}},aggregates:{c:"locale",D:"%m/%d/%y",F:"%Y-%m-%d",h:"%b",n:"\n",r:"locale",R:"%H:%M",t:"\t",T:"%H:%M:%S",x:"locale",X:"locale"},format:function(N,H){H=H||{};if(!D.Lang.isDate(N)){return D.Lang.isValue(N)?N:"";}var M=H.format||D.config.dateFormat,F=H.locale||D.config.locale,L=D.DataType.Date.Locale;F=F.replace(/_/g,"-");if(!L[F]){var G=F.replace(/-[a-zA-Z]+$/,"");if(G in L){F=G;}else{if(D.config.locale in L){F=D.config.locale;}else{F="en";}}}var J=L[F];var I=function(P,O){var Q=C.aggregates[O];return(Q==="locale"?J[O]:Q);};var E=function(P,O){var Q=C.formats[O];switch(D.Lang.type(Q)){case"string":return N[Q]();case"function":return Q.call(N,N,J);case"array":if(D.Lang.type(Q[0])==="string"){return A(N[Q[0]](),Q[1]);}default:return O;}};while(M.match(/%[cDFhnrRtTxX]/)){M=M.replace(/%([cDFhnrRtTxX])/g,I);}var K=M.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g,E);I=E=undefined;return K;}};D.mix(D.namespace("DataType.Date"),C);var B={a:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],A:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],b:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],B:["January","February","March","April","May","June","July","August","September","October","November","December"],c:"%a %d %b %Y %T %Z",p:["AM","PM"],P:["am","pm"],r:"%I:%M:%S %p",x:"%d/%m/%y",X:"%T"};D.namespace("DataType.Date.Locale");D.DataType.Date.Locale["en"]=B;D.DataType.Date.Locale["en-US"]=D.merge(B,{c:"%a %d %b %Y %I:%M:%S %p %Z",x:"%m/%d/%Y",X:"%I:%M:%S %p"});D.DataType.Date.Locale["en-GB"]=D.merge(B,{r:"%l:%M:%S %P %Z"});D.DataType.Date.Locale["en-AU"]=D.merge(B);},"3.0.0");YUI.add("datatype-date",function(A){},"3.0.0",{use:["datatype-date-parse","datatype-date-format"]});YUI.add("datatype-xml-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.XML"),{parse:function(E){var D=null;if(A.isString(E)){try{if(!A.isUndefined(DOMParser)){D=new DOMParser().parseFromString(E,"text/xml");}}catch(F){try{if(!A.isUndefined(ActiveXObject)){D=new ActiveXObject("Microsoft.XMLDOM");D.async=false;D.loadXML(E);}}catch(C){}}}if((A.isNull(D))||(A.isNull(D.documentElement))||(D.documentElement.nodeName==="parsererror")){}return D;}});B.namespace("Parsers").xml=B.DataType.XML.parse;},"3.0.0");YUI.add("datatype-xml-format",function(B){var A=B.Lang;B.mix(B.namespace("DataType.XML"),{format:function(C){try{if(!A.isUndefined(XMLSerializer)){return(new XMLSerializer()).serializeToString(C);
+}}catch(D){if(C&&C.xml){return C.xml;}else{return(A.isValue(C)&&C.toString)?C.toString():"";}}}});},"3.0.0");YUI.add("datatype-xml",function(A){},"3.0.0",{use:["datatype-xml-parse","datatype-xml-format"]});YUI.add("datatype",function(A){},"3.0.0",{use:["datatype-number","datatype-date","datatype-xml"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-parse
+ * @for DataType.Number
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Converts data to type Number.
+ *
+ * @method parse
+ * @param data {String | Number | Boolean} Data to convert. The following
+ * values return as null: null, undefined, NaN, "".
+ * @return {Number} A number, or null.
+ */
+ parse: function(data) {
+ var number = (data === null) ? data : +data;
+ if(LANG.isNumber(number)) {
+ return number;
+ }
+ else {
+ Y.log("Could not parse data " + Y.dump(data) + " to type Number", "warn", "datatype-number");
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").number = Y.DataType.Number.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-number-format', function(Y) {
+
+/**
+ * Number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number
+ */
+
+/**
+ * Format number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-format
+ */
+
+/**
+ * DataType.Number provides a set of utility functions to operate against Number objects.
+ *
+ * @class DataType.Number
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Takes a Number and formats to string for display to user.
+ *
+ * @method format
+ * @param data {Number} Number.
+ * @param config {Object} (Optional) Optional configuration values:
+ * <dl>
+ * <dt>prefix {String}</dd>
+ * <dd>String prepended before each number, like a currency designator "$"</dd>
+ * <dt>decimalPlaces {Number}</dd>
+ * <dd>Number of decimal places to round. Must be a number 0 to 20.</dd>
+ * <dt>decimalSeparator {String}</dd>
+ * <dd>Decimal separator</dd>
+ * <dt>thousandsSeparator {String}</dd>
+ * <dd>Thousands separator</dd>
+ * <dt>suffix {String}</dd>
+ * <dd>String appended after each number, like " items" (note the space)</dd>
+ * </dl>
+ * @return {String} Formatted number for display. Note, the following values
+ * return as "": null, undefined, NaN, "".
+ */
+ format: function(data, config) {
+ if(LANG.isNumber(data)) {
+ config = config || {};
+
+ var isNeg = (data < 0),
+ output = data + "",
+ decPlaces = config.decimalPlaces,
+ decSep = config.decimalSeparator || ".",
+ thouSep = config.thousandsSeparator,
+ decIndex,
+ newOutput, count, i;
+
+ // Decimal precision
+ if(LANG.isNumber(decPlaces) && (decPlaces >= 0) && (decPlaces <= 20)) {
+ // Round to the correct decimal place
+ output = data.toFixed(decPlaces);
+ }
+
+ // Decimal separator
+ if(decSep !== "."){
+ output = output.replace(".", decSep);
+ }
+
+ // Add the thousands separator
+ if(thouSep) {
+ // Find the dot or where it would be
+ decIndex = output.lastIndexOf(decSep);
+ decIndex = (decIndex > -1) ? decIndex : output.length;
+ // Start with the dot and everything to the right
+ newOutput = output.substring(decIndex);
+ // Working left, every third time add a separator, every time add a digit
+ for (count = 0, i=decIndex; i>0; i--) {
+ if ((count%3 === 0) && (i !== decIndex) && (!isNeg || (i > 1))) {
+ newOutput = thouSep + newOutput;
+ }
+ newOutput = output.charAt(i-1) + newOutput;
+ count++;
+ }
+ output = newOutput;
+ }
+
+ // Prepend prefix
+ output = (config.prefix) ? config.prefix + output : output;
+
+ // Append suffix
+ output = (config.suffix) ? output + config.suffix : output;
+
+ return output;
+ }
+ // Not a Number, just return as string
+ else {
+ Y.log("Could not format data " + Y.dump(data) + " from type Number", "warn", "datatype-number");
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+});
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-number', function(Y){}, '3.0.0' ,{use:['datatype-number-parse', 'datatype-number-format']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-format', function(Y) {
+
+/**
+ * Number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number
+ */
+
+/**
+ * Format number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-format
+ */
+
+/**
+ * DataType.Number provides a set of utility functions to operate against Number objects.
+ *
+ * @class DataType.Number
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Takes a Number and formats to string for display to user.
+ *
+ * @method format
+ * @param data {Number} Number.
+ * @param config {Object} (Optional) Optional configuration values:
+ * <dl>
+ * <dt>prefix {String}</dd>
+ * <dd>String prepended before each number, like a currency designator "$"</dd>
+ * <dt>decimalPlaces {Number}</dd>
+ * <dd>Number of decimal places to round. Must be a number 0 to 20.</dd>
+ * <dt>decimalSeparator {String}</dd>
+ * <dd>Decimal separator</dd>
+ * <dt>thousandsSeparator {String}</dd>
+ * <dd>Thousands separator</dd>
+ * <dt>suffix {String}</dd>
+ * <dd>String appended after each number, like " items" (note the space)</dd>
+ * </dl>
+ * @return {String} Formatted number for display. Note, the following values
+ * return as "": null, undefined, NaN, "".
+ */
+ format: function(data, config) {
+ if(LANG.isNumber(data)) {
+ config = config || {};
+
+ var isNeg = (data < 0),
+ output = data + "",
+ decPlaces = config.decimalPlaces,
+ decSep = config.decimalSeparator || ".",
+ thouSep = config.thousandsSeparator,
+ decIndex,
+ newOutput, count, i;
+
+ // Decimal precision
+ if(LANG.isNumber(decPlaces) && (decPlaces >= 0) && (decPlaces <= 20)) {
+ // Round to the correct decimal place
+ output = data.toFixed(decPlaces);
+ }
+
+ // Decimal separator
+ if(decSep !== "."){
+ output = output.replace(".", decSep);
+ }
+
+ // Add the thousands separator
+ if(thouSep) {
+ // Find the dot or where it would be
+ decIndex = output.lastIndexOf(decSep);
+ decIndex = (decIndex > -1) ? decIndex : output.length;
+ // Start with the dot and everything to the right
+ newOutput = output.substring(decIndex);
+ // Working left, every third time add a separator, every time add a digit
+ for (count = 0, i=decIndex; i>0; i--) {
+ if ((count%3 === 0) && (i !== decIndex) && (!isNeg || (i > 1))) {
+ newOutput = thouSep + newOutput;
+ }
+ newOutput = output.charAt(i-1) + newOutput;
+ count++;
+ }
+ output = newOutput;
+ }
+
+ // Prepend prefix
+ output = (config.prefix) ? config.prefix + output : output;
+
+ // Append suffix
+ output = (config.suffix) ? output + config.suffix : output;
+
+ return output;
+ }
+ // Not a Number, just return as string
+ else {
+ Y.log("Could not format data " + Y.dump(data) + " from type Number", "warn", "datatype-number");
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+});
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-number-format",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Number"),{format:function(I,E){if(A.isNumber(I)){E=E||{};var D=(I<0),F=I+"",M=E.decimalPlaces,C=E.decimalSeparator||".",L=E.thousandsSeparator,K,G,J,H;if(A.isNumber(M)&&(M>=0)&&(M<=20)){F=I.toFixed(M);}if(C!=="."){F=F.replace(".",C);}if(L){K=F.lastIndexOf(C);K=(K>-1)?K:F.length;G=F.substring(K);for(J=0,H=K;H>0;H--){if((J%3===0)&&(H!==K)&&(!D||(H>1))){G=L+G;}G=F.charAt(H-1)+G;J++;}F=G;}F=(E.prefix)?E.prefix+F:F;F=(E.suffix)?F+E.suffix:F;return F;}else{return(A.isValue(I)&&I.toString)?I.toString():"";}}});},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-format', function(Y) {
+
+/**
+ * Number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number
+ */
+
+/**
+ * Format number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-format
+ */
+
+/**
+ * DataType.Number provides a set of utility functions to operate against Number objects.
+ *
+ * @class DataType.Number
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Takes a Number and formats to string for display to user.
+ *
+ * @method format
+ * @param data {Number} Number.
+ * @param config {Object} (Optional) Optional configuration values:
+ * <dl>
+ * <dt>prefix {String}</dd>
+ * <dd>String prepended before each number, like a currency designator "$"</dd>
+ * <dt>decimalPlaces {Number}</dd>
+ * <dd>Number of decimal places to round. Must be a number 0 to 20.</dd>
+ * <dt>decimalSeparator {String}</dd>
+ * <dd>Decimal separator</dd>
+ * <dt>thousandsSeparator {String}</dd>
+ * <dd>Thousands separator</dd>
+ * <dt>suffix {String}</dd>
+ * <dd>String appended after each number, like " items" (note the space)</dd>
+ * </dl>
+ * @return {String} Formatted number for display. Note, the following values
+ * return as "": null, undefined, NaN, "".
+ */
+ format: function(data, config) {
+ if(LANG.isNumber(data)) {
+ config = config || {};
+
+ var isNeg = (data < 0),
+ output = data + "",
+ decPlaces = config.decimalPlaces,
+ decSep = config.decimalSeparator || ".",
+ thouSep = config.thousandsSeparator,
+ decIndex,
+ newOutput, count, i;
+
+ // Decimal precision
+ if(LANG.isNumber(decPlaces) && (decPlaces >= 0) && (decPlaces <= 20)) {
+ // Round to the correct decimal place
+ output = data.toFixed(decPlaces);
+ }
+
+ // Decimal separator
+ if(decSep !== "."){
+ output = output.replace(".", decSep);
+ }
+
+ // Add the thousands separator
+ if(thouSep) {
+ // Find the dot or where it would be
+ decIndex = output.lastIndexOf(decSep);
+ decIndex = (decIndex > -1) ? decIndex : output.length;
+ // Start with the dot and everything to the right
+ newOutput = output.substring(decIndex);
+ // Working left, every third time add a separator, every time add a digit
+ for (count = 0, i=decIndex; i>0; i--) {
+ if ((count%3 === 0) && (i !== decIndex) && (!isNeg || (i > 1))) {
+ newOutput = thouSep + newOutput;
+ }
+ newOutput = output.charAt(i-1) + newOutput;
+ count++;
+ }
+ output = newOutput;
+ }
+
+ // Prepend prefix
+ output = (config.prefix) ? config.prefix + output : output;
+
+ // Append suffix
+ output = (config.suffix) ? output + config.suffix : output;
+
+ return output;
+ }
+ // Not a Number, just return as string
+ else {
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+});
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-number-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Number"),{parse:function(D){var C=(D===null)?D:+D;if(A.isNumber(C)){return C;}else{return null;}}});B.namespace("Parsers").number=B.DataType.Number.parse;},"3.0.0");YUI.add("datatype-number-format",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Number"),{format:function(I,E){if(A.isNumber(I)){E=E||{};var D=(I<0),F=I+"",M=E.decimalPlaces,C=E.decimalSeparator||".",L=E.thousandsSeparator,K,G,J,H;if(A.isNumber(M)&&(M>=0)&&(M<=20)){F=I.toFixed(M);}if(C!=="."){F=F.replace(".",C);}if(L){K=F.lastIndexOf(C);K=(K>-1)?K:F.length;G=F.substring(K);for(J=0,H=K;H>0;H--){if((J%3===0)&&(H!==K)&&(!D||(H>1))){G=L+G;}G=F.charAt(H-1)+G;J++;}F=G;}F=(E.prefix)?E.prefix+F:F;F=(E.suffix)?F+E.suffix:F;return F;}else{return(A.isValue(I)&&I.toString)?I.toString():"";}}});},"3.0.0");YUI.add("datatype-number",function(A){},"3.0.0",{use:["datatype-number-parse","datatype-number-format"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-parse
+ * @for DataType.Number
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Converts data to type Number.
+ *
+ * @method parse
+ * @param data {String | Number | Boolean} Data to convert. The following
+ * values return as null: null, undefined, NaN, "".
+ * @return {Number} A number, or null.
+ */
+ parse: function(data) {
+ var number = (data === null) ? data : +data;
+ if(LANG.isNumber(number)) {
+ return number;
+ }
+ else {
+ Y.log("Could not parse data " + Y.dump(data) + " to type Number", "warn", "datatype-number");
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").number = Y.DataType.Number.parse;
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-number-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.Number"),{parse:function(D){var C=(D===null)?D:+D;if(A.isNumber(C)){return C;}else{return null;}}});B.namespace("Parsers").number=B.DataType.Number.parse;},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-parse
+ * @for DataType.Number
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Converts data to type Number.
+ *
+ * @method parse
+ * @param data {String | Number | Boolean} Data to convert. The following
+ * values return as null: null, undefined, NaN, "".
+ * @return {Number} A number, or null.
+ */
+ parse: function(data) {
+ var number = (data === null) ? data : +data;
+ if(LANG.isNumber(number)) {
+ return number;
+ }
+ else {
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").number = Y.DataType.Number.parse;
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-parse
+ * @for DataType.Number
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Converts data to type Number.
+ *
+ * @method parse
+ * @param data {String | Number | Boolean} Data to convert. The following
+ * values return as null: null, undefined, NaN, "".
+ * @return {Number} A number, or null.
+ */
+ parse: function(data) {
+ var number = (data === null) ? data : +data;
+ if(LANG.isNumber(number)) {
+ return number;
+ }
+ else {
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").number = Y.DataType.Number.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-number-format', function(Y) {
+
+/**
+ * Number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number
+ */
+
+/**
+ * Format number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-format
+ */
+
+/**
+ * DataType.Number provides a set of utility functions to operate against Number objects.
+ *
+ * @class DataType.Number
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Takes a Number and formats to string for display to user.
+ *
+ * @method format
+ * @param data {Number} Number.
+ * @param config {Object} (Optional) Optional configuration values:
+ * <dl>
+ * <dt>prefix {String}</dd>
+ * <dd>String prepended before each number, like a currency designator "$"</dd>
+ * <dt>decimalPlaces {Number}</dd>
+ * <dd>Number of decimal places to round. Must be a number 0 to 20.</dd>
+ * <dt>decimalSeparator {String}</dd>
+ * <dd>Decimal separator</dd>
+ * <dt>thousandsSeparator {String}</dd>
+ * <dd>Thousands separator</dd>
+ * <dt>suffix {String}</dd>
+ * <dd>String appended after each number, like " items" (note the space)</dd>
+ * </dl>
+ * @return {String} Formatted number for display. Note, the following values
+ * return as "": null, undefined, NaN, "".
+ */
+ format: function(data, config) {
+ if(LANG.isNumber(data)) {
+ config = config || {};
+
+ var isNeg = (data < 0),
+ output = data + "",
+ decPlaces = config.decimalPlaces,
+ decSep = config.decimalSeparator || ".",
+ thouSep = config.thousandsSeparator,
+ decIndex,
+ newOutput, count, i;
+
+ // Decimal precision
+ if(LANG.isNumber(decPlaces) && (decPlaces >= 0) && (decPlaces <= 20)) {
+ // Round to the correct decimal place
+ output = data.toFixed(decPlaces);
+ }
+
+ // Decimal separator
+ if(decSep !== "."){
+ output = output.replace(".", decSep);
+ }
+
+ // Add the thousands separator
+ if(thouSep) {
+ // Find the dot or where it would be
+ decIndex = output.lastIndexOf(decSep);
+ decIndex = (decIndex > -1) ? decIndex : output.length;
+ // Start with the dot and everything to the right
+ newOutput = output.substring(decIndex);
+ // Working left, every third time add a separator, every time add a digit
+ for (count = 0, i=decIndex; i>0; i--) {
+ if ((count%3 === 0) && (i !== decIndex) && (!isNeg || (i > 1))) {
+ newOutput = thouSep + newOutput;
+ }
+ newOutput = output.charAt(i-1) + newOutput;
+ count++;
+ }
+ output = newOutput;
+ }
+
+ // Prepend prefix
+ output = (config.prefix) ? config.prefix + output : output;
+
+ // Append suffix
+ output = (config.suffix) ? output + config.suffix : output;
+
+ return output;
+ }
+ // Not a Number, just return as string
+ else {
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+});
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-number', function(Y){}, '3.0.0' ,{use:['datatype-number-parse', 'datatype-number-format']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-xml-parse', function(Y) {
+
+/**
+ * Parse XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-parse
+ * @for DataType.XML
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method parse
+ * @param data {String} Data to convert.
+ * @return {XMLDoc} XML Document.
+ */
+ parse: function(data) {
+ var xmlDoc = null;
+ if(LANG.isString(data)) {
+ try {
+ if(!LANG.isUndefined(DOMParser)) {
+ xmlDoc = new DOMParser().parseFromString(data, "text/xml");
+ }
+ }
+ catch(e) {
+ try {
+ if(!LANG.isUndefined(ActiveXObject)) {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = false;
+ xmlDoc.loadXML(data);
+ }
+ }
+ catch(ee) {
+ Y.log(ee.message + " (Could not parse data " + Y.dump(data) + " to type XML Document)", "warn", "datatype-xml");
+ }
+ }
+ }
+
+ if( (LANG.isNull(xmlDoc)) || (LANG.isNull(xmlDoc.documentElement)) || (xmlDoc.documentElement.nodeName === "parsererror") ) {
+ Y.log("Could not parse data " + Y.dump(data) + " to type XML Document", "warn", "datatype-xml");
+ }
+
+ return xmlDoc;
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").xml = Y.DataType.XML.parse;
+
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-xml-format', function(Y) {
+
+/**
+ * Format XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-format
+ */
+
+/**
+ * XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml
+ */
+
+/**
+ * DataType.XML provides a set of utility functions to operate against XML documents.
+ *
+ * @class DataType.XML
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method format
+ * @param data {XMLDoc} Data to convert.
+ * @return {String} String.
+ */
+ format: function(data) {
+ try {
+ if(!LANG.isUndefined(XMLSerializer)) {
+ return (new XMLSerializer()).serializeToString(data);
+ }
+ }
+ catch(e) {
+ if(data && data.xml) {
+ return data.xml;
+ }
+ else {
+ Y.log("Could not format data " + Y.dump(data) + " from type XML", "warn", "datatype-xml");
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+ }
+});
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-xml', function(Y){}, '3.0.0' ,{use:['datatype-xml-parse', 'datatype-xml-format']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-xml-format', function(Y) {
+
+/**
+ * Format XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-format
+ */
+
+/**
+ * XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml
+ */
+
+/**
+ * DataType.XML provides a set of utility functions to operate against XML documents.
+ *
+ * @class DataType.XML
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method format
+ * @param data {XMLDoc} Data to convert.
+ * @return {String} String.
+ */
+ format: function(data) {
+ try {
+ if(!LANG.isUndefined(XMLSerializer)) {
+ return (new XMLSerializer()).serializeToString(data);
+ }
+ }
+ catch(e) {
+ if(data && data.xml) {
+ return data.xml;
+ }
+ else {
+ Y.log("Could not format data " + Y.dump(data) + " from type XML", "warn", "datatype-xml");
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+ }
+});
+
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-xml-format",function(B){var A=B.Lang;B.mix(B.namespace("DataType.XML"),{format:function(C){try{if(!A.isUndefined(XMLSerializer)){return(new XMLSerializer()).serializeToString(C);}}catch(D){if(C&&C.xml){return C.xml;}else{return(A.isValue(C)&&C.toString)?C.toString():"";}}}});},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-xml-format', function(Y) {
+
+/**
+ * Format XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-format
+ */
+
+/**
+ * XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml
+ */
+
+/**
+ * DataType.XML provides a set of utility functions to operate against XML documents.
+ *
+ * @class DataType.XML
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method format
+ * @param data {XMLDoc} Data to convert.
+ * @return {String} String.
+ */
+ format: function(data) {
+ try {
+ if(!LANG.isUndefined(XMLSerializer)) {
+ return (new XMLSerializer()).serializeToString(data);
+ }
+ }
+ catch(e) {
+ if(data && data.xml) {
+ return data.xml;
+ }
+ else {
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+ }
+});
+
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-xml-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.XML"),{parse:function(E){var D=null;if(A.isString(E)){try{if(!A.isUndefined(DOMParser)){D=new DOMParser().parseFromString(E,"text/xml");}}catch(F){try{if(!A.isUndefined(ActiveXObject)){D=new ActiveXObject("Microsoft.XMLDOM");D.async=false;D.loadXML(E);}}catch(C){}}}if((A.isNull(D))||(A.isNull(D.documentElement))||(D.documentElement.nodeName==="parsererror")){}return D;}});B.namespace("Parsers").xml=B.DataType.XML.parse;},"3.0.0");YUI.add("datatype-xml-format",function(B){var A=B.Lang;B.mix(B.namespace("DataType.XML"),{format:function(C){try{if(!A.isUndefined(XMLSerializer)){return(new XMLSerializer()).serializeToString(C);}}catch(D){if(C&&C.xml){return C.xml;}else{return(A.isValue(C)&&C.toString)?C.toString():"";}}}});},"3.0.0");YUI.add("datatype-xml",function(A){},"3.0.0",{use:["datatype-xml-parse","datatype-xml-format"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-xml-parse', function(Y) {
+
+/**
+ * Parse XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-parse
+ * @for DataType.XML
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method parse
+ * @param data {String} Data to convert.
+ * @return {XMLDoc} XML Document.
+ */
+ parse: function(data) {
+ var xmlDoc = null;
+ if(LANG.isString(data)) {
+ try {
+ if(!LANG.isUndefined(DOMParser)) {
+ xmlDoc = new DOMParser().parseFromString(data, "text/xml");
+ }
+ }
+ catch(e) {
+ try {
+ if(!LANG.isUndefined(ActiveXObject)) {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = false;
+ xmlDoc.loadXML(data);
+ }
+ }
+ catch(ee) {
+ Y.log(ee.message + " (Could not parse data " + Y.dump(data) + " to type XML Document)", "warn", "datatype-xml");
+ }
+ }
+ }
+
+ if( (LANG.isNull(xmlDoc)) || (LANG.isNull(xmlDoc.documentElement)) || (xmlDoc.documentElement.nodeName === "parsererror") ) {
+ Y.log("Could not parse data " + Y.dump(data) + " to type XML Document", "warn", "datatype-xml");
+ }
+
+ return xmlDoc;
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").xml = Y.DataType.XML.parse;
+
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("datatype-xml-parse",function(B){var A=B.Lang;B.mix(B.namespace("DataType.XML"),{parse:function(E){var D=null;if(A.isString(E)){try{if(!A.isUndefined(DOMParser)){D=new DOMParser().parseFromString(E,"text/xml");}}catch(F){try{if(!A.isUndefined(ActiveXObject)){D=new ActiveXObject("Microsoft.XMLDOM");D.async=false;D.loadXML(E);}}catch(C){}}}if((A.isNull(D))||(A.isNull(D.documentElement))||(D.documentElement.nodeName==="parsererror")){}return D;}});B.namespace("Parsers").xml=B.DataType.XML.parse;},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-xml-parse', function(Y) {
+
+/**
+ * Parse XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-parse
+ * @for DataType.XML
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method parse
+ * @param data {String} Data to convert.
+ * @return {XMLDoc} XML Document.
+ */
+ parse: function(data) {
+ var xmlDoc = null;
+ if(LANG.isString(data)) {
+ try {
+ if(!LANG.isUndefined(DOMParser)) {
+ xmlDoc = new DOMParser().parseFromString(data, "text/xml");
+ }
+ }
+ catch(e) {
+ try {
+ if(!LANG.isUndefined(ActiveXObject)) {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = false;
+ xmlDoc.loadXML(data);
+ }
+ }
+ catch(ee) {
+ }
+ }
+ }
+
+ if( (LANG.isNull(xmlDoc)) || (LANG.isNull(xmlDoc.documentElement)) || (xmlDoc.documentElement.nodeName === "parsererror") ) {
+ }
+
+ return xmlDoc;
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").xml = Y.DataType.XML.parse;
+
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-xml-parse', function(Y) {
+
+/**
+ * Parse XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-parse
+ * @for DataType.XML
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method parse
+ * @param data {String} Data to convert.
+ * @return {XMLDoc} XML Document.
+ */
+ parse: function(data) {
+ var xmlDoc = null;
+ if(LANG.isString(data)) {
+ try {
+ if(!LANG.isUndefined(DOMParser)) {
+ xmlDoc = new DOMParser().parseFromString(data, "text/xml");
+ }
+ }
+ catch(e) {
+ try {
+ if(!LANG.isUndefined(ActiveXObject)) {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = false;
+ xmlDoc.loadXML(data);
+ }
+ }
+ catch(ee) {
+ }
+ }
+ }
+
+ if( (LANG.isNull(xmlDoc)) || (LANG.isNull(xmlDoc.documentElement)) || (xmlDoc.documentElement.nodeName === "parsererror") ) {
+ }
+
+ return xmlDoc;
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").xml = Y.DataType.XML.parse;
+
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-xml-format', function(Y) {
+
+/**
+ * Format XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-format
+ */
+
+/**
+ * XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml
+ */
+
+/**
+ * DataType.XML provides a set of utility functions to operate against XML documents.
+ *
+ * @class DataType.XML
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method format
+ * @param data {XMLDoc} Data to convert.
+ * @return {String} String.
+ */
+ format: function(data) {
+ try {
+ if(!LANG.isUndefined(XMLSerializer)) {
+ return (new XMLSerializer()).serializeToString(data);
+ }
+ }
+ catch(e) {
+ if(data && data.xml) {
+ return data.xml;
+ }
+ else {
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+ }
+});
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-xml', function(Y){}, '3.0.0' ,{use:['datatype-xml-parse', 'datatype-xml-format']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('datatype-number-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-parse
+ * @for DataType.Number
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Converts data to type Number.
+ *
+ * @method parse
+ * @param data {String | Number | Boolean} Data to convert. The following
+ * values return as null: null, undefined, NaN, "".
+ * @return {Number} A number, or null.
+ */
+ parse: function(data) {
+ var number = (data === null) ? data : +data;
+ if(LANG.isNumber(number)) {
+ return number;
+ }
+ else {
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").number = Y.DataType.Number.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-number-format', function(Y) {
+
+/**
+ * Number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number
+ */
+
+/**
+ * Format number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-number-format
+ */
+
+/**
+ * DataType.Number provides a set of utility functions to operate against Number objects.
+ *
+ * @class DataType.Number
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Number"), {
+ /**
+ * Takes a Number and formats to string for display to user.
+ *
+ * @method format
+ * @param data {Number} Number.
+ * @param config {Object} (Optional) Optional configuration values:
+ * <dl>
+ * <dt>prefix {String}</dd>
+ * <dd>String prepended before each number, like a currency designator "$"</dd>
+ * <dt>decimalPlaces {Number}</dd>
+ * <dd>Number of decimal places to round. Must be a number 0 to 20.</dd>
+ * <dt>decimalSeparator {String}</dd>
+ * <dd>Decimal separator</dd>
+ * <dt>thousandsSeparator {String}</dd>
+ * <dd>Thousands separator</dd>
+ * <dt>suffix {String}</dd>
+ * <dd>String appended after each number, like " items" (note the space)</dd>
+ * </dl>
+ * @return {String} Formatted number for display. Note, the following values
+ * return as "": null, undefined, NaN, "".
+ */
+ format: function(data, config) {
+ if(LANG.isNumber(data)) {
+ config = config || {};
+
+ var isNeg = (data < 0),
+ output = data + "",
+ decPlaces = config.decimalPlaces,
+ decSep = config.decimalSeparator || ".",
+ thouSep = config.thousandsSeparator,
+ decIndex,
+ newOutput, count, i;
+
+ // Decimal precision
+ if(LANG.isNumber(decPlaces) && (decPlaces >= 0) && (decPlaces <= 20)) {
+ // Round to the correct decimal place
+ output = data.toFixed(decPlaces);
+ }
+
+ // Decimal separator
+ if(decSep !== "."){
+ output = output.replace(".", decSep);
+ }
+
+ // Add the thousands separator
+ if(thouSep) {
+ // Find the dot or where it would be
+ decIndex = output.lastIndexOf(decSep);
+ decIndex = (decIndex > -1) ? decIndex : output.length;
+ // Start with the dot and everything to the right
+ newOutput = output.substring(decIndex);
+ // Working left, every third time add a separator, every time add a digit
+ for (count = 0, i=decIndex; i>0; i--) {
+ if ((count%3 === 0) && (i !== decIndex) && (!isNeg || (i > 1))) {
+ newOutput = thouSep + newOutput;
+ }
+ newOutput = output.charAt(i-1) + newOutput;
+ count++;
+ }
+ output = newOutput;
+ }
+
+ // Prepend prefix
+ output = (config.prefix) ? config.prefix + output : output;
+
+ // Append suffix
+ output = (config.suffix) ? output + config.suffix : output;
+
+ return output;
+ }
+ // Not a Number, just return as string
+ else {
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+});
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-number', function(Y){}, '3.0.0' ,{use:['datatype-number-parse', 'datatype-number-format']});
+
+
+YUI.add('datatype-date-parse', function(Y) {
+
+/**
+ * Parse number submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date-parse
+ * @for DataType.Date
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.Date"), {
+ /**
+ * Converts data to type Date.
+ *
+ * @method parse
+ * @param data {String | Number} Data to convert. Values supported by the Date constructor are supported.
+ * @return {Date} A Date, or null.
+ */
+ parse: function(data) {
+ var date = null;
+
+ //Convert to date
+ if(!(LANG.isDate(data))) {
+ date = new Date(data);
+ }
+ else {
+ return date;
+ }
+
+ // Validate
+ if(LANG.isDate(date) && (date != "Invalid Date") && !isNaN(date)) { // Workaround for bug 2527965
+ return date;
+ }
+ else {
+ return null;
+ }
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").date = Y.DataType.Date.parse;
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-date-format', function(Y) {
+
+/**
+ * The DataType Utility provides type-conversion and string-formatting
+ * convenience methods for various JavaScript object types.
+ *
+ * @module datatype
+ */
+
+/**
+ * Date submodule.
+ *
+ * @module datatype
+ * @submodule datatype-date
+ */
+
+/**
+ * Format date submodule implements strftime formatters for javascript based on the
+ * Open Group specification defined at
+ * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html
+ * This implementation does not include modified conversion specifiers (i.e., Ex and Ox)
+ *
+ * @module datatype
+ * @submodule datatype-date-format
+ */
+
+/**
+ * DataType.Date provides a set of utility functions to operate against Date objects.
+ *
+ * @class DataType.Date
+ * @static
+ */
+
+/**
+ * Pad a number with leading spaces, zeroes or something else
+ * @method xPad
+ * @param x {Number} The number to be padded
+ * @param pad {String} The character to pad the number with
+ * @param r {Number} (optional) The base of the pad, eg, 10 implies to two digits, 100 implies to 3 digits.
+ * @private
+ */
+var xPad=function (x, pad, r)
+{
+ if(typeof r === "undefined")
+ {
+ r=10;
+ }
+ pad = pad.toString();
+ for( ; parseInt(x, 10)<r && r>1; r/=10) {
+ x = pad + x;
+ }
+ return x.toString();
+};
+
+/**
+ * Default date format.
+ *
+ * @for config
+ * @property dateFormat
+ * @type String
+ * @value "%Y-%m-%d"
+ */
+Y.config.dateFormat = Y.config.dateFormat || "%Y-%m-%d";
+
+/**
+ * Default locale for the YUI instance.
+ *
+ * @property locale
+ * @type String
+ * @value "en"
+ */
+Y.config.locale = Y.config.locale || "en";
+
+var Dt = {
+ formats: {
+ a: function (d, l) { return l.a[d.getDay()]; },
+ A: function (d, l) { return l.A[d.getDay()]; },
+ b: function (d, l) { return l.b[d.getMonth()]; },
+ B: function (d, l) { return l.B[d.getMonth()]; },
+ C: function (d) { return xPad(parseInt(d.getFullYear()/100, 10), 0); },
+ d: ["getDate", "0"],
+ e: ["getDate", " "],
+ g: function (d) { return xPad(parseInt(Dt.formats.G(d)%100, 10), 0); },
+ G: function (d) {
+ var y = d.getFullYear();
+ var V = parseInt(Dt.formats.V(d), 10);
+ var W = parseInt(Dt.formats.W(d), 10);
+
+ if(W > V) {
+ y++;
+ } else if(W===0 && V>=52) {
+ y--;
+ }
+
+ return y;
+ },
+ H: ["getHours", "0"],
+ I: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, 0); },
+ j: function (d) {
+ var gmd_1 = new Date("" + d.getFullYear() + "/1/1 GMT");
+ var gmdate = new Date("" + d.getFullYear() + "/" + (d.getMonth()+1) + "/" + d.getDate() + " GMT");
+ var ms = gmdate - gmd_1;
+ var doy = parseInt(ms/60000/60/24, 10)+1;
+ return xPad(doy, 0, 100);
+ },
+ k: ["getHours", " "],
+ l: function (d) { var I=d.getHours()%12; return xPad(I===0?12:I, " "); },
+ m: function (d) { return xPad(d.getMonth()+1, 0); },
+ M: ["getMinutes", "0"],
+ p: function (d, l) { return l.p[d.getHours() >= 12 ? 1 : 0 ]; },
+ P: function (d, l) { return l.P[d.getHours() >= 12 ? 1 : 0 ]; },
+ s: function (d, l) { return parseInt(d.getTime()/1000, 10); },
+ S: ["getSeconds", "0"],
+ u: function (d) { var dow = d.getDay(); return dow===0?7:dow; },
+ U: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 6-d.getDay();
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0);
+ },
+ V: function (d) {
+ var woy = parseInt(Dt.formats.W(d), 10);
+ var dow1_1 = (new Date("" + d.getFullYear() + "/1/1")).getDay();
+ // First week is 01 and not 00 as in the case of %U and %W,
+ // so we add 1 to the final result except if day 1 of the year
+ // is a Monday (then %W returns 01).
+ // We also need to subtract 1 if the day 1 of the year is
+ // Friday-Sunday, so the resulting equation becomes:
+ var idow = woy + (dow1_1 > 4 || dow1_1 <= 1 ? 0 : 1);
+ if(idow === 53 && (new Date("" + d.getFullYear() + "/12/31")).getDay() < 4)
+ {
+ idow = 1;
+ }
+ else if(idow === 0)
+ {
+ idow = Dt.formats.V(new Date("" + (d.getFullYear()-1) + "/12/31"));
+ }
+
+ return xPad(idow, 0);
+ },
+ w: "getDay",
+ W: function (d) {
+ var doy = parseInt(Dt.formats.j(d), 10);
+ var rdow = 7-Dt.formats.u(d);
+ var woy = parseInt((doy+rdow)/7, 10);
+ return xPad(woy, 0, 10);
+ },
+ y: function (d) { return xPad(d.getFullYear()%100, 0); },
+ Y: "getFullYear",
+ z: function (d) {
+ var o = d.getTimezoneOffset();
+ var H = xPad(parseInt(Math.abs(o/60), 10), 0);
+ var M = xPad(Math.abs(o%60), 0);
+ return (o>0?"-":"+") + H + M;
+ },
+ Z: function (d) {
+ var tz = d.toString().replace(/^.*:\d\d( GMT[+-]\d+)? \(?([A-Za-z ]+)\)?\d*$/, "$2").replace(/[a-z ]/g, "");
+ if(tz.length > 4) {
+ tz = Dt.formats.z(d);
+ }
+ return tz;
+ },
+ "%": function (d) { return "%"; }
+ },
+
+ aggregates: {
+ c: "locale",
+ D: "%m/%d/%y",
+ F: "%Y-%m-%d",
+ h: "%b",
+ n: "\n",
+ r: "locale",
+ R: "%H:%M",
+ t: "\t",
+ T: "%H:%M:%S",
+ x: "locale",
+ X: "locale"
+ //"+": "%a %b %e %T %Z %Y"
+ },
+
+ /**
+ * Takes a native JavaScript Date and formats it as a string for display to user.
+ *
+ * @for DataType.Date
+ * @method format
+ * @param oDate {Date} Date.
+ * @param oConfig {Object} (Optional) Object literal of configuration values:
+ * <dl>
+ * <dt>format {String} (Optional)</dt>
+ * <dd>
+ * <p>
+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at
+ * <a href="http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html">http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html</a>
+ * PHP added a few of its own, defined at <a href="http://www.php.net/strftime">http://www.php.net/strftime</a>
+ * </p>
+ * <p>
+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below.
+ * </p>
+ * <p>
+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat
+ * </p>
+ * <dl>
+ * <dt>%a</dt> <dd>abbreviated weekday name according to the current locale</dd>
+ * <dt>%A</dt> <dd>full weekday name according to the current locale</dd>
+ * <dt>%b</dt> <dd>abbreviated month name according to the current locale</dd>
+ * <dt>%B</dt> <dd>full month name according to the current locale</dd>
+ * <dt>%c</dt> <dd>preferred date and time representation for the current locale</dd>
+ * <dt>%C</dt> <dd>century number (the year divided by 100 and truncated to an integer, range 00 to 99)</dd>
+ * <dt>%d</dt> <dd>day of the month as a decimal number (range 01 to 31)</dd>
+ * <dt>%D</dt> <dd>same as %m/%d/%y</dd>
+ * <dt>%e</dt> <dd>day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")</dd>
+ * <dt>%F</dt> <dd>same as %Y-%m-%d (ISO 8601 date format)</dd>
+ * <dt>%g</dt> <dd>like %G, but without the century</dd>
+ * <dt>%G</dt> <dd>The 4-digit year corresponding to the ISO week number</dd>
+ * <dt>%h</dt> <dd>same as %b</dd>
+ * <dt>%H</dt> <dd>hour as a decimal number using a 24-hour clock (range 00 to 23)</dd>
+ * <dt>%I</dt> <dd>hour as a decimal number using a 12-hour clock (range 01 to 12)</dd>
+ * <dt>%j</dt> <dd>day of the year as a decimal number (range 001 to 366)</dd>
+ * <dt>%k</dt> <dd>hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)</dd>
+ * <dt>%l</dt> <dd>hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.) </dd>
+ * <dt>%m</dt> <dd>month as a decimal number (range 01 to 12)</dd>
+ * <dt>%M</dt> <dd>minute as a decimal number</dd>
+ * <dt>%n</dt> <dd>newline character</dd>
+ * <dt>%p</dt> <dd>either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale</dd>
+ * <dt>%P</dt> <dd>like %p, but lower case</dd>
+ * <dt>%r</dt> <dd>time in a.m. and p.m. notation equal to %I:%M:%S %p</dd>
+ * <dt>%R</dt> <dd>time in 24 hour notation equal to %H:%M</dd>
+ * <dt>%s</dt> <dd>number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC</dd>
+ * <dt>%S</dt> <dd>second as a decimal number</dd>
+ * <dt>%t</dt> <dd>tab character</dd>
+ * <dt>%T</dt> <dd>current time, equal to %H:%M:%S</dd>
+ * <dt>%u</dt> <dd>weekday as a decimal number [1,7], with 1 representing Monday</dd>
+ * <dt>%U</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Sunday as the first day of the first week</dd>
+ * <dt>%V</dt> <dd>The ISO 8601:1988 week number of the current year as a decimal number,
+ * range 01 to 53, where week 1 is the first week that has at least 4 days
+ * in the current year, and with Monday as the first day of the week.</dd>
+ * <dt>%w</dt> <dd>day of the week as a decimal, Sunday being 0</dd>
+ * <dt>%W</dt> <dd>week number of the current year as a decimal number, starting with the
+ * first Monday as the first day of the first week</dd>
+ * <dt>%x</dt> <dd>preferred date representation for the current locale without the time</dd>
+ * <dt>%X</dt> <dd>preferred time representation for the current locale without the date</dd>
+ * <dt>%y</dt> <dd>year as a decimal number without a century (range 00 to 99)</dd>
+ * <dt>%Y</dt> <dd>year as a decimal number including the century</dd>
+ * <dt>%z</dt> <dd>numerical time zone representation</dd>
+ * <dt>%Z</dt> <dd>time zone name or abbreviation</dd>
+ * <dt>%%</dt> <dd>a literal "%" character</dd>
+ * </dl>
+ * </dd>
+ * <dt>locale {String} (Optional)</dt>
+ * <dd>
+ * The locale to use when displaying days of week, months of the year, and other locale specific
+ * strings. If not specified, this defaults to "en" (though this may be overridden by changing Y.config.locale).
+ * The following locales are built in:
+ * <dl>
+ * <dt>en</dt>
+ * <dd>English</dd>
+ * <dt>en-US</dt>
+ * <dd>US English</dd>
+ * <dt>en-GB</dt>
+ * <dd>British English</dd>
+ * <dt>en-AU</dt>
+ * <dd>Australian English (identical to British English)</dd>
+ * </dl>
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"].
+ * See Y.DataType.Date.Locale for more information.
+ * </dd>
+ * </dl>
+ * @return {String} Formatted date for display.
+ */
+ format : function (oDate, oConfig) {
+ oConfig = oConfig || {};
+
+ if(!Y.Lang.isDate(oDate)) {
+ return Y.Lang.isValue(oDate) ? oDate : "";
+ }
+
+ var format = oConfig.format || Y.config.dateFormat,
+ sLocale = oConfig.locale || Y.config.locale,
+ LOCALE = Y.DataType.Date.Locale;
+
+ sLocale = sLocale.replace(/_/g, "-");
+
+ // Make sure we have a definition for the requested locale, or default to en.
+ if(!LOCALE[sLocale]) {
+ var tmpLocale = sLocale.replace(/-[a-zA-Z]+$/, "");
+ if(tmpLocale in LOCALE) {
+ sLocale = tmpLocale;
+ } else if(Y.config.locale in LOCALE) {
+ sLocale = Y.config.locale;
+ } else {
+ sLocale = "en";
+ }
+ }
+
+ var aLocale = LOCALE[sLocale];
+
+ var replace_aggs = function (m0, m1) {
+ var f = Dt.aggregates[m1];
+ return (f === "locale" ? aLocale[m1] : f);
+ };
+
+ var replace_formats = function (m0, m1) {
+ var f = Dt.formats[m1];
+ switch(Y.Lang.type(f)) {
+ case "string": // string => built in date function
+ return oDate[f]();
+ case "function": // function => our own function
+ return f.call(oDate, oDate, aLocale);
+ case "array": // built in function with padding
+ if(Y.Lang.type(f[0]) === "string") {
+ return xPad(oDate[f[0]](), f[1]);
+ } // no break; (fall through to default:)
+ default:
+ return m1;
+ }
+ };
+
+ // First replace aggregates (run in a loop because an agg may be made up of other aggs)
+ while(format.match(/%[cDFhnrRtTxX]/)) {
+ format = format.replace(/%([cDFhnrRtTxX])/g, replace_aggs);
+ }
+
+ // Now replace formats (do not run in a loop otherwise %%a will be replace with the value of %a)
+ var str = format.replace(/%([aAbBCdegGHIjklmMpPsSuUVwWyYzZ%])/g, replace_formats);
+
+ replace_aggs = replace_formats = undefined;
+
+ return str;
+ }
+};
+
+Y.mix(Y.namespace("DataType.Date"), Dt);
+
+/**
+ * @module datatype
+*/
+
+/**
+ * The Date.Locale class is a container for all localised date strings
+ * used by Y.DataType.Date. It is used internally, but may be extended
+ * to provide new date localisations.
+ *
+ * To create your own Locale, follow these steps:
+ * <ol>
+ * <li>Find an existing locale that matches closely with your needs</li>
+ * <li>Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing
+ * matches.</li>
+ * <li>Create your own class as an extension of the base class using
+ * Y.merge, and add your own localisations where needed.</li>
+ * </ol>
+ * See the Y.DataType.Date.Locale["en-US"] and Y.DataType.Date.Locale["en-GB"]
+ * classes which extend Y.DataType.Date.Locale["en"].
+ *
+ * For example, to implement locales for French french and Canadian french,
+ * we would do the following:
+ * <ol>
+ * <li>For French french, we have no existing similar locale, so use
+ * Y.DataType.Date.Locale["en"] as the base, and extend it:
+ * <pre>
+ * Y.DataType.Date.Locale["fr"] = Y.merge(Y.DataType.Date.Locale, {
+ * a: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
+ * A: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
+ * b: ["jan", "fév", "mar", "avr", "mai", "jun", "jui", "aoû", "sep", "oct", "nov", "déc"],
+ * B: ["janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre"],
+ * c: "%a %d %b %Y %T %Z",
+ * p: ["", ""],
+ * P: ["", ""],
+ * x: "%d.%m.%Y",
+ * X: "%T"
+ * });
+ * </pre>
+ * </li>
+ * <li>For Canadian french, we start with French french and change the meaning of \%x:
+ * <pre>
+ * Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
+ * x: "%Y-%m-%d"
+ * });
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * With that, you can use your new locales:
+ * <pre>
+ * var d = new Date("2008/04/22");
+ * Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * </pre>
+ * will return:
+ * <pre>
+ * mardi, 22 avril == 22.04.2008
+ * </pre>
+ * And
+ * <pre>
+ * Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * </pre>
+ * Will return:
+ * <pre>
+ * mardi, 22 avril == 2008-04-22
+ * </pre>
+ * @requires oop
+ * @class DataType.Date.Locale
+ * @static
+ */
+var YDateEn = {
+ a: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
+ A: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
+ b: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ B: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ c: "%a %d %b %Y %T %Z",
+ p: ["AM", "PM"],
+ P: ["am", "pm"],
+ r: "%I:%M:%S %p",
+ x: "%d/%m/%y",
+ X: "%T"
+};
+
+Y.namespace("DataType.Date.Locale");
+
+Y.DataType.Date.Locale["en"] = YDateEn;
+
+Y.DataType.Date.Locale["en-US"] = Y.merge(YDateEn, {
+ c: "%a %d %b %Y %I:%M:%S %p %Z",
+ x: "%m/%d/%Y",
+ X: "%I:%M:%S %p"
+});
+
+Y.DataType.Date.Locale["en-GB"] = Y.merge(YDateEn, {
+ r: "%l:%M:%S %P %Z"
+});
+Y.DataType.Date.Locale["en-AU"] = Y.merge(YDateEn);
+
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-date', function(Y){}, '3.0.0' ,{use:['datatype-date-parse', 'datatype-date-format']});
+
+
+YUI.add('datatype-xml-parse', function(Y) {
+
+/**
+ * Parse XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-parse
+ * @for DataType.XML
+ */
+
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method parse
+ * @param data {String} Data to convert.
+ * @return {XMLDoc} XML Document.
+ */
+ parse: function(data) {
+ var xmlDoc = null;
+ if(LANG.isString(data)) {
+ try {
+ if(!LANG.isUndefined(DOMParser)) {
+ xmlDoc = new DOMParser().parseFromString(data, "text/xml");
+ }
+ }
+ catch(e) {
+ try {
+ if(!LANG.isUndefined(ActiveXObject)) {
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = false;
+ xmlDoc.loadXML(data);
+ }
+ }
+ catch(ee) {
+ }
+ }
+ }
+
+ if( (LANG.isNull(xmlDoc)) || (LANG.isNull(xmlDoc.documentElement)) || (xmlDoc.documentElement.nodeName === "parsererror") ) {
+ }
+
+ return xmlDoc;
+ }
+});
+
+// Add Parsers shortcut
+Y.namespace("Parsers").xml = Y.DataType.XML.parse;
+
+
+
+
+}, '3.0.0' );
+
+YUI.add('datatype-xml-format', function(Y) {
+
+/**
+ * Format XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml-format
+ */
+
+/**
+ * XML submodule.
+ *
+ * @module datatype
+ * @submodule datatype-xml
+ */
+
+/**
+ * DataType.XML provides a set of utility functions to operate against XML documents.
+ *
+ * @class DataType.XML
+ * @static
+ */
+var LANG = Y.Lang;
+
+Y.mix(Y.namespace("DataType.XML"), {
+ /**
+ * Converts data to type XMLDocument.
+ *
+ * @method format
+ * @param data {XMLDoc} Data to convert.
+ * @return {String} String.
+ */
+ format: function(data) {
+ try {
+ if(!LANG.isUndefined(XMLSerializer)) {
+ return (new XMLSerializer()).serializeToString(data);
+ }
+ }
+ catch(e) {
+ if(data && data.xml) {
+ return data.xml;
+ }
+ else {
+ return (LANG.isValue(data) && data.toString) ? data.toString() : "";
+ }
+ }
+ }
+});
+
+
+
+
+}, '3.0.0' );
+
+
+
+YUI.add('datatype-xml', function(Y){}, '3.0.0' ,{use:['datatype-xml-parse', 'datatype-xml-format']});
+
+
+
+
+YUI.add('datatype', function(Y){}, '3.0.0' ,{use:['datatype-number', 'datatype-date', 'datatype-xml']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-constrain', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-constrain
+ */
+ /**
+ * This is a plugin for the dd-drag module to add the constraining methods to it. It supports constraining to a renodenode or viewport. It anode* supports tick based moves and XY axis constraints.
+ * @class DragConstrained
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+
+ var DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ HOST = 'host',
+ CON_2_REGION = 'constrain2region',
+ CON_2_NODE = 'constrain2node',
+ TICK_X_ARRAY = 'tickXArray',
+ TICK_Y_ARRAY = 'tickYArray',
+ DDM = Y.DD.DDM,
+ TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+ proto = null;
+
+ var C = function(config) {
+ C.superclass.constructor.apply(this, arguments);
+ };
+
+ C.NAME = 'DragConstrained';
+ /**
+ * @property con
+ * @description The Constrained instance will be placed on the Drag instance under the con namespace.
+ * @type {String}
+ */
+ C.NS = 'con';
+
+ C.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute stickX
+ * @description Stick the drag movement to the X-Axis. Default: false
+ * @type Boolean
+ */
+ stickX: {
+ value: false
+ },
+ /**
+ * @attribute stickY
+ * @description Stick the drag movement to the Y-Axis
+ * @type Boolean
+ */
+ stickY: {
+ value: false
+ },
+ /**
+ * @attribute tickX
+ * @description The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickX: {
+ value: false
+ },
+ /**
+ * @attribute tickY
+ * @description The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickY: {
+ value: false
+ },
+ /**
+ * @attribute tickXArray
+ * @description An array of page coordinates to use as X ticks for drag movement.
+ * @type Array
+ */
+ tickXArray: {
+ value: false
+ },
+ /**
+ * @attribute tickYArray
+ * @description An array of page coordinates to use as Y ticks for drag movement.
+ * @type Array
+ */
+ tickYArray: {
+ value: false
+ },
+ /**
+ * @attribute constrain2region
+ * @description An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to.
+ * @type Object
+ */
+ constrain2region: {
+ value: false,
+ getter: function(r) {
+ if (Y.Lang.isObject(r)) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ },
+ setter: function (r) {
+ if (Y.Lang.isObject(r)) {
+ if (Y.Lang.isNumber(r[TOP]) && Y.Lang.isNumber(r[RIGHT]) && Y.Lang.isNumber(r[LEFT]) && Y.Lang.isNumber(r[BOTTOM])) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ } else if (r !== false) {
+ return false;
+ }
+ return r;
+ }
+ },
+ /**
+ * @attribute gutter
+ * @description CSS style string for the gutter of a region (supports negative values): '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @type String
+ */
+ gutter: {
+ value: '0',
+ setter: function(gutter) {
+ return Y.DD.DDM.cssSizestoObject(gutter);
+ }
+ },
+ /**
+ * @attribute constrain2node
+ * @description Will attempt to constrain the drag node to the boundaries of this node.
+ * @type Object
+ */
+ constrain2node: {
+ value: false,
+ setter: function(n) {
+ if (!this.get(CON_2_REGION)) {
+ var node = Y.Node.get(n);
+ if (node) {
+ return node;
+ }
+ } else if (this.get(CON_2_REGION) !== false) {
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute constrain2view
+ * @description Will attempt to constrain the drag node to the boundaries of the viewport region.
+ * @type Object
+ */
+ constrain2view: {
+ value: false
+ }
+ };
+
+ proto = {
+ initializer: function() {
+ this.get(HOST).on('drag:start', Y.bind(this._handleStart, this));
+ this.get(HOST).after('drag:align', Y.bind(this.align, this));
+ },
+ /**
+ * @private
+ * @method _handleStart
+ * @description Fires on drag:start and clears the _regionCache
+ */
+ _handleStart: function() {
+ this._regionCache = null;
+ },
+ /**
+ * @private
+ * @property _regionCache
+ * @description Store a cache of the region that we are constraining to
+ * @type Object
+ */
+ _regionCache: null,
+ /**
+ * @private
+ * @method _cacheRegion
+ * @description Get's the region and caches it, called from window.resize and when the cache is null
+ */
+ _cacheRegion: function() {
+ this._regionCache = this.get(CON_2_NODE).get('region');
+ },
+ /**
+ * @method getRegion
+ * @description Get the active region: viewport, node, custom region
+ * @param {Boolean} inc Include the node's height and width
+ * @return {Object}
+ */
+ getRegion: function(inc) {
+ var r = {}, oh = null, ow = null,
+ g = this.get('gutter'),
+ host = this.get(HOST);
+
+ if (this.get(CON_2_NODE)) {
+ if (!this._regionCache) {
+ Y.on('resize', Y.bind(this._cacheRegion, this), window);
+ this._cacheRegion();
+ }
+ r = Y.clone(this._regionCache);
+ } else if (this.get(CON_2_REGION)) {
+ r = this.get(CON_2_REGION);
+ } else if (this.get('constrain2view')) {
+ r = host.get(DRAG_NODE).get('viewportRegion');
+ } else {
+ return false;
+ }
+
+ Y.each(g, function(i, n) {
+ if ((n == RIGHT) || (n == BOTTOM)) {
+ r[n] -= i;
+ } else {
+ r[n] += i;
+ }
+ });
+ if (inc) {
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT);
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+ r[RIGHT] = r[RIGHT] - ow;
+ r[BOTTOM] = r[BOTTOM] - oh;
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _checkRegion
+ * @description Check if xy is inside a given region, if not change to it be inside.
+ * @param {Array} _xy The XY to check if it's in the current region, if it isn't inside the region, it will reset the xy array to be inside the region.
+ * @return {Array} The new XY that is inside the region
+ */
+ _checkRegion: function(_xy) {
+ var oxy = _xy,
+ r = this.getRegion(),
+ host = this.get(HOST),
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+
+ if (oxy[1] > (r[BOTTOM] - oh)) {
+ _xy[1] = (r[BOTTOM] - oh);
+ }
+ if (r[TOP] > oxy[1]) {
+ _xy[1] = r[TOP];
+
+ }
+ if (oxy[0] > (r[RIGHT] - ow)) {
+ _xy[0] = (r[RIGHT] - ow);
+ }
+ if (r[LEFT] > oxy[0]) {
+ _xy[0] = r[LEFT];
+ }
+
+ return _xy;
+ },
+ /**
+ * @method inRegion
+ * @description Checks if the XY passed or the dragNode is inside the active region.
+ * @param {Array} xy Optional XY to check, if not supplied this.get('dragNode').getXY() is used.
+ * @return {Boolean} True if the XY is inside the region, false otherwise.
+ */
+ inRegion: function(xy) {
+ xy = xy || this.get(HOST).get(DRAG_NODE).getXY();
+
+ var _xy = this._checkRegion([xy[0], xy[1]]),
+ inside = false;
+ if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) {
+ inside = true;
+ }
+ return inside;
+ },
+ /**
+ * @method align
+ * @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
+ */
+ align: function() {
+ var host = this.get(HOST),
+ _xy = host.actXY,
+ r = this.getRegion(true);
+
+ if (this.get('stickX')) {
+ _xy[1] = (host.startXY[1] - host.deltaXY[1]);
+ }
+ if (this.get('stickY')) {
+ _xy[0] = (host.startXY[0] - host.deltaXY[0]);
+ }
+
+ if (r) {
+ _xy = this._checkRegion(_xy);
+ }
+
+ _xy = this._checkTicks(_xy, r);
+ host.actXY = _xy;
+ },
+ /**
+ * @private
+ * @method _checkTicks
+ * @description This method delegates the proper helper method for tick calculations
+ * @param {Array} xy The XY coords for the Drag
+ * @param {Object} r The optional region that we are bound to.
+ * @return {Array} The calced XY coords
+ */
+ _checkTicks: function(xy, r) {
+ var host = this.get(HOST),
+ lx = (host.startXY[0] - host.deltaXY[0]),
+ ly = (host.startXY[1] - host.deltaXY[1]),
+ xt = this.get('tickX'),
+ yt = this.get('tickY');
+ if (xt && !this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTicks(xy[0], lx, xt, r[LEFT], r[RIGHT]);
+ }
+ if (yt && !this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]);
+ }
+ if (this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]);
+ }
+ if (this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]);
+ }
+
+ return xy;
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(C, Y.Base, proto);
+ Y.Plugin.DDConstrained = C;
+
+ Y.mix(DDM, {
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTicks
+ * @description Helper method to calculate the tick offsets for a given position
+ * @param {Number} pos The current X or Y position
+ * @param {Number} start The start X or Y position
+ * @param {Number} tick The X or Y tick increment
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return {Number} The new position based on the tick calculation
+ */
+ _calcTicks: function(pos, start, tick, off1, off2) {
+ var ix = ((pos - start) / tick),
+ min = Math.floor(ix),
+ max = Math.ceil(ix);
+ if ((min !== 0) || (max !== 0)) {
+ if ((ix >= min) && (ix <= max)) {
+ pos = (start + (tick * min));
+ if (off1 && off2) {
+ if (pos < off1) {
+ pos = (start + (tick * (min + 1)));
+ }
+ if (pos > off2) {
+ pos = (start + (tick * (min - 1)));
+ }
+ }
+ }
+ }
+ return pos;
+ },
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTickArray
+ * @description This method is used with the tickXArray and tickYArray config options
+ * @param {Number} pos The current X or Y position
+ * @param {Number} ticks The array containing our custom tick positions.
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return The tick position
+ */
+ _calcTickArray: function(pos, ticks, off1, off2) {
+ var i = 0, len = ticks.length, next = 0,
+ diff1, diff2, ret;
+
+ if (!ticks || (ticks.length === 0)) {
+ return pos;
+ } else if (ticks[0] >= pos) {
+ return ticks[0];
+ } else {
+ for (i = 0; i < len; i++) {
+ next = (i + 1);
+ if (ticks[next] && ticks[next] >= pos) {
+ diff1 = pos - ticks[i];
+ diff2 = ticks[next] - pos;
+ ret = (diff2 > diff1) ? ticks[i] : ticks[next];
+ if (off1 && off2) {
+ if (ret > off2) {
+ if (ticks[i]) {
+ ret = ticks[i];
+ } else {
+ ret = ticks[len - 1];
+ }
+ }
+ }
+ return ret;
+ }
+
+ }
+ return ticks[ticks.length - 1];
+ }
+ }
+ });
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drag'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-constrain",function(B){var K="dragNode",M="offsetHeight",F="offsetWidth",Q="host",P="constrain2region",H="constrain2node",G="tickXArray",O="tickYArray",N=B.DD.DDM,E="top",J="right",L="bottom",D="left",I=null;var A=function(C){A.superclass.constructor.apply(this,arguments);};A.NAME="DragConstrained";A.NS="con";A.ATTRS={host:{},stickX:{value:false},stickY:{value:false},tickX:{value:false},tickY:{value:false},tickXArray:{value:false},tickYArray:{value:false},constrain2region:{value:false,getter:function(C){if(B.Lang.isObject(C)){var R={};B.mix(R,C);return R;}else{return false;}},setter:function(C){if(B.Lang.isObject(C)){if(B.Lang.isNumber(C[E])&&B.Lang.isNumber(C[J])&&B.Lang.isNumber(C[D])&&B.Lang.isNumber(C[L])){var R={};B.mix(R,C);return R;}else{return false;}}else{if(C!==false){return false;}}return C;}},gutter:{value:"0",setter:function(C){return B.DD.DDM.cssSizestoObject(C);}},constrain2node:{value:false,setter:function(R){if(!this.get(P)){var C=B.Node.get(R);if(C){return C;}}else{if(this.get(P)!==false){}}return false;}},constrain2view:{value:false}};I={initializer:function(){this.get(Q).on("drag:start",B.bind(this._handleStart,this));this.get(Q).after("drag:align",B.bind(this.align,this));},_handleStart:function(){this._regionCache=null;},_regionCache:null,_cacheRegion:function(){this._regionCache=this.get(H).get("region");},getRegion:function(V){var T={},U=null,C=null,S=this.get("gutter"),R=this.get(Q);if(this.get(H)){if(!this._regionCache){B.on("resize",B.bind(this._cacheRegion,this),window);this._cacheRegion();}T=B.clone(this._regionCache);}else{if(this.get(P)){T=this.get(P);}else{if(this.get("constrain2view")){T=R.get(K).get("viewportRegion");}else{return false;}}}B.each(S,function(W,X){if((X==J)||(X==L)){T[X]-=W;}else{T[X]+=W;}});if(V){U=R.get(K).get(M);C=R.get(K).get(F);T[J]=T[J]-C;T[L]=T[L]-U;}return T;},_checkRegion:function(C){var S=C,U=this.getRegion(),T=this.get(Q),V=T.get(K).get(M),R=T.get(K).get(F);if(S[1]>(U[L]-V)){C[1]=(U[L]-V);}if(U[E]>S[1]){C[1]=U[E];}if(S[0]>(U[J]-R)){C[0]=(U[J]-R);}if(U[D]>S[0]){C[0]=U[D];}return C;},inRegion:function(S){S=S||this.get(Q).get(K).getXY();var R=this._checkRegion([S[0],S[1]]),C=false;if((S[0]===R[0])&&(S[1]===R[1])){C=true;}return C;},align:function(){var S=this.get(Q),C=S.actXY,R=this.getRegion(true);if(this.get("stickX")){C[1]=(S.startXY[1]-S.deltaXY[1]);}if(this.get("stickY")){C[0]=(S.startXY[0]-S.deltaXY[0]);}if(R){C=this._checkRegion(C);}C=this._checkTicks(C,R);S.actXY=C;},_checkTicks:function(W,U){var T=this.get(Q),V=(T.startXY[0]-T.deltaXY[0]),S=(T.startXY[1]-T.deltaXY[1]),C=this.get("tickX"),R=this.get("tickY");if(C&&!this.get(G)){W[0]=N._calcTicks(W[0],V,C,U[D],U[J]);}if(R&&!this.get(O)){W[1]=N._calcTicks(W[1],S,R,U[E],U[L]);}if(this.get(G)){W[0]=N._calcTickArray(W[0],this.get(G),U[D],U[J]);}if(this.get(O)){W[1]=N._calcTickArray(W[1],this.get(O),U[E],U[L]);}return W;}};B.namespace("Plugin");B.extend(A,B.Base,I);B.Plugin.DDConstrained=A;B.mix(N,{_calcTicks:function(X,W,T,V,U){var R=((X-W)/T),S=Math.floor(R),C=Math.ceil(R);if((S!==0)||(C!==0)){if((R>=S)&&(R<=C)){X=(W+(T*S));if(V&&U){if(X<V){X=(W+(T*(S+1)));}if(X>U){X=(W+(T*(S-1)));}}}}return X;},_calcTickArray:function(Y,Z,X,U){var R=0,V=Z.length,T=0,S,C,W;if(!Z||(Z.length===0)){return Y;}else{if(Z[0]>=Y){return Z[0];}else{for(R=0;R<V;R++){T=(R+1);if(Z[T]&&Z[T]>=Y){S=Y-Z[R];C=Z[T]-Y;W=(C>S)?Z[R]:Z[T];if(X&&U){if(W>U){if(Z[R]){W=Z[R];}else{W=Z[V-1];}}}return W;}}return Z[Z.length-1];}}}});},"3.0.0",{requires:["dd-drag"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-constrain', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-constrain
+ */
+ /**
+ * This is a plugin for the dd-drag module to add the constraining methods to it. It supports constraining to a renodenode or viewport. It anode* supports tick based moves and XY axis constraints.
+ * @class DragConstrained
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+
+ var DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ HOST = 'host',
+ CON_2_REGION = 'constrain2region',
+ CON_2_NODE = 'constrain2node',
+ TICK_X_ARRAY = 'tickXArray',
+ TICK_Y_ARRAY = 'tickYArray',
+ DDM = Y.DD.DDM,
+ TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+ proto = null;
+
+ var C = function(config) {
+ C.superclass.constructor.apply(this, arguments);
+ };
+
+ C.NAME = 'DragConstrained';
+ /**
+ * @property con
+ * @description The Constrained instance will be placed on the Drag instance under the con namespace.
+ * @type {String}
+ */
+ C.NS = 'con';
+
+ C.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute stickX
+ * @description Stick the drag movement to the X-Axis. Default: false
+ * @type Boolean
+ */
+ stickX: {
+ value: false
+ },
+ /**
+ * @attribute stickY
+ * @description Stick the drag movement to the Y-Axis
+ * @type Boolean
+ */
+ stickY: {
+ value: false
+ },
+ /**
+ * @attribute tickX
+ * @description The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickX: {
+ value: false
+ },
+ /**
+ * @attribute tickY
+ * @description The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickY: {
+ value: false
+ },
+ /**
+ * @attribute tickXArray
+ * @description An array of page coordinates to use as X ticks for drag movement.
+ * @type Array
+ */
+ tickXArray: {
+ value: false
+ },
+ /**
+ * @attribute tickYArray
+ * @description An array of page coordinates to use as Y ticks for drag movement.
+ * @type Array
+ */
+ tickYArray: {
+ value: false
+ },
+ /**
+ * @attribute constrain2region
+ * @description An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to.
+ * @type Object
+ */
+ constrain2region: {
+ value: false,
+ getter: function(r) {
+ if (Y.Lang.isObject(r)) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ },
+ setter: function (r) {
+ if (Y.Lang.isObject(r)) {
+ if (Y.Lang.isNumber(r[TOP]) && Y.Lang.isNumber(r[RIGHT]) && Y.Lang.isNumber(r[LEFT]) && Y.Lang.isNumber(r[BOTTOM])) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ } else if (r !== false) {
+ return false;
+ }
+ return r;
+ }
+ },
+ /**
+ * @attribute gutter
+ * @description CSS style string for the gutter of a region (supports negative values): '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @type String
+ */
+ gutter: {
+ value: '0',
+ setter: function(gutter) {
+ return Y.DD.DDM.cssSizestoObject(gutter);
+ }
+ },
+ /**
+ * @attribute constrain2node
+ * @description Will attempt to constrain the drag node to the boundaries of this node.
+ * @type Object
+ */
+ constrain2node: {
+ value: false,
+ setter: function(n) {
+ if (!this.get(CON_2_REGION)) {
+ var node = Y.Node.get(n);
+ if (node) {
+ return node;
+ }
+ } else if (this.get(CON_2_REGION) !== false) {
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute constrain2view
+ * @description Will attempt to constrain the drag node to the boundaries of the viewport region.
+ * @type Object
+ */
+ constrain2view: {
+ value: false
+ }
+ };
+
+ proto = {
+ initializer: function() {
+ this.get(HOST).on('drag:start', Y.bind(this._handleStart, this));
+ this.get(HOST).after('drag:align', Y.bind(this.align, this));
+ },
+ /**
+ * @private
+ * @method _handleStart
+ * @description Fires on drag:start and clears the _regionCache
+ */
+ _handleStart: function() {
+ this._regionCache = null;
+ },
+ /**
+ * @private
+ * @property _regionCache
+ * @description Store a cache of the region that we are constraining to
+ * @type Object
+ */
+ _regionCache: null,
+ /**
+ * @private
+ * @method _cacheRegion
+ * @description Get's the region and caches it, called from window.resize and when the cache is null
+ */
+ _cacheRegion: function() {
+ this._regionCache = this.get(CON_2_NODE).get('region');
+ },
+ /**
+ * @method getRegion
+ * @description Get the active region: viewport, node, custom region
+ * @param {Boolean} inc Include the node's height and width
+ * @return {Object}
+ */
+ getRegion: function(inc) {
+ var r = {}, oh = null, ow = null,
+ g = this.get('gutter'),
+ host = this.get(HOST);
+
+ if (this.get(CON_2_NODE)) {
+ if (!this._regionCache) {
+ Y.on('resize', Y.bind(this._cacheRegion, this), window);
+ this._cacheRegion();
+ }
+ r = Y.clone(this._regionCache);
+ } else if (this.get(CON_2_REGION)) {
+ r = this.get(CON_2_REGION);
+ } else if (this.get('constrain2view')) {
+ r = host.get(DRAG_NODE).get('viewportRegion');
+ } else {
+ return false;
+ }
+
+ Y.each(g, function(i, n) {
+ if ((n == RIGHT) || (n == BOTTOM)) {
+ r[n] -= i;
+ } else {
+ r[n] += i;
+ }
+ });
+ if (inc) {
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT);
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+ r[RIGHT] = r[RIGHT] - ow;
+ r[BOTTOM] = r[BOTTOM] - oh;
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _checkRegion
+ * @description Check if xy is inside a given region, if not change to it be inside.
+ * @param {Array} _xy The XY to check if it's in the current region, if it isn't inside the region, it will reset the xy array to be inside the region.
+ * @return {Array} The new XY that is inside the region
+ */
+ _checkRegion: function(_xy) {
+ var oxy = _xy,
+ r = this.getRegion(),
+ host = this.get(HOST),
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+
+ if (oxy[1] > (r[BOTTOM] - oh)) {
+ _xy[1] = (r[BOTTOM] - oh);
+ }
+ if (r[TOP] > oxy[1]) {
+ _xy[1] = r[TOP];
+
+ }
+ if (oxy[0] > (r[RIGHT] - ow)) {
+ _xy[0] = (r[RIGHT] - ow);
+ }
+ if (r[LEFT] > oxy[0]) {
+ _xy[0] = r[LEFT];
+ }
+
+ return _xy;
+ },
+ /**
+ * @method inRegion
+ * @description Checks if the XY passed or the dragNode is inside the active region.
+ * @param {Array} xy Optional XY to check, if not supplied this.get('dragNode').getXY() is used.
+ * @return {Boolean} True if the XY is inside the region, false otherwise.
+ */
+ inRegion: function(xy) {
+ xy = xy || this.get(HOST).get(DRAG_NODE).getXY();
+
+ var _xy = this._checkRegion([xy[0], xy[1]]),
+ inside = false;
+ if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) {
+ inside = true;
+ }
+ return inside;
+ },
+ /**
+ * @method align
+ * @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
+ */
+ align: function() {
+ var host = this.get(HOST),
+ _xy = host.actXY,
+ r = this.getRegion(true);
+
+ if (this.get('stickX')) {
+ _xy[1] = (host.startXY[1] - host.deltaXY[1]);
+ }
+ if (this.get('stickY')) {
+ _xy[0] = (host.startXY[0] - host.deltaXY[0]);
+ }
+
+ if (r) {
+ _xy = this._checkRegion(_xy);
+ }
+
+ _xy = this._checkTicks(_xy, r);
+ host.actXY = _xy;
+ },
+ /**
+ * @private
+ * @method _checkTicks
+ * @description This method delegates the proper helper method for tick calculations
+ * @param {Array} xy The XY coords for the Drag
+ * @param {Object} r The optional region that we are bound to.
+ * @return {Array} The calced XY coords
+ */
+ _checkTicks: function(xy, r) {
+ var host = this.get(HOST),
+ lx = (host.startXY[0] - host.deltaXY[0]),
+ ly = (host.startXY[1] - host.deltaXY[1]),
+ xt = this.get('tickX'),
+ yt = this.get('tickY');
+ if (xt && !this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTicks(xy[0], lx, xt, r[LEFT], r[RIGHT]);
+ }
+ if (yt && !this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]);
+ }
+ if (this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]);
+ }
+ if (this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]);
+ }
+
+ return xy;
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(C, Y.Base, proto);
+ Y.Plugin.DDConstrained = C;
+
+ Y.mix(DDM, {
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTicks
+ * @description Helper method to calculate the tick offsets for a given position
+ * @param {Number} pos The current X or Y position
+ * @param {Number} start The start X or Y position
+ * @param {Number} tick The X or Y tick increment
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return {Number} The new position based on the tick calculation
+ */
+ _calcTicks: function(pos, start, tick, off1, off2) {
+ var ix = ((pos - start) / tick),
+ min = Math.floor(ix),
+ max = Math.ceil(ix);
+ if ((min !== 0) || (max !== 0)) {
+ if ((ix >= min) && (ix <= max)) {
+ pos = (start + (tick * min));
+ if (off1 && off2) {
+ if (pos < off1) {
+ pos = (start + (tick * (min + 1)));
+ }
+ if (pos > off2) {
+ pos = (start + (tick * (min - 1)));
+ }
+ }
+ }
+ }
+ return pos;
+ },
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTickArray
+ * @description This method is used with the tickXArray and tickYArray config options
+ * @param {Number} pos The current X or Y position
+ * @param {Number} ticks The array containing our custom tick positions.
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return The tick position
+ */
+ _calcTickArray: function(pos, ticks, off1, off2) {
+ var i = 0, len = ticks.length, next = 0,
+ diff1, diff2, ret;
+
+ if (!ticks || (ticks.length === 0)) {
+ return pos;
+ } else if (ticks[0] >= pos) {
+ return ticks[0];
+ } else {
+ for (i = 0; i < len; i++) {
+ next = (i + 1);
+ if (ticks[next] && ticks[next] >= pos) {
+ diff1 = pos - ticks[i];
+ diff2 = ticks[next] - pos;
+ ret = (diff2 > diff1) ? ticks[i] : ticks[next];
+ if (off1 && off2) {
+ if (ret > off2) {
+ if (ticks[i]) {
+ ret = ticks[i];
+ } else {
+ ret = ticks[len - 1];
+ }
+ }
+ }
+ return ret;
+ }
+
+ }
+ return ticks[ticks.length - 1];
+ }
+ }
+ });
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drag'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm-base', function(Y) {
+
+
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @module dd
+ * @submodule dd-ddm-base
+ */
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @class DDM
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDMBase = function() {
+ DDMBase.superclass.constructor.apply(this, arguments);
+ };
+
+ DDMBase.NAME = 'ddm';
+
+ DDMBase.ATTRS = {
+ /**
+ * @attribute dragCursor
+ * @description The cursor to apply when dragging, if shimmed the shim will get the cursor.
+ * @type String
+ */
+ dragCursor: {
+ value: 'move'
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: 3
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: 1000
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of all future Drag instances.
+ * @type String
+ */
+ dragMode: {
+ value: 'point',
+ setter: function(mode) {
+ this._setDragMode(mode);
+ return mode;
+ }
+ }
+
+ };
+
+ Y.extend(DDMBase, Y.Base, {
+ /**
+ * @property _active
+ * @description flag set when we activate our first drag, so DDM can start listening for events.
+ * @type {Boolean}
+ */
+ _active: null,
+ /**
+ * @private
+ * @method _setDragMode
+ * @description Handler for dragMode attribute setter.
+ * @param String/Number The Number value or the String for the DragMode to default all future drag instances to.
+ * @return Number The Mode to be set
+ */
+ _setDragMode: function(mode) {
+ if (mode === null) {
+ mode = Y.DD.DDM.get('dragMode');
+ }
+ switch (mode) {
+ case 1:
+ case 'intersect':
+ return 1;
+ case 2:
+ case 'strict':
+ return 2;
+ case 0:
+ case 'point':
+ return 0;
+ }
+ return 0;
+ },
+ /**
+ * @property CSS_PREFIX
+ * @description The PREFIX to attach to all DD CSS class names
+ * @type {String}
+ */
+ CSS_PREFIX: 'yui-dd',
+ _activateTargets: function() {},
+ /**
+ * @private
+ * @property _drags
+ * @description Holder for all registered drag elements.
+ * @type {Array}
+ */
+ _drags: [],
+ /**
+ * @property activeDrag
+ * @description A reference to the currently active draggable object.
+ * @type {Drag}
+ */
+ activeDrag: false,
+ /**
+ * @private
+ * @method _regDrag
+ * @description Adds a reference to the drag object to the DDM._drags array, called in the constructor of Drag.
+ * @param {Drag} d The Drag object
+ */
+ _regDrag: function(d) {
+ if (this.getDrag(d.get('node'))) {
+ return false;
+ }
+
+ if (!this._active) {
+ this._setupListeners();
+ }
+ this._drags.push(d);
+ return true;
+ },
+ /**
+ * @private
+ * @method _unregDrag
+ * @description Remove this drag object from the DDM._drags array.
+ * @param {Drag} d The drag object.
+ */
+ _unregDrag: function(d) {
+ var tmp = [];
+ Y.each(this._drags, function(n, i) {
+ if (n !== d) {
+ tmp[tmp.length] = n;
+ }
+ });
+ this._drags = tmp;
+ },
+ /**
+ * @private
+ * @method _setupListeners
+ * @description Add the document listeners.
+ */
+ _setupListeners: function() {
+ this._active = true;
+ var doc = Y.get(document);
+ doc.on('mousemove', Y.bind(this._move, this));
+ //Y.Event.nativeAdd(document, 'mousemove', Y.bind(this._move, this));
+ doc.on('mouseup', Y.bind(this._end, this));
+ },
+ /**
+ * @private
+ * @method _start
+ * @description Internal method used by Drag to signal the start of a drag operation
+ */
+ _start: function() {
+ this.fire('ddm:start');
+ this._startDrag();
+ },
+ /**
+ * @private
+ * @method _startDrag
+ * @description Factory method to be overwritten by other DDM's
+ * @param {Number} x The x position of the drag element
+ * @param {Number} y The y position of the drag element
+ * @param {Number} w The width of the drag element
+ * @param {Number} h The height of the drag element
+ */
+ _startDrag: function() {},
+ /**
+ * @private
+ * @method _endDrag
+ * @description Factory method to be overwritten by other DDM's
+ */
+ _endDrag: function() {},
+ _dropMove: function() {},
+ /**
+ * @private
+ * @method _end
+ * @description Internal method used by Drag to signal the end of a drag operation
+ */
+ _end: function() {
+ if (this.activeDrag) {
+ this._endDrag();
+ this.fire('ddm:end');
+ this.activeDrag.end.call(this.activeDrag);
+ this.activeDrag = null;
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.activeDrag) {
+ this._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Internal listener for the mousemove DOM event to pass to the Drag's move method.
+ * @param {Event.Facade} ev The Dom mousemove Event
+ */
+ _move: function(ev) {
+ if (this.activeDrag) {
+ this.activeDrag._move.call(this.activeDrag, ev);
+ this._dropMove();
+ }
+ },
+ /**
+ * //TODO Private, rename??...
+ * @private
+ * @method cssSizestoObject
+ * @description Helper method to use to set the gutter from the attribute setter.
+ * @param {String} gutter CSS style string for gutter: '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @return {Object} The gutter Object Literal.
+ */
+ cssSizestoObject: function(gutter) {
+ var x = gutter.split(' ');
+
+ switch (x.length) {
+ case 1: x[1] = x[2] = x[3] = x[0]; break;
+ case 2: x[2] = x[0]; x[3] = x[1]; break;
+ case 3: x[3] = x[1]; break;
+ }
+
+ return {
+ top : parseInt(x[0],10),
+ right : parseInt(x[1],10),
+ bottom: parseInt(x[2],10),
+ left : parseInt(x[3],10)
+ };
+ },
+ /**
+ * @method getDrag
+ * @description Get a valid Drag instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drag Object
+ * @return {Object}
+ */
+ getDrag: function(node) {
+ var drag = false,
+ n = Y.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this._drags, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drag = v;
+ }
+ });
+ }
+ return drag;
+ }
+ });
+
+ Y.namespace('DD');
+ Y.DD.DDM = new DDMBase();
+
+ /**
+ * @event ddm:start
+ * @description Fires from the DDM before all drag events fire.
+ * @type {Event.Custom}
+ */
+ /**
+ * @event ddm:end
+ * @description Fires from the DDM after the DDM finishes, before the drag end events.
+ * @type {Event.Custom}
+ */
+
+
+
+
+}, '3.0.0' ,{requires:['node', 'base'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-ddm-base",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};A.NAME="ddm";A.ATTRS={dragCursor:{value:"move"},clickPixelThresh:{value:3},clickTimeThresh:{value:1000},dragMode:{value:"point",setter:function(C){this._setDragMode(C);return C;}}};B.extend(A,B.Base,{_active:null,_setDragMode:function(C){if(C===null){C=B.DD.DDM.get("dragMode");}switch(C){case 1:case"intersect":return 1;case 2:case"strict":return 2;case 0:case"point":return 0;}return 0;},CSS_PREFIX:"yui-dd",_activateTargets:function(){},_drags:[],activeDrag:false,_regDrag:function(C){if(this.getDrag(C.get("node"))){return false;}if(!this._active){this._setupListeners();}this._drags.push(C);return true;},_unregDrag:function(D){var C=[];B.each(this._drags,function(F,E){if(F!==D){C[C.length]=F;}});this._drags=C;},_setupListeners:function(){this._active=true;var C=B.get(document);C.on("mousemove",B.bind(this._move,this));C.on("mouseup",B.bind(this._end,this));},_start:function(){this.fire("ddm:start");this._startDrag();},_startDrag:function(){},_endDrag:function(){},_dropMove:function(){},_end:function(){if(this.activeDrag){this._endDrag();this.fire("ddm:end");this.activeDrag.end.call(this.activeDrag);this.activeDrag=null;}},stopDrag:function(){if(this.activeDrag){this._end();}return this;},_move:function(C){if(this.activeDrag){this.activeDrag._move.call(this.activeDrag,C);this._dropMove();}},cssSizestoObject:function(D){var C=D.split(" ");switch(C.length){case 1:C[1]=C[2]=C[3]=C[0];break;case 2:C[2]=C[0];C[3]=C[1];break;case 3:C[3]=C[1];break;}return{top:parseInt(C[0],10),right:parseInt(C[1],10),bottom:parseInt(C[2],10),left:parseInt(C[3],10)};},getDrag:function(D){var C=false,E=B.get(D);if(E instanceof B.Node){B.each(this._drags,function(G,F){if(E.compareTo(G.get("node"))){C=G;}});}return C;}});B.namespace("DD");B.DD.DDM=new A();},"3.0.0",{requires:["node","base"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm-base', function(Y) {
+
+
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @module dd
+ * @submodule dd-ddm-base
+ */
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @class DDM
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDMBase = function() {
+ DDMBase.superclass.constructor.apply(this, arguments);
+ };
+
+ DDMBase.NAME = 'ddm';
+
+ DDMBase.ATTRS = {
+ /**
+ * @attribute dragCursor
+ * @description The cursor to apply when dragging, if shimmed the shim will get the cursor.
+ * @type String
+ */
+ dragCursor: {
+ value: 'move'
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: 3
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: 1000
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of all future Drag instances.
+ * @type String
+ */
+ dragMode: {
+ value: 'point',
+ setter: function(mode) {
+ this._setDragMode(mode);
+ return mode;
+ }
+ }
+
+ };
+
+ Y.extend(DDMBase, Y.Base, {
+ /**
+ * @property _active
+ * @description flag set when we activate our first drag, so DDM can start listening for events.
+ * @type {Boolean}
+ */
+ _active: null,
+ /**
+ * @private
+ * @method _setDragMode
+ * @description Handler for dragMode attribute setter.
+ * @param String/Number The Number value or the String for the DragMode to default all future drag instances to.
+ * @return Number The Mode to be set
+ */
+ _setDragMode: function(mode) {
+ if (mode === null) {
+ mode = Y.DD.DDM.get('dragMode');
+ }
+ switch (mode) {
+ case 1:
+ case 'intersect':
+ return 1;
+ case 2:
+ case 'strict':
+ return 2;
+ case 0:
+ case 'point':
+ return 0;
+ }
+ return 0;
+ },
+ /**
+ * @property CSS_PREFIX
+ * @description The PREFIX to attach to all DD CSS class names
+ * @type {String}
+ */
+ CSS_PREFIX: 'yui-dd',
+ _activateTargets: function() {},
+ /**
+ * @private
+ * @property _drags
+ * @description Holder for all registered drag elements.
+ * @type {Array}
+ */
+ _drags: [],
+ /**
+ * @property activeDrag
+ * @description A reference to the currently active draggable object.
+ * @type {Drag}
+ */
+ activeDrag: false,
+ /**
+ * @private
+ * @method _regDrag
+ * @description Adds a reference to the drag object to the DDM._drags array, called in the constructor of Drag.
+ * @param {Drag} d The Drag object
+ */
+ _regDrag: function(d) {
+ if (this.getDrag(d.get('node'))) {
+ return false;
+ }
+
+ if (!this._active) {
+ this._setupListeners();
+ }
+ this._drags.push(d);
+ return true;
+ },
+ /**
+ * @private
+ * @method _unregDrag
+ * @description Remove this drag object from the DDM._drags array.
+ * @param {Drag} d The drag object.
+ */
+ _unregDrag: function(d) {
+ var tmp = [];
+ Y.each(this._drags, function(n, i) {
+ if (n !== d) {
+ tmp[tmp.length] = n;
+ }
+ });
+ this._drags = tmp;
+ },
+ /**
+ * @private
+ * @method _setupListeners
+ * @description Add the document listeners.
+ */
+ _setupListeners: function() {
+ this._active = true;
+ var doc = Y.get(document);
+ doc.on('mousemove', Y.bind(this._move, this));
+ //Y.Event.nativeAdd(document, 'mousemove', Y.bind(this._move, this));
+ doc.on('mouseup', Y.bind(this._end, this));
+ },
+ /**
+ * @private
+ * @method _start
+ * @description Internal method used by Drag to signal the start of a drag operation
+ */
+ _start: function() {
+ this.fire('ddm:start');
+ this._startDrag();
+ },
+ /**
+ * @private
+ * @method _startDrag
+ * @description Factory method to be overwritten by other DDM's
+ * @param {Number} x The x position of the drag element
+ * @param {Number} y The y position of the drag element
+ * @param {Number} w The width of the drag element
+ * @param {Number} h The height of the drag element
+ */
+ _startDrag: function() {},
+ /**
+ * @private
+ * @method _endDrag
+ * @description Factory method to be overwritten by other DDM's
+ */
+ _endDrag: function() {},
+ _dropMove: function() {},
+ /**
+ * @private
+ * @method _end
+ * @description Internal method used by Drag to signal the end of a drag operation
+ */
+ _end: function() {
+ if (this.activeDrag) {
+ this._endDrag();
+ this.fire('ddm:end');
+ this.activeDrag.end.call(this.activeDrag);
+ this.activeDrag = null;
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.activeDrag) {
+ this._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Internal listener for the mousemove DOM event to pass to the Drag's move method.
+ * @param {Event.Facade} ev The Dom mousemove Event
+ */
+ _move: function(ev) {
+ if (this.activeDrag) {
+ this.activeDrag._move.call(this.activeDrag, ev);
+ this._dropMove();
+ }
+ },
+ /**
+ * //TODO Private, rename??...
+ * @private
+ * @method cssSizestoObject
+ * @description Helper method to use to set the gutter from the attribute setter.
+ * @param {String} gutter CSS style string for gutter: '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @return {Object} The gutter Object Literal.
+ */
+ cssSizestoObject: function(gutter) {
+ var x = gutter.split(' ');
+
+ switch (x.length) {
+ case 1: x[1] = x[2] = x[3] = x[0]; break;
+ case 2: x[2] = x[0]; x[3] = x[1]; break;
+ case 3: x[3] = x[1]; break;
+ }
+
+ return {
+ top : parseInt(x[0],10),
+ right : parseInt(x[1],10),
+ bottom: parseInt(x[2],10),
+ left : parseInt(x[3],10)
+ };
+ },
+ /**
+ * @method getDrag
+ * @description Get a valid Drag instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drag Object
+ * @return {Object}
+ */
+ getDrag: function(node) {
+ var drag = false,
+ n = Y.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this._drags, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drag = v;
+ }
+ });
+ }
+ return drag;
+ }
+ });
+
+ Y.namespace('DD');
+ Y.DD.DDM = new DDMBase();
+
+ /**
+ * @event ddm:start
+ * @description Fires from the DDM before all drag events fire.
+ * @type {Event.Custom}
+ */
+ /**
+ * @event ddm:end
+ * @description Fires from the DDM after the DDM finishes, before the drag end events.
+ * @type {Event.Custom}
+ */
+
+
+
+
+}, '3.0.0' ,{requires:['node', 'base'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm-base Class to add support for the viewport shim to allow a draggable node to drag to be dragged over an iframe or any other node that traps mousemove events.
+ * It is also required to have Drop Targets enabled, as the viewport shim will contain the shims for the Drop Targets.
+ * @module dd
+ * @submodule dd-ddm
+ * @for DDM
+ * @namespace DD
+ */
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _pg
+ * @description The shim placed over the screen to track the mousemove event.
+ * @type {Node}
+ */
+ _pg: null,
+ /**
+ * @private
+ * @property _debugShim
+ * @description Set this to true to set the shims opacity to .5 for debugging it, default: false.
+ * @type {Boolean}
+ */
+ _debugShim: false,
+ _activateTargets: function() {},
+ _deactivateTargets: function() {},
+ _startDrag: function() {
+ if (this.activeDrag.get('useShim')) {
+ this._pg_activate();
+ this._activateTargets();
+ }
+ },
+ _endDrag: function() {
+ this._pg_deactivate();
+ this._deactivateTargets();
+ },
+ /**
+ * @private
+ * @method _pg_deactivate
+ * @description Deactivates the shim
+ */
+ _pg_deactivate: function() {
+ this._pg.setStyle('display', 'none');
+ },
+ /**
+ * @private
+ * @method _pg_activate
+ * @description Activates the shim
+ */
+ _pg_activate: function() {
+ var ah = this.activeDrag.get('activeHandle'), cur = 'auto';
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = this.get('dragCursor');
+ }
+
+ this._pg_size();
+ this._pg.setStyles({
+ top: 0,
+ left: 0,
+ display: 'block',
+ opacity: ((this._debugShim) ? '.5' : '0'),
+ cursor: cur
+ });
+ },
+ /**
+ * @private
+ * @method _pg_size
+ * @description Sizes the shim on: activatation, window:scroll, window:resize
+ */
+ _pg_size: function() {
+ if (this.activeDrag) {
+ var b = Y.get('body'),
+ h = b.get('docHeight'),
+ w = b.get('docWidth');
+ this._pg.setStyles({
+ height: h + 'px',
+ width: w + 'px'
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _createPG
+ * @description Creates the shim and adds it's listeners to it.
+ */
+ _createPG: function() {
+ var pg = Y.Node.create('<div></div>'),
+ bd = Y.get('body');
+ pg.setStyles({
+ top: '0',
+ left: '0',
+ position: 'absolute',
+ zIndex: '9999',
+ overflow: 'hidden',
+ backgroundColor: 'red',
+ display: 'none',
+ height: '5px',
+ width: '5px'
+ });
+ pg.set('id', Y.stamp(pg));
+ pg.addClass('yui-dd-shim');
+ if (bd.get('firstChild')) {
+ bd.insertBefore(pg, bd.get('firstChild'));
+ } else {
+ bd.appendChild(pg);
+ }
+ this._pg = pg;
+ this._pg.on('mouseup', Y.bind(this._end, this));
+ this._pg.on('mousemove', Y.bind(this._move, this));
+
+ var win = Y.get(window);
+ Y.on('window:resize', Y.bind(this._pg_size, this));
+ win.on('scroll', Y.bind(this._pg_size, this));
+ }
+ }, true);
+
+ Y.on('domready', Y.bind(Y.DD.DDM._createPG, Y.DD.DDM));
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base', 'event-resize'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm-drop', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm Class to add support for the placement of Drop Target shims inside the viewport shim. It also handles all Drop Target related events and interactions.
+ * @module dd
+ * @submodule dd-ddm-drop
+ * @for DDM
+ * @namespace DD
+ */
+
+ //TODO CSS class name for the bestMatch..
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _noShim
+ * @description This flag turns off the use of the mouseover/mouseout shim. It should not be used unless you know what you are doing.
+ * @type {Boolean}
+ */
+ _noShim: false,
+ /**
+ * @private
+ * @property _activeShims
+ * @description Placeholder for all active shims on the page
+ * @type {Array}
+ */
+ _activeShims: [],
+ /**
+ * @private
+ * @method _hasActiveShim
+ * @description This method checks the _activeShims Object to see if there is a shim active.
+ * @return {Boolean}
+ */
+ _hasActiveShim: function() {
+ if (this._noShim) {
+ return true;
+ }
+ return this._activeShims.length;
+ },
+ /**
+ * @private
+ * @method _addActiveShim
+ * @description Adds a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to add to the list.
+ */
+ _addActiveShim: function(d) {
+ this._activeShims[this._activeShims.length] = d;
+ },
+ /**
+ * @private
+ * @method _removeActiveShim
+ * @description Removes a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to remove from the list.
+ */
+ _removeActiveShim: function(d) {
+ var s = [];
+ Y.each(this._activeShims, function(v, k) {
+ if (v._yuid !== d._yuid) {
+ s[s.length] = v;
+ }
+
+ });
+ this._activeShims = s;
+ },
+ /**
+ * @method syncActiveShims
+ * @description This method will sync the position of the shims on the Drop Targets that are currently active.
+ * @param {Boolean} force Resize/sync all Targets.
+ */
+ syncActiveShims: function(force) {
+ Y.later(0, this, function(force) {
+ var drops = ((force) ? this.targets : this._lookup());
+ Y.each(drops, function(v, k) {
+ v.sizeShim.call(v);
+ }, this);
+ }, force);
+ },
+ /**
+ * @private
+ * @property mode
+ * @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
+ * @type Number
+ */
+ mode: 0,
+ /**
+ * @private
+ * @property POINT
+ * @description In point mode, a Drop is targeted by the cursor being over the Target
+ * @type Number
+ */
+ POINT: 0,
+ /**
+ * @private
+ * @property INTERSECT
+ * @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
+ * @type Number
+ */
+ INTERSECT: 1,
+ /**
+ * @private
+ * @property STRICT
+ * @description In strict mode, a Drop is targeted by the "entire" drag node being over the Target
+ * @type Number
+ */
+ STRICT: 2,
+ /**
+ * @property useHash
+ * @description Should we only check targets that are in the viewport on drags (for performance), default: true
+ * @type {Boolean}
+ */
+ useHash: true,
+ /**
+ * @property activeDrop
+ * @description A reference to the active Drop Target
+ * @type {Object}
+ */
+ activeDrop: null,
+ /**
+ * @property validDrops
+ * @description An array of the valid Drop Targets for this interaction.
+ * @type {Array}
+ */
+ //TODO Change array/object literals to be in sync..
+ validDrops: [],
+ /**
+ * @property otherDrops
+ * @description An object literal of Other Drop Targets that we encountered during this interaction (in the case of overlapping Drop Targets)
+ * @type {Object}
+ */
+ otherDrops: {},
+ /**
+ * @property targets
+ * @description All of the Targets
+ * @type {Array}
+ */
+ targets: [],
+ /**
+ * @private
+ * @method _addValid
+ * @description Add a Drop Target to the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _addValid: function(drop) {
+ this.validDrops[this.validDrops.length] = drop;
+ return this;
+ },
+ /**
+ * @private
+ * @method _removeValid
+ * @description Removes a Drop Target from the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _removeValid: function(drop) {
+ var drops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ drops[drops.length] = v;
+ }
+ });
+
+ this.validDrops = drops;
+ return this;
+ },
+ /**
+ * @method isOverTarget
+ * @description Check to see if the Drag element is over the target, method varies on current mode
+ * @param {Object} drop The drop to check against
+ * @return {Boolean}
+ */
+ isOverTarget: function(drop) {
+ if (this.activeDrag && drop) {
+ var xy = this.activeDrag.mouseXY, r, dMode = this.activeDrag.get('dragMode'),
+ aRegion;
+ if (xy && this.activeDrag) {
+ aRegion = this.activeDrag.region;
+ if (dMode == this.STRICT) {
+ return this.activeDrag.get('dragNode').inRegion(drop.region, true, aRegion);
+ } else {
+ if (drop && drop.shim) {
+ if ((dMode == this.INTERSECT) && this._noShim) {
+ r = ((aRegion) ? aRegion : this.activeDrag.get('node'));
+ return drop.get('node').intersect(r).inRegion;
+ } else {
+ return drop.shim.intersect({
+ top: xy[1],
+ bottom: xy[1],
+ left: xy[0],
+ right: xy[0]
+ }, drop.region).inRegion;
+ }
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @method clearCache
+ * @description Clears the cache data used for this interaction.
+ */
+ clearCache: function() {
+ this.validDrops = [];
+ this.otherDrops = {};
+ this._activeShims = [];
+ },
+ /**
+ * @private
+ * @method _activateTargets
+ * @description Clear the cache and activate the shims of all the targets
+ */
+ _activateTargets: function() {
+ this.clearCache();
+ Y.each(this.targets, function(v, k) {
+ v._activateShim.apply(v, []);
+ }, this);
+ this._handleTargetOver();
+
+ },
+ /**
+ * @method getBestMatch
+ * @description This method will gather the area for all potential targets and see which has the hightest covered area and return it.
+ * @param {Array} drops An Array of drops to scan for the best match.
+ * @param {Boolean} all If present, it returns an Array. First item is best match, second is an Array of the other items in the original Array.
+ * @return {Object or Array}
+ */
+ getBestMatch: function(drops, all) {
+ var biggest = null, area = 0, out;
+
+ Y.each(drops, function(v, k) {
+ var inter = this.activeDrag.get('dragNode').intersect(v.get('node'));
+ v.region.area = inter.area;
+
+ if (inter.inRegion) {
+ if (inter.area > area) {
+ area = inter.area;
+ biggest = v;
+ }
+ }
+ }, this);
+ if (all) {
+ out = [];
+ //TODO Sort the others in numeric order by area covered..
+ Y.each(drops, function(v, k) {
+ if (v !== biggest) {
+ out[out.length] = v;
+ }
+ }, this);
+ return [biggest, out];
+ } else {
+ return biggest;
+ }
+ },
+ /**
+ * @private
+ * @method _deactivateTargets
+ * @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
+ */
+ _deactivateTargets: function() {
+ var other = [], tmp,
+ activeDrag = this.activeDrag,
+ activeDrop = this.activeDrop;
+
+ //TODO why is this check so hard??
+ if (activeDrag && activeDrop && this.otherDrops[activeDrop]) {
+ if (!activeDrag.get('dragMode')) {
+ //TODO otherDrops -- private..
+ other = this.otherDrops;
+ delete other[activeDrop];
+ } else {
+ tmp = this.getBestMatch(this.otherDrops, true);
+ activeDrop = tmp[0];
+ other = tmp[1];
+ }
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ if (activeDrop) {
+ activeDrop.fire('drop:hit', { drag: activeDrag, drop: activeDrop, others: other });
+ activeDrag.fire('drag:drophit', { drag: activeDrag, drop: activeDrop, others: other });
+ }
+ } else if (activeDrag) {
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ activeDrag.fire('drag:dropmiss', { pageX: activeDrag.lastXY[0], pageY: activeDrag.lastXY[1] });
+ } else {
+ }
+
+ this.activeDrop = null;
+
+ Y.each(this.targets, function(v, k) {
+ v._deactivateShim.apply(v, []);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _dropMove
+ * @description This method is called when the move method is called on the Drag Object.
+ */
+ _dropMove: function() {
+ if (this._hasActiveShim()) {
+ this._handleTargetOver();
+ } else {
+ Y.each(this.otherDrops, function(v, k) {
+ v._handleOut.apply(v, []);
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _lookup
+ * @description Filters the list of Drops down to those in the viewport.
+ * @return {Array} The valid Drop Targets that are in the viewport.
+ */
+ _lookup: function() {
+ if (!this.useHash || this._noShim) {
+ return this.validDrops;
+ }
+ var drops = [];
+ //Only scan drop shims that are in the Viewport
+ Y.each(this.validDrops, function(v, k) {
+ if (v.shim && v.shim.inViewportRegion(false, v.region)) {
+ drops[drops.length] = v;
+ }
+ });
+ return drops;
+
+ },
+ /**
+ * @private
+ * @method _handleTargetOver
+ * @description This method execs _handleTargetOver on all valid Drop Targets
+ */
+ _handleTargetOver: function() {
+ var drops = this._lookup();
+ Y.each(drops, function(v, k) {
+ v._handleTargetOver.call(v);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _regTarget
+ * @description Add the passed in Target to the targets collection
+ * @param {Object} t The Target to add to the targets collection
+ */
+ _regTarget: function(t) {
+ this.targets[this.targets.length] = t;
+ },
+ /**
+ * @private
+ * @method _unregTarget
+ * @description Remove the passed in Target from the targets collection
+ * @param {Object} drop The Target to remove from the targets collection
+ */
+ _unregTarget: function(drop) {
+ var targets = [], vdrops;
+ Y.each(this.targets, function(v, k) {
+ if (v != drop) {
+ targets[targets.length] = v;
+ }
+ }, this);
+ this.targets = targets;
+
+ vdrops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ vdrops[vdrops.length] = v;
+ }
+ });
+
+ this.validDrops = vdrops;
+ },
+ /**
+ * @method getDrop
+ * @description Get a valid Drop instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drop Object
+ * @return {Object}
+ */
+ getDrop: function(node) {
+ var drop = false,
+ n = Y.Node.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this.targets, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drop = v;
+ }
+ });
+ }
+ return drop;
+ }
+ }, true);
+
+
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-ddm-drop",function(A){A.mix(A.DD.DDM,{_noShim:false,_activeShims:[],_hasActiveShim:function(){if(this._noShim){return true;}return this._activeShims.length;},_addActiveShim:function(B){this._activeShims[this._activeShims.length]=B;},_removeActiveShim:function(C){var B=[];A.each(this._activeShims,function(E,D){if(E._yuid!==C._yuid){B[B.length]=E;}});this._activeShims=B;},syncActiveShims:function(B){A.later(0,this,function(C){var D=((C)?this.targets:this._lookup());A.each(D,function(F,E){F.sizeShim.call(F);},this);},B);},mode:0,POINT:0,INTERSECT:1,STRICT:2,useHash:true,activeDrop:null,validDrops:[],otherDrops:{},targets:[],_addValid:function(B){this.validDrops[this.validDrops.length]=B;return this;},_removeValid:function(B){var C=[];A.each(this.validDrops,function(E,D){if(E!==B){C[C.length]=E;}});this.validDrops=C;return this;},isOverTarget:function(C){if(this.activeDrag&&C){var F=this.activeDrag.mouseXY,E,B=this.activeDrag.get("dragMode"),D;if(F&&this.activeDrag){D=this.activeDrag.region;if(B==this.STRICT){return this.activeDrag.get("dragNode").inRegion(C.region,true,D);}else{if(C&&C.shim){if((B==this.INTERSECT)&&this._noShim){E=((D)?D:this.activeDrag.get("node"));return C.get("node").intersect(E).inRegion;}else{return C.shim.intersect({top:F[1],bottom:F[1],left:F[0],right:F[0]},C.region).inRegion;}}else{return false;}}}else{return false;}}else{return false;}},clearCache:function(){this.validDrops=[];this.otherDrops={};this._activeShims=[];},_activateTargets:function(){this.clearCache();A.each(this.targets,function(C,B){C._activateShim.apply(C,[]);},this);this._handleTargetOver();},getBestMatch:function(F,D){var C=null,E=0,B;A.each(F,function(I,H){var G=this.activeDrag.get("dragNode").intersect(I.get("node"));I.region.area=G.area;if(G.inRegion){if(G.area>E){E=G.area;C=I;}}},this);if(D){B=[];A.each(F,function(H,G){if(H!==C){B[B.length]=H;}},this);return[C,B];}else{return C;}},_deactivateTargets:function(){var B=[],C,E=this.activeDrag,D=this.activeDrop;if(E&&D&&this.otherDrops[D]){if(!E.get("dragMode")){B=this.otherDrops;delete B[D];}else{C=this.getBestMatch(this.otherDrops,true);D=C[0];B=C[1];}E.get("node").removeClass(this.CSS_PREFIX+"-drag-over");if(D){D.fire("drop:hit",{drag:E,drop:D,others:B});E.fire("drag:drophit",{drag:E,drop:D,others:B});}}else{if(E){E.get("node").removeClass(this.CSS_PREFIX+"-drag-over");E.fire("drag:dropmiss",{pageX:E.lastXY[0],pageY:E.lastXY[1]});}else{}}this.activeDrop=null;A.each(this.targets,function(G,F){G._deactivateShim.apply(G,[]);},this);},_dropMove:function(){if(this._hasActiveShim()){this._handleTargetOver();}else{A.each(this.otherDrops,function(C,B){C._handleOut.apply(C,[]);});}},_lookup:function(){if(!this.useHash||this._noShim){return this.validDrops;}var B=[];A.each(this.validDrops,function(D,C){if(D.shim&&D.shim.inViewportRegion(false,D.region)){B[B.length]=D;}});return B;},_handleTargetOver:function(){var B=this._lookup();A.each(B,function(D,C){D._handleTargetOver.call(D);},this);},_regTarget:function(B){this.targets[this.targets.length]=B;},_unregTarget:function(C){var B=[],D;A.each(this.targets,function(F,E){if(F!=C){B[B.length]=F;}},this);this.targets=B;D=[];A.each(this.validDrops,function(F,E){if(F!==C){D[D.length]=F;}});this.validDrops=D;},getDrop:function(C){var B=false,D=A.Node.get(C);if(D instanceof A.Node){A.each(this.targets,function(F,E){if(D.compareTo(F.get("node"))){B=F;}});}return B;}},true);},"3.0.0",{requires:["dd-ddm"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm-drop', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm Class to add support for the placement of Drop Target shims inside the viewport shim. It also handles all Drop Target related events and interactions.
+ * @module dd
+ * @submodule dd-ddm-drop
+ * @for DDM
+ * @namespace DD
+ */
+
+ //TODO CSS class name for the bestMatch..
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _noShim
+ * @description This flag turns off the use of the mouseover/mouseout shim. It should not be used unless you know what you are doing.
+ * @type {Boolean}
+ */
+ _noShim: false,
+ /**
+ * @private
+ * @property _activeShims
+ * @description Placeholder for all active shims on the page
+ * @type {Array}
+ */
+ _activeShims: [],
+ /**
+ * @private
+ * @method _hasActiveShim
+ * @description This method checks the _activeShims Object to see if there is a shim active.
+ * @return {Boolean}
+ */
+ _hasActiveShim: function() {
+ if (this._noShim) {
+ return true;
+ }
+ return this._activeShims.length;
+ },
+ /**
+ * @private
+ * @method _addActiveShim
+ * @description Adds a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to add to the list.
+ */
+ _addActiveShim: function(d) {
+ this._activeShims[this._activeShims.length] = d;
+ },
+ /**
+ * @private
+ * @method _removeActiveShim
+ * @description Removes a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to remove from the list.
+ */
+ _removeActiveShim: function(d) {
+ var s = [];
+ Y.each(this._activeShims, function(v, k) {
+ if (v._yuid !== d._yuid) {
+ s[s.length] = v;
+ }
+
+ });
+ this._activeShims = s;
+ },
+ /**
+ * @method syncActiveShims
+ * @description This method will sync the position of the shims on the Drop Targets that are currently active.
+ * @param {Boolean} force Resize/sync all Targets.
+ */
+ syncActiveShims: function(force) {
+ Y.later(0, this, function(force) {
+ var drops = ((force) ? this.targets : this._lookup());
+ Y.each(drops, function(v, k) {
+ v.sizeShim.call(v);
+ }, this);
+ }, force);
+ },
+ /**
+ * @private
+ * @property mode
+ * @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
+ * @type Number
+ */
+ mode: 0,
+ /**
+ * @private
+ * @property POINT
+ * @description In point mode, a Drop is targeted by the cursor being over the Target
+ * @type Number
+ */
+ POINT: 0,
+ /**
+ * @private
+ * @property INTERSECT
+ * @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
+ * @type Number
+ */
+ INTERSECT: 1,
+ /**
+ * @private
+ * @property STRICT
+ * @description In strict mode, a Drop is targeted by the "entire" drag node being over the Target
+ * @type Number
+ */
+ STRICT: 2,
+ /**
+ * @property useHash
+ * @description Should we only check targets that are in the viewport on drags (for performance), default: true
+ * @type {Boolean}
+ */
+ useHash: true,
+ /**
+ * @property activeDrop
+ * @description A reference to the active Drop Target
+ * @type {Object}
+ */
+ activeDrop: null,
+ /**
+ * @property validDrops
+ * @description An array of the valid Drop Targets for this interaction.
+ * @type {Array}
+ */
+ //TODO Change array/object literals to be in sync..
+ validDrops: [],
+ /**
+ * @property otherDrops
+ * @description An object literal of Other Drop Targets that we encountered during this interaction (in the case of overlapping Drop Targets)
+ * @type {Object}
+ */
+ otherDrops: {},
+ /**
+ * @property targets
+ * @description All of the Targets
+ * @type {Array}
+ */
+ targets: [],
+ /**
+ * @private
+ * @method _addValid
+ * @description Add a Drop Target to the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _addValid: function(drop) {
+ this.validDrops[this.validDrops.length] = drop;
+ return this;
+ },
+ /**
+ * @private
+ * @method _removeValid
+ * @description Removes a Drop Target from the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _removeValid: function(drop) {
+ var drops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ drops[drops.length] = v;
+ }
+ });
+
+ this.validDrops = drops;
+ return this;
+ },
+ /**
+ * @method isOverTarget
+ * @description Check to see if the Drag element is over the target, method varies on current mode
+ * @param {Object} drop The drop to check against
+ * @return {Boolean}
+ */
+ isOverTarget: function(drop) {
+ if (this.activeDrag && drop) {
+ var xy = this.activeDrag.mouseXY, r, dMode = this.activeDrag.get('dragMode'),
+ aRegion;
+ if (xy && this.activeDrag) {
+ aRegion = this.activeDrag.region;
+ if (dMode == this.STRICT) {
+ return this.activeDrag.get('dragNode').inRegion(drop.region, true, aRegion);
+ } else {
+ if (drop && drop.shim) {
+ if ((dMode == this.INTERSECT) && this._noShim) {
+ r = ((aRegion) ? aRegion : this.activeDrag.get('node'));
+ return drop.get('node').intersect(r).inRegion;
+ } else {
+ return drop.shim.intersect({
+ top: xy[1],
+ bottom: xy[1],
+ left: xy[0],
+ right: xy[0]
+ }, drop.region).inRegion;
+ }
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @method clearCache
+ * @description Clears the cache data used for this interaction.
+ */
+ clearCache: function() {
+ this.validDrops = [];
+ this.otherDrops = {};
+ this._activeShims = [];
+ },
+ /**
+ * @private
+ * @method _activateTargets
+ * @description Clear the cache and activate the shims of all the targets
+ */
+ _activateTargets: function() {
+ this.clearCache();
+ Y.each(this.targets, function(v, k) {
+ v._activateShim.apply(v, []);
+ }, this);
+ this._handleTargetOver();
+
+ },
+ /**
+ * @method getBestMatch
+ * @description This method will gather the area for all potential targets and see which has the hightest covered area and return it.
+ * @param {Array} drops An Array of drops to scan for the best match.
+ * @param {Boolean} all If present, it returns an Array. First item is best match, second is an Array of the other items in the original Array.
+ * @return {Object or Array}
+ */
+ getBestMatch: function(drops, all) {
+ var biggest = null, area = 0, out;
+
+ Y.each(drops, function(v, k) {
+ var inter = this.activeDrag.get('dragNode').intersect(v.get('node'));
+ v.region.area = inter.area;
+
+ if (inter.inRegion) {
+ if (inter.area > area) {
+ area = inter.area;
+ biggest = v;
+ }
+ }
+ }, this);
+ if (all) {
+ out = [];
+ //TODO Sort the others in numeric order by area covered..
+ Y.each(drops, function(v, k) {
+ if (v !== biggest) {
+ out[out.length] = v;
+ }
+ }, this);
+ return [biggest, out];
+ } else {
+ return biggest;
+ }
+ },
+ /**
+ * @private
+ * @method _deactivateTargets
+ * @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
+ */
+ _deactivateTargets: function() {
+ var other = [], tmp,
+ activeDrag = this.activeDrag,
+ activeDrop = this.activeDrop;
+
+ //TODO why is this check so hard??
+ if (activeDrag && activeDrop && this.otherDrops[activeDrop]) {
+ if (!activeDrag.get('dragMode')) {
+ //TODO otherDrops -- private..
+ other = this.otherDrops;
+ delete other[activeDrop];
+ } else {
+ tmp = this.getBestMatch(this.otherDrops, true);
+ activeDrop = tmp[0];
+ other = tmp[1];
+ }
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ if (activeDrop) {
+ activeDrop.fire('drop:hit', { drag: activeDrag, drop: activeDrop, others: other });
+ activeDrag.fire('drag:drophit', { drag: activeDrag, drop: activeDrop, others: other });
+ }
+ } else if (activeDrag) {
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ activeDrag.fire('drag:dropmiss', { pageX: activeDrag.lastXY[0], pageY: activeDrag.lastXY[1] });
+ } else {
+ }
+
+ this.activeDrop = null;
+
+ Y.each(this.targets, function(v, k) {
+ v._deactivateShim.apply(v, []);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _dropMove
+ * @description This method is called when the move method is called on the Drag Object.
+ */
+ _dropMove: function() {
+ if (this._hasActiveShim()) {
+ this._handleTargetOver();
+ } else {
+ Y.each(this.otherDrops, function(v, k) {
+ v._handleOut.apply(v, []);
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _lookup
+ * @description Filters the list of Drops down to those in the viewport.
+ * @return {Array} The valid Drop Targets that are in the viewport.
+ */
+ _lookup: function() {
+ if (!this.useHash || this._noShim) {
+ return this.validDrops;
+ }
+ var drops = [];
+ //Only scan drop shims that are in the Viewport
+ Y.each(this.validDrops, function(v, k) {
+ if (v.shim && v.shim.inViewportRegion(false, v.region)) {
+ drops[drops.length] = v;
+ }
+ });
+ return drops;
+
+ },
+ /**
+ * @private
+ * @method _handleTargetOver
+ * @description This method execs _handleTargetOver on all valid Drop Targets
+ */
+ _handleTargetOver: function() {
+ var drops = this._lookup();
+ Y.each(drops, function(v, k) {
+ v._handleTargetOver.call(v);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _regTarget
+ * @description Add the passed in Target to the targets collection
+ * @param {Object} t The Target to add to the targets collection
+ */
+ _regTarget: function(t) {
+ this.targets[this.targets.length] = t;
+ },
+ /**
+ * @private
+ * @method _unregTarget
+ * @description Remove the passed in Target from the targets collection
+ * @param {Object} drop The Target to remove from the targets collection
+ */
+ _unregTarget: function(drop) {
+ var targets = [], vdrops;
+ Y.each(this.targets, function(v, k) {
+ if (v != drop) {
+ targets[targets.length] = v;
+ }
+ }, this);
+ this.targets = targets;
+
+ vdrops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ vdrops[vdrops.length] = v;
+ }
+ });
+
+ this.validDrops = vdrops;
+ },
+ /**
+ * @method getDrop
+ * @description Get a valid Drop instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drop Object
+ * @return {Object}
+ */
+ getDrop: function(node) {
+ var drop = false,
+ n = Y.Node.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this.targets, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drop = v;
+ }
+ });
+ }
+ return drop;
+ }
+ }, true);
+
+
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-ddm",function(A){A.mix(A.DD.DDM,{_pg:null,_debugShim:false,_activateTargets:function(){},_deactivateTargets:function(){},_startDrag:function(){if(this.activeDrag.get("useShim")){this._pg_activate();this._activateTargets();}},_endDrag:function(){this._pg_deactivate();this._deactivateTargets();},_pg_deactivate:function(){this._pg.setStyle("display","none");},_pg_activate:function(){var B=this.activeDrag.get("activeHandle"),C="auto";if(B){C=B.getStyle("cursor");}if(C=="auto"){C=this.get("dragCursor");}this._pg_size();this._pg.setStyles({top:0,left:0,display:"block",opacity:((this._debugShim)?".5":"0"),cursor:C});},_pg_size:function(){if(this.activeDrag){var B=A.get("body"),D=B.get("docHeight"),C=B.get("docWidth");this._pg.setStyles({height:D+"px",width:C+"px"});}},_createPG:function(){var D=A.Node.create("<div></div>"),B=A.get("body");D.setStyles({top:"0",left:"0",position:"absolute",zIndex:"9999",overflow:"hidden",backgroundColor:"red",display:"none",height:"5px",width:"5px"});D.set("id",A.stamp(D));D.addClass("yui-dd-shim");if(B.get("firstChild")){B.insertBefore(D,B.get("firstChild"));}else{B.appendChild(D);}this._pg=D;this._pg.on("mouseup",A.bind(this._end,this));this._pg.on("mousemove",A.bind(this._move,this));var C=A.get(window);A.on("window:resize",A.bind(this._pg_size,this));C.on("scroll",A.bind(this._pg_size,this));}},true);A.on("domready",A.bind(A.DD.DDM._createPG,A.DD.DDM));},"3.0.0",{requires:["dd-ddm-base","event-resize"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm-base Class to add support for the viewport shim to allow a draggable node to drag to be dragged over an iframe or any other node that traps mousemove events.
+ * It is also required to have Drop Targets enabled, as the viewport shim will contain the shims for the Drop Targets.
+ * @module dd
+ * @submodule dd-ddm
+ * @for DDM
+ * @namespace DD
+ */
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _pg
+ * @description The shim placed over the screen to track the mousemove event.
+ * @type {Node}
+ */
+ _pg: null,
+ /**
+ * @private
+ * @property _debugShim
+ * @description Set this to true to set the shims opacity to .5 for debugging it, default: false.
+ * @type {Boolean}
+ */
+ _debugShim: false,
+ _activateTargets: function() {},
+ _deactivateTargets: function() {},
+ _startDrag: function() {
+ if (this.activeDrag.get('useShim')) {
+ this._pg_activate();
+ this._activateTargets();
+ }
+ },
+ _endDrag: function() {
+ this._pg_deactivate();
+ this._deactivateTargets();
+ },
+ /**
+ * @private
+ * @method _pg_deactivate
+ * @description Deactivates the shim
+ */
+ _pg_deactivate: function() {
+ this._pg.setStyle('display', 'none');
+ },
+ /**
+ * @private
+ * @method _pg_activate
+ * @description Activates the shim
+ */
+ _pg_activate: function() {
+ var ah = this.activeDrag.get('activeHandle'), cur = 'auto';
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = this.get('dragCursor');
+ }
+
+ this._pg_size();
+ this._pg.setStyles({
+ top: 0,
+ left: 0,
+ display: 'block',
+ opacity: ((this._debugShim) ? '.5' : '0'),
+ cursor: cur
+ });
+ },
+ /**
+ * @private
+ * @method _pg_size
+ * @description Sizes the shim on: activatation, window:scroll, window:resize
+ */
+ _pg_size: function() {
+ if (this.activeDrag) {
+ var b = Y.get('body'),
+ h = b.get('docHeight'),
+ w = b.get('docWidth');
+ this._pg.setStyles({
+ height: h + 'px',
+ width: w + 'px'
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _createPG
+ * @description Creates the shim and adds it's listeners to it.
+ */
+ _createPG: function() {
+ var pg = Y.Node.create('<div></div>'),
+ bd = Y.get('body');
+ pg.setStyles({
+ top: '0',
+ left: '0',
+ position: 'absolute',
+ zIndex: '9999',
+ overflow: 'hidden',
+ backgroundColor: 'red',
+ display: 'none',
+ height: '5px',
+ width: '5px'
+ });
+ pg.set('id', Y.stamp(pg));
+ pg.addClass('yui-dd-shim');
+ if (bd.get('firstChild')) {
+ bd.insertBefore(pg, bd.get('firstChild'));
+ } else {
+ bd.appendChild(pg);
+ }
+ this._pg = pg;
+ this._pg.on('mouseup', Y.bind(this._end, this));
+ this._pg.on('mousemove', Y.bind(this._move, this));
+
+ var win = Y.get(window);
+ Y.on('window:resize', Y.bind(this._pg_size, this));
+ win.on('scroll', Y.bind(this._pg_size, this));
+ }
+ }, true);
+
+ Y.on('domready', Y.bind(Y.DD.DDM._createPG, Y.DD.DDM));
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base', 'event-resize'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm-base', function(Y) {
+
+
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @module dd
+ * @submodule dd-ddm-base
+ */
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @class DDM
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDMBase = function() {
+ DDMBase.superclass.constructor.apply(this, arguments);
+ };
+
+ DDMBase.NAME = 'ddm';
+
+ DDMBase.ATTRS = {
+ /**
+ * @attribute dragCursor
+ * @description The cursor to apply when dragging, if shimmed the shim will get the cursor.
+ * @type String
+ */
+ dragCursor: {
+ value: 'move'
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: 3
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: 1000
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of all future Drag instances.
+ * @type String
+ */
+ dragMode: {
+ value: 'point',
+ setter: function(mode) {
+ this._setDragMode(mode);
+ return mode;
+ }
+ }
+
+ };
+
+ Y.extend(DDMBase, Y.Base, {
+ /**
+ * @property _active
+ * @description flag set when we activate our first drag, so DDM can start listening for events.
+ * @type {Boolean}
+ */
+ _active: null,
+ /**
+ * @private
+ * @method _setDragMode
+ * @description Handler for dragMode attribute setter.
+ * @param String/Number The Number value or the String for the DragMode to default all future drag instances to.
+ * @return Number The Mode to be set
+ */
+ _setDragMode: function(mode) {
+ if (mode === null) {
+ mode = Y.DD.DDM.get('dragMode');
+ }
+ switch (mode) {
+ case 1:
+ case 'intersect':
+ return 1;
+ case 2:
+ case 'strict':
+ return 2;
+ case 0:
+ case 'point':
+ return 0;
+ }
+ return 0;
+ },
+ /**
+ * @property CSS_PREFIX
+ * @description The PREFIX to attach to all DD CSS class names
+ * @type {String}
+ */
+ CSS_PREFIX: 'yui-dd',
+ _activateTargets: function() {},
+ /**
+ * @private
+ * @property _drags
+ * @description Holder for all registered drag elements.
+ * @type {Array}
+ */
+ _drags: [],
+ /**
+ * @property activeDrag
+ * @description A reference to the currently active draggable object.
+ * @type {Drag}
+ */
+ activeDrag: false,
+ /**
+ * @private
+ * @method _regDrag
+ * @description Adds a reference to the drag object to the DDM._drags array, called in the constructor of Drag.
+ * @param {Drag} d The Drag object
+ */
+ _regDrag: function(d) {
+ if (this.getDrag(d.get('node'))) {
+ return false;
+ }
+
+ if (!this._active) {
+ this._setupListeners();
+ }
+ this._drags.push(d);
+ return true;
+ },
+ /**
+ * @private
+ * @method _unregDrag
+ * @description Remove this drag object from the DDM._drags array.
+ * @param {Drag} d The drag object.
+ */
+ _unregDrag: function(d) {
+ var tmp = [];
+ Y.each(this._drags, function(n, i) {
+ if (n !== d) {
+ tmp[tmp.length] = n;
+ }
+ });
+ this._drags = tmp;
+ },
+ /**
+ * @private
+ * @method _setupListeners
+ * @description Add the document listeners.
+ */
+ _setupListeners: function() {
+ this._active = true;
+ var doc = Y.get(document);
+ doc.on('mousemove', Y.bind(this._move, this));
+ //Y.Event.nativeAdd(document, 'mousemove', Y.bind(this._move, this));
+ doc.on('mouseup', Y.bind(this._end, this));
+ },
+ /**
+ * @private
+ * @method _start
+ * @description Internal method used by Drag to signal the start of a drag operation
+ */
+ _start: function() {
+ this.fire('ddm:start');
+ this._startDrag();
+ },
+ /**
+ * @private
+ * @method _startDrag
+ * @description Factory method to be overwritten by other DDM's
+ * @param {Number} x The x position of the drag element
+ * @param {Number} y The y position of the drag element
+ * @param {Number} w The width of the drag element
+ * @param {Number} h The height of the drag element
+ */
+ _startDrag: function() {},
+ /**
+ * @private
+ * @method _endDrag
+ * @description Factory method to be overwritten by other DDM's
+ */
+ _endDrag: function() {},
+ _dropMove: function() {},
+ /**
+ * @private
+ * @method _end
+ * @description Internal method used by Drag to signal the end of a drag operation
+ */
+ _end: function() {
+ if (this.activeDrag) {
+ this._endDrag();
+ this.fire('ddm:end');
+ this.activeDrag.end.call(this.activeDrag);
+ this.activeDrag = null;
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.activeDrag) {
+ this._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Internal listener for the mousemove DOM event to pass to the Drag's move method.
+ * @param {Event.Facade} ev The Dom mousemove Event
+ */
+ _move: function(ev) {
+ if (this.activeDrag) {
+ this.activeDrag._move.call(this.activeDrag, ev);
+ this._dropMove();
+ }
+ },
+ /**
+ * //TODO Private, rename??...
+ * @private
+ * @method cssSizestoObject
+ * @description Helper method to use to set the gutter from the attribute setter.
+ * @param {String} gutter CSS style string for gutter: '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @return {Object} The gutter Object Literal.
+ */
+ cssSizestoObject: function(gutter) {
+ var x = gutter.split(' ');
+
+ switch (x.length) {
+ case 1: x[1] = x[2] = x[3] = x[0]; break;
+ case 2: x[2] = x[0]; x[3] = x[1]; break;
+ case 3: x[3] = x[1]; break;
+ }
+
+ return {
+ top : parseInt(x[0],10),
+ right : parseInt(x[1],10),
+ bottom: parseInt(x[2],10),
+ left : parseInt(x[3],10)
+ };
+ },
+ /**
+ * @method getDrag
+ * @description Get a valid Drag instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drag Object
+ * @return {Object}
+ */
+ getDrag: function(node) {
+ var drag = false,
+ n = Y.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this._drags, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drag = v;
+ }
+ });
+ }
+ return drag;
+ }
+ });
+
+ Y.namespace('DD');
+ Y.DD.DDM = new DDMBase();
+
+ /**
+ * @event ddm:start
+ * @description Fires from the DDM before all drag events fire.
+ * @type {Event.Custom}
+ */
+ /**
+ * @event ddm:end
+ * @description Fires from the DDM after the DDM finishes, before the drag end events.
+ * @type {Event.Custom}
+ */
+
+
+
+
+}, '3.0.0' ,{requires:['node', 'base'], skinnable:false});
+YUI.add('dd-ddm', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm-base Class to add support for the viewport shim to allow a draggable node to drag to be dragged over an iframe or any other node that traps mousemove events.
+ * It is also required to have Drop Targets enabled, as the viewport shim will contain the shims for the Drop Targets.
+ * @module dd
+ * @submodule dd-ddm
+ * @for DDM
+ * @namespace DD
+ */
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _pg
+ * @description The shim placed over the screen to track the mousemove event.
+ * @type {Node}
+ */
+ _pg: null,
+ /**
+ * @private
+ * @property _debugShim
+ * @description Set this to true to set the shims opacity to .5 for debugging it, default: false.
+ * @type {Boolean}
+ */
+ _debugShim: false,
+ _activateTargets: function() {},
+ _deactivateTargets: function() {},
+ _startDrag: function() {
+ if (this.activeDrag.get('useShim')) {
+ this._pg_activate();
+ this._activateTargets();
+ }
+ },
+ _endDrag: function() {
+ this._pg_deactivate();
+ this._deactivateTargets();
+ },
+ /**
+ * @private
+ * @method _pg_deactivate
+ * @description Deactivates the shim
+ */
+ _pg_deactivate: function() {
+ this._pg.setStyle('display', 'none');
+ },
+ /**
+ * @private
+ * @method _pg_activate
+ * @description Activates the shim
+ */
+ _pg_activate: function() {
+ var ah = this.activeDrag.get('activeHandle'), cur = 'auto';
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = this.get('dragCursor');
+ }
+
+ this._pg_size();
+ this._pg.setStyles({
+ top: 0,
+ left: 0,
+ display: 'block',
+ opacity: ((this._debugShim) ? '.5' : '0'),
+ cursor: cur
+ });
+ },
+ /**
+ * @private
+ * @method _pg_size
+ * @description Sizes the shim on: activatation, window:scroll, window:resize
+ */
+ _pg_size: function() {
+ if (this.activeDrag) {
+ var b = Y.get('body'),
+ h = b.get('docHeight'),
+ w = b.get('docWidth');
+ this._pg.setStyles({
+ height: h + 'px',
+ width: w + 'px'
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _createPG
+ * @description Creates the shim and adds it's listeners to it.
+ */
+ _createPG: function() {
+ var pg = Y.Node.create('<div></div>'),
+ bd = Y.get('body');
+ pg.setStyles({
+ top: '0',
+ left: '0',
+ position: 'absolute',
+ zIndex: '9999',
+ overflow: 'hidden',
+ backgroundColor: 'red',
+ display: 'none',
+ height: '5px',
+ width: '5px'
+ });
+ pg.set('id', Y.stamp(pg));
+ pg.addClass('yui-dd-shim');
+ if (bd.get('firstChild')) {
+ bd.insertBefore(pg, bd.get('firstChild'));
+ } else {
+ bd.appendChild(pg);
+ }
+ this._pg = pg;
+ this._pg.on('mouseup', Y.bind(this._end, this));
+ this._pg.on('mousemove', Y.bind(this._move, this));
+
+ var win = Y.get(window);
+ Y.on('window:resize', Y.bind(this._pg_size, this));
+ win.on('scroll', Y.bind(this._pg_size, this));
+ }
+ }, true);
+
+ Y.on('domready', Y.bind(Y.DD.DDM._createPG, Y.DD.DDM));
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base', 'event-resize'], skinnable:false});
+YUI.add('dd-ddm-drop', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm Class to add support for the placement of Drop Target shims inside the viewport shim. It also handles all Drop Target related events and interactions.
+ * @module dd
+ * @submodule dd-ddm-drop
+ * @for DDM
+ * @namespace DD
+ */
+
+ //TODO CSS class name for the bestMatch..
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _noShim
+ * @description This flag turns off the use of the mouseover/mouseout shim. It should not be used unless you know what you are doing.
+ * @type {Boolean}
+ */
+ _noShim: false,
+ /**
+ * @private
+ * @property _activeShims
+ * @description Placeholder for all active shims on the page
+ * @type {Array}
+ */
+ _activeShims: [],
+ /**
+ * @private
+ * @method _hasActiveShim
+ * @description This method checks the _activeShims Object to see if there is a shim active.
+ * @return {Boolean}
+ */
+ _hasActiveShim: function() {
+ if (this._noShim) {
+ return true;
+ }
+ return this._activeShims.length;
+ },
+ /**
+ * @private
+ * @method _addActiveShim
+ * @description Adds a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to add to the list.
+ */
+ _addActiveShim: function(d) {
+ this._activeShims[this._activeShims.length] = d;
+ },
+ /**
+ * @private
+ * @method _removeActiveShim
+ * @description Removes a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to remove from the list.
+ */
+ _removeActiveShim: function(d) {
+ var s = [];
+ Y.each(this._activeShims, function(v, k) {
+ if (v._yuid !== d._yuid) {
+ s[s.length] = v;
+ }
+
+ });
+ this._activeShims = s;
+ },
+ /**
+ * @method syncActiveShims
+ * @description This method will sync the position of the shims on the Drop Targets that are currently active.
+ * @param {Boolean} force Resize/sync all Targets.
+ */
+ syncActiveShims: function(force) {
+ Y.later(0, this, function(force) {
+ var drops = ((force) ? this.targets : this._lookup());
+ Y.each(drops, function(v, k) {
+ v.sizeShim.call(v);
+ }, this);
+ }, force);
+ },
+ /**
+ * @private
+ * @property mode
+ * @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
+ * @type Number
+ */
+ mode: 0,
+ /**
+ * @private
+ * @property POINT
+ * @description In point mode, a Drop is targeted by the cursor being over the Target
+ * @type Number
+ */
+ POINT: 0,
+ /**
+ * @private
+ * @property INTERSECT
+ * @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
+ * @type Number
+ */
+ INTERSECT: 1,
+ /**
+ * @private
+ * @property STRICT
+ * @description In strict mode, a Drop is targeted by the "entire" drag node being over the Target
+ * @type Number
+ */
+ STRICT: 2,
+ /**
+ * @property useHash
+ * @description Should we only check targets that are in the viewport on drags (for performance), default: true
+ * @type {Boolean}
+ */
+ useHash: true,
+ /**
+ * @property activeDrop
+ * @description A reference to the active Drop Target
+ * @type {Object}
+ */
+ activeDrop: null,
+ /**
+ * @property validDrops
+ * @description An array of the valid Drop Targets for this interaction.
+ * @type {Array}
+ */
+ //TODO Change array/object literals to be in sync..
+ validDrops: [],
+ /**
+ * @property otherDrops
+ * @description An object literal of Other Drop Targets that we encountered during this interaction (in the case of overlapping Drop Targets)
+ * @type {Object}
+ */
+ otherDrops: {},
+ /**
+ * @property targets
+ * @description All of the Targets
+ * @type {Array}
+ */
+ targets: [],
+ /**
+ * @private
+ * @method _addValid
+ * @description Add a Drop Target to the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _addValid: function(drop) {
+ this.validDrops[this.validDrops.length] = drop;
+ return this;
+ },
+ /**
+ * @private
+ * @method _removeValid
+ * @description Removes a Drop Target from the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _removeValid: function(drop) {
+ var drops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ drops[drops.length] = v;
+ }
+ });
+
+ this.validDrops = drops;
+ return this;
+ },
+ /**
+ * @method isOverTarget
+ * @description Check to see if the Drag element is over the target, method varies on current mode
+ * @param {Object} drop The drop to check against
+ * @return {Boolean}
+ */
+ isOverTarget: function(drop) {
+ if (this.activeDrag && drop) {
+ var xy = this.activeDrag.mouseXY, r, dMode = this.activeDrag.get('dragMode'),
+ aRegion;
+ if (xy && this.activeDrag) {
+ aRegion = this.activeDrag.region;
+ if (dMode == this.STRICT) {
+ return this.activeDrag.get('dragNode').inRegion(drop.region, true, aRegion);
+ } else {
+ if (drop && drop.shim) {
+ if ((dMode == this.INTERSECT) && this._noShim) {
+ r = ((aRegion) ? aRegion : this.activeDrag.get('node'));
+ return drop.get('node').intersect(r).inRegion;
+ } else {
+ return drop.shim.intersect({
+ top: xy[1],
+ bottom: xy[1],
+ left: xy[0],
+ right: xy[0]
+ }, drop.region).inRegion;
+ }
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @method clearCache
+ * @description Clears the cache data used for this interaction.
+ */
+ clearCache: function() {
+ this.validDrops = [];
+ this.otherDrops = {};
+ this._activeShims = [];
+ },
+ /**
+ * @private
+ * @method _activateTargets
+ * @description Clear the cache and activate the shims of all the targets
+ */
+ _activateTargets: function() {
+ this.clearCache();
+ Y.each(this.targets, function(v, k) {
+ v._activateShim.apply(v, []);
+ }, this);
+ this._handleTargetOver();
+
+ },
+ /**
+ * @method getBestMatch
+ * @description This method will gather the area for all potential targets and see which has the hightest covered area and return it.
+ * @param {Array} drops An Array of drops to scan for the best match.
+ * @param {Boolean} all If present, it returns an Array. First item is best match, second is an Array of the other items in the original Array.
+ * @return {Object or Array}
+ */
+ getBestMatch: function(drops, all) {
+ var biggest = null, area = 0, out;
+
+ Y.each(drops, function(v, k) {
+ var inter = this.activeDrag.get('dragNode').intersect(v.get('node'));
+ v.region.area = inter.area;
+
+ if (inter.inRegion) {
+ if (inter.area > area) {
+ area = inter.area;
+ biggest = v;
+ }
+ }
+ }, this);
+ if (all) {
+ out = [];
+ //TODO Sort the others in numeric order by area covered..
+ Y.each(drops, function(v, k) {
+ if (v !== biggest) {
+ out[out.length] = v;
+ }
+ }, this);
+ return [biggest, out];
+ } else {
+ return biggest;
+ }
+ },
+ /**
+ * @private
+ * @method _deactivateTargets
+ * @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
+ */
+ _deactivateTargets: function() {
+ var other = [], tmp,
+ activeDrag = this.activeDrag,
+ activeDrop = this.activeDrop;
+
+ //TODO why is this check so hard??
+ if (activeDrag && activeDrop && this.otherDrops[activeDrop]) {
+ if (!activeDrag.get('dragMode')) {
+ //TODO otherDrops -- private..
+ other = this.otherDrops;
+ delete other[activeDrop];
+ } else {
+ tmp = this.getBestMatch(this.otherDrops, true);
+ activeDrop = tmp[0];
+ other = tmp[1];
+ }
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ if (activeDrop) {
+ activeDrop.fire('drop:hit', { drag: activeDrag, drop: activeDrop, others: other });
+ activeDrag.fire('drag:drophit', { drag: activeDrag, drop: activeDrop, others: other });
+ }
+ } else if (activeDrag) {
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ activeDrag.fire('drag:dropmiss', { pageX: activeDrag.lastXY[0], pageY: activeDrag.lastXY[1] });
+ } else {
+ }
+
+ this.activeDrop = null;
+
+ Y.each(this.targets, function(v, k) {
+ v._deactivateShim.apply(v, []);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _dropMove
+ * @description This method is called when the move method is called on the Drag Object.
+ */
+ _dropMove: function() {
+ if (this._hasActiveShim()) {
+ this._handleTargetOver();
+ } else {
+ Y.each(this.otherDrops, function(v, k) {
+ v._handleOut.apply(v, []);
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _lookup
+ * @description Filters the list of Drops down to those in the viewport.
+ * @return {Array} The valid Drop Targets that are in the viewport.
+ */
+ _lookup: function() {
+ if (!this.useHash || this._noShim) {
+ return this.validDrops;
+ }
+ var drops = [];
+ //Only scan drop shims that are in the Viewport
+ Y.each(this.validDrops, function(v, k) {
+ if (v.shim && v.shim.inViewportRegion(false, v.region)) {
+ drops[drops.length] = v;
+ }
+ });
+ return drops;
+
+ },
+ /**
+ * @private
+ * @method _handleTargetOver
+ * @description This method execs _handleTargetOver on all valid Drop Targets
+ */
+ _handleTargetOver: function() {
+ var drops = this._lookup();
+ Y.each(drops, function(v, k) {
+ v._handleTargetOver.call(v);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _regTarget
+ * @description Add the passed in Target to the targets collection
+ * @param {Object} t The Target to add to the targets collection
+ */
+ _regTarget: function(t) {
+ this.targets[this.targets.length] = t;
+ },
+ /**
+ * @private
+ * @method _unregTarget
+ * @description Remove the passed in Target from the targets collection
+ * @param {Object} drop The Target to remove from the targets collection
+ */
+ _unregTarget: function(drop) {
+ var targets = [], vdrops;
+ Y.each(this.targets, function(v, k) {
+ if (v != drop) {
+ targets[targets.length] = v;
+ }
+ }, this);
+ this.targets = targets;
+
+ vdrops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ vdrops[vdrops.length] = v;
+ }
+ });
+
+ this.validDrops = vdrops;
+ },
+ /**
+ * @method getDrop
+ * @description Get a valid Drop instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drop Object
+ * @return {Object}
+ */
+ getDrop: function(node) {
+ var drop = false,
+ n = Y.Node.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this.targets, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drop = v;
+ }
+ });
+ }
+ return drop;
+ }
+ }, true);
+
+
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm'], skinnable:false});
+YUI.add('dd-drag', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drag
+ */
+ /**
+ * This class provides the ability to drag a Node.
+ * @class Drag
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAGGING = 'dragging',
+ DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ MOUSE_UP = 'mouseup',
+ MOUSE_DOWN = 'mousedown',
+ DRAG_START = 'dragstart',
+ /**
+ * @event drag:mouseDown
+ * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
+ * @preventable _defMouseDownFn
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_MOUSE_DOWN = 'drag:mouseDown',
+ /**
+ * @event drag:afterMouseDown
+ * @description Fires after the mousedown event has been cleared.
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
+ /**
+ * @event drag:removeHandle
+ * @description Fires after a handle is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_HANDLE = 'drag:removeHandle',
+ /**
+ * @event drag:addHandle
+ * @description Fires after a handle is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_HANDLE = 'drag:addHandle',
+ /**
+ * @event drag:removeInvalid
+ * @description Fires after an invalid selector is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_INVALID = 'drag:removeInvalid',
+ /**
+ * @event drag:addInvalid
+ * @description Fires after an invalid selector is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_INVALID = 'drag:addInvalid',
+ /**
+ * @event drag:start
+ * @description Fires at the start of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_START = 'drag:start',
+ /**
+ * @event drag:end
+ * @description Fires at the end of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_END = 'drag:end',
+ /**
+ * @event drag:drag
+ * @description Fires every mousemove during a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DRAG = 'drag:drag',
+ /**
+ * @event drag:align
+ * @preventable _defAlignFn
+ * @description Fires when this node is aligned.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ALIGN = 'drag:align',
+ /**
+ * @event drag:over
+ * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:enter
+ * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:exit
+ * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:drophit
+ * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:dropmiss
+ * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+ Drag = function(o) {
+ this._lazyAddAttrs = false;
+ Drag.superclass.constructor.apply(this, arguments);
+
+ var valid = DDM._regDrag(this);
+ if (!valid) {
+ Y.error('Failed to register node, already in use: ' + o.node);
+ }
+ };
+
+ Drag.NAME = 'drag';
+
+ Drag.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to initiate a drag operation
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid Node Given: ' + node);
+ } else {
+ n = n.item(0);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute dragNode
+ * @description Y.Node instanace to use as the draggable element, defaults to node
+ * @type Node
+ */
+ dragNode: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid dragNode Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute offsetNode
+ * @description Offset the drag element by the difference in cursor position: default true
+ * @type Boolean
+ */
+ offsetNode: {
+ value: true
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: DDM.get('clickPixelThresh')
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: DDM.get('clickTimeThresh')
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drag element so that it can't be dragged: default false.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute data
+ * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
+ * @type Mixed
+ */
+ data: {
+ value: false
+ },
+ /**
+ * @attribute move
+ * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
+ * @type Boolean
+ */
+ move: {
+ value: true
+ },
+ /**
+ * @attribute useShim
+ * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
+ * @type Boolean
+ */
+ useShim: {
+ value: true
+ },
+ /**
+ * @attribute activeHandle
+ * @description This config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
+ * @type Node
+ */
+ activeHandle: {
+ value: false
+ },
+ /**
+ * @attribute primaryButtonOnly
+ * @description By default a drag operation will only begin if the mousedown occurred with the primary mouse button. Setting this to false will allow for all mousedown events to trigger a drag.
+ * @type Boolean
+ */
+ primaryButtonOnly: {
+ value: true
+ },
+ /**
+ * @attribute dragging
+ * @description This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
+ * @type Boolean
+ */
+ dragging: {
+ value: false
+ },
+ parent: {
+ value: false
+ },
+ /**
+ * @attribute target
+ * @description This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
+ * @type Boolean
+ */
+ target: {
+ value: false,
+ setter: function(config) {
+ this._handleTarget(config);
+ return config;
+ }
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
+ * @type String
+ */
+ dragMode: {
+ value: null,
+ setter: function(mode) {
+ return DDM._setDragMode(mode);
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drag into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ getter: function() {
+ if (!this._groups) {
+ this._groups = {};
+ }
+ var ret = [];
+ Y.each(this._groups, function(v, k) {
+ ret[ret.length] = k;
+ });
+ return ret;
+ },
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute handles
+ * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
+ * @type Array
+ */
+ handles: {
+ value: null,
+ setter: function(g) {
+ if (g) {
+ this._handles = {};
+ Y.each(g, function(v, k) {
+ this._handles[v] = true;
+ }, this);
+ } else {
+ this._handles = null;
+ }
+ return g;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drag, Y.Base, {
+ /**
+ * @method addToGroup
+ * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
+ * @param {String} g The group to add this Drag Instance to.
+ * @return {Self}
+ * @chainable
+ */
+ addToGroup: function(g) {
+ this._groups[g] = true;
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @method removeFromGroup
+ * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
+ * @param {String} g The group to remove this Drag Instance from.
+ * @return {Self}
+ * @chainable
+ */
+ removeFromGroup: function(g) {
+ delete this._groups[g];
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @property target
+ * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
+ * @type {Object}
+ */
+ target: null,
+ /**
+ * @private
+ * @method _handleTarget
+ * @description Attribute handler for the target config attribute.
+ * @param {Boolean/Object}
+ * @return {Boolean/Object}
+ */
+ _handleTarget: function(config) {
+ if (Y.DD.Drop) {
+ if (config === false) {
+ if (this.target) {
+ DDM._unregTarget(this.target);
+ this.target = null;
+ }
+ return false;
+ } else {
+ if (!Y.Lang.isObject(config)) {
+ config = {};
+ }
+ config.bubbles = ('bubbles' in config) ? config.bubbles : this.get('bubbles');
+ config.node = this.get(NODE);
+ config.groups = config.groups || this.get('groups');
+ this.target = new Y.DD.Drop(config);
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @private
+ * @property _groups
+ * @description Storage Array for the groups this drag belongs to.
+ * @type {Array}
+ */
+ _groups: null,
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ this.publish(EV_MOUSE_DOWN, {
+ defaultFn: this._defMouseDownFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_ALIGN, {
+ defaultFn: this._defAlignFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_DRAG, {
+ defaultFn: this._defDragFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_END, {
+ preventedFn: this._prevEndFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ var ev = [
+ EV_AFTER_MOUSE_DOWN,
+ EV_REMOVE_HANDLE,
+ EV_ADD_HANDLE,
+ EV_REMOVE_INVALID,
+ EV_ADD_INVALID,
+ EV_START,
+ 'drag:drophit',
+ 'drag:dropmiss',
+ 'drag:over',
+ 'drag:enter',
+ 'drag:exit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ bubbles: true,
+ preventable: false,
+ queuable: false,
+ prefix: 'drag'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+
+ },
+ /**
+ * @private
+ * @property _ev_md
+ * @description A private reference to the mousedown DOM event
+ * @type {Event.Facade}
+ */
+ _ev_md: null,
+ /**
+ * @private
+ * @property _startTime
+ * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _startTime: null,
+ /**
+ * @private
+ * @property _endTime
+ * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _endTime: null,
+ /**
+ * @private
+ * @property _handles
+ * @description A private hash of the valid drag handles
+ * @type {Object}
+ */
+ _handles: null,
+ /**
+ * @private
+ * @property _invalids
+ * @description A private hash of the invalid selector strings
+ * @type {Object}
+ */
+ _invalids: null,
+ /**
+ * @private
+ * @property _invalidsDefault
+ * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
+ * @type {Object}
+ */
+ _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
+ /**
+ * @private
+ * @property _dragThreshMet
+ * @description Private flag to see if the drag threshhold was met
+ * @type {Boolean}
+ */
+ _dragThreshMet: null,
+ /**
+ * @private
+ * @property _fromTimeout
+ * @description Flag to determine if the drag operation came from a timeout
+ * @type {Boolean}
+ */
+ _fromTimeout: null,
+ /**
+ * @private
+ * @property _clickTimeout
+ * @description Holder for the setTimeout call
+ * @type {Boolean}
+ */
+ _clickTimeout: null,
+ /**
+ * @property deltaXY
+ * @description The offset of the mouse position to the element's position
+ * @type {Array}
+ */
+ deltaXY: null,
+ /**
+ * @property startXY
+ * @description The initial mouse position
+ * @type {Array}
+ */
+ startXY: null,
+ /**
+ * @property nodeXY
+ * @description The initial element position
+ * @type {Array}
+ */
+ nodeXY: null,
+ /**
+ * @property lastXY
+ * @description The position of the element as it's moving (for offset calculations)
+ * @type {Array}
+ */
+ lastXY: null,
+ /**
+ * @property actXY
+ * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
+ * @type {Array}
+ */
+ actXY: null,
+ /**
+ * @property realXY
+ * @description The real xy position of the node.
+ * @type {Array}
+ */
+ realXY: null,
+ /**
+ * @property mouseXY
+ * @description The XY coords of the mousemove
+ * @type {Array}
+ */
+ mouseXY: null,
+ /**
+ * @property region
+ * @description A region object associated with this drag, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @private
+ * @method _handleMouseUp
+ * @description Handler for the mouseup DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseUp: function(ev) {
+ this._fixIEMouseUp();
+ if (DDM.activeDrag) {
+ DDM._end();
+ }
+ },
+ /**
+ * @private
+ * @method _fixDragStart
+ * @description The function we use as the ondragstart handler when we start a drag in Internet Explorer. This keeps IE from blowing up on images as drag handles.
+ */
+ _fixDragStart: function(e) {
+ e.preventDefault();
+ },
+ /**
+ * @private
+ * @method _ieSelectFix
+ * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
+ */
+ _ieSelectFix: function() {
+ return false;
+ },
+ /**
+ * @private
+ * @property _ieSelectBack
+ * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
+ */
+ _ieSelectBack: null,
+ /**
+ * @private
+ * @method _fixIEMouseDown
+ * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
+ */
+ _fixIEMouseDown: function() {
+ if (Y.UA.ie) {
+ this._ieSelectBack = Y.config.doc.body.onselectstart;
+ Y.config.doc.body.onselectstart = this._ieSelectFix;
+ }
+ },
+ /**
+ * @private
+ * @method _fixIEMouseUp
+ * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
+ */
+ _fixIEMouseUp: function() {
+ if (Y.UA.ie) {
+ Y.config.doc.body.onselectstart = this._ieSelectBack;
+ }
+ },
+ /**
+ * @private
+ * @method _handleMouseDownEvent
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseDownEvent: function(ev) {
+ this.fire(EV_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @private
+ * @method _defMouseDownFn
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _defMouseDownFn: function(e) {
+ var ev = e.ev;
+ this._dragThreshMet = false;
+ this._ev_md = ev;
+
+ if (this.get('primaryButtonOnly') && ev.button > 1) {
+ return false;
+ }
+ if (this.validClick(ev)) {
+ this._fixIEMouseDown();
+ ev.halt();
+ this._setStartPosition([ev.pageX, ev.pageY]);
+
+ DDM.activeDrag = this;
+
+ this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
+ }
+ this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @method validClick
+ * @description Method first checks to see if we have handles, if so it validates the click against the handle. Then if it finds a valid handle, it checks it against the invalid handles list. Returns true if a good handle was used, false otherwise.
+ * @param {Event.Facade}
+ * @return {Boolean}
+ */
+ validClick: function(ev) {
+ var r = false, n = false,
+ tar = ev.target,
+ hTest = null,
+ els = null,
+ set = false;
+ if (this._handles) {
+ Y.each(this._handles, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *') && !hTest) {
+ hTest = n;
+ r = true;
+ }
+ }
+ });
+ } else {
+ n = this.get(NODE)
+ if (n.contains(tar) || n.compareTo(tar)) {
+ r = true;
+ }
+ }
+ if (r) {
+ if (this._invalids) {
+ Y.each(this._invalids, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *')) {
+ r = false;
+ }
+ }
+ });
+ }
+ }
+ if (r) {
+ if (hTest) {
+ els = ev.currentTarget.queryAll(hTest);
+ set = false;
+ els.each(function(n, i) {
+ if ((n.contains(tar) || n.compareTo(tar)) && !set) {
+ set = true;
+ this.set('activeHandle', n);
+ }
+ }, this);
+ } else {
+ this.set('activeHandle', this.get(NODE));
+ }
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _setStartPosition
+ * @description Sets the current position of the Element and calculates the offset
+ * @param {Array} xy The XY coords to set the position to.
+ */
+ _setStartPosition: function(xy) {
+ this.startXY = xy;
+
+ this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
+
+ if (this.get('offsetNode')) {
+ this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
+ } else {
+ this.deltaXY = [0, 0];
+ }
+ },
+ /**
+ * @private
+ * @method _timeoutCheck
+ * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
+ */
+ _timeoutCheck: function() {
+ if (!this.get('lock') && !this._dragThreshMet) {
+ this._fromTimeout = this._dragThreshMet = true;
+ this.start();
+ this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
+ }
+ },
+ /**
+ * @method removeHandle
+ * @description Remove a Selector added by addHandle
+ * @param {String} str The selector for the handle to be removed.
+ * @return {Self}
+ * @chainable
+ */
+ removeHandle: function(str) {
+ if (this._handles[str]) {
+ delete this._handles[str];
+ this.fire(EV_REMOVE_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addHandle
+ * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
+ * @param {String} str The selector to test for a valid handle. Must be a child of the element.
+ * @return {Self}
+ * @chainable
+ */
+ addHandle: function(str) {
+ if (!this._handles) {
+ this._handles = {};
+ }
+ if (Y.Lang.isString(str)) {
+ this._handles[str] = true;
+ this.fire(EV_ADD_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method removeInvalid
+ * @description Remove an invalid handle added by addInvalid
+ * @param {String} str The invalid handle to remove from the internal list.
+ * @return {Self}
+ * @chainable
+ */
+ removeInvalid: function(str) {
+ if (this._invalids[str]) {
+ this._invalids[str] = null;
+ delete this._invalids[str];
+ this.fire(EV_REMOVE_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addInvalid
+ * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
+ * @param {String} str The selector to test against to determine if this is an invalid drag handle.
+ * @return {Self}
+ * @chainable
+ */
+ addInvalid: function(str) {
+ if (Y.Lang.isString(str)) {
+ this._invalids[str] = true;
+ this.fire(EV_ADD_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Internal init handler
+ */
+ initializer: function() {
+ this.get(NODE).dd = this;
+
+ if (!this.get(NODE).get('id')) {
+ var id = Y.stamp(this.get(NODE));
+ this.get(NODE).set('id', id);
+ }
+
+ this.actXY = [];
+
+ this._invalids = Y.clone(this._invalidsDefault, true);
+
+ this._createEvents();
+
+ if (!this.get(DRAG_NODE)) {
+ this.set(DRAG_NODE, this.get(NODE));
+ }
+
+ //Fix for #2528096
+ //Don't prep the DD instance until all plugins are loaded.
+ this.on('initializedChange', Y.bind(this._prep, this));
+
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method _prep
+ * @description Attach event listners and add classname
+ */
+ _prep: function() {
+ this._dragThreshMet = false;
+ var node = this.get(NODE);
+ node.addClass(DDM.CSS_PREFIX + '-draggable');
+ node.on(MOUSE_DOWN, Y.bind(this._handleMouseDownEvent, this));
+ node.on(MOUSE_UP, Y.bind(this._handleMouseUp, this));
+ node.on(DRAG_START, Y.bind(this._fixDragStart, this));
+ },
+ /**
+ * @private
+ * @method _unprep
+ * @description Detach event listeners and remove classname
+ */
+ _unprep: function() {
+ var node = this.get(NODE);
+ node.removeClass(DDM.CSS_PREFIX + '-draggable');
+ node.detachAll();
+ },
+ /**
+ * @method start
+ * @description Starts the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ start: function() {
+ if (!this.get('lock') && !this.get(DRAGGING)) {
+ var node = this.get(NODE), ow = node.get(OFFSET_WIDTH), oh = node.get(OFFSET_HEIGHT);
+ this._startTime = (new Date()).getTime();
+
+ DDM._start();
+ node.addClass(DDM.CSS_PREFIX + '-dragging');
+ this.fire(EV_START, {
+ pageX: this.nodeXY[0],
+ pageY: this.nodeXY[1],
+ startTime: this._startTime
+ });
+ var xy = this.nodeXY;
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + ow,
+ bottom: xy[1] + oh,
+ left: xy[0]
+ };
+ this.set(DRAGGING, true);
+ }
+ return this;
+ },
+ /**
+ * @method end
+ * @description Ends the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ end: function() {
+ this._endTime = (new Date()).getTime();
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._dragThreshMet = false;
+ this._fromTimeout = false;
+ if (!this.get('lock') && this.get(DRAGGING)) {
+ this.fire(EV_END, {
+ pageX: this.lastXY[0],
+ pageY: this.lastXY[1],
+ startTime: this._startTime,
+ endTime: this._endTime
+ });
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
+ this.set(DRAGGING, false);
+ this.deltaXY = [0, 0];
+
+ return this;
+ },
+ /**
+ * @private
+ * @method _prevEndFn
+ * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
+ */
+ _prevEndFn: function(e) {
+ //Bug #1852577
+ this.get(DRAG_NODE).setXY(this.nodeXY);
+ },
+ /**
+ * @private
+ * @method _align
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Array} xy The xy coords to align with.
+ */
+ _align: function(xy) {
+ this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
+ },
+ /**
+ * @private
+ * @method _defAlignFn
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Event.Facade} e The drag:align event.
+ */
+ _defAlignFn: function(e) {
+ this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
+ },
+ /**
+ * @private
+ * @method _alignNode
+ * @description This method performs the alignment before the element move.
+ * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
+ */
+ _alignNode: function(eXY) {
+ this._align(eXY);
+ this._moveNode();
+ },
+ /**
+ * @private
+ * @method _moveNode
+ * @description This method performs the actual element move.
+ */
+ _moveNode: function(scroll) {
+ //if (!this.get(DRAGGING)) {
+ // return;
+ //}
+ var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
+
+ diffXY[0] = (xy[0] - this.lastXY[0]);
+ diffXY[1] = (xy[1] - this.lastXY[1]);
+
+ diffXY2[0] = (xy[0] - this.nodeXY[0]);
+ diffXY2[1] = (xy[1] - this.nodeXY[1]);
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
+ bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ left: xy[0]
+ };
+
+ this.fire(EV_DRAG, {
+ pageX: xy[0],
+ pageY: xy[1],
+ scroll: scroll,
+ info: {
+ start: startXY,
+ xy: xy,
+ delta: diffXY,
+ offset: diffXY2
+ }
+ });
+
+ this.lastXY = xy;
+ },
+ /**
+ * @private
+ * @method _defDragFn
+ * @description Default function for drag:drag. Fired from _moveNode.
+ * @param {Event.Facade} ev The drag:drag event
+ */
+ _defDragFn: function(e) {
+ if (this.get('move')) {
+ if (e.scroll) {
+ e.scroll.node.set('scrollTop', e.scroll.top);
+ e.scroll.node.set('scrollLeft', e.scroll.left);
+ }
+ this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
+ this.realXY = [e.pageX, e.pageY];
+ }
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Fired from DragDropMgr (DDM) on mousemove.
+ * @param {Event.Facade} ev The mousemove DOM event
+ */
+ _move: function(ev) {
+ if (this.get('lock')) {
+ return false;
+ } else {
+ this.mouseXY = [ev.pageX, ev.pageY];
+ if (!this._dragThreshMet) {
+ var diffX = Math.abs(this.startXY[0] - ev.pageX),
+ diffY = Math.abs(this.startXY[1] - ev.pageY);
+ if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
+ this._dragThreshMet = true;
+ this.start();
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ } else {
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.get(DRAGGING)) {
+ DDM._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ this._unprep();
+ this.detachAll();
+ if (this.target) {
+ this.target.destroy();
+ }
+ DDM._unregDrag(this);
+ }
+ });
+ Y.namespace('DD');
+ Y.DD.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base'], skinnable:false});
+YUI.add('dd-proxy', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-proxy
+ */
+ /**
+ * This plugin for dd-drag is for creating a proxy drag node, instead of dragging the original node.
+ * @class DDProxy
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAG_NODE = 'dragNode',
+ HOST = 'host',
+ TRUE = true;
+
+ var P = function(config) {
+ P.superclass.constructor.apply(this, arguments);
+ };
+
+ P.NAME = 'DDProxy';
+ /**
+ * @property proxy
+ * @description The Proxy instance will be placed on the Drag instance under the proxy namespace.
+ * @type {String}
+ */
+ P.NS = 'proxy';
+
+ P.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute moveOnEnd
+ * @description Move the original node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ moveOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute hideOnEnd
+ * @description Hide the drag node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ hideOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute resizeFrame
+ * @description Make the Proxy node assume the size of the original node. Default: true
+ * @type Boolean
+ */
+ resizeFrame: {
+ value: TRUE
+ },
+ /**
+ * @attribute positionProxy
+ * @description Make the Proxy node appear in the same place as the original node. Default: true
+ * @type Boolean
+ */
+ positionProxy: {
+ value: TRUE
+ },
+ /**
+ * @attribute borderStyle
+ * @description The default border style for the border of the proxy. Default: 1px solid #808080
+ * @type Boolean
+ */
+ borderStyle: {
+ value: '1px solid #808080'
+ }
+ };
+
+ var proto = {
+ /**
+ * @private
+ * @property _hands
+ * @description Holds the event handles for setting the proxy
+ */
+ _hands: null,
+ /**
+ * @private
+ * @method _init
+ * @description Handler for the proxy config attribute
+ */
+ _init: function() {
+ if (!DDM._proxy) {
+ Y.on('domready', Y.bind(this._init, this));
+ return;
+ }
+ if (!this._hands) {
+ this._hands = [];
+ }
+ var i, h, h1, host = this.get(HOST), dnode = host.get(DRAG_NODE);
+ if (dnode.compareTo(host.get(NODE))) {
+ if (DDM._proxy) {
+ host.set(DRAG_NODE, DDM._proxy);
+ }
+ }
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ h = DDM.on('ddm:start', Y.bind(function() {
+ if (DDM.activeDrag === host) {
+ DDM._setFrame(host);
+ }
+ }, this));
+ h1 = DDM.on('ddm:end', Y.bind(function() {
+ if (host.get('dragging')) {
+ if (this.get('moveOnEnd')) {
+ host.get(NODE).setXY(host.lastXY);
+ }
+ if (this.get('hideOnEnd')) {
+ host.get(DRAG_NODE).setStyle('display', 'none');
+ }
+ }
+ }, this));
+ this._hands = [h, h1];
+ },
+ initializer: function() {
+ this._init();
+ },
+ destructor: function() {
+ var host = this.get(HOST);
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ host.set(DRAG_NODE, host.get(NODE));
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(P, Y.Base, proto);
+ Y.Plugin.DDProxy = P;
+
+ //Add a couple of methods to the DDM
+ Y.mix(DDM, {
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _createFrame
+ * @description Create the proxy element if it doesn't already exist and set the DD.DDM._proxy value
+ */
+ _createFrame: function() {
+ if (!DDM._proxy) {
+ DDM._proxy = TRUE;
+
+ var p = Y.Node.create('<div></div>'),
+ b = Y.Node.get('body');
+
+ p.setStyles({
+ position: 'absolute',
+ display: 'none',
+ zIndex: '999',
+ top: '-999px',
+ left: '-999px'
+ });
+
+ b.insertBefore(p, b.get('firstChild'));
+ p.set('id', Y.stamp(p));
+ p.addClass(DDM.CSS_PREFIX + '-proxy');
+ DDM._proxy = p;
+ }
+ },
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _setFrame
+ * @description If resizeProxy is set to true (default) it will resize the proxy element to match the size of the Drag Element.
+ * If positionProxy is set to true (default) it will position the proxy element in the same location as the Drag Element.
+ */
+ _setFrame: function(drag) {
+ var n = drag.get(NODE), d = drag.get(DRAG_NODE), ah, cur = 'auto';
+ if (drag.proxy.get('resizeFrame')) {
+ DDM._proxy.setStyles({
+ height: n.get('offsetHeight') + 'px',
+ width: n.get('offsetWidth') + 'px'
+ });
+ }
+
+ ah = DDM.activeDrag.get('activeHandle');
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = DDM.get('dragCursor');
+ }
+
+
+ d.setStyles({
+ visibility: 'hidden',
+ display: 'block',
+ cursor: cur,
+ border: drag.proxy.get('borderStyle')
+ });
+
+
+
+ if (drag.proxy.get('positionProxy')) {
+ d.setXY(drag.nodeXY);
+ }
+ d.setStyle('visibility', 'visible');
+ }
+ });
+
+ //Create the frame when DOM is ready
+ Y.on('domready', Y.bind(DDM._createFrame, DDM));
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm', 'dd-drag'], skinnable:false});
+YUI.add('dd-constrain', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-constrain
+ */
+ /**
+ * This is a plugin for the dd-drag module to add the constraining methods to it. It supports constraining to a renodenode or viewport. It anode* supports tick based moves and XY axis constraints.
+ * @class DragConstrained
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+
+ var DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ HOST = 'host',
+ CON_2_REGION = 'constrain2region',
+ CON_2_NODE = 'constrain2node',
+ TICK_X_ARRAY = 'tickXArray',
+ TICK_Y_ARRAY = 'tickYArray',
+ DDM = Y.DD.DDM,
+ TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+ proto = null;
+
+ var C = function(config) {
+ C.superclass.constructor.apply(this, arguments);
+ };
+
+ C.NAME = 'DragConstrained';
+ /**
+ * @property con
+ * @description The Constrained instance will be placed on the Drag instance under the con namespace.
+ * @type {String}
+ */
+ C.NS = 'con';
+
+ C.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute stickX
+ * @description Stick the drag movement to the X-Axis. Default: false
+ * @type Boolean
+ */
+ stickX: {
+ value: false
+ },
+ /**
+ * @attribute stickY
+ * @description Stick the drag movement to the Y-Axis
+ * @type Boolean
+ */
+ stickY: {
+ value: false
+ },
+ /**
+ * @attribute tickX
+ * @description The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickX: {
+ value: false
+ },
+ /**
+ * @attribute tickY
+ * @description The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickY: {
+ value: false
+ },
+ /**
+ * @attribute tickXArray
+ * @description An array of page coordinates to use as X ticks for drag movement.
+ * @type Array
+ */
+ tickXArray: {
+ value: false
+ },
+ /**
+ * @attribute tickYArray
+ * @description An array of page coordinates to use as Y ticks for drag movement.
+ * @type Array
+ */
+ tickYArray: {
+ value: false
+ },
+ /**
+ * @attribute constrain2region
+ * @description An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to.
+ * @type Object
+ */
+ constrain2region: {
+ value: false,
+ getter: function(r) {
+ if (Y.Lang.isObject(r)) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ },
+ setter: function (r) {
+ if (Y.Lang.isObject(r)) {
+ if (Y.Lang.isNumber(r[TOP]) && Y.Lang.isNumber(r[RIGHT]) && Y.Lang.isNumber(r[LEFT]) && Y.Lang.isNumber(r[BOTTOM])) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ } else if (r !== false) {
+ return false;
+ }
+ return r;
+ }
+ },
+ /**
+ * @attribute gutter
+ * @description CSS style string for the gutter of a region (supports negative values): '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @type String
+ */
+ gutter: {
+ value: '0',
+ setter: function(gutter) {
+ return Y.DD.DDM.cssSizestoObject(gutter);
+ }
+ },
+ /**
+ * @attribute constrain2node
+ * @description Will attempt to constrain the drag node to the boundaries of this node.
+ * @type Object
+ */
+ constrain2node: {
+ value: false,
+ setter: function(n) {
+ if (!this.get(CON_2_REGION)) {
+ var node = Y.Node.get(n);
+ if (node) {
+ return node;
+ }
+ } else if (this.get(CON_2_REGION) !== false) {
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute constrain2view
+ * @description Will attempt to constrain the drag node to the boundaries of the viewport region.
+ * @type Object
+ */
+ constrain2view: {
+ value: false
+ }
+ };
+
+ proto = {
+ initializer: function() {
+ this.get(HOST).on('drag:start', Y.bind(this._handleStart, this));
+ this.get(HOST).after('drag:align', Y.bind(this.align, this));
+ },
+ /**
+ * @private
+ * @method _handleStart
+ * @description Fires on drag:start and clears the _regionCache
+ */
+ _handleStart: function() {
+ this._regionCache = null;
+ },
+ /**
+ * @private
+ * @property _regionCache
+ * @description Store a cache of the region that we are constraining to
+ * @type Object
+ */
+ _regionCache: null,
+ /**
+ * @private
+ * @method _cacheRegion
+ * @description Get's the region and caches it, called from window.resize and when the cache is null
+ */
+ _cacheRegion: function() {
+ this._regionCache = this.get(CON_2_NODE).get('region');
+ },
+ /**
+ * @method getRegion
+ * @description Get the active region: viewport, node, custom region
+ * @param {Boolean} inc Include the node's height and width
+ * @return {Object}
+ */
+ getRegion: function(inc) {
+ var r = {}, oh = null, ow = null,
+ g = this.get('gutter'),
+ host = this.get(HOST);
+
+ if (this.get(CON_2_NODE)) {
+ if (!this._regionCache) {
+ Y.on('resize', Y.bind(this._cacheRegion, this), window);
+ this._cacheRegion();
+ }
+ r = Y.clone(this._regionCache);
+ } else if (this.get(CON_2_REGION)) {
+ r = this.get(CON_2_REGION);
+ } else if (this.get('constrain2view')) {
+ r = host.get(DRAG_NODE).get('viewportRegion');
+ } else {
+ return false;
+ }
+
+ Y.each(g, function(i, n) {
+ if ((n == RIGHT) || (n == BOTTOM)) {
+ r[n] -= i;
+ } else {
+ r[n] += i;
+ }
+ });
+ if (inc) {
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT);
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+ r[RIGHT] = r[RIGHT] - ow;
+ r[BOTTOM] = r[BOTTOM] - oh;
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _checkRegion
+ * @description Check if xy is inside a given region, if not change to it be inside.
+ * @param {Array} _xy The XY to check if it's in the current region, if it isn't inside the region, it will reset the xy array to be inside the region.
+ * @return {Array} The new XY that is inside the region
+ */
+ _checkRegion: function(_xy) {
+ var oxy = _xy,
+ r = this.getRegion(),
+ host = this.get(HOST),
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+
+ if (oxy[1] > (r[BOTTOM] - oh)) {
+ _xy[1] = (r[BOTTOM] - oh);
+ }
+ if (r[TOP] > oxy[1]) {
+ _xy[1] = r[TOP];
+
+ }
+ if (oxy[0] > (r[RIGHT] - ow)) {
+ _xy[0] = (r[RIGHT] - ow);
+ }
+ if (r[LEFT] > oxy[0]) {
+ _xy[0] = r[LEFT];
+ }
+
+ return _xy;
+ },
+ /**
+ * @method inRegion
+ * @description Checks if the XY passed or the dragNode is inside the active region.
+ * @param {Array} xy Optional XY to check, if not supplied this.get('dragNode').getXY() is used.
+ * @return {Boolean} True if the XY is inside the region, false otherwise.
+ */
+ inRegion: function(xy) {
+ xy = xy || this.get(HOST).get(DRAG_NODE).getXY();
+
+ var _xy = this._checkRegion([xy[0], xy[1]]),
+ inside = false;
+ if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) {
+ inside = true;
+ }
+ return inside;
+ },
+ /**
+ * @method align
+ * @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
+ */
+ align: function() {
+ var host = this.get(HOST),
+ _xy = host.actXY,
+ r = this.getRegion(true);
+
+ if (this.get('stickX')) {
+ _xy[1] = (host.startXY[1] - host.deltaXY[1]);
+ }
+ if (this.get('stickY')) {
+ _xy[0] = (host.startXY[0] - host.deltaXY[0]);
+ }
+
+ if (r) {
+ _xy = this._checkRegion(_xy);
+ }
+
+ _xy = this._checkTicks(_xy, r);
+ host.actXY = _xy;
+ },
+ /**
+ * @private
+ * @method _checkTicks
+ * @description This method delegates the proper helper method for tick calculations
+ * @param {Array} xy The XY coords for the Drag
+ * @param {Object} r The optional region that we are bound to.
+ * @return {Array} The calced XY coords
+ */
+ _checkTicks: function(xy, r) {
+ var host = this.get(HOST),
+ lx = (host.startXY[0] - host.deltaXY[0]),
+ ly = (host.startXY[1] - host.deltaXY[1]),
+ xt = this.get('tickX'),
+ yt = this.get('tickY');
+ if (xt && !this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTicks(xy[0], lx, xt, r[LEFT], r[RIGHT]);
+ }
+ if (yt && !this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]);
+ }
+ if (this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]);
+ }
+ if (this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]);
+ }
+
+ return xy;
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(C, Y.Base, proto);
+ Y.Plugin.DDConstrained = C;
+
+ Y.mix(DDM, {
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTicks
+ * @description Helper method to calculate the tick offsets for a given position
+ * @param {Number} pos The current X or Y position
+ * @param {Number} start The start X or Y position
+ * @param {Number} tick The X or Y tick increment
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return {Number} The new position based on the tick calculation
+ */
+ _calcTicks: function(pos, start, tick, off1, off2) {
+ var ix = ((pos - start) / tick),
+ min = Math.floor(ix),
+ max = Math.ceil(ix);
+ if ((min !== 0) || (max !== 0)) {
+ if ((ix >= min) && (ix <= max)) {
+ pos = (start + (tick * min));
+ if (off1 && off2) {
+ if (pos < off1) {
+ pos = (start + (tick * (min + 1)));
+ }
+ if (pos > off2) {
+ pos = (start + (tick * (min - 1)));
+ }
+ }
+ }
+ }
+ return pos;
+ },
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTickArray
+ * @description This method is used with the tickXArray and tickYArray config options
+ * @param {Number} pos The current X or Y position
+ * @param {Number} ticks The array containing our custom tick positions.
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return The tick position
+ */
+ _calcTickArray: function(pos, ticks, off1, off2) {
+ var i = 0, len = ticks.length, next = 0,
+ diff1, diff2, ret;
+
+ if (!ticks || (ticks.length === 0)) {
+ return pos;
+ } else if (ticks[0] >= pos) {
+ return ticks[0];
+ } else {
+ for (i = 0; i < len; i++) {
+ next = (i + 1);
+ if (ticks[next] && ticks[next] >= pos) {
+ diff1 = pos - ticks[i];
+ diff2 = ticks[next] - pos;
+ ret = (diff2 > diff1) ? ticks[i] : ticks[next];
+ if (off1 && off2) {
+ if (ret > off2) {
+ if (ticks[i]) {
+ ret = ticks[i];
+ } else {
+ ret = ticks[len - 1];
+ }
+ }
+ }
+ return ret;
+ }
+
+ }
+ return ticks[ticks.length - 1];
+ }
+ }
+ });
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drag'], skinnable:false});
+YUI.add('dd-scroll', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-scroll
+ */
+ /**
+ * This class is the base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
+ * This class should not be called on it's own, it's designed to be a plugin.
+ * @class Scroll
+ * @extends Base
+ * @namespace DD
+ * @constructor
+ */
+
+ var S = function() {
+ S.superclass.constructor.apply(this, arguments);
+
+ },
+ HOST = 'host',
+ BUFFER = 'buffer',
+ PARENT_SCROLL = 'parentScroll',
+ WINDOW_SCROLL = 'windowScroll',
+ SCROLL_TOP = 'scrollTop',
+ SCROLL_LEFT = 'scrollLeft',
+ OFFSET_WIDTH = 'offsetWidth',
+ OFFSET_HEIGHT = 'offsetHeight';
+
+
+ S.ATTRS = {
+ /**
+ * @attribute parentScroll
+ * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
+ * @type Node
+ */
+ parentScroll: {
+ value: false,
+ setter: function(node) {
+ if (node) {
+ return node;
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute buffer
+ * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
+ * @type Number
+ */
+ buffer: {
+ value: 30
+ },
+ /**
+ * @attribute scrollDelay
+ * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
+ * @type Number
+ */
+ scrollDelay: {
+ value: 235
+ },
+ /**
+ * @attribute host
+ * @description The host we are plugged into.
+ * @type Object
+ */
+ host: {
+ value: null
+ },
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: false
+ * @type Boolean
+ */
+ windowScroll: {
+ value: false
+ },
+ /**
+ * @attribute vertical
+ * @description Allow vertical scrolling, default: true.
+ * @type Boolean
+ */
+ vertical: {
+ value: true
+ },
+ /**
+ * @attribute horizontal
+ * @description Allow horizontal scrolling, default: true.
+ * @type Boolean
+ */
+ horizontal: {
+ value: true
+ }
+ };
+
+ Y.extend(S, Y.Base, {
+ /**
+ * @private
+ * @property _scrolling
+ * @description Tells if we are actively scrolling or not.
+ * @type Boolean
+ */
+ _scrolling: null,
+ /**
+ * @private
+ * @property _vpRegionCache
+ * @description Cache of the Viewport dims.
+ * @type Object
+ */
+ _vpRegionCache: null,
+ /**
+ * @private
+ * @property _dimCache
+ * @description Cache of the dragNode dims.
+ * @type Object
+ */
+ _dimCache: null,
+ /**
+ * @private
+ * @property _scrollTimer
+ * @description Holder for the Timer object returned from Y.later.
+ * @type {Y.later}
+ */
+ _scrollTimer: null,
+ /**
+ * @private
+ * @method _getVPRegion
+ * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
+ */
+ _getVPRegion: function() {
+ var r = {};
+ //if (!this._vpRegionCache) {
+ var n = this.get(PARENT_SCROLL),
+ b = this.get(BUFFER),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ((ws) ? [] : n.getXY()),
+ w = ((ws) ? 'winWidth' : OFFSET_WIDTH),
+ h = ((ws) ? 'winHeight' : OFFSET_HEIGHT),
+ t = ((ws) ? n.get(SCROLL_TOP) : xy[1]),
+ l = ((ws) ? n.get(SCROLL_LEFT) : xy[0]);
+
+ r = {
+ top: t + b,
+ right: (n.get(w) + l) - b,
+ bottom: (n.get(h) + t) - b,
+ left: l + b
+ };
+ this._vpRegionCache = r;
+ //} else {
+ // r = this._vpRegionCache;
+ //}
+ return r;
+ },
+ initializer: function() {
+ var h = this.get(HOST);
+ h.after('drag:start', Y.bind(this.start, this));
+ h.after('drag:end', Y.bind(this.end, this));
+ h.on('drag:align', Y.bind(this.align, this));
+
+ //TODO - This doesn't work yet??
+ Y.get(window).on('scroll', Y.bind(function() {
+ this._vpRegionCache = null;
+ }, this));
+ },
+ /**
+ * @private
+ * @method _checkWinScroll
+ * @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
+ * @param {Boolean} move Should we move the window. From Y.later
+ */
+ _checkWinScroll: function(move) {
+ var r = this._getVPRegion(),
+ ho = this.get(HOST),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ho.lastXY,
+ scroll = false,
+ b = this.get(BUFFER),
+ win = this.get(PARENT_SCROLL),
+ sTop = win.get(SCROLL_TOP),
+ sLeft = win.get(SCROLL_LEFT),
+ w = this._dimCache.w,
+ h = this._dimCache.h,
+ bottom = xy[1] + h,
+ top = xy[1],
+ right = xy[0] + w,
+ left = xy[0],
+ nt = top,
+ nl = left,
+ st = sTop,
+ sl = sLeft;
+
+ if (this.get('horizontal')) {
+ if (left <= r.left) {
+ scroll = true;
+ nl = xy[0] - ((ws) ? b : 0);
+ sl = sLeft - b;
+ }
+ if (right >= r.right) {
+ scroll = true;
+ nl = xy[0] + ((ws) ? b : 0);
+ sl = sLeft + b;
+ }
+ }
+ if (this.get('vertical')) {
+ if (bottom >= r.bottom) {
+ scroll = true;
+ nt = xy[1] + ((ws) ? b : 0);
+ st = sTop + b;
+
+ }
+ if (top <= r.top) {
+ scroll = true;
+ nt = xy[1] - ((ws) ? b : 0);
+ st = sTop - b;
+ }
+ }
+
+ if (st < 0) {
+ st = 0;
+ nt = xy[1];
+ }
+
+ if (sl < 0) {
+ sl = 0;
+ nl = xy[0];
+ }
+
+ if (nt < 0) {
+ nt = xy[1];
+ }
+ if (nl < 0) {
+ nl = xy[0];
+ }
+ if (move) {
+ ho.actXY = [nl, nt];
+ ho._moveNode({ node: win, top: st, left: sl});
+ if (!st && !sl) {
+ this._cancelScroll();
+ }
+ } else {
+ if (scroll) {
+ this._initScroll();
+ } else {
+ this._cancelScroll();
+ }
+ }
+ },
+ /**
+ * @private
+ * @method _initScroll
+ * @description Cancel a previous scroll timer and init a new one.
+ */
+ _initScroll: function() {
+ this._cancelScroll();
+ this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
+
+ },
+ /**
+ * @private
+ * @method _cancelScroll
+ * @description Cancel a currently running scroll timer.
+ */
+ _cancelScroll: function() {
+ this._scrolling = false;
+ if (this._scrollTimer) {
+ this._scrollTimer.cancel();
+ delete this._scrollTimer;
+ }
+ },
+ /**
+ * @method align
+ * @description Called from the drag:align event to determine if we need to scroll.
+ */
+ align: function(e) {
+ if (this._scrolling) {
+ this._cancelScroll();
+ e.preventDefault();
+ }
+ if (!this._scrolling) {
+ this._checkWinScroll();
+ }
+ },
+ /**
+ * @private
+ * @method _setDimCache
+ * @description Set the cache of the dragNode dims.
+ */
+ _setDimCache: function() {
+ var node = this.get(HOST).get('dragNode');
+ this._dimCache = {
+ h: node.get(OFFSET_HEIGHT),
+ w: node.get(OFFSET_WIDTH)
+ };
+ },
+ /**
+ * @method start
+ * @description Called from the drag:start event
+ */
+ start: function() {
+ this._setDimCache();
+ },
+ /**
+ * @method end
+ * @description Called from the drag:end event
+ */
+ end: function(xy) {
+ this._dimCache = null;
+ this._cancelScroll();
+ },
+ /**
+ * @method toString
+ * @description General toString method for logging
+ * @return String name for the object
+ */
+ toString: function() {
+ return S.NAME + ' #' + this.get('node').get('id');
+ }
+ });
+
+ Y.namespace('Plugin');
+
+
+ /**
+ * Extends the Scroll class to make the window scroll while dragging.
+ * @class DDWindowScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var WS = function() {
+ WS.superclass.constructor.apply(this, arguments);
+ };
+ WS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: true
+ * @type Boolean
+ */
+ windowScroll: {
+ value: true,
+ setter: function(scroll) {
+ if (scroll) {
+ this.set(PARENT_SCROLL, Y.get(window));
+ }
+ return scroll;
+ }
+ }
+ });
+ Y.extend(WS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('windowScroll', this.get('windowScroll'));
+ }
+ });
+ WS.NAME = WS.NS = 'winscroll';
+ Y.Plugin.DDWinScroll = WS;
+
+
+ /**
+ * Extends the Scroll class to make a parent node scroll while dragging.
+ * @class DDNodeScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var NS = function() {
+ NS.superclass.constructor.apply(this, arguments);
+
+ };
+ NS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute node
+ * @description The node we want to scroll. Used to set the internal parentScroll attribute.
+ * @type Node
+ */
+ node: {
+ value: false,
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ if (node !== false) {
+ Y.error('DDNodeScroll: Invalid Node Given: ' + node);
+ }
+ } else {
+ n = n.item(0);
+ this.set(PARENT_SCROLL, n);
+ }
+ return n;
+ }
+ }
+ });
+ Y.extend(NS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('node', this.get('node'));
+ }
+ });
+ NS.NAME = NS.NS = 'nodescroll';
+ Y.Plugin.DDNodeScroll = NS;
+
+ Y.DD.Scroll = S;
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-proxy']});
+YUI.add('dd-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-plugin
+ */
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @class Drag
+ * @extends DD.Drag
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drag = function(config) {
+ config.node = ((Y.Widget && config.host instanceof Y.Widget) ? config.host.get('boundingBox') : config.host);
+ Drag.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-plugin
+ * @type {String}
+ */
+ Drag.NAME = "dd-plugin";
+
+ /**
+ * @property NS
+ * @description The Drag instance will be placed on the Node instance under the dd namespace. It can be accessed via Node.dd;
+ * @type {String}
+ */
+ Drag.NS = "dd";
+
+
+ Y.extend(Drag, Y.DD.Drag);
+ Y.namespace('Plugin');
+ Y.Plugin.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-constrain', 'dd-proxy']});
+YUI.add('dd-drop', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drop
+ */
+ /**
+ * This class provides the ability to create a Drop Target.
+ * @class Drop
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var NODE = 'node',
+ DDM = Y.DD.DDM,
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ /**
+ * @event drop:over
+ * @description Fires when a drag element is over this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_OVER = 'drop:over',
+ /**
+ * @event drop:enter
+ * @description Fires when a drag element enters this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_ENTER = 'drop:enter',
+ /**
+ * @event drop:exit
+ * @description Fires when a drag element exits this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_EXIT = 'drop:exit',
+
+ /**
+ * @event drop:hit
+ * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+
+ Drop = function() {
+ this._lazyAddAttrs = false;
+ Drop.superclass.constructor.apply(this, arguments);
+
+
+ //DD init speed up.
+ Y.on('domready', Y.bind(function() {
+ Y.later(100, this, this._createShim);
+ }, this));
+ DDM._regTarget(this);
+
+ /* TODO
+ if (Dom.getStyle(this.el, 'position') == 'fixed') {
+ Event.on(window, 'scroll', function() {
+ this.activateShim();
+ }, this, true);
+ }
+ */
+ };
+
+ Drop.NAME = 'drop';
+
+ Drop.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to make a Drop Target
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drop: Invalid Node Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drop into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute padding
+ * @description CSS style padding to make the Drop Target bigger than the node.
+ * @type String
+ */
+ padding: {
+ value: '0',
+ setter: function(p) {
+ return DDM.cssSizestoObject(p);
+ }
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drop element.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drop, Y.Base, {
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ var ev = [
+ EV_DROP_OVER,
+ EV_DROP_ENTER,
+ EV_DROP_EXIT,
+ 'drop:hit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ preventable: false,
+ bubbles: true,
+ queuable: false,
+ prefix: 'drop'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+ },
+ /**
+ * @private
+ * @property _valid
+ * @description Flag for determining if the target is valid in this operation.
+ * @type Boolean
+ */
+ _valid: null,
+ /**
+ * @private
+ * @property _groups
+ * @description The groups this target belongs to.
+ * @type Array
+ */
+ _groups: null,
+ /**
+ * @property shim
+ * @description Node reference to the targets shim
+ * @type {Object}
+ */
+ shim: null,
+ /**
+ * @property region
+ * @description A region object associated with this target, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @property overTarget
+ * @description This flag is tripped when a drag element is over this target.
+ * @type Boolean
+ */
+ overTarget: null,
+ /**
+ * @method inGroup
+ * @description Check if this target is in one of the supplied groups.
+ * @param {Array} groups The groups to check against
+ * @return Boolean
+ */
+ inGroup: function(groups) {
+ this._valid = false;
+ var ret = false;
+ Y.each(groups, function(v, k) {
+ if (this._groups[v]) {
+ ret = true;
+ this._valid = true;
+ }
+ }, this);
+ return ret;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Private lifecycle method
+ */
+ initializer: function() {
+ //this._createEvents();
+ Y.later(100, this, this._createEvents);
+
+ var node = this.get(NODE), id;
+ if (!node.get('id')) {
+ id = Y.stamp(node);
+ node.set('id', id);
+ }
+ node.addClass(DDM.CSS_PREFIX + '-drop');
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ DDM._unregTarget(this);
+ if (this.shim) {
+ this.shim.detachAll();
+ this.shim.get('parentNode').removeChild(this.shim);
+ this.shim = null;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
+ this.detachAll();
+ },
+ /**
+ * @private
+ * @method _deactivateShim
+ * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
+ */
+ _deactivateShim: function() {
+ if (!this.shim) {
+ return false;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ this.shim.setStyles({
+ top: '-999px',
+ left: '-999px',
+ zIndex: '1'
+ });
+ this.overTarget = false;
+ },
+ /**
+ * @private
+ * @method _activateShim
+ * @description Activates the shim and adds some interaction CSS classes
+ */
+ _activateShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ var node = this.get(NODE);
+ //TODO Visibility Check..
+ //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
+ if (this.inGroup(DDM.activeDrag.get('groups'))) {
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ DDM._addValid(this);
+ this.overTarget = false;
+ this.sizeShim();
+ } else {
+ DDM._removeValid(this);
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ }
+ },
+ /**
+ * @method sizeShim
+ * @description Positions and sizes the shim with the raw data from the node, this can be used to programatically adjust the Targets shim for Animation..
+ */
+ sizeShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ if (!this.shim) {
+ Y.later(100, this, this.sizeShim);
+ return false;
+ }
+ var node = this.get(NODE),
+ nh = node.get(OFFSET_HEIGHT),
+ nw = node.get(OFFSET_WIDTH),
+ xy = node.getXY(),
+ p = this.get('padding'),
+ dd, dH, dW;
+
+
+ //Apply padding
+ nw = nw + p.left + p.right;
+ nh = nh + p.top + p.bottom;
+ xy[0] = xy[0] - p.left;
+ xy[1] = xy[1] - p.top;
+
+
+ if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
+ //Intersect Mode, make the shim bigger
+ dd = DDM.activeDrag;
+ dH = dd.get(NODE).get(OFFSET_HEIGHT);
+ dW = dd.get(NODE).get(OFFSET_WIDTH);
+
+ nh = (nh + dH);
+ nw = (nw + dW);
+ xy[0] = xy[0] - (dW - dd.deltaXY[0]);
+ xy[1] = xy[1] - (dH - dd.deltaXY[1]);
+
+ }
+
+ //Set the style on the shim
+ this.shim.setStyles({
+ height: nh + 'px',
+ width: nw + 'px',
+ top: xy[1] + 'px',
+ left: xy[0] + 'px'
+ });
+
+ //Create the region to be used by intersect when a drag node is over us.
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + nw,
+ bottom: xy[1] + nh,
+ left: xy[0]
+ };
+ },
+ /**
+ * @private
+ * @method _createShim
+ * @description Creates the Target shim and adds it to the DDM's playground..
+ */
+ _createShim: function() {
+ //No playground, defer
+ if (!DDM._pg) {
+ Y.later(10, this, this._createShim);
+ return;
+ }
+ //Shim already here, cancel
+ if (this.shim) {
+ return;
+ }
+ var s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
+
+ s.setStyles({
+ height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
+ width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
+ backgroundColor: 'yellow',
+ opacity: '.5',
+ zIndex: '1',
+ overflow: 'hidden',
+ top: '-900px',
+ left: '-900px',
+ position: 'absolute'
+ });
+ DDM._pg.appendChild(s);
+ this.shim = s;
+
+ s.on('mouseover', Y.bind(this._handleOverEvent, this));
+ s.on('mouseout', Y.bind(this._handleOutEvent, this));
+ },
+ /**
+ * @private
+ * @method _handleOverTarget
+ * @description This handles the over target call made from this object or from the DDM
+ */
+ _handleTargetOver: function() {
+ if (DDM.isOverTarget(this)) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrop = this;
+ DDM.otherDrops[this] = this;
+ if (this.overTarget) {
+ DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
+ this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
+ } else {
+ this.overTarget = true;
+ this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
+ //TODO - Is this needed??
+ //DDM._handleTargetOver();
+ }
+ } else {
+ this._handleOut();
+ }
+ },
+ /**
+ * @private
+ * @method _handleOverEvent
+ * @description Handles the mouseover DOM event on the Target Shim
+ */
+ _handleOverEvent: function() {
+ this.shim.setStyle('zIndex', '999');
+ DDM._addActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOutEvent
+ * @description Handles the mouseout DOM event on the Target Shim
+ */
+ _handleOutEvent: function() {
+ this.shim.setStyle('zIndex', '1');
+ DDM._removeActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOut
+ * @description Handles out of target calls/checks
+ */
+ _handleOut: function(force) {
+ if (!DDM.isOverTarget(this) || force) {
+ if (this.overTarget) {
+ this.overTarget = false;
+ if (!force) {
+ DDM._removeActiveShim(this);
+ }
+ if (DDM.activeDrag) {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
+ this.fire(EV_DROP_EXIT);
+ DDM.activeDrag.fire('drag:exit', { drop: this });
+ delete DDM.otherDrops[this];
+ //if (DDM.activeDrop === this) {
+ // DDM.activeDrop = null;
+ //}
+ }
+ }
+ }
+ }
+ });
+
+ Y.DD.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});
+YUI.add('dd-drop-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-drop-plugin
+ */
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @class Drop
+ * @extends DD.Drop
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drop = function(config) {
+ config.node = config.host;
+ Drop.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-drop-plugin
+ * @type {String}
+ */
+ Drop.NAME = "dd-drop-plugin";
+ /**
+ * @property NS
+ * @description The Drop instance will be placed on the Node instance under the drop namespace. It can be accessed via Node.drop;
+ * @type {String}
+ */
+ Drop.NS = "drop";
+
+
+ Y.extend(Drop, Y.DD.Drop);
+ Y.namespace('Plugin');
+ Y.Plugin.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drop'], skinnable:false});
+
+
+YUI.add('dd', function(Y){}, '3.0.0' ,{use:['dd-ddm-base', 'dd-ddm', 'dd-ddm-drop', 'dd-drag', 'dd-proxy', 'dd-constrain', 'dd-plugin', 'dd-drop', 'dd-drop-plugin', 'dd-scroll'], skinnable:false});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-drag', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drag
+ */
+ /**
+ * This class provides the ability to drag a Node.
+ * @class Drag
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAGGING = 'dragging',
+ DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ MOUSE_UP = 'mouseup',
+ MOUSE_DOWN = 'mousedown',
+ DRAG_START = 'dragstart',
+ /**
+ * @event drag:mouseDown
+ * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
+ * @preventable _defMouseDownFn
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_MOUSE_DOWN = 'drag:mouseDown',
+ /**
+ * @event drag:afterMouseDown
+ * @description Fires after the mousedown event has been cleared.
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
+ /**
+ * @event drag:removeHandle
+ * @description Fires after a handle is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_HANDLE = 'drag:removeHandle',
+ /**
+ * @event drag:addHandle
+ * @description Fires after a handle is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_HANDLE = 'drag:addHandle',
+ /**
+ * @event drag:removeInvalid
+ * @description Fires after an invalid selector is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_INVALID = 'drag:removeInvalid',
+ /**
+ * @event drag:addInvalid
+ * @description Fires after an invalid selector is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_INVALID = 'drag:addInvalid',
+ /**
+ * @event drag:start
+ * @description Fires at the start of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_START = 'drag:start',
+ /**
+ * @event drag:end
+ * @description Fires at the end of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_END = 'drag:end',
+ /**
+ * @event drag:drag
+ * @description Fires every mousemove during a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DRAG = 'drag:drag',
+ /**
+ * @event drag:align
+ * @preventable _defAlignFn
+ * @description Fires when this node is aligned.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ALIGN = 'drag:align',
+ /**
+ * @event drag:over
+ * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:enter
+ * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:exit
+ * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:drophit
+ * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:dropmiss
+ * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+ Drag = function(o) {
+ this._lazyAddAttrs = false;
+ Drag.superclass.constructor.apply(this, arguments);
+
+ var valid = DDM._regDrag(this);
+ if (!valid) {
+ Y.error('Failed to register node, already in use: ' + o.node);
+ }
+ };
+
+ Drag.NAME = 'drag';
+
+ Drag.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to initiate a drag operation
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid Node Given: ' + node);
+ } else {
+ n = n.item(0);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute dragNode
+ * @description Y.Node instanace to use as the draggable element, defaults to node
+ * @type Node
+ */
+ dragNode: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid dragNode Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute offsetNode
+ * @description Offset the drag element by the difference in cursor position: default true
+ * @type Boolean
+ */
+ offsetNode: {
+ value: true
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: DDM.get('clickPixelThresh')
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: DDM.get('clickTimeThresh')
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drag element so that it can't be dragged: default false.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute data
+ * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
+ * @type Mixed
+ */
+ data: {
+ value: false
+ },
+ /**
+ * @attribute move
+ * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
+ * @type Boolean
+ */
+ move: {
+ value: true
+ },
+ /**
+ * @attribute useShim
+ * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
+ * @type Boolean
+ */
+ useShim: {
+ value: true
+ },
+ /**
+ * @attribute activeHandle
+ * @description This config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
+ * @type Node
+ */
+ activeHandle: {
+ value: false
+ },
+ /**
+ * @attribute primaryButtonOnly
+ * @description By default a drag operation will only begin if the mousedown occurred with the primary mouse button. Setting this to false will allow for all mousedown events to trigger a drag.
+ * @type Boolean
+ */
+ primaryButtonOnly: {
+ value: true
+ },
+ /**
+ * @attribute dragging
+ * @description This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
+ * @type Boolean
+ */
+ dragging: {
+ value: false
+ },
+ parent: {
+ value: false
+ },
+ /**
+ * @attribute target
+ * @description This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
+ * @type Boolean
+ */
+ target: {
+ value: false,
+ setter: function(config) {
+ this._handleTarget(config);
+ return config;
+ }
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
+ * @type String
+ */
+ dragMode: {
+ value: null,
+ setter: function(mode) {
+ return DDM._setDragMode(mode);
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drag into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ getter: function() {
+ if (!this._groups) {
+ this._groups = {};
+ }
+ var ret = [];
+ Y.each(this._groups, function(v, k) {
+ ret[ret.length] = k;
+ });
+ return ret;
+ },
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute handles
+ * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
+ * @type Array
+ */
+ handles: {
+ value: null,
+ setter: function(g) {
+ if (g) {
+ this._handles = {};
+ Y.each(g, function(v, k) {
+ this._handles[v] = true;
+ }, this);
+ } else {
+ this._handles = null;
+ }
+ return g;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drag, Y.Base, {
+ /**
+ * @method addToGroup
+ * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
+ * @param {String} g The group to add this Drag Instance to.
+ * @return {Self}
+ * @chainable
+ */
+ addToGroup: function(g) {
+ this._groups[g] = true;
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @method removeFromGroup
+ * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
+ * @param {String} g The group to remove this Drag Instance from.
+ * @return {Self}
+ * @chainable
+ */
+ removeFromGroup: function(g) {
+ delete this._groups[g];
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @property target
+ * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
+ * @type {Object}
+ */
+ target: null,
+ /**
+ * @private
+ * @method _handleTarget
+ * @description Attribute handler for the target config attribute.
+ * @param {Boolean/Object}
+ * @return {Boolean/Object}
+ */
+ _handleTarget: function(config) {
+ if (Y.DD.Drop) {
+ if (config === false) {
+ if (this.target) {
+ DDM._unregTarget(this.target);
+ this.target = null;
+ }
+ return false;
+ } else {
+ if (!Y.Lang.isObject(config)) {
+ config = {};
+ }
+ config.bubbles = ('bubbles' in config) ? config.bubbles : this.get('bubbles');
+ config.node = this.get(NODE);
+ config.groups = config.groups || this.get('groups');
+ this.target = new Y.DD.Drop(config);
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @private
+ * @property _groups
+ * @description Storage Array for the groups this drag belongs to.
+ * @type {Array}
+ */
+ _groups: null,
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ this.publish(EV_MOUSE_DOWN, {
+ defaultFn: this._defMouseDownFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_ALIGN, {
+ defaultFn: this._defAlignFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_DRAG, {
+ defaultFn: this._defDragFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_END, {
+ preventedFn: this._prevEndFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ var ev = [
+ EV_AFTER_MOUSE_DOWN,
+ EV_REMOVE_HANDLE,
+ EV_ADD_HANDLE,
+ EV_REMOVE_INVALID,
+ EV_ADD_INVALID,
+ EV_START,
+ 'drag:drophit',
+ 'drag:dropmiss',
+ 'drag:over',
+ 'drag:enter',
+ 'drag:exit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ bubbles: true,
+ preventable: false,
+ queuable: false,
+ prefix: 'drag'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+
+ },
+ /**
+ * @private
+ * @property _ev_md
+ * @description A private reference to the mousedown DOM event
+ * @type {Event.Facade}
+ */
+ _ev_md: null,
+ /**
+ * @private
+ * @property _startTime
+ * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _startTime: null,
+ /**
+ * @private
+ * @property _endTime
+ * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _endTime: null,
+ /**
+ * @private
+ * @property _handles
+ * @description A private hash of the valid drag handles
+ * @type {Object}
+ */
+ _handles: null,
+ /**
+ * @private
+ * @property _invalids
+ * @description A private hash of the invalid selector strings
+ * @type {Object}
+ */
+ _invalids: null,
+ /**
+ * @private
+ * @property _invalidsDefault
+ * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
+ * @type {Object}
+ */
+ _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
+ /**
+ * @private
+ * @property _dragThreshMet
+ * @description Private flag to see if the drag threshhold was met
+ * @type {Boolean}
+ */
+ _dragThreshMet: null,
+ /**
+ * @private
+ * @property _fromTimeout
+ * @description Flag to determine if the drag operation came from a timeout
+ * @type {Boolean}
+ */
+ _fromTimeout: null,
+ /**
+ * @private
+ * @property _clickTimeout
+ * @description Holder for the setTimeout call
+ * @type {Boolean}
+ */
+ _clickTimeout: null,
+ /**
+ * @property deltaXY
+ * @description The offset of the mouse position to the element's position
+ * @type {Array}
+ */
+ deltaXY: null,
+ /**
+ * @property startXY
+ * @description The initial mouse position
+ * @type {Array}
+ */
+ startXY: null,
+ /**
+ * @property nodeXY
+ * @description The initial element position
+ * @type {Array}
+ */
+ nodeXY: null,
+ /**
+ * @property lastXY
+ * @description The position of the element as it's moving (for offset calculations)
+ * @type {Array}
+ */
+ lastXY: null,
+ /**
+ * @property actXY
+ * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
+ * @type {Array}
+ */
+ actXY: null,
+ /**
+ * @property realXY
+ * @description The real xy position of the node.
+ * @type {Array}
+ */
+ realXY: null,
+ /**
+ * @property mouseXY
+ * @description The XY coords of the mousemove
+ * @type {Array}
+ */
+ mouseXY: null,
+ /**
+ * @property region
+ * @description A region object associated with this drag, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @private
+ * @method _handleMouseUp
+ * @description Handler for the mouseup DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseUp: function(ev) {
+ this._fixIEMouseUp();
+ if (DDM.activeDrag) {
+ DDM._end();
+ }
+ },
+ /**
+ * @private
+ * @method _fixDragStart
+ * @description The function we use as the ondragstart handler when we start a drag in Internet Explorer. This keeps IE from blowing up on images as drag handles.
+ */
+ _fixDragStart: function(e) {
+ e.preventDefault();
+ },
+ /**
+ * @private
+ * @method _ieSelectFix
+ * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
+ */
+ _ieSelectFix: function() {
+ return false;
+ },
+ /**
+ * @private
+ * @property _ieSelectBack
+ * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
+ */
+ _ieSelectBack: null,
+ /**
+ * @private
+ * @method _fixIEMouseDown
+ * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
+ */
+ _fixIEMouseDown: function() {
+ if (Y.UA.ie) {
+ this._ieSelectBack = Y.config.doc.body.onselectstart;
+ Y.config.doc.body.onselectstart = this._ieSelectFix;
+ }
+ },
+ /**
+ * @private
+ * @method _fixIEMouseUp
+ * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
+ */
+ _fixIEMouseUp: function() {
+ if (Y.UA.ie) {
+ Y.config.doc.body.onselectstart = this._ieSelectBack;
+ }
+ },
+ /**
+ * @private
+ * @method _handleMouseDownEvent
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseDownEvent: function(ev) {
+ this.fire(EV_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @private
+ * @method _defMouseDownFn
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _defMouseDownFn: function(e) {
+ var ev = e.ev;
+ this._dragThreshMet = false;
+ this._ev_md = ev;
+
+ if (this.get('primaryButtonOnly') && ev.button > 1) {
+ return false;
+ }
+ if (this.validClick(ev)) {
+ this._fixIEMouseDown();
+ ev.halt();
+ this._setStartPosition([ev.pageX, ev.pageY]);
+
+ DDM.activeDrag = this;
+
+ this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
+ }
+ this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @method validClick
+ * @description Method first checks to see if we have handles, if so it validates the click against the handle. Then if it finds a valid handle, it checks it against the invalid handles list. Returns true if a good handle was used, false otherwise.
+ * @param {Event.Facade}
+ * @return {Boolean}
+ */
+ validClick: function(ev) {
+ var r = false, n = false,
+ tar = ev.target,
+ hTest = null,
+ els = null,
+ set = false;
+ if (this._handles) {
+ Y.each(this._handles, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *') && !hTest) {
+ hTest = n;
+ r = true;
+ }
+ }
+ });
+ } else {
+ n = this.get(NODE)
+ if (n.contains(tar) || n.compareTo(tar)) {
+ r = true;
+ }
+ }
+ if (r) {
+ if (this._invalids) {
+ Y.each(this._invalids, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *')) {
+ r = false;
+ }
+ }
+ });
+ }
+ }
+ if (r) {
+ if (hTest) {
+ els = ev.currentTarget.queryAll(hTest);
+ set = false;
+ els.each(function(n, i) {
+ if ((n.contains(tar) || n.compareTo(tar)) && !set) {
+ set = true;
+ this.set('activeHandle', n);
+ }
+ }, this);
+ } else {
+ this.set('activeHandle', this.get(NODE));
+ }
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _setStartPosition
+ * @description Sets the current position of the Element and calculates the offset
+ * @param {Array} xy The XY coords to set the position to.
+ */
+ _setStartPosition: function(xy) {
+ this.startXY = xy;
+
+ this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
+
+ if (this.get('offsetNode')) {
+ this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
+ } else {
+ this.deltaXY = [0, 0];
+ }
+ },
+ /**
+ * @private
+ * @method _timeoutCheck
+ * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
+ */
+ _timeoutCheck: function() {
+ if (!this.get('lock') && !this._dragThreshMet) {
+ this._fromTimeout = this._dragThreshMet = true;
+ this.start();
+ this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
+ }
+ },
+ /**
+ * @method removeHandle
+ * @description Remove a Selector added by addHandle
+ * @param {String} str The selector for the handle to be removed.
+ * @return {Self}
+ * @chainable
+ */
+ removeHandle: function(str) {
+ if (this._handles[str]) {
+ delete this._handles[str];
+ this.fire(EV_REMOVE_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addHandle
+ * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
+ * @param {String} str The selector to test for a valid handle. Must be a child of the element.
+ * @return {Self}
+ * @chainable
+ */
+ addHandle: function(str) {
+ if (!this._handles) {
+ this._handles = {};
+ }
+ if (Y.Lang.isString(str)) {
+ this._handles[str] = true;
+ this.fire(EV_ADD_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method removeInvalid
+ * @description Remove an invalid handle added by addInvalid
+ * @param {String} str The invalid handle to remove from the internal list.
+ * @return {Self}
+ * @chainable
+ */
+ removeInvalid: function(str) {
+ if (this._invalids[str]) {
+ this._invalids[str] = null;
+ delete this._invalids[str];
+ this.fire(EV_REMOVE_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addInvalid
+ * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
+ * @param {String} str The selector to test against to determine if this is an invalid drag handle.
+ * @return {Self}
+ * @chainable
+ */
+ addInvalid: function(str) {
+ if (Y.Lang.isString(str)) {
+ this._invalids[str] = true;
+ this.fire(EV_ADD_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Internal init handler
+ */
+ initializer: function() {
+ this.get(NODE).dd = this;
+
+ if (!this.get(NODE).get('id')) {
+ var id = Y.stamp(this.get(NODE));
+ this.get(NODE).set('id', id);
+ }
+
+ this.actXY = [];
+
+ this._invalids = Y.clone(this._invalidsDefault, true);
+
+ this._createEvents();
+
+ if (!this.get(DRAG_NODE)) {
+ this.set(DRAG_NODE, this.get(NODE));
+ }
+
+ //Fix for #2528096
+ //Don't prep the DD instance until all plugins are loaded.
+ this.on('initializedChange', Y.bind(this._prep, this));
+
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method _prep
+ * @description Attach event listners and add classname
+ */
+ _prep: function() {
+ this._dragThreshMet = false;
+ var node = this.get(NODE);
+ node.addClass(DDM.CSS_PREFIX + '-draggable');
+ node.on(MOUSE_DOWN, Y.bind(this._handleMouseDownEvent, this));
+ node.on(MOUSE_UP, Y.bind(this._handleMouseUp, this));
+ node.on(DRAG_START, Y.bind(this._fixDragStart, this));
+ },
+ /**
+ * @private
+ * @method _unprep
+ * @description Detach event listeners and remove classname
+ */
+ _unprep: function() {
+ var node = this.get(NODE);
+ node.removeClass(DDM.CSS_PREFIX + '-draggable');
+ node.detachAll();
+ },
+ /**
+ * @method start
+ * @description Starts the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ start: function() {
+ if (!this.get('lock') && !this.get(DRAGGING)) {
+ var node = this.get(NODE), ow = node.get(OFFSET_WIDTH), oh = node.get(OFFSET_HEIGHT);
+ this._startTime = (new Date()).getTime();
+
+ DDM._start();
+ node.addClass(DDM.CSS_PREFIX + '-dragging');
+ this.fire(EV_START, {
+ pageX: this.nodeXY[0],
+ pageY: this.nodeXY[1],
+ startTime: this._startTime
+ });
+ var xy = this.nodeXY;
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + ow,
+ bottom: xy[1] + oh,
+ left: xy[0]
+ };
+ this.set(DRAGGING, true);
+ }
+ return this;
+ },
+ /**
+ * @method end
+ * @description Ends the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ end: function() {
+ this._endTime = (new Date()).getTime();
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._dragThreshMet = false;
+ this._fromTimeout = false;
+ if (!this.get('lock') && this.get(DRAGGING)) {
+ this.fire(EV_END, {
+ pageX: this.lastXY[0],
+ pageY: this.lastXY[1],
+ startTime: this._startTime,
+ endTime: this._endTime
+ });
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
+ this.set(DRAGGING, false);
+ this.deltaXY = [0, 0];
+
+ return this;
+ },
+ /**
+ * @private
+ * @method _prevEndFn
+ * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
+ */
+ _prevEndFn: function(e) {
+ //Bug #1852577
+ this.get(DRAG_NODE).setXY(this.nodeXY);
+ },
+ /**
+ * @private
+ * @method _align
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Array} xy The xy coords to align with.
+ */
+ _align: function(xy) {
+ this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
+ },
+ /**
+ * @private
+ * @method _defAlignFn
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Event.Facade} e The drag:align event.
+ */
+ _defAlignFn: function(e) {
+ this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
+ },
+ /**
+ * @private
+ * @method _alignNode
+ * @description This method performs the alignment before the element move.
+ * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
+ */
+ _alignNode: function(eXY) {
+ this._align(eXY);
+ this._moveNode();
+ },
+ /**
+ * @private
+ * @method _moveNode
+ * @description This method performs the actual element move.
+ */
+ _moveNode: function(scroll) {
+ //if (!this.get(DRAGGING)) {
+ // return;
+ //}
+ var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
+
+ diffXY[0] = (xy[0] - this.lastXY[0]);
+ diffXY[1] = (xy[1] - this.lastXY[1]);
+
+ diffXY2[0] = (xy[0] - this.nodeXY[0]);
+ diffXY2[1] = (xy[1] - this.nodeXY[1]);
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
+ bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ left: xy[0]
+ };
+
+ this.fire(EV_DRAG, {
+ pageX: xy[0],
+ pageY: xy[1],
+ scroll: scroll,
+ info: {
+ start: startXY,
+ xy: xy,
+ delta: diffXY,
+ offset: diffXY2
+ }
+ });
+
+ this.lastXY = xy;
+ },
+ /**
+ * @private
+ * @method _defDragFn
+ * @description Default function for drag:drag. Fired from _moveNode.
+ * @param {Event.Facade} ev The drag:drag event
+ */
+ _defDragFn: function(e) {
+ if (this.get('move')) {
+ if (e.scroll) {
+ e.scroll.node.set('scrollTop', e.scroll.top);
+ e.scroll.node.set('scrollLeft', e.scroll.left);
+ }
+ this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
+ this.realXY = [e.pageX, e.pageY];
+ }
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Fired from DragDropMgr (DDM) on mousemove.
+ * @param {Event.Facade} ev The mousemove DOM event
+ */
+ _move: function(ev) {
+ if (this.get('lock')) {
+ return false;
+ } else {
+ this.mouseXY = [ev.pageX, ev.pageY];
+ if (!this._dragThreshMet) {
+ var diffX = Math.abs(this.startXY[0] - ev.pageX),
+ diffY = Math.abs(this.startXY[1] - ev.pageY);
+ if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
+ this._dragThreshMet = true;
+ this.start();
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ } else {
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.get(DRAGGING)) {
+ DDM._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ this._unprep();
+ this.detachAll();
+ if (this.target) {
+ this.target.destroy();
+ }
+ DDM._unregDrag(this);
+ }
+ });
+ Y.namespace('DD');
+ Y.DD.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-drag",function(D){var E=D.DD.DDM,U="node",G="dragging",N="dragNode",C="offsetHeight",K="offsetWidth",S="mouseup",P="mousedown",M="dragstart",H="drag:mouseDown",B="drag:afterMouseDown",F="drag:removeHandle",L="drag:addHandle",R="drag:removeInvalid",T="drag:addInvalid",J="drag:start",I="drag:end",O="drag:drag",Q="drag:align",A=function(W){this._lazyAddAttrs=false;A.superclass.constructor.apply(this,arguments);var V=E._regDrag(this);if(!V){D.error("Failed to register node, already in use: "+W.node);}};A.NAME="drag";A.ATTRS={node:{setter:function(V){var W=D.get(V);if(!W){D.error("DD.Drag: Invalid Node Given: "+V);}else{W=W.item(0);}return W;}},dragNode:{setter:function(V){var W=D.Node.get(V);if(!W){D.error("DD.Drag: Invalid dragNode Given: "+V);}return W;}},offsetNode:{value:true},clickPixelThresh:{value:E.get("clickPixelThresh")},clickTimeThresh:{value:E.get("clickTimeThresh")},lock:{value:false,setter:function(V){if(V){this.get(U).addClass(E.CSS_PREFIX+"-locked");}else{this.get(U).removeClass(E.CSS_PREFIX+"-locked");}return V;}},data:{value:false},move:{value:true},useShim:{value:true},activeHandle:{value:false},primaryButtonOnly:{value:true},dragging:{value:false},parent:{value:false},target:{value:false,setter:function(V){this._handleTarget(V);return V;}},dragMode:{value:null,setter:function(V){return E._setDragMode(V);}},groups:{value:["default"],getter:function(){if(!this._groups){this._groups={};}var V=[];D.each(this._groups,function(X,W){V[V.length]=W;});return V;},setter:function(V){this._groups={};D.each(V,function(X,W){this._groups[X]=true;},this);return V;}},handles:{value:null,setter:function(V){if(V){this._handles={};D.each(V,function(X,W){this._handles[X]=true;},this);}else{this._handles=null;}return V;}},bubbles:{writeOnce:true,value:D.DD.DDM}};D.extend(A,D.Base,{addToGroup:function(V){this._groups[V]=true;E._activateTargets();return this;},removeFromGroup:function(V){delete this._groups[V];E._activateTargets();return this;},target:null,_handleTarget:function(V){if(D.DD.Drop){if(V===false){if(this.target){E._unregTarget(this.target);this.target=null;}return false;}else{if(!D.Lang.isObject(V)){V={};}V.bubbles=("bubbles" in V)?V.bubbles:this.get("bubbles");V.node=this.get(U);V.groups=V.groups||this.get("groups");this.target=new D.DD.Drop(V);}}else{return false;}},_groups:null,_createEvents:function(){this.publish(H,{defaultFn:this._defMouseDownFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});this.publish(Q,{defaultFn:this._defAlignFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});this.publish(O,{defaultFn:this._defDragFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});this.publish(I,{preventedFn:this._prevEndFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});var V=[B,F,L,R,T,J,"drag:drophit","drag:dropmiss","drag:over","drag:enter","drag:exit"];D.each(V,function(X,W){this.publish(X,{type:X,emitFacade:true,bubbles:true,preventable:false,queuable:false,prefix:"drag"});},this);if(this.get("bubbles")){this.addTarget(this.get("bubbles"));}},_ev_md:null,_startTime:null,_endTime:null,_handles:null,_invalids:null,_invalidsDefault:{"textarea":true,"input":true,"a":true,"button":true,"select":true},_dragThreshMet:null,_fromTimeout:null,_clickTimeout:null,deltaXY:null,startXY:null,nodeXY:null,lastXY:null,actXY:null,realXY:null,mouseXY:null,region:null,_handleMouseUp:function(V){this._fixIEMouseUp();if(E.activeDrag){E._end();}},_fixDragStart:function(V){V.preventDefault();},_ieSelectFix:function(){return false;},_ieSelectBack:null,_fixIEMouseDown:function(){if(D.UA.ie){this._ieSelectBack=D.config.doc.body.onselectstart;D.config.doc.body.onselectstart=this._ieSelectFix;}},_fixIEMouseUp:function(){if(D.UA.ie){D.config.doc.body.onselectstart=this._ieSelectBack;}},_handleMouseDownEvent:function(V){this.fire(H,{ev:V});},_defMouseDownFn:function(W){var V=W.ev;this._dragThreshMet=false;this._ev_md=V;if(this.get("primaryButtonOnly")&&V.button>1){return false;}if(this.validClick(V)){this._fixIEMouseDown();V.halt();this._setStartPosition([V.pageX,V.pageY]);E.activeDrag=this;this._clickTimeout=D.later(this.get("clickTimeThresh"),this,this._timeoutCheck);}this.fire(B,{ev:V});},validClick:function(Z){var Y=false,b=false,V=Z.target,X=null,W=null,a=false;if(this._handles){D.each(this._handles,function(c,d){if(D.Lang.isString(d)){if(V.test(d+", "+d+" *")&&!X){X=d;Y=true;}}});}else{b=this.get(U);if(b.contains(V)||b.compareTo(V)){Y=true;}}if(Y){if(this._invalids){D.each(this._invalids,function(c,d){if(D.Lang.isString(d)){if(V.test(d+", "+d+" *")){Y=false;}}});}}if(Y){if(X){W=Z.currentTarget.queryAll(X);a=false;W.each(function(d,c){if((d.contains(V)||d.compareTo(V))&&!a){a=true;this.set("activeHandle",d);}},this);}else{this.set("activeHandle",this.get(U));}}return Y;},_setStartPosition:function(V){this.startXY=V;this.nodeXY=this.lastXY=this.realXY=this.get(U).getXY();if(this.get("offsetNode")){this.deltaXY=[(this.startXY[0]-this.nodeXY[0]),(this.startXY[1]-this.nodeXY[1])];}else{this.deltaXY=[0,0];}},_timeoutCheck:function(){if(!this.get("lock")&&!this._dragThreshMet){this._fromTimeout=this._dragThreshMet=true;this.start();this._alignNode([this._ev_md.pageX,this._ev_md.pageY],true);}},removeHandle:function(V){if(this._handles[V]){delete this._handles[V];this.fire(F,{handle:V});}return this;},addHandle:function(V){if(!this._handles){this._handles={};}if(D.Lang.isString(V)){this._handles[V]=true;this.fire(L,{handle:V});}return this;},removeInvalid:function(V){if(this._invalids[V]){this._invalids[V]=null;delete this._invalids[V];this.fire(R,{handle:V});}return this;},addInvalid:function(V){if(D.Lang.isString(V)){this._invalids[V]=true;this.fire(T,{handle:V});}return this;},initializer:function(){this.get(U).dd=this;if(!this.get(U).get("id")){var V=D.stamp(this.get(U));this.get(U).set("id",V);}this.actXY=[];this._invalids=D.clone(this._invalidsDefault,true);this._createEvents();if(!this.get(N)){this.set(N,this.get(U));}this.on("initializedChange",D.bind(this._prep,this));
+this.set("groups",this.get("groups"));},_prep:function(){this._dragThreshMet=false;var V=this.get(U);V.addClass(E.CSS_PREFIX+"-draggable");V.on(P,D.bind(this._handleMouseDownEvent,this));V.on(S,D.bind(this._handleMouseUp,this));V.on(M,D.bind(this._fixDragStart,this));},_unprep:function(){var V=this.get(U);V.removeClass(E.CSS_PREFIX+"-draggable");V.detachAll();},start:function(){if(!this.get("lock")&&!this.get(G)){var W=this.get(U),V=W.get(K),X=W.get(C);this._startTime=(new Date()).getTime();E._start();W.addClass(E.CSS_PREFIX+"-dragging");this.fire(J,{pageX:this.nodeXY[0],pageY:this.nodeXY[1],startTime:this._startTime});var Y=this.nodeXY;this.region={"0":Y[0],"1":Y[1],area:0,top:Y[1],right:Y[0]+V,bottom:Y[1]+X,left:Y[0]};this.set(G,true);}return this;},end:function(){this._endTime=(new Date()).getTime();if(this._clickTimeout){this._clickTimeout.cancel();}this._dragThreshMet=false;this._fromTimeout=false;if(!this.get("lock")&&this.get(G)){this.fire(I,{pageX:this.lastXY[0],pageY:this.lastXY[1],startTime:this._startTime,endTime:this._endTime});}this.get(U).removeClass(E.CSS_PREFIX+"-dragging");this.set(G,false);this.deltaXY=[0,0];return this;},_prevEndFn:function(V){this.get(N).setXY(this.nodeXY);},_align:function(V){this.fire(Q,{pageX:V[0],pageY:V[1]});},_defAlignFn:function(V){this.actXY=[V.pageX-this.deltaXY[0],V.pageY-this.deltaXY[1]];},_alignNode:function(V){this._align(V);this._moveNode();},_moveNode:function(V){var W=[],X=[],Z=this.nodeXY,Y=this.actXY;W[0]=(Y[0]-this.lastXY[0]);W[1]=(Y[1]-this.lastXY[1]);X[0]=(Y[0]-this.nodeXY[0]);X[1]=(Y[1]-this.nodeXY[1]);this.region={"0":Y[0],"1":Y[1],area:0,top:Y[1],right:Y[0]+this.get(N).get(K),bottom:Y[1]+this.get(N).get(C),left:Y[0]};this.fire(O,{pageX:Y[0],pageY:Y[1],scroll:V,info:{start:Z,xy:Y,delta:W,offset:X}});this.lastXY=Y;},_defDragFn:function(V){if(this.get("move")){if(V.scroll){V.scroll.node.set("scrollTop",V.scroll.top);V.scroll.node.set("scrollLeft",V.scroll.left);}this.get(N).setXY([V.pageX,V.pageY]);this.realXY=[V.pageX,V.pageY];}},_move:function(X){if(this.get("lock")){return false;}else{this.mouseXY=[X.pageX,X.pageY];if(!this._dragThreshMet){var W=Math.abs(this.startXY[0]-X.pageX),V=Math.abs(this.startXY[1]-X.pageY);if(W>this.get("clickPixelThresh")||V>this.get("clickPixelThresh")){this._dragThreshMet=true;this.start();this._alignNode([X.pageX,X.pageY]);}}else{if(this._clickTimeout){this._clickTimeout.cancel();}this._alignNode([X.pageX,X.pageY]);}}},stopDrag:function(){if(this.get(G)){E._end();}return this;},destructor:function(){this._unprep();this.detachAll();if(this.target){this.target.destroy();}E._unregDrag(this);}});D.namespace("DD");D.DD.Drag=A;},"3.0.0",{requires:["dd-ddm-base"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-drag', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drag
+ */
+ /**
+ * This class provides the ability to drag a Node.
+ * @class Drag
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAGGING = 'dragging',
+ DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ MOUSE_UP = 'mouseup',
+ MOUSE_DOWN = 'mousedown',
+ DRAG_START = 'dragstart',
+ /**
+ * @event drag:mouseDown
+ * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
+ * @preventable _defMouseDownFn
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_MOUSE_DOWN = 'drag:mouseDown',
+ /**
+ * @event drag:afterMouseDown
+ * @description Fires after the mousedown event has been cleared.
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
+ /**
+ * @event drag:removeHandle
+ * @description Fires after a handle is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_HANDLE = 'drag:removeHandle',
+ /**
+ * @event drag:addHandle
+ * @description Fires after a handle is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_HANDLE = 'drag:addHandle',
+ /**
+ * @event drag:removeInvalid
+ * @description Fires after an invalid selector is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_INVALID = 'drag:removeInvalid',
+ /**
+ * @event drag:addInvalid
+ * @description Fires after an invalid selector is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_INVALID = 'drag:addInvalid',
+ /**
+ * @event drag:start
+ * @description Fires at the start of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_START = 'drag:start',
+ /**
+ * @event drag:end
+ * @description Fires at the end of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_END = 'drag:end',
+ /**
+ * @event drag:drag
+ * @description Fires every mousemove during a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DRAG = 'drag:drag',
+ /**
+ * @event drag:align
+ * @preventable _defAlignFn
+ * @description Fires when this node is aligned.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ALIGN = 'drag:align',
+ /**
+ * @event drag:over
+ * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:enter
+ * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:exit
+ * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:drophit
+ * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:dropmiss
+ * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+ Drag = function(o) {
+ this._lazyAddAttrs = false;
+ Drag.superclass.constructor.apply(this, arguments);
+
+ var valid = DDM._regDrag(this);
+ if (!valid) {
+ Y.error('Failed to register node, already in use: ' + o.node);
+ }
+ };
+
+ Drag.NAME = 'drag';
+
+ Drag.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to initiate a drag operation
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid Node Given: ' + node);
+ } else {
+ n = n.item(0);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute dragNode
+ * @description Y.Node instanace to use as the draggable element, defaults to node
+ * @type Node
+ */
+ dragNode: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid dragNode Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute offsetNode
+ * @description Offset the drag element by the difference in cursor position: default true
+ * @type Boolean
+ */
+ offsetNode: {
+ value: true
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: DDM.get('clickPixelThresh')
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: DDM.get('clickTimeThresh')
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drag element so that it can't be dragged: default false.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute data
+ * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
+ * @type Mixed
+ */
+ data: {
+ value: false
+ },
+ /**
+ * @attribute move
+ * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
+ * @type Boolean
+ */
+ move: {
+ value: true
+ },
+ /**
+ * @attribute useShim
+ * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
+ * @type Boolean
+ */
+ useShim: {
+ value: true
+ },
+ /**
+ * @attribute activeHandle
+ * @description This config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
+ * @type Node
+ */
+ activeHandle: {
+ value: false
+ },
+ /**
+ * @attribute primaryButtonOnly
+ * @description By default a drag operation will only begin if the mousedown occurred with the primary mouse button. Setting this to false will allow for all mousedown events to trigger a drag.
+ * @type Boolean
+ */
+ primaryButtonOnly: {
+ value: true
+ },
+ /**
+ * @attribute dragging
+ * @description This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
+ * @type Boolean
+ */
+ dragging: {
+ value: false
+ },
+ parent: {
+ value: false
+ },
+ /**
+ * @attribute target
+ * @description This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
+ * @type Boolean
+ */
+ target: {
+ value: false,
+ setter: function(config) {
+ this._handleTarget(config);
+ return config;
+ }
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
+ * @type String
+ */
+ dragMode: {
+ value: null,
+ setter: function(mode) {
+ return DDM._setDragMode(mode);
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drag into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ getter: function() {
+ if (!this._groups) {
+ this._groups = {};
+ }
+ var ret = [];
+ Y.each(this._groups, function(v, k) {
+ ret[ret.length] = k;
+ });
+ return ret;
+ },
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute handles
+ * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
+ * @type Array
+ */
+ handles: {
+ value: null,
+ setter: function(g) {
+ if (g) {
+ this._handles = {};
+ Y.each(g, function(v, k) {
+ this._handles[v] = true;
+ }, this);
+ } else {
+ this._handles = null;
+ }
+ return g;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drag, Y.Base, {
+ /**
+ * @method addToGroup
+ * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
+ * @param {String} g The group to add this Drag Instance to.
+ * @return {Self}
+ * @chainable
+ */
+ addToGroup: function(g) {
+ this._groups[g] = true;
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @method removeFromGroup
+ * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
+ * @param {String} g The group to remove this Drag Instance from.
+ * @return {Self}
+ * @chainable
+ */
+ removeFromGroup: function(g) {
+ delete this._groups[g];
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @property target
+ * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
+ * @type {Object}
+ */
+ target: null,
+ /**
+ * @private
+ * @method _handleTarget
+ * @description Attribute handler for the target config attribute.
+ * @param {Boolean/Object}
+ * @return {Boolean/Object}
+ */
+ _handleTarget: function(config) {
+ if (Y.DD.Drop) {
+ if (config === false) {
+ if (this.target) {
+ DDM._unregTarget(this.target);
+ this.target = null;
+ }
+ return false;
+ } else {
+ if (!Y.Lang.isObject(config)) {
+ config = {};
+ }
+ config.bubbles = ('bubbles' in config) ? config.bubbles : this.get('bubbles');
+ config.node = this.get(NODE);
+ config.groups = config.groups || this.get('groups');
+ this.target = new Y.DD.Drop(config);
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @private
+ * @property _groups
+ * @description Storage Array for the groups this drag belongs to.
+ * @type {Array}
+ */
+ _groups: null,
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ this.publish(EV_MOUSE_DOWN, {
+ defaultFn: this._defMouseDownFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_ALIGN, {
+ defaultFn: this._defAlignFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_DRAG, {
+ defaultFn: this._defDragFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_END, {
+ preventedFn: this._prevEndFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ var ev = [
+ EV_AFTER_MOUSE_DOWN,
+ EV_REMOVE_HANDLE,
+ EV_ADD_HANDLE,
+ EV_REMOVE_INVALID,
+ EV_ADD_INVALID,
+ EV_START,
+ 'drag:drophit',
+ 'drag:dropmiss',
+ 'drag:over',
+ 'drag:enter',
+ 'drag:exit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ bubbles: true,
+ preventable: false,
+ queuable: false,
+ prefix: 'drag'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+
+ },
+ /**
+ * @private
+ * @property _ev_md
+ * @description A private reference to the mousedown DOM event
+ * @type {Event.Facade}
+ */
+ _ev_md: null,
+ /**
+ * @private
+ * @property _startTime
+ * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _startTime: null,
+ /**
+ * @private
+ * @property _endTime
+ * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _endTime: null,
+ /**
+ * @private
+ * @property _handles
+ * @description A private hash of the valid drag handles
+ * @type {Object}
+ */
+ _handles: null,
+ /**
+ * @private
+ * @property _invalids
+ * @description A private hash of the invalid selector strings
+ * @type {Object}
+ */
+ _invalids: null,
+ /**
+ * @private
+ * @property _invalidsDefault
+ * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
+ * @type {Object}
+ */
+ _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
+ /**
+ * @private
+ * @property _dragThreshMet
+ * @description Private flag to see if the drag threshhold was met
+ * @type {Boolean}
+ */
+ _dragThreshMet: null,
+ /**
+ * @private
+ * @property _fromTimeout
+ * @description Flag to determine if the drag operation came from a timeout
+ * @type {Boolean}
+ */
+ _fromTimeout: null,
+ /**
+ * @private
+ * @property _clickTimeout
+ * @description Holder for the setTimeout call
+ * @type {Boolean}
+ */
+ _clickTimeout: null,
+ /**
+ * @property deltaXY
+ * @description The offset of the mouse position to the element's position
+ * @type {Array}
+ */
+ deltaXY: null,
+ /**
+ * @property startXY
+ * @description The initial mouse position
+ * @type {Array}
+ */
+ startXY: null,
+ /**
+ * @property nodeXY
+ * @description The initial element position
+ * @type {Array}
+ */
+ nodeXY: null,
+ /**
+ * @property lastXY
+ * @description The position of the element as it's moving (for offset calculations)
+ * @type {Array}
+ */
+ lastXY: null,
+ /**
+ * @property actXY
+ * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
+ * @type {Array}
+ */
+ actXY: null,
+ /**
+ * @property realXY
+ * @description The real xy position of the node.
+ * @type {Array}
+ */
+ realXY: null,
+ /**
+ * @property mouseXY
+ * @description The XY coords of the mousemove
+ * @type {Array}
+ */
+ mouseXY: null,
+ /**
+ * @property region
+ * @description A region object associated with this drag, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @private
+ * @method _handleMouseUp
+ * @description Handler for the mouseup DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseUp: function(ev) {
+ this._fixIEMouseUp();
+ if (DDM.activeDrag) {
+ DDM._end();
+ }
+ },
+ /**
+ * @private
+ * @method _fixDragStart
+ * @description The function we use as the ondragstart handler when we start a drag in Internet Explorer. This keeps IE from blowing up on images as drag handles.
+ */
+ _fixDragStart: function(e) {
+ e.preventDefault();
+ },
+ /**
+ * @private
+ * @method _ieSelectFix
+ * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
+ */
+ _ieSelectFix: function() {
+ return false;
+ },
+ /**
+ * @private
+ * @property _ieSelectBack
+ * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
+ */
+ _ieSelectBack: null,
+ /**
+ * @private
+ * @method _fixIEMouseDown
+ * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
+ */
+ _fixIEMouseDown: function() {
+ if (Y.UA.ie) {
+ this._ieSelectBack = Y.config.doc.body.onselectstart;
+ Y.config.doc.body.onselectstart = this._ieSelectFix;
+ }
+ },
+ /**
+ * @private
+ * @method _fixIEMouseUp
+ * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
+ */
+ _fixIEMouseUp: function() {
+ if (Y.UA.ie) {
+ Y.config.doc.body.onselectstart = this._ieSelectBack;
+ }
+ },
+ /**
+ * @private
+ * @method _handleMouseDownEvent
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseDownEvent: function(ev) {
+ this.fire(EV_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @private
+ * @method _defMouseDownFn
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _defMouseDownFn: function(e) {
+ var ev = e.ev;
+ this._dragThreshMet = false;
+ this._ev_md = ev;
+
+ if (this.get('primaryButtonOnly') && ev.button > 1) {
+ return false;
+ }
+ if (this.validClick(ev)) {
+ this._fixIEMouseDown();
+ ev.halt();
+ this._setStartPosition([ev.pageX, ev.pageY]);
+
+ DDM.activeDrag = this;
+
+ this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
+ }
+ this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @method validClick
+ * @description Method first checks to see if we have handles, if so it validates the click against the handle. Then if it finds a valid handle, it checks it against the invalid handles list. Returns true if a good handle was used, false otherwise.
+ * @param {Event.Facade}
+ * @return {Boolean}
+ */
+ validClick: function(ev) {
+ var r = false, n = false,
+ tar = ev.target,
+ hTest = null,
+ els = null,
+ set = false;
+ if (this._handles) {
+ Y.each(this._handles, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *') && !hTest) {
+ hTest = n;
+ r = true;
+ }
+ }
+ });
+ } else {
+ n = this.get(NODE)
+ if (n.contains(tar) || n.compareTo(tar)) {
+ r = true;
+ }
+ }
+ if (r) {
+ if (this._invalids) {
+ Y.each(this._invalids, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *')) {
+ r = false;
+ }
+ }
+ });
+ }
+ }
+ if (r) {
+ if (hTest) {
+ els = ev.currentTarget.queryAll(hTest);
+ set = false;
+ els.each(function(n, i) {
+ if ((n.contains(tar) || n.compareTo(tar)) && !set) {
+ set = true;
+ this.set('activeHandle', n);
+ }
+ }, this);
+ } else {
+ this.set('activeHandle', this.get(NODE));
+ }
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _setStartPosition
+ * @description Sets the current position of the Element and calculates the offset
+ * @param {Array} xy The XY coords to set the position to.
+ */
+ _setStartPosition: function(xy) {
+ this.startXY = xy;
+
+ this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
+
+ if (this.get('offsetNode')) {
+ this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
+ } else {
+ this.deltaXY = [0, 0];
+ }
+ },
+ /**
+ * @private
+ * @method _timeoutCheck
+ * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
+ */
+ _timeoutCheck: function() {
+ if (!this.get('lock') && !this._dragThreshMet) {
+ this._fromTimeout = this._dragThreshMet = true;
+ this.start();
+ this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
+ }
+ },
+ /**
+ * @method removeHandle
+ * @description Remove a Selector added by addHandle
+ * @param {String} str The selector for the handle to be removed.
+ * @return {Self}
+ * @chainable
+ */
+ removeHandle: function(str) {
+ if (this._handles[str]) {
+ delete this._handles[str];
+ this.fire(EV_REMOVE_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addHandle
+ * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
+ * @param {String} str The selector to test for a valid handle. Must be a child of the element.
+ * @return {Self}
+ * @chainable
+ */
+ addHandle: function(str) {
+ if (!this._handles) {
+ this._handles = {};
+ }
+ if (Y.Lang.isString(str)) {
+ this._handles[str] = true;
+ this.fire(EV_ADD_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method removeInvalid
+ * @description Remove an invalid handle added by addInvalid
+ * @param {String} str The invalid handle to remove from the internal list.
+ * @return {Self}
+ * @chainable
+ */
+ removeInvalid: function(str) {
+ if (this._invalids[str]) {
+ this._invalids[str] = null;
+ delete this._invalids[str];
+ this.fire(EV_REMOVE_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addInvalid
+ * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
+ * @param {String} str The selector to test against to determine if this is an invalid drag handle.
+ * @return {Self}
+ * @chainable
+ */
+ addInvalid: function(str) {
+ if (Y.Lang.isString(str)) {
+ this._invalids[str] = true;
+ this.fire(EV_ADD_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Internal init handler
+ */
+ initializer: function() {
+ this.get(NODE).dd = this;
+
+ if (!this.get(NODE).get('id')) {
+ var id = Y.stamp(this.get(NODE));
+ this.get(NODE).set('id', id);
+ }
+
+ this.actXY = [];
+
+ this._invalids = Y.clone(this._invalidsDefault, true);
+
+ this._createEvents();
+
+ if (!this.get(DRAG_NODE)) {
+ this.set(DRAG_NODE, this.get(NODE));
+ }
+
+ //Fix for #2528096
+ //Don't prep the DD instance until all plugins are loaded.
+ this.on('initializedChange', Y.bind(this._prep, this));
+
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method _prep
+ * @description Attach event listners and add classname
+ */
+ _prep: function() {
+ this._dragThreshMet = false;
+ var node = this.get(NODE);
+ node.addClass(DDM.CSS_PREFIX + '-draggable');
+ node.on(MOUSE_DOWN, Y.bind(this._handleMouseDownEvent, this));
+ node.on(MOUSE_UP, Y.bind(this._handleMouseUp, this));
+ node.on(DRAG_START, Y.bind(this._fixDragStart, this));
+ },
+ /**
+ * @private
+ * @method _unprep
+ * @description Detach event listeners and remove classname
+ */
+ _unprep: function() {
+ var node = this.get(NODE);
+ node.removeClass(DDM.CSS_PREFIX + '-draggable');
+ node.detachAll();
+ },
+ /**
+ * @method start
+ * @description Starts the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ start: function() {
+ if (!this.get('lock') && !this.get(DRAGGING)) {
+ var node = this.get(NODE), ow = node.get(OFFSET_WIDTH), oh = node.get(OFFSET_HEIGHT);
+ this._startTime = (new Date()).getTime();
+
+ DDM._start();
+ node.addClass(DDM.CSS_PREFIX + '-dragging');
+ this.fire(EV_START, {
+ pageX: this.nodeXY[0],
+ pageY: this.nodeXY[1],
+ startTime: this._startTime
+ });
+ var xy = this.nodeXY;
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + ow,
+ bottom: xy[1] + oh,
+ left: xy[0]
+ };
+ this.set(DRAGGING, true);
+ }
+ return this;
+ },
+ /**
+ * @method end
+ * @description Ends the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ end: function() {
+ this._endTime = (new Date()).getTime();
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._dragThreshMet = false;
+ this._fromTimeout = false;
+ if (!this.get('lock') && this.get(DRAGGING)) {
+ this.fire(EV_END, {
+ pageX: this.lastXY[0],
+ pageY: this.lastXY[1],
+ startTime: this._startTime,
+ endTime: this._endTime
+ });
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
+ this.set(DRAGGING, false);
+ this.deltaXY = [0, 0];
+
+ return this;
+ },
+ /**
+ * @private
+ * @method _prevEndFn
+ * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
+ */
+ _prevEndFn: function(e) {
+ //Bug #1852577
+ this.get(DRAG_NODE).setXY(this.nodeXY);
+ },
+ /**
+ * @private
+ * @method _align
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Array} xy The xy coords to align with.
+ */
+ _align: function(xy) {
+ this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
+ },
+ /**
+ * @private
+ * @method _defAlignFn
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Event.Facade} e The drag:align event.
+ */
+ _defAlignFn: function(e) {
+ this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
+ },
+ /**
+ * @private
+ * @method _alignNode
+ * @description This method performs the alignment before the element move.
+ * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
+ */
+ _alignNode: function(eXY) {
+ this._align(eXY);
+ this._moveNode();
+ },
+ /**
+ * @private
+ * @method _moveNode
+ * @description This method performs the actual element move.
+ */
+ _moveNode: function(scroll) {
+ //if (!this.get(DRAGGING)) {
+ // return;
+ //}
+ var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
+
+ diffXY[0] = (xy[0] - this.lastXY[0]);
+ diffXY[1] = (xy[1] - this.lastXY[1]);
+
+ diffXY2[0] = (xy[0] - this.nodeXY[0]);
+ diffXY2[1] = (xy[1] - this.nodeXY[1]);
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
+ bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ left: xy[0]
+ };
+
+ this.fire(EV_DRAG, {
+ pageX: xy[0],
+ pageY: xy[1],
+ scroll: scroll,
+ info: {
+ start: startXY,
+ xy: xy,
+ delta: diffXY,
+ offset: diffXY2
+ }
+ });
+
+ this.lastXY = xy;
+ },
+ /**
+ * @private
+ * @method _defDragFn
+ * @description Default function for drag:drag. Fired from _moveNode.
+ * @param {Event.Facade} ev The drag:drag event
+ */
+ _defDragFn: function(e) {
+ if (this.get('move')) {
+ if (e.scroll) {
+ e.scroll.node.set('scrollTop', e.scroll.top);
+ e.scroll.node.set('scrollLeft', e.scroll.left);
+ }
+ this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
+ this.realXY = [e.pageX, e.pageY];
+ }
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Fired from DragDropMgr (DDM) on mousemove.
+ * @param {Event.Facade} ev The mousemove DOM event
+ */
+ _move: function(ev) {
+ if (this.get('lock')) {
+ return false;
+ } else {
+ this.mouseXY = [ev.pageX, ev.pageY];
+ if (!this._dragThreshMet) {
+ var diffX = Math.abs(this.startXY[0] - ev.pageX),
+ diffY = Math.abs(this.startXY[1] - ev.pageY);
+ if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
+ this._dragThreshMet = true;
+ this.start();
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ } else {
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.get(DRAGGING)) {
+ DDM._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ this._unprep();
+ this.detachAll();
+ if (this.target) {
+ this.target.destroy();
+ }
+ DDM._unregDrag(this);
+ }
+ });
+ Y.namespace('DD');
+ Y.DD.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-drop', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drop
+ */
+ /**
+ * This class provides the ability to create a Drop Target.
+ * @class Drop
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var NODE = 'node',
+ DDM = Y.DD.DDM,
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ /**
+ * @event drop:over
+ * @description Fires when a drag element is over this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_OVER = 'drop:over',
+ /**
+ * @event drop:enter
+ * @description Fires when a drag element enters this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_ENTER = 'drop:enter',
+ /**
+ * @event drop:exit
+ * @description Fires when a drag element exits this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_EXIT = 'drop:exit',
+
+ /**
+ * @event drop:hit
+ * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+
+ Drop = function() {
+ this._lazyAddAttrs = false;
+ Drop.superclass.constructor.apply(this, arguments);
+
+
+ //DD init speed up.
+ Y.on('domready', Y.bind(function() {
+ Y.later(100, this, this._createShim);
+ }, this));
+ DDM._regTarget(this);
+
+ /* TODO
+ if (Dom.getStyle(this.el, 'position') == 'fixed') {
+ Event.on(window, 'scroll', function() {
+ this.activateShim();
+ }, this, true);
+ }
+ */
+ };
+
+ Drop.NAME = 'drop';
+
+ Drop.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to make a Drop Target
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drop: Invalid Node Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drop into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute padding
+ * @description CSS style padding to make the Drop Target bigger than the node.
+ * @type String
+ */
+ padding: {
+ value: '0',
+ setter: function(p) {
+ return DDM.cssSizestoObject(p);
+ }
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drop element.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drop, Y.Base, {
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ var ev = [
+ EV_DROP_OVER,
+ EV_DROP_ENTER,
+ EV_DROP_EXIT,
+ 'drop:hit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ preventable: false,
+ bubbles: true,
+ queuable: false,
+ prefix: 'drop'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+ },
+ /**
+ * @private
+ * @property _valid
+ * @description Flag for determining if the target is valid in this operation.
+ * @type Boolean
+ */
+ _valid: null,
+ /**
+ * @private
+ * @property _groups
+ * @description The groups this target belongs to.
+ * @type Array
+ */
+ _groups: null,
+ /**
+ * @property shim
+ * @description Node reference to the targets shim
+ * @type {Object}
+ */
+ shim: null,
+ /**
+ * @property region
+ * @description A region object associated with this target, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @property overTarget
+ * @description This flag is tripped when a drag element is over this target.
+ * @type Boolean
+ */
+ overTarget: null,
+ /**
+ * @method inGroup
+ * @description Check if this target is in one of the supplied groups.
+ * @param {Array} groups The groups to check against
+ * @return Boolean
+ */
+ inGroup: function(groups) {
+ this._valid = false;
+ var ret = false;
+ Y.each(groups, function(v, k) {
+ if (this._groups[v]) {
+ ret = true;
+ this._valid = true;
+ }
+ }, this);
+ return ret;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Private lifecycle method
+ */
+ initializer: function() {
+ //this._createEvents();
+ Y.later(100, this, this._createEvents);
+
+ var node = this.get(NODE), id;
+ if (!node.get('id')) {
+ id = Y.stamp(node);
+ node.set('id', id);
+ }
+ node.addClass(DDM.CSS_PREFIX + '-drop');
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ DDM._unregTarget(this);
+ if (this.shim) {
+ this.shim.detachAll();
+ this.shim.get('parentNode').removeChild(this.shim);
+ this.shim = null;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
+ this.detachAll();
+ },
+ /**
+ * @private
+ * @method _deactivateShim
+ * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
+ */
+ _deactivateShim: function() {
+ if (!this.shim) {
+ return false;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ this.shim.setStyles({
+ top: '-999px',
+ left: '-999px',
+ zIndex: '1'
+ });
+ this.overTarget = false;
+ },
+ /**
+ * @private
+ * @method _activateShim
+ * @description Activates the shim and adds some interaction CSS classes
+ */
+ _activateShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ var node = this.get(NODE);
+ //TODO Visibility Check..
+ //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
+ if (this.inGroup(DDM.activeDrag.get('groups'))) {
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ DDM._addValid(this);
+ this.overTarget = false;
+ this.sizeShim();
+ } else {
+ DDM._removeValid(this);
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ }
+ },
+ /**
+ * @method sizeShim
+ * @description Positions and sizes the shim with the raw data from the node, this can be used to programatically adjust the Targets shim for Animation..
+ */
+ sizeShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ if (!this.shim) {
+ Y.later(100, this, this.sizeShim);
+ return false;
+ }
+ var node = this.get(NODE),
+ nh = node.get(OFFSET_HEIGHT),
+ nw = node.get(OFFSET_WIDTH),
+ xy = node.getXY(),
+ p = this.get('padding'),
+ dd, dH, dW;
+
+
+ //Apply padding
+ nw = nw + p.left + p.right;
+ nh = nh + p.top + p.bottom;
+ xy[0] = xy[0] - p.left;
+ xy[1] = xy[1] - p.top;
+
+
+ if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
+ //Intersect Mode, make the shim bigger
+ dd = DDM.activeDrag;
+ dH = dd.get(NODE).get(OFFSET_HEIGHT);
+ dW = dd.get(NODE).get(OFFSET_WIDTH);
+
+ nh = (nh + dH);
+ nw = (nw + dW);
+ xy[0] = xy[0] - (dW - dd.deltaXY[0]);
+ xy[1] = xy[1] - (dH - dd.deltaXY[1]);
+
+ }
+
+ //Set the style on the shim
+ this.shim.setStyles({
+ height: nh + 'px',
+ width: nw + 'px',
+ top: xy[1] + 'px',
+ left: xy[0] + 'px'
+ });
+
+ //Create the region to be used by intersect when a drag node is over us.
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + nw,
+ bottom: xy[1] + nh,
+ left: xy[0]
+ };
+ },
+ /**
+ * @private
+ * @method _createShim
+ * @description Creates the Target shim and adds it to the DDM's playground..
+ */
+ _createShim: function() {
+ //No playground, defer
+ if (!DDM._pg) {
+ Y.later(10, this, this._createShim);
+ return;
+ }
+ //Shim already here, cancel
+ if (this.shim) {
+ return;
+ }
+ var s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
+
+ s.setStyles({
+ height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
+ width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
+ backgroundColor: 'yellow',
+ opacity: '.5',
+ zIndex: '1',
+ overflow: 'hidden',
+ top: '-900px',
+ left: '-900px',
+ position: 'absolute'
+ });
+ DDM._pg.appendChild(s);
+ this.shim = s;
+
+ s.on('mouseover', Y.bind(this._handleOverEvent, this));
+ s.on('mouseout', Y.bind(this._handleOutEvent, this));
+ },
+ /**
+ * @private
+ * @method _handleOverTarget
+ * @description This handles the over target call made from this object or from the DDM
+ */
+ _handleTargetOver: function() {
+ if (DDM.isOverTarget(this)) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrop = this;
+ DDM.otherDrops[this] = this;
+ if (this.overTarget) {
+ DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
+ this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
+ } else {
+ this.overTarget = true;
+ this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
+ //TODO - Is this needed??
+ //DDM._handleTargetOver();
+ }
+ } else {
+ this._handleOut();
+ }
+ },
+ /**
+ * @private
+ * @method _handleOverEvent
+ * @description Handles the mouseover DOM event on the Target Shim
+ */
+ _handleOverEvent: function() {
+ this.shim.setStyle('zIndex', '999');
+ DDM._addActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOutEvent
+ * @description Handles the mouseout DOM event on the Target Shim
+ */
+ _handleOutEvent: function() {
+ this.shim.setStyle('zIndex', '1');
+ DDM._removeActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOut
+ * @description Handles out of target calls/checks
+ */
+ _handleOut: function(force) {
+ if (!DDM.isOverTarget(this) || force) {
+ if (this.overTarget) {
+ this.overTarget = false;
+ if (!force) {
+ DDM._removeActiveShim(this);
+ }
+ if (DDM.activeDrag) {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
+ this.fire(EV_DROP_EXIT);
+ DDM.activeDrag.fire('drag:exit', { drop: this });
+ delete DDM.otherDrops[this];
+ //if (DDM.activeDrop === this) {
+ // DDM.activeDrop = null;
+ //}
+ }
+ }
+ }
+ }
+ });
+
+ Y.DD.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-drop",function(A){var B="node",G=A.DD.DDM,F="offsetHeight",C="offsetWidth",I="drop:over",H="drop:enter",D="drop:exit",E=function(){this._lazyAddAttrs=false;E.superclass.constructor.apply(this,arguments);A.on("domready",A.bind(function(){A.later(100,this,this._createShim);},this));G._regTarget(this);};E.NAME="drop";E.ATTRS={node:{setter:function(J){var K=A.Node.get(J);if(!K){A.error("DD.Drop: Invalid Node Given: "+J);}return K;}},groups:{value:["default"],setter:function(J){this._groups={};A.each(J,function(L,K){this._groups[L]=true;},this);return J;}},padding:{value:"0",setter:function(J){return G.cssSizestoObject(J);}},lock:{value:false,setter:function(J){if(J){this.get(B).addClass(G.CSS_PREFIX+"-drop-locked");}else{this.get(B).removeClass(G.CSS_PREFIX+"-drop-locked");}return J;}},bubbles:{writeOnce:true,value:A.DD.DDM}};A.extend(E,A.Base,{_createEvents:function(){var J=[I,H,D,"drop:hit"];A.each(J,function(L,K){this.publish(L,{type:L,emitFacade:true,preventable:false,bubbles:true,queuable:false,prefix:"drop"});},this);if(this.get("bubbles")){this.addTarget(this.get("bubbles"));}},_valid:null,_groups:null,shim:null,region:null,overTarget:null,inGroup:function(J){this._valid=false;var K=false;A.each(J,function(M,L){if(this._groups[M]){K=true;this._valid=true;}},this);return K;},initializer:function(){A.later(100,this,this._createEvents);var J=this.get(B),K;if(!J.get("id")){K=A.stamp(J);J.set("id",K);}J.addClass(G.CSS_PREFIX+"-drop");this.set("groups",this.get("groups"));},destructor:function(){G._unregTarget(this);if(this.shim){this.shim.detachAll();this.shim.get("parentNode").removeChild(this.shim);this.shim=null;}this.get(B).removeClass(G.CSS_PREFIX+"-drop");this.detachAll();},_deactivateShim:function(){if(!this.shim){return false;}this.get(B).removeClass(G.CSS_PREFIX+"-drop-active-valid");this.get(B).removeClass(G.CSS_PREFIX+"-drop-active-invalid");this.get(B).removeClass(G.CSS_PREFIX+"-drop-over");this.shim.setStyles({top:"-999px",left:"-999px",zIndex:"1"});this.overTarget=false;},_activateShim:function(){if(!G.activeDrag){return false;}if(this.get(B)===G.activeDrag.get(B)){return false;}if(this.get("lock")){return false;}var J=this.get(B);if(this.inGroup(G.activeDrag.get("groups"))){J.removeClass(G.CSS_PREFIX+"-drop-active-invalid");J.addClass(G.CSS_PREFIX+"-drop-active-valid");G._addValid(this);this.overTarget=false;this.sizeShim();}else{G._removeValid(this);J.removeClass(G.CSS_PREFIX+"-drop-active-valid");J.addClass(G.CSS_PREFIX+"-drop-active-invalid");}},sizeShim:function(){if(!G.activeDrag){return false;}if(this.get(B)===G.activeDrag.get(B)){return false;}if(this.get("lock")){return false;}if(!this.shim){A.later(100,this,this.sizeShim);return false;}var O=this.get(B),M=O.get(F),K=O.get(C),Q=O.getXY(),P=this.get("padding"),J,N,L;K=K+P.left+P.right;M=M+P.top+P.bottom;Q[0]=Q[0]-P.left;Q[1]=Q[1]-P.top;if(G.activeDrag.get("dragMode")===G.INTERSECT){J=G.activeDrag;N=J.get(B).get(F);L=J.get(B).get(C);M=(M+N);K=(K+L);Q[0]=Q[0]-(L-J.deltaXY[0]);Q[1]=Q[1]-(N-J.deltaXY[1]);}this.shim.setStyles({height:M+"px",width:K+"px",top:Q[1]+"px",left:Q[0]+"px"});this.region={"0":Q[0],"1":Q[1],area:0,top:Q[1],right:Q[0]+K,bottom:Q[1]+M,left:Q[0]};},_createShim:function(){if(!G._pg){A.later(10,this,this._createShim);return;}if(this.shim){return;}var J=A.Node.create('<div id="'+this.get(B).get("id")+'_shim"></div>');J.setStyles({height:this.get(B).get(F)+"px",width:this.get(B).get(C)+"px",backgroundColor:"yellow",opacity:".5",zIndex:"1",overflow:"hidden",top:"-900px",left:"-900px",position:"absolute"});G._pg.appendChild(J);this.shim=J;J.on("mouseover",A.bind(this._handleOverEvent,this));J.on("mouseout",A.bind(this._handleOutEvent,this));},_handleTargetOver:function(){if(G.isOverTarget(this)){this.get(B).addClass(G.CSS_PREFIX+"-drop-over");G.activeDrop=this;G.otherDrops[this]=this;if(this.overTarget){G.activeDrag.fire("drag:over",{drop:this,drag:G.activeDrag});this.fire(I,{drop:this,drag:G.activeDrag});}else{this.overTarget=true;this.fire(H,{drop:this,drag:G.activeDrag});G.activeDrag.fire("drag:enter",{drop:this,drag:G.activeDrag});G.activeDrag.get(B).addClass(G.CSS_PREFIX+"-drag-over");}}else{this._handleOut();}},_handleOverEvent:function(){this.shim.setStyle("zIndex","999");G._addActiveShim(this);},_handleOutEvent:function(){this.shim.setStyle("zIndex","1");G._removeActiveShim(this);},_handleOut:function(J){if(!G.isOverTarget(this)||J){if(this.overTarget){this.overTarget=false;if(!J){G._removeActiveShim(this);}if(G.activeDrag){this.get(B).removeClass(G.CSS_PREFIX+"-drop-over");G.activeDrag.get(B).removeClass(G.CSS_PREFIX+"-drag-over");this.fire(D);G.activeDrag.fire("drag:exit",{drop:this});delete G.otherDrops[this];}}}}});A.DD.Drop=E;},"3.0.0",{requires:["dd-ddm-drop","dd-drag"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-drop-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-drop-plugin
+ */
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @class Drop
+ * @extends DD.Drop
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drop = function(config) {
+ config.node = config.host;
+ Drop.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-drop-plugin
+ * @type {String}
+ */
+ Drop.NAME = "dd-drop-plugin";
+ /**
+ * @property NS
+ * @description The Drop instance will be placed on the Node instance under the drop namespace. It can be accessed via Node.drop;
+ * @type {String}
+ */
+ Drop.NS = "drop";
+
+
+ Y.extend(Drop, Y.DD.Drop);
+ Y.namespace('Plugin');
+ Y.Plugin.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drop'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-drop-plugin",function(A){var B=function(C){C.node=C.host;B.superclass.constructor.apply(this,arguments);};B.NAME="dd-drop-plugin";B.NS="drop";A.extend(B,A.DD.Drop);A.namespace("Plugin");A.Plugin.Drop=B;},"3.0.0",{requires:["dd-drop"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-drop-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-drop-plugin
+ */
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @class Drop
+ * @extends DD.Drop
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drop = function(config) {
+ config.node = config.host;
+ Drop.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-drop-plugin
+ * @type {String}
+ */
+ Drop.NAME = "dd-drop-plugin";
+ /**
+ * @property NS
+ * @description The Drop instance will be placed on the Node instance under the drop namespace. It can be accessed via Node.drop;
+ * @type {String}
+ */
+ Drop.NS = "drop";
+
+
+ Y.extend(Drop, Y.DD.Drop);
+ Y.namespace('Plugin');
+ Y.Plugin.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drop'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-drop', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drop
+ */
+ /**
+ * This class provides the ability to create a Drop Target.
+ * @class Drop
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var NODE = 'node',
+ DDM = Y.DD.DDM,
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ /**
+ * @event drop:over
+ * @description Fires when a drag element is over this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_OVER = 'drop:over',
+ /**
+ * @event drop:enter
+ * @description Fires when a drag element enters this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_ENTER = 'drop:enter',
+ /**
+ * @event drop:exit
+ * @description Fires when a drag element exits this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_EXIT = 'drop:exit',
+
+ /**
+ * @event drop:hit
+ * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+
+ Drop = function() {
+ this._lazyAddAttrs = false;
+ Drop.superclass.constructor.apply(this, arguments);
+
+
+ //DD init speed up.
+ Y.on('domready', Y.bind(function() {
+ Y.later(100, this, this._createShim);
+ }, this));
+ DDM._regTarget(this);
+
+ /* TODO
+ if (Dom.getStyle(this.el, 'position') == 'fixed') {
+ Event.on(window, 'scroll', function() {
+ this.activateShim();
+ }, this, true);
+ }
+ */
+ };
+
+ Drop.NAME = 'drop';
+
+ Drop.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to make a Drop Target
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drop: Invalid Node Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drop into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute padding
+ * @description CSS style padding to make the Drop Target bigger than the node.
+ * @type String
+ */
+ padding: {
+ value: '0',
+ setter: function(p) {
+ return DDM.cssSizestoObject(p);
+ }
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drop element.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drop, Y.Base, {
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ var ev = [
+ EV_DROP_OVER,
+ EV_DROP_ENTER,
+ EV_DROP_EXIT,
+ 'drop:hit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ preventable: false,
+ bubbles: true,
+ queuable: false,
+ prefix: 'drop'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+ },
+ /**
+ * @private
+ * @property _valid
+ * @description Flag for determining if the target is valid in this operation.
+ * @type Boolean
+ */
+ _valid: null,
+ /**
+ * @private
+ * @property _groups
+ * @description The groups this target belongs to.
+ * @type Array
+ */
+ _groups: null,
+ /**
+ * @property shim
+ * @description Node reference to the targets shim
+ * @type {Object}
+ */
+ shim: null,
+ /**
+ * @property region
+ * @description A region object associated with this target, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @property overTarget
+ * @description This flag is tripped when a drag element is over this target.
+ * @type Boolean
+ */
+ overTarget: null,
+ /**
+ * @method inGroup
+ * @description Check if this target is in one of the supplied groups.
+ * @param {Array} groups The groups to check against
+ * @return Boolean
+ */
+ inGroup: function(groups) {
+ this._valid = false;
+ var ret = false;
+ Y.each(groups, function(v, k) {
+ if (this._groups[v]) {
+ ret = true;
+ this._valid = true;
+ }
+ }, this);
+ return ret;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Private lifecycle method
+ */
+ initializer: function() {
+ //this._createEvents();
+ Y.later(100, this, this._createEvents);
+
+ var node = this.get(NODE), id;
+ if (!node.get('id')) {
+ id = Y.stamp(node);
+ node.set('id', id);
+ }
+ node.addClass(DDM.CSS_PREFIX + '-drop');
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ DDM._unregTarget(this);
+ if (this.shim) {
+ this.shim.detachAll();
+ this.shim.get('parentNode').removeChild(this.shim);
+ this.shim = null;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
+ this.detachAll();
+ },
+ /**
+ * @private
+ * @method _deactivateShim
+ * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
+ */
+ _deactivateShim: function() {
+ if (!this.shim) {
+ return false;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ this.shim.setStyles({
+ top: '-999px',
+ left: '-999px',
+ zIndex: '1'
+ });
+ this.overTarget = false;
+ },
+ /**
+ * @private
+ * @method _activateShim
+ * @description Activates the shim and adds some interaction CSS classes
+ */
+ _activateShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ var node = this.get(NODE);
+ //TODO Visibility Check..
+ //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
+ if (this.inGroup(DDM.activeDrag.get('groups'))) {
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ DDM._addValid(this);
+ this.overTarget = false;
+ this.sizeShim();
+ } else {
+ DDM._removeValid(this);
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ }
+ },
+ /**
+ * @method sizeShim
+ * @description Positions and sizes the shim with the raw data from the node, this can be used to programatically adjust the Targets shim for Animation..
+ */
+ sizeShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ if (!this.shim) {
+ Y.later(100, this, this.sizeShim);
+ return false;
+ }
+ var node = this.get(NODE),
+ nh = node.get(OFFSET_HEIGHT),
+ nw = node.get(OFFSET_WIDTH),
+ xy = node.getXY(),
+ p = this.get('padding'),
+ dd, dH, dW;
+
+
+ //Apply padding
+ nw = nw + p.left + p.right;
+ nh = nh + p.top + p.bottom;
+ xy[0] = xy[0] - p.left;
+ xy[1] = xy[1] - p.top;
+
+
+ if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
+ //Intersect Mode, make the shim bigger
+ dd = DDM.activeDrag;
+ dH = dd.get(NODE).get(OFFSET_HEIGHT);
+ dW = dd.get(NODE).get(OFFSET_WIDTH);
+
+ nh = (nh + dH);
+ nw = (nw + dW);
+ xy[0] = xy[0] - (dW - dd.deltaXY[0]);
+ xy[1] = xy[1] - (dH - dd.deltaXY[1]);
+
+ }
+
+ //Set the style on the shim
+ this.shim.setStyles({
+ height: nh + 'px',
+ width: nw + 'px',
+ top: xy[1] + 'px',
+ left: xy[0] + 'px'
+ });
+
+ //Create the region to be used by intersect when a drag node is over us.
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + nw,
+ bottom: xy[1] + nh,
+ left: xy[0]
+ };
+ },
+ /**
+ * @private
+ * @method _createShim
+ * @description Creates the Target shim and adds it to the DDM's playground..
+ */
+ _createShim: function() {
+ //No playground, defer
+ if (!DDM._pg) {
+ Y.later(10, this, this._createShim);
+ return;
+ }
+ //Shim already here, cancel
+ if (this.shim) {
+ return;
+ }
+ var s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
+
+ s.setStyles({
+ height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
+ width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
+ backgroundColor: 'yellow',
+ opacity: '.5',
+ zIndex: '1',
+ overflow: 'hidden',
+ top: '-900px',
+ left: '-900px',
+ position: 'absolute'
+ });
+ DDM._pg.appendChild(s);
+ this.shim = s;
+
+ s.on('mouseover', Y.bind(this._handleOverEvent, this));
+ s.on('mouseout', Y.bind(this._handleOutEvent, this));
+ },
+ /**
+ * @private
+ * @method _handleOverTarget
+ * @description This handles the over target call made from this object or from the DDM
+ */
+ _handleTargetOver: function() {
+ if (DDM.isOverTarget(this)) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrop = this;
+ DDM.otherDrops[this] = this;
+ if (this.overTarget) {
+ DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
+ this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
+ } else {
+ this.overTarget = true;
+ this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
+ //TODO - Is this needed??
+ //DDM._handleTargetOver();
+ }
+ } else {
+ this._handleOut();
+ }
+ },
+ /**
+ * @private
+ * @method _handleOverEvent
+ * @description Handles the mouseover DOM event on the Target Shim
+ */
+ _handleOverEvent: function() {
+ this.shim.setStyle('zIndex', '999');
+ DDM._addActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOutEvent
+ * @description Handles the mouseout DOM event on the Target Shim
+ */
+ _handleOutEvent: function() {
+ this.shim.setStyle('zIndex', '1');
+ DDM._removeActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOut
+ * @description Handles out of target calls/checks
+ */
+ _handleOut: function(force) {
+ if (!DDM.isOverTarget(this) || force) {
+ if (this.overTarget) {
+ this.overTarget = false;
+ if (!force) {
+ DDM._removeActiveShim(this);
+ }
+ if (DDM.activeDrag) {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
+ this.fire(EV_DROP_EXIT);
+ DDM.activeDrag.fire('drag:exit', { drop: this });
+ delete DDM.otherDrops[this];
+ //if (DDM.activeDrop === this) {
+ // DDM.activeDrop = null;
+ //}
+ }
+ }
+ }
+ }
+ });
+
+ Y.DD.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-ddm-base",function(B){var A=function(){A.superclass.constructor.apply(this,arguments);};A.NAME="ddm";A.ATTRS={dragCursor:{value:"move"},clickPixelThresh:{value:3},clickTimeThresh:{value:1000},dragMode:{value:"point",setter:function(C){this._setDragMode(C);return C;}}};B.extend(A,B.Base,{_active:null,_setDragMode:function(C){if(C===null){C=B.DD.DDM.get("dragMode");}switch(C){case 1:case"intersect":return 1;case 2:case"strict":return 2;case 0:case"point":return 0;}return 0;},CSS_PREFIX:"yui-dd",_activateTargets:function(){},_drags:[],activeDrag:false,_regDrag:function(C){if(this.getDrag(C.get("node"))){return false;}if(!this._active){this._setupListeners();}this._drags.push(C);return true;},_unregDrag:function(D){var C=[];B.each(this._drags,function(F,E){if(F!==D){C[C.length]=F;}});this._drags=C;},_setupListeners:function(){this._active=true;var C=B.get(document);C.on("mousemove",B.bind(this._move,this));C.on("mouseup",B.bind(this._end,this));},_start:function(){this.fire("ddm:start");this._startDrag();},_startDrag:function(){},_endDrag:function(){},_dropMove:function(){},_end:function(){if(this.activeDrag){this._endDrag();this.fire("ddm:end");this.activeDrag.end.call(this.activeDrag);this.activeDrag=null;}},stopDrag:function(){if(this.activeDrag){this._end();}return this;},_move:function(C){if(this.activeDrag){this.activeDrag._move.call(this.activeDrag,C);this._dropMove();}},cssSizestoObject:function(D){var C=D.split(" ");switch(C.length){case 1:C[1]=C[2]=C[3]=C[0];break;case 2:C[2]=C[0];C[3]=C[1];break;case 3:C[3]=C[1];break;}return{top:parseInt(C[0],10),right:parseInt(C[1],10),bottom:parseInt(C[2],10),left:parseInt(C[3],10)};},getDrag:function(D){var C=false,E=B.get(D);if(E instanceof B.Node){B.each(this._drags,function(G,F){if(E.compareTo(G.get("node"))){C=G;}});}return C;}});B.namespace("DD");B.DD.DDM=new A();},"3.0.0",{requires:["node","base"],skinnable:false});YUI.add("dd-ddm",function(A){A.mix(A.DD.DDM,{_pg:null,_debugShim:false,_activateTargets:function(){},_deactivateTargets:function(){},_startDrag:function(){if(this.activeDrag.get("useShim")){this._pg_activate();this._activateTargets();}},_endDrag:function(){this._pg_deactivate();this._deactivateTargets();},_pg_deactivate:function(){this._pg.setStyle("display","none");},_pg_activate:function(){var B=this.activeDrag.get("activeHandle"),C="auto";if(B){C=B.getStyle("cursor");}if(C=="auto"){C=this.get("dragCursor");}this._pg_size();this._pg.setStyles({top:0,left:0,display:"block",opacity:((this._debugShim)?".5":"0"),cursor:C});},_pg_size:function(){if(this.activeDrag){var B=A.get("body"),D=B.get("docHeight"),C=B.get("docWidth");this._pg.setStyles({height:D+"px",width:C+"px"});}},_createPG:function(){var D=A.Node.create("<div></div>"),B=A.get("body");D.setStyles({top:"0",left:"0",position:"absolute",zIndex:"9999",overflow:"hidden",backgroundColor:"red",display:"none",height:"5px",width:"5px"});D.set("id",A.stamp(D));D.addClass("yui-dd-shim");if(B.get("firstChild")){B.insertBefore(D,B.get("firstChild"));}else{B.appendChild(D);}this._pg=D;this._pg.on("mouseup",A.bind(this._end,this));this._pg.on("mousemove",A.bind(this._move,this));var C=A.get(window);A.on("window:resize",A.bind(this._pg_size,this));C.on("scroll",A.bind(this._pg_size,this));}},true);A.on("domready",A.bind(A.DD.DDM._createPG,A.DD.DDM));},"3.0.0",{requires:["dd-ddm-base","event-resize"],skinnable:false});YUI.add("dd-ddm-drop",function(A){A.mix(A.DD.DDM,{_noShim:false,_activeShims:[],_hasActiveShim:function(){if(this._noShim){return true;}return this._activeShims.length;},_addActiveShim:function(B){this._activeShims[this._activeShims.length]=B;},_removeActiveShim:function(C){var B=[];A.each(this._activeShims,function(E,D){if(E._yuid!==C._yuid){B[B.length]=E;}});this._activeShims=B;},syncActiveShims:function(B){A.later(0,this,function(C){var D=((C)?this.targets:this._lookup());A.each(D,function(F,E){F.sizeShim.call(F);},this);},B);},mode:0,POINT:0,INTERSECT:1,STRICT:2,useHash:true,activeDrop:null,validDrops:[],otherDrops:{},targets:[],_addValid:function(B){this.validDrops[this.validDrops.length]=B;return this;},_removeValid:function(B){var C=[];A.each(this.validDrops,function(E,D){if(E!==B){C[C.length]=E;}});this.validDrops=C;return this;},isOverTarget:function(C){if(this.activeDrag&&C){var F=this.activeDrag.mouseXY,E,B=this.activeDrag.get("dragMode"),D;if(F&&this.activeDrag){D=this.activeDrag.region;if(B==this.STRICT){return this.activeDrag.get("dragNode").inRegion(C.region,true,D);}else{if(C&&C.shim){if((B==this.INTERSECT)&&this._noShim){E=((D)?D:this.activeDrag.get("node"));return C.get("node").intersect(E).inRegion;}else{return C.shim.intersect({top:F[1],bottom:F[1],left:F[0],right:F[0]},C.region).inRegion;}}else{return false;}}}else{return false;}}else{return false;}},clearCache:function(){this.validDrops=[];this.otherDrops={};this._activeShims=[];},_activateTargets:function(){this.clearCache();A.each(this.targets,function(C,B){C._activateShim.apply(C,[]);},this);this._handleTargetOver();},getBestMatch:function(F,D){var C=null,E=0,B;A.each(F,function(I,H){var G=this.activeDrag.get("dragNode").intersect(I.get("node"));I.region.area=G.area;if(G.inRegion){if(G.area>E){E=G.area;C=I;}}},this);if(D){B=[];A.each(F,function(H,G){if(H!==C){B[B.length]=H;}},this);return[C,B];}else{return C;}},_deactivateTargets:function(){var B=[],C,E=this.activeDrag,D=this.activeDrop;if(E&&D&&this.otherDrops[D]){if(!E.get("dragMode")){B=this.otherDrops;delete B[D];}else{C=this.getBestMatch(this.otherDrops,true);D=C[0];B=C[1];}E.get("node").removeClass(this.CSS_PREFIX+"-drag-over");if(D){D.fire("drop:hit",{drag:E,drop:D,others:B});E.fire("drag:drophit",{drag:E,drop:D,others:B});}}else{if(E){E.get("node").removeClass(this.CSS_PREFIX+"-drag-over");E.fire("drag:dropmiss",{pageX:E.lastXY[0],pageY:E.lastXY[1]});}else{}}this.activeDrop=null;A.each(this.targets,function(G,F){G._deactivateShim.apply(G,[]);},this);},_dropMove:function(){if(this._hasActiveShim()){this._handleTargetOver();
+}else{A.each(this.otherDrops,function(C,B){C._handleOut.apply(C,[]);});}},_lookup:function(){if(!this.useHash||this._noShim){return this.validDrops;}var B=[];A.each(this.validDrops,function(D,C){if(D.shim&&D.shim.inViewportRegion(false,D.region)){B[B.length]=D;}});return B;},_handleTargetOver:function(){var B=this._lookup();A.each(B,function(D,C){D._handleTargetOver.call(D);},this);},_regTarget:function(B){this.targets[this.targets.length]=B;},_unregTarget:function(C){var B=[],D;A.each(this.targets,function(F,E){if(F!=C){B[B.length]=F;}},this);this.targets=B;D=[];A.each(this.validDrops,function(F,E){if(F!==C){D[D.length]=F;}});this.validDrops=D;},getDrop:function(C){var B=false,D=A.Node.get(C);if(D instanceof A.Node){A.each(this.targets,function(F,E){if(D.compareTo(F.get("node"))){B=F;}});}return B;}},true);},"3.0.0",{requires:["dd-ddm"],skinnable:false});YUI.add("dd-drag",function(D){var E=D.DD.DDM,U="node",G="dragging",N="dragNode",C="offsetHeight",K="offsetWidth",S="mouseup",P="mousedown",M="dragstart",H="drag:mouseDown",B="drag:afterMouseDown",F="drag:removeHandle",L="drag:addHandle",R="drag:removeInvalid",T="drag:addInvalid",J="drag:start",I="drag:end",O="drag:drag",Q="drag:align",A=function(W){this._lazyAddAttrs=false;A.superclass.constructor.apply(this,arguments);var V=E._regDrag(this);if(!V){D.error("Failed to register node, already in use: "+W.node);}};A.NAME="drag";A.ATTRS={node:{setter:function(V){var W=D.get(V);if(!W){D.error("DD.Drag: Invalid Node Given: "+V);}else{W=W.item(0);}return W;}},dragNode:{setter:function(V){var W=D.Node.get(V);if(!W){D.error("DD.Drag: Invalid dragNode Given: "+V);}return W;}},offsetNode:{value:true},clickPixelThresh:{value:E.get("clickPixelThresh")},clickTimeThresh:{value:E.get("clickTimeThresh")},lock:{value:false,setter:function(V){if(V){this.get(U).addClass(E.CSS_PREFIX+"-locked");}else{this.get(U).removeClass(E.CSS_PREFIX+"-locked");}return V;}},data:{value:false},move:{value:true},useShim:{value:true},activeHandle:{value:false},primaryButtonOnly:{value:true},dragging:{value:false},parent:{value:false},target:{value:false,setter:function(V){this._handleTarget(V);return V;}},dragMode:{value:null,setter:function(V){return E._setDragMode(V);}},groups:{value:["default"],getter:function(){if(!this._groups){this._groups={};}var V=[];D.each(this._groups,function(X,W){V[V.length]=W;});return V;},setter:function(V){this._groups={};D.each(V,function(X,W){this._groups[X]=true;},this);return V;}},handles:{value:null,setter:function(V){if(V){this._handles={};D.each(V,function(X,W){this._handles[X]=true;},this);}else{this._handles=null;}return V;}},bubbles:{writeOnce:true,value:D.DD.DDM}};D.extend(A,D.Base,{addToGroup:function(V){this._groups[V]=true;E._activateTargets();return this;},removeFromGroup:function(V){delete this._groups[V];E._activateTargets();return this;},target:null,_handleTarget:function(V){if(D.DD.Drop){if(V===false){if(this.target){E._unregTarget(this.target);this.target=null;}return false;}else{if(!D.Lang.isObject(V)){V={};}V.bubbles=("bubbles" in V)?V.bubbles:this.get("bubbles");V.node=this.get(U);V.groups=V.groups||this.get("groups");this.target=new D.DD.Drop(V);}}else{return false;}},_groups:null,_createEvents:function(){this.publish(H,{defaultFn:this._defMouseDownFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});this.publish(Q,{defaultFn:this._defAlignFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});this.publish(O,{defaultFn:this._defDragFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});this.publish(I,{preventedFn:this._prevEndFn,queuable:false,emitFacade:true,bubbles:true,prefix:"drag"});var V=[B,F,L,R,T,J,"drag:drophit","drag:dropmiss","drag:over","drag:enter","drag:exit"];D.each(V,function(X,W){this.publish(X,{type:X,emitFacade:true,bubbles:true,preventable:false,queuable:false,prefix:"drag"});},this);if(this.get("bubbles")){this.addTarget(this.get("bubbles"));}},_ev_md:null,_startTime:null,_endTime:null,_handles:null,_invalids:null,_invalidsDefault:{"textarea":true,"input":true,"a":true,"button":true,"select":true},_dragThreshMet:null,_fromTimeout:null,_clickTimeout:null,deltaXY:null,startXY:null,nodeXY:null,lastXY:null,actXY:null,realXY:null,mouseXY:null,region:null,_handleMouseUp:function(V){this._fixIEMouseUp();if(E.activeDrag){E._end();}},_fixDragStart:function(V){V.preventDefault();},_ieSelectFix:function(){return false;},_ieSelectBack:null,_fixIEMouseDown:function(){if(D.UA.ie){this._ieSelectBack=D.config.doc.body.onselectstart;D.config.doc.body.onselectstart=this._ieSelectFix;}},_fixIEMouseUp:function(){if(D.UA.ie){D.config.doc.body.onselectstart=this._ieSelectBack;}},_handleMouseDownEvent:function(V){this.fire(H,{ev:V});},_defMouseDownFn:function(W){var V=W.ev;this._dragThreshMet=false;this._ev_md=V;if(this.get("primaryButtonOnly")&&V.button>1){return false;}if(this.validClick(V)){this._fixIEMouseDown();V.halt();this._setStartPosition([V.pageX,V.pageY]);E.activeDrag=this;this._clickTimeout=D.later(this.get("clickTimeThresh"),this,this._timeoutCheck);}this.fire(B,{ev:V});},validClick:function(Z){var Y=false,b=false,V=Z.target,X=null,W=null,a=false;if(this._handles){D.each(this._handles,function(c,d){if(D.Lang.isString(d)){if(V.test(d+", "+d+" *")&&!X){X=d;Y=true;}}});}else{b=this.get(U);if(b.contains(V)||b.compareTo(V)){Y=true;}}if(Y){if(this._invalids){D.each(this._invalids,function(c,d){if(D.Lang.isString(d)){if(V.test(d+", "+d+" *")){Y=false;}}});}}if(Y){if(X){W=Z.currentTarget.queryAll(X);a=false;W.each(function(d,c){if((d.contains(V)||d.compareTo(V))&&!a){a=true;this.set("activeHandle",d);}},this);}else{this.set("activeHandle",this.get(U));}}return Y;},_setStartPosition:function(V){this.startXY=V;this.nodeXY=this.lastXY=this.realXY=this.get(U).getXY();if(this.get("offsetNode")){this.deltaXY=[(this.startXY[0]-this.nodeXY[0]),(this.startXY[1]-this.nodeXY[1])];}else{this.deltaXY=[0,0];}},_timeoutCheck:function(){if(!this.get("lock")&&!this._dragThreshMet){this._fromTimeout=this._dragThreshMet=true;
+this.start();this._alignNode([this._ev_md.pageX,this._ev_md.pageY],true);}},removeHandle:function(V){if(this._handles[V]){delete this._handles[V];this.fire(F,{handle:V});}return this;},addHandle:function(V){if(!this._handles){this._handles={};}if(D.Lang.isString(V)){this._handles[V]=true;this.fire(L,{handle:V});}return this;},removeInvalid:function(V){if(this._invalids[V]){this._invalids[V]=null;delete this._invalids[V];this.fire(R,{handle:V});}return this;},addInvalid:function(V){if(D.Lang.isString(V)){this._invalids[V]=true;this.fire(T,{handle:V});}return this;},initializer:function(){this.get(U).dd=this;if(!this.get(U).get("id")){var V=D.stamp(this.get(U));this.get(U).set("id",V);}this.actXY=[];this._invalids=D.clone(this._invalidsDefault,true);this._createEvents();if(!this.get(N)){this.set(N,this.get(U));}this.on("initializedChange",D.bind(this._prep,this));this.set("groups",this.get("groups"));},_prep:function(){this._dragThreshMet=false;var V=this.get(U);V.addClass(E.CSS_PREFIX+"-draggable");V.on(P,D.bind(this._handleMouseDownEvent,this));V.on(S,D.bind(this._handleMouseUp,this));V.on(M,D.bind(this._fixDragStart,this));},_unprep:function(){var V=this.get(U);V.removeClass(E.CSS_PREFIX+"-draggable");V.detachAll();},start:function(){if(!this.get("lock")&&!this.get(G)){var W=this.get(U),V=W.get(K),X=W.get(C);this._startTime=(new Date()).getTime();E._start();W.addClass(E.CSS_PREFIX+"-dragging");this.fire(J,{pageX:this.nodeXY[0],pageY:this.nodeXY[1],startTime:this._startTime});var Y=this.nodeXY;this.region={"0":Y[0],"1":Y[1],area:0,top:Y[1],right:Y[0]+V,bottom:Y[1]+X,left:Y[0]};this.set(G,true);}return this;},end:function(){this._endTime=(new Date()).getTime();if(this._clickTimeout){this._clickTimeout.cancel();}this._dragThreshMet=false;this._fromTimeout=false;if(!this.get("lock")&&this.get(G)){this.fire(I,{pageX:this.lastXY[0],pageY:this.lastXY[1],startTime:this._startTime,endTime:this._endTime});}this.get(U).removeClass(E.CSS_PREFIX+"-dragging");this.set(G,false);this.deltaXY=[0,0];return this;},_prevEndFn:function(V){this.get(N).setXY(this.nodeXY);},_align:function(V){this.fire(Q,{pageX:V[0],pageY:V[1]});},_defAlignFn:function(V){this.actXY=[V.pageX-this.deltaXY[0],V.pageY-this.deltaXY[1]];},_alignNode:function(V){this._align(V);this._moveNode();},_moveNode:function(V){var W=[],X=[],Z=this.nodeXY,Y=this.actXY;W[0]=(Y[0]-this.lastXY[0]);W[1]=(Y[1]-this.lastXY[1]);X[0]=(Y[0]-this.nodeXY[0]);X[1]=(Y[1]-this.nodeXY[1]);this.region={"0":Y[0],"1":Y[1],area:0,top:Y[1],right:Y[0]+this.get(N).get(K),bottom:Y[1]+this.get(N).get(C),left:Y[0]};this.fire(O,{pageX:Y[0],pageY:Y[1],scroll:V,info:{start:Z,xy:Y,delta:W,offset:X}});this.lastXY=Y;},_defDragFn:function(V){if(this.get("move")){if(V.scroll){V.scroll.node.set("scrollTop",V.scroll.top);V.scroll.node.set("scrollLeft",V.scroll.left);}this.get(N).setXY([V.pageX,V.pageY]);this.realXY=[V.pageX,V.pageY];}},_move:function(X){if(this.get("lock")){return false;}else{this.mouseXY=[X.pageX,X.pageY];if(!this._dragThreshMet){var W=Math.abs(this.startXY[0]-X.pageX),V=Math.abs(this.startXY[1]-X.pageY);if(W>this.get("clickPixelThresh")||V>this.get("clickPixelThresh")){this._dragThreshMet=true;this.start();this._alignNode([X.pageX,X.pageY]);}}else{if(this._clickTimeout){this._clickTimeout.cancel();}this._alignNode([X.pageX,X.pageY]);}}},stopDrag:function(){if(this.get(G)){E._end();}return this;},destructor:function(){this._unprep();this.detachAll();if(this.target){this.target.destroy();}E._unregDrag(this);}});D.namespace("DD");D.DD.Drag=A;},"3.0.0",{requires:["dd-ddm-base"],skinnable:false});YUI.add("dd-proxy",function(H){var F=H.DD.DDM,B="node",C="dragNode",A="host",D=true;var G=function(I){G.superclass.constructor.apply(this,arguments);};G.NAME="DDProxy";G.NS="proxy";G.ATTRS={host:{},moveOnEnd:{value:D},hideOnEnd:{value:D},resizeFrame:{value:D},positionProxy:{value:D},borderStyle:{value:"1px solid #808080"}};var E={_hands:null,_init:function(){if(!F._proxy){H.on("domready",H.bind(this._init,this));return;}if(!this._hands){this._hands=[];}var K,L,J,M=this.get(A),I=M.get(C);if(I.compareTo(M.get(B))){if(F._proxy){M.set(C,F._proxy);}}H.each(this._hands,function(N){N.detach();});L=F.on("ddm:start",H.bind(function(){if(F.activeDrag===M){F._setFrame(M);}},this));J=F.on("ddm:end",H.bind(function(){if(M.get("dragging")){if(this.get("moveOnEnd")){M.get(B).setXY(M.lastXY);}if(this.get("hideOnEnd")){M.get(C).setStyle("display","none");}}},this));this._hands=[L,J];},initializer:function(){this._init();},destructor:function(){var I=this.get(A);H.each(this._hands,function(J){J.detach();});I.set(C,I.get(B));}};H.namespace("Plugin");H.extend(G,H.Base,E);H.Plugin.DDProxy=G;H.mix(F,{_createFrame:function(){if(!F._proxy){F._proxy=D;var J=H.Node.create("<div></div>"),I=H.Node.get("body");J.setStyles({position:"absolute",display:"none",zIndex:"999",top:"-999px",left:"-999px"});I.insertBefore(J,I.get("firstChild"));J.set("id",H.stamp(J));J.addClass(F.CSS_PREFIX+"-proxy");F._proxy=J;}},_setFrame:function(J){var M=J.get(B),L=J.get(C),I,K="auto";if(J.proxy.get("resizeFrame")){F._proxy.setStyles({height:M.get("offsetHeight")+"px",width:M.get("offsetWidth")+"px"});}I=F.activeDrag.get("activeHandle");if(I){K=I.getStyle("cursor");}if(K=="auto"){K=F.get("dragCursor");}L.setStyles({visibility:"hidden",display:"block",cursor:K,border:J.proxy.get("borderStyle")});if(J.proxy.get("positionProxy")){L.setXY(J.nodeXY);}L.setStyle("visibility","visible");}});H.on("domready",H.bind(F._createFrame,F));},"3.0.0",{requires:["dd-ddm","dd-drag"],skinnable:false});YUI.add("dd-constrain",function(B){var K="dragNode",M="offsetHeight",F="offsetWidth",Q="host",P="constrain2region",H="constrain2node",G="tickXArray",O="tickYArray",N=B.DD.DDM,E="top",J="right",L="bottom",D="left",I=null;var A=function(C){A.superclass.constructor.apply(this,arguments);};A.NAME="DragConstrained";A.NS="con";A.ATTRS={host:{},stickX:{value:false},stickY:{value:false},tickX:{value:false},tickY:{value:false},tickXArray:{value:false},tickYArray:{value:false},constrain2region:{value:false,getter:function(C){if(B.Lang.isObject(C)){var R={};
+B.mix(R,C);return R;}else{return false;}},setter:function(C){if(B.Lang.isObject(C)){if(B.Lang.isNumber(C[E])&&B.Lang.isNumber(C[J])&&B.Lang.isNumber(C[D])&&B.Lang.isNumber(C[L])){var R={};B.mix(R,C);return R;}else{return false;}}else{if(C!==false){return false;}}return C;}},gutter:{value:"0",setter:function(C){return B.DD.DDM.cssSizestoObject(C);}},constrain2node:{value:false,setter:function(R){if(!this.get(P)){var C=B.Node.get(R);if(C){return C;}}else{if(this.get(P)!==false){}}return false;}},constrain2view:{value:false}};I={initializer:function(){this.get(Q).on("drag:start",B.bind(this._handleStart,this));this.get(Q).after("drag:align",B.bind(this.align,this));},_handleStart:function(){this._regionCache=null;},_regionCache:null,_cacheRegion:function(){this._regionCache=this.get(H).get("region");},getRegion:function(V){var T={},U=null,C=null,S=this.get("gutter"),R=this.get(Q);if(this.get(H)){if(!this._regionCache){B.on("resize",B.bind(this._cacheRegion,this),window);this._cacheRegion();}T=B.clone(this._regionCache);}else{if(this.get(P)){T=this.get(P);}else{if(this.get("constrain2view")){T=R.get(K).get("viewportRegion");}else{return false;}}}B.each(S,function(W,X){if((X==J)||(X==L)){T[X]-=W;}else{T[X]+=W;}});if(V){U=R.get(K).get(M);C=R.get(K).get(F);T[J]=T[J]-C;T[L]=T[L]-U;}return T;},_checkRegion:function(C){var S=C,U=this.getRegion(),T=this.get(Q),V=T.get(K).get(M),R=T.get(K).get(F);if(S[1]>(U[L]-V)){C[1]=(U[L]-V);}if(U[E]>S[1]){C[1]=U[E];}if(S[0]>(U[J]-R)){C[0]=(U[J]-R);}if(U[D]>S[0]){C[0]=U[D];}return C;},inRegion:function(S){S=S||this.get(Q).get(K).getXY();var R=this._checkRegion([S[0],S[1]]),C=false;if((S[0]===R[0])&&(S[1]===R[1])){C=true;}return C;},align:function(){var S=this.get(Q),C=S.actXY,R=this.getRegion(true);if(this.get("stickX")){C[1]=(S.startXY[1]-S.deltaXY[1]);}if(this.get("stickY")){C[0]=(S.startXY[0]-S.deltaXY[0]);}if(R){C=this._checkRegion(C);}C=this._checkTicks(C,R);S.actXY=C;},_checkTicks:function(W,U){var T=this.get(Q),V=(T.startXY[0]-T.deltaXY[0]),S=(T.startXY[1]-T.deltaXY[1]),C=this.get("tickX"),R=this.get("tickY");if(C&&!this.get(G)){W[0]=N._calcTicks(W[0],V,C,U[D],U[J]);}if(R&&!this.get(O)){W[1]=N._calcTicks(W[1],S,R,U[E],U[L]);}if(this.get(G)){W[0]=N._calcTickArray(W[0],this.get(G),U[D],U[J]);}if(this.get(O)){W[1]=N._calcTickArray(W[1],this.get(O),U[E],U[L]);}return W;}};B.namespace("Plugin");B.extend(A,B.Base,I);B.Plugin.DDConstrained=A;B.mix(N,{_calcTicks:function(X,W,T,V,U){var R=((X-W)/T),S=Math.floor(R),C=Math.ceil(R);if((S!==0)||(C!==0)){if((R>=S)&&(R<=C)){X=(W+(T*S));if(V&&U){if(X<V){X=(W+(T*(S+1)));}if(X>U){X=(W+(T*(S-1)));}}}}return X;},_calcTickArray:function(Y,Z,X,U){var R=0,V=Z.length,T=0,S,C,W;if(!Z||(Z.length===0)){return Y;}else{if(Z[0]>=Y){return Z[0];}else{for(R=0;R<V;R++){T=(R+1);if(Z[T]&&Z[T]>=Y){S=Y-Z[R];C=Z[T]-Y;W=(C>S)?Z[R]:Z[T];if(X&&U){if(W>U){if(Z[R]){W=Z[R];}else{W=Z[V-1];}}}return W;}}return Z[Z.length-1];}}}});},"3.0.0",{requires:["dd-drag"],skinnable:false});YUI.add("dd-scroll",function(C){var H=function(){H.superclass.constructor.apply(this,arguments);},L="host",A="buffer",J="parentScroll",G="windowScroll",I="scrollTop",F="scrollLeft",E="offsetWidth",K="offsetHeight";H.ATTRS={parentScroll:{value:false,setter:function(M){if(M){return M;}return false;}},buffer:{value:30},scrollDelay:{value:235},host:{value:null},windowScroll:{value:false},vertical:{value:true},horizontal:{value:true}};C.extend(H,C.Base,{_scrolling:null,_vpRegionCache:null,_dimCache:null,_scrollTimer:null,_getVPRegion:function(){var M={};var N=this.get(J),R=this.get(A),Q=this.get(G),U=((Q)?[]:N.getXY()),S=((Q)?"winWidth":E),P=((Q)?"winHeight":K),T=((Q)?N.get(I):U[1]),O=((Q)?N.get(F):U[0]);M={top:T+R,right:(N.get(S)+O)-R,bottom:(N.get(P)+T)-R,left:O+R};this._vpRegionCache=M;return M;},initializer:function(){var M=this.get(L);M.after("drag:start",C.bind(this.start,this));M.after("drag:end",C.bind(this.end,this));M.on("drag:align",C.bind(this.align,this));C.get(window).on("scroll",C.bind(function(){this._vpRegionCache=null;},this));},_checkWinScroll:function(Y){var X=this._getVPRegion(),M=this.get(L),O=this.get(G),S=M.lastXY,N=false,e=this.get(A),R=this.get(J),g=R.get(I),U=R.get(F),V=this._dimCache.w,a=this._dimCache.h,T=S[1]+a,W=S[1],d=S[0]+V,Q=S[0],f=W,P=Q,Z=g,c=U;if(this.get("horizontal")){if(Q<=X.left){N=true;P=S[0]-((O)?e:0);c=U-e;}if(d>=X.right){N=true;P=S[0]+((O)?e:0);c=U+e;}}if(this.get("vertical")){if(T>=X.bottom){N=true;f=S[1]+((O)?e:0);Z=g+e;}if(W<=X.top){N=true;f=S[1]-((O)?e:0);Z=g-e;}}if(Z<0){Z=0;f=S[1];}if(c<0){c=0;P=S[0];}if(f<0){f=S[1];}if(P<0){P=S[0];}if(Y){M.actXY=[P,f];M._moveNode({node:R,top:Z,left:c});if(!Z&&!c){this._cancelScroll();}}else{if(N){this._initScroll();}else{this._cancelScroll();}}},_initScroll:function(){this._cancelScroll();this._scrollTimer=C.Lang.later(this.get("scrollDelay"),this,this._checkWinScroll,[true],true);},_cancelScroll:function(){this._scrolling=false;if(this._scrollTimer){this._scrollTimer.cancel();delete this._scrollTimer;}},align:function(M){if(this._scrolling){this._cancelScroll();M.preventDefault();}if(!this._scrolling){this._checkWinScroll();}},_setDimCache:function(){var M=this.get(L).get("dragNode");this._dimCache={h:M.get(K),w:M.get(E)};},start:function(){this._setDimCache();},end:function(M){this._dimCache=null;this._cancelScroll();},toString:function(){return H.NAME+" #"+this.get("node").get("id");}});C.namespace("Plugin");var B=function(){B.superclass.constructor.apply(this,arguments);};B.ATTRS=C.merge(H.ATTRS,{windowScroll:{value:true,setter:function(M){if(M){this.set(J,C.get(window));}return M;}}});C.extend(B,H,{initializer:function(){this.set("windowScroll",this.get("windowScroll"));}});B.NAME=B.NS="winscroll";C.Plugin.DDWinScroll=B;var D=function(){D.superclass.constructor.apply(this,arguments);};D.ATTRS=C.merge(H.ATTRS,{node:{value:false,setter:function(M){var N=C.get(M);if(!N){if(M!==false){C.error("DDNodeScroll: Invalid Node Given: "+M);}}else{N=N.item(0);this.set(J,N);}return N;}}});C.extend(D,H,{initializer:function(){this.set("node",this.get("node"));
+}});D.NAME=D.NS="nodescroll";C.Plugin.DDNodeScroll=D;C.DD.Scroll=H;},"3.0.0",{skinnable:false,requires:["dd-drag"],optional:["dd-proxy"]});YUI.add("dd-plugin",function(B){var A=function(C){C.node=((B.Widget&&C.host instanceof B.Widget)?C.host.get("boundingBox"):C.host);A.superclass.constructor.apply(this,arguments);};A.NAME="dd-plugin";A.NS="dd";B.extend(A,B.DD.Drag);B.namespace("Plugin");B.Plugin.Drag=A;},"3.0.0",{skinnable:false,requires:["dd-drag"],optional:["dd-constrain","dd-proxy"]});YUI.add("dd-drop",function(A){var B="node",G=A.DD.DDM,F="offsetHeight",C="offsetWidth",I="drop:over",H="drop:enter",D="drop:exit",E=function(){this._lazyAddAttrs=false;E.superclass.constructor.apply(this,arguments);A.on("domready",A.bind(function(){A.later(100,this,this._createShim);},this));G._regTarget(this);};E.NAME="drop";E.ATTRS={node:{setter:function(J){var K=A.Node.get(J);if(!K){A.error("DD.Drop: Invalid Node Given: "+J);}return K;}},groups:{value:["default"],setter:function(J){this._groups={};A.each(J,function(L,K){this._groups[L]=true;},this);return J;}},padding:{value:"0",setter:function(J){return G.cssSizestoObject(J);}},lock:{value:false,setter:function(J){if(J){this.get(B).addClass(G.CSS_PREFIX+"-drop-locked");}else{this.get(B).removeClass(G.CSS_PREFIX+"-drop-locked");}return J;}},bubbles:{writeOnce:true,value:A.DD.DDM}};A.extend(E,A.Base,{_createEvents:function(){var J=[I,H,D,"drop:hit"];A.each(J,function(L,K){this.publish(L,{type:L,emitFacade:true,preventable:false,bubbles:true,queuable:false,prefix:"drop"});},this);if(this.get("bubbles")){this.addTarget(this.get("bubbles"));}},_valid:null,_groups:null,shim:null,region:null,overTarget:null,inGroup:function(J){this._valid=false;var K=false;A.each(J,function(M,L){if(this._groups[M]){K=true;this._valid=true;}},this);return K;},initializer:function(){A.later(100,this,this._createEvents);var J=this.get(B),K;if(!J.get("id")){K=A.stamp(J);J.set("id",K);}J.addClass(G.CSS_PREFIX+"-drop");this.set("groups",this.get("groups"));},destructor:function(){G._unregTarget(this);if(this.shim){this.shim.detachAll();this.shim.get("parentNode").removeChild(this.shim);this.shim=null;}this.get(B).removeClass(G.CSS_PREFIX+"-drop");this.detachAll();},_deactivateShim:function(){if(!this.shim){return false;}this.get(B).removeClass(G.CSS_PREFIX+"-drop-active-valid");this.get(B).removeClass(G.CSS_PREFIX+"-drop-active-invalid");this.get(B).removeClass(G.CSS_PREFIX+"-drop-over");this.shim.setStyles({top:"-999px",left:"-999px",zIndex:"1"});this.overTarget=false;},_activateShim:function(){if(!G.activeDrag){return false;}if(this.get(B)===G.activeDrag.get(B)){return false;}if(this.get("lock")){return false;}var J=this.get(B);if(this.inGroup(G.activeDrag.get("groups"))){J.removeClass(G.CSS_PREFIX+"-drop-active-invalid");J.addClass(G.CSS_PREFIX+"-drop-active-valid");G._addValid(this);this.overTarget=false;this.sizeShim();}else{G._removeValid(this);J.removeClass(G.CSS_PREFIX+"-drop-active-valid");J.addClass(G.CSS_PREFIX+"-drop-active-invalid");}},sizeShim:function(){if(!G.activeDrag){return false;}if(this.get(B)===G.activeDrag.get(B)){return false;}if(this.get("lock")){return false;}if(!this.shim){A.later(100,this,this.sizeShim);return false;}var O=this.get(B),M=O.get(F),K=O.get(C),Q=O.getXY(),P=this.get("padding"),J,N,L;K=K+P.left+P.right;M=M+P.top+P.bottom;Q[0]=Q[0]-P.left;Q[1]=Q[1]-P.top;if(G.activeDrag.get("dragMode")===G.INTERSECT){J=G.activeDrag;N=J.get(B).get(F);L=J.get(B).get(C);M=(M+N);K=(K+L);Q[0]=Q[0]-(L-J.deltaXY[0]);Q[1]=Q[1]-(N-J.deltaXY[1]);}this.shim.setStyles({height:M+"px",width:K+"px",top:Q[1]+"px",left:Q[0]+"px"});this.region={"0":Q[0],"1":Q[1],area:0,top:Q[1],right:Q[0]+K,bottom:Q[1]+M,left:Q[0]};},_createShim:function(){if(!G._pg){A.later(10,this,this._createShim);return;}if(this.shim){return;}var J=A.Node.create('<div id="'+this.get(B).get("id")+'_shim"></div>');J.setStyles({height:this.get(B).get(F)+"px",width:this.get(B).get(C)+"px",backgroundColor:"yellow",opacity:".5",zIndex:"1",overflow:"hidden",top:"-900px",left:"-900px",position:"absolute"});G._pg.appendChild(J);this.shim=J;J.on("mouseover",A.bind(this._handleOverEvent,this));J.on("mouseout",A.bind(this._handleOutEvent,this));},_handleTargetOver:function(){if(G.isOverTarget(this)){this.get(B).addClass(G.CSS_PREFIX+"-drop-over");G.activeDrop=this;G.otherDrops[this]=this;if(this.overTarget){G.activeDrag.fire("drag:over",{drop:this,drag:G.activeDrag});this.fire(I,{drop:this,drag:G.activeDrag});}else{this.overTarget=true;this.fire(H,{drop:this,drag:G.activeDrag});G.activeDrag.fire("drag:enter",{drop:this,drag:G.activeDrag});G.activeDrag.get(B).addClass(G.CSS_PREFIX+"-drag-over");}}else{this._handleOut();}},_handleOverEvent:function(){this.shim.setStyle("zIndex","999");G._addActiveShim(this);},_handleOutEvent:function(){this.shim.setStyle("zIndex","1");G._removeActiveShim(this);},_handleOut:function(J){if(!G.isOverTarget(this)||J){if(this.overTarget){this.overTarget=false;if(!J){G._removeActiveShim(this);}if(G.activeDrag){this.get(B).removeClass(G.CSS_PREFIX+"-drop-over");G.activeDrag.get(B).removeClass(G.CSS_PREFIX+"-drag-over");this.fire(D);G.activeDrag.fire("drag:exit",{drop:this});delete G.otherDrops[this];}}}}});A.DD.Drop=E;},"3.0.0",{requires:["dd-ddm-drop","dd-drag"],skinnable:false});YUI.add("dd-drop-plugin",function(A){var B=function(C){C.node=C.host;B.superclass.constructor.apply(this,arguments);};B.NAME="dd-drop-plugin";B.NS="drop";A.extend(B,A.DD.Drop);A.namespace("Plugin");A.Plugin.Drop=B;},"3.0.0",{requires:["dd-drop"],skinnable:false});YUI.add("dd",function(A){},"3.0.0",{use:["dd-ddm-base","dd-ddm","dd-ddm-drop","dd-drag","dd-proxy","dd-constrain","dd-plugin","dd-drop","dd-drop-plugin","dd-scroll"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-plugin
+ */
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @class Drag
+ * @extends DD.Drag
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drag = function(config) {
+ config.node = ((Y.Widget && config.host instanceof Y.Widget) ? config.host.get('boundingBox') : config.host);
+ Drag.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-plugin
+ * @type {String}
+ */
+ Drag.NAME = "dd-plugin";
+
+ /**
+ * @property NS
+ * @description The Drag instance will be placed on the Node instance under the dd namespace. It can be accessed via Node.dd;
+ * @type {String}
+ */
+ Drag.NS = "dd";
+
+
+ Y.extend(Drag, Y.DD.Drag);
+ Y.namespace('Plugin');
+ Y.Plugin.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-constrain', 'dd-proxy']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-plugin",function(B){var A=function(C){C.node=((B.Widget&&C.host instanceof B.Widget)?C.host.get("boundingBox"):C.host);A.superclass.constructor.apply(this,arguments);};A.NAME="dd-plugin";A.NS="dd";B.extend(A,B.DD.Drag);B.namespace("Plugin");B.Plugin.Drag=A;},"3.0.0",{skinnable:false,requires:["dd-drag"],optional:["dd-constrain","dd-proxy"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-plugin
+ */
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @class Drag
+ * @extends DD.Drag
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drag = function(config) {
+ config.node = ((Y.Widget && config.host instanceof Y.Widget) ? config.host.get('boundingBox') : config.host);
+ Drag.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-plugin
+ * @type {String}
+ */
+ Drag.NAME = "dd-plugin";
+
+ /**
+ * @property NS
+ * @description The Drag instance will be placed on the Node instance under the dd namespace. It can be accessed via Node.dd;
+ * @type {String}
+ */
+ Drag.NS = "dd";
+
+
+ Y.extend(Drag, Y.DD.Drag);
+ Y.namespace('Plugin');
+ Y.Plugin.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-constrain', 'dd-proxy']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-proxy', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-proxy
+ */
+ /**
+ * This plugin for dd-drag is for creating a proxy drag node, instead of dragging the original node.
+ * @class DDProxy
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAG_NODE = 'dragNode',
+ HOST = 'host',
+ TRUE = true;
+
+ var P = function(config) {
+ P.superclass.constructor.apply(this, arguments);
+ };
+
+ P.NAME = 'DDProxy';
+ /**
+ * @property proxy
+ * @description The Proxy instance will be placed on the Drag instance under the proxy namespace.
+ * @type {String}
+ */
+ P.NS = 'proxy';
+
+ P.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute moveOnEnd
+ * @description Move the original node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ moveOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute hideOnEnd
+ * @description Hide the drag node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ hideOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute resizeFrame
+ * @description Make the Proxy node assume the size of the original node. Default: true
+ * @type Boolean
+ */
+ resizeFrame: {
+ value: TRUE
+ },
+ /**
+ * @attribute positionProxy
+ * @description Make the Proxy node appear in the same place as the original node. Default: true
+ * @type Boolean
+ */
+ positionProxy: {
+ value: TRUE
+ },
+ /**
+ * @attribute borderStyle
+ * @description The default border style for the border of the proxy. Default: 1px solid #808080
+ * @type Boolean
+ */
+ borderStyle: {
+ value: '1px solid #808080'
+ }
+ };
+
+ var proto = {
+ /**
+ * @private
+ * @property _hands
+ * @description Holds the event handles for setting the proxy
+ */
+ _hands: null,
+ /**
+ * @private
+ * @method _init
+ * @description Handler for the proxy config attribute
+ */
+ _init: function() {
+ if (!DDM._proxy) {
+ Y.on('domready', Y.bind(this._init, this));
+ return;
+ }
+ if (!this._hands) {
+ this._hands = [];
+ }
+ var i, h, h1, host = this.get(HOST), dnode = host.get(DRAG_NODE);
+ if (dnode.compareTo(host.get(NODE))) {
+ if (DDM._proxy) {
+ host.set(DRAG_NODE, DDM._proxy);
+ }
+ }
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ h = DDM.on('ddm:start', Y.bind(function() {
+ if (DDM.activeDrag === host) {
+ DDM._setFrame(host);
+ }
+ }, this));
+ h1 = DDM.on('ddm:end', Y.bind(function() {
+ if (host.get('dragging')) {
+ if (this.get('moveOnEnd')) {
+ host.get(NODE).setXY(host.lastXY);
+ }
+ if (this.get('hideOnEnd')) {
+ host.get(DRAG_NODE).setStyle('display', 'none');
+ }
+ }
+ }, this));
+ this._hands = [h, h1];
+ },
+ initializer: function() {
+ this._init();
+ },
+ destructor: function() {
+ var host = this.get(HOST);
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ host.set(DRAG_NODE, host.get(NODE));
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(P, Y.Base, proto);
+ Y.Plugin.DDProxy = P;
+
+ //Add a couple of methods to the DDM
+ Y.mix(DDM, {
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _createFrame
+ * @description Create the proxy element if it doesn't already exist and set the DD.DDM._proxy value
+ */
+ _createFrame: function() {
+ if (!DDM._proxy) {
+ DDM._proxy = TRUE;
+
+ var p = Y.Node.create('<div></div>'),
+ b = Y.Node.get('body');
+
+ p.setStyles({
+ position: 'absolute',
+ display: 'none',
+ zIndex: '999',
+ top: '-999px',
+ left: '-999px'
+ });
+
+ b.insertBefore(p, b.get('firstChild'));
+ p.set('id', Y.stamp(p));
+ p.addClass(DDM.CSS_PREFIX + '-proxy');
+ DDM._proxy = p;
+ }
+ },
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _setFrame
+ * @description If resizeProxy is set to true (default) it will resize the proxy element to match the size of the Drag Element.
+ * If positionProxy is set to true (default) it will position the proxy element in the same location as the Drag Element.
+ */
+ _setFrame: function(drag) {
+ var n = drag.get(NODE), d = drag.get(DRAG_NODE), ah, cur = 'auto';
+ if (drag.proxy.get('resizeFrame')) {
+ DDM._proxy.setStyles({
+ height: n.get('offsetHeight') + 'px',
+ width: n.get('offsetWidth') + 'px'
+ });
+ }
+
+ ah = DDM.activeDrag.get('activeHandle');
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = DDM.get('dragCursor');
+ }
+
+
+ d.setStyles({
+ visibility: 'hidden',
+ display: 'block',
+ cursor: cur,
+ border: drag.proxy.get('borderStyle')
+ });
+
+
+
+ if (drag.proxy.get('positionProxy')) {
+ d.setXY(drag.nodeXY);
+ }
+ d.setStyle('visibility', 'visible');
+ }
+ });
+
+ //Create the frame when DOM is ready
+ Y.on('domready', Y.bind(DDM._createFrame, DDM));
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm', 'dd-drag'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-proxy",function(H){var F=H.DD.DDM,B="node",C="dragNode",A="host",D=true;var G=function(I){G.superclass.constructor.apply(this,arguments);};G.NAME="DDProxy";G.NS="proxy";G.ATTRS={host:{},moveOnEnd:{value:D},hideOnEnd:{value:D},resizeFrame:{value:D},positionProxy:{value:D},borderStyle:{value:"1px solid #808080"}};var E={_hands:null,_init:function(){if(!F._proxy){H.on("domready",H.bind(this._init,this));return;}if(!this._hands){this._hands=[];}var K,L,J,M=this.get(A),I=M.get(C);if(I.compareTo(M.get(B))){if(F._proxy){M.set(C,F._proxy);}}H.each(this._hands,function(N){N.detach();});L=F.on("ddm:start",H.bind(function(){if(F.activeDrag===M){F._setFrame(M);}},this));J=F.on("ddm:end",H.bind(function(){if(M.get("dragging")){if(this.get("moveOnEnd")){M.get(B).setXY(M.lastXY);}if(this.get("hideOnEnd")){M.get(C).setStyle("display","none");}}},this));this._hands=[L,J];},initializer:function(){this._init();},destructor:function(){var I=this.get(A);H.each(this._hands,function(J){J.detach();});I.set(C,I.get(B));}};H.namespace("Plugin");H.extend(G,H.Base,E);H.Plugin.DDProxy=G;H.mix(F,{_createFrame:function(){if(!F._proxy){F._proxy=D;var J=H.Node.create("<div></div>"),I=H.Node.get("body");J.setStyles({position:"absolute",display:"none",zIndex:"999",top:"-999px",left:"-999px"});I.insertBefore(J,I.get("firstChild"));J.set("id",H.stamp(J));J.addClass(F.CSS_PREFIX+"-proxy");F._proxy=J;}},_setFrame:function(J){var M=J.get(B),L=J.get(C),I,K="auto";if(J.proxy.get("resizeFrame")){F._proxy.setStyles({height:M.get("offsetHeight")+"px",width:M.get("offsetWidth")+"px"});}I=F.activeDrag.get("activeHandle");if(I){K=I.getStyle("cursor");}if(K=="auto"){K=F.get("dragCursor");}L.setStyles({visibility:"hidden",display:"block",cursor:K,border:J.proxy.get("borderStyle")});if(J.proxy.get("positionProxy")){L.setXY(J.nodeXY);}L.setStyle("visibility","visible");}});H.on("domready",H.bind(F._createFrame,F));},"3.0.0",{requires:["dd-ddm","dd-drag"],skinnable:false});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-proxy', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-proxy
+ */
+ /**
+ * This plugin for dd-drag is for creating a proxy drag node, instead of dragging the original node.
+ * @class DDProxy
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAG_NODE = 'dragNode',
+ HOST = 'host',
+ TRUE = true;
+
+ var P = function(config) {
+ P.superclass.constructor.apply(this, arguments);
+ };
+
+ P.NAME = 'DDProxy';
+ /**
+ * @property proxy
+ * @description The Proxy instance will be placed on the Drag instance under the proxy namespace.
+ * @type {String}
+ */
+ P.NS = 'proxy';
+
+ P.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute moveOnEnd
+ * @description Move the original node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ moveOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute hideOnEnd
+ * @description Hide the drag node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ hideOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute resizeFrame
+ * @description Make the Proxy node assume the size of the original node. Default: true
+ * @type Boolean
+ */
+ resizeFrame: {
+ value: TRUE
+ },
+ /**
+ * @attribute positionProxy
+ * @description Make the Proxy node appear in the same place as the original node. Default: true
+ * @type Boolean
+ */
+ positionProxy: {
+ value: TRUE
+ },
+ /**
+ * @attribute borderStyle
+ * @description The default border style for the border of the proxy. Default: 1px solid #808080
+ * @type Boolean
+ */
+ borderStyle: {
+ value: '1px solid #808080'
+ }
+ };
+
+ var proto = {
+ /**
+ * @private
+ * @property _hands
+ * @description Holds the event handles for setting the proxy
+ */
+ _hands: null,
+ /**
+ * @private
+ * @method _init
+ * @description Handler for the proxy config attribute
+ */
+ _init: function() {
+ if (!DDM._proxy) {
+ Y.on('domready', Y.bind(this._init, this));
+ return;
+ }
+ if (!this._hands) {
+ this._hands = [];
+ }
+ var i, h, h1, host = this.get(HOST), dnode = host.get(DRAG_NODE);
+ if (dnode.compareTo(host.get(NODE))) {
+ if (DDM._proxy) {
+ host.set(DRAG_NODE, DDM._proxy);
+ }
+ }
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ h = DDM.on('ddm:start', Y.bind(function() {
+ if (DDM.activeDrag === host) {
+ DDM._setFrame(host);
+ }
+ }, this));
+ h1 = DDM.on('ddm:end', Y.bind(function() {
+ if (host.get('dragging')) {
+ if (this.get('moveOnEnd')) {
+ host.get(NODE).setXY(host.lastXY);
+ }
+ if (this.get('hideOnEnd')) {
+ host.get(DRAG_NODE).setStyle('display', 'none');
+ }
+ }
+ }, this));
+ this._hands = [h, h1];
+ },
+ initializer: function() {
+ this._init();
+ },
+ destructor: function() {
+ var host = this.get(HOST);
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ host.set(DRAG_NODE, host.get(NODE));
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(P, Y.Base, proto);
+ Y.Plugin.DDProxy = P;
+
+ //Add a couple of methods to the DDM
+ Y.mix(DDM, {
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _createFrame
+ * @description Create the proxy element if it doesn't already exist and set the DD.DDM._proxy value
+ */
+ _createFrame: function() {
+ if (!DDM._proxy) {
+ DDM._proxy = TRUE;
+
+ var p = Y.Node.create('<div></div>'),
+ b = Y.Node.get('body');
+
+ p.setStyles({
+ position: 'absolute',
+ display: 'none',
+ zIndex: '999',
+ top: '-999px',
+ left: '-999px'
+ });
+
+ b.insertBefore(p, b.get('firstChild'));
+ p.set('id', Y.stamp(p));
+ p.addClass(DDM.CSS_PREFIX + '-proxy');
+ DDM._proxy = p;
+ }
+ },
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _setFrame
+ * @description If resizeProxy is set to true (default) it will resize the proxy element to match the size of the Drag Element.
+ * If positionProxy is set to true (default) it will position the proxy element in the same location as the Drag Element.
+ */
+ _setFrame: function(drag) {
+ var n = drag.get(NODE), d = drag.get(DRAG_NODE), ah, cur = 'auto';
+ if (drag.proxy.get('resizeFrame')) {
+ DDM._proxy.setStyles({
+ height: n.get('offsetHeight') + 'px',
+ width: n.get('offsetWidth') + 'px'
+ });
+ }
+
+ ah = DDM.activeDrag.get('activeHandle');
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = DDM.get('dragCursor');
+ }
+
+
+ d.setStyles({
+ visibility: 'hidden',
+ display: 'block',
+ cursor: cur,
+ border: drag.proxy.get('borderStyle')
+ });
+
+
+
+ if (drag.proxy.get('positionProxy')) {
+ d.setXY(drag.nodeXY);
+ }
+ d.setStyle('visibility', 'visible');
+ }
+ });
+
+ //Create the frame when DOM is ready
+ Y.on('domready', Y.bind(DDM._createFrame, DDM));
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm', 'dd-drag'], skinnable:false});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-scroll', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-scroll
+ */
+ /**
+ * This class is the base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
+ * This class should not be called on it's own, it's designed to be a plugin.
+ * @class Scroll
+ * @extends Base
+ * @namespace DD
+ * @constructor
+ */
+
+ var S = function() {
+ S.superclass.constructor.apply(this, arguments);
+
+ },
+ HOST = 'host',
+ BUFFER = 'buffer',
+ PARENT_SCROLL = 'parentScroll',
+ WINDOW_SCROLL = 'windowScroll',
+ SCROLL_TOP = 'scrollTop',
+ SCROLL_LEFT = 'scrollLeft',
+ OFFSET_WIDTH = 'offsetWidth',
+ OFFSET_HEIGHT = 'offsetHeight';
+
+
+ S.ATTRS = {
+ /**
+ * @attribute parentScroll
+ * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
+ * @type Node
+ */
+ parentScroll: {
+ value: false,
+ setter: function(node) {
+ if (node) {
+ return node;
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute buffer
+ * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
+ * @type Number
+ */
+ buffer: {
+ value: 30
+ },
+ /**
+ * @attribute scrollDelay
+ * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
+ * @type Number
+ */
+ scrollDelay: {
+ value: 235
+ },
+ /**
+ * @attribute host
+ * @description The host we are plugged into.
+ * @type Object
+ */
+ host: {
+ value: null
+ },
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: false
+ * @type Boolean
+ */
+ windowScroll: {
+ value: false
+ },
+ /**
+ * @attribute vertical
+ * @description Allow vertical scrolling, default: true.
+ * @type Boolean
+ */
+ vertical: {
+ value: true
+ },
+ /**
+ * @attribute horizontal
+ * @description Allow horizontal scrolling, default: true.
+ * @type Boolean
+ */
+ horizontal: {
+ value: true
+ }
+ };
+
+ Y.extend(S, Y.Base, {
+ /**
+ * @private
+ * @property _scrolling
+ * @description Tells if we are actively scrolling or not.
+ * @type Boolean
+ */
+ _scrolling: null,
+ /**
+ * @private
+ * @property _vpRegionCache
+ * @description Cache of the Viewport dims.
+ * @type Object
+ */
+ _vpRegionCache: null,
+ /**
+ * @private
+ * @property _dimCache
+ * @description Cache of the dragNode dims.
+ * @type Object
+ */
+ _dimCache: null,
+ /**
+ * @private
+ * @property _scrollTimer
+ * @description Holder for the Timer object returned from Y.later.
+ * @type {Y.later}
+ */
+ _scrollTimer: null,
+ /**
+ * @private
+ * @method _getVPRegion
+ * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
+ */
+ _getVPRegion: function() {
+ var r = {};
+ //if (!this._vpRegionCache) {
+ var n = this.get(PARENT_SCROLL),
+ b = this.get(BUFFER),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ((ws) ? [] : n.getXY()),
+ w = ((ws) ? 'winWidth' : OFFSET_WIDTH),
+ h = ((ws) ? 'winHeight' : OFFSET_HEIGHT),
+ t = ((ws) ? n.get(SCROLL_TOP) : xy[1]),
+ l = ((ws) ? n.get(SCROLL_LEFT) : xy[0]);
+
+ r = {
+ top: t + b,
+ right: (n.get(w) + l) - b,
+ bottom: (n.get(h) + t) - b,
+ left: l + b
+ };
+ this._vpRegionCache = r;
+ //} else {
+ // r = this._vpRegionCache;
+ //}
+ return r;
+ },
+ initializer: function() {
+ var h = this.get(HOST);
+ h.after('drag:start', Y.bind(this.start, this));
+ h.after('drag:end', Y.bind(this.end, this));
+ h.on('drag:align', Y.bind(this.align, this));
+
+ //TODO - This doesn't work yet??
+ Y.get(window).on('scroll', Y.bind(function() {
+ this._vpRegionCache = null;
+ }, this));
+ },
+ /**
+ * @private
+ * @method _checkWinScroll
+ * @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
+ * @param {Boolean} move Should we move the window. From Y.later
+ */
+ _checkWinScroll: function(move) {
+ var r = this._getVPRegion(),
+ ho = this.get(HOST),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ho.lastXY,
+ scroll = false,
+ b = this.get(BUFFER),
+ win = this.get(PARENT_SCROLL),
+ sTop = win.get(SCROLL_TOP),
+ sLeft = win.get(SCROLL_LEFT),
+ w = this._dimCache.w,
+ h = this._dimCache.h,
+ bottom = xy[1] + h,
+ top = xy[1],
+ right = xy[0] + w,
+ left = xy[0],
+ nt = top,
+ nl = left,
+ st = sTop,
+ sl = sLeft;
+
+ if (this.get('horizontal')) {
+ if (left <= r.left) {
+ scroll = true;
+ nl = xy[0] - ((ws) ? b : 0);
+ sl = sLeft - b;
+ }
+ if (right >= r.right) {
+ scroll = true;
+ nl = xy[0] + ((ws) ? b : 0);
+ sl = sLeft + b;
+ }
+ }
+ if (this.get('vertical')) {
+ if (bottom >= r.bottom) {
+ scroll = true;
+ nt = xy[1] + ((ws) ? b : 0);
+ st = sTop + b;
+
+ }
+ if (top <= r.top) {
+ scroll = true;
+ nt = xy[1] - ((ws) ? b : 0);
+ st = sTop - b;
+ }
+ }
+
+ if (st < 0) {
+ st = 0;
+ nt = xy[1];
+ }
+
+ if (sl < 0) {
+ sl = 0;
+ nl = xy[0];
+ }
+
+ if (nt < 0) {
+ nt = xy[1];
+ }
+ if (nl < 0) {
+ nl = xy[0];
+ }
+ if (move) {
+ ho.actXY = [nl, nt];
+ ho._moveNode({ node: win, top: st, left: sl});
+ if (!st && !sl) {
+ this._cancelScroll();
+ }
+ } else {
+ if (scroll) {
+ this._initScroll();
+ } else {
+ this._cancelScroll();
+ }
+ }
+ },
+ /**
+ * @private
+ * @method _initScroll
+ * @description Cancel a previous scroll timer and init a new one.
+ */
+ _initScroll: function() {
+ this._cancelScroll();
+ this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
+
+ },
+ /**
+ * @private
+ * @method _cancelScroll
+ * @description Cancel a currently running scroll timer.
+ */
+ _cancelScroll: function() {
+ this._scrolling = false;
+ if (this._scrollTimer) {
+ this._scrollTimer.cancel();
+ delete this._scrollTimer;
+ }
+ },
+ /**
+ * @method align
+ * @description Called from the drag:align event to determine if we need to scroll.
+ */
+ align: function(e) {
+ if (this._scrolling) {
+ this._cancelScroll();
+ e.preventDefault();
+ }
+ if (!this._scrolling) {
+ this._checkWinScroll();
+ }
+ },
+ /**
+ * @private
+ * @method _setDimCache
+ * @description Set the cache of the dragNode dims.
+ */
+ _setDimCache: function() {
+ var node = this.get(HOST).get('dragNode');
+ this._dimCache = {
+ h: node.get(OFFSET_HEIGHT),
+ w: node.get(OFFSET_WIDTH)
+ };
+ },
+ /**
+ * @method start
+ * @description Called from the drag:start event
+ */
+ start: function() {
+ this._setDimCache();
+ },
+ /**
+ * @method end
+ * @description Called from the drag:end event
+ */
+ end: function(xy) {
+ this._dimCache = null;
+ this._cancelScroll();
+ },
+ /**
+ * @method toString
+ * @description General toString method for logging
+ * @return String name for the object
+ */
+ toString: function() {
+ return S.NAME + ' #' + this.get('node').get('id');
+ }
+ });
+
+ Y.namespace('Plugin');
+
+
+ /**
+ * Extends the Scroll class to make the window scroll while dragging.
+ * @class DDWindowScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var WS = function() {
+ WS.superclass.constructor.apply(this, arguments);
+ };
+ WS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: true
+ * @type Boolean
+ */
+ windowScroll: {
+ value: true,
+ setter: function(scroll) {
+ if (scroll) {
+ this.set(PARENT_SCROLL, Y.get(window));
+ }
+ return scroll;
+ }
+ }
+ });
+ Y.extend(WS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('windowScroll', this.get('windowScroll'));
+ }
+ });
+ WS.NAME = WS.NS = 'winscroll';
+ Y.Plugin.DDWinScroll = WS;
+
+
+ /**
+ * Extends the Scroll class to make a parent node scroll while dragging.
+ * @class DDNodeScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var NS = function() {
+ NS.superclass.constructor.apply(this, arguments);
+
+ };
+ NS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute node
+ * @description The node we want to scroll. Used to set the internal parentScroll attribute.
+ * @type Node
+ */
+ node: {
+ value: false,
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ if (node !== false) {
+ Y.error('DDNodeScroll: Invalid Node Given: ' + node);
+ }
+ } else {
+ n = n.item(0);
+ this.set(PARENT_SCROLL, n);
+ }
+ return n;
+ }
+ }
+ });
+ Y.extend(NS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('node', this.get('node'));
+ }
+ });
+ NS.NAME = NS.NS = 'nodescroll';
+ Y.Plugin.DDNodeScroll = NS;
+
+ Y.DD.Scroll = S;
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-proxy']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dd-scroll",function(C){var H=function(){H.superclass.constructor.apply(this,arguments);},L="host",A="buffer",J="parentScroll",G="windowScroll",I="scrollTop",F="scrollLeft",E="offsetWidth",K="offsetHeight";H.ATTRS={parentScroll:{value:false,setter:function(M){if(M){return M;}return false;}},buffer:{value:30},scrollDelay:{value:235},host:{value:null},windowScroll:{value:false},vertical:{value:true},horizontal:{value:true}};C.extend(H,C.Base,{_scrolling:null,_vpRegionCache:null,_dimCache:null,_scrollTimer:null,_getVPRegion:function(){var M={};var N=this.get(J),R=this.get(A),Q=this.get(G),U=((Q)?[]:N.getXY()),S=((Q)?"winWidth":E),P=((Q)?"winHeight":K),T=((Q)?N.get(I):U[1]),O=((Q)?N.get(F):U[0]);M={top:T+R,right:(N.get(S)+O)-R,bottom:(N.get(P)+T)-R,left:O+R};this._vpRegionCache=M;return M;},initializer:function(){var M=this.get(L);M.after("drag:start",C.bind(this.start,this));M.after("drag:end",C.bind(this.end,this));M.on("drag:align",C.bind(this.align,this));C.get(window).on("scroll",C.bind(function(){this._vpRegionCache=null;},this));},_checkWinScroll:function(Y){var X=this._getVPRegion(),M=this.get(L),O=this.get(G),S=M.lastXY,N=false,e=this.get(A),R=this.get(J),g=R.get(I),U=R.get(F),V=this._dimCache.w,a=this._dimCache.h,T=S[1]+a,W=S[1],d=S[0]+V,Q=S[0],f=W,P=Q,Z=g,c=U;if(this.get("horizontal")){if(Q<=X.left){N=true;P=S[0]-((O)?e:0);c=U-e;}if(d>=X.right){N=true;P=S[0]+((O)?e:0);c=U+e;}}if(this.get("vertical")){if(T>=X.bottom){N=true;f=S[1]+((O)?e:0);Z=g+e;}if(W<=X.top){N=true;f=S[1]-((O)?e:0);Z=g-e;}}if(Z<0){Z=0;f=S[1];}if(c<0){c=0;P=S[0];}if(f<0){f=S[1];}if(P<0){P=S[0];}if(Y){M.actXY=[P,f];M._moveNode({node:R,top:Z,left:c});if(!Z&&!c){this._cancelScroll();}}else{if(N){this._initScroll();}else{this._cancelScroll();}}},_initScroll:function(){this._cancelScroll();this._scrollTimer=C.Lang.later(this.get("scrollDelay"),this,this._checkWinScroll,[true],true);},_cancelScroll:function(){this._scrolling=false;if(this._scrollTimer){this._scrollTimer.cancel();delete this._scrollTimer;}},align:function(M){if(this._scrolling){this._cancelScroll();M.preventDefault();}if(!this._scrolling){this._checkWinScroll();}},_setDimCache:function(){var M=this.get(L).get("dragNode");this._dimCache={h:M.get(K),w:M.get(E)};},start:function(){this._setDimCache();},end:function(M){this._dimCache=null;this._cancelScroll();},toString:function(){return H.NAME+" #"+this.get("node").get("id");}});C.namespace("Plugin");var B=function(){B.superclass.constructor.apply(this,arguments);};B.ATTRS=C.merge(H.ATTRS,{windowScroll:{value:true,setter:function(M){if(M){this.set(J,C.get(window));}return M;}}});C.extend(B,H,{initializer:function(){this.set("windowScroll",this.get("windowScroll"));}});B.NAME=B.NS="winscroll";C.Plugin.DDWinScroll=B;var D=function(){D.superclass.constructor.apply(this,arguments);};D.ATTRS=C.merge(H.ATTRS,{node:{value:false,setter:function(M){var N=C.get(M);if(!N){if(M!==false){C.error("DDNodeScroll: Invalid Node Given: "+M);}}else{N=N.item(0);this.set(J,N);}return N;}}});C.extend(D,H,{initializer:function(){this.set("node",this.get("node"));}});D.NAME=D.NS="nodescroll";C.Plugin.DDNodeScroll=D;C.DD.Scroll=H;},"3.0.0",{skinnable:false,requires:["dd-drag"],optional:["dd-proxy"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-scroll', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-scroll
+ */
+ /**
+ * This class is the base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
+ * This class should not be called on it's own, it's designed to be a plugin.
+ * @class Scroll
+ * @extends Base
+ * @namespace DD
+ * @constructor
+ */
+
+ var S = function() {
+ S.superclass.constructor.apply(this, arguments);
+
+ },
+ HOST = 'host',
+ BUFFER = 'buffer',
+ PARENT_SCROLL = 'parentScroll',
+ WINDOW_SCROLL = 'windowScroll',
+ SCROLL_TOP = 'scrollTop',
+ SCROLL_LEFT = 'scrollLeft',
+ OFFSET_WIDTH = 'offsetWidth',
+ OFFSET_HEIGHT = 'offsetHeight';
+
+
+ S.ATTRS = {
+ /**
+ * @attribute parentScroll
+ * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
+ * @type Node
+ */
+ parentScroll: {
+ value: false,
+ setter: function(node) {
+ if (node) {
+ return node;
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute buffer
+ * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
+ * @type Number
+ */
+ buffer: {
+ value: 30
+ },
+ /**
+ * @attribute scrollDelay
+ * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
+ * @type Number
+ */
+ scrollDelay: {
+ value: 235
+ },
+ /**
+ * @attribute host
+ * @description The host we are plugged into.
+ * @type Object
+ */
+ host: {
+ value: null
+ },
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: false
+ * @type Boolean
+ */
+ windowScroll: {
+ value: false
+ },
+ /**
+ * @attribute vertical
+ * @description Allow vertical scrolling, default: true.
+ * @type Boolean
+ */
+ vertical: {
+ value: true
+ },
+ /**
+ * @attribute horizontal
+ * @description Allow horizontal scrolling, default: true.
+ * @type Boolean
+ */
+ horizontal: {
+ value: true
+ }
+ };
+
+ Y.extend(S, Y.Base, {
+ /**
+ * @private
+ * @property _scrolling
+ * @description Tells if we are actively scrolling or not.
+ * @type Boolean
+ */
+ _scrolling: null,
+ /**
+ * @private
+ * @property _vpRegionCache
+ * @description Cache of the Viewport dims.
+ * @type Object
+ */
+ _vpRegionCache: null,
+ /**
+ * @private
+ * @property _dimCache
+ * @description Cache of the dragNode dims.
+ * @type Object
+ */
+ _dimCache: null,
+ /**
+ * @private
+ * @property _scrollTimer
+ * @description Holder for the Timer object returned from Y.later.
+ * @type {Y.later}
+ */
+ _scrollTimer: null,
+ /**
+ * @private
+ * @method _getVPRegion
+ * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
+ */
+ _getVPRegion: function() {
+ var r = {};
+ //if (!this._vpRegionCache) {
+ var n = this.get(PARENT_SCROLL),
+ b = this.get(BUFFER),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ((ws) ? [] : n.getXY()),
+ w = ((ws) ? 'winWidth' : OFFSET_WIDTH),
+ h = ((ws) ? 'winHeight' : OFFSET_HEIGHT),
+ t = ((ws) ? n.get(SCROLL_TOP) : xy[1]),
+ l = ((ws) ? n.get(SCROLL_LEFT) : xy[0]);
+
+ r = {
+ top: t + b,
+ right: (n.get(w) + l) - b,
+ bottom: (n.get(h) + t) - b,
+ left: l + b
+ };
+ this._vpRegionCache = r;
+ //} else {
+ // r = this._vpRegionCache;
+ //}
+ return r;
+ },
+ initializer: function() {
+ var h = this.get(HOST);
+ h.after('drag:start', Y.bind(this.start, this));
+ h.after('drag:end', Y.bind(this.end, this));
+ h.on('drag:align', Y.bind(this.align, this));
+
+ //TODO - This doesn't work yet??
+ Y.get(window).on('scroll', Y.bind(function() {
+ this._vpRegionCache = null;
+ }, this));
+ },
+ /**
+ * @private
+ * @method _checkWinScroll
+ * @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
+ * @param {Boolean} move Should we move the window. From Y.later
+ */
+ _checkWinScroll: function(move) {
+ var r = this._getVPRegion(),
+ ho = this.get(HOST),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ho.lastXY,
+ scroll = false,
+ b = this.get(BUFFER),
+ win = this.get(PARENT_SCROLL),
+ sTop = win.get(SCROLL_TOP),
+ sLeft = win.get(SCROLL_LEFT),
+ w = this._dimCache.w,
+ h = this._dimCache.h,
+ bottom = xy[1] + h,
+ top = xy[1],
+ right = xy[0] + w,
+ left = xy[0],
+ nt = top,
+ nl = left,
+ st = sTop,
+ sl = sLeft;
+
+ if (this.get('horizontal')) {
+ if (left <= r.left) {
+ scroll = true;
+ nl = xy[0] - ((ws) ? b : 0);
+ sl = sLeft - b;
+ }
+ if (right >= r.right) {
+ scroll = true;
+ nl = xy[0] + ((ws) ? b : 0);
+ sl = sLeft + b;
+ }
+ }
+ if (this.get('vertical')) {
+ if (bottom >= r.bottom) {
+ scroll = true;
+ nt = xy[1] + ((ws) ? b : 0);
+ st = sTop + b;
+
+ }
+ if (top <= r.top) {
+ scroll = true;
+ nt = xy[1] - ((ws) ? b : 0);
+ st = sTop - b;
+ }
+ }
+
+ if (st < 0) {
+ st = 0;
+ nt = xy[1];
+ }
+
+ if (sl < 0) {
+ sl = 0;
+ nl = xy[0];
+ }
+
+ if (nt < 0) {
+ nt = xy[1];
+ }
+ if (nl < 0) {
+ nl = xy[0];
+ }
+ if (move) {
+ ho.actXY = [nl, nt];
+ ho._moveNode({ node: win, top: st, left: sl});
+ if (!st && !sl) {
+ this._cancelScroll();
+ }
+ } else {
+ if (scroll) {
+ this._initScroll();
+ } else {
+ this._cancelScroll();
+ }
+ }
+ },
+ /**
+ * @private
+ * @method _initScroll
+ * @description Cancel a previous scroll timer and init a new one.
+ */
+ _initScroll: function() {
+ this._cancelScroll();
+ this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
+
+ },
+ /**
+ * @private
+ * @method _cancelScroll
+ * @description Cancel a currently running scroll timer.
+ */
+ _cancelScroll: function() {
+ this._scrolling = false;
+ if (this._scrollTimer) {
+ this._scrollTimer.cancel();
+ delete this._scrollTimer;
+ }
+ },
+ /**
+ * @method align
+ * @description Called from the drag:align event to determine if we need to scroll.
+ */
+ align: function(e) {
+ if (this._scrolling) {
+ this._cancelScroll();
+ e.preventDefault();
+ }
+ if (!this._scrolling) {
+ this._checkWinScroll();
+ }
+ },
+ /**
+ * @private
+ * @method _setDimCache
+ * @description Set the cache of the dragNode dims.
+ */
+ _setDimCache: function() {
+ var node = this.get(HOST).get('dragNode');
+ this._dimCache = {
+ h: node.get(OFFSET_HEIGHT),
+ w: node.get(OFFSET_WIDTH)
+ };
+ },
+ /**
+ * @method start
+ * @description Called from the drag:start event
+ */
+ start: function() {
+ this._setDimCache();
+ },
+ /**
+ * @method end
+ * @description Called from the drag:end event
+ */
+ end: function(xy) {
+ this._dimCache = null;
+ this._cancelScroll();
+ },
+ /**
+ * @method toString
+ * @description General toString method for logging
+ * @return String name for the object
+ */
+ toString: function() {
+ return S.NAME + ' #' + this.get('node').get('id');
+ }
+ });
+
+ Y.namespace('Plugin');
+
+
+ /**
+ * Extends the Scroll class to make the window scroll while dragging.
+ * @class DDWindowScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var WS = function() {
+ WS.superclass.constructor.apply(this, arguments);
+ };
+ WS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: true
+ * @type Boolean
+ */
+ windowScroll: {
+ value: true,
+ setter: function(scroll) {
+ if (scroll) {
+ this.set(PARENT_SCROLL, Y.get(window));
+ }
+ return scroll;
+ }
+ }
+ });
+ Y.extend(WS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('windowScroll', this.get('windowScroll'));
+ }
+ });
+ WS.NAME = WS.NS = 'winscroll';
+ Y.Plugin.DDWinScroll = WS;
+
+
+ /**
+ * Extends the Scroll class to make a parent node scroll while dragging.
+ * @class DDNodeScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var NS = function() {
+ NS.superclass.constructor.apply(this, arguments);
+
+ };
+ NS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute node
+ * @description The node we want to scroll. Used to set the internal parentScroll attribute.
+ * @type Node
+ */
+ node: {
+ value: false,
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ if (node !== false) {
+ Y.error('DDNodeScroll: Invalid Node Given: ' + node);
+ }
+ } else {
+ n = n.item(0);
+ this.set(PARENT_SCROLL, n);
+ }
+ return n;
+ }
+ }
+ });
+ Y.extend(NS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('node', this.get('node'));
+ }
+ });
+ NS.NAME = NS.NS = 'nodescroll';
+ Y.Plugin.DDNodeScroll = NS;
+
+ Y.DD.Scroll = S;
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-proxy']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dd-ddm-base', function(Y) {
+
+
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @module dd
+ * @submodule dd-ddm-base
+ */
+ /**
+ * Provides the base Drag Drop Manger required for making a Node draggable.
+ * @class DDM
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDMBase = function() {
+ DDMBase.superclass.constructor.apply(this, arguments);
+ };
+
+ DDMBase.NAME = 'ddm';
+
+ DDMBase.ATTRS = {
+ /**
+ * @attribute dragCursor
+ * @description The cursor to apply when dragging, if shimmed the shim will get the cursor.
+ * @type String
+ */
+ dragCursor: {
+ value: 'move'
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: 3
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: 1000
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of all future Drag instances.
+ * @type String
+ */
+ dragMode: {
+ value: 'point',
+ setter: function(mode) {
+ this._setDragMode(mode);
+ return mode;
+ }
+ }
+
+ };
+
+ Y.extend(DDMBase, Y.Base, {
+ /**
+ * @property _active
+ * @description flag set when we activate our first drag, so DDM can start listening for events.
+ * @type {Boolean}
+ */
+ _active: null,
+ /**
+ * @private
+ * @method _setDragMode
+ * @description Handler for dragMode attribute setter.
+ * @param String/Number The Number value or the String for the DragMode to default all future drag instances to.
+ * @return Number The Mode to be set
+ */
+ _setDragMode: function(mode) {
+ if (mode === null) {
+ mode = Y.DD.DDM.get('dragMode');
+ }
+ switch (mode) {
+ case 1:
+ case 'intersect':
+ return 1;
+ case 2:
+ case 'strict':
+ return 2;
+ case 0:
+ case 'point':
+ return 0;
+ }
+ return 0;
+ },
+ /**
+ * @property CSS_PREFIX
+ * @description The PREFIX to attach to all DD CSS class names
+ * @type {String}
+ */
+ CSS_PREFIX: 'yui-dd',
+ _activateTargets: function() {},
+ /**
+ * @private
+ * @property _drags
+ * @description Holder for all registered drag elements.
+ * @type {Array}
+ */
+ _drags: [],
+ /**
+ * @property activeDrag
+ * @description A reference to the currently active draggable object.
+ * @type {Drag}
+ */
+ activeDrag: false,
+ /**
+ * @private
+ * @method _regDrag
+ * @description Adds a reference to the drag object to the DDM._drags array, called in the constructor of Drag.
+ * @param {Drag} d The Drag object
+ */
+ _regDrag: function(d) {
+ if (this.getDrag(d.get('node'))) {
+ return false;
+ }
+
+ if (!this._active) {
+ this._setupListeners();
+ }
+ this._drags.push(d);
+ return true;
+ },
+ /**
+ * @private
+ * @method _unregDrag
+ * @description Remove this drag object from the DDM._drags array.
+ * @param {Drag} d The drag object.
+ */
+ _unregDrag: function(d) {
+ var tmp = [];
+ Y.each(this._drags, function(n, i) {
+ if (n !== d) {
+ tmp[tmp.length] = n;
+ }
+ });
+ this._drags = tmp;
+ },
+ /**
+ * @private
+ * @method _setupListeners
+ * @description Add the document listeners.
+ */
+ _setupListeners: function() {
+ this._active = true;
+ var doc = Y.get(document);
+ doc.on('mousemove', Y.bind(this._move, this));
+ //Y.Event.nativeAdd(document, 'mousemove', Y.bind(this._move, this));
+ doc.on('mouseup', Y.bind(this._end, this));
+ },
+ /**
+ * @private
+ * @method _start
+ * @description Internal method used by Drag to signal the start of a drag operation
+ */
+ _start: function() {
+ this.fire('ddm:start');
+ this._startDrag();
+ },
+ /**
+ * @private
+ * @method _startDrag
+ * @description Factory method to be overwritten by other DDM's
+ * @param {Number} x The x position of the drag element
+ * @param {Number} y The y position of the drag element
+ * @param {Number} w The width of the drag element
+ * @param {Number} h The height of the drag element
+ */
+ _startDrag: function() {},
+ /**
+ * @private
+ * @method _endDrag
+ * @description Factory method to be overwritten by other DDM's
+ */
+ _endDrag: function() {},
+ _dropMove: function() {},
+ /**
+ * @private
+ * @method _end
+ * @description Internal method used by Drag to signal the end of a drag operation
+ */
+ _end: function() {
+ if (this.activeDrag) {
+ this._endDrag();
+ this.fire('ddm:end');
+ this.activeDrag.end.call(this.activeDrag);
+ this.activeDrag = null;
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.activeDrag) {
+ this._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Internal listener for the mousemove DOM event to pass to the Drag's move method.
+ * @param {Event.Facade} ev The Dom mousemove Event
+ */
+ _move: function(ev) {
+ if (this.activeDrag) {
+ this.activeDrag._move.call(this.activeDrag, ev);
+ this._dropMove();
+ }
+ },
+ /**
+ * //TODO Private, rename??...
+ * @private
+ * @method cssSizestoObject
+ * @description Helper method to use to set the gutter from the attribute setter.
+ * @param {String} gutter CSS style string for gutter: '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @return {Object} The gutter Object Literal.
+ */
+ cssSizestoObject: function(gutter) {
+ var x = gutter.split(' ');
+
+ switch (x.length) {
+ case 1: x[1] = x[2] = x[3] = x[0]; break;
+ case 2: x[2] = x[0]; x[3] = x[1]; break;
+ case 3: x[3] = x[1]; break;
+ }
+
+ return {
+ top : parseInt(x[0],10),
+ right : parseInt(x[1],10),
+ bottom: parseInt(x[2],10),
+ left : parseInt(x[3],10)
+ };
+ },
+ /**
+ * @method getDrag
+ * @description Get a valid Drag instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drag Object
+ * @return {Object}
+ */
+ getDrag: function(node) {
+ var drag = false,
+ n = Y.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this._drags, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drag = v;
+ }
+ });
+ }
+ return drag;
+ }
+ });
+
+ Y.namespace('DD');
+ Y.DD.DDM = new DDMBase();
+
+ /**
+ * @event ddm:start
+ * @description Fires from the DDM before all drag events fire.
+ * @type {Event.Custom}
+ */
+ /**
+ * @event ddm:end
+ * @description Fires from the DDM after the DDM finishes, before the drag end events.
+ * @type {Event.Custom}
+ */
+
+
+
+
+}, '3.0.0' ,{requires:['node', 'base'], skinnable:false});
+YUI.add('dd-ddm', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm-base Class to add support for the viewport shim to allow a draggable node to drag to be dragged over an iframe or any other node that traps mousemove events.
+ * It is also required to have Drop Targets enabled, as the viewport shim will contain the shims for the Drop Targets.
+ * @module dd
+ * @submodule dd-ddm
+ * @for DDM
+ * @namespace DD
+ */
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _pg
+ * @description The shim placed over the screen to track the mousemove event.
+ * @type {Node}
+ */
+ _pg: null,
+ /**
+ * @private
+ * @property _debugShim
+ * @description Set this to true to set the shims opacity to .5 for debugging it, default: false.
+ * @type {Boolean}
+ */
+ _debugShim: false,
+ _activateTargets: function() {},
+ _deactivateTargets: function() {},
+ _startDrag: function() {
+ if (this.activeDrag.get('useShim')) {
+ this._pg_activate();
+ this._activateTargets();
+ }
+ },
+ _endDrag: function() {
+ this._pg_deactivate();
+ this._deactivateTargets();
+ },
+ /**
+ * @private
+ * @method _pg_deactivate
+ * @description Deactivates the shim
+ */
+ _pg_deactivate: function() {
+ this._pg.setStyle('display', 'none');
+ },
+ /**
+ * @private
+ * @method _pg_activate
+ * @description Activates the shim
+ */
+ _pg_activate: function() {
+ var ah = this.activeDrag.get('activeHandle'), cur = 'auto';
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = this.get('dragCursor');
+ }
+
+ this._pg_size();
+ this._pg.setStyles({
+ top: 0,
+ left: 0,
+ display: 'block',
+ opacity: ((this._debugShim) ? '.5' : '0'),
+ cursor: cur
+ });
+ },
+ /**
+ * @private
+ * @method _pg_size
+ * @description Sizes the shim on: activatation, window:scroll, window:resize
+ */
+ _pg_size: function() {
+ if (this.activeDrag) {
+ var b = Y.get('body'),
+ h = b.get('docHeight'),
+ w = b.get('docWidth');
+ this._pg.setStyles({
+ height: h + 'px',
+ width: w + 'px'
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _createPG
+ * @description Creates the shim and adds it's listeners to it.
+ */
+ _createPG: function() {
+ var pg = Y.Node.create('<div></div>'),
+ bd = Y.get('body');
+ pg.setStyles({
+ top: '0',
+ left: '0',
+ position: 'absolute',
+ zIndex: '9999',
+ overflow: 'hidden',
+ backgroundColor: 'red',
+ display: 'none',
+ height: '5px',
+ width: '5px'
+ });
+ pg.set('id', Y.stamp(pg));
+ pg.addClass('yui-dd-shim');
+ if (bd.get('firstChild')) {
+ bd.insertBefore(pg, bd.get('firstChild'));
+ } else {
+ bd.appendChild(pg);
+ }
+ this._pg = pg;
+ this._pg.on('mouseup', Y.bind(this._end, this));
+ this._pg.on('mousemove', Y.bind(this._move, this));
+
+ var win = Y.get(window);
+ Y.on('window:resize', Y.bind(this._pg_size, this));
+ win.on('scroll', Y.bind(this._pg_size, this));
+ }
+ }, true);
+
+ Y.on('domready', Y.bind(Y.DD.DDM._createPG, Y.DD.DDM));
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base', 'event-resize'], skinnable:false});
+YUI.add('dd-ddm-drop', function(Y) {
+
+
+ /**
+ * Extends the dd-ddm Class to add support for the placement of Drop Target shims inside the viewport shim. It also handles all Drop Target related events and interactions.
+ * @module dd
+ * @submodule dd-ddm-drop
+ * @for DDM
+ * @namespace DD
+ */
+
+ //TODO CSS class name for the bestMatch..
+ Y.mix(Y.DD.DDM, {
+ /**
+ * @private
+ * @property _noShim
+ * @description This flag turns off the use of the mouseover/mouseout shim. It should not be used unless you know what you are doing.
+ * @type {Boolean}
+ */
+ _noShim: false,
+ /**
+ * @private
+ * @property _activeShims
+ * @description Placeholder for all active shims on the page
+ * @type {Array}
+ */
+ _activeShims: [],
+ /**
+ * @private
+ * @method _hasActiveShim
+ * @description This method checks the _activeShims Object to see if there is a shim active.
+ * @return {Boolean}
+ */
+ _hasActiveShim: function() {
+ if (this._noShim) {
+ return true;
+ }
+ return this._activeShims.length;
+ },
+ /**
+ * @private
+ * @method _addActiveShim
+ * @description Adds a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to add to the list.
+ */
+ _addActiveShim: function(d) {
+ this._activeShims[this._activeShims.length] = d;
+ },
+ /**
+ * @private
+ * @method _removeActiveShim
+ * @description Removes a Drop Target to the list of active shims
+ * @param {Object} d The Drop instance to remove from the list.
+ */
+ _removeActiveShim: function(d) {
+ var s = [];
+ Y.each(this._activeShims, function(v, k) {
+ if (v._yuid !== d._yuid) {
+ s[s.length] = v;
+ }
+
+ });
+ this._activeShims = s;
+ },
+ /**
+ * @method syncActiveShims
+ * @description This method will sync the position of the shims on the Drop Targets that are currently active.
+ * @param {Boolean} force Resize/sync all Targets.
+ */
+ syncActiveShims: function(force) {
+ Y.later(0, this, function(force) {
+ var drops = ((force) ? this.targets : this._lookup());
+ Y.each(drops, function(v, k) {
+ v.sizeShim.call(v);
+ }, this);
+ }, force);
+ },
+ /**
+ * @private
+ * @property mode
+ * @description The mode that the drag operations will run in 0 for Point, 1 for Intersect, 2 for Strict
+ * @type Number
+ */
+ mode: 0,
+ /**
+ * @private
+ * @property POINT
+ * @description In point mode, a Drop is targeted by the cursor being over the Target
+ * @type Number
+ */
+ POINT: 0,
+ /**
+ * @private
+ * @property INTERSECT
+ * @description In intersect mode, a Drop is targeted by "part" of the drag node being over the Target
+ * @type Number
+ */
+ INTERSECT: 1,
+ /**
+ * @private
+ * @property STRICT
+ * @description In strict mode, a Drop is targeted by the "entire" drag node being over the Target
+ * @type Number
+ */
+ STRICT: 2,
+ /**
+ * @property useHash
+ * @description Should we only check targets that are in the viewport on drags (for performance), default: true
+ * @type {Boolean}
+ */
+ useHash: true,
+ /**
+ * @property activeDrop
+ * @description A reference to the active Drop Target
+ * @type {Object}
+ */
+ activeDrop: null,
+ /**
+ * @property validDrops
+ * @description An array of the valid Drop Targets for this interaction.
+ * @type {Array}
+ */
+ //TODO Change array/object literals to be in sync..
+ validDrops: [],
+ /**
+ * @property otherDrops
+ * @description An object literal of Other Drop Targets that we encountered during this interaction (in the case of overlapping Drop Targets)
+ * @type {Object}
+ */
+ otherDrops: {},
+ /**
+ * @property targets
+ * @description All of the Targets
+ * @type {Array}
+ */
+ targets: [],
+ /**
+ * @private
+ * @method _addValid
+ * @description Add a Drop Target to the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _addValid: function(drop) {
+ this.validDrops[this.validDrops.length] = drop;
+ return this;
+ },
+ /**
+ * @private
+ * @method _removeValid
+ * @description Removes a Drop Target from the list of Valid Targets. This list get's regenerated on each new drag operation.
+ * @param {Object} drop
+ * @return {Self}
+ * @chainable
+ */
+ _removeValid: function(drop) {
+ var drops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ drops[drops.length] = v;
+ }
+ });
+
+ this.validDrops = drops;
+ return this;
+ },
+ /**
+ * @method isOverTarget
+ * @description Check to see if the Drag element is over the target, method varies on current mode
+ * @param {Object} drop The drop to check against
+ * @return {Boolean}
+ */
+ isOverTarget: function(drop) {
+ if (this.activeDrag && drop) {
+ var xy = this.activeDrag.mouseXY, r, dMode = this.activeDrag.get('dragMode'),
+ aRegion;
+ if (xy && this.activeDrag) {
+ aRegion = this.activeDrag.region;
+ if (dMode == this.STRICT) {
+ return this.activeDrag.get('dragNode').inRegion(drop.region, true, aRegion);
+ } else {
+ if (drop && drop.shim) {
+ if ((dMode == this.INTERSECT) && this._noShim) {
+ r = ((aRegion) ? aRegion : this.activeDrag.get('node'));
+ return drop.get('node').intersect(r).inRegion;
+ } else {
+ return drop.shim.intersect({
+ top: xy[1],
+ bottom: xy[1],
+ left: xy[0],
+ right: xy[0]
+ }, drop.region).inRegion;
+ }
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @method clearCache
+ * @description Clears the cache data used for this interaction.
+ */
+ clearCache: function() {
+ this.validDrops = [];
+ this.otherDrops = {};
+ this._activeShims = [];
+ },
+ /**
+ * @private
+ * @method _activateTargets
+ * @description Clear the cache and activate the shims of all the targets
+ */
+ _activateTargets: function() {
+ this.clearCache();
+ Y.each(this.targets, function(v, k) {
+ v._activateShim.apply(v, []);
+ }, this);
+ this._handleTargetOver();
+
+ },
+ /**
+ * @method getBestMatch
+ * @description This method will gather the area for all potential targets and see which has the hightest covered area and return it.
+ * @param {Array} drops An Array of drops to scan for the best match.
+ * @param {Boolean} all If present, it returns an Array. First item is best match, second is an Array of the other items in the original Array.
+ * @return {Object or Array}
+ */
+ getBestMatch: function(drops, all) {
+ var biggest = null, area = 0, out;
+
+ Y.each(drops, function(v, k) {
+ var inter = this.activeDrag.get('dragNode').intersect(v.get('node'));
+ v.region.area = inter.area;
+
+ if (inter.inRegion) {
+ if (inter.area > area) {
+ area = inter.area;
+ biggest = v;
+ }
+ }
+ }, this);
+ if (all) {
+ out = [];
+ //TODO Sort the others in numeric order by area covered..
+ Y.each(drops, function(v, k) {
+ if (v !== biggest) {
+ out[out.length] = v;
+ }
+ }, this);
+ return [biggest, out];
+ } else {
+ return biggest;
+ }
+ },
+ /**
+ * @private
+ * @method _deactivateTargets
+ * @description This method fires the drop:hit, drag:drophit, drag:dropmiss methods and deactivates the shims..
+ */
+ _deactivateTargets: function() {
+ var other = [], tmp,
+ activeDrag = this.activeDrag,
+ activeDrop = this.activeDrop;
+
+ //TODO why is this check so hard??
+ if (activeDrag && activeDrop && this.otherDrops[activeDrop]) {
+ if (!activeDrag.get('dragMode')) {
+ //TODO otherDrops -- private..
+ other = this.otherDrops;
+ delete other[activeDrop];
+ } else {
+ tmp = this.getBestMatch(this.otherDrops, true);
+ activeDrop = tmp[0];
+ other = tmp[1];
+ }
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ if (activeDrop) {
+ activeDrop.fire('drop:hit', { drag: activeDrag, drop: activeDrop, others: other });
+ activeDrag.fire('drag:drophit', { drag: activeDrag, drop: activeDrop, others: other });
+ }
+ } else if (activeDrag) {
+ activeDrag.get('node').removeClass(this.CSS_PREFIX + '-drag-over');
+ activeDrag.fire('drag:dropmiss', { pageX: activeDrag.lastXY[0], pageY: activeDrag.lastXY[1] });
+ } else {
+ }
+
+ this.activeDrop = null;
+
+ Y.each(this.targets, function(v, k) {
+ v._deactivateShim.apply(v, []);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _dropMove
+ * @description This method is called when the move method is called on the Drag Object.
+ */
+ _dropMove: function() {
+ if (this._hasActiveShim()) {
+ this._handleTargetOver();
+ } else {
+ Y.each(this.otherDrops, function(v, k) {
+ v._handleOut.apply(v, []);
+ });
+ }
+ },
+ /**
+ * @private
+ * @method _lookup
+ * @description Filters the list of Drops down to those in the viewport.
+ * @return {Array} The valid Drop Targets that are in the viewport.
+ */
+ _lookup: function() {
+ if (!this.useHash || this._noShim) {
+ return this.validDrops;
+ }
+ var drops = [];
+ //Only scan drop shims that are in the Viewport
+ Y.each(this.validDrops, function(v, k) {
+ if (v.shim && v.shim.inViewportRegion(false, v.region)) {
+ drops[drops.length] = v;
+ }
+ });
+ return drops;
+
+ },
+ /**
+ * @private
+ * @method _handleTargetOver
+ * @description This method execs _handleTargetOver on all valid Drop Targets
+ */
+ _handleTargetOver: function() {
+ var drops = this._lookup();
+ Y.each(drops, function(v, k) {
+ v._handleTargetOver.call(v);
+ }, this);
+ },
+ /**
+ * @private
+ * @method _regTarget
+ * @description Add the passed in Target to the targets collection
+ * @param {Object} t The Target to add to the targets collection
+ */
+ _regTarget: function(t) {
+ this.targets[this.targets.length] = t;
+ },
+ /**
+ * @private
+ * @method _unregTarget
+ * @description Remove the passed in Target from the targets collection
+ * @param {Object} drop The Target to remove from the targets collection
+ */
+ _unregTarget: function(drop) {
+ var targets = [], vdrops;
+ Y.each(this.targets, function(v, k) {
+ if (v != drop) {
+ targets[targets.length] = v;
+ }
+ }, this);
+ this.targets = targets;
+
+ vdrops = [];
+ Y.each(this.validDrops, function(v, k) {
+ if (v !== drop) {
+ vdrops[vdrops.length] = v;
+ }
+ });
+
+ this.validDrops = vdrops;
+ },
+ /**
+ * @method getDrop
+ * @description Get a valid Drop instance back from a Node or a selector string, false otherwise
+ * @param {String/Object} node The Node instance or Selector string to check for a valid Drop Object
+ * @return {Object}
+ */
+ getDrop: function(node) {
+ var drop = false,
+ n = Y.Node.get(node);
+ if (n instanceof Y.Node) {
+ Y.each(this.targets, function(v, k) {
+ if (n.compareTo(v.get('node'))) {
+ drop = v;
+ }
+ });
+ }
+ return drop;
+ }
+ }, true);
+
+
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm'], skinnable:false});
+YUI.add('dd-drag', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drag
+ */
+ /**
+ * This class provides the ability to drag a Node.
+ * @class Drag
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAGGING = 'dragging',
+ DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ MOUSE_UP = 'mouseup',
+ MOUSE_DOWN = 'mousedown',
+ DRAG_START = 'dragstart',
+ /**
+ * @event drag:mouseDown
+ * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
+ * @preventable _defMouseDownFn
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_MOUSE_DOWN = 'drag:mouseDown',
+ /**
+ * @event drag:afterMouseDown
+ * @description Fires after the mousedown event has been cleared.
+ * @param {Event.Facade} ev The mousedown event.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
+ /**
+ * @event drag:removeHandle
+ * @description Fires after a handle is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_HANDLE = 'drag:removeHandle',
+ /**
+ * @event drag:addHandle
+ * @description Fires after a handle is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_HANDLE = 'drag:addHandle',
+ /**
+ * @event drag:removeInvalid
+ * @description Fires after an invalid selector is removed.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_REMOVE_INVALID = 'drag:removeInvalid',
+ /**
+ * @event drag:addInvalid
+ * @description Fires after an invalid selector is added.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ADD_INVALID = 'drag:addInvalid',
+ /**
+ * @event drag:start
+ * @description Fires at the start of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_START = 'drag:start',
+ /**
+ * @event drag:end
+ * @description Fires at the end of a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_END = 'drag:end',
+ /**
+ * @event drag:drag
+ * @description Fires every mousemove during a drag operation.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DRAG = 'drag:drag',
+ /**
+ * @event drag:align
+ * @preventable _defAlignFn
+ * @description Fires when this node is aligned.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_ALIGN = 'drag:align',
+ /**
+ * @event drag:over
+ * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:enter
+ * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:exit
+ * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:drophit
+ * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ /**
+ * @event drag:dropmiss
+ * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+ Drag = function(o) {
+ this._lazyAddAttrs = false;
+ Drag.superclass.constructor.apply(this, arguments);
+
+ var valid = DDM._regDrag(this);
+ if (!valid) {
+ Y.error('Failed to register node, already in use: ' + o.node);
+ }
+ };
+
+ Drag.NAME = 'drag';
+
+ Drag.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to initiate a drag operation
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid Node Given: ' + node);
+ } else {
+ n = n.item(0);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute dragNode
+ * @description Y.Node instanace to use as the draggable element, defaults to node
+ * @type Node
+ */
+ dragNode: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drag: Invalid dragNode Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute offsetNode
+ * @description Offset the drag element by the difference in cursor position: default true
+ * @type Boolean
+ */
+ offsetNode: {
+ value: true
+ },
+ /**
+ * @attribute clickPixelThresh
+ * @description The number of pixels to move to start a drag operation, default is 3.
+ * @type Number
+ */
+ clickPixelThresh: {
+ value: DDM.get('clickPixelThresh')
+ },
+ /**
+ * @attribute clickTimeThresh
+ * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
+ * @type Number
+ */
+ clickTimeThresh: {
+ value: DDM.get('clickTimeThresh')
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drag element so that it can't be dragged: default false.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute data
+ * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
+ * @type Mixed
+ */
+ data: {
+ value: false
+ },
+ /**
+ * @attribute move
+ * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
+ * @type Boolean
+ */
+ move: {
+ value: true
+ },
+ /**
+ * @attribute useShim
+ * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
+ * @type Boolean
+ */
+ useShim: {
+ value: true
+ },
+ /**
+ * @attribute activeHandle
+ * @description This config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
+ * @type Node
+ */
+ activeHandle: {
+ value: false
+ },
+ /**
+ * @attribute primaryButtonOnly
+ * @description By default a drag operation will only begin if the mousedown occurred with the primary mouse button. Setting this to false will allow for all mousedown events to trigger a drag.
+ * @type Boolean
+ */
+ primaryButtonOnly: {
+ value: true
+ },
+ /**
+ * @attribute dragging
+ * @description This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
+ * @type Boolean
+ */
+ dragging: {
+ value: false
+ },
+ parent: {
+ value: false
+ },
+ /**
+ * @attribute target
+ * @description This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
+ * @type Boolean
+ */
+ target: {
+ value: false,
+ setter: function(config) {
+ this._handleTarget(config);
+ return config;
+ }
+ },
+ /**
+ * @attribute dragMode
+ * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
+ * @type String
+ */
+ dragMode: {
+ value: null,
+ setter: function(mode) {
+ return DDM._setDragMode(mode);
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drag into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ getter: function() {
+ if (!this._groups) {
+ this._groups = {};
+ }
+ var ret = [];
+ Y.each(this._groups, function(v, k) {
+ ret[ret.length] = k;
+ });
+ return ret;
+ },
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute handles
+ * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
+ * @type Array
+ */
+ handles: {
+ value: null,
+ setter: function(g) {
+ if (g) {
+ this._handles = {};
+ Y.each(g, function(v, k) {
+ this._handles[v] = true;
+ }, this);
+ } else {
+ this._handles = null;
+ }
+ return g;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drag instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drag, Y.Base, {
+ /**
+ * @method addToGroup
+ * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
+ * @param {String} g The group to add this Drag Instance to.
+ * @return {Self}
+ * @chainable
+ */
+ addToGroup: function(g) {
+ this._groups[g] = true;
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @method removeFromGroup
+ * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
+ * @param {String} g The group to remove this Drag Instance from.
+ * @return {Self}
+ * @chainable
+ */
+ removeFromGroup: function(g) {
+ delete this._groups[g];
+ DDM._activateTargets();
+ return this;
+ },
+ /**
+ * @property target
+ * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
+ * @type {Object}
+ */
+ target: null,
+ /**
+ * @private
+ * @method _handleTarget
+ * @description Attribute handler for the target config attribute.
+ * @param {Boolean/Object}
+ * @return {Boolean/Object}
+ */
+ _handleTarget: function(config) {
+ if (Y.DD.Drop) {
+ if (config === false) {
+ if (this.target) {
+ DDM._unregTarget(this.target);
+ this.target = null;
+ }
+ return false;
+ } else {
+ if (!Y.Lang.isObject(config)) {
+ config = {};
+ }
+ config.bubbles = ('bubbles' in config) ? config.bubbles : this.get('bubbles');
+ config.node = this.get(NODE);
+ config.groups = config.groups || this.get('groups');
+ this.target = new Y.DD.Drop(config);
+ }
+ } else {
+ return false;
+ }
+ },
+ /**
+ * @private
+ * @property _groups
+ * @description Storage Array for the groups this drag belongs to.
+ * @type {Array}
+ */
+ _groups: null,
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ this.publish(EV_MOUSE_DOWN, {
+ defaultFn: this._defMouseDownFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_ALIGN, {
+ defaultFn: this._defAlignFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_DRAG, {
+ defaultFn: this._defDragFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ this.publish(EV_END, {
+ preventedFn: this._prevEndFn,
+ queuable: false,
+ emitFacade: true,
+ bubbles: true,
+ prefix: 'drag'
+ });
+
+ var ev = [
+ EV_AFTER_MOUSE_DOWN,
+ EV_REMOVE_HANDLE,
+ EV_ADD_HANDLE,
+ EV_REMOVE_INVALID,
+ EV_ADD_INVALID,
+ EV_START,
+ 'drag:drophit',
+ 'drag:dropmiss',
+ 'drag:over',
+ 'drag:enter',
+ 'drag:exit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ bubbles: true,
+ preventable: false,
+ queuable: false,
+ prefix: 'drag'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+
+ },
+ /**
+ * @private
+ * @property _ev_md
+ * @description A private reference to the mousedown DOM event
+ * @type {Event.Facade}
+ */
+ _ev_md: null,
+ /**
+ * @private
+ * @property _startTime
+ * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _startTime: null,
+ /**
+ * @private
+ * @property _endTime
+ * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
+ * @type Date
+ */
+ _endTime: null,
+ /**
+ * @private
+ * @property _handles
+ * @description A private hash of the valid drag handles
+ * @type {Object}
+ */
+ _handles: null,
+ /**
+ * @private
+ * @property _invalids
+ * @description A private hash of the invalid selector strings
+ * @type {Object}
+ */
+ _invalids: null,
+ /**
+ * @private
+ * @property _invalidsDefault
+ * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true}
+ * @type {Object}
+ */
+ _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true, 'select': true },
+ /**
+ * @private
+ * @property _dragThreshMet
+ * @description Private flag to see if the drag threshhold was met
+ * @type {Boolean}
+ */
+ _dragThreshMet: null,
+ /**
+ * @private
+ * @property _fromTimeout
+ * @description Flag to determine if the drag operation came from a timeout
+ * @type {Boolean}
+ */
+ _fromTimeout: null,
+ /**
+ * @private
+ * @property _clickTimeout
+ * @description Holder for the setTimeout call
+ * @type {Boolean}
+ */
+ _clickTimeout: null,
+ /**
+ * @property deltaXY
+ * @description The offset of the mouse position to the element's position
+ * @type {Array}
+ */
+ deltaXY: null,
+ /**
+ * @property startXY
+ * @description The initial mouse position
+ * @type {Array}
+ */
+ startXY: null,
+ /**
+ * @property nodeXY
+ * @description The initial element position
+ * @type {Array}
+ */
+ nodeXY: null,
+ /**
+ * @property lastXY
+ * @description The position of the element as it's moving (for offset calculations)
+ * @type {Array}
+ */
+ lastXY: null,
+ /**
+ * @property actXY
+ * @description The xy that the node will be set to. Changing this will alter the position as it's dragged.
+ * @type {Array}
+ */
+ actXY: null,
+ /**
+ * @property realXY
+ * @description The real xy position of the node.
+ * @type {Array}
+ */
+ realXY: null,
+ /**
+ * @property mouseXY
+ * @description The XY coords of the mousemove
+ * @type {Array}
+ */
+ mouseXY: null,
+ /**
+ * @property region
+ * @description A region object associated with this drag, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @private
+ * @method _handleMouseUp
+ * @description Handler for the mouseup DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseUp: function(ev) {
+ this._fixIEMouseUp();
+ if (DDM.activeDrag) {
+ DDM._end();
+ }
+ },
+ /**
+ * @private
+ * @method _fixDragStart
+ * @description The function we use as the ondragstart handler when we start a drag in Internet Explorer. This keeps IE from blowing up on images as drag handles.
+ */
+ _fixDragStart: function(e) {
+ e.preventDefault();
+ },
+ /**
+ * @private
+ * @method _ieSelectFix
+ * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
+ */
+ _ieSelectFix: function() {
+ return false;
+ },
+ /**
+ * @private
+ * @property _ieSelectBack
+ * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
+ */
+ _ieSelectBack: null,
+ /**
+ * @private
+ * @method _fixIEMouseDown
+ * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
+ */
+ _fixIEMouseDown: function() {
+ if (Y.UA.ie) {
+ this._ieSelectBack = Y.config.doc.body.onselectstart;
+ Y.config.doc.body.onselectstart = this._ieSelectFix;
+ }
+ },
+ /**
+ * @private
+ * @method _fixIEMouseUp
+ * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
+ */
+ _fixIEMouseUp: function() {
+ if (Y.UA.ie) {
+ Y.config.doc.body.onselectstart = this._ieSelectBack;
+ }
+ },
+ /**
+ * @private
+ * @method _handleMouseDownEvent
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _handleMouseDownEvent: function(ev) {
+ this.fire(EV_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @private
+ * @method _defMouseDownFn
+ * @description Handler for the mousedown DOM event
+ * @param {Event.Facade}
+ */
+ _defMouseDownFn: function(e) {
+ var ev = e.ev;
+ this._dragThreshMet = false;
+ this._ev_md = ev;
+
+ if (this.get('primaryButtonOnly') && ev.button > 1) {
+ return false;
+ }
+ if (this.validClick(ev)) {
+ this._fixIEMouseDown();
+ ev.halt();
+ this._setStartPosition([ev.pageX, ev.pageY]);
+
+ DDM.activeDrag = this;
+
+ this._clickTimeout = Y.later(this.get('clickTimeThresh'), this, this._timeoutCheck);
+ }
+ this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
+ },
+ /**
+ * @method validClick
+ * @description Method first checks to see if we have handles, if so it validates the click against the handle. Then if it finds a valid handle, it checks it against the invalid handles list. Returns true if a good handle was used, false otherwise.
+ * @param {Event.Facade}
+ * @return {Boolean}
+ */
+ validClick: function(ev) {
+ var r = false, n = false,
+ tar = ev.target,
+ hTest = null,
+ els = null,
+ set = false;
+ if (this._handles) {
+ Y.each(this._handles, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *') && !hTest) {
+ hTest = n;
+ r = true;
+ }
+ }
+ });
+ } else {
+ n = this.get(NODE)
+ if (n.contains(tar) || n.compareTo(tar)) {
+ r = true;
+ }
+ }
+ if (r) {
+ if (this._invalids) {
+ Y.each(this._invalids, function(i, n) {
+ if (Y.Lang.isString(n)) {
+ //Am I this or am I inside this
+ if (tar.test(n + ', ' + n + ' *')) {
+ r = false;
+ }
+ }
+ });
+ }
+ }
+ if (r) {
+ if (hTest) {
+ els = ev.currentTarget.queryAll(hTest);
+ set = false;
+ els.each(function(n, i) {
+ if ((n.contains(tar) || n.compareTo(tar)) && !set) {
+ set = true;
+ this.set('activeHandle', n);
+ }
+ }, this);
+ } else {
+ this.set('activeHandle', this.get(NODE));
+ }
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _setStartPosition
+ * @description Sets the current position of the Element and calculates the offset
+ * @param {Array} xy The XY coords to set the position to.
+ */
+ _setStartPosition: function(xy) {
+ this.startXY = xy;
+
+ this.nodeXY = this.lastXY = this.realXY = this.get(NODE).getXY();
+
+ if (this.get('offsetNode')) {
+ this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
+ } else {
+ this.deltaXY = [0, 0];
+ }
+ },
+ /**
+ * @private
+ * @method _timeoutCheck
+ * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
+ */
+ _timeoutCheck: function() {
+ if (!this.get('lock') && !this._dragThreshMet) {
+ this._fromTimeout = this._dragThreshMet = true;
+ this.start();
+ this._alignNode([this._ev_md.pageX, this._ev_md.pageY], true);
+ }
+ },
+ /**
+ * @method removeHandle
+ * @description Remove a Selector added by addHandle
+ * @param {String} str The selector for the handle to be removed.
+ * @return {Self}
+ * @chainable
+ */
+ removeHandle: function(str) {
+ if (this._handles[str]) {
+ delete this._handles[str];
+ this.fire(EV_REMOVE_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addHandle
+ * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
+ * @param {String} str The selector to test for a valid handle. Must be a child of the element.
+ * @return {Self}
+ * @chainable
+ */
+ addHandle: function(str) {
+ if (!this._handles) {
+ this._handles = {};
+ }
+ if (Y.Lang.isString(str)) {
+ this._handles[str] = true;
+ this.fire(EV_ADD_HANDLE, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method removeInvalid
+ * @description Remove an invalid handle added by addInvalid
+ * @param {String} str The invalid handle to remove from the internal list.
+ * @return {Self}
+ * @chainable
+ */
+ removeInvalid: function(str) {
+ if (this._invalids[str]) {
+ this._invalids[str] = null;
+ delete this._invalids[str];
+ this.fire(EV_REMOVE_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @method addInvalid
+ * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
+ * @param {String} str The selector to test against to determine if this is an invalid drag handle.
+ * @return {Self}
+ * @chainable
+ */
+ addInvalid: function(str) {
+ if (Y.Lang.isString(str)) {
+ this._invalids[str] = true;
+ this.fire(EV_ADD_INVALID, { handle: str });
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Internal init handler
+ */
+ initializer: function() {
+ this.get(NODE).dd = this;
+
+ if (!this.get(NODE).get('id')) {
+ var id = Y.stamp(this.get(NODE));
+ this.get(NODE).set('id', id);
+ }
+
+ this.actXY = [];
+
+ this._invalids = Y.clone(this._invalidsDefault, true);
+
+ this._createEvents();
+
+ if (!this.get(DRAG_NODE)) {
+ this.set(DRAG_NODE, this.get(NODE));
+ }
+
+ //Fix for #2528096
+ //Don't prep the DD instance until all plugins are loaded.
+ this.on('initializedChange', Y.bind(this._prep, this));
+
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method _prep
+ * @description Attach event listners and add classname
+ */
+ _prep: function() {
+ this._dragThreshMet = false;
+ var node = this.get(NODE);
+ node.addClass(DDM.CSS_PREFIX + '-draggable');
+ node.on(MOUSE_DOWN, Y.bind(this._handleMouseDownEvent, this));
+ node.on(MOUSE_UP, Y.bind(this._handleMouseUp, this));
+ node.on(DRAG_START, Y.bind(this._fixDragStart, this));
+ },
+ /**
+ * @private
+ * @method _unprep
+ * @description Detach event listeners and remove classname
+ */
+ _unprep: function() {
+ var node = this.get(NODE);
+ node.removeClass(DDM.CSS_PREFIX + '-draggable');
+ node.detachAll();
+ },
+ /**
+ * @method start
+ * @description Starts the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ start: function() {
+ if (!this.get('lock') && !this.get(DRAGGING)) {
+ var node = this.get(NODE), ow = node.get(OFFSET_WIDTH), oh = node.get(OFFSET_HEIGHT);
+ this._startTime = (new Date()).getTime();
+
+ DDM._start();
+ node.addClass(DDM.CSS_PREFIX + '-dragging');
+ this.fire(EV_START, {
+ pageX: this.nodeXY[0],
+ pageY: this.nodeXY[1],
+ startTime: this._startTime
+ });
+ var xy = this.nodeXY;
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + ow,
+ bottom: xy[1] + oh,
+ left: xy[0]
+ };
+ this.set(DRAGGING, true);
+ }
+ return this;
+ },
+ /**
+ * @method end
+ * @description Ends the drag operation
+ * @return {Self}
+ * @chainable
+ */
+ end: function() {
+ this._endTime = (new Date()).getTime();
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._dragThreshMet = false;
+ this._fromTimeout = false;
+ if (!this.get('lock') && this.get(DRAGGING)) {
+ this.fire(EV_END, {
+ pageX: this.lastXY[0],
+ pageY: this.lastXY[1],
+ startTime: this._startTime,
+ endTime: this._endTime
+ });
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
+ this.set(DRAGGING, false);
+ this.deltaXY = [0, 0];
+
+ return this;
+ },
+ /**
+ * @private
+ * @method _prevEndFn
+ * @description Handler for preventing the drag:end event. It will reset the node back to it's start position
+ */
+ _prevEndFn: function(e) {
+ //Bug #1852577
+ this.get(DRAG_NODE).setXY(this.nodeXY);
+ },
+ /**
+ * @private
+ * @method _align
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Array} xy The xy coords to align with.
+ */
+ _align: function(xy) {
+ this.fire(EV_ALIGN, {pageX: xy[0], pageY: xy[1] });
+ },
+ /**
+ * @private
+ * @method _defAlignFn
+ * @description Calculates the offsets and set's the XY that the element will move to.
+ * @param {Event.Facade} e The drag:align event.
+ */
+ _defAlignFn: function(e) {
+ this.actXY = [e.pageX - this.deltaXY[0], e.pageY - this.deltaXY[1]];
+ },
+ /**
+ * @private
+ * @method _alignNode
+ * @description This method performs the alignment before the element move.
+ * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
+ */
+ _alignNode: function(eXY) {
+ this._align(eXY);
+ this._moveNode();
+ },
+ /**
+ * @private
+ * @method _moveNode
+ * @description This method performs the actual element move.
+ */
+ _moveNode: function(scroll) {
+ //if (!this.get(DRAGGING)) {
+ // return;
+ //}
+ var diffXY = [], diffXY2 = [], startXY = this.nodeXY, xy = this.actXY;
+
+ diffXY[0] = (xy[0] - this.lastXY[0]);
+ diffXY[1] = (xy[1] - this.lastXY[1]);
+
+ diffXY2[0] = (xy[0] - this.nodeXY[0]);
+ diffXY2[1] = (xy[1] - this.nodeXY[1]);
+
+
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + this.get(DRAG_NODE).get(OFFSET_WIDTH),
+ bottom: xy[1] + this.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ left: xy[0]
+ };
+
+ this.fire(EV_DRAG, {
+ pageX: xy[0],
+ pageY: xy[1],
+ scroll: scroll,
+ info: {
+ start: startXY,
+ xy: xy,
+ delta: diffXY,
+ offset: diffXY2
+ }
+ });
+
+ this.lastXY = xy;
+ },
+ /**
+ * @private
+ * @method _defDragFn
+ * @description Default function for drag:drag. Fired from _moveNode.
+ * @param {Event.Facade} ev The drag:drag event
+ */
+ _defDragFn: function(e) {
+ if (this.get('move')) {
+ if (e.scroll) {
+ e.scroll.node.set('scrollTop', e.scroll.top);
+ e.scroll.node.set('scrollLeft', e.scroll.left);
+ }
+ this.get(DRAG_NODE).setXY([e.pageX, e.pageY]);
+ this.realXY = [e.pageX, e.pageY];
+ }
+ },
+ /**
+ * @private
+ * @method _move
+ * @description Fired from DragDropMgr (DDM) on mousemove.
+ * @param {Event.Facade} ev The mousemove DOM event
+ */
+ _move: function(ev) {
+ if (this.get('lock')) {
+ return false;
+ } else {
+ this.mouseXY = [ev.pageX, ev.pageY];
+ if (!this._dragThreshMet) {
+ var diffX = Math.abs(this.startXY[0] - ev.pageX),
+ diffY = Math.abs(this.startXY[1] - ev.pageY);
+ if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
+ this._dragThreshMet = true;
+ this.start();
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ } else {
+ if (this._clickTimeout) {
+ this._clickTimeout.cancel();
+ }
+ this._alignNode([ev.pageX, ev.pageY]);
+ }
+ }
+ },
+ /**
+ * @method stopDrag
+ * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
+ * @return {Self}
+ * @chainable
+ */
+ stopDrag: function() {
+ if (this.get(DRAGGING)) {
+ DDM._end();
+ }
+ return this;
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ this._unprep();
+ this.detachAll();
+ if (this.target) {
+ this.target.destroy();
+ }
+ DDM._unregDrag(this);
+ }
+ });
+ Y.namespace('DD');
+ Y.DD.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-base'], skinnable:false});
+YUI.add('dd-proxy', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-proxy
+ */
+ /**
+ * This plugin for dd-drag is for creating a proxy drag node, instead of dragging the original node.
+ * @class DDProxy
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+ var DDM = Y.DD.DDM,
+ NODE = 'node',
+ DRAG_NODE = 'dragNode',
+ HOST = 'host',
+ TRUE = true;
+
+ var P = function(config) {
+ P.superclass.constructor.apply(this, arguments);
+ };
+
+ P.NAME = 'DDProxy';
+ /**
+ * @property proxy
+ * @description The Proxy instance will be placed on the Drag instance under the proxy namespace.
+ * @type {String}
+ */
+ P.NS = 'proxy';
+
+ P.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute moveOnEnd
+ * @description Move the original node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ moveOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute hideOnEnd
+ * @description Hide the drag node at the end of the drag. Default: true
+ * @type Boolean
+ */
+ hideOnEnd: {
+ value: TRUE
+ },
+ /**
+ * @attribute resizeFrame
+ * @description Make the Proxy node assume the size of the original node. Default: true
+ * @type Boolean
+ */
+ resizeFrame: {
+ value: TRUE
+ },
+ /**
+ * @attribute positionProxy
+ * @description Make the Proxy node appear in the same place as the original node. Default: true
+ * @type Boolean
+ */
+ positionProxy: {
+ value: TRUE
+ },
+ /**
+ * @attribute borderStyle
+ * @description The default border style for the border of the proxy. Default: 1px solid #808080
+ * @type Boolean
+ */
+ borderStyle: {
+ value: '1px solid #808080'
+ }
+ };
+
+ var proto = {
+ /**
+ * @private
+ * @property _hands
+ * @description Holds the event handles for setting the proxy
+ */
+ _hands: null,
+ /**
+ * @private
+ * @method _init
+ * @description Handler for the proxy config attribute
+ */
+ _init: function() {
+ if (!DDM._proxy) {
+ Y.on('domready', Y.bind(this._init, this));
+ return;
+ }
+ if (!this._hands) {
+ this._hands = [];
+ }
+ var i, h, h1, host = this.get(HOST), dnode = host.get(DRAG_NODE);
+ if (dnode.compareTo(host.get(NODE))) {
+ if (DDM._proxy) {
+ host.set(DRAG_NODE, DDM._proxy);
+ }
+ }
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ h = DDM.on('ddm:start', Y.bind(function() {
+ if (DDM.activeDrag === host) {
+ DDM._setFrame(host);
+ }
+ }, this));
+ h1 = DDM.on('ddm:end', Y.bind(function() {
+ if (host.get('dragging')) {
+ if (this.get('moveOnEnd')) {
+ host.get(NODE).setXY(host.lastXY);
+ }
+ if (this.get('hideOnEnd')) {
+ host.get(DRAG_NODE).setStyle('display', 'none');
+ }
+ }
+ }, this));
+ this._hands = [h, h1];
+ },
+ initializer: function() {
+ this._init();
+ },
+ destructor: function() {
+ var host = this.get(HOST);
+ Y.each(this._hands, function(v) {
+ v.detach();
+ });
+ host.set(DRAG_NODE, host.get(NODE));
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(P, Y.Base, proto);
+ Y.Plugin.DDProxy = P;
+
+ //Add a couple of methods to the DDM
+ Y.mix(DDM, {
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _createFrame
+ * @description Create the proxy element if it doesn't already exist and set the DD.DDM._proxy value
+ */
+ _createFrame: function() {
+ if (!DDM._proxy) {
+ DDM._proxy = TRUE;
+
+ var p = Y.Node.create('<div></div>'),
+ b = Y.Node.get('body');
+
+ p.setStyles({
+ position: 'absolute',
+ display: 'none',
+ zIndex: '999',
+ top: '-999px',
+ left: '-999px'
+ });
+
+ b.insertBefore(p, b.get('firstChild'));
+ p.set('id', Y.stamp(p));
+ p.addClass(DDM.CSS_PREFIX + '-proxy');
+ DDM._proxy = p;
+ }
+ },
+ /**
+ * @private
+ * @for DDM
+ * @namespace DD
+ * @method _setFrame
+ * @description If resizeProxy is set to true (default) it will resize the proxy element to match the size of the Drag Element.
+ * If positionProxy is set to true (default) it will position the proxy element in the same location as the Drag Element.
+ */
+ _setFrame: function(drag) {
+ var n = drag.get(NODE), d = drag.get(DRAG_NODE), ah, cur = 'auto';
+ if (drag.proxy.get('resizeFrame')) {
+ DDM._proxy.setStyles({
+ height: n.get('offsetHeight') + 'px',
+ width: n.get('offsetWidth') + 'px'
+ });
+ }
+
+ ah = DDM.activeDrag.get('activeHandle');
+ if (ah) {
+ cur = ah.getStyle('cursor');
+ }
+ if (cur == 'auto') {
+ cur = DDM.get('dragCursor');
+ }
+
+
+ d.setStyles({
+ visibility: 'hidden',
+ display: 'block',
+ cursor: cur,
+ border: drag.proxy.get('borderStyle')
+ });
+
+
+
+ if (drag.proxy.get('positionProxy')) {
+ d.setXY(drag.nodeXY);
+ }
+ d.setStyle('visibility', 'visible');
+ }
+ });
+
+ //Create the frame when DOM is ready
+ Y.on('domready', Y.bind(DDM._createFrame, DDM));
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm', 'dd-drag'], skinnable:false});
+YUI.add('dd-constrain', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-constrain
+ */
+ /**
+ * This is a plugin for the dd-drag module to add the constraining methods to it. It supports constraining to a renodenode or viewport. It anode* supports tick based moves and XY axis constraints.
+ * @class DragConstrained
+ * @extends Base
+ * @constructor
+ * @namespace Plugin
+ */
+
+ var DRAG_NODE = 'dragNode',
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ HOST = 'host',
+ CON_2_REGION = 'constrain2region',
+ CON_2_NODE = 'constrain2node',
+ TICK_X_ARRAY = 'tickXArray',
+ TICK_Y_ARRAY = 'tickYArray',
+ DDM = Y.DD.DDM,
+ TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+ proto = null;
+
+ var C = function(config) {
+ C.superclass.constructor.apply(this, arguments);
+ };
+
+ C.NAME = 'DragConstrained';
+ /**
+ * @property con
+ * @description The Constrained instance will be placed on the Drag instance under the con namespace.
+ * @type {String}
+ */
+ C.NS = 'con';
+
+ C.ATTRS = {
+ host: {
+ },
+ /**
+ * @attribute stickX
+ * @description Stick the drag movement to the X-Axis. Default: false
+ * @type Boolean
+ */
+ stickX: {
+ value: false
+ },
+ /**
+ * @attribute stickY
+ * @description Stick the drag movement to the Y-Axis
+ * @type Boolean
+ */
+ stickY: {
+ value: false
+ },
+ /**
+ * @attribute tickX
+ * @description The X tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickX: {
+ value: false
+ },
+ /**
+ * @attribute tickY
+ * @description The Y tick offset the drag node should snap to on each drag move. False for no ticks. Default: false
+ * @type Number/false
+ */
+ tickY: {
+ value: false
+ },
+ /**
+ * @attribute tickXArray
+ * @description An array of page coordinates to use as X ticks for drag movement.
+ * @type Array
+ */
+ tickXArray: {
+ value: false
+ },
+ /**
+ * @attribute tickYArray
+ * @description An array of page coordinates to use as Y ticks for drag movement.
+ * @type Array
+ */
+ tickYArray: {
+ value: false
+ },
+ /**
+ * @attribute constrain2region
+ * @description An Object Literal containing a valid region (top, right, bottom, left) of page positions to constrain the drag node to.
+ * @type Object
+ */
+ constrain2region: {
+ value: false,
+ getter: function(r) {
+ if (Y.Lang.isObject(r)) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ },
+ setter: function (r) {
+ if (Y.Lang.isObject(r)) {
+ if (Y.Lang.isNumber(r[TOP]) && Y.Lang.isNumber(r[RIGHT]) && Y.Lang.isNumber(r[LEFT]) && Y.Lang.isNumber(r[BOTTOM])) {
+ var o = {};
+ Y.mix(o, r);
+ return o;
+ } else {
+ return false;
+ }
+ } else if (r !== false) {
+ return false;
+ }
+ return r;
+ }
+ },
+ /**
+ * @attribute gutter
+ * @description CSS style string for the gutter of a region (supports negative values): '5 0' (sets top and bottom to 5px, left and right to 0px), '1 2 3 4' (top 1px, right 2px, bottom 3px, left 4px)
+ * @type String
+ */
+ gutter: {
+ value: '0',
+ setter: function(gutter) {
+ return Y.DD.DDM.cssSizestoObject(gutter);
+ }
+ },
+ /**
+ * @attribute constrain2node
+ * @description Will attempt to constrain the drag node to the boundaries of this node.
+ * @type Object
+ */
+ constrain2node: {
+ value: false,
+ setter: function(n) {
+ if (!this.get(CON_2_REGION)) {
+ var node = Y.Node.get(n);
+ if (node) {
+ return node;
+ }
+ } else if (this.get(CON_2_REGION) !== false) {
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute constrain2view
+ * @description Will attempt to constrain the drag node to the boundaries of the viewport region.
+ * @type Object
+ */
+ constrain2view: {
+ value: false
+ }
+ };
+
+ proto = {
+ initializer: function() {
+ this.get(HOST).on('drag:start', Y.bind(this._handleStart, this));
+ this.get(HOST).after('drag:align', Y.bind(this.align, this));
+ },
+ /**
+ * @private
+ * @method _handleStart
+ * @description Fires on drag:start and clears the _regionCache
+ */
+ _handleStart: function() {
+ this._regionCache = null;
+ },
+ /**
+ * @private
+ * @property _regionCache
+ * @description Store a cache of the region that we are constraining to
+ * @type Object
+ */
+ _regionCache: null,
+ /**
+ * @private
+ * @method _cacheRegion
+ * @description Get's the region and caches it, called from window.resize and when the cache is null
+ */
+ _cacheRegion: function() {
+ this._regionCache = this.get(CON_2_NODE).get('region');
+ },
+ /**
+ * @method getRegion
+ * @description Get the active region: viewport, node, custom region
+ * @param {Boolean} inc Include the node's height and width
+ * @return {Object}
+ */
+ getRegion: function(inc) {
+ var r = {}, oh = null, ow = null,
+ g = this.get('gutter'),
+ host = this.get(HOST);
+
+ if (this.get(CON_2_NODE)) {
+ if (!this._regionCache) {
+ Y.on('resize', Y.bind(this._cacheRegion, this), window);
+ this._cacheRegion();
+ }
+ r = Y.clone(this._regionCache);
+ } else if (this.get(CON_2_REGION)) {
+ r = this.get(CON_2_REGION);
+ } else if (this.get('constrain2view')) {
+ r = host.get(DRAG_NODE).get('viewportRegion');
+ } else {
+ return false;
+ }
+
+ Y.each(g, function(i, n) {
+ if ((n == RIGHT) || (n == BOTTOM)) {
+ r[n] -= i;
+ } else {
+ r[n] += i;
+ }
+ });
+ if (inc) {
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT);
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+ r[RIGHT] = r[RIGHT] - ow;
+ r[BOTTOM] = r[BOTTOM] - oh;
+ }
+ return r;
+ },
+ /**
+ * @private
+ * @method _checkRegion
+ * @description Check if xy is inside a given region, if not change to it be inside.
+ * @param {Array} _xy The XY to check if it's in the current region, if it isn't inside the region, it will reset the xy array to be inside the region.
+ * @return {Array} The new XY that is inside the region
+ */
+ _checkRegion: function(_xy) {
+ var oxy = _xy,
+ r = this.getRegion(),
+ host = this.get(HOST),
+ oh = host.get(DRAG_NODE).get(OFFSET_HEIGHT),
+ ow = host.get(DRAG_NODE).get(OFFSET_WIDTH);
+
+ if (oxy[1] > (r[BOTTOM] - oh)) {
+ _xy[1] = (r[BOTTOM] - oh);
+ }
+ if (r[TOP] > oxy[1]) {
+ _xy[1] = r[TOP];
+
+ }
+ if (oxy[0] > (r[RIGHT] - ow)) {
+ _xy[0] = (r[RIGHT] - ow);
+ }
+ if (r[LEFT] > oxy[0]) {
+ _xy[0] = r[LEFT];
+ }
+
+ return _xy;
+ },
+ /**
+ * @method inRegion
+ * @description Checks if the XY passed or the dragNode is inside the active region.
+ * @param {Array} xy Optional XY to check, if not supplied this.get('dragNode').getXY() is used.
+ * @return {Boolean} True if the XY is inside the region, false otherwise.
+ */
+ inRegion: function(xy) {
+ xy = xy || this.get(HOST).get(DRAG_NODE).getXY();
+
+ var _xy = this._checkRegion([xy[0], xy[1]]),
+ inside = false;
+ if ((xy[0] === _xy[0]) && (xy[1] === _xy[1])) {
+ inside = true;
+ }
+ return inside;
+ },
+ /**
+ * @method align
+ * @description Modifies the Drag.actXY method from the after drag:align event. This is where the constraining happens.
+ */
+ align: function() {
+ var host = this.get(HOST),
+ _xy = host.actXY,
+ r = this.getRegion(true);
+
+ if (this.get('stickX')) {
+ _xy[1] = (host.startXY[1] - host.deltaXY[1]);
+ }
+ if (this.get('stickY')) {
+ _xy[0] = (host.startXY[0] - host.deltaXY[0]);
+ }
+
+ if (r) {
+ _xy = this._checkRegion(_xy);
+ }
+
+ _xy = this._checkTicks(_xy, r);
+ host.actXY = _xy;
+ },
+ /**
+ * @private
+ * @method _checkTicks
+ * @description This method delegates the proper helper method for tick calculations
+ * @param {Array} xy The XY coords for the Drag
+ * @param {Object} r The optional region that we are bound to.
+ * @return {Array} The calced XY coords
+ */
+ _checkTicks: function(xy, r) {
+ var host = this.get(HOST),
+ lx = (host.startXY[0] - host.deltaXY[0]),
+ ly = (host.startXY[1] - host.deltaXY[1]),
+ xt = this.get('tickX'),
+ yt = this.get('tickY');
+ if (xt && !this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTicks(xy[0], lx, xt, r[LEFT], r[RIGHT]);
+ }
+ if (yt && !this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTicks(xy[1], ly, yt, r[TOP], r[BOTTOM]);
+ }
+ if (this.get(TICK_X_ARRAY)) {
+ xy[0] = DDM._calcTickArray(xy[0], this.get(TICK_X_ARRAY), r[LEFT], r[RIGHT]);
+ }
+ if (this.get(TICK_Y_ARRAY)) {
+ xy[1] = DDM._calcTickArray(xy[1], this.get(TICK_Y_ARRAY), r[TOP], r[BOTTOM]);
+ }
+
+ return xy;
+ }
+ };
+
+ Y.namespace('Plugin');
+ Y.extend(C, Y.Base, proto);
+ Y.Plugin.DDConstrained = C;
+
+ Y.mix(DDM, {
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTicks
+ * @description Helper method to calculate the tick offsets for a given position
+ * @param {Number} pos The current X or Y position
+ * @param {Number} start The start X or Y position
+ * @param {Number} tick The X or Y tick increment
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return {Number} The new position based on the tick calculation
+ */
+ _calcTicks: function(pos, start, tick, off1, off2) {
+ var ix = ((pos - start) / tick),
+ min = Math.floor(ix),
+ max = Math.ceil(ix);
+ if ((min !== 0) || (max !== 0)) {
+ if ((ix >= min) && (ix <= max)) {
+ pos = (start + (tick * min));
+ if (off1 && off2) {
+ if (pos < off1) {
+ pos = (start + (tick * (min + 1)));
+ }
+ if (pos > off2) {
+ pos = (start + (tick * (min - 1)));
+ }
+ }
+ }
+ }
+ return pos;
+ },
+ /**
+ * @for DDM
+ * @namespace DD
+ * @private
+ * @method _calcTickArray
+ * @description This method is used with the tickXArray and tickYArray config options
+ * @param {Number} pos The current X or Y position
+ * @param {Number} ticks The array containing our custom tick positions.
+ * @param {Number} off1 The min offset that we can't pass (region)
+ * @param {Number} off2 The max offset that we can't pass (region)
+ * @return The tick position
+ */
+ _calcTickArray: function(pos, ticks, off1, off2) {
+ var i = 0, len = ticks.length, next = 0,
+ diff1, diff2, ret;
+
+ if (!ticks || (ticks.length === 0)) {
+ return pos;
+ } else if (ticks[0] >= pos) {
+ return ticks[0];
+ } else {
+ for (i = 0; i < len; i++) {
+ next = (i + 1);
+ if (ticks[next] && ticks[next] >= pos) {
+ diff1 = pos - ticks[i];
+ diff2 = ticks[next] - pos;
+ ret = (diff2 > diff1) ? ticks[i] : ticks[next];
+ if (off1 && off2) {
+ if (ret > off2) {
+ if (ticks[i]) {
+ ret = ticks[i];
+ } else {
+ ret = ticks[len - 1];
+ }
+ }
+ }
+ return ret;
+ }
+
+ }
+ return ticks[ticks.length - 1];
+ }
+ }
+ });
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drag'], skinnable:false});
+YUI.add('dd-scroll', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-scroll
+ */
+ /**
+ * This class is the base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
+ * This class should not be called on it's own, it's designed to be a plugin.
+ * @class Scroll
+ * @extends Base
+ * @namespace DD
+ * @constructor
+ */
+
+ var S = function() {
+ S.superclass.constructor.apply(this, arguments);
+
+ },
+ HOST = 'host',
+ BUFFER = 'buffer',
+ PARENT_SCROLL = 'parentScroll',
+ WINDOW_SCROLL = 'windowScroll',
+ SCROLL_TOP = 'scrollTop',
+ SCROLL_LEFT = 'scrollLeft',
+ OFFSET_WIDTH = 'offsetWidth',
+ OFFSET_HEIGHT = 'offsetHeight';
+
+
+ S.ATTRS = {
+ /**
+ * @attribute parentScroll
+ * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
+ * @type Node
+ */
+ parentScroll: {
+ value: false,
+ setter: function(node) {
+ if (node) {
+ return node;
+ }
+ return false;
+ }
+ },
+ /**
+ * @attribute buffer
+ * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
+ * @type Number
+ */
+ buffer: {
+ value: 30
+ },
+ /**
+ * @attribute scrollDelay
+ * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
+ * @type Number
+ */
+ scrollDelay: {
+ value: 235
+ },
+ /**
+ * @attribute host
+ * @description The host we are plugged into.
+ * @type Object
+ */
+ host: {
+ value: null
+ },
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: false
+ * @type Boolean
+ */
+ windowScroll: {
+ value: false
+ },
+ /**
+ * @attribute vertical
+ * @description Allow vertical scrolling, default: true.
+ * @type Boolean
+ */
+ vertical: {
+ value: true
+ },
+ /**
+ * @attribute horizontal
+ * @description Allow horizontal scrolling, default: true.
+ * @type Boolean
+ */
+ horizontal: {
+ value: true
+ }
+ };
+
+ Y.extend(S, Y.Base, {
+ /**
+ * @private
+ * @property _scrolling
+ * @description Tells if we are actively scrolling or not.
+ * @type Boolean
+ */
+ _scrolling: null,
+ /**
+ * @private
+ * @property _vpRegionCache
+ * @description Cache of the Viewport dims.
+ * @type Object
+ */
+ _vpRegionCache: null,
+ /**
+ * @private
+ * @property _dimCache
+ * @description Cache of the dragNode dims.
+ * @type Object
+ */
+ _dimCache: null,
+ /**
+ * @private
+ * @property _scrollTimer
+ * @description Holder for the Timer object returned from Y.later.
+ * @type {Y.later}
+ */
+ _scrollTimer: null,
+ /**
+ * @private
+ * @method _getVPRegion
+ * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
+ */
+ _getVPRegion: function() {
+ var r = {};
+ //if (!this._vpRegionCache) {
+ var n = this.get(PARENT_SCROLL),
+ b = this.get(BUFFER),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ((ws) ? [] : n.getXY()),
+ w = ((ws) ? 'winWidth' : OFFSET_WIDTH),
+ h = ((ws) ? 'winHeight' : OFFSET_HEIGHT),
+ t = ((ws) ? n.get(SCROLL_TOP) : xy[1]),
+ l = ((ws) ? n.get(SCROLL_LEFT) : xy[0]);
+
+ r = {
+ top: t + b,
+ right: (n.get(w) + l) - b,
+ bottom: (n.get(h) + t) - b,
+ left: l + b
+ };
+ this._vpRegionCache = r;
+ //} else {
+ // r = this._vpRegionCache;
+ //}
+ return r;
+ },
+ initializer: function() {
+ var h = this.get(HOST);
+ h.after('drag:start', Y.bind(this.start, this));
+ h.after('drag:end', Y.bind(this.end, this));
+ h.on('drag:align', Y.bind(this.align, this));
+
+ //TODO - This doesn't work yet??
+ Y.get(window).on('scroll', Y.bind(function() {
+ this._vpRegionCache = null;
+ }, this));
+ },
+ /**
+ * @private
+ * @method _checkWinScroll
+ * @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
+ * @param {Boolean} move Should we move the window. From Y.later
+ */
+ _checkWinScroll: function(move) {
+ var r = this._getVPRegion(),
+ ho = this.get(HOST),
+ ws = this.get(WINDOW_SCROLL),
+ xy = ho.lastXY,
+ scroll = false,
+ b = this.get(BUFFER),
+ win = this.get(PARENT_SCROLL),
+ sTop = win.get(SCROLL_TOP),
+ sLeft = win.get(SCROLL_LEFT),
+ w = this._dimCache.w,
+ h = this._dimCache.h,
+ bottom = xy[1] + h,
+ top = xy[1],
+ right = xy[0] + w,
+ left = xy[0],
+ nt = top,
+ nl = left,
+ st = sTop,
+ sl = sLeft;
+
+ if (this.get('horizontal')) {
+ if (left <= r.left) {
+ scroll = true;
+ nl = xy[0] - ((ws) ? b : 0);
+ sl = sLeft - b;
+ }
+ if (right >= r.right) {
+ scroll = true;
+ nl = xy[0] + ((ws) ? b : 0);
+ sl = sLeft + b;
+ }
+ }
+ if (this.get('vertical')) {
+ if (bottom >= r.bottom) {
+ scroll = true;
+ nt = xy[1] + ((ws) ? b : 0);
+ st = sTop + b;
+
+ }
+ if (top <= r.top) {
+ scroll = true;
+ nt = xy[1] - ((ws) ? b : 0);
+ st = sTop - b;
+ }
+ }
+
+ if (st < 0) {
+ st = 0;
+ nt = xy[1];
+ }
+
+ if (sl < 0) {
+ sl = 0;
+ nl = xy[0];
+ }
+
+ if (nt < 0) {
+ nt = xy[1];
+ }
+ if (nl < 0) {
+ nl = xy[0];
+ }
+ if (move) {
+ ho.actXY = [nl, nt];
+ ho._moveNode({ node: win, top: st, left: sl});
+ if (!st && !sl) {
+ this._cancelScroll();
+ }
+ } else {
+ if (scroll) {
+ this._initScroll();
+ } else {
+ this._cancelScroll();
+ }
+ }
+ },
+ /**
+ * @private
+ * @method _initScroll
+ * @description Cancel a previous scroll timer and init a new one.
+ */
+ _initScroll: function() {
+ this._cancelScroll();
+ this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
+
+ },
+ /**
+ * @private
+ * @method _cancelScroll
+ * @description Cancel a currently running scroll timer.
+ */
+ _cancelScroll: function() {
+ this._scrolling = false;
+ if (this._scrollTimer) {
+ this._scrollTimer.cancel();
+ delete this._scrollTimer;
+ }
+ },
+ /**
+ * @method align
+ * @description Called from the drag:align event to determine if we need to scroll.
+ */
+ align: function(e) {
+ if (this._scrolling) {
+ this._cancelScroll();
+ e.preventDefault();
+ }
+ if (!this._scrolling) {
+ this._checkWinScroll();
+ }
+ },
+ /**
+ * @private
+ * @method _setDimCache
+ * @description Set the cache of the dragNode dims.
+ */
+ _setDimCache: function() {
+ var node = this.get(HOST).get('dragNode');
+ this._dimCache = {
+ h: node.get(OFFSET_HEIGHT),
+ w: node.get(OFFSET_WIDTH)
+ };
+ },
+ /**
+ * @method start
+ * @description Called from the drag:start event
+ */
+ start: function() {
+ this._setDimCache();
+ },
+ /**
+ * @method end
+ * @description Called from the drag:end event
+ */
+ end: function(xy) {
+ this._dimCache = null;
+ this._cancelScroll();
+ },
+ /**
+ * @method toString
+ * @description General toString method for logging
+ * @return String name for the object
+ */
+ toString: function() {
+ return S.NAME + ' #' + this.get('node').get('id');
+ }
+ });
+
+ Y.namespace('Plugin');
+
+
+ /**
+ * Extends the Scroll class to make the window scroll while dragging.
+ * @class DDWindowScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var WS = function() {
+ WS.superclass.constructor.apply(this, arguments);
+ };
+ WS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute windowScroll
+ * @description Turn on window scroll support, default: true
+ * @type Boolean
+ */
+ windowScroll: {
+ value: true,
+ setter: function(scroll) {
+ if (scroll) {
+ this.set(PARENT_SCROLL, Y.get(window));
+ }
+ return scroll;
+ }
+ }
+ });
+ Y.extend(WS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('windowScroll', this.get('windowScroll'));
+ }
+ });
+ WS.NAME = WS.NS = 'winscroll';
+ Y.Plugin.DDWinScroll = WS;
+
+
+ /**
+ * Extends the Scroll class to make a parent node scroll while dragging.
+ * @class DDNodeScroll
+ * @extends DD.Scroll
+ * @namespace Plugin
+ * @constructor
+ */
+ var NS = function() {
+ NS.superclass.constructor.apply(this, arguments);
+
+ };
+ NS.ATTRS = Y.merge(S.ATTRS, {
+ /**
+ * @attribute node
+ * @description The node we want to scroll. Used to set the internal parentScroll attribute.
+ * @type Node
+ */
+ node: {
+ value: false,
+ setter: function(node) {
+ var n = Y.get(node);
+ if (!n) {
+ if (node !== false) {
+ Y.error('DDNodeScroll: Invalid Node Given: ' + node);
+ }
+ } else {
+ n = n.item(0);
+ this.set(PARENT_SCROLL, n);
+ }
+ return n;
+ }
+ }
+ });
+ Y.extend(NS, S, {
+ //Shouldn't have to do this..
+ initializer: function() {
+ this.set('node', this.get('node'));
+ }
+ });
+ NS.NAME = NS.NS = 'nodescroll';
+ Y.Plugin.DDNodeScroll = NS;
+
+ Y.DD.Scroll = S;
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-proxy']});
+YUI.add('dd-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-plugin
+ */
+ /**
+ * This is a simple Drag plugin that can be attached to a Node via the plug method.
+ * @class Drag
+ * @extends DD.Drag
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drag = function(config) {
+ config.node = ((Y.Widget && config.host instanceof Y.Widget) ? config.host.get('boundingBox') : config.host);
+ Drag.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-plugin
+ * @type {String}
+ */
+ Drag.NAME = "dd-plugin";
+
+ /**
+ * @property NS
+ * @description The Drag instance will be placed on the Node instance under the dd namespace. It can be accessed via Node.dd;
+ * @type {String}
+ */
+ Drag.NS = "dd";
+
+
+ Y.extend(Drag, Y.DD.Drag);
+ Y.namespace('Plugin');
+ Y.Plugin.Drag = Drag;
+
+
+
+
+
+}, '3.0.0' ,{skinnable:false, requires:['dd-drag'], optional:['dd-constrain', 'dd-proxy']});
+YUI.add('dd-drop', function(Y) {
+
+
+ /**
+ * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
+ * @module dd
+ * @submodule dd-drop
+ */
+ /**
+ * This class provides the ability to create a Drop Target.
+ * @class Drop
+ * @extends Base
+ * @constructor
+ * @namespace DD
+ */
+
+ var NODE = 'node',
+ DDM = Y.DD.DDM,
+ OFFSET_HEIGHT = 'offsetHeight',
+ OFFSET_WIDTH = 'offsetWidth',
+ /**
+ * @event drop:over
+ * @description Fires when a drag element is over this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_OVER = 'drop:over',
+ /**
+ * @event drop:enter
+ * @description Fires when a drag element enters this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_ENTER = 'drop:enter',
+ /**
+ * @event drop:exit
+ * @description Fires when a drag element exits this target.
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+ EV_DROP_EXIT = 'drop:exit',
+
+ /**
+ * @event drop:hit
+ * @description Fires when a draggable node is dropped on this Drop Target. (Fired from dd-ddm-drop)
+ * @bubbles DDM
+ * @type {Event.Custom}
+ */
+
+
+ Drop = function() {
+ this._lazyAddAttrs = false;
+ Drop.superclass.constructor.apply(this, arguments);
+
+
+ //DD init speed up.
+ Y.on('domready', Y.bind(function() {
+ Y.later(100, this, this._createShim);
+ }, this));
+ DDM._regTarget(this);
+
+ /* TODO
+ if (Dom.getStyle(this.el, 'position') == 'fixed') {
+ Event.on(window, 'scroll', function() {
+ this.activateShim();
+ }, this, true);
+ }
+ */
+ };
+
+ Drop.NAME = 'drop';
+
+ Drop.ATTRS = {
+ /**
+ * @attribute node
+ * @description Y.Node instanace to use as the element to make a Drop Target
+ * @type Node
+ */
+ node: {
+ setter: function(node) {
+ var n = Y.Node.get(node);
+ if (!n) {
+ Y.error('DD.Drop: Invalid Node Given: ' + node);
+ }
+ return n;
+ }
+ },
+ /**
+ * @attribute groups
+ * @description Array of groups to add this drop into.
+ * @type Array
+ */
+ groups: {
+ value: ['default'],
+ setter: function(g) {
+ this._groups = {};
+ Y.each(g, function(v, k) {
+ this._groups[v] = true;
+ }, this);
+ return g;
+ }
+ },
+ /**
+ * @attribute padding
+ * @description CSS style padding to make the Drop Target bigger than the node.
+ * @type String
+ */
+ padding: {
+ value: '0',
+ setter: function(p) {
+ return DDM.cssSizestoObject(p);
+ }
+ },
+ /**
+ * @attribute lock
+ * @description Set to lock this drop element.
+ * @type Boolean
+ */
+ lock: {
+ value: false,
+ setter: function(lock) {
+ if (lock) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-locked');
+ } else {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-locked');
+ }
+ return lock;
+ }
+ },
+ /**
+ * @attribute bubbles
+ * @description Controls the default bubble parent for this Drop instance. Default: Y.DD.DDM. Set to false to disable bubbling.
+ * @type Object
+ */
+ bubbles: {
+ writeOnce: true,
+ value: Y.DD.DDM
+ }
+ };
+
+ Y.extend(Drop, Y.Base, {
+ /**
+ * @private
+ * @method _createEvents
+ * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
+ */
+ _createEvents: function() {
+
+ var ev = [
+ EV_DROP_OVER,
+ EV_DROP_ENTER,
+ EV_DROP_EXIT,
+ 'drop:hit'
+ ];
+
+ Y.each(ev, function(v, k) {
+ this.publish(v, {
+ type: v,
+ emitFacade: true,
+ preventable: false,
+ bubbles: true,
+ queuable: false,
+ prefix: 'drop'
+ });
+ }, this);
+
+ if (this.get('bubbles')) {
+ this.addTarget(this.get('bubbles'));
+ }
+
+ },
+ /**
+ * @private
+ * @property _valid
+ * @description Flag for determining if the target is valid in this operation.
+ * @type Boolean
+ */
+ _valid: null,
+ /**
+ * @private
+ * @property _groups
+ * @description The groups this target belongs to.
+ * @type Array
+ */
+ _groups: null,
+ /**
+ * @property shim
+ * @description Node reference to the targets shim
+ * @type {Object}
+ */
+ shim: null,
+ /**
+ * @property region
+ * @description A region object associated with this target, used for checking regions while dragging.
+ * @type Object
+ */
+ region: null,
+ /**
+ * @property overTarget
+ * @description This flag is tripped when a drag element is over this target.
+ * @type Boolean
+ */
+ overTarget: null,
+ /**
+ * @method inGroup
+ * @description Check if this target is in one of the supplied groups.
+ * @param {Array} groups The groups to check against
+ * @return Boolean
+ */
+ inGroup: function(groups) {
+ this._valid = false;
+ var ret = false;
+ Y.each(groups, function(v, k) {
+ if (this._groups[v]) {
+ ret = true;
+ this._valid = true;
+ }
+ }, this);
+ return ret;
+ },
+ /**
+ * @private
+ * @method initializer
+ * @description Private lifecycle method
+ */
+ initializer: function() {
+ //this._createEvents();
+ Y.later(100, this, this._createEvents);
+
+ var node = this.get(NODE), id;
+ if (!node.get('id')) {
+ id = Y.stamp(node);
+ node.set('id', id);
+ }
+ node.addClass(DDM.CSS_PREFIX + '-drop');
+ //Shouldn't have to do this..
+ this.set('groups', this.get('groups'));
+ },
+ /**
+ * @private
+ * @method destructor
+ * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
+ */
+ destructor: function() {
+ DDM._unregTarget(this);
+ if (this.shim) {
+ this.shim.detachAll();
+ this.shim.get('parentNode').removeChild(this.shim);
+ this.shim = null;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop');
+ this.detachAll();
+ },
+ /**
+ * @private
+ * @method _deactivateShim
+ * @description Removes classes from the target, resets some flags and sets the shims deactive position [-999, -999]
+ */
+ _deactivateShim: function() {
+ if (!this.shim) {
+ return false;
+ }
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ this.shim.setStyles({
+ top: '-999px',
+ left: '-999px',
+ zIndex: '1'
+ });
+ this.overTarget = false;
+ },
+ /**
+ * @private
+ * @method _activateShim
+ * @description Activates the shim and adds some interaction CSS classes
+ */
+ _activateShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ var node = this.get(NODE);
+ //TODO Visibility Check..
+ //if (this.inGroup(DDM.activeDrag.get('groups')) && this.get(NODE).isVisible()) {
+ if (this.inGroup(DDM.activeDrag.get('groups'))) {
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ DDM._addValid(this);
+ this.overTarget = false;
+ this.sizeShim();
+ } else {
+ DDM._removeValid(this);
+ node.removeClass(DDM.CSS_PREFIX + '-drop-active-valid');
+ node.addClass(DDM.CSS_PREFIX + '-drop-active-invalid');
+ }
+ },
+ /**
+ * @method sizeShim
+ * @description Positions and sizes the shim with the raw data from the node, this can be used to programatically adjust the Targets shim for Animation..
+ */
+ sizeShim: function() {
+ if (!DDM.activeDrag) {
+ return false; //Nothing is dragging, no reason to activate.
+ }
+ if (this.get(NODE) === DDM.activeDrag.get(NODE)) {
+ return false;
+ }
+ if (this.get('lock')) {
+ return false;
+ }
+ if (!this.shim) {
+ Y.later(100, this, this.sizeShim);
+ return false;
+ }
+ var node = this.get(NODE),
+ nh = node.get(OFFSET_HEIGHT),
+ nw = node.get(OFFSET_WIDTH),
+ xy = node.getXY(),
+ p = this.get('padding'),
+ dd, dH, dW;
+
+
+ //Apply padding
+ nw = nw + p.left + p.right;
+ nh = nh + p.top + p.bottom;
+ xy[0] = xy[0] - p.left;
+ xy[1] = xy[1] - p.top;
+
+
+ if (DDM.activeDrag.get('dragMode') === DDM.INTERSECT) {
+ //Intersect Mode, make the shim bigger
+ dd = DDM.activeDrag;
+ dH = dd.get(NODE).get(OFFSET_HEIGHT);
+ dW = dd.get(NODE).get(OFFSET_WIDTH);
+
+ nh = (nh + dH);
+ nw = (nw + dW);
+ xy[0] = xy[0] - (dW - dd.deltaXY[0]);
+ xy[1] = xy[1] - (dH - dd.deltaXY[1]);
+
+ }
+
+ //Set the style on the shim
+ this.shim.setStyles({
+ height: nh + 'px',
+ width: nw + 'px',
+ top: xy[1] + 'px',
+ left: xy[0] + 'px'
+ });
+
+ //Create the region to be used by intersect when a drag node is over us.
+ this.region = {
+ '0': xy[0],
+ '1': xy[1],
+ area: 0,
+ top: xy[1],
+ right: xy[0] + nw,
+ bottom: xy[1] + nh,
+ left: xy[0]
+ };
+ },
+ /**
+ * @private
+ * @method _createShim
+ * @description Creates the Target shim and adds it to the DDM's playground..
+ */
+ _createShim: function() {
+ //No playground, defer
+ if (!DDM._pg) {
+ Y.later(10, this, this._createShim);
+ return;
+ }
+ //Shim already here, cancel
+ if (this.shim) {
+ return;
+ }
+ var s = Y.Node.create('<div id="' + this.get(NODE).get('id') + '_shim"></div>');
+
+ s.setStyles({
+ height: this.get(NODE).get(OFFSET_HEIGHT) + 'px',
+ width: this.get(NODE).get(OFFSET_WIDTH) + 'px',
+ backgroundColor: 'yellow',
+ opacity: '.5',
+ zIndex: '1',
+ overflow: 'hidden',
+ top: '-900px',
+ left: '-900px',
+ position: 'absolute'
+ });
+ DDM._pg.appendChild(s);
+ this.shim = s;
+
+ s.on('mouseover', Y.bind(this._handleOverEvent, this));
+ s.on('mouseout', Y.bind(this._handleOutEvent, this));
+ },
+ /**
+ * @private
+ * @method _handleOverTarget
+ * @description This handles the over target call made from this object or from the DDM
+ */
+ _handleTargetOver: function() {
+ if (DDM.isOverTarget(this)) {
+ this.get(NODE).addClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrop = this;
+ DDM.otherDrops[this] = this;
+ if (this.overTarget) {
+ DDM.activeDrag.fire('drag:over', { drop: this, drag: DDM.activeDrag });
+ this.fire(EV_DROP_OVER, { drop: this, drag: DDM.activeDrag });
+ } else {
+ this.overTarget = true;
+ this.fire(EV_DROP_ENTER, { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.fire('drag:enter', { drop: this, drag: DDM.activeDrag });
+ DDM.activeDrag.get(NODE).addClass(DDM.CSS_PREFIX + '-drag-over');
+ //TODO - Is this needed??
+ //DDM._handleTargetOver();
+ }
+ } else {
+ this._handleOut();
+ }
+ },
+ /**
+ * @private
+ * @method _handleOverEvent
+ * @description Handles the mouseover DOM event on the Target Shim
+ */
+ _handleOverEvent: function() {
+ this.shim.setStyle('zIndex', '999');
+ DDM._addActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOutEvent
+ * @description Handles the mouseout DOM event on the Target Shim
+ */
+ _handleOutEvent: function() {
+ this.shim.setStyle('zIndex', '1');
+ DDM._removeActiveShim(this);
+ },
+ /**
+ * @private
+ * @method _handleOut
+ * @description Handles out of target calls/checks
+ */
+ _handleOut: function(force) {
+ if (!DDM.isOverTarget(this) || force) {
+ if (this.overTarget) {
+ this.overTarget = false;
+ if (!force) {
+ DDM._removeActiveShim(this);
+ }
+ if (DDM.activeDrag) {
+ this.get(NODE).removeClass(DDM.CSS_PREFIX + '-drop-over');
+ DDM.activeDrag.get(NODE).removeClass(DDM.CSS_PREFIX + '-drag-over');
+ this.fire(EV_DROP_EXIT);
+ DDM.activeDrag.fire('drag:exit', { drop: this });
+ delete DDM.otherDrops[this];
+ //if (DDM.activeDrop === this) {
+ // DDM.activeDrop = null;
+ //}
+ }
+ }
+ }
+ }
+ });
+
+ Y.DD.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-ddm-drop', 'dd-drag'], skinnable:false});
+YUI.add('dd-drop-plugin', function(Y) {
+
+
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @module dd
+ * @submodule dd-drop-plugin
+ */
+ /**
+ * This is a simple Drop plugin that can be attached to a Node via the plug method.
+ * @class Drop
+ * @extends DD.Drop
+ * @constructor
+ * @namespace Plugin
+ */
+
+
+ var Drop = function(config) {
+ config.node = config.host;
+ Drop.superclass.constructor.apply(this, arguments);
+ };
+
+ /**
+ * @property NAME
+ * @description dd-drop-plugin
+ * @type {String}
+ */
+ Drop.NAME = "dd-drop-plugin";
+ /**
+ * @property NS
+ * @description The Drop instance will be placed on the Node instance under the drop namespace. It can be accessed via Node.drop;
+ * @type {String}
+ */
+ Drop.NS = "drop";
+
+
+ Y.extend(Drop, Y.DD.Drop);
+ Y.namespace('Plugin');
+ Y.Plugin.Drop = Drop;
+
+
+
+
+
+}, '3.0.0' ,{requires:['dd-drop'], skinnable:false});
+
+
+YUI.add('dd', function(Y){}, '3.0.0' ,{use:['dd-ddm-base', 'dd-ddm', 'dd-ddm-drop', 'dd-drag', 'dd-proxy', 'dd-constrain', 'dd-plugin', 'dd-drop', 'dd-drop-plugin', 'dd-scroll'], skinnable:false});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-base', function(Y) {
+
+(function(Y) {
+/**
+ * The DOM utility provides a cross-browser abtraction layer
+ * normalizing DOM tasks, and adds extra helper functionality
+ * for other common tasks.
+ * @module dom
+ * @submodule dom-base
+ *
+ */
+
+/**
+ * Provides DOM helper methods.
+ * @class DOM
+ *
+ */
+var NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ DEFAULT_VIEW = 'defaultView',
+ PARENT_WINDOW = 'parentWindow',
+ TAG_NAME = 'tagName',
+ PARENT_NODE = 'parentNode',
+ FIRST_CHILD = 'firstChild',
+ PREVIOUS_SIBLING = 'previousSibling',
+ NEXT_SIBLING = 'nextSibling',
+ CONTAINS = 'contains',
+ COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+
+ documentElement = document.documentElement,
+
+ re_tag = /<([a-z]+)/i;
+
+Y.DOM = {
+ /**
+ * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
+ * @method byId
+ * @param {String} id the id attribute
+ * @param {Object} doc optional The document to search. Defaults to current document
+ * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
+ */
+ byId: function(id, doc) {
+ doc = doc || Y.config.doc;
+ // TODO: IE Name
+ return doc.getElementById(id);
+ },
+
+ // @deprecated
+ children: function(node, tag) {
+ var ret = [];
+ if (node) {
+ tag = tag || '*';
+ ret = Y.Selector.query('> ' + tag, node);
+ }
+ return ret;
+ },
+
+ // @deprecated
+ firstByTag: function(tag, root) {
+ var ret;
+ root = root || Y.config.doc;
+
+ if (tag && root.getElementsByTagName) {
+ ret = root.getElementsByTagName(tag)[0];
+ }
+
+ return ret || null;
+ },
+
+ /**
+ * Returns the text content of the HTMLElement.
+ * @method getText
+ * @param {HTMLElement} element The html element.
+ * @return {String} The text content of the element (includes text of any descending elements).
+ */
+ getText: (documentElement.textContent !== undefined) ?
+ function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.textContent;
+ }
+ return ret || '';
+ } : function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.innerText;
+ }
+ return ret || '';
+ },
+
+ /**
+ * Sets the text content of the HTMLElement.
+ * @method setText
+ * @param {HTMLElement} element The html element.
+ * @param {String} content The content to add.
+ */
+ setText: (documentElement.textContent !== undefined) ?
+ function(element, content) {
+ if (element) {
+ element.textContent = content;
+ }
+ } : function(element, content) {
+ if (element) {
+ element.innerText = content;
+ }
+ },
+
+ /*
+ * Finds the previous sibling of the element.
+ * @method previous
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ previous: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the next sibling of the element.
+ * @method next
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ next: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the ancestor of the element.
+ * @method ancestor
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the parentNode is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ // TODO: optional stopAt node?
+ ancestor: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
+ },
+
+ /**
+ * Searches the element by the given axis for the first matching element.
+ * @method elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
+ * @param {Function} fn optional An optional boolean test to apply.
+ * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
+ * The optional function is passed the current HTMLElement being tested as its only argument.
+ * If no function is given, the first element is returned.
+ * @return {HTMLElement | null} The matching element or null if none found.
+ */
+ elementByAxis: function(element, axis, fn, all) {
+ while (element && (element = element[axis])) { // NOTE: assignment
+ if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
+ return element;
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Determines whether or not one HTMLElement is or contains another HTMLElement.
+ * @method contains
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ contains: function(element, needle) {
+ var ret = false;
+
+ if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
+ ret = false;
+ } else if (element[CONTAINS]) {
+ if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
+ ret = element[CONTAINS](needle);
+ } else {
+ ret = Y.DOM._bruteContains(element, needle);
+ }
+ } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
+ if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
+ ret = true;
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Determines whether or not the HTMLElement is part of the document.
+ * @method inDoc
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} doc optional The document to check.
+ * @return {Boolean} Whether or not the element is attached to the document.
+ */
+ inDoc: function(element, doc) {
+ doc = doc || element[OWNER_DOCUMENT];
+ var id = element.id;
+ if (!id) { // TODO: remove when done?
+ id = element.id = Y.guid();
+ }
+
+ return !! (doc.getElementById(id));
+ },
+
+ /**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ */
+ create: function(html, doc) {
+ if (typeof html === 'string') {
+ html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
+ }
+
+ if (!doc && Y.DOM._cloneCache[html]) {
+ return Y.DOM._cloneCache[html].cloneNode(true); // NOTE: return
+ }
+
+ doc = doc || Y.config.doc;
+ var m = re_tag.exec(html),
+ create = Y.DOM._create,
+ custom = Y.DOM.creators,
+ ret = null,
+ tag, nodes;
+
+ if (m && custom[m[1]]) {
+ if (typeof custom[m[1]] === 'function') {
+ create = custom[m[1]];
+ } else {
+ tag = custom[m[1]];
+ }
+ }
+
+ nodes = create(html, doc, tag).childNodes;
+
+ if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
+ ret = nodes[0].parentNode.removeChild(nodes[0]);
+ } else { // return multiple nodes as a fragment
+ ret = Y.DOM._nl2frag(nodes, doc);
+ }
+
+ if (ret) {
+ Y.DOM._cloneCache[html] = ret.cloneNode(true);
+ }
+ return ret;
+ },
+
+ _nl2frag: function(nodes, doc) {
+ var ret = null,
+ i, len;
+
+ if (nodes && (nodes.push || nodes.item) && nodes[0]) {
+ doc = doc || nodes[0].ownerDocument;
+ ret = doc.createDocumentFragment();
+
+ if (nodes.item) { // convert live list to static array
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ for (i = 0, len = nodes.length; i < len; i++) {
+ ret.appendChild(nodes[i]);
+ }
+ } // else inline with log for minification
+ else { Y.log('unable to convert ' + nodes + ' to fragment', 'warn', 'dom'); }
+ return ret;
+ },
+
+
+ CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
+ 'for': 'htmlFor',
+ 'class': 'className'
+ } : { // w3c
+ 'htmlFor': 'for',
+ 'className': 'class'
+ },
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method setAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to set.
+ * @param {String} val The value of the attribute.
+ */
+ setAttribute: function(el, attr, val, ieAttr) {
+ if (el && el.setAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ el.setAttribute(attr, val, ieAttr);
+ }
+ },
+
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method getAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to get.
+ * @return {String} The current value of the attribute.
+ */
+ getAttribute: function(el, attr, ieAttr) {
+ ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
+ var ret = '';
+ if (el && el.getAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ ret = el.getAttribute(attr, ieAttr);
+
+ if (ret === null) {
+ ret = ''; // per DOM spec
+ }
+ }
+ return ret;
+ },
+
+ isWindow: function(obj) {
+ return obj.alert && obj.document;
+ },
+
+ _fragClones: {
+ div: document.createElement('div')
+ },
+
+ _create: function(html, doc, tag) {
+ tag = tag || 'div';
+
+ var frag = Y.DOM._fragClones[tag];
+ if (frag) {
+ frag = frag.cloneNode(false);
+ } else {
+ frag = Y.DOM._fragClones[tag] = doc.createElement(tag);
+ }
+ frag.innerHTML = html;
+ return frag;
+ },
+
+ _removeChildNodes: function(node) {
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ },
+
+ _cloneCache: {},
+
+ /**
+ * Inserts content in a node at the given location
+ * @method addHTML
+ * @param {HTMLElement} node The node to insert into
+ * @param {String} content The content to be inserted
+ * @param {String} where Where to insert the content; default is after lastChild
+ */
+ addHTML: function(node, content, where) {
+ if (typeof content === 'string') {
+ content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
+ }
+
+ var newNode = Y.DOM._cloneCache[content],
+ nodeParent = node.parentNode;
+
+ if (newNode) {
+ newNode = newNode.cloneNode(true);
+ } else {
+ if (content.nodeType) { // domNode
+ newNode = content;
+ } else { // create from string and cache
+ newNode = Y.DOM.create(content);
+ }
+ }
+
+ if (where) {
+ if (where.nodeType) { // insert regardless of relationship to node
+ // TODO: check if node.contains(where)?
+ where.parentNode.insertBefore(newNode, where);
+ } else {
+ switch (where) {
+ case 'replace':
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ node.appendChild(newNode);
+ break;
+ case 'before':
+ nodeParent.insertBefore(newNode, node);
+ break;
+ case 'after':
+ if (node.nextSibling) { // IE errors if refNode is null
+ nodeParent.insertBefore(newNode, node.nextSibling);
+ } else {
+ nodeParent.appendChild(newNode);
+ }
+ break;
+ default:
+ node.appendChild(newNode);
+ }
+ }
+ } else {
+ node.appendChild(newNode);
+ }
+
+ return newNode;
+ },
+
+ VALUE_SETTERS: {},
+
+ VALUE_GETTERS: {},
+
+ getValue: function(node) {
+ var ret = '', // TODO: return null?
+ getter;
+
+ if (node && node[TAG_NAME]) {
+ getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (getter) {
+ ret = getter(node);
+ } else {
+ ret = node.value;
+ }
+ }
+
+ return (typeof ret === 'string') ? ret : '';
+ },
+
+ setValue: function(node, val) {
+ var setter;
+
+ if (node && node[TAG_NAME]) {
+ setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (setter) {
+ setter(node, val);
+ } else {
+ node.value = val;
+ }
+ }
+ },
+
+ /**
+ * Brute force version of contains.
+ * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
+ * @method _bruteContains
+ * @private
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ _bruteContains: function(element, needle) {
+ while (needle) {
+ if (element === needle) {
+ return true;
+ }
+ needle = needle.parentNode;
+ }
+ return false;
+ },
+
+// TODO: move to Lang?
+ /**
+ * Memoizes dynamic regular expressions to boost runtime performance.
+ * @method _getRegExp
+ * @private
+ * @param {String} str The string to convert to a regular expression.
+ * @param {String} flags optional An optinal string of flags.
+ * @return {RegExp} An instance of RegExp
+ */
+ _getRegExp: function(str, flags) {
+ flags = flags || '';
+ Y.DOM._regexCache = Y.DOM._regexCache || {};
+ if (!Y.DOM._regexCache[str + flags]) {
+ Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return Y.DOM._regexCache[str + flags];
+ },
+
+// TODO: make getDoc/Win true privates?
+ /**
+ * returns the appropriate document.
+ * @method _getDoc
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The document for the given element or the default document.
+ */
+ _getDoc: function(element) {
+ element = element || {};
+
+ return (element[NODE_TYPE] === 9) ? element : // element === document
+ element[OWNER_DOCUMENT] || // element === DOM node
+ element.document || // element === window
+ Y.config.doc; // default
+ },
+
+ /**
+ * returns the appropriate window.
+ * @method _getWin
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The window for the given element or the default window.
+ */
+ _getWin: function(element) {
+ var doc = Y.DOM._getDoc(element);
+ return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
+ },
+
+ _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
+ fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
+ var result,
+ ret = [];
+
+ if (fn && nodes) {
+ Y.each(nodes, function(node) {
+ if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
+ ret[ret.length] = result;
+ }
+ });
+ }
+
+ return ret.length ? ret : nodes;
+ },
+
+ _testElement: function(element, tag, fn) {
+ tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
+ return (element && element[TAG_NAME] &&
+ (!tag || element[TAG_NAME].toUpperCase() === tag) &&
+ (!fn || fn(element)));
+ },
+
+ creators: {},
+
+ _IESimpleCreate: function(html, doc) {
+ doc = doc || Y.config.doc;
+ return doc.createElement(html);
+ }
+};
+
+
+(function(Y) {
+ var creators = Y.DOM.creators,
+ create = Y.DOM.create,
+ re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
+
+ TABLE_OPEN = '<table>',
+ TABLE_CLOSE = '</table>';
+
+ if (Y.UA.ie) {
+ Y.mix(creators, {
+ // TODO: thead/tfoot with nested tbody
+ // IE adds TBODY when creating TABLE elements (which may share this impl)
+ tbody: function(html, doc) {
+ var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
+ tb = frag.children.tags('tbody')[0];
+
+ if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
+ tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
+ }
+ return frag;
+ },
+
+ script: function(html, doc) {
+ var frag = doc.createElement('div');
+
+ frag.innerHTML = '-' + html;
+ frag.removeChild(frag[FIRST_CHILD]);
+ return frag;
+ }
+
+ }, true);
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ button: function(node) {
+ return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
+ }
+ });
+
+ Y.mix(Y.DOM.VALUE_SETTERS, {
+ // IE: node.value changes the button text, which should be handled via innerHTML
+ button: function(node, val) {
+ var attr = node.attributes.value;
+ if (!attr) {
+ attr = node[OWNER_DOCUMENT].createAttribute('value');
+ node.setAttributeNode(attr);
+ }
+
+ attr.value = val;
+ }
+ });
+ }
+
+ if (Y.UA.gecko || Y.UA.ie) {
+ Y.mix(creators, {
+ option: function(html, doc) {
+ return create('<select>' + html + '</select>', doc);
+ },
+
+ tr: function(html, doc) {
+ return create('<tbody>' + html + '</tbody>', doc);
+ },
+
+ td: function(html, doc) {
+ return create('<tr>' + html + '</tr>', doc);
+ },
+
+ tbody: function(html, doc) {
+ return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
+ }
+ });
+
+ Y.mix(creators, {
+ legend: 'fieldset',
+ th: creators.td,
+ thead: creators.tbody,
+ tfoot: creators.tbody,
+ caption: creators.tbody,
+ colgroup: creators.tbody,
+ col: creators.tbody,
+ optgroup: creators.option
+ });
+ }
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ option: function(node) {
+ var attrs = node.attributes;
+ return (attrs.value && attrs.value.specified) ? node.value : node.text;
+ },
+
+ select: function(node) {
+ var val = node.value,
+ options = node.options;
+
+ if (options && val === '') {
+ if (node.multiple) {
+ Y.log('multiple select normalization not implemented', 'warn', 'DOM');
+ } else {
+ val = Y.DOM.getValue(options[node.selectedIndex], 'value');
+ }
+ }
+
+ return val;
+ }
+ });
+})(Y);
+
+})(Y);
+var addClass, hasClass, removeClass;
+
+Y.mix(Y.DOM, {
+ /**
+ * Determines whether a DOM element has the given className.
+ * @method hasClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to search for
+ * @return {Boolean} Whether or not the element has the given class.
+ */
+ hasClass: function(node, className) {
+ var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
+ return re.test(node.className);
+ },
+
+ /**
+ * Adds a class name to a given DOM element.
+ * @method addClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to add to the class attribute
+ */
+ addClass: function(node, className) {
+ if (!Y.DOM.hasClass(node, className)) { // skip if already present
+ node.className = Y.Lang.trim([node.className, className].join(' '));
+ }
+ },
+
+ /**
+ * Removes a class name from a given element.
+ * @method removeClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to remove from the class attribute
+ */
+ removeClass: function(node, className) {
+ if (className && hasClass(node, className)) {
+ node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
+ className + '(?:\\s+|$)'), ' '));
+
+ if ( hasClass(node, className) ) { // in case of multiple adjacent
+ removeClass(node, className);
+ }
+ }
+ },
+
+ /**
+ * Replace a class with another class for a given element.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ */
+ replaceClass: function(node, oldC, newC) {
+ //Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node');
+ addClass(node, newC);
+ removeClass(node, oldC);
+ },
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to be toggled
+ */
+ toggleClass: function(node, className) {
+ if (hasClass(node, className)) {
+ removeClass(node, className);
+ } else {
+ addClass(node, className);
+ }
+ }
+});
+
+hasClass = Y.DOM.hasClass;
+removeClass = Y.DOM.removeClass;
+addClass = Y.DOM.addClass;
+
+
+
+}, '3.0.0' ,{requires:['oop']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dom-base",function(D){(function(H){var R="nodeType",F="ownerDocument",E="defaultView",J="parentWindow",M="tagName",O="parentNode",Q="firstChild",L="previousSibling",P="nextSibling",K="contains",G="compareDocumentPosition",N=document.documentElement,I=/<([a-z]+)/i;H.DOM={byId:function(T,S){S=S||H.config.doc;return S.getElementById(T);},children:function(U,S){var T=[];if(U){S=S||"*";T=H.Selector.query("> "+S,U);}return T;},firstByTag:function(S,T){var U;T=T||H.config.doc;if(S&&T.getElementsByTagName){U=T.getElementsByTagName(S)[0];}return U||null;},getText:(N.textContent!==undefined)?function(T){var S="";if(T){S=T.textContent;}return S||"";}:function(T){var S="";if(T){S=T.innerText;}return S||"";},setText:(N.textContent!==undefined)?function(S,T){if(S){S.textContent=T;}}:function(S,T){if(S){S.innerText=T;}},previous:function(S,U,T){return H.DOM.elementByAxis(S,L,U,T);},next:function(S,U,T){return H.DOM.elementByAxis(S,P,U,T);},ancestor:function(S,U,T){return H.DOM.elementByAxis(S,O,U,T);},elementByAxis:function(S,V,U,T){while(S&&(S=S[V])){if((T||S[M])&&(!U||U(S))){return S;}}return null;},contains:function(T,U){var S=false;if(!U||!T||!U[R]||!T[R]){S=false;}else{if(T[K]){if(H.UA.opera||U[R]===1){S=T[K](U);}else{S=H.DOM._bruteContains(T,U);}}else{if(T[G]){if(T===U||!!(T[G](U)&16)){S=true;}}}}return S;},inDoc:function(S,T){T=T||S[F];var U=S.id;if(!U){U=S.id=H.guid();}return !!(T.getElementById(U));},create:function(X,Z){if(typeof X==="string"){X=H.Lang.trim(X);}if(!Z&&H.DOM._cloneCache[X]){return H.DOM._cloneCache[X].cloneNode(true);}Z=Z||H.config.doc;var T=I.exec(X),W=H.DOM._create,Y=H.DOM.creators,V=null,S,U;if(T&&Y[T[1]]){if(typeof Y[T[1]]==="function"){W=Y[T[1]];}else{S=Y[T[1]];}}U=W(X,Z,S).childNodes;if(U.length===1){V=U[0].parentNode.removeChild(U[0]);}else{V=H.DOM._nl2frag(U,Z);}if(V){H.DOM._cloneCache[X]=V.cloneNode(true);}return V;},_nl2frag:function(T,W){var U=null,V,S;if(T&&(T.push||T.item)&&T[0]){W=W||T[0].ownerDocument;U=W.createDocumentFragment();if(T.item){T=H.Array(T,0,true);}for(V=0,S=T.length;V<S;V++){U.appendChild(T[V]);}}return U;},CUSTOM_ATTRIBUTES:(!N.hasAttribute)?{"for":"htmlFor","class":"className"}:{"htmlFor":"for","className":"class"},setAttribute:function(U,S,V,T){if(U&&U.setAttribute){S=H.DOM.CUSTOM_ATTRIBUTES[S]||S;U.setAttribute(S,V,T);}},getAttribute:function(V,S,U){U=(U!==undefined)?U:2;var T="";if(V&&V.getAttribute){S=H.DOM.CUSTOM_ATTRIBUTES[S]||S;T=V.getAttribute(S,U);if(T===null){T="";}}return T;},isWindow:function(S){return S.alert&&S.document;},_fragClones:{div:document.createElement("div")},_create:function(T,U,S){S=S||"div";var V=H.DOM._fragClones[S];if(V){V=V.cloneNode(false);}else{V=H.DOM._fragClones[S]=U.createElement(S);}V.innerHTML=T;return V;},_removeChildNodes:function(S){while(S.firstChild){S.removeChild(S.firstChild);}},_cloneCache:{},addHTML:function(W,V,T){if(typeof V==="string"){V=H.Lang.trim(V);}var U=H.DOM._cloneCache[V],S=W.parentNode;if(U){U=U.cloneNode(true);}else{if(V.nodeType){U=V;}else{U=H.DOM.create(V);}}if(T){if(T.nodeType){T.parentNode.insertBefore(U,T);}else{switch(T){case"replace":while(W.firstChild){W.removeChild(W.firstChild);}W.appendChild(U);break;case"before":S.insertBefore(U,W);break;case"after":if(W.nextSibling){S.insertBefore(U,W.nextSibling);}else{S.appendChild(U);}break;default:W.appendChild(U);}}}else{W.appendChild(U);}return U;},VALUE_SETTERS:{},VALUE_GETTERS:{},getValue:function(U){var T="",S;if(U&&U[M]){S=H.DOM.VALUE_GETTERS[U[M].toLowerCase()];if(S){T=S(U);}else{T=U.value;}}return(typeof T==="string")?T:"";},setValue:function(S,T){var U;if(S&&S[M]){U=H.DOM.VALUE_SETTERS[S[M].toLowerCase()];if(U){U(S,T);}else{S.value=T;}}},_bruteContains:function(S,T){while(T){if(S===T){return true;}T=T.parentNode;}return false;},_getRegExp:function(T,S){S=S||"";H.DOM._regexCache=H.DOM._regexCache||{};if(!H.DOM._regexCache[T+S]){H.DOM._regexCache[T+S]=new RegExp(T,S);}return H.DOM._regexCache[T+S];},_getDoc:function(S){S=S||{};return(S[R]===9)?S:S[F]||S.document||H.config.doc;},_getWin:function(S){var T=H.DOM._getDoc(S);return T[E]||T[J]||H.config.win;},_batch:function(V,Z,Y,U,T,X){Z=(typeof name==="string")?H.DOM[Z]:Z;var S,W=[];if(Z&&V){H.each(V,function(a){if((S=Z.call(H.DOM,a,Y,U,T,X))!==undefined){W[W.length]=S;}});}return W.length?W:V;},_testElement:function(T,S,U){S=(S&&S!=="*")?S.toUpperCase():null;return(T&&T[M]&&(!S||T[M].toUpperCase()===S)&&(!U||U(T)));},creators:{},_IESimpleCreate:function(S,T){T=T||H.config.doc;return T.createElement(S);}};(function(W){var X=W.DOM.creators,S=W.DOM.create,V=/(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,U="<table>",T="</table>";if(W.UA.ie){W.mix(X,{tbody:function(Z,a){var b=S(U+Z+T,a),Y=b.children.tags("tbody")[0];if(b.children.length>1&&Y&&!V.test(Z)){Y[O].removeChild(Y);}return b;},script:function(Y,Z){var a=Z.createElement("div");a.innerHTML="-"+Y;a.removeChild(a[Q]);return a;}},true);W.mix(W.DOM.VALUE_GETTERS,{button:function(Y){return(Y.attributes&&Y.attributes.value)?Y.attributes.value.value:"";}});W.mix(W.DOM.VALUE_SETTERS,{button:function(Z,a){var Y=Z.attributes.value;if(!Y){Y=Z[F].createAttribute("value");Z.setAttributeNode(Y);}Y.value=a;}});}if(W.UA.gecko||W.UA.ie){W.mix(X,{option:function(Y,Z){return S("<select>"+Y+"</select>",Z);},tr:function(Y,Z){return S("<tbody>"+Y+"</tbody>",Z);},td:function(Y,Z){return S("<tr>"+Y+"</tr>",Z);},tbody:function(Y,Z){return S(U+Y+T,Z);}});W.mix(X,{legend:"fieldset",th:X.td,thead:X.tbody,tfoot:X.tbody,caption:X.tbody,colgroup:X.tbody,col:X.tbody,optgroup:X.option});}W.mix(W.DOM.VALUE_GETTERS,{option:function(Z){var Y=Z.attributes;return(Y.value&&Y.value.specified)?Z.value:Z.text;},select:function(Z){var a=Z.value,Y=Z.options;if(Y&&a===""){if(Z.multiple){}else{a=W.DOM.getValue(Y[Z.selectedIndex],"value");}}return a;}});})(H);})(D);var B,A,C;D.mix(D.DOM,{hasClass:function(G,F){var E=D.DOM._getRegExp("(?:^|\\s+)"+F+"(?:\\s+|$)");return E.test(G.className);},addClass:function(F,E){if(!D.DOM.hasClass(F,E)){F.className=D.Lang.trim([F.className,E].join(" "));
+}},removeClass:function(F,E){if(E&&A(F,E)){F.className=D.Lang.trim(F.className.replace(D.DOM._getRegExp("(?:^|\\s+)"+E+"(?:\\s+|$)")," "));if(A(F,E)){C(F,E);}}},replaceClass:function(F,E,G){B(F,G);C(F,E);},toggleClass:function(F,E){if(A(F,E)){C(F,E);}else{B(F,E);}}});A=D.DOM.hasClass;C=D.DOM.removeClass;B=D.DOM.addClass;},"3.0.0",{requires:["oop"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-base', function(Y) {
+
+(function(Y) {
+/**
+ * The DOM utility provides a cross-browser abtraction layer
+ * normalizing DOM tasks, and adds extra helper functionality
+ * for other common tasks.
+ * @module dom
+ * @submodule dom-base
+ *
+ */
+
+/**
+ * Provides DOM helper methods.
+ * @class DOM
+ *
+ */
+var NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ DEFAULT_VIEW = 'defaultView',
+ PARENT_WINDOW = 'parentWindow',
+ TAG_NAME = 'tagName',
+ PARENT_NODE = 'parentNode',
+ FIRST_CHILD = 'firstChild',
+ PREVIOUS_SIBLING = 'previousSibling',
+ NEXT_SIBLING = 'nextSibling',
+ CONTAINS = 'contains',
+ COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+
+ documentElement = document.documentElement,
+
+ re_tag = /<([a-z]+)/i;
+
+Y.DOM = {
+ /**
+ * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
+ * @method byId
+ * @param {String} id the id attribute
+ * @param {Object} doc optional The document to search. Defaults to current document
+ * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
+ */
+ byId: function(id, doc) {
+ doc = doc || Y.config.doc;
+ // TODO: IE Name
+ return doc.getElementById(id);
+ },
+
+ // @deprecated
+ children: function(node, tag) {
+ var ret = [];
+ if (node) {
+ tag = tag || '*';
+ ret = Y.Selector.query('> ' + tag, node);
+ }
+ return ret;
+ },
+
+ // @deprecated
+ firstByTag: function(tag, root) {
+ var ret;
+ root = root || Y.config.doc;
+
+ if (tag && root.getElementsByTagName) {
+ ret = root.getElementsByTagName(tag)[0];
+ }
+
+ return ret || null;
+ },
+
+ /**
+ * Returns the text content of the HTMLElement.
+ * @method getText
+ * @param {HTMLElement} element The html element.
+ * @return {String} The text content of the element (includes text of any descending elements).
+ */
+ getText: (documentElement.textContent !== undefined) ?
+ function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.textContent;
+ }
+ return ret || '';
+ } : function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.innerText;
+ }
+ return ret || '';
+ },
+
+ /**
+ * Sets the text content of the HTMLElement.
+ * @method setText
+ * @param {HTMLElement} element The html element.
+ * @param {String} content The content to add.
+ */
+ setText: (documentElement.textContent !== undefined) ?
+ function(element, content) {
+ if (element) {
+ element.textContent = content;
+ }
+ } : function(element, content) {
+ if (element) {
+ element.innerText = content;
+ }
+ },
+
+ /*
+ * Finds the previous sibling of the element.
+ * @method previous
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ previous: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the next sibling of the element.
+ * @method next
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ next: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the ancestor of the element.
+ * @method ancestor
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the parentNode is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ // TODO: optional stopAt node?
+ ancestor: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
+ },
+
+ /**
+ * Searches the element by the given axis for the first matching element.
+ * @method elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
+ * @param {Function} fn optional An optional boolean test to apply.
+ * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
+ * The optional function is passed the current HTMLElement being tested as its only argument.
+ * If no function is given, the first element is returned.
+ * @return {HTMLElement | null} The matching element or null if none found.
+ */
+ elementByAxis: function(element, axis, fn, all) {
+ while (element && (element = element[axis])) { // NOTE: assignment
+ if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
+ return element;
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Determines whether or not one HTMLElement is or contains another HTMLElement.
+ * @method contains
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ contains: function(element, needle) {
+ var ret = false;
+
+ if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
+ ret = false;
+ } else if (element[CONTAINS]) {
+ if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
+ ret = element[CONTAINS](needle);
+ } else {
+ ret = Y.DOM._bruteContains(element, needle);
+ }
+ } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
+ if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
+ ret = true;
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Determines whether or not the HTMLElement is part of the document.
+ * @method inDoc
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} doc optional The document to check.
+ * @return {Boolean} Whether or not the element is attached to the document.
+ */
+ inDoc: function(element, doc) {
+ doc = doc || element[OWNER_DOCUMENT];
+ var id = element.id;
+ if (!id) { // TODO: remove when done?
+ id = element.id = Y.guid();
+ }
+
+ return !! (doc.getElementById(id));
+ },
+
+ /**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ */
+ create: function(html, doc) {
+ if (typeof html === 'string') {
+ html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
+ }
+
+ if (!doc && Y.DOM._cloneCache[html]) {
+ return Y.DOM._cloneCache[html].cloneNode(true); // NOTE: return
+ }
+
+ doc = doc || Y.config.doc;
+ var m = re_tag.exec(html),
+ create = Y.DOM._create,
+ custom = Y.DOM.creators,
+ ret = null,
+ tag, nodes;
+
+ if (m && custom[m[1]]) {
+ if (typeof custom[m[1]] === 'function') {
+ create = custom[m[1]];
+ } else {
+ tag = custom[m[1]];
+ }
+ }
+
+ nodes = create(html, doc, tag).childNodes;
+
+ if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
+ ret = nodes[0].parentNode.removeChild(nodes[0]);
+ } else { // return multiple nodes as a fragment
+ ret = Y.DOM._nl2frag(nodes, doc);
+ }
+
+ if (ret) {
+ Y.DOM._cloneCache[html] = ret.cloneNode(true);
+ }
+ return ret;
+ },
+
+ _nl2frag: function(nodes, doc) {
+ var ret = null,
+ i, len;
+
+ if (nodes && (nodes.push || nodes.item) && nodes[0]) {
+ doc = doc || nodes[0].ownerDocument;
+ ret = doc.createDocumentFragment();
+
+ if (nodes.item) { // convert live list to static array
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ for (i = 0, len = nodes.length; i < len; i++) {
+ ret.appendChild(nodes[i]);
+ }
+ } // else inline with log for minification
+ return ret;
+ },
+
+
+ CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
+ 'for': 'htmlFor',
+ 'class': 'className'
+ } : { // w3c
+ 'htmlFor': 'for',
+ 'className': 'class'
+ },
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method setAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to set.
+ * @param {String} val The value of the attribute.
+ */
+ setAttribute: function(el, attr, val, ieAttr) {
+ if (el && el.setAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ el.setAttribute(attr, val, ieAttr);
+ }
+ },
+
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method getAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to get.
+ * @return {String} The current value of the attribute.
+ */
+ getAttribute: function(el, attr, ieAttr) {
+ ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
+ var ret = '';
+ if (el && el.getAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ ret = el.getAttribute(attr, ieAttr);
+
+ if (ret === null) {
+ ret = ''; // per DOM spec
+ }
+ }
+ return ret;
+ },
+
+ isWindow: function(obj) {
+ return obj.alert && obj.document;
+ },
+
+ _fragClones: {
+ div: document.createElement('div')
+ },
+
+ _create: function(html, doc, tag) {
+ tag = tag || 'div';
+
+ var frag = Y.DOM._fragClones[tag];
+ if (frag) {
+ frag = frag.cloneNode(false);
+ } else {
+ frag = Y.DOM._fragClones[tag] = doc.createElement(tag);
+ }
+ frag.innerHTML = html;
+ return frag;
+ },
+
+ _removeChildNodes: function(node) {
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ },
+
+ _cloneCache: {},
+
+ /**
+ * Inserts content in a node at the given location
+ * @method addHTML
+ * @param {HTMLElement} node The node to insert into
+ * @param {String} content The content to be inserted
+ * @param {String} where Where to insert the content; default is after lastChild
+ */
+ addHTML: function(node, content, where) {
+ if (typeof content === 'string') {
+ content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
+ }
+
+ var newNode = Y.DOM._cloneCache[content],
+ nodeParent = node.parentNode;
+
+ if (newNode) {
+ newNode = newNode.cloneNode(true);
+ } else {
+ if (content.nodeType) { // domNode
+ newNode = content;
+ } else { // create from string and cache
+ newNode = Y.DOM.create(content);
+ }
+ }
+
+ if (where) {
+ if (where.nodeType) { // insert regardless of relationship to node
+ // TODO: check if node.contains(where)?
+ where.parentNode.insertBefore(newNode, where);
+ } else {
+ switch (where) {
+ case 'replace':
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ node.appendChild(newNode);
+ break;
+ case 'before':
+ nodeParent.insertBefore(newNode, node);
+ break;
+ case 'after':
+ if (node.nextSibling) { // IE errors if refNode is null
+ nodeParent.insertBefore(newNode, node.nextSibling);
+ } else {
+ nodeParent.appendChild(newNode);
+ }
+ break;
+ default:
+ node.appendChild(newNode);
+ }
+ }
+ } else {
+ node.appendChild(newNode);
+ }
+
+ return newNode;
+ },
+
+ VALUE_SETTERS: {},
+
+ VALUE_GETTERS: {},
+
+ getValue: function(node) {
+ var ret = '', // TODO: return null?
+ getter;
+
+ if (node && node[TAG_NAME]) {
+ getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (getter) {
+ ret = getter(node);
+ } else {
+ ret = node.value;
+ }
+ }
+
+ return (typeof ret === 'string') ? ret : '';
+ },
+
+ setValue: function(node, val) {
+ var setter;
+
+ if (node && node[TAG_NAME]) {
+ setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (setter) {
+ setter(node, val);
+ } else {
+ node.value = val;
+ }
+ }
+ },
+
+ /**
+ * Brute force version of contains.
+ * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
+ * @method _bruteContains
+ * @private
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ _bruteContains: function(element, needle) {
+ while (needle) {
+ if (element === needle) {
+ return true;
+ }
+ needle = needle.parentNode;
+ }
+ return false;
+ },
+
+// TODO: move to Lang?
+ /**
+ * Memoizes dynamic regular expressions to boost runtime performance.
+ * @method _getRegExp
+ * @private
+ * @param {String} str The string to convert to a regular expression.
+ * @param {String} flags optional An optinal string of flags.
+ * @return {RegExp} An instance of RegExp
+ */
+ _getRegExp: function(str, flags) {
+ flags = flags || '';
+ Y.DOM._regexCache = Y.DOM._regexCache || {};
+ if (!Y.DOM._regexCache[str + flags]) {
+ Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return Y.DOM._regexCache[str + flags];
+ },
+
+// TODO: make getDoc/Win true privates?
+ /**
+ * returns the appropriate document.
+ * @method _getDoc
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The document for the given element or the default document.
+ */
+ _getDoc: function(element) {
+ element = element || {};
+
+ return (element[NODE_TYPE] === 9) ? element : // element === document
+ element[OWNER_DOCUMENT] || // element === DOM node
+ element.document || // element === window
+ Y.config.doc; // default
+ },
+
+ /**
+ * returns the appropriate window.
+ * @method _getWin
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The window for the given element or the default window.
+ */
+ _getWin: function(element) {
+ var doc = Y.DOM._getDoc(element);
+ return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
+ },
+
+ _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
+ fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
+ var result,
+ ret = [];
+
+ if (fn && nodes) {
+ Y.each(nodes, function(node) {
+ if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
+ ret[ret.length] = result;
+ }
+ });
+ }
+
+ return ret.length ? ret : nodes;
+ },
+
+ _testElement: function(element, tag, fn) {
+ tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
+ return (element && element[TAG_NAME] &&
+ (!tag || element[TAG_NAME].toUpperCase() === tag) &&
+ (!fn || fn(element)));
+ },
+
+ creators: {},
+
+ _IESimpleCreate: function(html, doc) {
+ doc = doc || Y.config.doc;
+ return doc.createElement(html);
+ }
+};
+
+
+(function(Y) {
+ var creators = Y.DOM.creators,
+ create = Y.DOM.create,
+ re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
+
+ TABLE_OPEN = '<table>',
+ TABLE_CLOSE = '</table>';
+
+ if (Y.UA.ie) {
+ Y.mix(creators, {
+ // TODO: thead/tfoot with nested tbody
+ // IE adds TBODY when creating TABLE elements (which may share this impl)
+ tbody: function(html, doc) {
+ var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
+ tb = frag.children.tags('tbody')[0];
+
+ if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
+ tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
+ }
+ return frag;
+ },
+
+ script: function(html, doc) {
+ var frag = doc.createElement('div');
+
+ frag.innerHTML = '-' + html;
+ frag.removeChild(frag[FIRST_CHILD]);
+ return frag;
+ }
+
+ }, true);
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ button: function(node) {
+ return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
+ }
+ });
+
+ Y.mix(Y.DOM.VALUE_SETTERS, {
+ // IE: node.value changes the button text, which should be handled via innerHTML
+ button: function(node, val) {
+ var attr = node.attributes.value;
+ if (!attr) {
+ attr = node[OWNER_DOCUMENT].createAttribute('value');
+ node.setAttributeNode(attr);
+ }
+
+ attr.value = val;
+ }
+ });
+ }
+
+ if (Y.UA.gecko || Y.UA.ie) {
+ Y.mix(creators, {
+ option: function(html, doc) {
+ return create('<select>' + html + '</select>', doc);
+ },
+
+ tr: function(html, doc) {
+ return create('<tbody>' + html + '</tbody>', doc);
+ },
+
+ td: function(html, doc) {
+ return create('<tr>' + html + '</tr>', doc);
+ },
+
+ tbody: function(html, doc) {
+ return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
+ }
+ });
+
+ Y.mix(creators, {
+ legend: 'fieldset',
+ th: creators.td,
+ thead: creators.tbody,
+ tfoot: creators.tbody,
+ caption: creators.tbody,
+ colgroup: creators.tbody,
+ col: creators.tbody,
+ optgroup: creators.option
+ });
+ }
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ option: function(node) {
+ var attrs = node.attributes;
+ return (attrs.value && attrs.value.specified) ? node.value : node.text;
+ },
+
+ select: function(node) {
+ var val = node.value,
+ options = node.options;
+
+ if (options && val === '') {
+ if (node.multiple) {
+ } else {
+ val = Y.DOM.getValue(options[node.selectedIndex], 'value');
+ }
+ }
+
+ return val;
+ }
+ });
+})(Y);
+
+})(Y);
+var addClass, hasClass, removeClass;
+
+Y.mix(Y.DOM, {
+ /**
+ * Determines whether a DOM element has the given className.
+ * @method hasClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to search for
+ * @return {Boolean} Whether or not the element has the given class.
+ */
+ hasClass: function(node, className) {
+ var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
+ return re.test(node.className);
+ },
+
+ /**
+ * Adds a class name to a given DOM element.
+ * @method addClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to add to the class attribute
+ */
+ addClass: function(node, className) {
+ if (!Y.DOM.hasClass(node, className)) { // skip if already present
+ node.className = Y.Lang.trim([node.className, className].join(' '));
+ }
+ },
+
+ /**
+ * Removes a class name from a given element.
+ * @method removeClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to remove from the class attribute
+ */
+ removeClass: function(node, className) {
+ if (className && hasClass(node, className)) {
+ node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
+ className + '(?:\\s+|$)'), ' '));
+
+ if ( hasClass(node, className) ) { // in case of multiple adjacent
+ removeClass(node, className);
+ }
+ }
+ },
+
+ /**
+ * Replace a class with another class for a given element.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ */
+ replaceClass: function(node, oldC, newC) {
+ addClass(node, newC);
+ removeClass(node, oldC);
+ },
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to be toggled
+ */
+ toggleClass: function(node, className) {
+ if (hasClass(node, className)) {
+ removeClass(node, className);
+ } else {
+ addClass(node, className);
+ }
+ }
+});
+
+hasClass = Y.DOM.hasClass;
+removeClass = Y.DOM.removeClass;
+addClass = Y.DOM.addClass;
+
+
+
+}, '3.0.0' ,{requires:['oop']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-base', function(Y) {
+
+(function(Y) {
+/**
+ * The DOM utility provides a cross-browser abtraction layer
+ * normalizing DOM tasks, and adds extra helper functionality
+ * for other common tasks.
+ * @module dom
+ * @submodule dom-base
+ *
+ */
+
+/**
+ * Provides DOM helper methods.
+ * @class DOM
+ *
+ */
+var NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ DEFAULT_VIEW = 'defaultView',
+ PARENT_WINDOW = 'parentWindow',
+ TAG_NAME = 'tagName',
+ PARENT_NODE = 'parentNode',
+ FIRST_CHILD = 'firstChild',
+ PREVIOUS_SIBLING = 'previousSibling',
+ NEXT_SIBLING = 'nextSibling',
+ CONTAINS = 'contains',
+ COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+
+ documentElement = document.documentElement,
+
+ re_tag = /<([a-z]+)/i;
+
+Y.DOM = {
+ /**
+ * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
+ * @method byId
+ * @param {String} id the id attribute
+ * @param {Object} doc optional The document to search. Defaults to current document
+ * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
+ */
+ byId: function(id, doc) {
+ doc = doc || Y.config.doc;
+ // TODO: IE Name
+ return doc.getElementById(id);
+ },
+
+ // @deprecated
+ children: function(node, tag) {
+ var ret = [];
+ if (node) {
+ tag = tag || '*';
+ ret = Y.Selector.query('> ' + tag, node);
+ }
+ return ret;
+ },
+
+ // @deprecated
+ firstByTag: function(tag, root) {
+ var ret;
+ root = root || Y.config.doc;
+
+ if (tag && root.getElementsByTagName) {
+ ret = root.getElementsByTagName(tag)[0];
+ }
+
+ return ret || null;
+ },
+
+ /**
+ * Returns the text content of the HTMLElement.
+ * @method getText
+ * @param {HTMLElement} element The html element.
+ * @return {String} The text content of the element (includes text of any descending elements).
+ */
+ getText: (documentElement.textContent !== undefined) ?
+ function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.textContent;
+ }
+ return ret || '';
+ } : function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.innerText;
+ }
+ return ret || '';
+ },
+
+ /**
+ * Sets the text content of the HTMLElement.
+ * @method setText
+ * @param {HTMLElement} element The html element.
+ * @param {String} content The content to add.
+ */
+ setText: (documentElement.textContent !== undefined) ?
+ function(element, content) {
+ if (element) {
+ element.textContent = content;
+ }
+ } : function(element, content) {
+ if (element) {
+ element.innerText = content;
+ }
+ },
+
+ /*
+ * Finds the previous sibling of the element.
+ * @method previous
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ previous: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the next sibling of the element.
+ * @method next
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ next: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the ancestor of the element.
+ * @method ancestor
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the parentNode is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ // TODO: optional stopAt node?
+ ancestor: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
+ },
+
+ /**
+ * Searches the element by the given axis for the first matching element.
+ * @method elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
+ * @param {Function} fn optional An optional boolean test to apply.
+ * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
+ * The optional function is passed the current HTMLElement being tested as its only argument.
+ * If no function is given, the first element is returned.
+ * @return {HTMLElement | null} The matching element or null if none found.
+ */
+ elementByAxis: function(element, axis, fn, all) {
+ while (element && (element = element[axis])) { // NOTE: assignment
+ if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
+ return element;
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Determines whether or not one HTMLElement is or contains another HTMLElement.
+ * @method contains
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ contains: function(element, needle) {
+ var ret = false;
+
+ if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
+ ret = false;
+ } else if (element[CONTAINS]) {
+ if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
+ ret = element[CONTAINS](needle);
+ } else {
+ ret = Y.DOM._bruteContains(element, needle);
+ }
+ } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
+ if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
+ ret = true;
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Determines whether or not the HTMLElement is part of the document.
+ * @method inDoc
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} doc optional The document to check.
+ * @return {Boolean} Whether or not the element is attached to the document.
+ */
+ inDoc: function(element, doc) {
+ doc = doc || element[OWNER_DOCUMENT];
+ var id = element.id;
+ if (!id) { // TODO: remove when done?
+ id = element.id = Y.guid();
+ }
+
+ return !! (doc.getElementById(id));
+ },
+
+ /**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ */
+ create: function(html, doc) {
+ if (typeof html === 'string') {
+ html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
+ }
+
+ if (!doc && Y.DOM._cloneCache[html]) {
+ return Y.DOM._cloneCache[html].cloneNode(true); // NOTE: return
+ }
+
+ doc = doc || Y.config.doc;
+ var m = re_tag.exec(html),
+ create = Y.DOM._create,
+ custom = Y.DOM.creators,
+ ret = null,
+ tag, nodes;
+
+ if (m && custom[m[1]]) {
+ if (typeof custom[m[1]] === 'function') {
+ create = custom[m[1]];
+ } else {
+ tag = custom[m[1]];
+ }
+ }
+
+ nodes = create(html, doc, tag).childNodes;
+
+ if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
+ ret = nodes[0].parentNode.removeChild(nodes[0]);
+ } else { // return multiple nodes as a fragment
+ ret = Y.DOM._nl2frag(nodes, doc);
+ }
+
+ if (ret) {
+ Y.DOM._cloneCache[html] = ret.cloneNode(true);
+ }
+ return ret;
+ },
+
+ _nl2frag: function(nodes, doc) {
+ var ret = null,
+ i, len;
+
+ if (nodes && (nodes.push || nodes.item) && nodes[0]) {
+ doc = doc || nodes[0].ownerDocument;
+ ret = doc.createDocumentFragment();
+
+ if (nodes.item) { // convert live list to static array
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ for (i = 0, len = nodes.length; i < len; i++) {
+ ret.appendChild(nodes[i]);
+ }
+ } // else inline with log for minification
+ else { Y.log('unable to convert ' + nodes + ' to fragment', 'warn', 'dom'); }
+ return ret;
+ },
+
+
+ CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
+ 'for': 'htmlFor',
+ 'class': 'className'
+ } : { // w3c
+ 'htmlFor': 'for',
+ 'className': 'class'
+ },
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method setAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to set.
+ * @param {String} val The value of the attribute.
+ */
+ setAttribute: function(el, attr, val, ieAttr) {
+ if (el && el.setAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ el.setAttribute(attr, val, ieAttr);
+ }
+ },
+
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method getAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to get.
+ * @return {String} The current value of the attribute.
+ */
+ getAttribute: function(el, attr, ieAttr) {
+ ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
+ var ret = '';
+ if (el && el.getAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ ret = el.getAttribute(attr, ieAttr);
+
+ if (ret === null) {
+ ret = ''; // per DOM spec
+ }
+ }
+ return ret;
+ },
+
+ isWindow: function(obj) {
+ return obj.alert && obj.document;
+ },
+
+ _fragClones: {
+ div: document.createElement('div')
+ },
+
+ _create: function(html, doc, tag) {
+ tag = tag || 'div';
+
+ var frag = Y.DOM._fragClones[tag];
+ if (frag) {
+ frag = frag.cloneNode(false);
+ } else {
+ frag = Y.DOM._fragClones[tag] = doc.createElement(tag);
+ }
+ frag.innerHTML = html;
+ return frag;
+ },
+
+ _removeChildNodes: function(node) {
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ },
+
+ _cloneCache: {},
+
+ /**
+ * Inserts content in a node at the given location
+ * @method addHTML
+ * @param {HTMLElement} node The node to insert into
+ * @param {String} content The content to be inserted
+ * @param {String} where Where to insert the content; default is after lastChild
+ */
+ addHTML: function(node, content, where) {
+ if (typeof content === 'string') {
+ content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
+ }
+
+ var newNode = Y.DOM._cloneCache[content],
+ nodeParent = node.parentNode;
+
+ if (newNode) {
+ newNode = newNode.cloneNode(true);
+ } else {
+ if (content.nodeType) { // domNode
+ newNode = content;
+ } else { // create from string and cache
+ newNode = Y.DOM.create(content);
+ }
+ }
+
+ if (where) {
+ if (where.nodeType) { // insert regardless of relationship to node
+ // TODO: check if node.contains(where)?
+ where.parentNode.insertBefore(newNode, where);
+ } else {
+ switch (where) {
+ case 'replace':
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ node.appendChild(newNode);
+ break;
+ case 'before':
+ nodeParent.insertBefore(newNode, node);
+ break;
+ case 'after':
+ if (node.nextSibling) { // IE errors if refNode is null
+ nodeParent.insertBefore(newNode, node.nextSibling);
+ } else {
+ nodeParent.appendChild(newNode);
+ }
+ break;
+ default:
+ node.appendChild(newNode);
+ }
+ }
+ } else {
+ node.appendChild(newNode);
+ }
+
+ return newNode;
+ },
+
+ VALUE_SETTERS: {},
+
+ VALUE_GETTERS: {},
+
+ getValue: function(node) {
+ var ret = '', // TODO: return null?
+ getter;
+
+ if (node && node[TAG_NAME]) {
+ getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (getter) {
+ ret = getter(node);
+ } else {
+ ret = node.value;
+ }
+ }
+
+ return (typeof ret === 'string') ? ret : '';
+ },
+
+ setValue: function(node, val) {
+ var setter;
+
+ if (node && node[TAG_NAME]) {
+ setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (setter) {
+ setter(node, val);
+ } else {
+ node.value = val;
+ }
+ }
+ },
+
+ /**
+ * Brute force version of contains.
+ * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
+ * @method _bruteContains
+ * @private
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ _bruteContains: function(element, needle) {
+ while (needle) {
+ if (element === needle) {
+ return true;
+ }
+ needle = needle.parentNode;
+ }
+ return false;
+ },
+
+// TODO: move to Lang?
+ /**
+ * Memoizes dynamic regular expressions to boost runtime performance.
+ * @method _getRegExp
+ * @private
+ * @param {String} str The string to convert to a regular expression.
+ * @param {String} flags optional An optinal string of flags.
+ * @return {RegExp} An instance of RegExp
+ */
+ _getRegExp: function(str, flags) {
+ flags = flags || '';
+ Y.DOM._regexCache = Y.DOM._regexCache || {};
+ if (!Y.DOM._regexCache[str + flags]) {
+ Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return Y.DOM._regexCache[str + flags];
+ },
+
+// TODO: make getDoc/Win true privates?
+ /**
+ * returns the appropriate document.
+ * @method _getDoc
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The document for the given element or the default document.
+ */
+ _getDoc: function(element) {
+ element = element || {};
+
+ return (element[NODE_TYPE] === 9) ? element : // element === document
+ element[OWNER_DOCUMENT] || // element === DOM node
+ element.document || // element === window
+ Y.config.doc; // default
+ },
+
+ /**
+ * returns the appropriate window.
+ * @method _getWin
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The window for the given element or the default window.
+ */
+ _getWin: function(element) {
+ var doc = Y.DOM._getDoc(element);
+ return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
+ },
+
+ _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
+ fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
+ var result,
+ ret = [];
+
+ if (fn && nodes) {
+ Y.each(nodes, function(node) {
+ if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
+ ret[ret.length] = result;
+ }
+ });
+ }
+
+ return ret.length ? ret : nodes;
+ },
+
+ _testElement: function(element, tag, fn) {
+ tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
+ return (element && element[TAG_NAME] &&
+ (!tag || element[TAG_NAME].toUpperCase() === tag) &&
+ (!fn || fn(element)));
+ },
+
+ creators: {},
+
+ _IESimpleCreate: function(html, doc) {
+ doc = doc || Y.config.doc;
+ return doc.createElement(html);
+ }
+};
+
+
+(function(Y) {
+ var creators = Y.DOM.creators,
+ create = Y.DOM.create,
+ re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
+
+ TABLE_OPEN = '<table>',
+ TABLE_CLOSE = '</table>';
+
+ if (Y.UA.ie) {
+ Y.mix(creators, {
+ // TODO: thead/tfoot with nested tbody
+ // IE adds TBODY when creating TABLE elements (which may share this impl)
+ tbody: function(html, doc) {
+ var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
+ tb = frag.children.tags('tbody')[0];
+
+ if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
+ tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
+ }
+ return frag;
+ },
+
+ script: function(html, doc) {
+ var frag = doc.createElement('div');
+
+ frag.innerHTML = '-' + html;
+ frag.removeChild(frag[FIRST_CHILD]);
+ return frag;
+ }
+
+ }, true);
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ button: function(node) {
+ return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
+ }
+ });
+
+ Y.mix(Y.DOM.VALUE_SETTERS, {
+ // IE: node.value changes the button text, which should be handled via innerHTML
+ button: function(node, val) {
+ var attr = node.attributes.value;
+ if (!attr) {
+ attr = node[OWNER_DOCUMENT].createAttribute('value');
+ node.setAttributeNode(attr);
+ }
+
+ attr.value = val;
+ }
+ });
+ }
+
+ if (Y.UA.gecko || Y.UA.ie) {
+ Y.mix(creators, {
+ option: function(html, doc) {
+ return create('<select>' + html + '</select>', doc);
+ },
+
+ tr: function(html, doc) {
+ return create('<tbody>' + html + '</tbody>', doc);
+ },
+
+ td: function(html, doc) {
+ return create('<tr>' + html + '</tr>', doc);
+ },
+
+ tbody: function(html, doc) {
+ return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
+ }
+ });
+
+ Y.mix(creators, {
+ legend: 'fieldset',
+ th: creators.td,
+ thead: creators.tbody,
+ tfoot: creators.tbody,
+ caption: creators.tbody,
+ colgroup: creators.tbody,
+ col: creators.tbody,
+ optgroup: creators.option
+ });
+ }
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ option: function(node) {
+ var attrs = node.attributes;
+ return (attrs.value && attrs.value.specified) ? node.value : node.text;
+ },
+
+ select: function(node) {
+ var val = node.value,
+ options = node.options;
+
+ if (options && val === '') {
+ if (node.multiple) {
+ Y.log('multiple select normalization not implemented', 'warn', 'DOM');
+ } else {
+ val = Y.DOM.getValue(options[node.selectedIndex], 'value');
+ }
+ }
+
+ return val;
+ }
+ });
+})(Y);
+
+})(Y);
+var addClass, hasClass, removeClass;
+
+Y.mix(Y.DOM, {
+ /**
+ * Determines whether a DOM element has the given className.
+ * @method hasClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to search for
+ * @return {Boolean} Whether or not the element has the given class.
+ */
+ hasClass: function(node, className) {
+ var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
+ return re.test(node.className);
+ },
+
+ /**
+ * Adds a class name to a given DOM element.
+ * @method addClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to add to the class attribute
+ */
+ addClass: function(node, className) {
+ if (!Y.DOM.hasClass(node, className)) { // skip if already present
+ node.className = Y.Lang.trim([node.className, className].join(' '));
+ }
+ },
+
+ /**
+ * Removes a class name from a given element.
+ * @method removeClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to remove from the class attribute
+ */
+ removeClass: function(node, className) {
+ if (className && hasClass(node, className)) {
+ node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
+ className + '(?:\\s+|$)'), ' '));
+
+ if ( hasClass(node, className) ) { // in case of multiple adjacent
+ removeClass(node, className);
+ }
+ }
+ },
+
+ /**
+ * Replace a class with another class for a given element.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ */
+ replaceClass: function(node, oldC, newC) {
+ //Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node');
+ addClass(node, newC);
+ removeClass(node, oldC);
+ },
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to be toggled
+ */
+ toggleClass: function(node, className) {
+ if (hasClass(node, className)) {
+ removeClass(node, className);
+ } else {
+ addClass(node, className);
+ }
+ }
+});
+
+hasClass = Y.DOM.hasClass;
+removeClass = Y.DOM.removeClass;
+addClass = Y.DOM.addClass;
+
+
+
+}, '3.0.0' ,{requires:['oop']});
+YUI.add('dom-style', function(Y) {
+
+(function(Y) {
+/**
+ * Add style management functionality to DOM.
+ * @module dom
+ * @submodule dom-style
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ DEFAULT_VIEW = 'defaultView',
+ OWNER_DOCUMENT = 'ownerDocument',
+ STYLE = 'style',
+ FLOAT = 'float',
+ CSS_FLOAT = 'cssFloat',
+ STYLE_FLOAT = 'styleFloat',
+ TRANSPARENT = 'transparent',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ DOCUMENT = Y.config.doc,
+ UNDEFINED = undefined,
+
+ re_color = /color$/i;
+
+
+Y.mix(Y.DOM, {
+ CUSTOM_STYLES: {
+ },
+
+
+ /**
+ * Sets a style property for a given element.
+ * @method setStyle
+ * @param {HTMLElement} An HTMLElement to apply the style to.
+ * @param {String} att The style property to set.
+ * @param {String|Number} val The value.
+ */
+ setStyle: function(node, att, val, style) {
+ style = style || node.style;
+ var CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES;
+
+ if (style) {
+ if (val === null) {
+ val = ''; // normalize for unsetting
+ }
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].set) {
+ CUSTOM_STYLES[att].set(node, val, style);
+ return; // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ style[att] = val;
+ }
+ },
+
+ /**
+ * Returns the current style value for the given property.
+ * @method getStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ */
+ getStyle: function(node, att) {
+ var style = node[STYLE],
+ CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES,
+ val = '';
+
+ if (style) {
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].get) {
+ return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ val = style[att];
+ if (val === '') { // TODO: is empty string sufficient?
+ val = Y.DOM[GET_COMPUTED_STYLE](node, att);
+ }
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets multiple style properties.
+ * @method setStyles
+ * @param {HTMLElement} node An HTMLElement to apply the styles to.
+ * @param {Object} hash An object literal of property:value pairs.
+ */
+ setStyles: function(node, hash) {
+ var style = node.style;
+ Y.each(hash, function(v, n) {
+ Y.DOM.setStyle(node, n, v, style);
+ }, Y.DOM);
+ },
+
+ /**
+ * Returns the computed style for the given node.
+ * @method getComputedStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ * @return {String} The computed value of the style property.
+ */
+ getComputedStyle: function(node, att) {
+ var val = '',
+ doc = node[OWNER_DOCUMENT];
+
+ if (node[STYLE]) {
+ val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
+ }
+ return val;
+ }
+});
+
+// normalize reserved word float alternatives ("cssFloat" or "styleFloat")
+if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
+} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
+}
+
+// fix opera computedStyle default color unit (convert to rgb)
+if (Y.UA.opera) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (re_color.test(att)) {
+ val = Y.Color.toRGB(val);
+ }
+
+ return val;
+ };
+
+}
+
+// safari converts transparent to rgba(), others use "transparent"
+if (Y.UA.webkit) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (val === 'rgba(0, 0, 0, 0)') {
+ val = TRANSPARENT;
+ }
+
+ return val;
+ };
+
+}
+})(Y);
+(function(Y) {
+var PARSE_INT = parseInt,
+ RE = RegExp;
+
+Y.Color = {
+ KEYWORDS: {
+ black: '000',
+ silver: 'c0c0c0',
+ gray: '808080',
+ white: 'fff',
+ maroon: '800000',
+ red: 'f00',
+ purple: '800080',
+ fuchsia: 'f0f',
+ green: '008000',
+ lime: '0f0',
+ olive: '808000',
+ yellow: 'ff0',
+ navy: '000080',
+ blue: '00f',
+ teal: '008080',
+ aqua: '0ff'
+ },
+
+ re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
+ re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
+ re_hex3: /([0-9A-F])/gi,
+
+ toRGB: function(val) {
+ if (!Y.Color.re_RGB.test(val)) {
+ val = Y.Color.toHex(val);
+ }
+
+ if(Y.Color.re_hex.exec(val)) {
+ val = 'rgb(' + [
+ PARSE_INT(RE.$1, 16),
+ PARSE_INT(RE.$2, 16),
+ PARSE_INT(RE.$3, 16)
+ ].join(', ') + ')';
+ }
+ return val;
+ },
+
+ toHex: function(val) {
+ val = Y.Color.KEYWORDS[val] || val;
+ if (Y.Color.re_RGB.exec(val)) {
+ val = [
+ Number(RE.$1).toString(16),
+ Number(RE.$2).toString(16),
+ Number(RE.$3).toString(16)
+ ];
+
+ for (var i = 0; i < val.length; i++) {
+ if (val[i].length < 2) {
+ val[i] = val[i].replace(Y.Color.re_hex3, '$1$1');
+ }
+ }
+
+ val = '#' + val.join('');
+ }
+
+ if (val.length < 6) {
+ val = val.replace(Y.Color.re_hex3, '$1$1');
+ }
+
+ if (val !== 'transparent' && val.indexOf('#') < 0) {
+ val = '#' + val;
+ }
+
+ return val.toLowerCase();
+ }
+};
+})(Y);
+
+(function(Y) {
+var HAS_LAYOUT = 'hasLayout',
+ PX = 'px',
+ FILTER = 'filter',
+ FILTERS = 'filters',
+ OPACITY = 'opacity',
+ AUTO = 'auto',
+
+ BORDER_WIDTH = 'borderWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ BORDER_RIGHT_WIDTH = 'borderRightWidth',
+ BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ WIDTH = 'width',
+ HEIGHT = 'height',
+ TRANSPARENT = 'transparent',
+ VISIBLE = 'visible',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+ UNDEFINED = undefined,
+ documentElement = document.documentElement,
+
+ // TODO: unit-less lineHeight (e.g. 1.22)
+ re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
+
+ _getStyleObj = function(node) {
+ return node.currentStyle || node.style;
+ },
+
+ ComputedStyle = {
+ CUSTOM_STYLES: {},
+
+ get: function(el, property) {
+ var value = '',
+ current;
+
+ if (el) {
+ current = _getStyleObj(el)[property];
+
+ if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
+ value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
+ } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
+ value = current;
+ } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
+ value = Y.DOM.IE.COMPUTED[property](el, property);
+ } else if (re_unit.test(current)) { // convert to pixel
+ value = ComputedStyle.getPixel(el, property) + PX;
+ } else {
+ value = current;
+ }
+ }
+
+ return value;
+ },
+
+ sizeOffsets: {
+ width: ['Left', 'Right'],
+ height: ['Top', 'Bottom'],
+ top: ['Top'],
+ bottom: ['Bottom']
+ },
+
+ getOffset: function(el, prop) {
+ var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
+ capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
+ offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
+ pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
+ sizeOffsets = ComputedStyle.sizeOffsets[prop],
+ value = '';
+
+ // IE pixelWidth incorrect for percent
+ // manually compute by subtracting padding and border from offset size
+ // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
+ // reverting to auto from auto causes position stacking issues (old impl)
+ if (current === AUTO || current.indexOf('%') > -1) {
+ value = el['offset' + capped];
+
+ if (sizeOffsets[0]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
+ }
+
+ if (sizeOffsets[1]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
+ }
+
+ } else { // use style.pixelWidth, etc. to convert to pixels
+ // need to map style.width to currentStyle (no currentStyle.pixelWidth)
+ if (!el.style[pixel] && !el.style[prop]) {
+ el.style[prop] = current;
+ }
+ value = el.style[pixel];
+
+ }
+ return value + PX;
+ },
+
+ borderMap: {
+ thin: '2px',
+ medium: '4px',
+ thick: '6px'
+ },
+
+ getBorderWidth: function(el, property, omitUnit) {
+ var unit = omitUnit ? '' : PX,
+ current = el.currentStyle[property];
+
+ if (current.indexOf(PX) < 0) { // look up keywords
+ if (ComputedStyle.borderMap[current]) {
+ current = ComputedStyle.borderMap[current];
+ } else {
+ Y.log('borderWidth computing not implemented', 'warn', 'dom-ie-style');
+ }
+ }
+ return (omitUnit) ? parseFloat(current) : current;
+ },
+
+ getPixel: function(node, att) {
+ // use pixelRight to convert to px
+ var val = null,
+ style = _getStyleObj(node),
+ styleRight = style.right,
+ current = style[att];
+
+ node.style.right = current;
+ val = node.style.pixelRight;
+ node.style.right = styleRight; // revert
+
+ return val;
+ },
+
+ getMargin: function(node, att) {
+ var val,
+ style = _getStyleObj(node);
+
+ if (style[att] == AUTO) {
+ val = 0;
+ } else {
+ val = ComputedStyle.getPixel(node, att);
+ }
+ return val + PX;
+ },
+
+ getVisibility: function(node, att) {
+ var current;
+ while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
+ node = node.parentNode;
+ }
+ return (current) ? current[att] : VISIBLE;
+ },
+
+ getColor: function(node, att) {
+ var current = _getStyleObj(node)[att];
+
+ if (!current || current === TRANSPARENT) {
+ Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
+ current = _getStyleObj(parent)[att];
+ if (current && current !== TRANSPARENT) {
+ node = parent;
+ return true;
+ }
+ });
+ }
+
+ return Y.Color.toRGB(current);
+ },
+
+ getBorderColor: function(node, att) {
+ var current = _getStyleObj(node),
+ val = current[att] || current.color;
+ return Y.Color.toRGB(Y.Color.toHex(val));
+ }
+ },
+
+ //fontSize: getPixelFont,
+ IEComputed = {};
+
+// use alpha filter for IE opacity
+try {
+ if (documentElement.style[OPACITY] === UNDEFINED &&
+ documentElement[FILTERS]) {
+ Y.DOM.CUSTOM_STYLES[OPACITY] = {
+ get: function(node) {
+ var val = 100;
+ try { // will error if no DXImageTransform
+ val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
+
+ } catch(e) {
+ try { // make sure its in the document
+ val = node[FILTERS]('alpha')[OPACITY];
+ } catch(err) {
+ Y.log('getStyle: IE opacity filter not found; returning 1', 'warn', 'dom-style');
+ }
+ }
+ return val / 100;
+ },
+
+ set: function(node, val, style) {
+ var current,
+ styleObj;
+
+ if (val === '') { // normalize inline style behavior
+ styleObj = _getStyleObj(node);
+ current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
+ val = current;
+ }
+
+ if (typeof style[FILTER] == 'string') { // in case not appended
+ style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
+
+ if (!node.currentStyle || !node.currentStyle[HAS_LAYOUT]) {
+ style.zoom = 1; // needs layout
+ }
+ }
+ }
+ };
+ }
+} catch(e) {
+ Y.log('document.documentElement.filters error (activeX disabled)', 'warn', 'dom-style');
+}
+
+try {
+ document.createElement('div').style.height = '-1px';
+} catch(e) { // IE throws error on invalid style set; trap common cases
+ Y.DOM.CUSTOM_STYLES.height = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.height = val;
+ } else {
+ Y.log('invalid style value for height: ' + val, 'warn', 'dom-style');
+ }
+ }
+ };
+
+ Y.DOM.CUSTOM_STYLES.width = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.width = val;
+ } else {
+ Y.log('invalid style value for width: ' + val, 'warn', 'dom-style');
+ }
+ }
+ };
+}
+
+// TODO: top, right, bottom, left
+IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
+
+IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
+
+IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
+ IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
+ ComputedStyle.getBorderWidth;
+
+IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
+ IEComputed.marginLeft = ComputedStyle.getMargin;
+
+IEComputed.visibility = ComputedStyle.getVisibility;
+IEComputed.borderColor = IEComputed.borderTopColor =
+ IEComputed.borderRightColor = IEComputed.borderBottomColor =
+ IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
+
+if (!Y.config.win[GET_COMPUTED_STYLE]) {
+ Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
+}
+
+Y.namespace('DOM.IE');
+Y.DOM.IE.COMPUTED = IEComputed;
+Y.DOM.IE.ComputedStyle = ComputedStyle;
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
+YUI.add('dom-screen', function(Y) {
+
+(function(Y) {
+
+/**
+ * Adds position and region management functionality to DOM.
+ * @module dom
+ * @submodule dom-screen
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ COMPAT_MODE = 'compatMode',
+ POSITION = 'position',
+ FIXED = 'fixed',
+ RELATIVE = 'relative',
+ LEFT = 'left',
+ TOP = 'top',
+ _BACK_COMPAT = 'BackCompat',
+ MEDIUM = 'medium',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ // TODO: how about thead/tbody/tfoot/tr?
+ // TODO: does caption matter?
+ RE_TABLE = /^t(?:able|d|h)$/i;
+
+Y.mix(Y.DOM, {
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @method winHeight
+ * @return {Number} The current height of the viewport.
+ */
+ winHeight: function(node) {
+ var h = Y.DOM._getWinSize(node).height;
+ Y.log('winHeight returning ' + h, 'info', 'dom-screen');
+ return h;
+ },
+
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @method winWidth
+ * @return {Number} The current width of the viewport.
+ */
+ winWidth: function(node) {
+ var w = Y.DOM._getWinSize(node).width;
+ Y.log('winWidth returning ' + w, 'info', 'dom-screen');
+ return w;
+ },
+
+ /**
+ * Document height
+ * @method docHeight
+ * @return {Number} The current height of the document.
+ */
+ docHeight: function(node) {
+ var h = Y.DOM._getDocSize(node).height;
+ Y.log('docHeight returning ' + h, 'info', 'dom-screen');
+ return Math.max(h, Y.DOM._getWinSize(node).height);
+ },
+
+ /**
+ * Document width
+ * @method docWidth
+ * @return {Number} The current width of the document.
+ */
+ docWidth: function(node) {
+ var w = Y.DOM._getDocSize(node).width;
+ Y.log('docWidth returning ' + w, 'info', 'dom-screen');
+ return Math.max(w, Y.DOM._getWinSize(node).width);
+ },
+
+ /**
+ * Amount page has been scroll horizontally
+ * @method docScrollX
+ * @return {Number} The current amount the screen is scrolled horizontally.
+ */
+ docScrollX: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
+ },
+
+ /**
+ * Amount page has been scroll vertically
+ * @method docScrollY
+ * @return {Number} The current amount the screen is scrolled vertically.
+ */
+ docScrollY: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
+ },
+
+ /**
+ * Gets the current position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getXY
+ * @param element The target element
+ * @return {Array} The XY position of the element
+
+ TODO: test inDocument/display?
+ */
+ getXY: function() {
+ if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
+ return function(node) {
+ var xy = null,
+ scrollLeft,
+ scrollTop,
+ box,
+ off1, off2,
+ bLeft, bTop,
+ mode,
+ doc;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ scrollLeft = Y.DOM.docScrollX(node);
+ scrollTop = Y.DOM.docScrollY(node);
+ box = node[GET_BOUNDING_CLIENT_RECT]();
+ doc = Y.DOM._getDoc(node);
+ xy = [box.left, box.top];
+
+ if (Y.UA.ie) {
+ off1 = 2;
+ off2 = 2;
+ mode = doc[COMPAT_MODE];
+ bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
+ bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
+
+ if (Y.UA.ie === 6) {
+ if (mode !== _BACK_COMPAT) {
+ off1 = 0;
+ off2 = 0;
+ }
+ }
+
+ if ((mode == _BACK_COMPAT)) {
+ if (bLeft !== MEDIUM) {
+ off1 = parseInt(bLeft, 10);
+ }
+ if (bTop !== MEDIUM) {
+ off2 = parseInt(bTop, 10);
+ }
+ }
+
+ xy[0] -= off1;
+ xy[1] -= off2;
+
+ }
+
+ if ((scrollTop || scrollLeft)) {
+ xy[0] += scrollLeft;
+ xy[1] += scrollTop;
+ }
+ } else { // default to current offsets
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+ return xy;
+ };
+ } else {
+ return function(node) { // manually calculate by crawling up offsetParents
+ //Calculate the Top and Left border sizes (assumes pixels)
+ var xy = null,
+ parentNode,
+ bCheck,
+ scrollTop,
+ scrollLeft;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ xy = [node.offsetLeft, node.offsetTop];
+ parentNode = node;
+ // TODO: refactor with !! or just falsey
+ bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
+
+ // TODO: worth refactoring for TOP/LEFT only?
+ while ((parentNode = parentNode.offsetParent)) {
+ xy[0] += parentNode.offsetLeft;
+ xy[1] += parentNode.offsetTop;
+ if (bCheck) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+ }
+
+ // account for any scrolled ancestors
+ if (Y.DOM.getStyle(node, POSITION) != FIXED) {
+ parentNode = node;
+
+ while ((parentNode = parentNode.parentNode)) {
+ scrollTop = parentNode.scrollTop;
+ scrollLeft = parentNode.scrollLeft;
+
+ //Firefox does something funky with borders when overflow is not visible.
+ if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+
+
+ if (scrollTop || scrollLeft) {
+ xy[0] -= scrollLeft;
+ xy[1] -= scrollTop;
+ }
+ }
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+
+ } else {
+ //Fix FIXED position -- add scrollbars
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+ }
+ } else {
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+
+ return xy;
+ };
+ }
+ }(),// NOTE: Executing for loadtime branching
+
+ _getOffset: function(node) {
+ var pos,
+ xy = null;
+
+ if (node) {
+ pos = Y.DOM.getStyle(node, POSITION);
+ xy = [
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10),
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10)
+ ];
+
+ if ( isNaN(xy[0]) ) { // in case of 'auto'
+ xy[0] = parseInt(Y.DOM.getStyle(node, LEFT), 10); // try inline
+ if ( isNaN(xy[0]) ) { // default to offset value
+ xy[0] = (pos === RELATIVE) ? 0 : node.offsetLeft || 0;
+ }
+ }
+
+ if ( isNaN(xy[1]) ) { // in case of 'auto'
+ xy[1] = parseInt(Y.DOM.getStyle(node, TOP), 10); // try inline
+ if ( isNaN(xy[1]) ) { // default to offset value
+ xy[1] = (pos === RELATIVE) ? 0 : node.offsetTop || 0;
+ }
+ }
+ }
+
+ return xy;
+
+ },
+
+ /**
+ * Gets the current X position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getX
+ * @param element The target element
+ * @return {Int} The X position of the element
+ */
+
+ getX: function(node) {
+ return Y.DOM.getXY(node)[0];
+ },
+
+ /**
+ * Gets the current Y position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getY
+ * @param element The target element
+ * @return {Int} The Y position of the element
+ */
+
+ getY: function(node) {
+ return Y.DOM.getXY(node)[1];
+ },
+
+ /**
+ * Set the position of an html element in page coordinates.
+ * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setXY
+ * @param element The target element
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
+ */
+ setXY: function(node, xy, noRetry) {
+ var setStyle = Y.DOM.setStyle,
+ pos,
+ delta,
+ newXY,
+ currentXY;
+
+ if (node && xy) {
+ pos = Y.DOM.getStyle(node, POSITION);
+
+ delta = Y.DOM._getOffset(node);
+
+ if (pos == 'static') { // default to relative
+ pos = RELATIVE;
+ setStyle(node, POSITION, pos);
+ }
+
+ currentXY = Y.DOM.getXY(node);
+
+ if (xy[0] !== null) {
+ setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
+ }
+
+ if (xy[1] !== null) {
+ setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
+ }
+
+ if (!noRetry) {
+ newXY = Y.DOM.getXY(node);
+ if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
+ Y.DOM.setXY(node, xy, true);
+ }
+ }
+
+ Y.log('setXY setting position to ' + xy, 'info', 'dom-screen');
+ } else {
+ Y.log('setXY failed to set ' + node + ' to ' + xy, 'info', 'dom-screen');
+ }
+ },
+
+ /**
+ * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setX
+ * @param element The target element
+ * @param {Int} x The X values for new position (coordinates are page-based)
+ */
+ setX: function(node, x) {
+ return Y.DOM.setXY(node, [x, null]);
+ },
+
+ /**
+ * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setY
+ * @param element The target element
+ * @param {Int} y The Y values for new position (coordinates are page-based)
+ */
+ setY: function(node, y) {
+ return Y.DOM.setXY(node, [null, y]);
+ },
+
+ _calcBorders: function(node, xy2) {
+ var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
+ l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
+ if (Y.UA.gecko) {
+ if (RE_TABLE.test(node.tagName)) {
+ t = 0;
+ l = 0;
+ }
+ }
+ xy2[0] += l;
+ xy2[1] += t;
+ return xy2;
+ },
+
+ _getWinSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ win = doc.defaultView || doc.parentWindow,
+ mode = doc[COMPAT_MODE],
+ h = win.innerHeight,
+ w = win.innerWidth,
+ root = doc[DOCUMENT_ELEMENT];
+
+ if ( mode && !Y.UA.opera ) { // IE, Gecko
+ if (mode != 'CSS1Compat') { // Quirks
+ root = doc.body;
+ }
+ h = root.clientHeight;
+ w = root.clientWidth;
+ }
+ return { height: h, width: w };
+ },
+
+ _getDocSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ root = doc[DOCUMENT_ELEMENT];
+
+ if (doc[COMPAT_MODE] != 'CSS1Compat') {
+ root = doc.body;
+ }
+
+ return { height: root.scrollHeight, width: root.scrollWidth };
+ }
+});
+})(Y);
+(function(Y) {
+var TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+
+ getOffsets = function(r1, r2) {
+ var t = Math.max(r1[TOP], r2[TOP]),
+ r = Math.min(r1[RIGHT], r2[RIGHT]),
+ b = Math.min(r1[BOTTOM], r2[BOTTOM]),
+ l = Math.max(r1[LEFT], r2[LEFT]),
+ ret = {};
+
+ ret[TOP] = t;
+ ret[RIGHT] = r;
+ ret[BOTTOM] = b;
+ ret[LEFT] = l;
+ return ret;
+ },
+
+ DOM = Y.DOM;
+
+Y.mix(DOM, {
+ /**
+ * Returns an Object literal containing the following about this element: (top, right, bottom, left)
+ * @method region
+ * @param {HTMLElement} element The DOM element.
+ @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
+ */
+ region: function(node) {
+ var xy = DOM.getXY(node),
+ ret = false;
+
+ if (node && xy) {
+ ret = DOM._getRegion(
+ xy[1], // top
+ xy[0] + node.offsetWidth, // right
+ xy[1] + node.offsetHeight, // bottom
+ xy[0] // left
+ );
+ }
+
+ return ret;
+ },
+
+ /**
+ * Find the intersect information for the passes nodes.
+ * @method intersect
+ * @param {HTMLElement} element The first element
+ * @param {HTMLElement | Object} element2 The element or region to check the interect with
+ * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
+ @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
+ */
+ intersect: function(node, node2, altRegion) {
+ var r = altRegion || DOM.region(node), region = {},
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ off = getOffsets(region, r);
+ return {
+ top: off[TOP],
+ right: off[RIGHT],
+ bottom: off[BOTTOM],
+ left: off[LEFT],
+ area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
+ yoff: ((off[BOTTOM] - off[TOP])),
+ xoff: (off[RIGHT] - off[LEFT]),
+ inRegion: DOM.inRegion(node, node2, false, altRegion)
+ };
+
+ },
+ /**
+ * Check if any part of this node is in the passed region
+ * @method inRegion
+ * @param {Object} node2 The node to get the region from or an Object literal of the region
+ * $param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inRegion: function(node, node2, all, altRegion) {
+ var region = {},
+ r = altRegion || DOM.region(node),
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ if (all) {
+ return (
+ r[LEFT] >= region[LEFT] &&
+ r[RIGHT] <= region[RIGHT] &&
+ r[TOP] >= region[TOP] &&
+ r[BOTTOM] <= region[BOTTOM] );
+ } else {
+ off = getOffsets(region, r);
+ if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ },
+
+ /**
+ * Check if any part of this element is in the viewport
+ * @method inViewportRegion
+ * @param {HTMLElement} element The DOM element.
+ * @param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inViewportRegion: function(node, all, altRegion) {
+ return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
+
+ },
+
+ _getRegion: function(t, r, b, l) {
+ var region = {};
+
+ region[TOP] = region[1] = t;
+ region[LEFT] = region[0] = l;
+ region[BOTTOM] = b;
+ region[RIGHT] = r;
+ region.width = region[RIGHT] - region[LEFT];
+ region.height = region[BOTTOM] - region[TOP];
+
+ return region;
+ },
+
+ /**
+ * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
+ * @method viewportRegion
+ @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
+ */
+ viewportRegion: function(node) {
+ node = node || Y.config.doc.documentElement;
+ var ret = false,
+ scrollX,
+ scrollY;
+
+ if (node) {
+ scrollX = DOM.docScrollX(node);
+ scrollY = DOM.docScrollY(node);
+
+ ret = DOM._getRegion(scrollY, // top
+ DOM.winWidth(node) + scrollX, // right
+ scrollY + DOM.winHeight(node), // bottom
+ scrollX); // left
+ }
+
+ return ret;
+ }
+});
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base', 'dom-style']});
+YUI.add('selector-native', function(Y) {
+
+(function(Y) {
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
+
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
+
+Y.namespace('Selector'); // allow native module to standalone
+
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TMP_PREFIX = 'yui-tmp-',
+ g_counter = 0;
+
+var Selector = {
+ _foundCache: [],
+
+ useNative: true,
+
+ _compare: ('sourceIndex' in document.documentElement) ?
+ function(nodeA, nodeB) {
+ var a = nodeA.sourceIndex,
+ b = nodeB.sourceIndex;
+
+ if (a === b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ }
+
+ return -1;
+
+ } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
+ function(nodeA, nodeB) {
+ if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } :
+ function(nodeA, nodeB) {
+ var rangeA, rangeB, compare;
+ if (nodeA && nodeB) {
+ rangeA = nodeA[OWNER_DOCUMENT].createRange();
+ rangeA.setStart(nodeA, 0);
+ rangeB = nodeB[OWNER_DOCUMENT].createRange();
+ rangeB.setStart(nodeB, 0);
+ compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
+ }
+
+ return compare;
+
+ }),
+
+ _sort: function(nodes) {
+ if (nodes) {
+ nodes = Y.Array(nodes, 0, true);
+ if (nodes.sort) {
+ nodes.sort(Selector._compare);
+ }
+ }
+
+ return nodes;
+ },
+
+ _deDupe: function(nodes) {
+ var ret = [],
+ i, node;
+
+ for (i = 0; (node = nodes[i++]);) {
+ if (!node._found) {
+ ret[ret.length] = node;
+ node._found = true;
+ }
+ }
+
+ for (i = 0; (node = ret[i++]);) {
+ node._found = null;
+ node.removeAttribute('_found');
+ }
+
+ return ret;
+ },
+
+ /**
+ * Retrieves a set of nodes based on a given CSS selector.
+ * @method query
+ *
+ * @param {string} selector The CSS Selector to test the node against.
+ * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
+ * @param {Boolean} firstOnly optional Whether or not to return only the first match.
+ * @return {Array} An array of nodes that match the given selector.
+ * @static
+ */
+ query: function(selector, root, firstOnly, skipNative) {
+ root = root || Y.config.doc;
+ var ret = [],
+ useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
+ queries = [[selector, root]],
+ query,
+ result,
+ i,
+ fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
+
+ if (selector && fn) {
+ // split group into seperate queries
+ if (!skipNative && // already done if skipping
+ (!useNative || root.tagName)) { // split native when element scoping is needed
+ queries = Selector._splitQueries(selector, root);
+ }
+
+ for (i = 0; (query = queries[i++]);) {
+ result = fn(query[0], query[1], firstOnly);
+ if (!firstOnly) { // coerce DOM Collection to Array
+ result = Y.Array(result, 0, true);
+ }
+ if (result) {
+ ret = ret.concat(result);
+ }
+ }
+
+ if (queries.length > 1) { // remove dupes and sort by doc order
+ ret = Selector._sort(Selector._deDupe(ret));
+ }
+ }
+
+ Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
+ return (firstOnly) ? (ret[0] || null) : ret;
+
+ },
+
+ // allows element scoped queries to begin with combinator
+ // e.g. query('> p', document.body) === query('body > p')
+ _splitQueries: function(selector, node) {
+ var groups = selector.split(','),
+ queries = [],
+ prefix = '',
+ i, len;
+
+ if (node) {
+ // enforce for element scoping
+ if (node.tagName) {
+ node.id = node.id || Y.guid();
+ prefix = '#' + node.id + ' ';
+ }
+
+ for (i = 0, len = groups.length; i < len; ++i) {
+ selector = prefix + groups[i];
+ queries.push([selector, node]);
+ }
+ }
+
+ return queries;
+ },
+
+ _nativeQuery: function(selector, root, one) {
+ try {
+ //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
+ return root['querySelector' + (one ? '' : 'All')](selector);
+ } catch(e) { // fallback to brute if available
+ //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
+ return Y.Selector.query(selector, root, one, true); // redo with skipNative true
+ }
+ },
+
+ filter: function(nodes, selector) {
+ var ret = [],
+ i, node;
+
+ if (nodes && selector) {
+ for (i = 0; (node = nodes[i++]);) {
+ if (Y.Selector.test(node, selector)) {
+ ret[ret.length] = node;
+ }
+ }
+ } else {
+ Y.log('invalid filter input (nodes: ' + nodes +
+ ', selector: ' + selector + ')', 'warn', 'Selector');
+ }
+
+ return ret;
+ },
+
+ test: function(node, selector, root) {
+ var ret = false,
+ groups = selector.split(','),
+ item,
+ i, group;
+
+ if (node && node.tagName) { // only test HTMLElements
+ root = root || node.ownerDocument;
+
+ if (!node.id) {
+ node.id = TMP_PREFIX + g_counter++;
+ }
+ for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
+ group += '#' + node.id; // add ID for uniqueness
+ item = Y.Selector.query(group, root, true);
+ ret = (item === node);
+ if (ret) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+};
+
+Y.mix(Y.Selector, Selector, true);
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
+YUI.add('selector-css2', function(Y) {
+
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
+
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
+
+ return ret || [];
+ },
+
+ _regexCache: {},
+
+ _re: {
+ attr: /(\[.*\])/g,
+ pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
+ },
+
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
+ },
+
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
+
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
+
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ id,
+ className,
+ tagName;
+
+
+ // if we have an initial ID, set to root when in document
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ // try ID first
+ if (id) {
+ if (rootDoc.getElementById(id)) { // if in document
+ nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
+ }
+ // try className if supported
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else if (tagName) { // default to tagName
+ nodes = root.getElementsByTagName(tagName || '*');
+ }
+
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
+ }
+ }
+
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
+
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (operator.test && !operator.test(value)) || // regex test
+ (operator.call && !operator(tmpNode, test[0]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
+
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
+ }
+
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
+
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
+ }
+ break;
+ }
+ }
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ _getRegExp: function(str, flags) {
+ var regexCache = Selector._regexCache;
+ flags = flags || '';
+ if (!regexCache[str + flags]) {
+ regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return regexCache[str + flags];
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
+ }
+ },
+
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Y.Selector.operators,
+ test;
+
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ document.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
+ token[match[1]] = match[3];
+ }
+
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
+ }
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
+ }
+ }
+ }
+ ],
+
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
+
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
+
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
+
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
+
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
+
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
+
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
+
+ if (!found || selector.length) { // not fully parsed
+ Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
+ tokens = [];
+ }
+ return tokens;
+ },
+
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
+ pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
+ re, i, len;
+
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
+ }
+
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
+ }
+
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
+ }
+ }
+
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
+ }
+ }
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
+ }
+ }
+ return selector;
+ },
+
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
+
+// IE wants class with native queries
+if (Y.Selector.useNative && document.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
+
+
+
+}, '3.0.0' ,{requires:['selector-native']});
+
+
+YUI.add('selector', function(Y){}, '3.0.0' ,{use:['selector-native', 'selector-css2']});
+
+
+
+YUI.add('dom', function(Y){}, '3.0.0' ,{use:['dom-base', 'dom-style', 'dom-screen', 'selector']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dom-base",function(D){(function(H){var R="nodeType",F="ownerDocument",E="defaultView",J="parentWindow",M="tagName",O="parentNode",Q="firstChild",L="previousSibling",P="nextSibling",K="contains",G="compareDocumentPosition",N=document.documentElement,I=/<([a-z]+)/i;H.DOM={byId:function(T,S){S=S||H.config.doc;return S.getElementById(T);},children:function(U,S){var T=[];if(U){S=S||"*";T=H.Selector.query("> "+S,U);}return T;},firstByTag:function(S,T){var U;T=T||H.config.doc;if(S&&T.getElementsByTagName){U=T.getElementsByTagName(S)[0];}return U||null;},getText:(N.textContent!==undefined)?function(T){var S="";if(T){S=T.textContent;}return S||"";}:function(T){var S="";if(T){S=T.innerText;}return S||"";},setText:(N.textContent!==undefined)?function(S,T){if(S){S.textContent=T;}}:function(S,T){if(S){S.innerText=T;}},previous:function(S,U,T){return H.DOM.elementByAxis(S,L,U,T);},next:function(S,U,T){return H.DOM.elementByAxis(S,P,U,T);},ancestor:function(S,U,T){return H.DOM.elementByAxis(S,O,U,T);},elementByAxis:function(S,V,U,T){while(S&&(S=S[V])){if((T||S[M])&&(!U||U(S))){return S;}}return null;},contains:function(T,U){var S=false;if(!U||!T||!U[R]||!T[R]){S=false;}else{if(T[K]){if(H.UA.opera||U[R]===1){S=T[K](U);}else{S=H.DOM._bruteContains(T,U);}}else{if(T[G]){if(T===U||!!(T[G](U)&16)){S=true;}}}}return S;},inDoc:function(S,T){T=T||S[F];var U=S.id;if(!U){U=S.id=H.guid();}return !!(T.getElementById(U));},create:function(X,Z){if(typeof X==="string"){X=H.Lang.trim(X);}if(!Z&&H.DOM._cloneCache[X]){return H.DOM._cloneCache[X].cloneNode(true);}Z=Z||H.config.doc;var T=I.exec(X),W=H.DOM._create,Y=H.DOM.creators,V=null,S,U;if(T&&Y[T[1]]){if(typeof Y[T[1]]==="function"){W=Y[T[1]];}else{S=Y[T[1]];}}U=W(X,Z,S).childNodes;if(U.length===1){V=U[0].parentNode.removeChild(U[0]);}else{V=H.DOM._nl2frag(U,Z);}if(V){H.DOM._cloneCache[X]=V.cloneNode(true);}return V;},_nl2frag:function(T,W){var U=null,V,S;if(T&&(T.push||T.item)&&T[0]){W=W||T[0].ownerDocument;U=W.createDocumentFragment();if(T.item){T=H.Array(T,0,true);}for(V=0,S=T.length;V<S;V++){U.appendChild(T[V]);}}return U;},CUSTOM_ATTRIBUTES:(!N.hasAttribute)?{"for":"htmlFor","class":"className"}:{"htmlFor":"for","className":"class"},setAttribute:function(U,S,V,T){if(U&&U.setAttribute){S=H.DOM.CUSTOM_ATTRIBUTES[S]||S;U.setAttribute(S,V,T);}},getAttribute:function(V,S,U){U=(U!==undefined)?U:2;var T="";if(V&&V.getAttribute){S=H.DOM.CUSTOM_ATTRIBUTES[S]||S;T=V.getAttribute(S,U);if(T===null){T="";}}return T;},isWindow:function(S){return S.alert&&S.document;},_fragClones:{div:document.createElement("div")},_create:function(T,U,S){S=S||"div";var V=H.DOM._fragClones[S];if(V){V=V.cloneNode(false);}else{V=H.DOM._fragClones[S]=U.createElement(S);}V.innerHTML=T;return V;},_removeChildNodes:function(S){while(S.firstChild){S.removeChild(S.firstChild);}},_cloneCache:{},addHTML:function(W,V,T){if(typeof V==="string"){V=H.Lang.trim(V);}var U=H.DOM._cloneCache[V],S=W.parentNode;if(U){U=U.cloneNode(true);}else{if(V.nodeType){U=V;}else{U=H.DOM.create(V);}}if(T){if(T.nodeType){T.parentNode.insertBefore(U,T);}else{switch(T){case"replace":while(W.firstChild){W.removeChild(W.firstChild);}W.appendChild(U);break;case"before":S.insertBefore(U,W);break;case"after":if(W.nextSibling){S.insertBefore(U,W.nextSibling);}else{S.appendChild(U);}break;default:W.appendChild(U);}}}else{W.appendChild(U);}return U;},VALUE_SETTERS:{},VALUE_GETTERS:{},getValue:function(U){var T="",S;if(U&&U[M]){S=H.DOM.VALUE_GETTERS[U[M].toLowerCase()];if(S){T=S(U);}else{T=U.value;}}return(typeof T==="string")?T:"";},setValue:function(S,T){var U;if(S&&S[M]){U=H.DOM.VALUE_SETTERS[S[M].toLowerCase()];if(U){U(S,T);}else{S.value=T;}}},_bruteContains:function(S,T){while(T){if(S===T){return true;}T=T.parentNode;}return false;},_getRegExp:function(T,S){S=S||"";H.DOM._regexCache=H.DOM._regexCache||{};if(!H.DOM._regexCache[T+S]){H.DOM._regexCache[T+S]=new RegExp(T,S);}return H.DOM._regexCache[T+S];},_getDoc:function(S){S=S||{};return(S[R]===9)?S:S[F]||S.document||H.config.doc;},_getWin:function(S){var T=H.DOM._getDoc(S);return T[E]||T[J]||H.config.win;},_batch:function(V,Z,Y,U,T,X){Z=(typeof name==="string")?H.DOM[Z]:Z;var S,W=[];if(Z&&V){H.each(V,function(a){if((S=Z.call(H.DOM,a,Y,U,T,X))!==undefined){W[W.length]=S;}});}return W.length?W:V;},_testElement:function(T,S,U){S=(S&&S!=="*")?S.toUpperCase():null;return(T&&T[M]&&(!S||T[M].toUpperCase()===S)&&(!U||U(T)));},creators:{},_IESimpleCreate:function(S,T){T=T||H.config.doc;return T.createElement(S);}};(function(W){var X=W.DOM.creators,S=W.DOM.create,V=/(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,U="<table>",T="</table>";if(W.UA.ie){W.mix(X,{tbody:function(Z,a){var b=S(U+Z+T,a),Y=b.children.tags("tbody")[0];if(b.children.length>1&&Y&&!V.test(Z)){Y[O].removeChild(Y);}return b;},script:function(Y,Z){var a=Z.createElement("div");a.innerHTML="-"+Y;a.removeChild(a[Q]);return a;}},true);W.mix(W.DOM.VALUE_GETTERS,{button:function(Y){return(Y.attributes&&Y.attributes.value)?Y.attributes.value.value:"";}});W.mix(W.DOM.VALUE_SETTERS,{button:function(Z,a){var Y=Z.attributes.value;if(!Y){Y=Z[F].createAttribute("value");Z.setAttributeNode(Y);}Y.value=a;}});}if(W.UA.gecko||W.UA.ie){W.mix(X,{option:function(Y,Z){return S("<select>"+Y+"</select>",Z);},tr:function(Y,Z){return S("<tbody>"+Y+"</tbody>",Z);},td:function(Y,Z){return S("<tr>"+Y+"</tr>",Z);},tbody:function(Y,Z){return S(U+Y+T,Z);}});W.mix(X,{legend:"fieldset",th:X.td,thead:X.tbody,tfoot:X.tbody,caption:X.tbody,colgroup:X.tbody,col:X.tbody,optgroup:X.option});}W.mix(W.DOM.VALUE_GETTERS,{option:function(Z){var Y=Z.attributes;return(Y.value&&Y.value.specified)?Z.value:Z.text;},select:function(Z){var a=Z.value,Y=Z.options;if(Y&&a===""){if(Z.multiple){}else{a=W.DOM.getValue(Y[Z.selectedIndex],"value");}}return a;}});})(H);})(D);var B,A,C;D.mix(D.DOM,{hasClass:function(G,F){var E=D.DOM._getRegExp("(?:^|\\s+)"+F+"(?:\\s+|$)");return E.test(G.className);},addClass:function(F,E){if(!D.DOM.hasClass(F,E)){F.className=D.Lang.trim([F.className,E].join(" "));
+}},removeClass:function(F,E){if(E&&A(F,E)){F.className=D.Lang.trim(F.className.replace(D.DOM._getRegExp("(?:^|\\s+)"+E+"(?:\\s+|$)")," "));if(A(F,E)){C(F,E);}}},replaceClass:function(F,E,G){B(F,G);C(F,E);},toggleClass:function(F,E){if(A(F,E)){C(F,E);}else{B(F,E);}}});A=D.DOM.hasClass;C=D.DOM.removeClass;B=D.DOM.addClass;},"3.0.0",{requires:["oop"]});YUI.add("dom-style",function(A){(function(E){var C="documentElement",B="defaultView",D="ownerDocument",L="style",N="float",F="cssFloat",G="styleFloat",J="transparent",H="getComputedStyle",M=E.config.doc,I=undefined,K=/color$/i;E.mix(E.DOM,{CUSTOM_STYLES:{},setStyle:function(R,O,S,Q){Q=Q||R.style;var P=E.DOM.CUSTOM_STYLES;if(Q){if(S===null){S="";}if(O in P){if(P[O].set){P[O].set(R,S,Q);return;}else{if(typeof P[O]==="string"){O=P[O];}}}Q[O]=S;}},getStyle:function(R,O){var Q=R[L],P=E.DOM.CUSTOM_STYLES,S="";if(Q){if(O in P){if(P[O].get){return P[O].get(R,O,Q);}else{if(typeof P[O]==="string"){O=P[O];}}}S=Q[O];if(S===""){S=E.DOM[H](R,O);}}return S;},setStyles:function(P,Q){var O=P.style;E.each(Q,function(R,S){E.DOM.setStyle(P,S,R,O);},E.DOM);},getComputedStyle:function(P,O){var R="",Q=P[D];if(P[L]){R=Q[B][H](P,null)[O];}return R;}});if(M[C][L][F]!==I){E.DOM.CUSTOM_STYLES[N]=F;}else{if(M[C][L][G]!==I){E.DOM.CUSTOM_STYLES[N]=G;}}if(E.UA.opera){E.DOM[H]=function(Q,P){var O=Q[D][B],R=O[H](Q,"")[P];if(K.test(P)){R=E.Color.toRGB(R);}return R;};}if(E.UA.webkit){E.DOM[H]=function(Q,P){var O=Q[D][B],R=O[H](Q,"")[P];if(R==="rgba(0, 0, 0, 0)"){R=J;}return R;};}})(A);(function(D){var B=parseInt,C=RegExp;D.Color={KEYWORDS:{black:"000",silver:"c0c0c0",gray:"808080",white:"fff",maroon:"800000",red:"f00",purple:"800080",fuchsia:"f0f",green:"008000",lime:"0f0",olive:"808000",yellow:"ff0",navy:"000080",blue:"00f",teal:"008080",aqua:"0ff"},re_RGB:/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,re_hex:/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,re_hex3:/([0-9A-F])/gi,toRGB:function(E){if(!D.Color.re_RGB.test(E)){E=D.Color.toHex(E);}if(D.Color.re_hex.exec(E)){E="rgb("+[B(C.$1,16),B(C.$2,16),B(C.$3,16)].join(", ")+")";}return E;},toHex:function(F){F=D.Color.KEYWORDS[F]||F;if(D.Color.re_RGB.exec(F)){F=[Number(C.$1).toString(16),Number(C.$2).toString(16),Number(C.$3).toString(16)];for(var E=0;E<F.length;E++){if(F[E].length<2){F[E]=F[E].replace(D.Color.re_hex3,"$1$1");}}F="#"+F.join("");}if(F.length<6){F=F.replace(D.Color.re_hex3,"$1$1");}if(F!=="transparent"&&F.indexOf("#")<0){F="#"+F;}return F.toLowerCase();}};})(A);(function(D){var W="hasLayout",K="px",L="filter",B="filters",T="opacity",M="auto",G="borderWidth",J="borderTopWidth",Q="borderRightWidth",V="borderBottomWidth",H="borderLeftWidth",I="width",O="height",R="transparent",S="visible",C="getComputedStyle",Z=undefined,X=document.documentElement,P=/^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,E=function(Y){return Y.currentStyle||Y.style;},N={CUSTOM_STYLES:{},get:function(Y,b){var a="",c;if(Y){c=E(Y)[b];if(b===T&&D.DOM.CUSTOM_STYLES[T]){a=D.DOM.CUSTOM_STYLES[T].get(Y);}else{if(!c||(c.indexOf&&c.indexOf(K)>-1)){a=c;}else{if(D.DOM.IE.COMPUTED[b]){a=D.DOM.IE.COMPUTED[b](Y,b);}else{if(P.test(c)){a=N.getPixel(Y,b)+K;}else{a=c;}}}}}return a;},sizeOffsets:{width:["Left","Right"],height:["Top","Bottom"],top:["Top"],bottom:["Bottom"]},getOffset:function(b,g){var d=E(b)[g],Y=g.charAt(0).toUpperCase()+g.substr(1),f="offset"+Y,a="pixel"+Y,e=N.sizeOffsets[g],c="";if(d===M||d.indexOf("%")>-1){c=b["offset"+Y];if(e[0]){c-=N.getPixel(b,"padding"+e[0]);c-=N.getBorderWidth(b,"border"+e[0]+"Width",1);}if(e[1]){c-=N.getPixel(b,"padding"+e[1]);c-=N.getBorderWidth(b,"border"+e[1]+"Width",1);}}else{if(!b.style[a]&&!b.style[g]){b.style[g]=d;}c=b.style[a];}return c+K;},borderMap:{thin:"2px",medium:"4px",thick:"6px"},getBorderWidth:function(a,c,Y){var b=Y?"":K,d=a.currentStyle[c];if(d.indexOf(K)<0){if(N.borderMap[d]){d=N.borderMap[d];}else{}}return(Y)?parseFloat(d):d;},getPixel:function(b,Y){var d=null,a=E(b),e=a.right,c=a[Y];b.style.right=c;d=b.style.pixelRight;b.style.right=e;return d;},getMargin:function(b,Y){var c,a=E(b);if(a[Y]==M){c=0;}else{c=N.getPixel(b,Y);}return c+K;},getVisibility:function(a,Y){var b;while((b=a.currentStyle)&&b[Y]=="inherit"){a=a.parentNode;}return(b)?b[Y]:S;},getColor:function(a,Y){var b=E(a)[Y];if(!b||b===R){D.DOM.elementByAxis(a,"parentNode",null,function(c){b=E(c)[Y];if(b&&b!==R){a=c;return true;}});}return D.Color.toRGB(b);},getBorderColor:function(a,Y){var b=E(a),c=b[Y]||b.color;return D.Color.toRGB(D.Color.toHex(c));}},F={};try{if(X.style[T]===Z&&X[B]){D.DOM.CUSTOM_STYLES[T]={get:function(a){var c=100;try{c=a[B]["DXImageTransform.Microsoft.Alpha"][T];}catch(b){try{c=a[B]("alpha")[T];}catch(Y){}}return c/100;},set:function(a,d,Y){var c,b;if(d===""){b=E(a);c=(T in b)?b[T]:1;d=c;}if(typeof Y[L]=="string"){Y[L]="alpha("+T+"="+d*100+")";if(!a.currentStyle||!a.currentStyle[W]){Y.zoom=1;}}}};}}catch(U){}try{document.createElement("div").style.height="-1px";}catch(U){D.DOM.CUSTOM_STYLES.height={set:function(b,c,a){var Y=parseFloat(c);if(isNaN(Y)||Y>=0){a.height=c;}else{}}};D.DOM.CUSTOM_STYLES.width={set:function(b,c,a){var Y=parseFloat(c);if(isNaN(Y)||Y>=0){a.width=c;}else{}}};}F[I]=F[O]=N.getOffset;F.color=F.backgroundColor=N.getColor;F[G]=F[J]=F[Q]=F[V]=F[H]=N.getBorderWidth;F.marginTop=F.marginRight=F.marginBottom=F.marginLeft=N.getMargin;F.visibility=N.getVisibility;F.borderColor=F.borderTopColor=F.borderRightColor=F.borderBottomColor=F.borderLeftColor=N.getBorderColor;if(!D.config.win[C]){D.DOM[C]=N.get;}D.namespace("DOM.IE");D.DOM.IE.COMPUTED=F;D.DOM.IE.ComputedStyle=N;})(A);},"3.0.0",{requires:["dom-base"]});YUI.add("dom-screen",function(A){(function(F){var D="documentElement",O="compatMode",M="position",C="fixed",K="relative",G="left",H="top",I="BackCompat",N="medium",E="borderLeftWidth",B="borderTopWidth",P="getBoundingClientRect",J="getComputedStyle",L=/^t(?:able|d|h)$/i;F.mix(F.DOM,{winHeight:function(R){var Q=F.DOM._getWinSize(R).height;return Q;},winWidth:function(R){var Q=F.DOM._getWinSize(R).width;
+return Q;},docHeight:function(R){var Q=F.DOM._getDocSize(R).height;return Math.max(Q,F.DOM._getWinSize(R).height);},docWidth:function(R){var Q=F.DOM._getDocSize(R).width;return Math.max(Q,F.DOM._getWinSize(R).width);},docScrollX:function(Q){var R=F.DOM._getDoc(Q);return Math.max(R[D].scrollLeft,R.body.scrollLeft);},docScrollY:function(Q){var R=F.DOM._getDoc(Q);return Math.max(R[D].scrollTop,R.body.scrollTop);},getXY:function(){if(document[D][P]){return function(T){var a=null,U,R,V,Y,X,Q,S,W,Z;if(T){if(F.DOM.inDoc(T)){U=F.DOM.docScrollX(T);R=F.DOM.docScrollY(T);V=T[P]();Z=F.DOM._getDoc(T);a=[V.left,V.top];if(F.UA.ie){Y=2;X=2;W=Z[O];Q=F.DOM[J](Z[D],E);S=F.DOM[J](Z[D],B);if(F.UA.ie===6){if(W!==I){Y=0;X=0;}}if((W==I)){if(Q!==N){Y=parseInt(Q,10);}if(S!==N){X=parseInt(S,10);}}a[0]-=Y;a[1]-=X;}if((R||U)){a[0]+=U;a[1]+=R;}}else{a=F.DOM._getOffset(T);}}return a;};}else{return function(R){var T=null,Q,V,S,U;if(R){if(F.DOM.inDoc(R)){T=[R.offsetLeft,R.offsetTop];Q=R;V=((F.UA.gecko||F.UA.webkit>519)?true:false);while((Q=Q.offsetParent)){T[0]+=Q.offsetLeft;T[1]+=Q.offsetTop;if(V){T=F.DOM._calcBorders(Q,T);}}if(F.DOM.getStyle(R,M)!=C){Q=R;while((Q=Q.parentNode)){S=Q.scrollTop;U=Q.scrollLeft;if(F.UA.gecko&&(F.DOM.getStyle(Q,"overflow")!=="visible")){T=F.DOM._calcBorders(Q,T);}if(S||U){T[0]-=U;T[1]-=S;}}T[0]+=F.DOM.docScrollX(R);T[1]+=F.DOM.docScrollY(R);}else{T[0]+=F.DOM.docScrollX(R);T[1]+=F.DOM.docScrollY(R);}}else{T=F.DOM._getOffset(R);}}return T;};}}(),_getOffset:function(Q){var S,R=null;if(Q){S=F.DOM.getStyle(Q,M);R=[parseInt(F.DOM[J](Q,G),10),parseInt(F.DOM[J](Q,H),10)];if(isNaN(R[0])){R[0]=parseInt(F.DOM.getStyle(Q,G),10);if(isNaN(R[0])){R[0]=(S===K)?0:Q.offsetLeft||0;}}if(isNaN(R[1])){R[1]=parseInt(F.DOM.getStyle(Q,H),10);if(isNaN(R[1])){R[1]=(S===K)?0:Q.offsetTop||0;}}}return R;},getX:function(Q){return F.DOM.getXY(Q)[0];},getY:function(Q){return F.DOM.getXY(Q)[1];},setXY:function(R,U,X){var S=F.DOM.setStyle,W,V,Q,T;if(R&&U){W=F.DOM.getStyle(R,M);V=F.DOM._getOffset(R);if(W=="static"){W=K;S(R,M,W);}T=F.DOM.getXY(R);if(U[0]!==null){S(R,G,U[0]-T[0]+V[0]+"px");}if(U[1]!==null){S(R,H,U[1]-T[1]+V[1]+"px");}if(!X){Q=F.DOM.getXY(R);if(Q[0]!==U[0]||Q[1]!==U[1]){F.DOM.setXY(R,U,true);}}}else{}},setX:function(R,Q){return F.DOM.setXY(R,[Q,null]);},setY:function(Q,R){return F.DOM.setXY(Q,[null,R]);},_calcBorders:function(S,T){var R=parseInt(F.DOM[J](S,B),10)||0,Q=parseInt(F.DOM[J](S,E),10)||0;if(F.UA.gecko){if(L.test(S.tagName)){R=0;Q=0;}}T[0]+=Q;T[1]+=R;return T;},_getWinSize:function(T){var V=F.DOM._getDoc(),U=V.defaultView||V.parentWindow,W=V[O],S=U.innerHeight,R=U.innerWidth,Q=V[D];if(W&&!F.UA.opera){if(W!="CSS1Compat"){Q=V.body;}S=Q.clientHeight;R=Q.clientWidth;}return{height:S,width:R};},_getDocSize:function(R){var S=F.DOM._getDoc(),Q=S[D];if(S[O]!="CSS1Compat"){Q=S.body;}return{height:Q.scrollHeight,width:Q.scrollWidth};}});})(A);(function(G){var D="top",C="right",H="bottom",B="left",F=function(L,K){var N=Math.max(L[D],K[D]),O=Math.min(L[C],K[C]),I=Math.min(L[H],K[H]),J=Math.max(L[B],K[B]),M={};M[D]=N;M[C]=O;M[H]=I;M[B]=J;return M;},E=G.DOM;G.mix(E,{region:function(J){var K=E.getXY(J),I=false;if(J&&K){I=E._getRegion(K[1],K[0]+J.offsetWidth,K[1]+J.offsetHeight,K[0]);}return I;},intersect:function(K,I,M){var J=M||E.region(K),L={},O=I,N;if(O.tagName){L=E.region(O);}else{if(G.Lang.isObject(I)){L=I;}else{return false;}}N=F(L,J);return{top:N[D],right:N[C],bottom:N[H],left:N[B],area:((N[H]-N[D])*(N[C]-N[B])),yoff:((N[H]-N[D])),xoff:(N[C]-N[B]),inRegion:E.inRegion(K,I,false,M)};},inRegion:function(L,I,J,N){var M={},K=N||E.region(L),P=I,O;if(P.tagName){M=E.region(P);}else{if(G.Lang.isObject(I)){M=I;}else{return false;}}if(J){return(K[B]>=M[B]&&K[C]<=M[C]&&K[D]>=M[D]&&K[H]<=M[H]);}else{O=F(M,K);if(O[H]>=O[D]&&O[C]>=O[B]){return true;}else{return false;}}},inViewportRegion:function(J,I,K){return E.inRegion(J,E.viewportRegion(J),I,K);},_getRegion:function(K,L,I,J){var M={};M[D]=M[1]=K;M[B]=M[0]=J;M[H]=I;M[C]=L;M.width=M[C]-M[B];M.height=M[H]-M[D];return M;},viewportRegion:function(J){J=J||G.config.doc.documentElement;var I=false,L,K;if(J){L=E.docScrollX(J);K=E.docScrollY(J);I=E._getRegion(K,E.winWidth(J)+L,K+E.winHeight(J),L);}return I;}});})(A);},"3.0.0",{requires:["dom-base","dom-style"]});YUI.add("selector-native",function(A){(function(G){G.namespace("Selector");var E="compareDocumentPosition",F="ownerDocument",D="yui-tmp-",C=0;var B={_foundCache:[],useNative:true,_compare:("sourceIndex" in document.documentElement)?function(K,J){var I=K.sourceIndex,H=J.sourceIndex;if(I===H){return 0;}else{if(I>H){return 1;}}return -1;}:(document.documentElement[E]?function(I,H){if(I[E](H)&4){return -1;}else{return 1;}}:function(L,K){var J,H,I;if(L&&K){J=L[F].createRange();J.setStart(L,0);H=K[F].createRange();H.setStart(K,0);I=J.compareBoundaryPoints(1,H);}return I;}),_sort:function(H){if(H){H=G.Array(H,0,true);if(H.sort){H.sort(B._compare);}}return H;},_deDupe:function(H){var I=[],J,K;for(J=0;(K=H[J++]);){if(!K._found){I[I.length]=K;K._found=true;}}for(J=0;(K=I[J++]);){K._found=null;K.removeAttribute("_found");}return I;},query:function(I,P,Q,H){P=P||G.config.doc;var M=[],J=(G.Selector.useNative&&document.querySelector&&!H),L=[[I,P]],N,R,K,O=(J)?G.Selector._nativeQuery:G.Selector._bruteQuery;if(I&&O){if(!H&&(!J||P.tagName)){L=B._splitQueries(I,P);}for(K=0;(N=L[K++]);){R=O(N[0],N[1],Q);if(!Q){R=G.Array(R,0,true);}if(R){M=M.concat(R);}}if(L.length>1){M=B._sort(B._deDupe(M));}}return(Q)?(M[0]||null):M;},_splitQueries:function(J,M){var I=J.split(","),K=[],N="",L,H;if(M){if(M.tagName){M.id=M.id||G.guid();N="#"+M.id+" ";}for(L=0,H=I.length;L<H;++L){J=N+I[L];K.push([J,M]);}}return K;},_nativeQuery:function(H,I,J){try{return I["querySelector"+(J?"":"All")](H);}catch(K){return G.Selector.query(H,I,J,true);}},filter:function(I,H){var J=[],K,L;if(I&&H){for(K=0;(L=I[K++]);){if(G.Selector.test(L,H)){J[J.length]=L;}}}else{}return J;},test:function(N,I,J){var K=false,H=I.split(","),M,L,O;if(N&&N.tagName){J=J||N.ownerDocument;if(!N.id){N.id=D+C++;
+}for(L=0;(O=H[L++]);){O+="#"+N.id;M=G.Selector.query(O,J,true);K=(M===N);if(K){break;}}}return K;}};G.mix(G.Selector,B,true);})(A);},"3.0.0",{requires:["dom-base"]});YUI.add("selector-css2",function(G){var H="parentNode",D="tagName",E="attributes",A="combinator",F="pseudos",C=G.Selector,B={SORT_RESULTS:true,_children:function(M,I){var J=M.children,L,K=[],N,O;if(M.children&&I&&M.children.tags){K=M.children.tags(I);}else{if((!J&&M[D])||(J&&I)){N=J||M.childNodes;J=[];for(L=0;(O=N[L++]);){if(O.tagName){if(!I||I===O.tagName){J.push(O);}}}}}return J||[];},_regexCache:{},_re:{attr:/(\[.*\])/g,pseudos:/:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i},shorthand:{"\\#(-?[_a-z]+[-\\w]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w]*)":"[className~=$1]"},operators:{"":function(J,I){return G.DOM.getAttribute(J,I)!=="";},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}-?"},pseudos:{"first-child":function(I){return G.Selector._children(I[H])[0]===I;}},_bruteQuery:function(M,Q,S){var N=[],I=[],P=C._tokenize(M),L=P[P.length-1],R=G.DOM._getDoc(Q),J,O,K;if(P[0]&&R===Q&&(J=P[0].id)&&R.getElementById(J)){Q=R.getElementById(J);}if(L){J=L.id;O=L.className;K=L.tagName||"*";if(J){if(R.getElementById(J)){I=[R.getElementById(J)];}}else{if(O){I=Q.getElementsByClassName(O);}else{if(K){I=Q.getElementsByTagName(K||"*");}}}if(I.length){N=C._filterNodes(I,P,S);}}return N;},_filterNodes:function(R,N,P){var W=0,V,X=N.length,Q=X-1,M=[],T=R[0],a=T,Y=G.Selector.getters,L,U,K,O,I,S,J,Z;for(W=0;(a=T=R[W++]);){Q=X-1;O=null;testLoop:while(a&&a.tagName){K=N[Q];J=K.tests;V=J.length;if(V&&!I){while((Z=J[--V])){L=Z[1];if(Y[Z[0]]){S=Y[Z[0]](a,Z[0]);}else{S=a[Z[0]];if(S===undefined&&a.getAttribute){S=a.getAttribute(Z[0]);}}if((L==="="&&S!==Z[2])||(L.test&&!L.test(S))||(L.call&&!L(a,Z[0]))){if((a=a[O])){while(a&&(!a.tagName||(K.tagName&&K.tagName!==a.tagName))){a=a[O];}}continue testLoop;}}}Q--;if(!I&&(U=K.combinator)){O=U.axis;a=a[O];while(a&&!a.tagName){a=a[O];}if(U.direct){O=null;}}else{M.push(T);if(P){return M;}break;}}}T=a=null;return M;},_getRegExp:function(K,I){var J=C._regexCache;I=I||"";if(!J[K+I]){J[K+I]=new RegExp(K,I);}return J[K+I];},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:true},"+":{axis:"previousSibling",direct:true}},_parsers:[{name:E,re:/^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,fn:function(K,L){var J=K[2]||"",I=G.Selector.operators,M;if((K[1]==="id"&&J==="=")||(K[1]==="className"&&document.getElementsByClassName&&(J==="~="||J==="="))){L.prefilter=K[1];L[K[1]]=K[3];}if(J in I){M=I[J];if(typeof M==="string"){M=G.Selector._getRegExp(M.replace("{val}",K[3]));}K[2]=M;}if(!L.last||L.prefilter!==K[1]){return K.slice(1);}}},{name:D,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(J,K){var I=J[1].toUpperCase();K.tagName=I;if(I!=="*"&&(!K.last||K.prefilter)){return[D,"=",I];}if(!K.prefilter){K.prefilter="tagName";}}},{name:A,re:/^\s*([>+~]|\s)\s*/,fn:function(I,J){}},{name:F,re:/^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,fn:function(I,J){var K=C[F][I[1]];if(K){return[I[2],K];}else{return false;}}}],_getToken:function(I){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]};},_tokenize:function(K){K=K||"";K=C._replaceShorthand(G.Lang.trim(K));var J=C._getToken(),P=K,O=[],Q=false,M,N,L,I;outer:do{Q=false;for(L=0;(I=C._parsers[L++]);){if((M=I.re.exec(K))){if(I!==A){J.selector=K;}K=K.replace(M[0],"");if(!K.length){J.last=true;}if(C._attrFilters[M[1]]){M[1]=C._attrFilters[M[1]];}N=I.fn(M,J);if(N===false){Q=false;break outer;}else{if(N){J.tests.push(N);}}if(!K.length||I.name===A){O.push(J);J=C._getToken(J);if(I.name===A){J.combinator=G.Selector.combinators[M[1]];}}Q=true;}}}while(Q&&K.length);if(!Q||K.length){O=[];}return O;},_replaceShorthand:function(J){var K=C.shorthand,L=J.match(C._re.attr),O=J.match(C._re.pseudos),N,M,I;if(O){J=J.replace(C._re.pseudos,"!!REPLACED_PSEUDO!!");}if(L){J=J.replace(C._re.attr,"!!REPLACED_ATTRIBUTE!!");}for(N in K){if(K.hasOwnProperty(N)){J=J.replace(C._getRegExp(N,"gi"),K[N]);}}if(L){for(M=0,I=L.length;M<I;++M){J=J.replace("!!REPLACED_ATTRIBUTE!!",L[M]);}}if(O){for(M=0,I=O.length;M<I;++M){J=J.replace("!!REPLACED_PSEUDO!!",O[M]);}}return J;},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(J,I){return G.DOM.getAttribute(J,I);}}};G.mix(G.Selector,B,true);G.Selector.getters.src=G.Selector.getters.rel=G.Selector.getters.href;if(G.Selector.useNative&&document.querySelector){G.Selector.shorthand["\\.(-?[_a-z]+[-\\w]*)"]="[class~=$1]";}},"3.0.0",{requires:["selector-native"]});YUI.add("selector",function(A){},"3.0.0",{use:["selector-native","selector-css2"]});YUI.add("dom",function(A){},"3.0.0",{use:["dom-base","dom-style","dom-screen","selector"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-screen', function(Y) {
+
+(function(Y) {
+
+/**
+ * Adds position and region management functionality to DOM.
+ * @module dom
+ * @submodule dom-screen
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ COMPAT_MODE = 'compatMode',
+ POSITION = 'position',
+ FIXED = 'fixed',
+ RELATIVE = 'relative',
+ LEFT = 'left',
+ TOP = 'top',
+ _BACK_COMPAT = 'BackCompat',
+ MEDIUM = 'medium',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ // TODO: how about thead/tbody/tfoot/tr?
+ // TODO: does caption matter?
+ RE_TABLE = /^t(?:able|d|h)$/i;
+
+Y.mix(Y.DOM, {
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @method winHeight
+ * @return {Number} The current height of the viewport.
+ */
+ winHeight: function(node) {
+ var h = Y.DOM._getWinSize(node).height;
+ Y.log('winHeight returning ' + h, 'info', 'dom-screen');
+ return h;
+ },
+
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @method winWidth
+ * @return {Number} The current width of the viewport.
+ */
+ winWidth: function(node) {
+ var w = Y.DOM._getWinSize(node).width;
+ Y.log('winWidth returning ' + w, 'info', 'dom-screen');
+ return w;
+ },
+
+ /**
+ * Document height
+ * @method docHeight
+ * @return {Number} The current height of the document.
+ */
+ docHeight: function(node) {
+ var h = Y.DOM._getDocSize(node).height;
+ Y.log('docHeight returning ' + h, 'info', 'dom-screen');
+ return Math.max(h, Y.DOM._getWinSize(node).height);
+ },
+
+ /**
+ * Document width
+ * @method docWidth
+ * @return {Number} The current width of the document.
+ */
+ docWidth: function(node) {
+ var w = Y.DOM._getDocSize(node).width;
+ Y.log('docWidth returning ' + w, 'info', 'dom-screen');
+ return Math.max(w, Y.DOM._getWinSize(node).width);
+ },
+
+ /**
+ * Amount page has been scroll horizontally
+ * @method docScrollX
+ * @return {Number} The current amount the screen is scrolled horizontally.
+ */
+ docScrollX: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
+ },
+
+ /**
+ * Amount page has been scroll vertically
+ * @method docScrollY
+ * @return {Number} The current amount the screen is scrolled vertically.
+ */
+ docScrollY: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
+ },
+
+ /**
+ * Gets the current position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getXY
+ * @param element The target element
+ * @return {Array} The XY position of the element
+
+ TODO: test inDocument/display?
+ */
+ getXY: function() {
+ if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
+ return function(node) {
+ var xy = null,
+ scrollLeft,
+ scrollTop,
+ box,
+ off1, off2,
+ bLeft, bTop,
+ mode,
+ doc;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ scrollLeft = Y.DOM.docScrollX(node);
+ scrollTop = Y.DOM.docScrollY(node);
+ box = node[GET_BOUNDING_CLIENT_RECT]();
+ doc = Y.DOM._getDoc(node);
+ xy = [box.left, box.top];
+
+ if (Y.UA.ie) {
+ off1 = 2;
+ off2 = 2;
+ mode = doc[COMPAT_MODE];
+ bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
+ bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
+
+ if (Y.UA.ie === 6) {
+ if (mode !== _BACK_COMPAT) {
+ off1 = 0;
+ off2 = 0;
+ }
+ }
+
+ if ((mode == _BACK_COMPAT)) {
+ if (bLeft !== MEDIUM) {
+ off1 = parseInt(bLeft, 10);
+ }
+ if (bTop !== MEDIUM) {
+ off2 = parseInt(bTop, 10);
+ }
+ }
+
+ xy[0] -= off1;
+ xy[1] -= off2;
+
+ }
+
+ if ((scrollTop || scrollLeft)) {
+ xy[0] += scrollLeft;
+ xy[1] += scrollTop;
+ }
+ } else { // default to current offsets
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+ return xy;
+ };
+ } else {
+ return function(node) { // manually calculate by crawling up offsetParents
+ //Calculate the Top and Left border sizes (assumes pixels)
+ var xy = null,
+ parentNode,
+ bCheck,
+ scrollTop,
+ scrollLeft;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ xy = [node.offsetLeft, node.offsetTop];
+ parentNode = node;
+ // TODO: refactor with !! or just falsey
+ bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
+
+ // TODO: worth refactoring for TOP/LEFT only?
+ while ((parentNode = parentNode.offsetParent)) {
+ xy[0] += parentNode.offsetLeft;
+ xy[1] += parentNode.offsetTop;
+ if (bCheck) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+ }
+
+ // account for any scrolled ancestors
+ if (Y.DOM.getStyle(node, POSITION) != FIXED) {
+ parentNode = node;
+
+ while ((parentNode = parentNode.parentNode)) {
+ scrollTop = parentNode.scrollTop;
+ scrollLeft = parentNode.scrollLeft;
+
+ //Firefox does something funky with borders when overflow is not visible.
+ if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+
+
+ if (scrollTop || scrollLeft) {
+ xy[0] -= scrollLeft;
+ xy[1] -= scrollTop;
+ }
+ }
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+
+ } else {
+ //Fix FIXED position -- add scrollbars
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+ }
+ } else {
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+
+ return xy;
+ };
+ }
+ }(),// NOTE: Executing for loadtime branching
+
+ _getOffset: function(node) {
+ var pos,
+ xy = null;
+
+ if (node) {
+ pos = Y.DOM.getStyle(node, POSITION);
+ xy = [
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10),
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10)
+ ];
+
+ if ( isNaN(xy[0]) ) { // in case of 'auto'
+ xy[0] = parseInt(Y.DOM.getStyle(node, LEFT), 10); // try inline
+ if ( isNaN(xy[0]) ) { // default to offset value
+ xy[0] = (pos === RELATIVE) ? 0 : node.offsetLeft || 0;
+ }
+ }
+
+ if ( isNaN(xy[1]) ) { // in case of 'auto'
+ xy[1] = parseInt(Y.DOM.getStyle(node, TOP), 10); // try inline
+ if ( isNaN(xy[1]) ) { // default to offset value
+ xy[1] = (pos === RELATIVE) ? 0 : node.offsetTop || 0;
+ }
+ }
+ }
+
+ return xy;
+
+ },
+
+ /**
+ * Gets the current X position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getX
+ * @param element The target element
+ * @return {Int} The X position of the element
+ */
+
+ getX: function(node) {
+ return Y.DOM.getXY(node)[0];
+ },
+
+ /**
+ * Gets the current Y position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getY
+ * @param element The target element
+ * @return {Int} The Y position of the element
+ */
+
+ getY: function(node) {
+ return Y.DOM.getXY(node)[1];
+ },
+
+ /**
+ * Set the position of an html element in page coordinates.
+ * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setXY
+ * @param element The target element
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
+ */
+ setXY: function(node, xy, noRetry) {
+ var setStyle = Y.DOM.setStyle,
+ pos,
+ delta,
+ newXY,
+ currentXY;
+
+ if (node && xy) {
+ pos = Y.DOM.getStyle(node, POSITION);
+
+ delta = Y.DOM._getOffset(node);
+
+ if (pos == 'static') { // default to relative
+ pos = RELATIVE;
+ setStyle(node, POSITION, pos);
+ }
+
+ currentXY = Y.DOM.getXY(node);
+
+ if (xy[0] !== null) {
+ setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
+ }
+
+ if (xy[1] !== null) {
+ setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
+ }
+
+ if (!noRetry) {
+ newXY = Y.DOM.getXY(node);
+ if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
+ Y.DOM.setXY(node, xy, true);
+ }
+ }
+
+ Y.log('setXY setting position to ' + xy, 'info', 'dom-screen');
+ } else {
+ Y.log('setXY failed to set ' + node + ' to ' + xy, 'info', 'dom-screen');
+ }
+ },
+
+ /**
+ * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setX
+ * @param element The target element
+ * @param {Int} x The X values for new position (coordinates are page-based)
+ */
+ setX: function(node, x) {
+ return Y.DOM.setXY(node, [x, null]);
+ },
+
+ /**
+ * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setY
+ * @param element The target element
+ * @param {Int} y The Y values for new position (coordinates are page-based)
+ */
+ setY: function(node, y) {
+ return Y.DOM.setXY(node, [null, y]);
+ },
+
+ _calcBorders: function(node, xy2) {
+ var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
+ l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
+ if (Y.UA.gecko) {
+ if (RE_TABLE.test(node.tagName)) {
+ t = 0;
+ l = 0;
+ }
+ }
+ xy2[0] += l;
+ xy2[1] += t;
+ return xy2;
+ },
+
+ _getWinSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ win = doc.defaultView || doc.parentWindow,
+ mode = doc[COMPAT_MODE],
+ h = win.innerHeight,
+ w = win.innerWidth,
+ root = doc[DOCUMENT_ELEMENT];
+
+ if ( mode && !Y.UA.opera ) { // IE, Gecko
+ if (mode != 'CSS1Compat') { // Quirks
+ root = doc.body;
+ }
+ h = root.clientHeight;
+ w = root.clientWidth;
+ }
+ return { height: h, width: w };
+ },
+
+ _getDocSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ root = doc[DOCUMENT_ELEMENT];
+
+ if (doc[COMPAT_MODE] != 'CSS1Compat') {
+ root = doc.body;
+ }
+
+ return { height: root.scrollHeight, width: root.scrollWidth };
+ }
+});
+})(Y);
+(function(Y) {
+var TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+
+ getOffsets = function(r1, r2) {
+ var t = Math.max(r1[TOP], r2[TOP]),
+ r = Math.min(r1[RIGHT], r2[RIGHT]),
+ b = Math.min(r1[BOTTOM], r2[BOTTOM]),
+ l = Math.max(r1[LEFT], r2[LEFT]),
+ ret = {};
+
+ ret[TOP] = t;
+ ret[RIGHT] = r;
+ ret[BOTTOM] = b;
+ ret[LEFT] = l;
+ return ret;
+ },
+
+ DOM = Y.DOM;
+
+Y.mix(DOM, {
+ /**
+ * Returns an Object literal containing the following about this element: (top, right, bottom, left)
+ * @method region
+ * @param {HTMLElement} element The DOM element.
+ @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
+ */
+ region: function(node) {
+ var xy = DOM.getXY(node),
+ ret = false;
+
+ if (node && xy) {
+ ret = DOM._getRegion(
+ xy[1], // top
+ xy[0] + node.offsetWidth, // right
+ xy[1] + node.offsetHeight, // bottom
+ xy[0] // left
+ );
+ }
+
+ return ret;
+ },
+
+ /**
+ * Find the intersect information for the passes nodes.
+ * @method intersect
+ * @param {HTMLElement} element The first element
+ * @param {HTMLElement | Object} element2 The element or region to check the interect with
+ * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
+ @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
+ */
+ intersect: function(node, node2, altRegion) {
+ var r = altRegion || DOM.region(node), region = {},
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ off = getOffsets(region, r);
+ return {
+ top: off[TOP],
+ right: off[RIGHT],
+ bottom: off[BOTTOM],
+ left: off[LEFT],
+ area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
+ yoff: ((off[BOTTOM] - off[TOP])),
+ xoff: (off[RIGHT] - off[LEFT]),
+ inRegion: DOM.inRegion(node, node2, false, altRegion)
+ };
+
+ },
+ /**
+ * Check if any part of this node is in the passed region
+ * @method inRegion
+ * @param {Object} node2 The node to get the region from or an Object literal of the region
+ * $param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inRegion: function(node, node2, all, altRegion) {
+ var region = {},
+ r = altRegion || DOM.region(node),
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ if (all) {
+ return (
+ r[LEFT] >= region[LEFT] &&
+ r[RIGHT] <= region[RIGHT] &&
+ r[TOP] >= region[TOP] &&
+ r[BOTTOM] <= region[BOTTOM] );
+ } else {
+ off = getOffsets(region, r);
+ if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ },
+
+ /**
+ * Check if any part of this element is in the viewport
+ * @method inViewportRegion
+ * @param {HTMLElement} element The DOM element.
+ * @param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inViewportRegion: function(node, all, altRegion) {
+ return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
+
+ },
+
+ _getRegion: function(t, r, b, l) {
+ var region = {};
+
+ region[TOP] = region[1] = t;
+ region[LEFT] = region[0] = l;
+ region[BOTTOM] = b;
+ region[RIGHT] = r;
+ region.width = region[RIGHT] - region[LEFT];
+ region.height = region[BOTTOM] - region[TOP];
+
+ return region;
+ },
+
+ /**
+ * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
+ * @method viewportRegion
+ @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
+ */
+ viewportRegion: function(node) {
+ node = node || Y.config.doc.documentElement;
+ var ret = false,
+ scrollX,
+ scrollY;
+
+ if (node) {
+ scrollX = DOM.docScrollX(node);
+ scrollY = DOM.docScrollY(node);
+
+ ret = DOM._getRegion(scrollY, // top
+ DOM.winWidth(node) + scrollX, // right
+ scrollY + DOM.winHeight(node), // bottom
+ scrollX); // left
+ }
+
+ return ret;
+ }
+});
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base', 'dom-style']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dom-screen",function(A){(function(F){var D="documentElement",O="compatMode",M="position",C="fixed",K="relative",G="left",H="top",I="BackCompat",N="medium",E="borderLeftWidth",B="borderTopWidth",P="getBoundingClientRect",J="getComputedStyle",L=/^t(?:able|d|h)$/i;F.mix(F.DOM,{winHeight:function(R){var Q=F.DOM._getWinSize(R).height;return Q;},winWidth:function(R){var Q=F.DOM._getWinSize(R).width;return Q;},docHeight:function(R){var Q=F.DOM._getDocSize(R).height;return Math.max(Q,F.DOM._getWinSize(R).height);},docWidth:function(R){var Q=F.DOM._getDocSize(R).width;return Math.max(Q,F.DOM._getWinSize(R).width);},docScrollX:function(Q){var R=F.DOM._getDoc(Q);return Math.max(R[D].scrollLeft,R.body.scrollLeft);},docScrollY:function(Q){var R=F.DOM._getDoc(Q);return Math.max(R[D].scrollTop,R.body.scrollTop);},getXY:function(){if(document[D][P]){return function(T){var a=null,U,R,V,Y,X,Q,S,W,Z;if(T){if(F.DOM.inDoc(T)){U=F.DOM.docScrollX(T);R=F.DOM.docScrollY(T);V=T[P]();Z=F.DOM._getDoc(T);a=[V.left,V.top];if(F.UA.ie){Y=2;X=2;W=Z[O];Q=F.DOM[J](Z[D],E);S=F.DOM[J](Z[D],B);if(F.UA.ie===6){if(W!==I){Y=0;X=0;}}if((W==I)){if(Q!==N){Y=parseInt(Q,10);}if(S!==N){X=parseInt(S,10);}}a[0]-=Y;a[1]-=X;}if((R||U)){a[0]+=U;a[1]+=R;}}else{a=F.DOM._getOffset(T);}}return a;};}else{return function(R){var T=null,Q,V,S,U;if(R){if(F.DOM.inDoc(R)){T=[R.offsetLeft,R.offsetTop];Q=R;V=((F.UA.gecko||F.UA.webkit>519)?true:false);while((Q=Q.offsetParent)){T[0]+=Q.offsetLeft;T[1]+=Q.offsetTop;if(V){T=F.DOM._calcBorders(Q,T);}}if(F.DOM.getStyle(R,M)!=C){Q=R;while((Q=Q.parentNode)){S=Q.scrollTop;U=Q.scrollLeft;if(F.UA.gecko&&(F.DOM.getStyle(Q,"overflow")!=="visible")){T=F.DOM._calcBorders(Q,T);}if(S||U){T[0]-=U;T[1]-=S;}}T[0]+=F.DOM.docScrollX(R);T[1]+=F.DOM.docScrollY(R);}else{T[0]+=F.DOM.docScrollX(R);T[1]+=F.DOM.docScrollY(R);}}else{T=F.DOM._getOffset(R);}}return T;};}}(),_getOffset:function(Q){var S,R=null;if(Q){S=F.DOM.getStyle(Q,M);R=[parseInt(F.DOM[J](Q,G),10),parseInt(F.DOM[J](Q,H),10)];if(isNaN(R[0])){R[0]=parseInt(F.DOM.getStyle(Q,G),10);if(isNaN(R[0])){R[0]=(S===K)?0:Q.offsetLeft||0;}}if(isNaN(R[1])){R[1]=parseInt(F.DOM.getStyle(Q,H),10);if(isNaN(R[1])){R[1]=(S===K)?0:Q.offsetTop||0;}}}return R;},getX:function(Q){return F.DOM.getXY(Q)[0];},getY:function(Q){return F.DOM.getXY(Q)[1];},setXY:function(R,U,X){var S=F.DOM.setStyle,W,V,Q,T;if(R&&U){W=F.DOM.getStyle(R,M);V=F.DOM._getOffset(R);if(W=="static"){W=K;S(R,M,W);}T=F.DOM.getXY(R);if(U[0]!==null){S(R,G,U[0]-T[0]+V[0]+"px");}if(U[1]!==null){S(R,H,U[1]-T[1]+V[1]+"px");}if(!X){Q=F.DOM.getXY(R);if(Q[0]!==U[0]||Q[1]!==U[1]){F.DOM.setXY(R,U,true);}}}else{}},setX:function(R,Q){return F.DOM.setXY(R,[Q,null]);},setY:function(Q,R){return F.DOM.setXY(Q,[null,R]);},_calcBorders:function(S,T){var R=parseInt(F.DOM[J](S,B),10)||0,Q=parseInt(F.DOM[J](S,E),10)||0;if(F.UA.gecko){if(L.test(S.tagName)){R=0;Q=0;}}T[0]+=Q;T[1]+=R;return T;},_getWinSize:function(T){var V=F.DOM._getDoc(),U=V.defaultView||V.parentWindow,W=V[O],S=U.innerHeight,R=U.innerWidth,Q=V[D];if(W&&!F.UA.opera){if(W!="CSS1Compat"){Q=V.body;}S=Q.clientHeight;R=Q.clientWidth;}return{height:S,width:R};},_getDocSize:function(R){var S=F.DOM._getDoc(),Q=S[D];if(S[O]!="CSS1Compat"){Q=S.body;}return{height:Q.scrollHeight,width:Q.scrollWidth};}});})(A);(function(G){var D="top",C="right",H="bottom",B="left",F=function(L,K){var N=Math.max(L[D],K[D]),O=Math.min(L[C],K[C]),I=Math.min(L[H],K[H]),J=Math.max(L[B],K[B]),M={};M[D]=N;M[C]=O;M[H]=I;M[B]=J;return M;},E=G.DOM;G.mix(E,{region:function(J){var K=E.getXY(J),I=false;if(J&&K){I=E._getRegion(K[1],K[0]+J.offsetWidth,K[1]+J.offsetHeight,K[0]);}return I;},intersect:function(K,I,M){var J=M||E.region(K),L={},O=I,N;if(O.tagName){L=E.region(O);}else{if(G.Lang.isObject(I)){L=I;}else{return false;}}N=F(L,J);return{top:N[D],right:N[C],bottom:N[H],left:N[B],area:((N[H]-N[D])*(N[C]-N[B])),yoff:((N[H]-N[D])),xoff:(N[C]-N[B]),inRegion:E.inRegion(K,I,false,M)};},inRegion:function(L,I,J,N){var M={},K=N||E.region(L),P=I,O;if(P.tagName){M=E.region(P);}else{if(G.Lang.isObject(I)){M=I;}else{return false;}}if(J){return(K[B]>=M[B]&&K[C]<=M[C]&&K[D]>=M[D]&&K[H]<=M[H]);}else{O=F(M,K);if(O[H]>=O[D]&&O[C]>=O[B]){return true;}else{return false;}}},inViewportRegion:function(J,I,K){return E.inRegion(J,E.viewportRegion(J),I,K);},_getRegion:function(K,L,I,J){var M={};M[D]=M[1]=K;M[B]=M[0]=J;M[H]=I;M[C]=L;M.width=M[C]-M[B];M.height=M[H]-M[D];return M;},viewportRegion:function(J){J=J||G.config.doc.documentElement;var I=false,L,K;if(J){L=E.docScrollX(J);K=E.docScrollY(J);I=E._getRegion(K,E.winWidth(J)+L,K+E.winHeight(J),L);}return I;}});})(A);},"3.0.0",{requires:["dom-base","dom-style"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-screen', function(Y) {
+
+(function(Y) {
+
+/**
+ * Adds position and region management functionality to DOM.
+ * @module dom
+ * @submodule dom-screen
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ COMPAT_MODE = 'compatMode',
+ POSITION = 'position',
+ FIXED = 'fixed',
+ RELATIVE = 'relative',
+ LEFT = 'left',
+ TOP = 'top',
+ _BACK_COMPAT = 'BackCompat',
+ MEDIUM = 'medium',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ // TODO: how about thead/tbody/tfoot/tr?
+ // TODO: does caption matter?
+ RE_TABLE = /^t(?:able|d|h)$/i;
+
+Y.mix(Y.DOM, {
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @method winHeight
+ * @return {Number} The current height of the viewport.
+ */
+ winHeight: function(node) {
+ var h = Y.DOM._getWinSize(node).height;
+ return h;
+ },
+
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @method winWidth
+ * @return {Number} The current width of the viewport.
+ */
+ winWidth: function(node) {
+ var w = Y.DOM._getWinSize(node).width;
+ return w;
+ },
+
+ /**
+ * Document height
+ * @method docHeight
+ * @return {Number} The current height of the document.
+ */
+ docHeight: function(node) {
+ var h = Y.DOM._getDocSize(node).height;
+ return Math.max(h, Y.DOM._getWinSize(node).height);
+ },
+
+ /**
+ * Document width
+ * @method docWidth
+ * @return {Number} The current width of the document.
+ */
+ docWidth: function(node) {
+ var w = Y.DOM._getDocSize(node).width;
+ return Math.max(w, Y.DOM._getWinSize(node).width);
+ },
+
+ /**
+ * Amount page has been scroll horizontally
+ * @method docScrollX
+ * @return {Number} The current amount the screen is scrolled horizontally.
+ */
+ docScrollX: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
+ },
+
+ /**
+ * Amount page has been scroll vertically
+ * @method docScrollY
+ * @return {Number} The current amount the screen is scrolled vertically.
+ */
+ docScrollY: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
+ },
+
+ /**
+ * Gets the current position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getXY
+ * @param element The target element
+ * @return {Array} The XY position of the element
+
+ TODO: test inDocument/display?
+ */
+ getXY: function() {
+ if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
+ return function(node) {
+ var xy = null,
+ scrollLeft,
+ scrollTop,
+ box,
+ off1, off2,
+ bLeft, bTop,
+ mode,
+ doc;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ scrollLeft = Y.DOM.docScrollX(node);
+ scrollTop = Y.DOM.docScrollY(node);
+ box = node[GET_BOUNDING_CLIENT_RECT]();
+ doc = Y.DOM._getDoc(node);
+ xy = [box.left, box.top];
+
+ if (Y.UA.ie) {
+ off1 = 2;
+ off2 = 2;
+ mode = doc[COMPAT_MODE];
+ bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
+ bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
+
+ if (Y.UA.ie === 6) {
+ if (mode !== _BACK_COMPAT) {
+ off1 = 0;
+ off2 = 0;
+ }
+ }
+
+ if ((mode == _BACK_COMPAT)) {
+ if (bLeft !== MEDIUM) {
+ off1 = parseInt(bLeft, 10);
+ }
+ if (bTop !== MEDIUM) {
+ off2 = parseInt(bTop, 10);
+ }
+ }
+
+ xy[0] -= off1;
+ xy[1] -= off2;
+
+ }
+
+ if ((scrollTop || scrollLeft)) {
+ xy[0] += scrollLeft;
+ xy[1] += scrollTop;
+ }
+ } else { // default to current offsets
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+ return xy;
+ };
+ } else {
+ return function(node) { // manually calculate by crawling up offsetParents
+ //Calculate the Top and Left border sizes (assumes pixels)
+ var xy = null,
+ parentNode,
+ bCheck,
+ scrollTop,
+ scrollLeft;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ xy = [node.offsetLeft, node.offsetTop];
+ parentNode = node;
+ // TODO: refactor with !! or just falsey
+ bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
+
+ // TODO: worth refactoring for TOP/LEFT only?
+ while ((parentNode = parentNode.offsetParent)) {
+ xy[0] += parentNode.offsetLeft;
+ xy[1] += parentNode.offsetTop;
+ if (bCheck) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+ }
+
+ // account for any scrolled ancestors
+ if (Y.DOM.getStyle(node, POSITION) != FIXED) {
+ parentNode = node;
+
+ while ((parentNode = parentNode.parentNode)) {
+ scrollTop = parentNode.scrollTop;
+ scrollLeft = parentNode.scrollLeft;
+
+ //Firefox does something funky with borders when overflow is not visible.
+ if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+
+
+ if (scrollTop || scrollLeft) {
+ xy[0] -= scrollLeft;
+ xy[1] -= scrollTop;
+ }
+ }
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+
+ } else {
+ //Fix FIXED position -- add scrollbars
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+ }
+ } else {
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+
+ return xy;
+ };
+ }
+ }(),// NOTE: Executing for loadtime branching
+
+ _getOffset: function(node) {
+ var pos,
+ xy = null;
+
+ if (node) {
+ pos = Y.DOM.getStyle(node, POSITION);
+ xy = [
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10),
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10)
+ ];
+
+ if ( isNaN(xy[0]) ) { // in case of 'auto'
+ xy[0] = parseInt(Y.DOM.getStyle(node, LEFT), 10); // try inline
+ if ( isNaN(xy[0]) ) { // default to offset value
+ xy[0] = (pos === RELATIVE) ? 0 : node.offsetLeft || 0;
+ }
+ }
+
+ if ( isNaN(xy[1]) ) { // in case of 'auto'
+ xy[1] = parseInt(Y.DOM.getStyle(node, TOP), 10); // try inline
+ if ( isNaN(xy[1]) ) { // default to offset value
+ xy[1] = (pos === RELATIVE) ? 0 : node.offsetTop || 0;
+ }
+ }
+ }
+
+ return xy;
+
+ },
+
+ /**
+ * Gets the current X position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getX
+ * @param element The target element
+ * @return {Int} The X position of the element
+ */
+
+ getX: function(node) {
+ return Y.DOM.getXY(node)[0];
+ },
+
+ /**
+ * Gets the current Y position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getY
+ * @param element The target element
+ * @return {Int} The Y position of the element
+ */
+
+ getY: function(node) {
+ return Y.DOM.getXY(node)[1];
+ },
+
+ /**
+ * Set the position of an html element in page coordinates.
+ * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setXY
+ * @param element The target element
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
+ */
+ setXY: function(node, xy, noRetry) {
+ var setStyle = Y.DOM.setStyle,
+ pos,
+ delta,
+ newXY,
+ currentXY;
+
+ if (node && xy) {
+ pos = Y.DOM.getStyle(node, POSITION);
+
+ delta = Y.DOM._getOffset(node);
+
+ if (pos == 'static') { // default to relative
+ pos = RELATIVE;
+ setStyle(node, POSITION, pos);
+ }
+
+ currentXY = Y.DOM.getXY(node);
+
+ if (xy[0] !== null) {
+ setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
+ }
+
+ if (xy[1] !== null) {
+ setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
+ }
+
+ if (!noRetry) {
+ newXY = Y.DOM.getXY(node);
+ if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
+ Y.DOM.setXY(node, xy, true);
+ }
+ }
+
+ } else {
+ }
+ },
+
+ /**
+ * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setX
+ * @param element The target element
+ * @param {Int} x The X values for new position (coordinates are page-based)
+ */
+ setX: function(node, x) {
+ return Y.DOM.setXY(node, [x, null]);
+ },
+
+ /**
+ * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setY
+ * @param element The target element
+ * @param {Int} y The Y values for new position (coordinates are page-based)
+ */
+ setY: function(node, y) {
+ return Y.DOM.setXY(node, [null, y]);
+ },
+
+ _calcBorders: function(node, xy2) {
+ var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
+ l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
+ if (Y.UA.gecko) {
+ if (RE_TABLE.test(node.tagName)) {
+ t = 0;
+ l = 0;
+ }
+ }
+ xy2[0] += l;
+ xy2[1] += t;
+ return xy2;
+ },
+
+ _getWinSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ win = doc.defaultView || doc.parentWindow,
+ mode = doc[COMPAT_MODE],
+ h = win.innerHeight,
+ w = win.innerWidth,
+ root = doc[DOCUMENT_ELEMENT];
+
+ if ( mode && !Y.UA.opera ) { // IE, Gecko
+ if (mode != 'CSS1Compat') { // Quirks
+ root = doc.body;
+ }
+ h = root.clientHeight;
+ w = root.clientWidth;
+ }
+ return { height: h, width: w };
+ },
+
+ _getDocSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ root = doc[DOCUMENT_ELEMENT];
+
+ if (doc[COMPAT_MODE] != 'CSS1Compat') {
+ root = doc.body;
+ }
+
+ return { height: root.scrollHeight, width: root.scrollWidth };
+ }
+});
+})(Y);
+(function(Y) {
+var TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+
+ getOffsets = function(r1, r2) {
+ var t = Math.max(r1[TOP], r2[TOP]),
+ r = Math.min(r1[RIGHT], r2[RIGHT]),
+ b = Math.min(r1[BOTTOM], r2[BOTTOM]),
+ l = Math.max(r1[LEFT], r2[LEFT]),
+ ret = {};
+
+ ret[TOP] = t;
+ ret[RIGHT] = r;
+ ret[BOTTOM] = b;
+ ret[LEFT] = l;
+ return ret;
+ },
+
+ DOM = Y.DOM;
+
+Y.mix(DOM, {
+ /**
+ * Returns an Object literal containing the following about this element: (top, right, bottom, left)
+ * @method region
+ * @param {HTMLElement} element The DOM element.
+ @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
+ */
+ region: function(node) {
+ var xy = DOM.getXY(node),
+ ret = false;
+
+ if (node && xy) {
+ ret = DOM._getRegion(
+ xy[1], // top
+ xy[0] + node.offsetWidth, // right
+ xy[1] + node.offsetHeight, // bottom
+ xy[0] // left
+ );
+ }
+
+ return ret;
+ },
+
+ /**
+ * Find the intersect information for the passes nodes.
+ * @method intersect
+ * @param {HTMLElement} element The first element
+ * @param {HTMLElement | Object} element2 The element or region to check the interect with
+ * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
+ @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
+ */
+ intersect: function(node, node2, altRegion) {
+ var r = altRegion || DOM.region(node), region = {},
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ off = getOffsets(region, r);
+ return {
+ top: off[TOP],
+ right: off[RIGHT],
+ bottom: off[BOTTOM],
+ left: off[LEFT],
+ area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
+ yoff: ((off[BOTTOM] - off[TOP])),
+ xoff: (off[RIGHT] - off[LEFT]),
+ inRegion: DOM.inRegion(node, node2, false, altRegion)
+ };
+
+ },
+ /**
+ * Check if any part of this node is in the passed region
+ * @method inRegion
+ * @param {Object} node2 The node to get the region from or an Object literal of the region
+ * $param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inRegion: function(node, node2, all, altRegion) {
+ var region = {},
+ r = altRegion || DOM.region(node),
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ if (all) {
+ return (
+ r[LEFT] >= region[LEFT] &&
+ r[RIGHT] <= region[RIGHT] &&
+ r[TOP] >= region[TOP] &&
+ r[BOTTOM] <= region[BOTTOM] );
+ } else {
+ off = getOffsets(region, r);
+ if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ },
+
+ /**
+ * Check if any part of this element is in the viewport
+ * @method inViewportRegion
+ * @param {HTMLElement} element The DOM element.
+ * @param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inViewportRegion: function(node, all, altRegion) {
+ return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
+
+ },
+
+ _getRegion: function(t, r, b, l) {
+ var region = {};
+
+ region[TOP] = region[1] = t;
+ region[LEFT] = region[0] = l;
+ region[BOTTOM] = b;
+ region[RIGHT] = r;
+ region.width = region[RIGHT] - region[LEFT];
+ region.height = region[BOTTOM] - region[TOP];
+
+ return region;
+ },
+
+ /**
+ * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
+ * @method viewportRegion
+ @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
+ */
+ viewportRegion: function(node) {
+ node = node || Y.config.doc.documentElement;
+ var ret = false,
+ scrollX,
+ scrollY;
+
+ if (node) {
+ scrollX = DOM.docScrollX(node);
+ scrollY = DOM.docScrollY(node);
+
+ ret = DOM._getRegion(scrollY, // top
+ DOM.winWidth(node) + scrollX, // right
+ scrollY + DOM.winHeight(node), // bottom
+ scrollX); // left
+ }
+
+ return ret;
+ }
+});
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base', 'dom-style']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-style', function(Y) {
+
+(function(Y) {
+/**
+ * Add style management functionality to DOM.
+ * @module dom
+ * @submodule dom-style
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ DEFAULT_VIEW = 'defaultView',
+ OWNER_DOCUMENT = 'ownerDocument',
+ STYLE = 'style',
+ FLOAT = 'float',
+ CSS_FLOAT = 'cssFloat',
+ STYLE_FLOAT = 'styleFloat',
+ TRANSPARENT = 'transparent',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ DOCUMENT = Y.config.doc,
+ UNDEFINED = undefined,
+
+ re_color = /color$/i;
+
+
+Y.mix(Y.DOM, {
+ CUSTOM_STYLES: {
+ },
+
+
+ /**
+ * Sets a style property for a given element.
+ * @method setStyle
+ * @param {HTMLElement} An HTMLElement to apply the style to.
+ * @param {String} att The style property to set.
+ * @param {String|Number} val The value.
+ */
+ setStyle: function(node, att, val, style) {
+ style = style || node.style;
+ var CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES;
+
+ if (style) {
+ if (val === null) {
+ val = ''; // normalize for unsetting
+ }
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].set) {
+ CUSTOM_STYLES[att].set(node, val, style);
+ return; // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ style[att] = val;
+ }
+ },
+
+ /**
+ * Returns the current style value for the given property.
+ * @method getStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ */
+ getStyle: function(node, att) {
+ var style = node[STYLE],
+ CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES,
+ val = '';
+
+ if (style) {
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].get) {
+ return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ val = style[att];
+ if (val === '') { // TODO: is empty string sufficient?
+ val = Y.DOM[GET_COMPUTED_STYLE](node, att);
+ }
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets multiple style properties.
+ * @method setStyles
+ * @param {HTMLElement} node An HTMLElement to apply the styles to.
+ * @param {Object} hash An object literal of property:value pairs.
+ */
+ setStyles: function(node, hash) {
+ var style = node.style;
+ Y.each(hash, function(v, n) {
+ Y.DOM.setStyle(node, n, v, style);
+ }, Y.DOM);
+ },
+
+ /**
+ * Returns the computed style for the given node.
+ * @method getComputedStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ * @return {String} The computed value of the style property.
+ */
+ getComputedStyle: function(node, att) {
+ var val = '',
+ doc = node[OWNER_DOCUMENT];
+
+ if (node[STYLE]) {
+ val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
+ }
+ return val;
+ }
+});
+
+// normalize reserved word float alternatives ("cssFloat" or "styleFloat")
+if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
+} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
+}
+
+// fix opera computedStyle default color unit (convert to rgb)
+if (Y.UA.opera) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (re_color.test(att)) {
+ val = Y.Color.toRGB(val);
+ }
+
+ return val;
+ };
+
+}
+
+// safari converts transparent to rgba(), others use "transparent"
+if (Y.UA.webkit) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (val === 'rgba(0, 0, 0, 0)') {
+ val = TRANSPARENT;
+ }
+
+ return val;
+ };
+
+}
+})(Y);
+(function(Y) {
+var PARSE_INT = parseInt,
+ RE = RegExp;
+
+Y.Color = {
+ KEYWORDS: {
+ black: '000',
+ silver: 'c0c0c0',
+ gray: '808080',
+ white: 'fff',
+ maroon: '800000',
+ red: 'f00',
+ purple: '800080',
+ fuchsia: 'f0f',
+ green: '008000',
+ lime: '0f0',
+ olive: '808000',
+ yellow: 'ff0',
+ navy: '000080',
+ blue: '00f',
+ teal: '008080',
+ aqua: '0ff'
+ },
+
+ re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
+ re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
+ re_hex3: /([0-9A-F])/gi,
+
+ toRGB: function(val) {
+ if (!Y.Color.re_RGB.test(val)) {
+ val = Y.Color.toHex(val);
+ }
+
+ if(Y.Color.re_hex.exec(val)) {
+ val = 'rgb(' + [
+ PARSE_INT(RE.$1, 16),
+ PARSE_INT(RE.$2, 16),
+ PARSE_INT(RE.$3, 16)
+ ].join(', ') + ')';
+ }
+ return val;
+ },
+
+ toHex: function(val) {
+ val = Y.Color.KEYWORDS[val] || val;
+ if (Y.Color.re_RGB.exec(val)) {
+ val = [
+ Number(RE.$1).toString(16),
+ Number(RE.$2).toString(16),
+ Number(RE.$3).toString(16)
+ ];
+
+ for (var i = 0; i < val.length; i++) {
+ if (val[i].length < 2) {
+ val[i] = val[i].replace(Y.Color.re_hex3, '$1$1');
+ }
+ }
+
+ val = '#' + val.join('');
+ }
+
+ if (val.length < 6) {
+ val = val.replace(Y.Color.re_hex3, '$1$1');
+ }
+
+ if (val !== 'transparent' && val.indexOf('#') < 0) {
+ val = '#' + val;
+ }
+
+ return val.toLowerCase();
+ }
+};
+})(Y);
+
+(function(Y) {
+var HAS_LAYOUT = 'hasLayout',
+ PX = 'px',
+ FILTER = 'filter',
+ FILTERS = 'filters',
+ OPACITY = 'opacity',
+ AUTO = 'auto',
+
+ BORDER_WIDTH = 'borderWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ BORDER_RIGHT_WIDTH = 'borderRightWidth',
+ BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ WIDTH = 'width',
+ HEIGHT = 'height',
+ TRANSPARENT = 'transparent',
+ VISIBLE = 'visible',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+ UNDEFINED = undefined,
+ documentElement = document.documentElement,
+
+ // TODO: unit-less lineHeight (e.g. 1.22)
+ re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
+
+ _getStyleObj = function(node) {
+ return node.currentStyle || node.style;
+ },
+
+ ComputedStyle = {
+ CUSTOM_STYLES: {},
+
+ get: function(el, property) {
+ var value = '',
+ current;
+
+ if (el) {
+ current = _getStyleObj(el)[property];
+
+ if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
+ value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
+ } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
+ value = current;
+ } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
+ value = Y.DOM.IE.COMPUTED[property](el, property);
+ } else if (re_unit.test(current)) { // convert to pixel
+ value = ComputedStyle.getPixel(el, property) + PX;
+ } else {
+ value = current;
+ }
+ }
+
+ return value;
+ },
+
+ sizeOffsets: {
+ width: ['Left', 'Right'],
+ height: ['Top', 'Bottom'],
+ top: ['Top'],
+ bottom: ['Bottom']
+ },
+
+ getOffset: function(el, prop) {
+ var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
+ capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
+ offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
+ pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
+ sizeOffsets = ComputedStyle.sizeOffsets[prop],
+ value = '';
+
+ // IE pixelWidth incorrect for percent
+ // manually compute by subtracting padding and border from offset size
+ // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
+ // reverting to auto from auto causes position stacking issues (old impl)
+ if (current === AUTO || current.indexOf('%') > -1) {
+ value = el['offset' + capped];
+
+ if (sizeOffsets[0]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
+ }
+
+ if (sizeOffsets[1]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
+ }
+
+ } else { // use style.pixelWidth, etc. to convert to pixels
+ // need to map style.width to currentStyle (no currentStyle.pixelWidth)
+ if (!el.style[pixel] && !el.style[prop]) {
+ el.style[prop] = current;
+ }
+ value = el.style[pixel];
+
+ }
+ return value + PX;
+ },
+
+ borderMap: {
+ thin: '2px',
+ medium: '4px',
+ thick: '6px'
+ },
+
+ getBorderWidth: function(el, property, omitUnit) {
+ var unit = omitUnit ? '' : PX,
+ current = el.currentStyle[property];
+
+ if (current.indexOf(PX) < 0) { // look up keywords
+ if (ComputedStyle.borderMap[current]) {
+ current = ComputedStyle.borderMap[current];
+ } else {
+ Y.log('borderWidth computing not implemented', 'warn', 'dom-ie-style');
+ }
+ }
+ return (omitUnit) ? parseFloat(current) : current;
+ },
+
+ getPixel: function(node, att) {
+ // use pixelRight to convert to px
+ var val = null,
+ style = _getStyleObj(node),
+ styleRight = style.right,
+ current = style[att];
+
+ node.style.right = current;
+ val = node.style.pixelRight;
+ node.style.right = styleRight; // revert
+
+ return val;
+ },
+
+ getMargin: function(node, att) {
+ var val,
+ style = _getStyleObj(node);
+
+ if (style[att] == AUTO) {
+ val = 0;
+ } else {
+ val = ComputedStyle.getPixel(node, att);
+ }
+ return val + PX;
+ },
+
+ getVisibility: function(node, att) {
+ var current;
+ while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
+ node = node.parentNode;
+ }
+ return (current) ? current[att] : VISIBLE;
+ },
+
+ getColor: function(node, att) {
+ var current = _getStyleObj(node)[att];
+
+ if (!current || current === TRANSPARENT) {
+ Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
+ current = _getStyleObj(parent)[att];
+ if (current && current !== TRANSPARENT) {
+ node = parent;
+ return true;
+ }
+ });
+ }
+
+ return Y.Color.toRGB(current);
+ },
+
+ getBorderColor: function(node, att) {
+ var current = _getStyleObj(node),
+ val = current[att] || current.color;
+ return Y.Color.toRGB(Y.Color.toHex(val));
+ }
+ },
+
+ //fontSize: getPixelFont,
+ IEComputed = {};
+
+// use alpha filter for IE opacity
+try {
+ if (documentElement.style[OPACITY] === UNDEFINED &&
+ documentElement[FILTERS]) {
+ Y.DOM.CUSTOM_STYLES[OPACITY] = {
+ get: function(node) {
+ var val = 100;
+ try { // will error if no DXImageTransform
+ val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
+
+ } catch(e) {
+ try { // make sure its in the document
+ val = node[FILTERS]('alpha')[OPACITY];
+ } catch(err) {
+ Y.log('getStyle: IE opacity filter not found; returning 1', 'warn', 'dom-style');
+ }
+ }
+ return val / 100;
+ },
+
+ set: function(node, val, style) {
+ var current,
+ styleObj;
+
+ if (val === '') { // normalize inline style behavior
+ styleObj = _getStyleObj(node);
+ current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
+ val = current;
+ }
+
+ if (typeof style[FILTER] == 'string') { // in case not appended
+ style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
+
+ if (!node.currentStyle || !node.currentStyle[HAS_LAYOUT]) {
+ style.zoom = 1; // needs layout
+ }
+ }
+ }
+ };
+ }
+} catch(e) {
+ Y.log('document.documentElement.filters error (activeX disabled)', 'warn', 'dom-style');
+}
+
+try {
+ document.createElement('div').style.height = '-1px';
+} catch(e) { // IE throws error on invalid style set; trap common cases
+ Y.DOM.CUSTOM_STYLES.height = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.height = val;
+ } else {
+ Y.log('invalid style value for height: ' + val, 'warn', 'dom-style');
+ }
+ }
+ };
+
+ Y.DOM.CUSTOM_STYLES.width = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.width = val;
+ } else {
+ Y.log('invalid style value for width: ' + val, 'warn', 'dom-style');
+ }
+ }
+ };
+}
+
+// TODO: top, right, bottom, left
+IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
+
+IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
+
+IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
+ IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
+ ComputedStyle.getBorderWidth;
+
+IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
+ IEComputed.marginLeft = ComputedStyle.getMargin;
+
+IEComputed.visibility = ComputedStyle.getVisibility;
+IEComputed.borderColor = IEComputed.borderTopColor =
+ IEComputed.borderRightColor = IEComputed.borderBottomColor =
+ IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
+
+if (!Y.config.win[GET_COMPUTED_STYLE]) {
+ Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
+}
+
+Y.namespace('DOM.IE');
+Y.DOM.IE.COMPUTED = IEComputed;
+Y.DOM.IE.ComputedStyle = ComputedStyle;
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dom-style",function(A){(function(E){var C="documentElement",B="defaultView",D="ownerDocument",L="style",N="float",F="cssFloat",G="styleFloat",J="transparent",H="getComputedStyle",M=E.config.doc,I=undefined,K=/color$/i;E.mix(E.DOM,{CUSTOM_STYLES:{},setStyle:function(R,O,S,Q){Q=Q||R.style;var P=E.DOM.CUSTOM_STYLES;if(Q){if(S===null){S="";}if(O in P){if(P[O].set){P[O].set(R,S,Q);return;}else{if(typeof P[O]==="string"){O=P[O];}}}Q[O]=S;}},getStyle:function(R,O){var Q=R[L],P=E.DOM.CUSTOM_STYLES,S="";if(Q){if(O in P){if(P[O].get){return P[O].get(R,O,Q);}else{if(typeof P[O]==="string"){O=P[O];}}}S=Q[O];if(S===""){S=E.DOM[H](R,O);}}return S;},setStyles:function(P,Q){var O=P.style;E.each(Q,function(R,S){E.DOM.setStyle(P,S,R,O);},E.DOM);},getComputedStyle:function(P,O){var R="",Q=P[D];if(P[L]){R=Q[B][H](P,null)[O];}return R;}});if(M[C][L][F]!==I){E.DOM.CUSTOM_STYLES[N]=F;}else{if(M[C][L][G]!==I){E.DOM.CUSTOM_STYLES[N]=G;}}if(E.UA.opera){E.DOM[H]=function(Q,P){var O=Q[D][B],R=O[H](Q,"")[P];if(K.test(P)){R=E.Color.toRGB(R);}return R;};}if(E.UA.webkit){E.DOM[H]=function(Q,P){var O=Q[D][B],R=O[H](Q,"")[P];if(R==="rgba(0, 0, 0, 0)"){R=J;}return R;};}})(A);(function(D){var B=parseInt,C=RegExp;D.Color={KEYWORDS:{black:"000",silver:"c0c0c0",gray:"808080",white:"fff",maroon:"800000",red:"f00",purple:"800080",fuchsia:"f0f",green:"008000",lime:"0f0",olive:"808000",yellow:"ff0",navy:"000080",blue:"00f",teal:"008080",aqua:"0ff"},re_RGB:/^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,re_hex:/^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,re_hex3:/([0-9A-F])/gi,toRGB:function(E){if(!D.Color.re_RGB.test(E)){E=D.Color.toHex(E);}if(D.Color.re_hex.exec(E)){E="rgb("+[B(C.$1,16),B(C.$2,16),B(C.$3,16)].join(", ")+")";}return E;},toHex:function(F){F=D.Color.KEYWORDS[F]||F;if(D.Color.re_RGB.exec(F)){F=[Number(C.$1).toString(16),Number(C.$2).toString(16),Number(C.$3).toString(16)];for(var E=0;E<F.length;E++){if(F[E].length<2){F[E]=F[E].replace(D.Color.re_hex3,"$1$1");}}F="#"+F.join("");}if(F.length<6){F=F.replace(D.Color.re_hex3,"$1$1");}if(F!=="transparent"&&F.indexOf("#")<0){F="#"+F;}return F.toLowerCase();}};})(A);(function(D){var W="hasLayout",K="px",L="filter",B="filters",T="opacity",M="auto",G="borderWidth",J="borderTopWidth",Q="borderRightWidth",V="borderBottomWidth",H="borderLeftWidth",I="width",O="height",R="transparent",S="visible",C="getComputedStyle",Z=undefined,X=document.documentElement,P=/^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,E=function(Y){return Y.currentStyle||Y.style;},N={CUSTOM_STYLES:{},get:function(Y,b){var a="",c;if(Y){c=E(Y)[b];if(b===T&&D.DOM.CUSTOM_STYLES[T]){a=D.DOM.CUSTOM_STYLES[T].get(Y);}else{if(!c||(c.indexOf&&c.indexOf(K)>-1)){a=c;}else{if(D.DOM.IE.COMPUTED[b]){a=D.DOM.IE.COMPUTED[b](Y,b);}else{if(P.test(c)){a=N.getPixel(Y,b)+K;}else{a=c;}}}}}return a;},sizeOffsets:{width:["Left","Right"],height:["Top","Bottom"],top:["Top"],bottom:["Bottom"]},getOffset:function(b,g){var d=E(b)[g],Y=g.charAt(0).toUpperCase()+g.substr(1),f="offset"+Y,a="pixel"+Y,e=N.sizeOffsets[g],c="";if(d===M||d.indexOf("%")>-1){c=b["offset"+Y];if(e[0]){c-=N.getPixel(b,"padding"+e[0]);c-=N.getBorderWidth(b,"border"+e[0]+"Width",1);}if(e[1]){c-=N.getPixel(b,"padding"+e[1]);c-=N.getBorderWidth(b,"border"+e[1]+"Width",1);}}else{if(!b.style[a]&&!b.style[g]){b.style[g]=d;}c=b.style[a];}return c+K;},borderMap:{thin:"2px",medium:"4px",thick:"6px"},getBorderWidth:function(a,c,Y){var b=Y?"":K,d=a.currentStyle[c];if(d.indexOf(K)<0){if(N.borderMap[d]){d=N.borderMap[d];}else{}}return(Y)?parseFloat(d):d;},getPixel:function(b,Y){var d=null,a=E(b),e=a.right,c=a[Y];b.style.right=c;d=b.style.pixelRight;b.style.right=e;return d;},getMargin:function(b,Y){var c,a=E(b);if(a[Y]==M){c=0;}else{c=N.getPixel(b,Y);}return c+K;},getVisibility:function(a,Y){var b;while((b=a.currentStyle)&&b[Y]=="inherit"){a=a.parentNode;}return(b)?b[Y]:S;},getColor:function(a,Y){var b=E(a)[Y];if(!b||b===R){D.DOM.elementByAxis(a,"parentNode",null,function(c){b=E(c)[Y];if(b&&b!==R){a=c;return true;}});}return D.Color.toRGB(b);},getBorderColor:function(a,Y){var b=E(a),c=b[Y]||b.color;return D.Color.toRGB(D.Color.toHex(c));}},F={};try{if(X.style[T]===Z&&X[B]){D.DOM.CUSTOM_STYLES[T]={get:function(a){var c=100;try{c=a[B]["DXImageTransform.Microsoft.Alpha"][T];}catch(b){try{c=a[B]("alpha")[T];}catch(Y){}}return c/100;},set:function(a,d,Y){var c,b;if(d===""){b=E(a);c=(T in b)?b[T]:1;d=c;}if(typeof Y[L]=="string"){Y[L]="alpha("+T+"="+d*100+")";if(!a.currentStyle||!a.currentStyle[W]){Y.zoom=1;}}}};}}catch(U){}try{document.createElement("div").style.height="-1px";}catch(U){D.DOM.CUSTOM_STYLES.height={set:function(b,c,a){var Y=parseFloat(c);if(isNaN(Y)||Y>=0){a.height=c;}else{}}};D.DOM.CUSTOM_STYLES.width={set:function(b,c,a){var Y=parseFloat(c);if(isNaN(Y)||Y>=0){a.width=c;}else{}}};}F[I]=F[O]=N.getOffset;F.color=F.backgroundColor=N.getColor;F[G]=F[J]=F[Q]=F[V]=F[H]=N.getBorderWidth;F.marginTop=F.marginRight=F.marginBottom=F.marginLeft=N.getMargin;F.visibility=N.getVisibility;F.borderColor=F.borderTopColor=F.borderRightColor=F.borderBottomColor=F.borderLeftColor=N.getBorderColor;if(!D.config.win[C]){D.DOM[C]=N.get;}D.namespace("DOM.IE");D.DOM.IE.COMPUTED=F;D.DOM.IE.ComputedStyle=N;})(A);},"3.0.0",{requires:["dom-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-style', function(Y) {
+
+(function(Y) {
+/**
+ * Add style management functionality to DOM.
+ * @module dom
+ * @submodule dom-style
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ DEFAULT_VIEW = 'defaultView',
+ OWNER_DOCUMENT = 'ownerDocument',
+ STYLE = 'style',
+ FLOAT = 'float',
+ CSS_FLOAT = 'cssFloat',
+ STYLE_FLOAT = 'styleFloat',
+ TRANSPARENT = 'transparent',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ DOCUMENT = Y.config.doc,
+ UNDEFINED = undefined,
+
+ re_color = /color$/i;
+
+
+Y.mix(Y.DOM, {
+ CUSTOM_STYLES: {
+ },
+
+
+ /**
+ * Sets a style property for a given element.
+ * @method setStyle
+ * @param {HTMLElement} An HTMLElement to apply the style to.
+ * @param {String} att The style property to set.
+ * @param {String|Number} val The value.
+ */
+ setStyle: function(node, att, val, style) {
+ style = style || node.style;
+ var CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES;
+
+ if (style) {
+ if (val === null) {
+ val = ''; // normalize for unsetting
+ }
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].set) {
+ CUSTOM_STYLES[att].set(node, val, style);
+ return; // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ style[att] = val;
+ }
+ },
+
+ /**
+ * Returns the current style value for the given property.
+ * @method getStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ */
+ getStyle: function(node, att) {
+ var style = node[STYLE],
+ CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES,
+ val = '';
+
+ if (style) {
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].get) {
+ return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ val = style[att];
+ if (val === '') { // TODO: is empty string sufficient?
+ val = Y.DOM[GET_COMPUTED_STYLE](node, att);
+ }
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets multiple style properties.
+ * @method setStyles
+ * @param {HTMLElement} node An HTMLElement to apply the styles to.
+ * @param {Object} hash An object literal of property:value pairs.
+ */
+ setStyles: function(node, hash) {
+ var style = node.style;
+ Y.each(hash, function(v, n) {
+ Y.DOM.setStyle(node, n, v, style);
+ }, Y.DOM);
+ },
+
+ /**
+ * Returns the computed style for the given node.
+ * @method getComputedStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ * @return {String} The computed value of the style property.
+ */
+ getComputedStyle: function(node, att) {
+ var val = '',
+ doc = node[OWNER_DOCUMENT];
+
+ if (node[STYLE]) {
+ val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
+ }
+ return val;
+ }
+});
+
+// normalize reserved word float alternatives ("cssFloat" or "styleFloat")
+if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
+} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
+}
+
+// fix opera computedStyle default color unit (convert to rgb)
+if (Y.UA.opera) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (re_color.test(att)) {
+ val = Y.Color.toRGB(val);
+ }
+
+ return val;
+ };
+
+}
+
+// safari converts transparent to rgba(), others use "transparent"
+if (Y.UA.webkit) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (val === 'rgba(0, 0, 0, 0)') {
+ val = TRANSPARENT;
+ }
+
+ return val;
+ };
+
+}
+})(Y);
+(function(Y) {
+var PARSE_INT = parseInt,
+ RE = RegExp;
+
+Y.Color = {
+ KEYWORDS: {
+ black: '000',
+ silver: 'c0c0c0',
+ gray: '808080',
+ white: 'fff',
+ maroon: '800000',
+ red: 'f00',
+ purple: '800080',
+ fuchsia: 'f0f',
+ green: '008000',
+ lime: '0f0',
+ olive: '808000',
+ yellow: 'ff0',
+ navy: '000080',
+ blue: '00f',
+ teal: '008080',
+ aqua: '0ff'
+ },
+
+ re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
+ re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
+ re_hex3: /([0-9A-F])/gi,
+
+ toRGB: function(val) {
+ if (!Y.Color.re_RGB.test(val)) {
+ val = Y.Color.toHex(val);
+ }
+
+ if(Y.Color.re_hex.exec(val)) {
+ val = 'rgb(' + [
+ PARSE_INT(RE.$1, 16),
+ PARSE_INT(RE.$2, 16),
+ PARSE_INT(RE.$3, 16)
+ ].join(', ') + ')';
+ }
+ return val;
+ },
+
+ toHex: function(val) {
+ val = Y.Color.KEYWORDS[val] || val;
+ if (Y.Color.re_RGB.exec(val)) {
+ val = [
+ Number(RE.$1).toString(16),
+ Number(RE.$2).toString(16),
+ Number(RE.$3).toString(16)
+ ];
+
+ for (var i = 0; i < val.length; i++) {
+ if (val[i].length < 2) {
+ val[i] = val[i].replace(Y.Color.re_hex3, '$1$1');
+ }
+ }
+
+ val = '#' + val.join('');
+ }
+
+ if (val.length < 6) {
+ val = val.replace(Y.Color.re_hex3, '$1$1');
+ }
+
+ if (val !== 'transparent' && val.indexOf('#') < 0) {
+ val = '#' + val;
+ }
+
+ return val.toLowerCase();
+ }
+};
+})(Y);
+
+(function(Y) {
+var HAS_LAYOUT = 'hasLayout',
+ PX = 'px',
+ FILTER = 'filter',
+ FILTERS = 'filters',
+ OPACITY = 'opacity',
+ AUTO = 'auto',
+
+ BORDER_WIDTH = 'borderWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ BORDER_RIGHT_WIDTH = 'borderRightWidth',
+ BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ WIDTH = 'width',
+ HEIGHT = 'height',
+ TRANSPARENT = 'transparent',
+ VISIBLE = 'visible',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+ UNDEFINED = undefined,
+ documentElement = document.documentElement,
+
+ // TODO: unit-less lineHeight (e.g. 1.22)
+ re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
+
+ _getStyleObj = function(node) {
+ return node.currentStyle || node.style;
+ },
+
+ ComputedStyle = {
+ CUSTOM_STYLES: {},
+
+ get: function(el, property) {
+ var value = '',
+ current;
+
+ if (el) {
+ current = _getStyleObj(el)[property];
+
+ if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
+ value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
+ } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
+ value = current;
+ } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
+ value = Y.DOM.IE.COMPUTED[property](el, property);
+ } else if (re_unit.test(current)) { // convert to pixel
+ value = ComputedStyle.getPixel(el, property) + PX;
+ } else {
+ value = current;
+ }
+ }
+
+ return value;
+ },
+
+ sizeOffsets: {
+ width: ['Left', 'Right'],
+ height: ['Top', 'Bottom'],
+ top: ['Top'],
+ bottom: ['Bottom']
+ },
+
+ getOffset: function(el, prop) {
+ var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
+ capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
+ offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
+ pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
+ sizeOffsets = ComputedStyle.sizeOffsets[prop],
+ value = '';
+
+ // IE pixelWidth incorrect for percent
+ // manually compute by subtracting padding and border from offset size
+ // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
+ // reverting to auto from auto causes position stacking issues (old impl)
+ if (current === AUTO || current.indexOf('%') > -1) {
+ value = el['offset' + capped];
+
+ if (sizeOffsets[0]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
+ }
+
+ if (sizeOffsets[1]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
+ }
+
+ } else { // use style.pixelWidth, etc. to convert to pixels
+ // need to map style.width to currentStyle (no currentStyle.pixelWidth)
+ if (!el.style[pixel] && !el.style[prop]) {
+ el.style[prop] = current;
+ }
+ value = el.style[pixel];
+
+ }
+ return value + PX;
+ },
+
+ borderMap: {
+ thin: '2px',
+ medium: '4px',
+ thick: '6px'
+ },
+
+ getBorderWidth: function(el, property, omitUnit) {
+ var unit = omitUnit ? '' : PX,
+ current = el.currentStyle[property];
+
+ if (current.indexOf(PX) < 0) { // look up keywords
+ if (ComputedStyle.borderMap[current]) {
+ current = ComputedStyle.borderMap[current];
+ } else {
+ }
+ }
+ return (omitUnit) ? parseFloat(current) : current;
+ },
+
+ getPixel: function(node, att) {
+ // use pixelRight to convert to px
+ var val = null,
+ style = _getStyleObj(node),
+ styleRight = style.right,
+ current = style[att];
+
+ node.style.right = current;
+ val = node.style.pixelRight;
+ node.style.right = styleRight; // revert
+
+ return val;
+ },
+
+ getMargin: function(node, att) {
+ var val,
+ style = _getStyleObj(node);
+
+ if (style[att] == AUTO) {
+ val = 0;
+ } else {
+ val = ComputedStyle.getPixel(node, att);
+ }
+ return val + PX;
+ },
+
+ getVisibility: function(node, att) {
+ var current;
+ while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
+ node = node.parentNode;
+ }
+ return (current) ? current[att] : VISIBLE;
+ },
+
+ getColor: function(node, att) {
+ var current = _getStyleObj(node)[att];
+
+ if (!current || current === TRANSPARENT) {
+ Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
+ current = _getStyleObj(parent)[att];
+ if (current && current !== TRANSPARENT) {
+ node = parent;
+ return true;
+ }
+ });
+ }
+
+ return Y.Color.toRGB(current);
+ },
+
+ getBorderColor: function(node, att) {
+ var current = _getStyleObj(node),
+ val = current[att] || current.color;
+ return Y.Color.toRGB(Y.Color.toHex(val));
+ }
+ },
+
+ //fontSize: getPixelFont,
+ IEComputed = {};
+
+// use alpha filter for IE opacity
+try {
+ if (documentElement.style[OPACITY] === UNDEFINED &&
+ documentElement[FILTERS]) {
+ Y.DOM.CUSTOM_STYLES[OPACITY] = {
+ get: function(node) {
+ var val = 100;
+ try { // will error if no DXImageTransform
+ val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
+
+ } catch(e) {
+ try { // make sure its in the document
+ val = node[FILTERS]('alpha')[OPACITY];
+ } catch(err) {
+ }
+ }
+ return val / 100;
+ },
+
+ set: function(node, val, style) {
+ var current,
+ styleObj;
+
+ if (val === '') { // normalize inline style behavior
+ styleObj = _getStyleObj(node);
+ current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
+ val = current;
+ }
+
+ if (typeof style[FILTER] == 'string') { // in case not appended
+ style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
+
+ if (!node.currentStyle || !node.currentStyle[HAS_LAYOUT]) {
+ style.zoom = 1; // needs layout
+ }
+ }
+ }
+ };
+ }
+} catch(e) {
+}
+
+try {
+ document.createElement('div').style.height = '-1px';
+} catch(e) { // IE throws error on invalid style set; trap common cases
+ Y.DOM.CUSTOM_STYLES.height = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.height = val;
+ } else {
+ }
+ }
+ };
+
+ Y.DOM.CUSTOM_STYLES.width = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.width = val;
+ } else {
+ }
+ }
+ };
+}
+
+// TODO: top, right, bottom, left
+IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
+
+IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
+
+IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
+ IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
+ ComputedStyle.getBorderWidth;
+
+IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
+ IEComputed.marginLeft = ComputedStyle.getMargin;
+
+IEComputed.visibility = ComputedStyle.getVisibility;
+IEComputed.borderColor = IEComputed.borderTopColor =
+ IEComputed.borderRightColor = IEComputed.borderBottomColor =
+ IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
+
+if (!Y.config.win[GET_COMPUTED_STYLE]) {
+ Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
+}
+
+Y.namespace('DOM.IE');
+Y.DOM.IE.COMPUTED = IEComputed;
+Y.DOM.IE.ComputedStyle = ComputedStyle;
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dom-base', function(Y) {
+
+(function(Y) {
+/**
+ * The DOM utility provides a cross-browser abtraction layer
+ * normalizing DOM tasks, and adds extra helper functionality
+ * for other common tasks.
+ * @module dom
+ * @submodule dom-base
+ *
+ */
+
+/**
+ * Provides DOM helper methods.
+ * @class DOM
+ *
+ */
+var NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ DEFAULT_VIEW = 'defaultView',
+ PARENT_WINDOW = 'parentWindow',
+ TAG_NAME = 'tagName',
+ PARENT_NODE = 'parentNode',
+ FIRST_CHILD = 'firstChild',
+ PREVIOUS_SIBLING = 'previousSibling',
+ NEXT_SIBLING = 'nextSibling',
+ CONTAINS = 'contains',
+ COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+
+ documentElement = document.documentElement,
+
+ re_tag = /<([a-z]+)/i;
+
+Y.DOM = {
+ /**
+ * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
+ * @method byId
+ * @param {String} id the id attribute
+ * @param {Object} doc optional The document to search. Defaults to current document
+ * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
+ */
+ byId: function(id, doc) {
+ doc = doc || Y.config.doc;
+ // TODO: IE Name
+ return doc.getElementById(id);
+ },
+
+ // @deprecated
+ children: function(node, tag) {
+ var ret = [];
+ if (node) {
+ tag = tag || '*';
+ ret = Y.Selector.query('> ' + tag, node);
+ }
+ return ret;
+ },
+
+ // @deprecated
+ firstByTag: function(tag, root) {
+ var ret;
+ root = root || Y.config.doc;
+
+ if (tag && root.getElementsByTagName) {
+ ret = root.getElementsByTagName(tag)[0];
+ }
+
+ return ret || null;
+ },
+
+ /**
+ * Returns the text content of the HTMLElement.
+ * @method getText
+ * @param {HTMLElement} element The html element.
+ * @return {String} The text content of the element (includes text of any descending elements).
+ */
+ getText: (documentElement.textContent !== undefined) ?
+ function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.textContent;
+ }
+ return ret || '';
+ } : function(element) {
+ var ret = '';
+ if (element) {
+ ret = element.innerText;
+ }
+ return ret || '';
+ },
+
+ /**
+ * Sets the text content of the HTMLElement.
+ * @method setText
+ * @param {HTMLElement} element The html element.
+ * @param {String} content The content to add.
+ */
+ setText: (documentElement.textContent !== undefined) ?
+ function(element, content) {
+ if (element) {
+ element.textContent = content;
+ }
+ } : function(element, content) {
+ if (element) {
+ element.innerText = content;
+ }
+ },
+
+ /*
+ * Finds the previous sibling of the element.
+ * @method previous
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ previous: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the next sibling of the element.
+ * @method next
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the first sibling is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ next: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
+ },
+
+ /*
+ * Finds the ancestor of the element.
+ * @method ancestor
+ * @deprecated Use elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {Function} fn optional An optional boolean test to apply.
+ * The optional function is passed the current DOM node being tested as its only argument.
+ * If no function is given, the parentNode is returned.
+ * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
+ * @return {HTMLElement | null} The matching DOM node or null if none found.
+ */
+ // TODO: optional stopAt node?
+ ancestor: function(element, fn, all) {
+ return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
+ },
+
+ /**
+ * Searches the element by the given axis for the first matching element.
+ * @method elementByAxis
+ * @param {HTMLElement} element The html element.
+ * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
+ * @param {Function} fn optional An optional boolean test to apply.
+ * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
+ * The optional function is passed the current HTMLElement being tested as its only argument.
+ * If no function is given, the first element is returned.
+ * @return {HTMLElement | null} The matching element or null if none found.
+ */
+ elementByAxis: function(element, axis, fn, all) {
+ while (element && (element = element[axis])) { // NOTE: assignment
+ if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
+ return element;
+ }
+ }
+ return null;
+ },
+
+ /**
+ * Determines whether or not one HTMLElement is or contains another HTMLElement.
+ * @method contains
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ contains: function(element, needle) {
+ var ret = false;
+
+ if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
+ ret = false;
+ } else if (element[CONTAINS]) {
+ if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
+ ret = element[CONTAINS](needle);
+ } else {
+ ret = Y.DOM._bruteContains(element, needle);
+ }
+ } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
+ if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
+ ret = true;
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Determines whether or not the HTMLElement is part of the document.
+ * @method inDoc
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} doc optional The document to check.
+ * @return {Boolean} Whether or not the element is attached to the document.
+ */
+ inDoc: function(element, doc) {
+ doc = doc || element[OWNER_DOCUMENT];
+ var id = element.id;
+ if (!id) { // TODO: remove when done?
+ id = element.id = Y.guid();
+ }
+
+ return !! (doc.getElementById(id));
+ },
+
+ /**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ */
+ create: function(html, doc) {
+ if (typeof html === 'string') {
+ html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
+ }
+
+ if (!doc && Y.DOM._cloneCache[html]) {
+ return Y.DOM._cloneCache[html].cloneNode(true); // NOTE: return
+ }
+
+ doc = doc || Y.config.doc;
+ var m = re_tag.exec(html),
+ create = Y.DOM._create,
+ custom = Y.DOM.creators,
+ ret = null,
+ tag, nodes;
+
+ if (m && custom[m[1]]) {
+ if (typeof custom[m[1]] === 'function') {
+ create = custom[m[1]];
+ } else {
+ tag = custom[m[1]];
+ }
+ }
+
+ nodes = create(html, doc, tag).childNodes;
+
+ if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
+ ret = nodes[0].parentNode.removeChild(nodes[0]);
+ } else { // return multiple nodes as a fragment
+ ret = Y.DOM._nl2frag(nodes, doc);
+ }
+
+ if (ret) {
+ Y.DOM._cloneCache[html] = ret.cloneNode(true);
+ }
+ return ret;
+ },
+
+ _nl2frag: function(nodes, doc) {
+ var ret = null,
+ i, len;
+
+ if (nodes && (nodes.push || nodes.item) && nodes[0]) {
+ doc = doc || nodes[0].ownerDocument;
+ ret = doc.createDocumentFragment();
+
+ if (nodes.item) { // convert live list to static array
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ for (i = 0, len = nodes.length; i < len; i++) {
+ ret.appendChild(nodes[i]);
+ }
+ } // else inline with log for minification
+ return ret;
+ },
+
+
+ CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
+ 'for': 'htmlFor',
+ 'class': 'className'
+ } : { // w3c
+ 'htmlFor': 'for',
+ 'className': 'class'
+ },
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method setAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to set.
+ * @param {String} val The value of the attribute.
+ */
+ setAttribute: function(el, attr, val, ieAttr) {
+ if (el && el.setAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ el.setAttribute(attr, val, ieAttr);
+ }
+ },
+
+
+ /**
+ * Provides a normalized attribute interface.
+ * @method getAttibute
+ * @param {String | HTMLElement} el The target element for the attribute.
+ * @param {String} attr The attribute to get.
+ * @return {String} The current value of the attribute.
+ */
+ getAttribute: function(el, attr, ieAttr) {
+ ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
+ var ret = '';
+ if (el && el.getAttribute) {
+ attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
+ ret = el.getAttribute(attr, ieAttr);
+
+ if (ret === null) {
+ ret = ''; // per DOM spec
+ }
+ }
+ return ret;
+ },
+
+ isWindow: function(obj) {
+ return obj.alert && obj.document;
+ },
+
+ _fragClones: {
+ div: document.createElement('div')
+ },
+
+ _create: function(html, doc, tag) {
+ tag = tag || 'div';
+
+ var frag = Y.DOM._fragClones[tag];
+ if (frag) {
+ frag = frag.cloneNode(false);
+ } else {
+ frag = Y.DOM._fragClones[tag] = doc.createElement(tag);
+ }
+ frag.innerHTML = html;
+ return frag;
+ },
+
+ _removeChildNodes: function(node) {
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ },
+
+ _cloneCache: {},
+
+ /**
+ * Inserts content in a node at the given location
+ * @method addHTML
+ * @param {HTMLElement} node The node to insert into
+ * @param {String} content The content to be inserted
+ * @param {String} where Where to insert the content; default is after lastChild
+ */
+ addHTML: function(node, content, where) {
+ if (typeof content === 'string') {
+ content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
+ }
+
+ var newNode = Y.DOM._cloneCache[content],
+ nodeParent = node.parentNode;
+
+ if (newNode) {
+ newNode = newNode.cloneNode(true);
+ } else {
+ if (content.nodeType) { // domNode
+ newNode = content;
+ } else { // create from string and cache
+ newNode = Y.DOM.create(content);
+ }
+ }
+
+ if (where) {
+ if (where.nodeType) { // insert regardless of relationship to node
+ // TODO: check if node.contains(where)?
+ where.parentNode.insertBefore(newNode, where);
+ } else {
+ switch (where) {
+ case 'replace':
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ node.appendChild(newNode);
+ break;
+ case 'before':
+ nodeParent.insertBefore(newNode, node);
+ break;
+ case 'after':
+ if (node.nextSibling) { // IE errors if refNode is null
+ nodeParent.insertBefore(newNode, node.nextSibling);
+ } else {
+ nodeParent.appendChild(newNode);
+ }
+ break;
+ default:
+ node.appendChild(newNode);
+ }
+ }
+ } else {
+ node.appendChild(newNode);
+ }
+
+ return newNode;
+ },
+
+ VALUE_SETTERS: {},
+
+ VALUE_GETTERS: {},
+
+ getValue: function(node) {
+ var ret = '', // TODO: return null?
+ getter;
+
+ if (node && node[TAG_NAME]) {
+ getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (getter) {
+ ret = getter(node);
+ } else {
+ ret = node.value;
+ }
+ }
+
+ return (typeof ret === 'string') ? ret : '';
+ },
+
+ setValue: function(node, val) {
+ var setter;
+
+ if (node && node[TAG_NAME]) {
+ setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
+
+ if (setter) {
+ setter(node, val);
+ } else {
+ node.value = val;
+ }
+ }
+ },
+
+ /**
+ * Brute force version of contains.
+ * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
+ * @method _bruteContains
+ * @private
+ * @param {HTMLElement} element The containing html element.
+ * @param {HTMLElement} needle The html element that may be contained.
+ * @return {Boolean} Whether or not the element is or contains the needle.
+ */
+ _bruteContains: function(element, needle) {
+ while (needle) {
+ if (element === needle) {
+ return true;
+ }
+ needle = needle.parentNode;
+ }
+ return false;
+ },
+
+// TODO: move to Lang?
+ /**
+ * Memoizes dynamic regular expressions to boost runtime performance.
+ * @method _getRegExp
+ * @private
+ * @param {String} str The string to convert to a regular expression.
+ * @param {String} flags optional An optinal string of flags.
+ * @return {RegExp} An instance of RegExp
+ */
+ _getRegExp: function(str, flags) {
+ flags = flags || '';
+ Y.DOM._regexCache = Y.DOM._regexCache || {};
+ if (!Y.DOM._regexCache[str + flags]) {
+ Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return Y.DOM._regexCache[str + flags];
+ },
+
+// TODO: make getDoc/Win true privates?
+ /**
+ * returns the appropriate document.
+ * @method _getDoc
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The document for the given element or the default document.
+ */
+ _getDoc: function(element) {
+ element = element || {};
+
+ return (element[NODE_TYPE] === 9) ? element : // element === document
+ element[OWNER_DOCUMENT] || // element === DOM node
+ element.document || // element === window
+ Y.config.doc; // default
+ },
+
+ /**
+ * returns the appropriate window.
+ * @method _getWin
+ * @private
+ * @param {HTMLElement} element optional Target element.
+ * @return {Object} The window for the given element or the default window.
+ */
+ _getWin: function(element) {
+ var doc = Y.DOM._getDoc(element);
+ return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
+ },
+
+ _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
+ fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
+ var result,
+ ret = [];
+
+ if (fn && nodes) {
+ Y.each(nodes, function(node) {
+ if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
+ ret[ret.length] = result;
+ }
+ });
+ }
+
+ return ret.length ? ret : nodes;
+ },
+
+ _testElement: function(element, tag, fn) {
+ tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
+ return (element && element[TAG_NAME] &&
+ (!tag || element[TAG_NAME].toUpperCase() === tag) &&
+ (!fn || fn(element)));
+ },
+
+ creators: {},
+
+ _IESimpleCreate: function(html, doc) {
+ doc = doc || Y.config.doc;
+ return doc.createElement(html);
+ }
+};
+
+
+(function(Y) {
+ var creators = Y.DOM.creators,
+ create = Y.DOM.create,
+ re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
+
+ TABLE_OPEN = '<table>',
+ TABLE_CLOSE = '</table>';
+
+ if (Y.UA.ie) {
+ Y.mix(creators, {
+ // TODO: thead/tfoot with nested tbody
+ // IE adds TBODY when creating TABLE elements (which may share this impl)
+ tbody: function(html, doc) {
+ var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
+ tb = frag.children.tags('tbody')[0];
+
+ if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
+ tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
+ }
+ return frag;
+ },
+
+ script: function(html, doc) {
+ var frag = doc.createElement('div');
+
+ frag.innerHTML = '-' + html;
+ frag.removeChild(frag[FIRST_CHILD]);
+ return frag;
+ }
+
+ }, true);
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ button: function(node) {
+ return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
+ }
+ });
+
+ Y.mix(Y.DOM.VALUE_SETTERS, {
+ // IE: node.value changes the button text, which should be handled via innerHTML
+ button: function(node, val) {
+ var attr = node.attributes.value;
+ if (!attr) {
+ attr = node[OWNER_DOCUMENT].createAttribute('value');
+ node.setAttributeNode(attr);
+ }
+
+ attr.value = val;
+ }
+ });
+ }
+
+ if (Y.UA.gecko || Y.UA.ie) {
+ Y.mix(creators, {
+ option: function(html, doc) {
+ return create('<select>' + html + '</select>', doc);
+ },
+
+ tr: function(html, doc) {
+ return create('<tbody>' + html + '</tbody>', doc);
+ },
+
+ td: function(html, doc) {
+ return create('<tr>' + html + '</tr>', doc);
+ },
+
+ tbody: function(html, doc) {
+ return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
+ }
+ });
+
+ Y.mix(creators, {
+ legend: 'fieldset',
+ th: creators.td,
+ thead: creators.tbody,
+ tfoot: creators.tbody,
+ caption: creators.tbody,
+ colgroup: creators.tbody,
+ col: creators.tbody,
+ optgroup: creators.option
+ });
+ }
+
+ Y.mix(Y.DOM.VALUE_GETTERS, {
+ option: function(node) {
+ var attrs = node.attributes;
+ return (attrs.value && attrs.value.specified) ? node.value : node.text;
+ },
+
+ select: function(node) {
+ var val = node.value,
+ options = node.options;
+
+ if (options && val === '') {
+ if (node.multiple) {
+ } else {
+ val = Y.DOM.getValue(options[node.selectedIndex], 'value');
+ }
+ }
+
+ return val;
+ }
+ });
+})(Y);
+
+})(Y);
+var addClass, hasClass, removeClass;
+
+Y.mix(Y.DOM, {
+ /**
+ * Determines whether a DOM element has the given className.
+ * @method hasClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to search for
+ * @return {Boolean} Whether or not the element has the given class.
+ */
+ hasClass: function(node, className) {
+ var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
+ return re.test(node.className);
+ },
+
+ /**
+ * Adds a class name to a given DOM element.
+ * @method addClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to add to the class attribute
+ */
+ addClass: function(node, className) {
+ if (!Y.DOM.hasClass(node, className)) { // skip if already present
+ node.className = Y.Lang.trim([node.className, className].join(' '));
+ }
+ },
+
+ /**
+ * Removes a class name from a given element.
+ * @method removeClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to remove from the class attribute
+ */
+ removeClass: function(node, className) {
+ if (className && hasClass(node, className)) {
+ node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
+ className + '(?:\\s+|$)'), ' '));
+
+ if ( hasClass(node, className) ) { // in case of multiple adjacent
+ removeClass(node, className);
+ }
+ }
+ },
+
+ /**
+ * Replace a class with another class for a given element.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ */
+ replaceClass: function(node, oldC, newC) {
+ addClass(node, newC);
+ removeClass(node, oldC);
+ },
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {HTMLElement} element The DOM element.
+ * @param {String} className the class name to be toggled
+ */
+ toggleClass: function(node, className) {
+ if (hasClass(node, className)) {
+ removeClass(node, className);
+ } else {
+ addClass(node, className);
+ }
+ }
+});
+
+hasClass = Y.DOM.hasClass;
+removeClass = Y.DOM.removeClass;
+addClass = Y.DOM.addClass;
+
+
+
+}, '3.0.0' ,{requires:['oop']});
+YUI.add('dom-style', function(Y) {
+
+(function(Y) {
+/**
+ * Add style management functionality to DOM.
+ * @module dom
+ * @submodule dom-style
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ DEFAULT_VIEW = 'defaultView',
+ OWNER_DOCUMENT = 'ownerDocument',
+ STYLE = 'style',
+ FLOAT = 'float',
+ CSS_FLOAT = 'cssFloat',
+ STYLE_FLOAT = 'styleFloat',
+ TRANSPARENT = 'transparent',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ DOCUMENT = Y.config.doc,
+ UNDEFINED = undefined,
+
+ re_color = /color$/i;
+
+
+Y.mix(Y.DOM, {
+ CUSTOM_STYLES: {
+ },
+
+
+ /**
+ * Sets a style property for a given element.
+ * @method setStyle
+ * @param {HTMLElement} An HTMLElement to apply the style to.
+ * @param {String} att The style property to set.
+ * @param {String|Number} val The value.
+ */
+ setStyle: function(node, att, val, style) {
+ style = style || node.style;
+ var CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES;
+
+ if (style) {
+ if (val === null) {
+ val = ''; // normalize for unsetting
+ }
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].set) {
+ CUSTOM_STYLES[att].set(node, val, style);
+ return; // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ style[att] = val;
+ }
+ },
+
+ /**
+ * Returns the current style value for the given property.
+ * @method getStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ */
+ getStyle: function(node, att) {
+ var style = node[STYLE],
+ CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES,
+ val = '';
+
+ if (style) {
+ if (att in CUSTOM_STYLES) {
+ if (CUSTOM_STYLES[att].get) {
+ return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
+ } else if (typeof CUSTOM_STYLES[att] === 'string') {
+ att = CUSTOM_STYLES[att];
+ }
+ }
+ val = style[att];
+ if (val === '') { // TODO: is empty string sufficient?
+ val = Y.DOM[GET_COMPUTED_STYLE](node, att);
+ }
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets multiple style properties.
+ * @method setStyles
+ * @param {HTMLElement} node An HTMLElement to apply the styles to.
+ * @param {Object} hash An object literal of property:value pairs.
+ */
+ setStyles: function(node, hash) {
+ var style = node.style;
+ Y.each(hash, function(v, n) {
+ Y.DOM.setStyle(node, n, v, style);
+ }, Y.DOM);
+ },
+
+ /**
+ * Returns the computed style for the given node.
+ * @method getComputedStyle
+ * @param {HTMLElement} An HTMLElement to get the style from.
+ * @param {String} att The style property to get.
+ * @return {String} The computed value of the style property.
+ */
+ getComputedStyle: function(node, att) {
+ var val = '',
+ doc = node[OWNER_DOCUMENT];
+
+ if (node[STYLE]) {
+ val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
+ }
+ return val;
+ }
+});
+
+// normalize reserved word float alternatives ("cssFloat" or "styleFloat")
+if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
+} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
+ Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
+}
+
+// fix opera computedStyle default color unit (convert to rgb)
+if (Y.UA.opera) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (re_color.test(att)) {
+ val = Y.Color.toRGB(val);
+ }
+
+ return val;
+ };
+
+}
+
+// safari converts transparent to rgba(), others use "transparent"
+if (Y.UA.webkit) {
+ Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
+ var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
+ val = view[GET_COMPUTED_STYLE](node, '')[att];
+
+ if (val === 'rgba(0, 0, 0, 0)') {
+ val = TRANSPARENT;
+ }
+
+ return val;
+ };
+
+}
+})(Y);
+(function(Y) {
+var PARSE_INT = parseInt,
+ RE = RegExp;
+
+Y.Color = {
+ KEYWORDS: {
+ black: '000',
+ silver: 'c0c0c0',
+ gray: '808080',
+ white: 'fff',
+ maroon: '800000',
+ red: 'f00',
+ purple: '800080',
+ fuchsia: 'f0f',
+ green: '008000',
+ lime: '0f0',
+ olive: '808000',
+ yellow: 'ff0',
+ navy: '000080',
+ blue: '00f',
+ teal: '008080',
+ aqua: '0ff'
+ },
+
+ re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
+ re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
+ re_hex3: /([0-9A-F])/gi,
+
+ toRGB: function(val) {
+ if (!Y.Color.re_RGB.test(val)) {
+ val = Y.Color.toHex(val);
+ }
+
+ if(Y.Color.re_hex.exec(val)) {
+ val = 'rgb(' + [
+ PARSE_INT(RE.$1, 16),
+ PARSE_INT(RE.$2, 16),
+ PARSE_INT(RE.$3, 16)
+ ].join(', ') + ')';
+ }
+ return val;
+ },
+
+ toHex: function(val) {
+ val = Y.Color.KEYWORDS[val] || val;
+ if (Y.Color.re_RGB.exec(val)) {
+ val = [
+ Number(RE.$1).toString(16),
+ Number(RE.$2).toString(16),
+ Number(RE.$3).toString(16)
+ ];
+
+ for (var i = 0; i < val.length; i++) {
+ if (val[i].length < 2) {
+ val[i] = val[i].replace(Y.Color.re_hex3, '$1$1');
+ }
+ }
+
+ val = '#' + val.join('');
+ }
+
+ if (val.length < 6) {
+ val = val.replace(Y.Color.re_hex3, '$1$1');
+ }
+
+ if (val !== 'transparent' && val.indexOf('#') < 0) {
+ val = '#' + val;
+ }
+
+ return val.toLowerCase();
+ }
+};
+})(Y);
+
+(function(Y) {
+var HAS_LAYOUT = 'hasLayout',
+ PX = 'px',
+ FILTER = 'filter',
+ FILTERS = 'filters',
+ OPACITY = 'opacity',
+ AUTO = 'auto',
+
+ BORDER_WIDTH = 'borderWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ BORDER_RIGHT_WIDTH = 'borderRightWidth',
+ BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ WIDTH = 'width',
+ HEIGHT = 'height',
+ TRANSPARENT = 'transparent',
+ VISIBLE = 'visible',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+ UNDEFINED = undefined,
+ documentElement = document.documentElement,
+
+ // TODO: unit-less lineHeight (e.g. 1.22)
+ re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
+
+ _getStyleObj = function(node) {
+ return node.currentStyle || node.style;
+ },
+
+ ComputedStyle = {
+ CUSTOM_STYLES: {},
+
+ get: function(el, property) {
+ var value = '',
+ current;
+
+ if (el) {
+ current = _getStyleObj(el)[property];
+
+ if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
+ value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
+ } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
+ value = current;
+ } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
+ value = Y.DOM.IE.COMPUTED[property](el, property);
+ } else if (re_unit.test(current)) { // convert to pixel
+ value = ComputedStyle.getPixel(el, property) + PX;
+ } else {
+ value = current;
+ }
+ }
+
+ return value;
+ },
+
+ sizeOffsets: {
+ width: ['Left', 'Right'],
+ height: ['Top', 'Bottom'],
+ top: ['Top'],
+ bottom: ['Bottom']
+ },
+
+ getOffset: function(el, prop) {
+ var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
+ capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
+ offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
+ pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
+ sizeOffsets = ComputedStyle.sizeOffsets[prop],
+ value = '';
+
+ // IE pixelWidth incorrect for percent
+ // manually compute by subtracting padding and border from offset size
+ // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
+ // reverting to auto from auto causes position stacking issues (old impl)
+ if (current === AUTO || current.indexOf('%') > -1) {
+ value = el['offset' + capped];
+
+ if (sizeOffsets[0]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
+ }
+
+ if (sizeOffsets[1]) {
+ value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
+ value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
+ }
+
+ } else { // use style.pixelWidth, etc. to convert to pixels
+ // need to map style.width to currentStyle (no currentStyle.pixelWidth)
+ if (!el.style[pixel] && !el.style[prop]) {
+ el.style[prop] = current;
+ }
+ value = el.style[pixel];
+
+ }
+ return value + PX;
+ },
+
+ borderMap: {
+ thin: '2px',
+ medium: '4px',
+ thick: '6px'
+ },
+
+ getBorderWidth: function(el, property, omitUnit) {
+ var unit = omitUnit ? '' : PX,
+ current = el.currentStyle[property];
+
+ if (current.indexOf(PX) < 0) { // look up keywords
+ if (ComputedStyle.borderMap[current]) {
+ current = ComputedStyle.borderMap[current];
+ } else {
+ }
+ }
+ return (omitUnit) ? parseFloat(current) : current;
+ },
+
+ getPixel: function(node, att) {
+ // use pixelRight to convert to px
+ var val = null,
+ style = _getStyleObj(node),
+ styleRight = style.right,
+ current = style[att];
+
+ node.style.right = current;
+ val = node.style.pixelRight;
+ node.style.right = styleRight; // revert
+
+ return val;
+ },
+
+ getMargin: function(node, att) {
+ var val,
+ style = _getStyleObj(node);
+
+ if (style[att] == AUTO) {
+ val = 0;
+ } else {
+ val = ComputedStyle.getPixel(node, att);
+ }
+ return val + PX;
+ },
+
+ getVisibility: function(node, att) {
+ var current;
+ while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
+ node = node.parentNode;
+ }
+ return (current) ? current[att] : VISIBLE;
+ },
+
+ getColor: function(node, att) {
+ var current = _getStyleObj(node)[att];
+
+ if (!current || current === TRANSPARENT) {
+ Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
+ current = _getStyleObj(parent)[att];
+ if (current && current !== TRANSPARENT) {
+ node = parent;
+ return true;
+ }
+ });
+ }
+
+ return Y.Color.toRGB(current);
+ },
+
+ getBorderColor: function(node, att) {
+ var current = _getStyleObj(node),
+ val = current[att] || current.color;
+ return Y.Color.toRGB(Y.Color.toHex(val));
+ }
+ },
+
+ //fontSize: getPixelFont,
+ IEComputed = {};
+
+// use alpha filter for IE opacity
+try {
+ if (documentElement.style[OPACITY] === UNDEFINED &&
+ documentElement[FILTERS]) {
+ Y.DOM.CUSTOM_STYLES[OPACITY] = {
+ get: function(node) {
+ var val = 100;
+ try { // will error if no DXImageTransform
+ val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
+
+ } catch(e) {
+ try { // make sure its in the document
+ val = node[FILTERS]('alpha')[OPACITY];
+ } catch(err) {
+ }
+ }
+ return val / 100;
+ },
+
+ set: function(node, val, style) {
+ var current,
+ styleObj;
+
+ if (val === '') { // normalize inline style behavior
+ styleObj = _getStyleObj(node);
+ current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
+ val = current;
+ }
+
+ if (typeof style[FILTER] == 'string') { // in case not appended
+ style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
+
+ if (!node.currentStyle || !node.currentStyle[HAS_LAYOUT]) {
+ style.zoom = 1; // needs layout
+ }
+ }
+ }
+ };
+ }
+} catch(e) {
+}
+
+try {
+ document.createElement('div').style.height = '-1px';
+} catch(e) { // IE throws error on invalid style set; trap common cases
+ Y.DOM.CUSTOM_STYLES.height = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.height = val;
+ } else {
+ }
+ }
+ };
+
+ Y.DOM.CUSTOM_STYLES.width = {
+ set: function(node, val, style) {
+ var floatVal = parseFloat(val);
+ if (isNaN(floatVal) || floatVal >= 0) {
+ style.width = val;
+ } else {
+ }
+ }
+ };
+}
+
+// TODO: top, right, bottom, left
+IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
+
+IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
+
+IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
+ IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
+ ComputedStyle.getBorderWidth;
+
+IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
+ IEComputed.marginLeft = ComputedStyle.getMargin;
+
+IEComputed.visibility = ComputedStyle.getVisibility;
+IEComputed.borderColor = IEComputed.borderTopColor =
+ IEComputed.borderRightColor = IEComputed.borderBottomColor =
+ IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
+
+if (!Y.config.win[GET_COMPUTED_STYLE]) {
+ Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
+}
+
+Y.namespace('DOM.IE');
+Y.DOM.IE.COMPUTED = IEComputed;
+Y.DOM.IE.ComputedStyle = ComputedStyle;
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
+YUI.add('dom-screen', function(Y) {
+
+(function(Y) {
+
+/**
+ * Adds position and region management functionality to DOM.
+ * @module dom
+ * @submodule dom-screen
+ * @for DOM
+ */
+
+var DOCUMENT_ELEMENT = 'documentElement',
+ COMPAT_MODE = 'compatMode',
+ POSITION = 'position',
+ FIXED = 'fixed',
+ RELATIVE = 'relative',
+ LEFT = 'left',
+ TOP = 'top',
+ _BACK_COMPAT = 'BackCompat',
+ MEDIUM = 'medium',
+ BORDER_LEFT_WIDTH = 'borderLeftWidth',
+ BORDER_TOP_WIDTH = 'borderTopWidth',
+ GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
+ GET_COMPUTED_STYLE = 'getComputedStyle',
+
+ // TODO: how about thead/tbody/tfoot/tr?
+ // TODO: does caption matter?
+ RE_TABLE = /^t(?:able|d|h)$/i;
+
+Y.mix(Y.DOM, {
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @method winHeight
+ * @return {Number} The current height of the viewport.
+ */
+ winHeight: function(node) {
+ var h = Y.DOM._getWinSize(node).height;
+ return h;
+ },
+
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @method winWidth
+ * @return {Number} The current width of the viewport.
+ */
+ winWidth: function(node) {
+ var w = Y.DOM._getWinSize(node).width;
+ return w;
+ },
+
+ /**
+ * Document height
+ * @method docHeight
+ * @return {Number} The current height of the document.
+ */
+ docHeight: function(node) {
+ var h = Y.DOM._getDocSize(node).height;
+ return Math.max(h, Y.DOM._getWinSize(node).height);
+ },
+
+ /**
+ * Document width
+ * @method docWidth
+ * @return {Number} The current width of the document.
+ */
+ docWidth: function(node) {
+ var w = Y.DOM._getDocSize(node).width;
+ return Math.max(w, Y.DOM._getWinSize(node).width);
+ },
+
+ /**
+ * Amount page has been scroll horizontally
+ * @method docScrollX
+ * @return {Number} The current amount the screen is scrolled horizontally.
+ */
+ docScrollX: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
+ },
+
+ /**
+ * Amount page has been scroll vertically
+ * @method docScrollY
+ * @return {Number} The current amount the screen is scrolled vertically.
+ */
+ docScrollY: function(node) {
+ var doc = Y.DOM._getDoc(node);
+ return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
+ },
+
+ /**
+ * Gets the current position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getXY
+ * @param element The target element
+ * @return {Array} The XY position of the element
+
+ TODO: test inDocument/display?
+ */
+ getXY: function() {
+ if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
+ return function(node) {
+ var xy = null,
+ scrollLeft,
+ scrollTop,
+ box,
+ off1, off2,
+ bLeft, bTop,
+ mode,
+ doc;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ scrollLeft = Y.DOM.docScrollX(node);
+ scrollTop = Y.DOM.docScrollY(node);
+ box = node[GET_BOUNDING_CLIENT_RECT]();
+ doc = Y.DOM._getDoc(node);
+ xy = [box.left, box.top];
+
+ if (Y.UA.ie) {
+ off1 = 2;
+ off2 = 2;
+ mode = doc[COMPAT_MODE];
+ bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
+ bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
+
+ if (Y.UA.ie === 6) {
+ if (mode !== _BACK_COMPAT) {
+ off1 = 0;
+ off2 = 0;
+ }
+ }
+
+ if ((mode == _BACK_COMPAT)) {
+ if (bLeft !== MEDIUM) {
+ off1 = parseInt(bLeft, 10);
+ }
+ if (bTop !== MEDIUM) {
+ off2 = parseInt(bTop, 10);
+ }
+ }
+
+ xy[0] -= off1;
+ xy[1] -= off2;
+
+ }
+
+ if ((scrollTop || scrollLeft)) {
+ xy[0] += scrollLeft;
+ xy[1] += scrollTop;
+ }
+ } else { // default to current offsets
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+ return xy;
+ };
+ } else {
+ return function(node) { // manually calculate by crawling up offsetParents
+ //Calculate the Top and Left border sizes (assumes pixels)
+ var xy = null,
+ parentNode,
+ bCheck,
+ scrollTop,
+ scrollLeft;
+
+ if (node) {
+ if (Y.DOM.inDoc(node)) {
+ xy = [node.offsetLeft, node.offsetTop];
+ parentNode = node;
+ // TODO: refactor with !! or just falsey
+ bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
+
+ // TODO: worth refactoring for TOP/LEFT only?
+ while ((parentNode = parentNode.offsetParent)) {
+ xy[0] += parentNode.offsetLeft;
+ xy[1] += parentNode.offsetTop;
+ if (bCheck) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+ }
+
+ // account for any scrolled ancestors
+ if (Y.DOM.getStyle(node, POSITION) != FIXED) {
+ parentNode = node;
+
+ while ((parentNode = parentNode.parentNode)) {
+ scrollTop = parentNode.scrollTop;
+ scrollLeft = parentNode.scrollLeft;
+
+ //Firefox does something funky with borders when overflow is not visible.
+ if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
+ xy = Y.DOM._calcBorders(parentNode, xy);
+ }
+
+
+ if (scrollTop || scrollLeft) {
+ xy[0] -= scrollLeft;
+ xy[1] -= scrollTop;
+ }
+ }
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+
+ } else {
+ //Fix FIXED position -- add scrollbars
+ xy[0] += Y.DOM.docScrollX(node);
+ xy[1] += Y.DOM.docScrollY(node);
+ }
+ } else {
+ xy = Y.DOM._getOffset(node);
+ }
+ }
+
+ return xy;
+ };
+ }
+ }(),// NOTE: Executing for loadtime branching
+
+ _getOffset: function(node) {
+ var pos,
+ xy = null;
+
+ if (node) {
+ pos = Y.DOM.getStyle(node, POSITION);
+ xy = [
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10),
+ parseInt(Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10)
+ ];
+
+ if ( isNaN(xy[0]) ) { // in case of 'auto'
+ xy[0] = parseInt(Y.DOM.getStyle(node, LEFT), 10); // try inline
+ if ( isNaN(xy[0]) ) { // default to offset value
+ xy[0] = (pos === RELATIVE) ? 0 : node.offsetLeft || 0;
+ }
+ }
+
+ if ( isNaN(xy[1]) ) { // in case of 'auto'
+ xy[1] = parseInt(Y.DOM.getStyle(node, TOP), 10); // try inline
+ if ( isNaN(xy[1]) ) { // default to offset value
+ xy[1] = (pos === RELATIVE) ? 0 : node.offsetTop || 0;
+ }
+ }
+ }
+
+ return xy;
+
+ },
+
+ /**
+ * Gets the current X position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getX
+ * @param element The target element
+ * @return {Int} The X position of the element
+ */
+
+ getX: function(node) {
+ return Y.DOM.getXY(node)[0];
+ },
+
+ /**
+ * Gets the current Y position of an element based on page coordinates.
+ * Element must be part of the DOM tree to have page coordinates
+ * (display:none or elements not appended return false).
+ * @method getY
+ * @param element The target element
+ * @return {Int} The Y position of the element
+ */
+
+ getY: function(node) {
+ return Y.DOM.getXY(node)[1];
+ },
+
+ /**
+ * Set the position of an html element in page coordinates.
+ * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setXY
+ * @param element The target element
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
+ */
+ setXY: function(node, xy, noRetry) {
+ var setStyle = Y.DOM.setStyle,
+ pos,
+ delta,
+ newXY,
+ currentXY;
+
+ if (node && xy) {
+ pos = Y.DOM.getStyle(node, POSITION);
+
+ delta = Y.DOM._getOffset(node);
+
+ if (pos == 'static') { // default to relative
+ pos = RELATIVE;
+ setStyle(node, POSITION, pos);
+ }
+
+ currentXY = Y.DOM.getXY(node);
+
+ if (xy[0] !== null) {
+ setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
+ }
+
+ if (xy[1] !== null) {
+ setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
+ }
+
+ if (!noRetry) {
+ newXY = Y.DOM.getXY(node);
+ if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
+ Y.DOM.setXY(node, xy, true);
+ }
+ }
+
+ } else {
+ }
+ },
+
+ /**
+ * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setX
+ * @param element The target element
+ * @param {Int} x The X values for new position (coordinates are page-based)
+ */
+ setX: function(node, x) {
+ return Y.DOM.setXY(node, [x, null]);
+ },
+
+ /**
+ * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
+ * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
+ * @method setY
+ * @param element The target element
+ * @param {Int} y The Y values for new position (coordinates are page-based)
+ */
+ setY: function(node, y) {
+ return Y.DOM.setXY(node, [null, y]);
+ },
+
+ _calcBorders: function(node, xy2) {
+ var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
+ l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
+ if (Y.UA.gecko) {
+ if (RE_TABLE.test(node.tagName)) {
+ t = 0;
+ l = 0;
+ }
+ }
+ xy2[0] += l;
+ xy2[1] += t;
+ return xy2;
+ },
+
+ _getWinSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ win = doc.defaultView || doc.parentWindow,
+ mode = doc[COMPAT_MODE],
+ h = win.innerHeight,
+ w = win.innerWidth,
+ root = doc[DOCUMENT_ELEMENT];
+
+ if ( mode && !Y.UA.opera ) { // IE, Gecko
+ if (mode != 'CSS1Compat') { // Quirks
+ root = doc.body;
+ }
+ h = root.clientHeight;
+ w = root.clientWidth;
+ }
+ return { height: h, width: w };
+ },
+
+ _getDocSize: function(node) {
+ var doc = Y.DOM._getDoc(),
+ root = doc[DOCUMENT_ELEMENT];
+
+ if (doc[COMPAT_MODE] != 'CSS1Compat') {
+ root = doc.body;
+ }
+
+ return { height: root.scrollHeight, width: root.scrollWidth };
+ }
+});
+})(Y);
+(function(Y) {
+var TOP = 'top',
+ RIGHT = 'right',
+ BOTTOM = 'bottom',
+ LEFT = 'left',
+
+ getOffsets = function(r1, r2) {
+ var t = Math.max(r1[TOP], r2[TOP]),
+ r = Math.min(r1[RIGHT], r2[RIGHT]),
+ b = Math.min(r1[BOTTOM], r2[BOTTOM]),
+ l = Math.max(r1[LEFT], r2[LEFT]),
+ ret = {};
+
+ ret[TOP] = t;
+ ret[RIGHT] = r;
+ ret[BOTTOM] = b;
+ ret[LEFT] = l;
+ return ret;
+ },
+
+ DOM = Y.DOM;
+
+Y.mix(DOM, {
+ /**
+ * Returns an Object literal containing the following about this element: (top, right, bottom, left)
+ * @method region
+ * @param {HTMLElement} element The DOM element.
+ @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
+ */
+ region: function(node) {
+ var xy = DOM.getXY(node),
+ ret = false;
+
+ if (node && xy) {
+ ret = DOM._getRegion(
+ xy[1], // top
+ xy[0] + node.offsetWidth, // right
+ xy[1] + node.offsetHeight, // bottom
+ xy[0] // left
+ );
+ }
+
+ return ret;
+ },
+
+ /**
+ * Find the intersect information for the passes nodes.
+ * @method intersect
+ * @param {HTMLElement} element The first element
+ * @param {HTMLElement | Object} element2 The element or region to check the interect with
+ * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
+ @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
+ */
+ intersect: function(node, node2, altRegion) {
+ var r = altRegion || DOM.region(node), region = {},
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ off = getOffsets(region, r);
+ return {
+ top: off[TOP],
+ right: off[RIGHT],
+ bottom: off[BOTTOM],
+ left: off[LEFT],
+ area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
+ yoff: ((off[BOTTOM] - off[TOP])),
+ xoff: (off[RIGHT] - off[LEFT]),
+ inRegion: DOM.inRegion(node, node2, false, altRegion)
+ };
+
+ },
+ /**
+ * Check if any part of this node is in the passed region
+ * @method inRegion
+ * @param {Object} node2 The node to get the region from or an Object literal of the region
+ * $param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inRegion: function(node, node2, all, altRegion) {
+ var region = {},
+ r = altRegion || DOM.region(node),
+ n = node2,
+ off;
+
+ if (n.tagName) {
+ region = DOM.region(n);
+ } else if (Y.Lang.isObject(node2)) {
+ region = node2;
+ } else {
+ return false;
+ }
+
+ if (all) {
+ return (
+ r[LEFT] >= region[LEFT] &&
+ r[RIGHT] <= region[RIGHT] &&
+ r[TOP] >= region[TOP] &&
+ r[BOTTOM] <= region[BOTTOM] );
+ } else {
+ off = getOffsets(region, r);
+ if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+ },
+
+ /**
+ * Check if any part of this element is in the viewport
+ * @method inViewportRegion
+ * @param {HTMLElement} element The DOM element.
+ * @param {Boolean} all Should all of the node be inside the region
+ * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
+ * @return {Boolean} True if in region, false if not.
+ */
+ inViewportRegion: function(node, all, altRegion) {
+ return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
+
+ },
+
+ _getRegion: function(t, r, b, l) {
+ var region = {};
+
+ region[TOP] = region[1] = t;
+ region[LEFT] = region[0] = l;
+ region[BOTTOM] = b;
+ region[RIGHT] = r;
+ region.width = region[RIGHT] - region[LEFT];
+ region.height = region[BOTTOM] - region[TOP];
+
+ return region;
+ },
+
+ /**
+ * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
+ * @method viewportRegion
+ @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
+ */
+ viewportRegion: function(node) {
+ node = node || Y.config.doc.documentElement;
+ var ret = false,
+ scrollX,
+ scrollY;
+
+ if (node) {
+ scrollX = DOM.docScrollX(node);
+ scrollY = DOM.docScrollY(node);
+
+ ret = DOM._getRegion(scrollY, // top
+ DOM.winWidth(node) + scrollX, // right
+ scrollY + DOM.winHeight(node), // bottom
+ scrollX); // left
+ }
+
+ return ret;
+ }
+});
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base', 'dom-style']});
+YUI.add('selector-native', function(Y) {
+
+(function(Y) {
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
+
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
+
+Y.namespace('Selector'); // allow native module to standalone
+
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TMP_PREFIX = 'yui-tmp-',
+ g_counter = 0;
+
+var Selector = {
+ _foundCache: [],
+
+ useNative: true,
+
+ _compare: ('sourceIndex' in document.documentElement) ?
+ function(nodeA, nodeB) {
+ var a = nodeA.sourceIndex,
+ b = nodeB.sourceIndex;
+
+ if (a === b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ }
+
+ return -1;
+
+ } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
+ function(nodeA, nodeB) {
+ if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } :
+ function(nodeA, nodeB) {
+ var rangeA, rangeB, compare;
+ if (nodeA && nodeB) {
+ rangeA = nodeA[OWNER_DOCUMENT].createRange();
+ rangeA.setStart(nodeA, 0);
+ rangeB = nodeB[OWNER_DOCUMENT].createRange();
+ rangeB.setStart(nodeB, 0);
+ compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
+ }
+
+ return compare;
+
+ }),
+
+ _sort: function(nodes) {
+ if (nodes) {
+ nodes = Y.Array(nodes, 0, true);
+ if (nodes.sort) {
+ nodes.sort(Selector._compare);
+ }
+ }
+
+ return nodes;
+ },
+
+ _deDupe: function(nodes) {
+ var ret = [],
+ i, node;
+
+ for (i = 0; (node = nodes[i++]);) {
+ if (!node._found) {
+ ret[ret.length] = node;
+ node._found = true;
+ }
+ }
+
+ for (i = 0; (node = ret[i++]);) {
+ node._found = null;
+ node.removeAttribute('_found');
+ }
+
+ return ret;
+ },
+
+ /**
+ * Retrieves a set of nodes based on a given CSS selector.
+ * @method query
+ *
+ * @param {string} selector The CSS Selector to test the node against.
+ * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
+ * @param {Boolean} firstOnly optional Whether or not to return only the first match.
+ * @return {Array} An array of nodes that match the given selector.
+ * @static
+ */
+ query: function(selector, root, firstOnly, skipNative) {
+ root = root || Y.config.doc;
+ var ret = [],
+ useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
+ queries = [[selector, root]],
+ query,
+ result,
+ i,
+ fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
+
+ if (selector && fn) {
+ // split group into seperate queries
+ if (!skipNative && // already done if skipping
+ (!useNative || root.tagName)) { // split native when element scoping is needed
+ queries = Selector._splitQueries(selector, root);
+ }
+
+ for (i = 0; (query = queries[i++]);) {
+ result = fn(query[0], query[1], firstOnly);
+ if (!firstOnly) { // coerce DOM Collection to Array
+ result = Y.Array(result, 0, true);
+ }
+ if (result) {
+ ret = ret.concat(result);
+ }
+ }
+
+ if (queries.length > 1) { // remove dupes and sort by doc order
+ ret = Selector._sort(Selector._deDupe(ret));
+ }
+ }
+
+ return (firstOnly) ? (ret[0] || null) : ret;
+
+ },
+
+ // allows element scoped queries to begin with combinator
+ // e.g. query('> p', document.body) === query('body > p')
+ _splitQueries: function(selector, node) {
+ var groups = selector.split(','),
+ queries = [],
+ prefix = '',
+ i, len;
+
+ if (node) {
+ // enforce for element scoping
+ if (node.tagName) {
+ node.id = node.id || Y.guid();
+ prefix = '#' + node.id + ' ';
+ }
+
+ for (i = 0, len = groups.length; i < len; ++i) {
+ selector = prefix + groups[i];
+ queries.push([selector, node]);
+ }
+ }
+
+ return queries;
+ },
+
+ _nativeQuery: function(selector, root, one) {
+ try {
+ return root['querySelector' + (one ? '' : 'All')](selector);
+ } catch(e) { // fallback to brute if available
+ return Y.Selector.query(selector, root, one, true); // redo with skipNative true
+ }
+ },
+
+ filter: function(nodes, selector) {
+ var ret = [],
+ i, node;
+
+ if (nodes && selector) {
+ for (i = 0; (node = nodes[i++]);) {
+ if (Y.Selector.test(node, selector)) {
+ ret[ret.length] = node;
+ }
+ }
+ } else {
+ }
+
+ return ret;
+ },
+
+ test: function(node, selector, root) {
+ var ret = false,
+ groups = selector.split(','),
+ item,
+ i, group;
+
+ if (node && node.tagName) { // only test HTMLElements
+ root = root || node.ownerDocument;
+
+ if (!node.id) {
+ node.id = TMP_PREFIX + g_counter++;
+ }
+ for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
+ group += '#' + node.id; // add ID for uniqueness
+ item = Y.Selector.query(group, root, true);
+ ret = (item === node);
+ if (ret) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+};
+
+Y.mix(Y.Selector, Selector, true);
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
+YUI.add('selector-css2', function(Y) {
+
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
+
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
+
+ return ret || [];
+ },
+
+ _regexCache: {},
+
+ _re: {
+ attr: /(\[.*\])/g,
+ pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
+ },
+
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
+ },
+
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
+
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
+
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ id,
+ className,
+ tagName;
+
+
+ // if we have an initial ID, set to root when in document
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ // try ID first
+ if (id) {
+ if (rootDoc.getElementById(id)) { // if in document
+ nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
+ }
+ // try className if supported
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else if (tagName) { // default to tagName
+ nodes = root.getElementsByTagName(tagName || '*');
+ }
+
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
+ }
+ }
+
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
+
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (operator.test && !operator.test(value)) || // regex test
+ (operator.call && !operator(tmpNode, test[0]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
+
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
+ }
+
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
+
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
+ }
+ break;
+ }
+ }
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ _getRegExp: function(str, flags) {
+ var regexCache = Selector._regexCache;
+ flags = flags || '';
+ if (!regexCache[str + flags]) {
+ regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return regexCache[str + flags];
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
+ }
+ },
+
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Y.Selector.operators,
+ test;
+
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ document.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
+ token[match[1]] = match[3];
+ }
+
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
+ }
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
+ }
+ }
+ }
+ ],
+
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
+
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
+
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
+
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
+
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
+
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
+
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
+
+ if (!found || selector.length) { // not fully parsed
+ tokens = [];
+ }
+ return tokens;
+ },
+
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
+ pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
+ re, i, len;
+
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
+ }
+
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
+ }
+
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
+ }
+ }
+
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
+ }
+ }
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
+ }
+ }
+ return selector;
+ },
+
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
+
+// IE wants class with native queries
+if (Y.Selector.useNative && document.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
+
+
+
+}, '3.0.0' ,{requires:['selector-native']});
+
+
+YUI.add('selector', function(Y){}, '3.0.0' ,{use:['selector-native', 'selector-css2']});
+
+
+
+YUI.add('dom', function(Y){}, '3.0.0' ,{use:['dom-base', 'dom-style', 'dom-screen', 'selector']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-css2', function(Y) {
+
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
+
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
+
+ return ret || [];
+ },
+
+ _regexCache: {},
+
+ _re: {
+ attr: /(\[.*\])/g,
+ pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
+ },
+
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
+ },
+
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
+
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
+
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ id,
+ className,
+ tagName;
+
+
+ // if we have an initial ID, set to root when in document
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ // try ID first
+ if (id) {
+ if (rootDoc.getElementById(id)) { // if in document
+ nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
+ }
+ // try className if supported
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else if (tagName) { // default to tagName
+ nodes = root.getElementsByTagName(tagName || '*');
+ }
+
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
+ }
+ }
+
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
+
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (operator.test && !operator.test(value)) || // regex test
+ (operator.call && !operator(tmpNode, test[0]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
+
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
+ }
+
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
+
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
+ }
+ break;
+ }
+ }
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ _getRegExp: function(str, flags) {
+ var regexCache = Selector._regexCache;
+ flags = flags || '';
+ if (!regexCache[str + flags]) {
+ regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return regexCache[str + flags];
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
+ }
+ },
+
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Y.Selector.operators,
+ test;
+
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ document.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
+ token[match[1]] = match[3];
+ }
+
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
+ }
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
+ }
+ }
+ }
+ ],
+
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
+
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
+
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
+
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
+
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
+
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
+
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
+
+ if (!found || selector.length) { // not fully parsed
+ Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
+ tokens = [];
+ }
+ return tokens;
+ },
+
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
+ pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
+ re, i, len;
+
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
+ }
+
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
+ }
+
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
+ }
+ }
+
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
+ }
+ }
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
+ }
+ }
+ return selector;
+ },
+
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
+
+// IE wants class with native queries
+if (Y.Selector.useNative && document.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
+
+
+
+}, '3.0.0' ,{requires:['selector-native']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("selector-css2",function(G){var H="parentNode",D="tagName",E="attributes",A="combinator",F="pseudos",C=G.Selector,B={SORT_RESULTS:true,_children:function(M,I){var J=M.children,L,K=[],N,O;if(M.children&&I&&M.children.tags){K=M.children.tags(I);}else{if((!J&&M[D])||(J&&I)){N=J||M.childNodes;J=[];for(L=0;(O=N[L++]);){if(O.tagName){if(!I||I===O.tagName){J.push(O);}}}}}return J||[];},_regexCache:{},_re:{attr:/(\[.*\])/g,pseudos:/:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i},shorthand:{"\\#(-?[_a-z]+[-\\w]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w]*)":"[className~=$1]"},operators:{"":function(J,I){return G.DOM.getAttribute(J,I)!=="";},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}-?"},pseudos:{"first-child":function(I){return G.Selector._children(I[H])[0]===I;}},_bruteQuery:function(M,Q,S){var N=[],I=[],P=C._tokenize(M),L=P[P.length-1],R=G.DOM._getDoc(Q),J,O,K;if(P[0]&&R===Q&&(J=P[0].id)&&R.getElementById(J)){Q=R.getElementById(J);}if(L){J=L.id;O=L.className;K=L.tagName||"*";if(J){if(R.getElementById(J)){I=[R.getElementById(J)];}}else{if(O){I=Q.getElementsByClassName(O);}else{if(K){I=Q.getElementsByTagName(K||"*");}}}if(I.length){N=C._filterNodes(I,P,S);}}return N;},_filterNodes:function(R,N,P){var W=0,V,X=N.length,Q=X-1,M=[],T=R[0],a=T,Y=G.Selector.getters,L,U,K,O,I,S,J,Z;for(W=0;(a=T=R[W++]);){Q=X-1;O=null;testLoop:while(a&&a.tagName){K=N[Q];J=K.tests;V=J.length;if(V&&!I){while((Z=J[--V])){L=Z[1];if(Y[Z[0]]){S=Y[Z[0]](a,Z[0]);}else{S=a[Z[0]];if(S===undefined&&a.getAttribute){S=a.getAttribute(Z[0]);}}if((L==="="&&S!==Z[2])||(L.test&&!L.test(S))||(L.call&&!L(a,Z[0]))){if((a=a[O])){while(a&&(!a.tagName||(K.tagName&&K.tagName!==a.tagName))){a=a[O];}}continue testLoop;}}}Q--;if(!I&&(U=K.combinator)){O=U.axis;a=a[O];while(a&&!a.tagName){a=a[O];}if(U.direct){O=null;}}else{M.push(T);if(P){return M;}break;}}}T=a=null;return M;},_getRegExp:function(K,I){var J=C._regexCache;I=I||"";if(!J[K+I]){J[K+I]=new RegExp(K,I);}return J[K+I];},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:true},"+":{axis:"previousSibling",direct:true}},_parsers:[{name:E,re:/^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,fn:function(K,L){var J=K[2]||"",I=G.Selector.operators,M;if((K[1]==="id"&&J==="=")||(K[1]==="className"&&document.getElementsByClassName&&(J==="~="||J==="="))){L.prefilter=K[1];L[K[1]]=K[3];}if(J in I){M=I[J];if(typeof M==="string"){M=G.Selector._getRegExp(M.replace("{val}",K[3]));}K[2]=M;}if(!L.last||L.prefilter!==K[1]){return K.slice(1);}}},{name:D,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(J,K){var I=J[1].toUpperCase();K.tagName=I;if(I!=="*"&&(!K.last||K.prefilter)){return[D,"=",I];}if(!K.prefilter){K.prefilter="tagName";}}},{name:A,re:/^\s*([>+~]|\s)\s*/,fn:function(I,J){}},{name:F,re:/^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,fn:function(I,J){var K=C[F][I[1]];if(K){return[I[2],K];}else{return false;}}}],_getToken:function(I){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]};},_tokenize:function(K){K=K||"";K=C._replaceShorthand(G.Lang.trim(K));var J=C._getToken(),P=K,O=[],Q=false,M,N,L,I;outer:do{Q=false;for(L=0;(I=C._parsers[L++]);){if((M=I.re.exec(K))){if(I!==A){J.selector=K;}K=K.replace(M[0],"");if(!K.length){J.last=true;}if(C._attrFilters[M[1]]){M[1]=C._attrFilters[M[1]];}N=I.fn(M,J);if(N===false){Q=false;break outer;}else{if(N){J.tests.push(N);}}if(!K.length||I.name===A){O.push(J);J=C._getToken(J);if(I.name===A){J.combinator=G.Selector.combinators[M[1]];}}Q=true;}}}while(Q&&K.length);if(!Q||K.length){O=[];}return O;},_replaceShorthand:function(J){var K=C.shorthand,L=J.match(C._re.attr),O=J.match(C._re.pseudos),N,M,I;if(O){J=J.replace(C._re.pseudos,"!!REPLACED_PSEUDO!!");}if(L){J=J.replace(C._re.attr,"!!REPLACED_ATTRIBUTE!!");}for(N in K){if(K.hasOwnProperty(N)){J=J.replace(C._getRegExp(N,"gi"),K[N]);}}if(L){for(M=0,I=L.length;M<I;++M){J=J.replace("!!REPLACED_ATTRIBUTE!!",L[M]);}}if(O){for(M=0,I=O.length;M<I;++M){J=J.replace("!!REPLACED_PSEUDO!!",O[M]);}}return J;},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(J,I){return G.DOM.getAttribute(J,I);}}};G.mix(G.Selector,B,true);G.Selector.getters.src=G.Selector.getters.rel=G.Selector.getters.href;if(G.Selector.useNative&&document.querySelector){G.Selector.shorthand["\\.(-?[_a-z]+[-\\w]*)"]="[class~=$1]";}},"3.0.0",{requires:["selector-native"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-css2', function(Y) {
+
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
+
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
+
+ return ret || [];
+ },
+
+ _regexCache: {},
+
+ _re: {
+ attr: /(\[.*\])/g,
+ pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
+ },
+
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
+ },
+
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
+
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
+
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ id,
+ className,
+ tagName;
+
+
+ // if we have an initial ID, set to root when in document
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ // try ID first
+ if (id) {
+ if (rootDoc.getElementById(id)) { // if in document
+ nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
+ }
+ // try className if supported
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else if (tagName) { // default to tagName
+ nodes = root.getElementsByTagName(tagName || '*');
+ }
+
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
+ }
+ }
+
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
+
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (operator.test && !operator.test(value)) || // regex test
+ (operator.call && !operator(tmpNode, test[0]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
+
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
+ }
+
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
+
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
+ }
+ break;
+ }
+ }
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ _getRegExp: function(str, flags) {
+ var regexCache = Selector._regexCache;
+ flags = flags || '';
+ if (!regexCache[str + flags]) {
+ regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return regexCache[str + flags];
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
+ }
+ },
+
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Y.Selector.operators,
+ test;
+
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ document.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
+ token[match[1]] = match[3];
+ }
+
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
+ }
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
+ }
+ }
+ }
+ ],
+
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
+
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
+
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
+
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
+
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
+
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
+
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
+
+ if (!found || selector.length) { // not fully parsed
+ tokens = [];
+ }
+ return tokens;
+ },
+
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
+ pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
+ re, i, len;
+
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
+ }
+
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
+ }
+
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
+ }
+ }
+
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
+ }
+ }
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
+ }
+ }
+ return selector;
+ },
+
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
+
+// IE wants class with native queries
+if (Y.Selector.useNative && document.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
+
+
+
+}, '3.0.0' ,{requires:['selector-native']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-css3', function(Y) {
+
+/**
+ * The selector css3 module provides support for css3 selectors.
+ * @module dom
+ * @submodule selector-css3
+ * @for Selector
+ */
+
+/*
+ an+b = get every _a_th node starting at the _b_th
+ 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
+ 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
+ an+0 = get every _a_th element, "0" may be omitted
+*/
+
+Y.Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
+
+Y.Selector._getNth = function(node, expr, tag, reverse) {
+ Y.Selector._reNth.test(expr);
+ var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
+ n = RegExp.$2, // "n"
+ oddeven = RegExp.$3, // "odd" or "even"
+ b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
+ result = [],
+ siblings = Y.Selector._children(node.parentNode, tag),
+ op;
+
+ if (oddeven) {
+ a = 2; // always every other
+ op = '+';
+ n = 'n';
+ b = (oddeven === 'odd') ? 1 : 0;
+ } else if ( isNaN(a) ) {
+ a = (n) ? 1 : 0; // start from the first or no repeat
+ }
+
+ if (a === 0) { // just the first
+ if (reverse) {
+ b = siblings.length - b + 1;
+ }
+
+ if (siblings[b - 1] === node) {
+ return true;
+ } else {
+ return false;
+ }
+
+ } else if (a < 0) {
+ reverse = !!reverse;
+ a = Math.abs(a);
+ }
+
+ if (!reverse) {
+ for (var i = b - 1, len = siblings.length; i < len; i += a) {
+ if ( i >= 0 && siblings[i] === node ) {
+ return true;
+ }
+ }
+ } else {
+ for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
+ if ( i < len && siblings[i] === node ) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+Y.mix(Y.Selector.pseudos, {
+ 'root': function(node) {
+ return node === node.ownerDocument.documentElement;
+ },
+
+ 'nth-child': function(node, expr) {
+ return Y.Selector._getNth(node, expr);
+ },
+
+ 'nth-last-child': function(node, expr) {
+ return Y.Selector._getNth(node, expr, null, true);
+ },
+
+ 'nth-of-type': function(node, expr) {
+ return Y.Selector._getNth(node, expr, node.tagName);
+ },
+
+ 'nth-last-of-type': function(node, expr) {
+ return Y.Selector._getNth(node, expr, node.tagName, true);
+ },
+
+ 'last-child': function(node) {
+ var children = Y.Selector._children(node.parentNode);
+ return children[children.length - 1] === node;
+ },
+
+ 'first-of-type': function(node) {
+ return Y.Selector._children(node.parentNode, node.tagName)[0] === node;
+ },
+
+ 'last-of-type': function(node) {
+ var children = Y.Selector._children(node.parentNode, node.tagName);
+ return children[children.length - 1] === node;
+ },
+
+ 'only-child': function(node) {
+ var children = Y.Selector._children(node.parentNode);
+ return children.length === 1 && children[0] === node;
+ },
+
+ 'only-of-type': function(node) {
+ var children = Y.Selector._children(node.parentNode, node.tagName);
+ return children.length === 1 && children[0] === node;
+ },
+
+ 'empty': function(node) {
+ return node.childNodes.length === 0;
+ },
+
+ 'not': function(node, expr) {
+ return !Y.Selector.test(node, expr);
+ },
+
+ 'contains': function(node, expr) {
+ var text = node.innerText || node.textContent || '';
+ return text.indexOf(expr) > -1;
+ },
+
+ 'checked': function(node) {
+ return node.checked === true;
+ }
+});
+
+Y.mix(Y.Selector.operators, {
+ '^=': '^{val}', // Match starts with value
+ '$=': '{val}$', // Match ends with value
+ '*=': '{val}' // Match contains value as substring
+});
+
+Y.Selector.combinators['~'] = {
+ axis: 'previousSibling'
+};
+
+
+}, '3.0.0' ,{requires:['dom-base', 'selector-native', 'selector-css2']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("selector-css3",function(A){A.Selector._reNth=/^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;A.Selector._getNth=function(C,L,N,G){A.Selector._reNth.test(L);var K=parseInt(RegExp.$1,10),B=RegExp.$2,H=RegExp.$3,I=parseInt(RegExp.$4,10)||0,M=[],J=A.Selector._children(C.parentNode,N),E;if(H){K=2;E="+";B="n";I=(H==="odd")?1:0;}else{if(isNaN(K)){K=(B)?1:0;}}if(K===0){if(G){I=J.length-I+1;}if(J[I-1]===C){return true;}else{return false;}}else{if(K<0){G=!!G;K=Math.abs(K);}}if(!G){for(var D=I-1,F=J.length;D<F;D+=K){if(D>=0&&J[D]===C){return true;}}}else{for(var D=J.length-I,F=J.length;D>=0;D-=K){if(D<F&&J[D]===C){return true;}}}return false;};A.mix(A.Selector.pseudos,{"root":function(B){return B===B.ownerDocument.documentElement;},"nth-child":function(B,C){return A.Selector._getNth(B,C);},"nth-last-child":function(B,C){return A.Selector._getNth(B,C,null,true);},"nth-of-type":function(B,C){return A.Selector._getNth(B,C,B.tagName);},"nth-last-of-type":function(B,C){return A.Selector._getNth(B,C,B.tagName,true);},"last-child":function(C){var B=A.Selector._children(C.parentNode);return B[B.length-1]===C;},"first-of-type":function(B){return A.Selector._children(B.parentNode,B.tagName)[0]===B;},"last-of-type":function(C){var B=A.Selector._children(C.parentNode,C.tagName);return B[B.length-1]===C;},"only-child":function(C){var B=A.Selector._children(C.parentNode);return B.length===1&&B[0]===C;},"only-of-type":function(C){var B=A.Selector._children(C.parentNode,C.tagName);return B.length===1&&B[0]===C;},"empty":function(B){return B.childNodes.length===0;},"not":function(B,C){return !A.Selector.test(B,C);},"contains":function(B,C){var D=B.innerText||B.textContent||"";return D.indexOf(C)>-1;},"checked":function(B){return B.checked===true;}});A.mix(A.Selector.operators,{"^=":"^{val}","$=":"{val}$","*=":"{val}"});A.Selector.combinators["~"]={axis:"previousSibling"};},"3.0.0",{requires:["dom-base","selector-native","selector-css2"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-css3', function(Y) {
+
+/**
+ * The selector css3 module provides support for css3 selectors.
+ * @module dom
+ * @submodule selector-css3
+ * @for Selector
+ */
+
+/*
+ an+b = get every _a_th node starting at the _b_th
+ 0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
+ 1n+b = get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
+ an+0 = get every _a_th element, "0" may be omitted
+*/
+
+Y.Selector._reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
+
+Y.Selector._getNth = function(node, expr, tag, reverse) {
+ Y.Selector._reNth.test(expr);
+ var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
+ n = RegExp.$2, // "n"
+ oddeven = RegExp.$3, // "odd" or "even"
+ b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
+ result = [],
+ siblings = Y.Selector._children(node.parentNode, tag),
+ op;
+
+ if (oddeven) {
+ a = 2; // always every other
+ op = '+';
+ n = 'n';
+ b = (oddeven === 'odd') ? 1 : 0;
+ } else if ( isNaN(a) ) {
+ a = (n) ? 1 : 0; // start from the first or no repeat
+ }
+
+ if (a === 0) { // just the first
+ if (reverse) {
+ b = siblings.length - b + 1;
+ }
+
+ if (siblings[b - 1] === node) {
+ return true;
+ } else {
+ return false;
+ }
+
+ } else if (a < 0) {
+ reverse = !!reverse;
+ a = Math.abs(a);
+ }
+
+ if (!reverse) {
+ for (var i = b - 1, len = siblings.length; i < len; i += a) {
+ if ( i >= 0 && siblings[i] === node ) {
+ return true;
+ }
+ }
+ } else {
+ for (var i = siblings.length - b, len = siblings.length; i >= 0; i -= a) {
+ if ( i < len && siblings[i] === node ) {
+ return true;
+ }
+ }
+ }
+ return false;
+};
+
+Y.mix(Y.Selector.pseudos, {
+ 'root': function(node) {
+ return node === node.ownerDocument.documentElement;
+ },
+
+ 'nth-child': function(node, expr) {
+ return Y.Selector._getNth(node, expr);
+ },
+
+ 'nth-last-child': function(node, expr) {
+ return Y.Selector._getNth(node, expr, null, true);
+ },
+
+ 'nth-of-type': function(node, expr) {
+ return Y.Selector._getNth(node, expr, node.tagName);
+ },
+
+ 'nth-last-of-type': function(node, expr) {
+ return Y.Selector._getNth(node, expr, node.tagName, true);
+ },
+
+ 'last-child': function(node) {
+ var children = Y.Selector._children(node.parentNode);
+ return children[children.length - 1] === node;
+ },
+
+ 'first-of-type': function(node) {
+ return Y.Selector._children(node.parentNode, node.tagName)[0] === node;
+ },
+
+ 'last-of-type': function(node) {
+ var children = Y.Selector._children(node.parentNode, node.tagName);
+ return children[children.length - 1] === node;
+ },
+
+ 'only-child': function(node) {
+ var children = Y.Selector._children(node.parentNode);
+ return children.length === 1 && children[0] === node;
+ },
+
+ 'only-of-type': function(node) {
+ var children = Y.Selector._children(node.parentNode, node.tagName);
+ return children.length === 1 && children[0] === node;
+ },
+
+ 'empty': function(node) {
+ return node.childNodes.length === 0;
+ },
+
+ 'not': function(node, expr) {
+ return !Y.Selector.test(node, expr);
+ },
+
+ 'contains': function(node, expr) {
+ var text = node.innerText || node.textContent || '';
+ return text.indexOf(expr) > -1;
+ },
+
+ 'checked': function(node) {
+ return node.checked === true;
+ }
+});
+
+Y.mix(Y.Selector.operators, {
+ '^=': '^{val}', // Match starts with value
+ '$=': '{val}$', // Match ends with value
+ '*=': '{val}' // Match contains value as substring
+});
+
+Y.Selector.combinators['~'] = {
+ axis: 'previousSibling'
+};
+
+
+}, '3.0.0' ,{requires:['dom-base', 'selector-native', 'selector-css2']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-native', function(Y) {
+
+(function(Y) {
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
+
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
+
+Y.namespace('Selector'); // allow native module to standalone
+
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TMP_PREFIX = 'yui-tmp-',
+ g_counter = 0;
+
+var Selector = {
+ _foundCache: [],
+
+ useNative: true,
+
+ _compare: ('sourceIndex' in document.documentElement) ?
+ function(nodeA, nodeB) {
+ var a = nodeA.sourceIndex,
+ b = nodeB.sourceIndex;
+
+ if (a === b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ }
+
+ return -1;
+
+ } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
+ function(nodeA, nodeB) {
+ if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } :
+ function(nodeA, nodeB) {
+ var rangeA, rangeB, compare;
+ if (nodeA && nodeB) {
+ rangeA = nodeA[OWNER_DOCUMENT].createRange();
+ rangeA.setStart(nodeA, 0);
+ rangeB = nodeB[OWNER_DOCUMENT].createRange();
+ rangeB.setStart(nodeB, 0);
+ compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
+ }
+
+ return compare;
+
+ }),
+
+ _sort: function(nodes) {
+ if (nodes) {
+ nodes = Y.Array(nodes, 0, true);
+ if (nodes.sort) {
+ nodes.sort(Selector._compare);
+ }
+ }
+
+ return nodes;
+ },
+
+ _deDupe: function(nodes) {
+ var ret = [],
+ i, node;
+
+ for (i = 0; (node = nodes[i++]);) {
+ if (!node._found) {
+ ret[ret.length] = node;
+ node._found = true;
+ }
+ }
+
+ for (i = 0; (node = ret[i++]);) {
+ node._found = null;
+ node.removeAttribute('_found');
+ }
+
+ return ret;
+ },
+
+ /**
+ * Retrieves a set of nodes based on a given CSS selector.
+ * @method query
+ *
+ * @param {string} selector The CSS Selector to test the node against.
+ * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
+ * @param {Boolean} firstOnly optional Whether or not to return only the first match.
+ * @return {Array} An array of nodes that match the given selector.
+ * @static
+ */
+ query: function(selector, root, firstOnly, skipNative) {
+ root = root || Y.config.doc;
+ var ret = [],
+ useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
+ queries = [[selector, root]],
+ query,
+ result,
+ i,
+ fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
+
+ if (selector && fn) {
+ // split group into seperate queries
+ if (!skipNative && // already done if skipping
+ (!useNative || root.tagName)) { // split native when element scoping is needed
+ queries = Selector._splitQueries(selector, root);
+ }
+
+ for (i = 0; (query = queries[i++]);) {
+ result = fn(query[0], query[1], firstOnly);
+ if (!firstOnly) { // coerce DOM Collection to Array
+ result = Y.Array(result, 0, true);
+ }
+ if (result) {
+ ret = ret.concat(result);
+ }
+ }
+
+ if (queries.length > 1) { // remove dupes and sort by doc order
+ ret = Selector._sort(Selector._deDupe(ret));
+ }
+ }
+
+ Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
+ return (firstOnly) ? (ret[0] || null) : ret;
+
+ },
+
+ // allows element scoped queries to begin with combinator
+ // e.g. query('> p', document.body) === query('body > p')
+ _splitQueries: function(selector, node) {
+ var groups = selector.split(','),
+ queries = [],
+ prefix = '',
+ i, len;
+
+ if (node) {
+ // enforce for element scoping
+ if (node.tagName) {
+ node.id = node.id || Y.guid();
+ prefix = '#' + node.id + ' ';
+ }
+
+ for (i = 0, len = groups.length; i < len; ++i) {
+ selector = prefix + groups[i];
+ queries.push([selector, node]);
+ }
+ }
+
+ return queries;
+ },
+
+ _nativeQuery: function(selector, root, one) {
+ try {
+ //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
+ return root['querySelector' + (one ? '' : 'All')](selector);
+ } catch(e) { // fallback to brute if available
+ //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
+ return Y.Selector.query(selector, root, one, true); // redo with skipNative true
+ }
+ },
+
+ filter: function(nodes, selector) {
+ var ret = [],
+ i, node;
+
+ if (nodes && selector) {
+ for (i = 0; (node = nodes[i++]);) {
+ if (Y.Selector.test(node, selector)) {
+ ret[ret.length] = node;
+ }
+ }
+ } else {
+ Y.log('invalid filter input (nodes: ' + nodes +
+ ', selector: ' + selector + ')', 'warn', 'Selector');
+ }
+
+ return ret;
+ },
+
+ test: function(node, selector, root) {
+ var ret = false,
+ groups = selector.split(','),
+ item,
+ i, group;
+
+ if (node && node.tagName) { // only test HTMLElements
+ root = root || node.ownerDocument;
+
+ if (!node.id) {
+ node.id = TMP_PREFIX + g_counter++;
+ }
+ for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
+ group += '#' + node.id; // add ID for uniqueness
+ item = Y.Selector.query(group, root, true);
+ ret = (item === node);
+ if (ret) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+};
+
+Y.mix(Y.Selector, Selector, true);
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
+YUI.add('selector-css2', function(Y) {
+
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
+
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
+
+ return ret || [];
+ },
+
+ _regexCache: {},
+
+ _re: {
+ attr: /(\[.*\])/g,
+ pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
+ },
+
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
+ },
+
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
+
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
+
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ id,
+ className,
+ tagName;
+
+
+ // if we have an initial ID, set to root when in document
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ // try ID first
+ if (id) {
+ if (rootDoc.getElementById(id)) { // if in document
+ nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
+ }
+ // try className if supported
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else if (tagName) { // default to tagName
+ nodes = root.getElementsByTagName(tagName || '*');
+ }
+
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
+ }
+ }
+
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
+
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (operator.test && !operator.test(value)) || // regex test
+ (operator.call && !operator(tmpNode, test[0]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
+
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
+ }
+
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
+
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
+ }
+ break;
+ }
+ }
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ _getRegExp: function(str, flags) {
+ var regexCache = Selector._regexCache;
+ flags = flags || '';
+ if (!regexCache[str + flags]) {
+ regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return regexCache[str + flags];
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
+ }
+ },
+
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Y.Selector.operators,
+ test;
+
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ document.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
+ token[match[1]] = match[3];
+ }
+
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
+ }
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
+ }
+ }
+ }
+ ],
+
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
+
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
+
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
+
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
+
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
+
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
+
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
+
+ if (!found || selector.length) { // not fully parsed
+ Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
+ tokens = [];
+ }
+ return tokens;
+ },
+
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
+ pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
+ re, i, len;
+
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
+ }
+
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
+ }
+
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
+ }
+ }
+
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
+ }
+ }
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
+ }
+ }
+ return selector;
+ },
+
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
+
+// IE wants class with native queries
+if (Y.Selector.useNative && document.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
+
+
+
+}, '3.0.0' ,{requires:['selector-native']});
+
+
+YUI.add('selector', function(Y){}, '3.0.0' ,{use:['selector-native', 'selector-css2']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("selector-native",function(A){(function(G){G.namespace("Selector");var E="compareDocumentPosition",F="ownerDocument",D="yui-tmp-",C=0;var B={_foundCache:[],useNative:true,_compare:("sourceIndex" in document.documentElement)?function(K,J){var I=K.sourceIndex,H=J.sourceIndex;if(I===H){return 0;}else{if(I>H){return 1;}}return -1;}:(document.documentElement[E]?function(I,H){if(I[E](H)&4){return -1;}else{return 1;}}:function(L,K){var J,H,I;if(L&&K){J=L[F].createRange();J.setStart(L,0);H=K[F].createRange();H.setStart(K,0);I=J.compareBoundaryPoints(1,H);}return I;}),_sort:function(H){if(H){H=G.Array(H,0,true);if(H.sort){H.sort(B._compare);}}return H;},_deDupe:function(H){var I=[],J,K;for(J=0;(K=H[J++]);){if(!K._found){I[I.length]=K;K._found=true;}}for(J=0;(K=I[J++]);){K._found=null;K.removeAttribute("_found");}return I;},query:function(I,P,Q,H){P=P||G.config.doc;var M=[],J=(G.Selector.useNative&&document.querySelector&&!H),L=[[I,P]],N,R,K,O=(J)?G.Selector._nativeQuery:G.Selector._bruteQuery;if(I&&O){if(!H&&(!J||P.tagName)){L=B._splitQueries(I,P);}for(K=0;(N=L[K++]);){R=O(N[0],N[1],Q);if(!Q){R=G.Array(R,0,true);}if(R){M=M.concat(R);}}if(L.length>1){M=B._sort(B._deDupe(M));}}return(Q)?(M[0]||null):M;},_splitQueries:function(J,M){var I=J.split(","),K=[],N="",L,H;if(M){if(M.tagName){M.id=M.id||G.guid();N="#"+M.id+" ";}for(L=0,H=I.length;L<H;++L){J=N+I[L];K.push([J,M]);}}return K;},_nativeQuery:function(H,I,J){try{return I["querySelector"+(J?"":"All")](H);}catch(K){return G.Selector.query(H,I,J,true);}},filter:function(I,H){var J=[],K,L;if(I&&H){for(K=0;(L=I[K++]);){if(G.Selector.test(L,H)){J[J.length]=L;}}}else{}return J;},test:function(N,I,J){var K=false,H=I.split(","),M,L,O;if(N&&N.tagName){J=J||N.ownerDocument;if(!N.id){N.id=D+C++;}for(L=0;(O=H[L++]);){O+="#"+N.id;M=G.Selector.query(O,J,true);K=(M===N);if(K){break;}}}return K;}};G.mix(G.Selector,B,true);})(A);},"3.0.0",{requires:["dom-base"]});YUI.add("selector-css2",function(G){var H="parentNode",D="tagName",E="attributes",A="combinator",F="pseudos",C=G.Selector,B={SORT_RESULTS:true,_children:function(M,I){var J=M.children,L,K=[],N,O;if(M.children&&I&&M.children.tags){K=M.children.tags(I);}else{if((!J&&M[D])||(J&&I)){N=J||M.childNodes;J=[];for(L=0;(O=N[L++]);){if(O.tagName){if(!I||I===O.tagName){J.push(O);}}}}}return J||[];},_regexCache:{},_re:{attr:/(\[.*\])/g,pseudos:/:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i},shorthand:{"\\#(-?[_a-z]+[-\\w]*)":"[id=$1]","\\.(-?[_a-z]+[-\\w]*)":"[className~=$1]"},operators:{"":function(J,I){return G.DOM.getAttribute(J,I)!=="";},"~=":"(?:^|\\s+){val}(?:\\s+|$)","|=":"^{val}-?"},pseudos:{"first-child":function(I){return G.Selector._children(I[H])[0]===I;}},_bruteQuery:function(M,Q,S){var N=[],I=[],P=C._tokenize(M),L=P[P.length-1],R=G.DOM._getDoc(Q),J,O,K;if(P[0]&&R===Q&&(J=P[0].id)&&R.getElementById(J)){Q=R.getElementById(J);}if(L){J=L.id;O=L.className;K=L.tagName||"*";if(J){if(R.getElementById(J)){I=[R.getElementById(J)];}}else{if(O){I=Q.getElementsByClassName(O);}else{if(K){I=Q.getElementsByTagName(K||"*");}}}if(I.length){N=C._filterNodes(I,P,S);}}return N;},_filterNodes:function(R,N,P){var W=0,V,X=N.length,Q=X-1,M=[],T=R[0],a=T,Y=G.Selector.getters,L,U,K,O,I,S,J,Z;for(W=0;(a=T=R[W++]);){Q=X-1;O=null;testLoop:while(a&&a.tagName){K=N[Q];J=K.tests;V=J.length;if(V&&!I){while((Z=J[--V])){L=Z[1];if(Y[Z[0]]){S=Y[Z[0]](a,Z[0]);}else{S=a[Z[0]];if(S===undefined&&a.getAttribute){S=a.getAttribute(Z[0]);}}if((L==="="&&S!==Z[2])||(L.test&&!L.test(S))||(L.call&&!L(a,Z[0]))){if((a=a[O])){while(a&&(!a.tagName||(K.tagName&&K.tagName!==a.tagName))){a=a[O];}}continue testLoop;}}}Q--;if(!I&&(U=K.combinator)){O=U.axis;a=a[O];while(a&&!a.tagName){a=a[O];}if(U.direct){O=null;}}else{M.push(T);if(P){return M;}break;}}}T=a=null;return M;},_getRegExp:function(K,I){var J=C._regexCache;I=I||"";if(!J[K+I]){J[K+I]=new RegExp(K,I);}return J[K+I];},combinators:{" ":{axis:"parentNode"},">":{axis:"parentNode",direct:true},"+":{axis:"previousSibling",direct:true}},_parsers:[{name:E,re:/^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,fn:function(K,L){var J=K[2]||"",I=G.Selector.operators,M;if((K[1]==="id"&&J==="=")||(K[1]==="className"&&document.getElementsByClassName&&(J==="~="||J==="="))){L.prefilter=K[1];L[K[1]]=K[3];}if(J in I){M=I[J];if(typeof M==="string"){M=G.Selector._getRegExp(M.replace("{val}",K[3]));}K[2]=M;}if(!L.last||L.prefilter!==K[1]){return K.slice(1);}}},{name:D,re:/^((?:-?[_a-z]+[\w-]*)|\*)/i,fn:function(J,K){var I=J[1].toUpperCase();K.tagName=I;if(I!=="*"&&(!K.last||K.prefilter)){return[D,"=",I];}if(!K.prefilter){K.prefilter="tagName";}}},{name:A,re:/^\s*([>+~]|\s)\s*/,fn:function(I,J){}},{name:F,re:/^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,fn:function(I,J){var K=C[F][I[1]];if(K){return[I[2],K];}else{return false;}}}],_getToken:function(I){return{tagName:null,id:null,className:null,attributes:{},combinator:null,tests:[]};},_tokenize:function(K){K=K||"";K=C._replaceShorthand(G.Lang.trim(K));var J=C._getToken(),P=K,O=[],Q=false,M,N,L,I;outer:do{Q=false;for(L=0;(I=C._parsers[L++]);){if((M=I.re.exec(K))){if(I!==A){J.selector=K;}K=K.replace(M[0],"");if(!K.length){J.last=true;}if(C._attrFilters[M[1]]){M[1]=C._attrFilters[M[1]];}N=I.fn(M,J);if(N===false){Q=false;break outer;}else{if(N){J.tests.push(N);}}if(!K.length||I.name===A){O.push(J);J=C._getToken(J);if(I.name===A){J.combinator=G.Selector.combinators[M[1]];}}Q=true;}}}while(Q&&K.length);if(!Q||K.length){O=[];}return O;},_replaceShorthand:function(J){var K=C.shorthand,L=J.match(C._re.attr),O=J.match(C._re.pseudos),N,M,I;if(O){J=J.replace(C._re.pseudos,"!!REPLACED_PSEUDO!!");}if(L){J=J.replace(C._re.attr,"!!REPLACED_ATTRIBUTE!!");}for(N in K){if(K.hasOwnProperty(N)){J=J.replace(C._getRegExp(N,"gi"),K[N]);}}if(L){for(M=0,I=L.length;M<I;++M){J=J.replace("!!REPLACED_ATTRIBUTE!!",L[M]);}}if(O){for(M=0,I=O.length;M<I;++M){J=J.replace("!!REPLACED_PSEUDO!!",O[M]);}}return J;},_attrFilters:{"class":"className","for":"htmlFor"},getters:{href:function(J,I){return G.DOM.getAttribute(J,I);
+}}};G.mix(G.Selector,B,true);G.Selector.getters.src=G.Selector.getters.rel=G.Selector.getters.href;if(G.Selector.useNative&&document.querySelector){G.Selector.shorthand["\\.(-?[_a-z]+[-\\w]*)"]="[class~=$1]";}},"3.0.0",{requires:["selector-native"]});YUI.add("selector",function(A){},"3.0.0",{use:["selector-native","selector-css2"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-native', function(Y) {
+
+(function(Y) {
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
+
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
+
+Y.namespace('Selector'); // allow native module to standalone
+
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TMP_PREFIX = 'yui-tmp-',
+ g_counter = 0;
+
+var Selector = {
+ _foundCache: [],
+
+ useNative: true,
+
+ _compare: ('sourceIndex' in document.documentElement) ?
+ function(nodeA, nodeB) {
+ var a = nodeA.sourceIndex,
+ b = nodeB.sourceIndex;
+
+ if (a === b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ }
+
+ return -1;
+
+ } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
+ function(nodeA, nodeB) {
+ if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } :
+ function(nodeA, nodeB) {
+ var rangeA, rangeB, compare;
+ if (nodeA && nodeB) {
+ rangeA = nodeA[OWNER_DOCUMENT].createRange();
+ rangeA.setStart(nodeA, 0);
+ rangeB = nodeB[OWNER_DOCUMENT].createRange();
+ rangeB.setStart(nodeB, 0);
+ compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
+ }
+
+ return compare;
+
+ }),
+
+ _sort: function(nodes) {
+ if (nodes) {
+ nodes = Y.Array(nodes, 0, true);
+ if (nodes.sort) {
+ nodes.sort(Selector._compare);
+ }
+ }
+
+ return nodes;
+ },
+
+ _deDupe: function(nodes) {
+ var ret = [],
+ i, node;
+
+ for (i = 0; (node = nodes[i++]);) {
+ if (!node._found) {
+ ret[ret.length] = node;
+ node._found = true;
+ }
+ }
+
+ for (i = 0; (node = ret[i++]);) {
+ node._found = null;
+ node.removeAttribute('_found');
+ }
+
+ return ret;
+ },
+
+ /**
+ * Retrieves a set of nodes based on a given CSS selector.
+ * @method query
+ *
+ * @param {string} selector The CSS Selector to test the node against.
+ * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
+ * @param {Boolean} firstOnly optional Whether or not to return only the first match.
+ * @return {Array} An array of nodes that match the given selector.
+ * @static
+ */
+ query: function(selector, root, firstOnly, skipNative) {
+ root = root || Y.config.doc;
+ var ret = [],
+ useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
+ queries = [[selector, root]],
+ query,
+ result,
+ i,
+ fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
+
+ if (selector && fn) {
+ // split group into seperate queries
+ if (!skipNative && // already done if skipping
+ (!useNative || root.tagName)) { // split native when element scoping is needed
+ queries = Selector._splitQueries(selector, root);
+ }
+
+ for (i = 0; (query = queries[i++]);) {
+ result = fn(query[0], query[1], firstOnly);
+ if (!firstOnly) { // coerce DOM Collection to Array
+ result = Y.Array(result, 0, true);
+ }
+ if (result) {
+ ret = ret.concat(result);
+ }
+ }
+
+ if (queries.length > 1) { // remove dupes and sort by doc order
+ ret = Selector._sort(Selector._deDupe(ret));
+ }
+ }
+
+ Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
+ return (firstOnly) ? (ret[0] || null) : ret;
+
+ },
+
+ // allows element scoped queries to begin with combinator
+ // e.g. query('> p', document.body) === query('body > p')
+ _splitQueries: function(selector, node) {
+ var groups = selector.split(','),
+ queries = [],
+ prefix = '',
+ i, len;
+
+ if (node) {
+ // enforce for element scoping
+ if (node.tagName) {
+ node.id = node.id || Y.guid();
+ prefix = '#' + node.id + ' ';
+ }
+
+ for (i = 0, len = groups.length; i < len; ++i) {
+ selector = prefix + groups[i];
+ queries.push([selector, node]);
+ }
+ }
+
+ return queries;
+ },
+
+ _nativeQuery: function(selector, root, one) {
+ try {
+ //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
+ return root['querySelector' + (one ? '' : 'All')](selector);
+ } catch(e) { // fallback to brute if available
+ //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
+ return Y.Selector.query(selector, root, one, true); // redo with skipNative true
+ }
+ },
+
+ filter: function(nodes, selector) {
+ var ret = [],
+ i, node;
+
+ if (nodes && selector) {
+ for (i = 0; (node = nodes[i++]);) {
+ if (Y.Selector.test(node, selector)) {
+ ret[ret.length] = node;
+ }
+ }
+ } else {
+ Y.log('invalid filter input (nodes: ' + nodes +
+ ', selector: ' + selector + ')', 'warn', 'Selector');
+ }
+
+ return ret;
+ },
+
+ test: function(node, selector, root) {
+ var ret = false,
+ groups = selector.split(','),
+ item,
+ i, group;
+
+ if (node && node.tagName) { // only test HTMLElements
+ root = root || node.ownerDocument;
+
+ if (!node.id) {
+ node.id = TMP_PREFIX + g_counter++;
+ }
+ for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
+ group += '#' + node.id; // add ID for uniqueness
+ item = Y.Selector.query(group, root, true);
+ ret = (item === node);
+ if (ret) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+};
+
+Y.mix(Y.Selector, Selector, true);
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("selector-native",function(A){(function(G){G.namespace("Selector");var E="compareDocumentPosition",F="ownerDocument",D="yui-tmp-",C=0;var B={_foundCache:[],useNative:true,_compare:("sourceIndex" in document.documentElement)?function(K,J){var I=K.sourceIndex,H=J.sourceIndex;if(I===H){return 0;}else{if(I>H){return 1;}}return -1;}:(document.documentElement[E]?function(I,H){if(I[E](H)&4){return -1;}else{return 1;}}:function(L,K){var J,H,I;if(L&&K){J=L[F].createRange();J.setStart(L,0);H=K[F].createRange();H.setStart(K,0);I=J.compareBoundaryPoints(1,H);}return I;}),_sort:function(H){if(H){H=G.Array(H,0,true);if(H.sort){H.sort(B._compare);}}return H;},_deDupe:function(H){var I=[],J,K;for(J=0;(K=H[J++]);){if(!K._found){I[I.length]=K;K._found=true;}}for(J=0;(K=I[J++]);){K._found=null;K.removeAttribute("_found");}return I;},query:function(I,P,Q,H){P=P||G.config.doc;var M=[],J=(G.Selector.useNative&&document.querySelector&&!H),L=[[I,P]],N,R,K,O=(J)?G.Selector._nativeQuery:G.Selector._bruteQuery;if(I&&O){if(!H&&(!J||P.tagName)){L=B._splitQueries(I,P);}for(K=0;(N=L[K++]);){R=O(N[0],N[1],Q);if(!Q){R=G.Array(R,0,true);}if(R){M=M.concat(R);}}if(L.length>1){M=B._sort(B._deDupe(M));}}return(Q)?(M[0]||null):M;},_splitQueries:function(J,M){var I=J.split(","),K=[],N="",L,H;if(M){if(M.tagName){M.id=M.id||G.guid();N="#"+M.id+" ";}for(L=0,H=I.length;L<H;++L){J=N+I[L];K.push([J,M]);}}return K;},_nativeQuery:function(H,I,J){try{return I["querySelector"+(J?"":"All")](H);}catch(K){return G.Selector.query(H,I,J,true);}},filter:function(I,H){var J=[],K,L;if(I&&H){for(K=0;(L=I[K++]);){if(G.Selector.test(L,H)){J[J.length]=L;}}}else{}return J;},test:function(N,I,J){var K=false,H=I.split(","),M,L,O;if(N&&N.tagName){J=J||N.ownerDocument;if(!N.id){N.id=D+C++;}for(L=0;(O=H[L++]);){O+="#"+N.id;M=G.Selector.query(O,J,true);K=(M===N);if(K){break;}}}return K;}};G.mix(G.Selector,B,true);})(A);},"3.0.0",{requires:["dom-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-native', function(Y) {
+
+(function(Y) {
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
+
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
+
+Y.namespace('Selector'); // allow native module to standalone
+
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TMP_PREFIX = 'yui-tmp-',
+ g_counter = 0;
+
+var Selector = {
+ _foundCache: [],
+
+ useNative: true,
+
+ _compare: ('sourceIndex' in document.documentElement) ?
+ function(nodeA, nodeB) {
+ var a = nodeA.sourceIndex,
+ b = nodeB.sourceIndex;
+
+ if (a === b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ }
+
+ return -1;
+
+ } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
+ function(nodeA, nodeB) {
+ if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } :
+ function(nodeA, nodeB) {
+ var rangeA, rangeB, compare;
+ if (nodeA && nodeB) {
+ rangeA = nodeA[OWNER_DOCUMENT].createRange();
+ rangeA.setStart(nodeA, 0);
+ rangeB = nodeB[OWNER_DOCUMENT].createRange();
+ rangeB.setStart(nodeB, 0);
+ compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
+ }
+
+ return compare;
+
+ }),
+
+ _sort: function(nodes) {
+ if (nodes) {
+ nodes = Y.Array(nodes, 0, true);
+ if (nodes.sort) {
+ nodes.sort(Selector._compare);
+ }
+ }
+
+ return nodes;
+ },
+
+ _deDupe: function(nodes) {
+ var ret = [],
+ i, node;
+
+ for (i = 0; (node = nodes[i++]);) {
+ if (!node._found) {
+ ret[ret.length] = node;
+ node._found = true;
+ }
+ }
+
+ for (i = 0; (node = ret[i++]);) {
+ node._found = null;
+ node.removeAttribute('_found');
+ }
+
+ return ret;
+ },
+
+ /**
+ * Retrieves a set of nodes based on a given CSS selector.
+ * @method query
+ *
+ * @param {string} selector The CSS Selector to test the node against.
+ * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
+ * @param {Boolean} firstOnly optional Whether or not to return only the first match.
+ * @return {Array} An array of nodes that match the given selector.
+ * @static
+ */
+ query: function(selector, root, firstOnly, skipNative) {
+ root = root || Y.config.doc;
+ var ret = [],
+ useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
+ queries = [[selector, root]],
+ query,
+ result,
+ i,
+ fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
+
+ if (selector && fn) {
+ // split group into seperate queries
+ if (!skipNative && // already done if skipping
+ (!useNative || root.tagName)) { // split native when element scoping is needed
+ queries = Selector._splitQueries(selector, root);
+ }
+
+ for (i = 0; (query = queries[i++]);) {
+ result = fn(query[0], query[1], firstOnly);
+ if (!firstOnly) { // coerce DOM Collection to Array
+ result = Y.Array(result, 0, true);
+ }
+ if (result) {
+ ret = ret.concat(result);
+ }
+ }
+
+ if (queries.length > 1) { // remove dupes and sort by doc order
+ ret = Selector._sort(Selector._deDupe(ret));
+ }
+ }
+
+ return (firstOnly) ? (ret[0] || null) : ret;
+
+ },
+
+ // allows element scoped queries to begin with combinator
+ // e.g. query('> p', document.body) === query('body > p')
+ _splitQueries: function(selector, node) {
+ var groups = selector.split(','),
+ queries = [],
+ prefix = '',
+ i, len;
+
+ if (node) {
+ // enforce for element scoping
+ if (node.tagName) {
+ node.id = node.id || Y.guid();
+ prefix = '#' + node.id + ' ';
+ }
+
+ for (i = 0, len = groups.length; i < len; ++i) {
+ selector = prefix + groups[i];
+ queries.push([selector, node]);
+ }
+ }
+
+ return queries;
+ },
+
+ _nativeQuery: function(selector, root, one) {
+ try {
+ return root['querySelector' + (one ? '' : 'All')](selector);
+ } catch(e) { // fallback to brute if available
+ return Y.Selector.query(selector, root, one, true); // redo with skipNative true
+ }
+ },
+
+ filter: function(nodes, selector) {
+ var ret = [],
+ i, node;
+
+ if (nodes && selector) {
+ for (i = 0; (node = nodes[i++]);) {
+ if (Y.Selector.test(node, selector)) {
+ ret[ret.length] = node;
+ }
+ }
+ } else {
+ }
+
+ return ret;
+ },
+
+ test: function(node, selector, root) {
+ var ret = false,
+ groups = selector.split(','),
+ item,
+ i, group;
+
+ if (node && node.tagName) { // only test HTMLElements
+ root = root || node.ownerDocument;
+
+ if (!node.id) {
+ node.id = TMP_PREFIX + g_counter++;
+ }
+ for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
+ group += '#' + node.id; // add ID for uniqueness
+ item = Y.Selector.query(group, root, true);
+ ret = (item === node);
+ if (ret) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+};
+
+Y.mix(Y.Selector, Selector, true);
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('selector-native', function(Y) {
+
+(function(Y) {
+/**
+ * The selector-native module provides support for native querySelector
+ * @module dom
+ * @submodule selector-native
+ * @for Selector
+ */
+
+/**
+ * Provides support for using CSS selectors to query the DOM
+ * @class Selector
+ * @static
+ * @for Selector
+ */
+
+Y.namespace('Selector'); // allow native module to standalone
+
+var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TMP_PREFIX = 'yui-tmp-',
+ g_counter = 0;
+
+var Selector = {
+ _foundCache: [],
+
+ useNative: true,
+
+ _compare: ('sourceIndex' in document.documentElement) ?
+ function(nodeA, nodeB) {
+ var a = nodeA.sourceIndex,
+ b = nodeB.sourceIndex;
+
+ if (a === b) {
+ return 0;
+ } else if (a > b) {
+ return 1;
+ }
+
+ return -1;
+
+ } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
+ function(nodeA, nodeB) {
+ if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } :
+ function(nodeA, nodeB) {
+ var rangeA, rangeB, compare;
+ if (nodeA && nodeB) {
+ rangeA = nodeA[OWNER_DOCUMENT].createRange();
+ rangeA.setStart(nodeA, 0);
+ rangeB = nodeB[OWNER_DOCUMENT].createRange();
+ rangeB.setStart(nodeB, 0);
+ compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
+ }
+
+ return compare;
+
+ }),
+
+ _sort: function(nodes) {
+ if (nodes) {
+ nodes = Y.Array(nodes, 0, true);
+ if (nodes.sort) {
+ nodes.sort(Selector._compare);
+ }
+ }
+
+ return nodes;
+ },
+
+ _deDupe: function(nodes) {
+ var ret = [],
+ i, node;
+
+ for (i = 0; (node = nodes[i++]);) {
+ if (!node._found) {
+ ret[ret.length] = node;
+ node._found = true;
+ }
+ }
+
+ for (i = 0; (node = ret[i++]);) {
+ node._found = null;
+ node.removeAttribute('_found');
+ }
+
+ return ret;
+ },
+
+ /**
+ * Retrieves a set of nodes based on a given CSS selector.
+ * @method query
+ *
+ * @param {string} selector The CSS Selector to test the node against.
+ * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
+ * @param {Boolean} firstOnly optional Whether or not to return only the first match.
+ * @return {Array} An array of nodes that match the given selector.
+ * @static
+ */
+ query: function(selector, root, firstOnly, skipNative) {
+ root = root || Y.config.doc;
+ var ret = [],
+ useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
+ queries = [[selector, root]],
+ query,
+ result,
+ i,
+ fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
+
+ if (selector && fn) {
+ // split group into seperate queries
+ if (!skipNative && // already done if skipping
+ (!useNative || root.tagName)) { // split native when element scoping is needed
+ queries = Selector._splitQueries(selector, root);
+ }
+
+ for (i = 0; (query = queries[i++]);) {
+ result = fn(query[0], query[1], firstOnly);
+ if (!firstOnly) { // coerce DOM Collection to Array
+ result = Y.Array(result, 0, true);
+ }
+ if (result) {
+ ret = ret.concat(result);
+ }
+ }
+
+ if (queries.length > 1) { // remove dupes and sort by doc order
+ ret = Selector._sort(Selector._deDupe(ret));
+ }
+ }
+
+ return (firstOnly) ? (ret[0] || null) : ret;
+
+ },
+
+ // allows element scoped queries to begin with combinator
+ // e.g. query('> p', document.body) === query('body > p')
+ _splitQueries: function(selector, node) {
+ var groups = selector.split(','),
+ queries = [],
+ prefix = '',
+ i, len;
+
+ if (node) {
+ // enforce for element scoping
+ if (node.tagName) {
+ node.id = node.id || Y.guid();
+ prefix = '#' + node.id + ' ';
+ }
+
+ for (i = 0, len = groups.length; i < len; ++i) {
+ selector = prefix + groups[i];
+ queries.push([selector, node]);
+ }
+ }
+
+ return queries;
+ },
+
+ _nativeQuery: function(selector, root, one) {
+ try {
+ return root['querySelector' + (one ? '' : 'All')](selector);
+ } catch(e) { // fallback to brute if available
+ return Y.Selector.query(selector, root, one, true); // redo with skipNative true
+ }
+ },
+
+ filter: function(nodes, selector) {
+ var ret = [],
+ i, node;
+
+ if (nodes && selector) {
+ for (i = 0; (node = nodes[i++]);) {
+ if (Y.Selector.test(node, selector)) {
+ ret[ret.length] = node;
+ }
+ }
+ } else {
+ }
+
+ return ret;
+ },
+
+ test: function(node, selector, root) {
+ var ret = false,
+ groups = selector.split(','),
+ item,
+ i, group;
+
+ if (node && node.tagName) { // only test HTMLElements
+ root = root || node.ownerDocument;
+
+ if (!node.id) {
+ node.id = TMP_PREFIX + g_counter++;
+ }
+ for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
+ group += '#' + node.id; // add ID for uniqueness
+ item = Y.Selector.query(group, root, true);
+ ret = (item === node);
+ if (ret) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+};
+
+Y.mix(Y.Selector, Selector, true);
+
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-base']});
+YUI.add('selector-css2', function(Y) {
+
+/**
+ * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
+ * @module dom
+ * @submodule selector-css2
+ * @for Selector
+ */
+
+/**
+ * Provides helper methods for collecting and filtering DOM elements.
+ */
+
+var PARENT_NODE = 'parentNode',
+ TAG_NAME = 'tagName',
+ ATTRIBUTES = 'attributes',
+ COMBINATOR = 'combinator',
+ PSEUDOS = 'pseudos',
+
+ Selector = Y.Selector,
+
+ SelectorCSS2 = {
+ SORT_RESULTS: true,
+ _children: function(node, tag) {
+ var ret = node.children,
+ i,
+ children = [],
+ childNodes,
+ child;
+
+ if (node.children && tag && node.children.tags) {
+ children = node.children.tags(tag);
+ } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
+ childNodes = ret || node.childNodes;
+ ret = [];
+ for (i = 0; (child = childNodes[i++]);) {
+ if (child.tagName) {
+ if (!tag || tag === child.tagName) {
+ ret.push(child);
+ }
+ }
+ }
+ }
+
+ return ret || [];
+ },
+
+ _regexCache: {},
+
+ _re: {
+ attr: /(\[.*\])/g,
+ pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
+ },
+
+ /**
+ * Mapping of shorthand tokens to corresponding attribute selector
+ * @property shorthand
+ * @type object
+ */
+ shorthand: {
+ '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
+ '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
+ },
+
+ /**
+ * List of operators and corresponding boolean functions.
+ * These functions are passed the attribute and the current node's value of the attribute.
+ * @property operators
+ * @type object
+ */
+ operators: {
+ '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
+ //'': '.+',
+ //'=': '^{val}$', // equality
+ '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
+ '|=': '^{val}-?' // optional hyphen-delimited
+ },
+
+ pseudos: {
+ 'first-child': function(node) {
+ return Y.Selector._children(node[PARENT_NODE])[0] === node;
+ }
+ },
+
+ _bruteQuery: function(selector, root, firstOnly) {
+ var ret = [],
+ nodes = [],
+ tokens = Selector._tokenize(selector),
+ token = tokens[tokens.length - 1],
+ rootDoc = Y.DOM._getDoc(root),
+ id,
+ className,
+ tagName;
+
+
+ // if we have an initial ID, set to root when in document
+ if (tokens[0] && rootDoc === root &&
+ (id = tokens[0].id) &&
+ rootDoc.getElementById(id)) {
+ root = rootDoc.getElementById(id);
+ }
+
+ if (token) {
+ // prefilter nodes
+ id = token.id;
+ className = token.className;
+ tagName = token.tagName || '*';
+
+ // try ID first
+ if (id) {
+ if (rootDoc.getElementById(id)) { // if in document
+ nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
+ }
+ // try className if supported
+ } else if (className) {
+ nodes = root.getElementsByClassName(className);
+ } else if (tagName) { // default to tagName
+ nodes = root.getElementsByTagName(tagName || '*');
+ }
+
+ if (nodes.length) {
+ ret = Selector._filterNodes(nodes, tokens, firstOnly);
+ }
+ }
+
+ return ret;
+ },
+
+ _filterNodes: function(nodes, tokens, firstOnly) {
+ var i = 0,
+ j,
+ len = tokens.length,
+ n = len - 1,
+ result = [],
+ node = nodes[0],
+ tmpNode = node,
+ getters = Y.Selector.getters,
+ operator,
+ combinator,
+ token,
+ path,
+ pass,
+ //FUNCTION = 'function',
+ value,
+ tests,
+ test;
+
+ //do {
+ for (i = 0; (tmpNode = node = nodes[i++]);) {
+ n = len - 1;
+ path = null;
+
+ testLoop:
+ while (tmpNode && tmpNode.tagName) {
+ token = tokens[n];
+ tests = token.tests;
+ j = tests.length;
+ if (j && !pass) {
+ while ((test = tests[--j])) {
+ operator = test[1];
+ if (getters[test[0]]) {
+ value = getters[test[0]](tmpNode, test[0]);
+ } else {
+ value = tmpNode[test[0]];
+ // use getAttribute for non-standard attributes
+ if (value === undefined && tmpNode.getAttribute) {
+ value = tmpNode.getAttribute(test[0]);
+ }
+ }
+
+ if ((operator === '=' && value !== test[2]) || // fast path for equality
+ (operator.test && !operator.test(value)) || // regex test
+ (operator.call && !operator(tmpNode, test[0]))) { // function test
+
+ // skip non element nodes or non-matching tags
+ if ((tmpNode = tmpNode[path])) {
+ while (tmpNode &&
+ (!tmpNode.tagName ||
+ (token.tagName && token.tagName !== tmpNode.tagName))
+ ) {
+ tmpNode = tmpNode[path];
+ }
+ }
+ continue testLoop;
+ }
+ }
+ }
+
+ n--; // move to next token
+ // now that we've passed the test, move up the tree by combinator
+ if (!pass && (combinator = token.combinator)) {
+ path = combinator.axis;
+ tmpNode = tmpNode[path];
+
+ // skip non element nodes
+ while (tmpNode && !tmpNode.tagName) {
+ tmpNode = tmpNode[path];
+ }
+
+ if (combinator.direct) { // one pass only
+ path = null;
+ }
+
+ } else { // success if we made it this far
+ result.push(node);
+ if (firstOnly) {
+ return result;
+ }
+ break;
+ }
+ }
+ }// while (tmpNode = node = nodes[++i]);
+ node = tmpNode = null;
+ return result;
+ },
+
+ _getRegExp: function(str, flags) {
+ var regexCache = Selector._regexCache;
+ flags = flags || '';
+ if (!regexCache[str + flags]) {
+ regexCache[str + flags] = new RegExp(str, flags);
+ }
+ return regexCache[str + flags];
+ },
+
+ combinators: {
+ ' ': {
+ axis: 'parentNode'
+ },
+
+ '>': {
+ axis: 'parentNode',
+ direct: true
+ },
+
+
+ '+': {
+ axis: 'previousSibling',
+ direct: true
+ }
+ },
+
+ _parsers: [
+ {
+ name: ATTRIBUTES,
+ re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
+ fn: function(match, token) {
+ var operator = match[2] || '',
+ operators = Y.Selector.operators,
+ test;
+
+ // add prefiltering for ID and CLASS
+ if ((match[1] === 'id' && operator === '=') ||
+ (match[1] === 'className' &&
+ document.getElementsByClassName &&
+ (operator === '~=' || operator === '='))) {
+ token.prefilter = match[1];
+ token[match[1]] = match[3];
+ }
+
+ // add tests
+ if (operator in operators) {
+ test = operators[operator];
+ if (typeof test === 'string') {
+ test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
+ }
+ match[2] = test;
+ }
+ if (!token.last || token.prefilter !== match[1]) {
+ return match.slice(1);
+ }
+ }
+
+ },
+ {
+ name: TAG_NAME,
+ re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
+ fn: function(match, token) {
+ var tag = match[1].toUpperCase();
+ token.tagName = tag;
+
+ if (tag !== '*' && (!token.last || token.prefilter)) {
+ return [TAG_NAME, '=', tag];
+ }
+ if (!token.prefilter) {
+ token.prefilter = 'tagName';
+ }
+ }
+ },
+ {
+ name: COMBINATOR,
+ re: /^\s*([>+~]|\s)\s*/,
+ fn: function(match, token) {
+ }
+ },
+ {
+ name: PSEUDOS,
+ re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
+ fn: function(match, token) {
+ var test = Selector[PSEUDOS][match[1]];
+ if (test) { // reorder match array
+ return [match[2], test];
+ } else { // selector token not supported (possibly missing CSS3 module)
+ return false;
+ }
+ }
+ }
+ ],
+
+ _getToken: function(token) {
+ return {
+ tagName: null,
+ id: null,
+ className: null,
+ attributes: {},
+ combinator: null,
+ tests: []
+ };
+ },
+
+ /**
+ Break selector into token units per simple selector.
+ Combinator is attached to the previous token.
+ */
+ _tokenize: function(selector) {
+ selector = selector || '';
+ selector = Selector._replaceShorthand(Y.Lang.trim(selector));
+ var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
+ query = selector, // original query for debug report
+ tokens = [], // array of tokens
+ found = false, // whether or not any matches were found this pass
+ match, // the regex match
+ test,
+ i, parser;
+
+ /*
+ Search for selector patterns, store, and strip them from the selector string
+ until no patterns match (invalid selector) or we run out of chars.
+
+ Multiple attributes and pseudos are allowed, in any order.
+ for example:
+ 'form:first-child[type=button]:not(button)[lang|=en]'
+ */
+ outer:
+ do {
+ found = false; // reset after full pass
+ for (i = 0; (parser = Selector._parsers[i++]);) {
+ if ( (match = parser.re.exec(selector)) ) { // note assignment
+ if (parser !== COMBINATOR ) {
+ token.selector = selector;
+ }
+ selector = selector.replace(match[0], ''); // strip current match from selector
+ if (!selector.length) {
+ token.last = true;
+ }
+
+ if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
+ match[1] = Selector._attrFilters[match[1]];
+ }
+
+ test = parser.fn(match, token);
+ if (test === false) { // selector not supported
+ found = false;
+ break outer;
+ } else if (test) {
+ token.tests.push(test);
+ }
+
+ if (!selector.length || parser.name === COMBINATOR) {
+ tokens.push(token);
+ token = Selector._getToken(token);
+ if (parser.name === COMBINATOR) {
+ token.combinator = Y.Selector.combinators[match[1]];
+ }
+ }
+ found = true;
+ }
+ }
+ } while (found && selector.length);
+
+ if (!found || selector.length) { // not fully parsed
+ tokens = [];
+ }
+ return tokens;
+ },
+
+ _replaceShorthand: function(selector) {
+ var shorthand = Selector.shorthand,
+ attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
+ pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
+ re, i, len;
+
+ if (pseudos) {
+ selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
+ }
+
+ if (attrs) {
+ selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
+ }
+
+ for (re in shorthand) {
+ if (shorthand.hasOwnProperty(re)) {
+ selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
+ }
+ }
+
+ if (attrs) {
+ for (i = 0, len = attrs.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
+ }
+ }
+ if (pseudos) {
+ for (i = 0, len = pseudos.length; i < len; ++i) {
+ selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
+ }
+ }
+ return selector;
+ },
+
+ _attrFilters: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+
+ getters: {
+ href: function(node, attr) {
+ return Y.DOM.getAttribute(node, attr);
+ }
+ }
+ };
+
+Y.mix(Y.Selector, SelectorCSS2, true);
+Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
+
+// IE wants class with native queries
+if (Y.Selector.useNative && document.querySelector) {
+ Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
+}
+
+
+
+}, '3.0.0' ,{requires:['selector-native']});
+
+
+YUI.add('selector', function(Y){}, '3.0.0' ,{use:['selector-native', 'selector-css2']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dump', function(Y) {
+
+/**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * If included, the dump method is added to the YUI instance.
+ *
+ * @module dump
+ */
+
+ var L=Y.Lang, OBJ='{...}', FUN='f(){...}', COMMA=', ', ARROW=' => ',
+
+ /**
+ * The following methods are added to the YUI instance
+ * @class YUI~dump
+ */
+
+ /**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * @TODO dumping a window is causing an unhandled exception in
+ * FireFox.
+ *
+ * This method is in the 'dump' module, which is not bundled with
+ * the core YUI object
+ *
+ * @method dump
+ * @param o {object} The object to dump
+ * @param d {int} How deep to recurse child objects, default 3
+ * @return {string} the dump result
+ */
+ dump = function(o, d) {
+ var i, len, s = [], type = L.type(o);
+
+ // Cast non-objects to string
+ // Skip dates because the std toString is what we want
+ // Skip HTMLElement-like objects because trying to dump
+ // an element will cause an unhandled exception in FF 2.x
+ if (!L.isObject(o)) {
+ return o + "";
+ } else if (type == "date") {
+ return o;
+ } else if (o.nodeType && o.tagName) {
+ return o.tagName + '#' + o.id;
+ } else if (o.document && o.navigator) {
+ return 'window';
+ } else if (o.location && o.body) {
+ return 'document';
+ } else if (type == "function") {
+ return FUN;
+ }
+
+ // dig into child objects the depth specifed. Default 3
+ d = (L.isNumber(d)) ? d : 3;
+
+ // arrays [1, 2, 3]
+ if (type == "array") {
+ s.push("[");
+ for (i=0,len=o.length;i<len;i=i+1) {
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d-1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push("]");
+ // regexp /foo/
+ } else if (type == "regexp") {
+ s.push(o.toString());
+ // objects {k1 => v1, k2 => v2}
+ } else {
+ s.push("{");
+ for (i in o) {
+ if (o.hasOwnProperty(i)) {
+ try {
+ s.push(i + ARROW);
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d-1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ } catch(e) {
+ s.push('Error: ' + e.message);
+ }
+ }
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push("}");
+ }
+
+ return s.join("");
+ };
+
+ Y.dump = dump;
+ L.dump = dump;
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("dump",function(G){var B=G.Lang,C="{...}",F="f(){...}",A=", ",D=" => ",E=function(N,M){var I,H,K=[],J=B.type(N);if(!B.isObject(N)){return N+"";}else{if(J=="date"){return N;}else{if(N.nodeType&&N.tagName){return N.tagName+"#"+N.id;}else{if(N.document&&N.navigator){return"window";}else{if(N.location&&N.body){return"document";}else{if(J=="function"){return F;}}}}}}M=(B.isNumber(M))?M:3;if(J=="array"){K.push("[");for(I=0,H=N.length;I<H;I=I+1){if(B.isObject(N[I])){K.push((M>0)?B.dump(N[I],M-1):C);}else{K.push(N[I]);}K.push(A);}if(K.length>1){K.pop();}K.push("]");}else{if(J=="regexp"){K.push(N.toString());}else{K.push("{");for(I in N){if(N.hasOwnProperty(I)){try{K.push(I+D);if(B.isObject(N[I])){K.push((M>0)?B.dump(N[I],M-1):C);}else{K.push(N[I]);}K.push(A);}catch(L){K.push("Error: "+L.message);}}}if(K.length>1){K.pop();}K.push("}");}}return K.join("");};G.dump=E;B.dump=E;},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('dump', function(Y) {
+
+/**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * If included, the dump method is added to the YUI instance.
+ *
+ * @module dump
+ */
+
+ var L=Y.Lang, OBJ='{...}', FUN='f(){...}', COMMA=', ', ARROW=' => ',
+
+ /**
+ * The following methods are added to the YUI instance
+ * @class YUI~dump
+ */
+
+ /**
+ * Returns a simple string representation of the object or array.
+ * Other types of objects will be returned unprocessed. Arrays
+ * are expected to be indexed. Use object notation for
+ * associative arrays.
+ *
+ * @TODO dumping a window is causing an unhandled exception in
+ * FireFox.
+ *
+ * This method is in the 'dump' module, which is not bundled with
+ * the core YUI object
+ *
+ * @method dump
+ * @param o {object} The object to dump
+ * @param d {int} How deep to recurse child objects, default 3
+ * @return {string} the dump result
+ */
+ dump = function(o, d) {
+ var i, len, s = [], type = L.type(o);
+
+ // Cast non-objects to string
+ // Skip dates because the std toString is what we want
+ // Skip HTMLElement-like objects because trying to dump
+ // an element will cause an unhandled exception in FF 2.x
+ if (!L.isObject(o)) {
+ return o + "";
+ } else if (type == "date") {
+ return o;
+ } else if (o.nodeType && o.tagName) {
+ return o.tagName + '#' + o.id;
+ } else if (o.document && o.navigator) {
+ return 'window';
+ } else if (o.location && o.body) {
+ return 'document';
+ } else if (type == "function") {
+ return FUN;
+ }
+
+ // dig into child objects the depth specifed. Default 3
+ d = (L.isNumber(d)) ? d : 3;
+
+ // arrays [1, 2, 3]
+ if (type == "array") {
+ s.push("[");
+ for (i=0,len=o.length;i<len;i=i+1) {
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d-1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push("]");
+ // regexp /foo/
+ } else if (type == "regexp") {
+ s.push(o.toString());
+ // objects {k1 => v1, k2 => v2}
+ } else {
+ s.push("{");
+ for (i in o) {
+ if (o.hasOwnProperty(i)) {
+ try {
+ s.push(i + ARROW);
+ if (L.isObject(o[i])) {
+ s.push((d > 0) ? L.dump(o[i], d-1) : OBJ);
+ } else {
+ s.push(o[i]);
+ }
+ s.push(COMMA);
+ } catch(e) {
+ s.push('Error: ' + e.message);
+ }
+ }
+ }
+ if (s.length > 1) {
+ s.pop();
+ }
+ s.push("}");
+ }
+
+ return s.join("");
+ };
+
+ Y.dump = dump;
+ L.dump = dump;
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-custom-base', function(Y) {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ */
+
+Y.Env.evt = {
+ handles: {},
+ plugins: {}
+};
+
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * Allows for the insertion of methods that are executed before or after
+ * a specified method
+ * @class Do
+ * @static
+ */
+
+var BEFORE = 0,
+ AFTER = 1;
+
+Y.Do = {
+
+ /**
+ * Cache of objects touched by the utility
+ * @property objs
+ * @static
+ */
+ objs: {},
+
+ /**
+ * Execute the supplied method before the specified function
+ * @method before
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ before: function(fn, obj, sFn, c) {
+ // Y.log('Do before: ' + sFn, 'info', 'event');
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(BEFORE, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ after: function(fn, obj, sFn, c) {
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(AFTER, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method _inject
+ * @param when {string} before or after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @private
+ * @static
+ */
+ _inject: function(when, fn, obj, sFn) {
+
+ // object id
+ var id = Y.stamp(obj), o, sid;
+
+ if (! this.objs[id]) {
+ // create a map entry for the obj if it doesn't exist
+ this.objs[id] = {};
+ }
+
+ o = this.objs[id];
+
+ if (! o[sFn]) {
+ // create a map entry for the method if it doesn't exist
+ o[sFn] = new Y.Do.Method(obj, sFn);
+
+ // re-route the method to our wrapper
+ obj[sFn] =
+ function() {
+ return o[sFn].exec.apply(o[sFn], arguments);
+ };
+ }
+
+ // subscriber id
+ sid = id + Y.stamp(fn) + sFn;
+
+ // register the callback
+ o[sFn].register(sid, fn, when);
+
+ return new Y.EventHandle(o[sFn], sid);
+
+ },
+
+ /**
+ * Detach a before or after subscription
+ * @method detach
+ * @param handle {string} the subscription handle
+ */
+ detach: function(handle) {
+
+ if (handle.detach) {
+ handle.detach();
+ }
+
+ },
+
+ _unload: function(e, me) {
+
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * Wrapper for a displaced method with aop enabled
+ * @class Do.Method
+ * @constructor
+ * @param obj The object to operate on
+ * @param sFn The name of the method to displace
+ */
+Y.Do.Method = function(obj, sFn) {
+ this.obj = obj;
+ this.methodName = sFn;
+ this.method = obj[sFn];
+ this.before = {};
+ this.after = {};
+};
+
+/**
+ * Register a aop subscriber
+ * @method register
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype.register = function (sid, fn, when) {
+ if (when) {
+ this.after[sid] = fn;
+ } else {
+ this.before[sid] = fn;
+ }
+};
+
+/**
+ * Unregister a aop subscriber
+ * @method delete
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype._delete = function (sid) {
+ // Y.log('Y.Do._delete: ' + sid, 'info', 'Event');
+ delete this.before[sid];
+ delete this.after[sid];
+};
+
+/**
+ * Execute the wrapped method
+ * @method exec
+ */
+Y.Do.Method.prototype.exec = function () {
+
+ var args = Y.Array(arguments, 0, true),
+ i, ret, newRet,
+ bf = this.before,
+ af = this.after,
+ prevented = false;
+
+ // execute before
+ for (i in bf) {
+ if (bf.hasOwnProperty(i)) {
+ ret = bf[i].apply(this.obj, args);
+ if (ret) {
+ switch (ret.constructor) {
+ case Y.Do.Halt:
+ return ret.retVal;
+ case Y.Do.AlterArgs:
+ args = ret.newArgs;
+ break;
+ case Y.Do.Prevent:
+ prevented = true;
+ break;
+ default:
+ }
+ }
+ }
+ }
+
+ // execute method
+ if (!prevented) {
+ ret = this.method.apply(this.obj, args);
+ }
+
+ // execute after methods.
+ for (i in af) {
+ if (af.hasOwnProperty(i)) {
+ newRet = af[i].apply(this.obj, args);
+ // Stop processing if a Halt object is returned
+ if (newRet && newRet.constructor == Y.Do.Halt) {
+ return newRet.retVal;
+ // Check for a new return value
+ } else if (newRet && newRet.constructor == Y.Do.AlterReturn) {
+ ret = newRet.newRetVal;
+ }
+ }
+ }
+
+ return ret;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Return an AlterArgs object when you want to change the arguments that
+ * were passed into the function. An example would be a service that scrubs
+ * out illegal characters prior to executing the core business logic.
+ * @class Do.AlterArgs
+ */
+Y.Do.AlterArgs = function(msg, newArgs) {
+ this.msg = msg;
+ this.newArgs = newArgs;
+};
+
+/**
+ * Return an AlterReturn object when you want to change the result returned
+ * from the core method to the caller
+ * @class Do.AlterReturn
+ */
+Y.Do.AlterReturn = function(msg, newRetVal) {
+ this.msg = msg;
+ this.newRetVal = newRetVal;
+};
+
+/**
+ * Return a Halt object when you want to terminate the execution
+ * of all subsequent subscribers as well as the wrapped method
+ * if it has not exectued yet.
+ * @class Do.Halt
+ */
+Y.Do.Halt = function(msg, retVal) {
+ this.msg = msg;
+ this.retVal = retVal;
+};
+
+/**
+ * Return a Prevent object when you want to prevent the wrapped function
+ * from executing, but want the remaining listeners to execute
+ * @class Do.Prevent
+ */
+Y.Do.Prevent = function(msg) {
+ this.msg = msg;
+};
+
+/**
+ * Return an Error object when you want to terminate the execution
+ * of all subsequent method calls.
+ * @class Do.Error
+ * @deprecated use Y.Do.Halt or Y.Do.Prevent
+ */
+Y.Do.Error = Y.Do.Halt;
+
+//////////////////////////////////////////////////////////////////////////
+
+// Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
+
+})();
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+
+/**
+ * Return value from all subscribe operations
+ * @class EventHandle
+ * @constructor
+ * @param evt {CustomEvent} the custom event
+ * @param sub {Subscriber} the subscriber
+ */
+
+// var onsubscribeType = "_event:onsub",
+var AFTER = 'after',
+ CONFIGS = [
+ 'broadcast',
+ 'bubbles',
+ 'context',
+ 'contextFn',
+ 'currentTarget',
+ 'defaultFn',
+ 'details',
+ 'emitFacade',
+ 'fireOnce',
+ 'host',
+ 'preventable',
+ 'preventedFn',
+ 'queuable',
+ 'silent',
+ 'stoppedFn',
+ 'target',
+ 'type'
+ ],
+
+
+ YUI3_SIGNATURE = 9,
+ YUI_LOG = 'yui:log';
+
+Y.EventHandle = function(evt, sub) {
+
+ /**
+ * The custom event
+ * @type CustomEvent
+ */
+ this.evt = evt;
+
+ /**
+ * The subscriber object
+ * @type Subscriber
+ */
+ this.sub = sub;
+};
+
+Y.EventHandle.prototype = {
+
+ /**
+ * Detaches this subscriber
+ * @method detach
+ */
+ detach: function() {
+ var evt = this.evt, i;
+ if (evt) {
+ // Y.log('EventHandle.detach: ' + this.sub, 'info', 'Event');
+ if (Y.Lang.isArray(evt)) {
+ for (i=0; i<evt.length; i++) {
+ evt[i].detach();
+ }
+ } else {
+ evt._delete(this.sub);
+ }
+ }
+ }
+};
+
+/**
+ * The CustomEvent class lets you define events for your application
+ * that can be subscribed to by one or more independent component.
+ *
+ * @param {String} type The type of event, which is passed to the callback
+ * when the event fires
+ * @param o configuration object
+ * @class CustomEvent
+ * @constructor
+ */
+Y.CustomEvent = function(type, o) {
+
+ // if (arguments.length > 2) {
+// this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
+ // }
+
+ o = o || {};
+
+ this.id = Y.stamp(this);
+
+ /**
+ * The type of event, returned to subscribers when the event fires
+ * @property type
+ * @type string
+ */
+ this.type = type;
+
+ /**
+ * The context the the event will fire from by default. Defaults to the YUI
+ * instance.
+ * @property context
+ * @type object
+ */
+ this.context = Y;
+
+ this.logSystem = (type == YUI_LOG);
+
+ /**
+ * If 0, this event does not broadcast. If 1, the YUI instance is notified
+ * every time this event fires. If 2, the YUI instance and the YUI global
+ * (if event is enabled on the global) are notified every time this event
+ * fires.
+ * @property broadcast
+ * @type int
+ */
+ // this.broadcast = 0;
+
+ /**
+ * By default all custom events are logged in the debug build, set silent
+ * to true to disable debug outpu for this event.
+ * @property silent
+ * @type boolean
+ */
+ this.silent = this.logSystem;
+
+ /**
+ * Specifies whether this event should be queued when the host is actively
+ * processing an event. This will effect exectution order of the callbacks
+ * for the various events.
+ * @property queuable
+ * @type boolean
+ * @default false
+ */
+ // this.queuable = false;
+
+ /**
+ * The subscribers to this event
+ * @property subscribers
+ * @type Subscriber{}
+ */
+ this.subscribers = {};
+
+ /**
+ * 'After' subscribers
+ * @property afters
+ * @type Subscriber{}
+ */
+ this.afters = {};
+
+ /**
+ * This event has fired if true
+ *
+ * @property fired
+ * @type boolean
+ * @default false;
+ */
+ // this.fired = false;
+
+ /**
+ * An array containing the arguments the custom event
+ * was last fired with.
+ * @property firedWith
+ * @type Array
+ */
+ // this.firedWith;
+
+ /**
+ * This event should only fire one time if true, and if
+ * it has fired, any new subscribers should be notified
+ * immediately.
+ *
+ * @property fireOnce
+ * @type boolean
+ * @default false;
+ */
+ // this.fireOnce = false;
+
+ /**
+ * Flag for stopPropagation that is modified during fire()
+ * 1 means to stop propagation to bubble targets. 2 means
+ * to also stop additional subscribers on this target.
+ * @property stopped
+ * @type int
+ */
+ // this.stopped = 0;
+
+ /**
+ * Flag for preventDefault that is modified during fire().
+ * if it is not 0, the default behavior for this event
+ * @property prevented
+ * @type int
+ */
+ // this.prevented = 0;
+
+ /**
+ * Specifies the host for this custom event. This is used
+ * to enable event bubbling
+ * @property host
+ * @type EventTarget
+ */
+ // this.host = null;
+
+ /**
+ * The default function to execute after event listeners
+ * have fire, but only if the default action was not
+ * prevented.
+ * @property defaultFn
+ * @type Function
+ */
+ // this.defaultFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * stopPropagation or stopImmediatePropagation
+ * @property stoppedFn
+ * @type Function
+ */
+ // this.stoppedFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * preventDefault
+ * @property preventedFn
+ * @type Function
+ */
+ // this.preventedFn = null;
+
+ /**
+ * Specifies whether or not this event's default function
+ * can be cancelled by a subscriber by executing preventDefault()
+ * on the event facade
+ * @property preventable
+ * @type boolean
+ * @default true
+ */
+ this.preventable = true;
+
+ /**
+ * Specifies whether or not a subscriber can stop the event propagation
+ * via stopPropagation(), stopImmediatePropagation(), or halt()
+ * @property bubbles
+ * @type boolean
+ * @default true
+ */
+ this.bubbles = true;
+
+ /**
+ * Supports multiple options for listener signatures in order to
+ * port YUI 2 apps.
+ * @property signature
+ * @type int
+ * @default 9
+ */
+ this.signature = YUI3_SIGNATURE;
+
+ // this.hasSubscribers = false;
+
+ // this.hasAfters = false;
+
+ /**
+ * If set to true, the custom event will deliver an EventFacade object
+ * that is similar to a DOM event object.
+ * @property emitFacade
+ * @type boolean
+ * @default false
+ */
+ // this.emitFacade = false;
+
+ this.applyConfig(o, true);
+
+ // this.log("Creating " + this.type);
+
+};
+
+Y.CustomEvent.prototype = {
+
+ /**
+ * Apply configuration properties. Only applies the CONFIG whitelist
+ * @method applyConfig
+ * @param o hash of properties to apply
+ * @param force {boolean} if true, properties that exist on the event
+ * will be overwritten.
+ */
+ applyConfig: function(o, force) {
+ if (o) {
+ Y.mix(this, o, force, CONFIGS);
+ }
+ },
+
+ _on: function(fn, context, args, when) {
+
+ if (!fn) {
+ this.log("Invalid callback for CE: " + this.type);
+ }
+
+ var s = new Y.Subscriber(fn, context, args, when);
+
+ if (this.fireOnce && this.fired) {
+ Y.later(0, this, Y.bind(this._notify, this, s, this.firedWith));
+ }
+
+ if (when == AFTER) {
+ this.afters[s.id] = s;
+ this.hasAfters = true;
+ } else {
+ this.subscribers[s.id] = s;
+ this.hasSubscribers = true;
+ }
+
+ return new Y.EventHandle(this, s);
+
+ },
+
+ /**
+ * Listen for this event
+ * @method subscribe
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ * @deprecated use on
+ */
+ subscribe: function(fn, context) {
+ Y.log('ce.subscribe deprecated, use "on"', 'warn', 'deprecated');
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event
+ * @method on
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ on: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event after the normal subscribers have been notified and
+ * the default behavior has been applied. If a normal subscriber prevents the
+ * default behavior, it also prevents after listeners from firing.
+ * @method after
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ after: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, AFTER);
+ },
+
+ /**
+ * Detach listeners.
+ * @method detach
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {int|EventTarget} returns a chainable event target
+ * or the number of subscribers unsubscribed.
+ */
+ detach: function(fn, context) {
+ // unsubscribe handle
+ if (fn && fn.detach) {
+ return fn.detach();
+ }
+
+ var found = 0, subs = this.subscribers, i, s;
+
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && (!fn || fn === s.fn)) {
+ this._delete(s);
+ found++;
+ }
+ }
+ }
+
+ return found;
+ },
+
+ /**
+ * Detach listeners.
+ * @method unsubscribe
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {boolean|EventTarget} returns a chainable event target
+ * or a boolean for legacy detach support.
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+
+ /**
+ * Notify a single subscriber
+ * @method _notify
+ * @param s {Subscriber} the subscriber
+ * @param args {Array} the arguments array to apply to the listener
+ * @private
+ */
+ _notify: function(s, args, ef) {
+
+ this.log(this.type + "->" + "sub: " + s.id);
+
+ var ret;
+
+ ret = s.notify(args, this);
+
+ if (false === ret || this.stopped > 1) {
+ this.log(this.type + " cancelled by subscriber");
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * Logger abstraction to centralize the application of the silent flag
+ * @method log
+ * @param msg {string} message to log
+ * @param cat {string} log category
+ */
+ log: function(msg, cat) {
+ if (!this.silent) {
+ Y.log(this.id + ': ' + msg, cat || "info", "event");
+ }
+ },
+
+ /**
+ * Notifies the subscribers. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters:
+ * <ul>
+ * <li>The type of event</li>
+ * <li>All of the arguments fire() was executed with as an array</li>
+ * <li>The custom object (if any) that was passed into the subscribe()
+ * method</li>
+ * </ul>
+ * @method fire
+ * @param {Object*} arguments an arbitrary set of parameters to pass to
+ * the handler.
+ * @return {boolean} false if one of the subscribers returned false,
+ * true otherwise
+ *
+ */
+ fire: function() {
+ if (this.fireOnce && this.fired) {
+ this.log('fireOnce event: ' + this.type + ' already fired');
+ return true;
+ } else {
+
+ var args = Y.Array(arguments, 0, true);
+
+ this.fired = true;
+ this.firedWith = args;
+
+ if (this.emitFacade) {
+ return this.fireComplex(args);
+ } else {
+ return this.fireSimple(args);
+ }
+ }
+ },
+
+ fireSimple: function(args) {
+ if (this.hasSubscribers || this.hasAfters) {
+ this._procSubs(Y.merge(this.subscribers, this.afters), args);
+ }
+ this._broadcast(args);
+ return this.stopped ? false : true;
+ },
+
+ // Requires the event-custom-complex module for full funcitonality.
+ fireComplex: function(args) {
+ args[0] = args[0] || {};
+ return this.fireSimple(args);
+ },
+
+ _procSubs: function(subs, args, ef) {
+ var s, i;
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && s.fn) {
+ if (false === this._notify(s, args, ef)) {
+ this.stopped = 2;
+ }
+ if (this.stopped == 2) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ },
+
+ _broadcast: function(args) {
+ if (!this.stopped && this.broadcast) {
+
+ var a = Y.Array(args);
+ a.unshift(this.type);
+
+ if (this.host !== Y) {
+ Y.fire.apply(Y, a);
+ }
+
+ if (this.broadcast == 2) {
+ Y.Global.fire.apply(Y.Global, a);
+ }
+ }
+ },
+
+ /**
+ * Removes all listeners
+ * @method unsubscribeAll
+ * @return {int} The number of listeners unsubscribed
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners
+ * @method detachAll
+ * @return {int} The number of listeners unsubscribed
+ */
+ detachAll: function() {
+ return this.detach();
+ },
+
+ /**
+ * @method _delete
+ * @param subscriber object
+ * @private
+ */
+ _delete: function(s) {
+ if (s) {
+ delete s.fn;
+ delete s.context;
+ delete this.subscribers[s.id];
+ delete this.afters[s.id];
+ }
+ }
+};
+
+/////////////////////////////////////////////////////////////////////
+
+/**
+ * Stores the subscriber information to be used when the event fires.
+ * @param {Function} fn The wrapped function to execute
+ * @param {Object} context The value of the keyword 'this' in the listener
+ * @param {Array} args* 0..n additional arguments to supply the listener
+ *
+ * @class Subscriber
+ * @constructor
+ */
+Y.Subscriber = function(fn, context, args) {
+
+ /**
+ * The callback that will be execute when the event fires
+ * This is wrapped by Y.rbind if obj was supplied.
+ * @property fn
+ * @type Function
+ */
+ this.fn = fn;
+
+ /**
+ * Optional 'this' keyword for the listener
+ * @property context
+ * @type Object
+ */
+ this.context = context;
+
+ /**
+ * Unique subscriber id
+ * @property id
+ * @type String
+ */
+ this.id = Y.stamp(this);
+
+ /**
+ * Additional arguments to propagate to the subscriber
+ * @property args
+ * @type Array
+ */
+ this.args = args;
+
+ /**
+ * Custom events for a given fire transaction.
+ * @property events
+ * @type {EventTarget}
+ */
+ this.events = null;
+
+};
+
+Y.Subscriber.prototype = {
+
+ _notify: function(c, args, ce) {
+ var a = this.args, ret;
+ switch (ce.signature) {
+ case 0:
+ ret = this.fn.call(c, ce.type, args, c);
+ break;
+ case 1:
+ ret = this.fn.call(c, args[0] || null, c);
+ break;
+ default:
+ if (a || args) {
+ args = args || [];
+ a = (a) ? args.concat(a) : args;
+ ret = this.fn.apply(c, a);
+ } else {
+ ret = this.fn.call(c);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Executes the subscriber.
+ * @method notify
+ * @param args {Array} Arguments array for the subscriber
+ * @param ce {CustomEvent} The custom event that sent the notification
+ */
+ notify: function(args, ce) {
+ var c = this.context,
+ ret = true;
+
+ if (!c) {
+ c = (ce.contextFn) ? ce.contextFn() : ce.context;
+ }
+
+ // only catch errors if we will not re-throw them.
+ if (Y.config.throwFail) {
+ ret = this._notify(c, args, ce);
+ } else {
+ try {
+ ret = this._notify(c, args, ce);
+ } catch(e) {
+ Y.error(this + ' failed: ' + e.message, e);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Returns true if the fn and obj match this objects properties.
+ * Used by the unsubscribe method to match the right subscriber.
+ *
+ * @method contains
+ * @param {Function} fn the function to execute
+ * @param {Object} context optional 'this' keyword for the listener
+ * @return {boolean} true if the supplied arguments match this
+ * subscriber's signature.
+ */
+ contains: function(fn, context) {
+ if (context) {
+ return ((this.fn == fn) && this.context == context);
+ } else {
+ return (this.fn == fn);
+ }
+ }
+
+};
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * EventTarget provides the implementation for any object to
+ * publish, subscribe and fire to custom events, and also
+ * alows other EventTargets to target the object with events
+ * sourced from the other object.
+ * EventTarget is designed to be used with Y.augment to wrap
+ * EventCustom in an interface that allows events to be listened to
+ * and fired by name. This makes it possible for implementing code to
+ * subscribe to an event that either has not been created yet, or will
+ * not be created at all.
+ * @class EventTarget
+ * @param opts a configuration object
+ * @config emitFacade {boolean} if true, all events will emit event
+ * facade payloads by default (default false)
+ * @config prefix {string} the prefix to apply to non-prefixed event names
+ * @config chain {boolean} if true, on/after/detach return the host to allow
+ * chaining, otherwise they return an EventHandle (default false)
+ */
+
+var L = Y.Lang,
+ PREFIX_DELIMITER = ':',
+ CATEGORY_DELIMITER = '|',
+ AFTER_PREFIX = '~AFTER~',
+
+ /**
+ * If the instance has a prefix attribute and the
+ * event type is not prefixed, the instance prefix is
+ * applied to the supplied type.
+ * @method _getType
+ * @private
+ */
+ _getType = Y.cached(function(type, pre) {
+
+ if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
+ return type;
+ }
+
+ return pre + PREFIX_DELIMITER + type;
+ }),
+
+ /**
+ * Returns an array with the detach key (if provided),
+ * and the prefixed event name from _getType
+ * Y.on('detachcategory, menu:click', fn)
+ * @method _parseType
+ * @private
+ */
+ _parseType = Y.cached(function(type, pre) {
+
+ var t = type, detachcategory, after, i;
+
+ if (!L.isString(t)) {
+ return t;
+ }
+
+ i = t.indexOf(AFTER_PREFIX);
+
+ if (i > -1) {
+ after = true;
+ t = t.substr(AFTER_PREFIX.length);
+ // Y.log(t);
+ }
+
+ i = t.indexOf(CATEGORY_DELIMITER);
+
+ if (i > -1) {
+ detachcategory = t.substr(0, (i));
+ t = t.substr(i+1);
+ if (t == '*') {
+ t = null;
+ }
+ }
+
+ // detach category, full type with instance prefix, is this an after listener, short type
+ return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
+ }),
+
+ ET = function(opts) {
+
+ // Y.log('EventTarget constructor executed: ' + this._yuid);
+
+ var o = (L.isObject(opts)) ? opts : {};
+
+ this._yuievt = this._yuievt || {
+
+ id: Y.guid(),
+
+ events: {},
+
+ targets: {},
+
+ config: o,
+
+ chain: ('chain' in o) ? o.chain : Y.config.chain,
+
+ defaults: {
+ context: o.context || this,
+ host: this,
+ emitFacade: o.emitFacade,
+ fireOnce: o.fireOnce,
+ queuable: o.queuable,
+ broadcast: o.broadcast,
+ bubbles: ('bubbles' in o) ? o.bubbles : true
+ }
+ };
+
+ };
+
+
+ET.prototype = {
+
+ /**
+ * Subscribe to a custom event hosted by this object
+ * @method on
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ on: function(type, fn, context, x) {
+
+ var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
+ detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
+ Node = Y.Node, n, domevent;
+
+ if (L.isObject(type)) {
+
+ if (L.isFunction(type)) {
+ return Y.Do.before.apply(Y.Do, arguments);
+ }
+
+ f = fn;
+ c = context;
+ args = Y.Array(arguments, 0, true);
+ ret = {};
+ after = type._after;
+ delete type._after;
+
+ Y.each(type, function(v, k) {
+
+ if (v) {
+ f = v.fn || ((Y.Lang.isFunction(v)) ? v : f);
+ c = v.context || c;
+ }
+
+ args[0] = (after) ? AFTER_PREFIX + k : k;
+ args[1] = f;
+ args[2] = c;
+
+ ret[k] = this.on.apply(this, args);
+
+ }, this);
+
+ return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
+
+ }
+
+ detachcategory = parts[0];
+ after = parts[2];
+ shorttype = parts[3];
+
+ // extra redirection so we catch adaptor events too. take a look at this.
+ if (Node && (this instanceof Node) && (shorttype in Node.DOM_EVENTS)) {
+ args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, Node.getDOMNode(this));
+ // Y.log("Node detected, redirecting with these args: " + args);
+ return Y.on.apply(Y, args);
+ }
+
+ type = parts[1];
+
+ if (this instanceof YUI) {
+
+ adapt = Y.Env.evt.plugins[type];
+ args = Y.Array(arguments, 0, true);
+ args[0] = shorttype;
+
+ if (Node) {
+ n = args[2];
+
+ if (n instanceof Y.NodeList) {
+ n = Y.NodeList.getDOMNodes(n);
+ } else if (n instanceof Node) {
+ n = Node.getDOMNode(n);
+ }
+
+ domevent = (shorttype in Node.DOM_EVENTS);
+
+ // Captures both DOM events and event plugins.
+ if (domevent) {
+ args[2] = n;
+ }
+ }
+
+ // check for the existance of an event adaptor
+ if (adapt) {
+ Y.log('Using adaptor for ' + shorttype + ', ' + n, 'info', 'event');
+ handle = adapt.on.apply(Y, args);
+ } else if ((!type) || domevent) {
+ handle = Y.Event._attach(args);
+ }
+
+ }
+
+ if (!handle) {
+ ce = this._yuievt.events[type] || this.publish(type);
+ handle = ce._on(fn, context, (arguments.length > 3) ? Y.Array(arguments, 3, true) : null, (after) ? 'after' : true);
+ }
+
+ if (detachcategory) {
+ store[detachcategory] = store[detachcategory] || {};
+ store[detachcategory][type] = store[detachcategory][type] || [];
+ store[detachcategory][type].push(handle);
+ }
+
+ return (this._yuievt.chain) ? this : handle;
+
+ },
+
+ /**
+ * subscribe to an event
+ * @method subscribe
+ * @deprecated use on
+ */
+ subscribe: function() {
+ Y.log('EventTarget subscribe() is deprecated, use on()', 'warn', 'deprecated');
+ return this.on.apply(this, arguments);
+ },
+
+ /**
+ * Detach one or more listeners the from the specified event
+ * @method detach
+ * @param type {string|Object} Either the handle to the subscriber or the
+ * type of event. If the type
+ * is not specified, it will attempt to remove
+ * the listener from all hosted events.
+ * @param fn {Function} The subscribed function to unsubscribe, if not
+ * supplied, all subscribers will be removed.
+ * @param context {Object} The custom object passed to subscribe. This is
+ * optional, but if supplied will be used to
+ * disambiguate multiple listeners that are the same
+ * (e.g., you subscribe many object using a function
+ * that lives on the prototype)
+ * @return {EventTarget} the host
+ */
+ detach: function(type, fn, context) {
+ var evts = this._yuievt.events, i, ret,
+ Node = Y.Node, isNode = (this instanceof Node);
+
+ // detachAll disabled on the Y instance.
+ if (!type && (this !== Y)) {
+ for (i in evts) {
+ if (evts.hasOwnProperty(i)) {
+ ret = evts[i].detach(fn, context);
+ }
+ }
+ if (isNode) {
+
+ Y.Event.purgeElement(Node.getDOMNode(this));
+ }
+
+ return ret;
+ }
+
+ var parts = _parseType(type, this._yuievt.config.prefix),
+ detachcategory = L.isArray(parts) ? parts[0] : null,
+ shorttype = (parts) ? parts[3] : null,
+ handle, adapt, store = Y.Env.evt.handles, cat, args,
+ ce,
+
+ keyDetacher = function(lcat, ltype) {
+ var handles = lcat[ltype];
+ if (handles) {
+ while (handles.length) {
+ handle = handles.pop();
+ handle.detach();
+ }
+ }
+ };
+
+ if (detachcategory) {
+
+ cat = store[detachcategory];
+ type = parts[1];
+
+ if (cat) {
+ if (type) {
+ keyDetacher(cat, type);
+ } else {
+ for (i in cat) {
+ if (cat.hasOwnProperty(i)) {
+ keyDetacher(cat, i);
+ }
+ }
+ }
+
+ return (this._yuievt.chain) ? this : true;
+ }
+
+ // If this is an event handle, use it to detach
+ } else if (L.isObject(type) && type.detach) {
+ ret = type.detach();
+ return (this._yuievt.chain) ? this : ret;
+ // extra redirection so we catch adaptor events too. take a look at this.
+ } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
+ args = Y.Array(arguments, 0, true);
+ args[2] = Node.getDOMNode(this);
+ return Y.detach.apply(Y, args);
+ }
+
+ adapt = Y.Env.evt.plugins[shorttype];
+
+ // The YUI instance handles DOM events and adaptors
+ if (this instanceof YUI) {
+ args = Y.Array(arguments, 0, true);
+ // use the adaptor specific detach code if
+ if (adapt && adapt.detach) {
+ return adapt.detach.apply(Y, args);
+ // DOM event fork
+ } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
+ args[0] = type;
+ return Y.Event.detach.apply(Y.Event, args);
+ }
+ }
+
+ ce = evts[type];
+ if (ce) {
+ ret = ce.detach(fn, context);
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * detach a listener
+ * @method unsubscribe
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method detachAll
+ * @param type {string} The type, or name of the event
+ */
+ detachAll: function(type) {
+ return this.detach(type);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method unsubscribeAll
+ * @param type {string} The type, or name of the event
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Creates a new custom event of the specified type. If a custom event
+ * by that name already exists, it will not be re-created. In either
+ * case the custom event is returned.
+ *
+ * @method publish
+ *
+ * @param type {string} the type, or name of the event
+ * @param opts {object} optional config params. Valid properties are:
+ *
+ * <ul>
+ * <li>
+ * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
+ * </li>
+ * <li>
+ * 'bubbles': whether or not this event bubbles (true)
+ * </li>
+ * <li>
+ * 'context': the default execution context for the listeners (this)
+ * </li>
+ * <li>
+ * 'defaultFn': the default function to execute when this event fires if preventDefault was not called
+ * </li>
+ * <li>
+ * 'emitFacade': whether or not this event emits a facade (false)
+ * </li>
+ * <li>
+ * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
+ * </li>
+ * <li>
+ * 'fireOnce': if an event is configured to fire once, new subscribers after
+ * the fire will be notified immediately.
+ * </li>
+ * <li>
+ * 'preventable': whether or not preventDefault() has an effect (true)
+ * </li>
+ * <li>
+ * 'preventedFn': a function that is executed when preventDefault is called
+ * </li>
+ * <li>
+ * 'queuable': whether or not this event can be queued during bubbling (false)
+ * </li>
+ * <li>
+ * 'silent': if silent is true, debug messages are not provided for this event.
+ * </li>
+ * <li>
+ * 'stoppedFn': a function that is executed when stopPropagation is called
+ * </li>
+ * <li>
+ * 'type': the event type (valid option if not provided as the first parameter to publish)
+ * </li>
+ * </ul>
+ *
+ * @return {Event.Custom} the custom event
+ *
+ */
+ publish: function(type, opts) {
+ var events, ce, ret, pre = this._yuievt.config.prefix;
+
+ type = (pre) ? _getType(type, pre) : type;
+
+
+ if (L.isObject(type)) {
+ ret = {};
+ Y.each(type, function(v, k) {
+ ret[k] = this.publish(k, v || opts);
+ }, this);
+
+ return ret;
+ }
+
+ events = this._yuievt.events;
+ ce = events[type];
+
+ if (ce) {
+// ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
+ if (opts) {
+ ce.applyConfig(opts, true);
+ }
+ } else {
+ // apply defaults
+ ce = new Y.CustomEvent(type, (opts) ? Y.mix(opts, this._yuievt.defaults) : this._yuievt.defaults);
+ events[type] = ce;
+ }
+
+ // make sure we turn the broadcast flag off if this
+ // event was published as a result of bubbling
+ // if (opts instanceof Y.CustomEvent) {
+ // events[type].broadcast = false;
+ // }
+
+ return events[type];
+ },
+
+ /**
+ * Registers another EventTarget as a bubble target. Bubble order
+ * is determined by the order registered. Multiple targets can
+ * be specified.
+ * @method addTarget
+ * @param o {EventTarget} the target to add
+ */
+ addTarget: function(o) {
+ this._yuievt.targets[Y.stamp(o)] = o;
+ this._yuievt.hasTargets = true;
+ },
+
+ /**
+ * Removes a bubble target
+ * @method removeTarget
+ * @param o {EventTarget} the target to remove
+ */
+ removeTarget: function(o) {
+ delete this._yuievt.targets[Y.stamp(o)];
+ },
+
+ /**
+ * Fire a custom event by name. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters.
+ *
+ * If the custom event object hasn't been created, then the event hasn't
+ * been published and it has no subscribers. For performance sake, we
+ * immediate exit in this case. This means the event won't bubble, so
+ * if the intention is that a bubble target be notified, the event must
+ * be published on this object first.
+ *
+ * The first argument is the event type, and any additional arguments are
+ * passed to the listeners as parameters. If the first of these is an
+ * object literal, and the event is configured to emit an event facade,
+ * that object is mixed into the event facade and the facade is provided
+ * in place of the original object.
+ *
+ * @method fire
+ * @param type {String|Object} The type of the event, or an object that contains
+ * a 'type' property.
+ * @param arguments {Object*} an arbitrary set of parameters to pass to
+ * the handler. If the first of these is an object literal and the event is
+ * configured to emit an event facade, the event facade will replace that
+ * parameter after the properties the object literal contains are copied to
+ * the event facade.
+ * @return {Event.Target} the event host
+ *
+ */
+ fire: function(type) {
+
+ var typeIncluded = L.isString(type),
+ t = (typeIncluded) ? type : (type && type.type),
+ ce, a, ret, pre=this._yuievt.config.prefix;
+
+ t = (pre) ? _getType(t, pre) : t;
+ ce = this.getEvent(t, true);
+
+ // this event has not been published or subscribed to
+ if (!ce) {
+
+ if (this._yuievt.hasTargets) {
+ a = (typeIncluded) ? arguments : Y.Array(arguments, 0, true).unshift(t);
+ return this.bubble(null, a, this);
+ }
+
+ // otherwise there is nothing to be done
+ ret = true;
+
+ } else {
+
+ a = Y.Array(arguments, (typeIncluded) ? 1 : 0, true);
+ ret = ce.fire.apply(ce, a);
+
+ // clear target for next fire()
+ ce.target = null;
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * Returns the custom event of the provided type has been created, a
+ * falsy value otherwise
+ * @method getEvent
+ * @param type {string} the type, or name of the event
+ * @param prefixed {string} if true, the type is prefixed already
+ * @return {Event.Custom} the custom event or null
+ */
+ getEvent: function(type, prefixed) {
+ var pre, e;
+ if (!prefixed) {
+ pre = this._yuievt.config.prefix;
+ type = (pre) ? _getType(type, pre) : type;
+ }
+ e = this._yuievt.events;
+ return (e && type in e) ? e[type] : null;
+ },
+
+ /**
+ * Subscribe to a custom event hosted by this object. The
+ * supplied callback will execute after any listeners add
+ * via the subscribe method, and after the default function,
+ * if configured for the event, has executed.
+ * @method after
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ after: function(type, fn) {
+
+ var a = Y.Array(arguments, 0, true);
+
+ switch (L.type(type)) {
+ case 'function':
+ return Y.Do.after.apply(Y.Do, arguments);
+ case 'object':
+ a[0]._after = true;
+ break;
+ default:
+ a[0] = AFTER_PREFIX + type;
+ }
+
+ return this.on.apply(this, a);
+
+ },
+
+ /**
+ * Executes the callback before a DOM event, custom event
+ * or method. If the first argument is a function, it
+ * is assumed the target is a method. For DOM and custom
+ * events, this is an alias for Y.on.
+ *
+ * For DOM and custom events:
+ * type, callback, context, 0-n arguments
+ *
+ * For methods:
+ * callback, object (method host), methodName, context, 0-n arguments
+ *
+ * @method before
+ * @return detach handle
+ * @deprecated use the on method
+ */
+ before: function() {
+ return this.on.apply(this, arguments);
+ }
+
+};
+
+Y.EventTarget = ET;
+
+// make Y an event target
+Y.mix(Y, ET.prototype, false, false, {
+ bubbles: false
+});
+
+ET.call(Y);
+
+YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
+
+/**
+ * Hosts YUI page level events. This is where events bubble to
+ * when the broadcast config is set to 2. This property is
+ * only available if the custom event module is loaded.
+ * @property Global
+ * @type EventTarget
+ * @for YUI
+ */
+Y.Global = YUI.Env.globalEvents;
+
+// @TODO implement a global namespace function on Y.Global?
+
+})();
+
+
+/**
+ * <code>YUI</code>'s <code>on</code> method is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events, DOM events, and
+ * function events. <code>detach</code> is also provided to remove listeners
+ * serviced by this function.
+ *
+ * The signature that <code>on</code> accepts varies depending on the type
+ * of event being consumed. Refer to the specific methods that will
+ * service a specific request for additional information about subscribing
+ * to that type of event.
+ *
+ * <ul>
+ * <li>Custom events. These events are defined by various
+ * modules in the library. This type of event is delegated to
+ * <code>EventTarget</code>'s <code>on</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('domready', function() { // start work });</code>
+ * </li>
+ * <li>DOM events. These are moments reported by the browser related
+ * to browser functionality and user interaction.
+ * This type of event is delegated to <code>Event</code>'s
+ * <code>attach</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>The specification for the Node(s) to attach the listener
+ * to. This can be a selector, collections, or Node/Element
+ * refereces.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('click', function(e) { // something was clicked }, '#someelement');</code>
+ * </li>
+ * <li>Function events. These events can be used to react before or after a
+ * function is executed. This type of event is delegated to <code>Event.Do</code>'s
+ * <code>before</code> method.
+ * <ul>
+ * <li>The callback to execute</li>
+ * <li>The object that has the function that will be listened for.</li>
+ * <li>The name of the function to listen for.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example <code>Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');</code>
+ * </li>
+ * </ul>
+ *
+ * <code>on</code> corresponds to the moment before any default behavior of
+ * the event. <code>after</code> works the same way, but these listeners
+ * execute after the event's default behavior. <code>before</code> is an
+ * alias for <code>on</code>.
+ *
+ * @method on
+ * @param type** event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target** a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra** 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+/**
+ * after() is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events,
+ * DOM events, and AOP events. This works the same way as
+ * the on() function, only it operates after any default
+ * behavior for the event has executed. @see <code>on</code> for more
+ * information.
+ * @method after
+ * @param type event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+
+}, '3.0.0' ,{requires:['oop']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-custom-base",function(E){E.Env.evt={handles:{},plugins:{}};(function(){var F=0,G=1;E.Do={objs:{},before:function(I,K,L,M){var J=I,H;if(M){H=[I,M].concat(E.Array(arguments,4,true));J=E.rbind.apply(E,H);}return this._inject(F,J,K,L);},after:function(I,K,L,M){var J=I,H;if(M){H=[I,M].concat(E.Array(arguments,4,true));J=E.rbind.apply(E,H);}return this._inject(G,J,K,L);},_inject:function(H,J,K,M){var N=E.stamp(K),L,I;if(!this.objs[N]){this.objs[N]={};}L=this.objs[N];if(!L[M]){L[M]=new E.Do.Method(K,M);K[M]=function(){return L[M].exec.apply(L[M],arguments);};}I=N+E.stamp(J)+M;L[M].register(I,J,H);return new E.EventHandle(L[M],I);},detach:function(H){if(H.detach){H.detach();}},_unload:function(I,H){}};E.Do.Method=function(H,I){this.obj=H;this.methodName=I;this.method=H[I];this.before={};this.after={};};E.Do.Method.prototype.register=function(I,J,H){if(H){this.after[I]=J;}else{this.before[I]=J;}};E.Do.Method.prototype._delete=function(H){delete this.before[H];delete this.after[H];};E.Do.Method.prototype.exec=function(){var J=E.Array(arguments,0,true),K,I,N,L=this.before,H=this.after,M=false;for(K in L){if(L.hasOwnProperty(K)){I=L[K].apply(this.obj,J);if(I){switch(I.constructor){case E.Do.Halt:return I.retVal;case E.Do.AlterArgs:J=I.newArgs;break;case E.Do.Prevent:M=true;break;default:}}}}if(!M){I=this.method.apply(this.obj,J);}for(K in H){if(H.hasOwnProperty(K)){N=H[K].apply(this.obj,J);if(N&&N.constructor==E.Do.Halt){return N.retVal;}else{if(N&&N.constructor==E.Do.AlterReturn){I=N.newRetVal;}}}}return I;};E.Do.AlterArgs=function(I,H){this.msg=I;this.newArgs=H;};E.Do.AlterReturn=function(I,H){this.msg=I;this.newRetVal=H;};E.Do.Halt=function(I,H){this.msg=I;this.retVal=H;};E.Do.Prevent=function(H){this.msg=H;};E.Do.Error=E.Do.Halt;})();var D="after",B=["broadcast","bubbles","context","contextFn","currentTarget","defaultFn","details","emitFacade","fireOnce","host","preventable","preventedFn","queuable","silent","stoppedFn","target","type"],C=9,A="yui:log";E.EventHandle=function(F,G){this.evt=F;this.sub=G;};E.EventHandle.prototype={detach:function(){var F=this.evt,G;if(F){if(E.Lang.isArray(F)){for(G=0;G<F.length;G++){F[G].detach();}}else{F._delete(this.sub);}}}};E.CustomEvent=function(F,G){G=G||{};this.id=E.stamp(this);this.type=F;this.context=E;this.logSystem=(F==A);this.silent=this.logSystem;this.subscribers={};this.afters={};this.preventable=true;this.bubbles=true;this.signature=C;this.applyConfig(G,true);};E.CustomEvent.prototype={applyConfig:function(G,F){if(G){E.mix(this,G,F,B);}},_on:function(J,H,G,F){if(!J){this.log("Invalid callback for CE: "+this.type);}var I=new E.Subscriber(J,H,G,F);if(this.fireOnce&&this.fired){E.later(0,this,E.bind(this._notify,this,I,this.firedWith));}if(F==D){this.afters[I.id]=I;this.hasAfters=true;}else{this.subscribers[I.id]=I;this.hasSubscribers=true;}return new E.EventHandle(this,I);},subscribe:function(H,G){var F=(arguments.length>2)?E.Array(arguments,2,true):null;return this._on(H,G,F,true);},on:function(H,G){var F=(arguments.length>2)?E.Array(arguments,2,true):null;return this._on(H,G,F,true);},after:function(H,G){var F=(arguments.length>2)?E.Array(arguments,2,true):null;return this._on(H,G,F,D);},detach:function(J,H){if(J&&J.detach){return J.detach();}var K=0,G=this.subscribers,F,I;for(F in G){if(G.hasOwnProperty(F)){I=G[F];if(I&&(!J||J===I.fn)){this._delete(I);K++;}}}return K;},unsubscribe:function(){return this.detach.apply(this,arguments);},_notify:function(I,H,F){this.log(this.type+"->"+"sub: "+I.id);var G;G=I.notify(H,this);if(false===G||this.stopped>1){this.log(this.type+" cancelled by subscriber");return false;}return true;},log:function(G,F){if(!this.silent){}},fire:function(){if(this.fireOnce&&this.fired){this.log("fireOnce event: "+this.type+" already fired");return true;}else{var F=E.Array(arguments,0,true);this.fired=true;this.firedWith=F;if(this.emitFacade){return this.fireComplex(F);}else{return this.fireSimple(F);}}},fireSimple:function(F){if(this.hasSubscribers||this.hasAfters){this._procSubs(E.merge(this.subscribers,this.afters),F);}this._broadcast(F);return this.stopped?false:true;},fireComplex:function(F){F[0]=F[0]||{};return this.fireSimple(F);},_procSubs:function(I,G,F){var J,H;for(H in I){if(I.hasOwnProperty(H)){J=I[H];if(J&&J.fn){if(false===this._notify(J,G,F)){this.stopped=2;}if(this.stopped==2){return false;}}}}return true;},_broadcast:function(G){if(!this.stopped&&this.broadcast){var F=E.Array(G);F.unshift(this.type);if(this.host!==E){E.fire.apply(E,F);}if(this.broadcast==2){E.Global.fire.apply(E.Global,F);}}},unsubscribeAll:function(){return this.detachAll.apply(this,arguments);},detachAll:function(){return this.detach();},_delete:function(F){if(F){delete F.fn;delete F.context;delete this.subscribers[F.id];delete this.afters[F.id];}}};E.Subscriber=function(H,G,F){this.fn=H;this.context=G;this.id=E.stamp(this);this.args=F;this.events=null;};E.Subscriber.prototype={_notify:function(J,H,I){var F=this.args,G;switch(I.signature){case 0:G=this.fn.call(J,I.type,H,J);break;case 1:G=this.fn.call(J,H[0]||null,J);break;default:if(F||H){H=H||[];F=(F)?H.concat(F):H;G=this.fn.apply(J,F);}else{G=this.fn.call(J);}}return G;},notify:function(G,I){var J=this.context,F=true;if(!J){J=(I.contextFn)?I.contextFn():I.context;}if(E.config.throwFail){F=this._notify(J,G,I);}else{try{F=this._notify(J,G,I);}catch(H){E.error(this+" failed: "+H.message,H);}}return F;},contains:function(G,F){if(F){return((this.fn==G)&&this.context==F);}else{return(this.fn==G);}}};(function(){var F=E.Lang,H=":",I="|",J="~AFTER~",K=E.cached(function(L,N){if(!N||!F.isString(L)||L.indexOf(H)>-1){return L;}return N+H+L;}),G=E.cached(function(O,Q){var N=O,P,R,L;if(!F.isString(N)){return N;}L=N.indexOf(J);if(L>-1){R=true;N=N.substr(J.length);}L=N.indexOf(I);if(L>-1){P=N.substr(0,(L));N=N.substr(L+1);if(N=="*"){N=null;}}return[P,(Q)?K(N,Q):N,R,N];}),M=function(L){var N=(F.isObject(L))?L:{};this._yuievt=this._yuievt||{id:E.guid(),events:{},targets:{},config:N,chain:("chain" in N)?N.chain:E.config.chain,defaults:{context:N.context||this,host:this,emitFacade:N.emitFacade,fireOnce:N.fireOnce,queuable:N.queuable,broadcast:N.broadcast,bubbles:("bubbles" in N)?N.bubbles:true}};
+};M.prototype={on:function(Q,U,O,V){var Z=G(Q,this._yuievt.config.prefix),a,b,N,g,X,W,d,R=E.Env.evt.handles,P,L,S,e=E.Node,Y,T;if(F.isObject(Q)){if(F.isFunction(Q)){return E.Do.before.apply(E.Do,arguments);}a=U;b=O;N=E.Array(arguments,0,true);g={};P=Q._after;delete Q._after;E.each(Q,function(f,c){if(f){a=f.fn||((E.Lang.isFunction(f))?f:a);b=f.context||b;}N[0]=(P)?J+c:c;N[1]=a;N[2]=b;g[c]=this.on.apply(this,N);},this);return(this._yuievt.chain)?this:new E.EventHandle(g);}W=Z[0];P=Z[2];S=Z[3];if(e&&(this instanceof e)&&(S in e.DOM_EVENTS)){N=E.Array(arguments,0,true);N.splice(2,0,e.getDOMNode(this));return E.on.apply(E,N);}Q=Z[1];if(this instanceof YUI){L=E.Env.evt.plugins[Q];N=E.Array(arguments,0,true);N[0]=S;if(e){Y=N[2];if(Y instanceof E.NodeList){Y=E.NodeList.getDOMNodes(Y);}else{if(Y instanceof e){Y=e.getDOMNode(Y);}}T=(S in e.DOM_EVENTS);if(T){N[2]=Y;}}if(L){d=L.on.apply(E,N);}else{if((!Q)||T){d=E.Event._attach(N);}}}if(!d){X=this._yuievt.events[Q]||this.publish(Q);d=X._on(U,O,(arguments.length>3)?E.Array(arguments,3,true):null,(P)?"after":true);}if(W){R[W]=R[W]||{};R[W][Q]=R[W][Q]||[];R[W][Q].push(d);}return(this._yuievt.chain)?this:d;},subscribe:function(){return this.on.apply(this,arguments);},detach:function(P,U,O){var T=this._yuievt.events,Z,d,c=E.Node,Y=(this instanceof c);if(!P&&(this!==E)){for(Z in T){if(T.hasOwnProperty(Z)){d=T[Z].detach(U,O);}}if(Y){E.Event.purgeElement(c.getDOMNode(this));}return d;}var X=G(P,this._yuievt.config.prefix),V=F.isArray(X)?X[0]:null,R=(X)?X[3]:null,b,L,Q=E.Env.evt.handles,S,N,W,a=function(g,f){var e=g[f];if(e){while(e.length){b=e.pop();b.detach();}}};if(V){S=Q[V];P=X[1];if(S){if(P){a(S,P);}else{for(Z in S){if(S.hasOwnProperty(Z)){a(S,Z);}}}return(this._yuievt.chain)?this:true;}}else{if(F.isObject(P)&&P.detach){d=P.detach();return(this._yuievt.chain)?this:d;}else{if(Y&&((!R)||(R in c.DOM_EVENTS))){N=E.Array(arguments,0,true);N[2]=c.getDOMNode(this);return E.detach.apply(E,N);}}}L=E.Env.evt.plugins[R];if(this instanceof YUI){N=E.Array(arguments,0,true);if(L&&L.detach){return L.detach.apply(E,N);}else{if(!P||(!L&&c&&(P in c.DOM_EVENTS))){N[0]=P;return E.Event.detach.apply(E.Event,N);}}}W=T[P];if(W){d=W.detach(U,O);}return(this._yuievt.chain)?this:d;},unsubscribe:function(){return this.detach.apply(this,arguments);},detachAll:function(L){return this.detach(L);},unsubscribeAll:function(){return this.detachAll.apply(this,arguments);},publish:function(O,P){var N,R,L,Q=this._yuievt.config.prefix;O=(Q)?K(O,Q):O;if(F.isObject(O)){L={};E.each(O,function(T,S){L[S]=this.publish(S,T||P);},this);return L;}N=this._yuievt.events;R=N[O];if(R){if(P){R.applyConfig(P,true);}}else{R=new E.CustomEvent(O,(P)?E.mix(P,this._yuievt.defaults):this._yuievt.defaults);N[O]=R;}return N[O];},addTarget:function(L){this._yuievt.targets[E.stamp(L)]=L;this._yuievt.hasTargets=true;},removeTarget:function(L){delete this._yuievt.targets[E.stamp(L)];},fire:function(P){var S=F.isString(P),O=(S)?P:(P&&P.type),R,L,N,Q=this._yuievt.config.prefix;O=(Q)?K(O,Q):O;R=this.getEvent(O,true);if(!R){if(this._yuievt.hasTargets){L=(S)?arguments:E.Array(arguments,0,true).unshift(O);return this.bubble(null,L,this);}N=true;}else{L=E.Array(arguments,(S)?1:0,true);N=R.fire.apply(R,L);R.target=null;}return(this._yuievt.chain)?this:N;},getEvent:function(N,L){var P,O;if(!L){P=this._yuievt.config.prefix;N=(P)?K(N,P):N;}O=this._yuievt.events;return(O&&N in O)?O[N]:null;},after:function(O,N){var L=E.Array(arguments,0,true);switch(F.type(O)){case"function":return E.Do.after.apply(E.Do,arguments);case"object":L[0]._after=true;break;default:L[0]=J+O;}return this.on.apply(this,L);},before:function(){return this.on.apply(this,arguments);}};E.EventTarget=M;E.mix(E,M.prototype,false,false,{bubbles:false});M.call(E);YUI.Env.globalEvents=YUI.Env.globalEvents||new M();E.Global=YUI.Env.globalEvents;})();},"3.0.0",{requires:["oop"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-custom-base', function(Y) {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ */
+
+Y.Env.evt = {
+ handles: {},
+ plugins: {}
+};
+
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * Allows for the insertion of methods that are executed before or after
+ * a specified method
+ * @class Do
+ * @static
+ */
+
+var BEFORE = 0,
+ AFTER = 1;
+
+Y.Do = {
+
+ /**
+ * Cache of objects touched by the utility
+ * @property objs
+ * @static
+ */
+ objs: {},
+
+ /**
+ * Execute the supplied method before the specified function
+ * @method before
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ before: function(fn, obj, sFn, c) {
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(BEFORE, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ after: function(fn, obj, sFn, c) {
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(AFTER, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method _inject
+ * @param when {string} before or after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @private
+ * @static
+ */
+ _inject: function(when, fn, obj, sFn) {
+
+ // object id
+ var id = Y.stamp(obj), o, sid;
+
+ if (! this.objs[id]) {
+ // create a map entry for the obj if it doesn't exist
+ this.objs[id] = {};
+ }
+
+ o = this.objs[id];
+
+ if (! o[sFn]) {
+ // create a map entry for the method if it doesn't exist
+ o[sFn] = new Y.Do.Method(obj, sFn);
+
+ // re-route the method to our wrapper
+ obj[sFn] =
+ function() {
+ return o[sFn].exec.apply(o[sFn], arguments);
+ };
+ }
+
+ // subscriber id
+ sid = id + Y.stamp(fn) + sFn;
+
+ // register the callback
+ o[sFn].register(sid, fn, when);
+
+ return new Y.EventHandle(o[sFn], sid);
+
+ },
+
+ /**
+ * Detach a before or after subscription
+ * @method detach
+ * @param handle {string} the subscription handle
+ */
+ detach: function(handle) {
+
+ if (handle.detach) {
+ handle.detach();
+ }
+
+ },
+
+ _unload: function(e, me) {
+
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * Wrapper for a displaced method with aop enabled
+ * @class Do.Method
+ * @constructor
+ * @param obj The object to operate on
+ * @param sFn The name of the method to displace
+ */
+Y.Do.Method = function(obj, sFn) {
+ this.obj = obj;
+ this.methodName = sFn;
+ this.method = obj[sFn];
+ this.before = {};
+ this.after = {};
+};
+
+/**
+ * Register a aop subscriber
+ * @method register
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype.register = function (sid, fn, when) {
+ if (when) {
+ this.after[sid] = fn;
+ } else {
+ this.before[sid] = fn;
+ }
+};
+
+/**
+ * Unregister a aop subscriber
+ * @method delete
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype._delete = function (sid) {
+ delete this.before[sid];
+ delete this.after[sid];
+};
+
+/**
+ * Execute the wrapped method
+ * @method exec
+ */
+Y.Do.Method.prototype.exec = function () {
+
+ var args = Y.Array(arguments, 0, true),
+ i, ret, newRet,
+ bf = this.before,
+ af = this.after,
+ prevented = false;
+
+ // execute before
+ for (i in bf) {
+ if (bf.hasOwnProperty(i)) {
+ ret = bf[i].apply(this.obj, args);
+ if (ret) {
+ switch (ret.constructor) {
+ case Y.Do.Halt:
+ return ret.retVal;
+ case Y.Do.AlterArgs:
+ args = ret.newArgs;
+ break;
+ case Y.Do.Prevent:
+ prevented = true;
+ break;
+ default:
+ }
+ }
+ }
+ }
+
+ // execute method
+ if (!prevented) {
+ ret = this.method.apply(this.obj, args);
+ }
+
+ // execute after methods.
+ for (i in af) {
+ if (af.hasOwnProperty(i)) {
+ newRet = af[i].apply(this.obj, args);
+ // Stop processing if a Halt object is returned
+ if (newRet && newRet.constructor == Y.Do.Halt) {
+ return newRet.retVal;
+ // Check for a new return value
+ } else if (newRet && newRet.constructor == Y.Do.AlterReturn) {
+ ret = newRet.newRetVal;
+ }
+ }
+ }
+
+ return ret;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Return an AlterArgs object when you want to change the arguments that
+ * were passed into the function. An example would be a service that scrubs
+ * out illegal characters prior to executing the core business logic.
+ * @class Do.AlterArgs
+ */
+Y.Do.AlterArgs = function(msg, newArgs) {
+ this.msg = msg;
+ this.newArgs = newArgs;
+};
+
+/**
+ * Return an AlterReturn object when you want to change the result returned
+ * from the core method to the caller
+ * @class Do.AlterReturn
+ */
+Y.Do.AlterReturn = function(msg, newRetVal) {
+ this.msg = msg;
+ this.newRetVal = newRetVal;
+};
+
+/**
+ * Return a Halt object when you want to terminate the execution
+ * of all subsequent subscribers as well as the wrapped method
+ * if it has not exectued yet.
+ * @class Do.Halt
+ */
+Y.Do.Halt = function(msg, retVal) {
+ this.msg = msg;
+ this.retVal = retVal;
+};
+
+/**
+ * Return a Prevent object when you want to prevent the wrapped function
+ * from executing, but want the remaining listeners to execute
+ * @class Do.Prevent
+ */
+Y.Do.Prevent = function(msg) {
+ this.msg = msg;
+};
+
+/**
+ * Return an Error object when you want to terminate the execution
+ * of all subsequent method calls.
+ * @class Do.Error
+ * @deprecated use Y.Do.Halt or Y.Do.Prevent
+ */
+Y.Do.Error = Y.Do.Halt;
+
+//////////////////////////////////////////////////////////////////////////
+
+// Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
+
+})();
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+
+/**
+ * Return value from all subscribe operations
+ * @class EventHandle
+ * @constructor
+ * @param evt {CustomEvent} the custom event
+ * @param sub {Subscriber} the subscriber
+ */
+
+// var onsubscribeType = "_event:onsub",
+var AFTER = 'after',
+ CONFIGS = [
+ 'broadcast',
+ 'bubbles',
+ 'context',
+ 'contextFn',
+ 'currentTarget',
+ 'defaultFn',
+ 'details',
+ 'emitFacade',
+ 'fireOnce',
+ 'host',
+ 'preventable',
+ 'preventedFn',
+ 'queuable',
+ 'silent',
+ 'stoppedFn',
+ 'target',
+ 'type'
+ ],
+
+
+ YUI3_SIGNATURE = 9,
+ YUI_LOG = 'yui:log';
+
+Y.EventHandle = function(evt, sub) {
+
+ /**
+ * The custom event
+ * @type CustomEvent
+ */
+ this.evt = evt;
+
+ /**
+ * The subscriber object
+ * @type Subscriber
+ */
+ this.sub = sub;
+};
+
+Y.EventHandle.prototype = {
+
+ /**
+ * Detaches this subscriber
+ * @method detach
+ */
+ detach: function() {
+ var evt = this.evt, i;
+ if (evt) {
+ if (Y.Lang.isArray(evt)) {
+ for (i=0; i<evt.length; i++) {
+ evt[i].detach();
+ }
+ } else {
+ evt._delete(this.sub);
+ }
+ }
+ }
+};
+
+/**
+ * The CustomEvent class lets you define events for your application
+ * that can be subscribed to by one or more independent component.
+ *
+ * @param {String} type The type of event, which is passed to the callback
+ * when the event fires
+ * @param o configuration object
+ * @class CustomEvent
+ * @constructor
+ */
+Y.CustomEvent = function(type, o) {
+
+ // if (arguments.length > 2) {
+// this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
+ // }
+
+ o = o || {};
+
+ this.id = Y.stamp(this);
+
+ /**
+ * The type of event, returned to subscribers when the event fires
+ * @property type
+ * @type string
+ */
+ this.type = type;
+
+ /**
+ * The context the the event will fire from by default. Defaults to the YUI
+ * instance.
+ * @property context
+ * @type object
+ */
+ this.context = Y;
+
+ this.logSystem = (type == YUI_LOG);
+
+ /**
+ * If 0, this event does not broadcast. If 1, the YUI instance is notified
+ * every time this event fires. If 2, the YUI instance and the YUI global
+ * (if event is enabled on the global) are notified every time this event
+ * fires.
+ * @property broadcast
+ * @type int
+ */
+ // this.broadcast = 0;
+
+ /**
+ * By default all custom events are logged in the debug build, set silent
+ * to true to disable debug outpu for this event.
+ * @property silent
+ * @type boolean
+ */
+ this.silent = this.logSystem;
+
+ /**
+ * Specifies whether this event should be queued when the host is actively
+ * processing an event. This will effect exectution order of the callbacks
+ * for the various events.
+ * @property queuable
+ * @type boolean
+ * @default false
+ */
+ // this.queuable = false;
+
+ /**
+ * The subscribers to this event
+ * @property subscribers
+ * @type Subscriber{}
+ */
+ this.subscribers = {};
+
+ /**
+ * 'After' subscribers
+ * @property afters
+ * @type Subscriber{}
+ */
+ this.afters = {};
+
+ /**
+ * This event has fired if true
+ *
+ * @property fired
+ * @type boolean
+ * @default false;
+ */
+ // this.fired = false;
+
+ /**
+ * An array containing the arguments the custom event
+ * was last fired with.
+ * @property firedWith
+ * @type Array
+ */
+ // this.firedWith;
+
+ /**
+ * This event should only fire one time if true, and if
+ * it has fired, any new subscribers should be notified
+ * immediately.
+ *
+ * @property fireOnce
+ * @type boolean
+ * @default false;
+ */
+ // this.fireOnce = false;
+
+ /**
+ * Flag for stopPropagation that is modified during fire()
+ * 1 means to stop propagation to bubble targets. 2 means
+ * to also stop additional subscribers on this target.
+ * @property stopped
+ * @type int
+ */
+ // this.stopped = 0;
+
+ /**
+ * Flag for preventDefault that is modified during fire().
+ * if it is not 0, the default behavior for this event
+ * @property prevented
+ * @type int
+ */
+ // this.prevented = 0;
+
+ /**
+ * Specifies the host for this custom event. This is used
+ * to enable event bubbling
+ * @property host
+ * @type EventTarget
+ */
+ // this.host = null;
+
+ /**
+ * The default function to execute after event listeners
+ * have fire, but only if the default action was not
+ * prevented.
+ * @property defaultFn
+ * @type Function
+ */
+ // this.defaultFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * stopPropagation or stopImmediatePropagation
+ * @property stoppedFn
+ * @type Function
+ */
+ // this.stoppedFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * preventDefault
+ * @property preventedFn
+ * @type Function
+ */
+ // this.preventedFn = null;
+
+ /**
+ * Specifies whether or not this event's default function
+ * can be cancelled by a subscriber by executing preventDefault()
+ * on the event facade
+ * @property preventable
+ * @type boolean
+ * @default true
+ */
+ this.preventable = true;
+
+ /**
+ * Specifies whether or not a subscriber can stop the event propagation
+ * via stopPropagation(), stopImmediatePropagation(), or halt()
+ * @property bubbles
+ * @type boolean
+ * @default true
+ */
+ this.bubbles = true;
+
+ /**
+ * Supports multiple options for listener signatures in order to
+ * port YUI 2 apps.
+ * @property signature
+ * @type int
+ * @default 9
+ */
+ this.signature = YUI3_SIGNATURE;
+
+ // this.hasSubscribers = false;
+
+ // this.hasAfters = false;
+
+ /**
+ * If set to true, the custom event will deliver an EventFacade object
+ * that is similar to a DOM event object.
+ * @property emitFacade
+ * @type boolean
+ * @default false
+ */
+ // this.emitFacade = false;
+
+ this.applyConfig(o, true);
+
+ // this.log("Creating " + this.type);
+
+};
+
+Y.CustomEvent.prototype = {
+
+ /**
+ * Apply configuration properties. Only applies the CONFIG whitelist
+ * @method applyConfig
+ * @param o hash of properties to apply
+ * @param force {boolean} if true, properties that exist on the event
+ * will be overwritten.
+ */
+ applyConfig: function(o, force) {
+ if (o) {
+ Y.mix(this, o, force, CONFIGS);
+ }
+ },
+
+ _on: function(fn, context, args, when) {
+
+ if (!fn) {
+ this.log("Invalid callback for CE: " + this.type);
+ }
+
+ var s = new Y.Subscriber(fn, context, args, when);
+
+ if (this.fireOnce && this.fired) {
+ Y.later(0, this, Y.bind(this._notify, this, s, this.firedWith));
+ }
+
+ if (when == AFTER) {
+ this.afters[s.id] = s;
+ this.hasAfters = true;
+ } else {
+ this.subscribers[s.id] = s;
+ this.hasSubscribers = true;
+ }
+
+ return new Y.EventHandle(this, s);
+
+ },
+
+ /**
+ * Listen for this event
+ * @method subscribe
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ * @deprecated use on
+ */
+ subscribe: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event
+ * @method on
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ on: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event after the normal subscribers have been notified and
+ * the default behavior has been applied. If a normal subscriber prevents the
+ * default behavior, it also prevents after listeners from firing.
+ * @method after
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ after: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, AFTER);
+ },
+
+ /**
+ * Detach listeners.
+ * @method detach
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {int|EventTarget} returns a chainable event target
+ * or the number of subscribers unsubscribed.
+ */
+ detach: function(fn, context) {
+ // unsubscribe handle
+ if (fn && fn.detach) {
+ return fn.detach();
+ }
+
+ var found = 0, subs = this.subscribers, i, s;
+
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && (!fn || fn === s.fn)) {
+ this._delete(s);
+ found++;
+ }
+ }
+ }
+
+ return found;
+ },
+
+ /**
+ * Detach listeners.
+ * @method unsubscribe
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {boolean|EventTarget} returns a chainable event target
+ * or a boolean for legacy detach support.
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+
+ /**
+ * Notify a single subscriber
+ * @method _notify
+ * @param s {Subscriber} the subscriber
+ * @param args {Array} the arguments array to apply to the listener
+ * @private
+ */
+ _notify: function(s, args, ef) {
+
+ this.log(this.type + "->" + "sub: " + s.id);
+
+ var ret;
+
+ ret = s.notify(args, this);
+
+ if (false === ret || this.stopped > 1) {
+ this.log(this.type + " cancelled by subscriber");
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * Logger abstraction to centralize the application of the silent flag
+ * @method log
+ * @param msg {string} message to log
+ * @param cat {string} log category
+ */
+ log: function(msg, cat) {
+ if (!this.silent) {
+ }
+ },
+
+ /**
+ * Notifies the subscribers. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters:
+ * <ul>
+ * <li>The type of event</li>
+ * <li>All of the arguments fire() was executed with as an array</li>
+ * <li>The custom object (if any) that was passed into the subscribe()
+ * method</li>
+ * </ul>
+ * @method fire
+ * @param {Object*} arguments an arbitrary set of parameters to pass to
+ * the handler.
+ * @return {boolean} false if one of the subscribers returned false,
+ * true otherwise
+ *
+ */
+ fire: function() {
+ if (this.fireOnce && this.fired) {
+ this.log('fireOnce event: ' + this.type + ' already fired');
+ return true;
+ } else {
+
+ var args = Y.Array(arguments, 0, true);
+
+ this.fired = true;
+ this.firedWith = args;
+
+ if (this.emitFacade) {
+ return this.fireComplex(args);
+ } else {
+ return this.fireSimple(args);
+ }
+ }
+ },
+
+ fireSimple: function(args) {
+ if (this.hasSubscribers || this.hasAfters) {
+ this._procSubs(Y.merge(this.subscribers, this.afters), args);
+ }
+ this._broadcast(args);
+ return this.stopped ? false : true;
+ },
+
+ // Requires the event-custom-complex module for full funcitonality.
+ fireComplex: function(args) {
+ args[0] = args[0] || {};
+ return this.fireSimple(args);
+ },
+
+ _procSubs: function(subs, args, ef) {
+ var s, i;
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && s.fn) {
+ if (false === this._notify(s, args, ef)) {
+ this.stopped = 2;
+ }
+ if (this.stopped == 2) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ },
+
+ _broadcast: function(args) {
+ if (!this.stopped && this.broadcast) {
+
+ var a = Y.Array(args);
+ a.unshift(this.type);
+
+ if (this.host !== Y) {
+ Y.fire.apply(Y, a);
+ }
+
+ if (this.broadcast == 2) {
+ Y.Global.fire.apply(Y.Global, a);
+ }
+ }
+ },
+
+ /**
+ * Removes all listeners
+ * @method unsubscribeAll
+ * @return {int} The number of listeners unsubscribed
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners
+ * @method detachAll
+ * @return {int} The number of listeners unsubscribed
+ */
+ detachAll: function() {
+ return this.detach();
+ },
+
+ /**
+ * @method _delete
+ * @param subscriber object
+ * @private
+ */
+ _delete: function(s) {
+ if (s) {
+ delete s.fn;
+ delete s.context;
+ delete this.subscribers[s.id];
+ delete this.afters[s.id];
+ }
+ }
+};
+
+/////////////////////////////////////////////////////////////////////
+
+/**
+ * Stores the subscriber information to be used when the event fires.
+ * @param {Function} fn The wrapped function to execute
+ * @param {Object} context The value of the keyword 'this' in the listener
+ * @param {Array} args* 0..n additional arguments to supply the listener
+ *
+ * @class Subscriber
+ * @constructor
+ */
+Y.Subscriber = function(fn, context, args) {
+
+ /**
+ * The callback that will be execute when the event fires
+ * This is wrapped by Y.rbind if obj was supplied.
+ * @property fn
+ * @type Function
+ */
+ this.fn = fn;
+
+ /**
+ * Optional 'this' keyword for the listener
+ * @property context
+ * @type Object
+ */
+ this.context = context;
+
+ /**
+ * Unique subscriber id
+ * @property id
+ * @type String
+ */
+ this.id = Y.stamp(this);
+
+ /**
+ * Additional arguments to propagate to the subscriber
+ * @property args
+ * @type Array
+ */
+ this.args = args;
+
+ /**
+ * Custom events for a given fire transaction.
+ * @property events
+ * @type {EventTarget}
+ */
+ this.events = null;
+
+};
+
+Y.Subscriber.prototype = {
+
+ _notify: function(c, args, ce) {
+ var a = this.args, ret;
+ switch (ce.signature) {
+ case 0:
+ ret = this.fn.call(c, ce.type, args, c);
+ break;
+ case 1:
+ ret = this.fn.call(c, args[0] || null, c);
+ break;
+ default:
+ if (a || args) {
+ args = args || [];
+ a = (a) ? args.concat(a) : args;
+ ret = this.fn.apply(c, a);
+ } else {
+ ret = this.fn.call(c);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Executes the subscriber.
+ * @method notify
+ * @param args {Array} Arguments array for the subscriber
+ * @param ce {CustomEvent} The custom event that sent the notification
+ */
+ notify: function(args, ce) {
+ var c = this.context,
+ ret = true;
+
+ if (!c) {
+ c = (ce.contextFn) ? ce.contextFn() : ce.context;
+ }
+
+ // only catch errors if we will not re-throw them.
+ if (Y.config.throwFail) {
+ ret = this._notify(c, args, ce);
+ } else {
+ try {
+ ret = this._notify(c, args, ce);
+ } catch(e) {
+ Y.error(this + ' failed: ' + e.message, e);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Returns true if the fn and obj match this objects properties.
+ * Used by the unsubscribe method to match the right subscriber.
+ *
+ * @method contains
+ * @param {Function} fn the function to execute
+ * @param {Object} context optional 'this' keyword for the listener
+ * @return {boolean} true if the supplied arguments match this
+ * subscriber's signature.
+ */
+ contains: function(fn, context) {
+ if (context) {
+ return ((this.fn == fn) && this.context == context);
+ } else {
+ return (this.fn == fn);
+ }
+ }
+
+};
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * EventTarget provides the implementation for any object to
+ * publish, subscribe and fire to custom events, and also
+ * alows other EventTargets to target the object with events
+ * sourced from the other object.
+ * EventTarget is designed to be used with Y.augment to wrap
+ * EventCustom in an interface that allows events to be listened to
+ * and fired by name. This makes it possible for implementing code to
+ * subscribe to an event that either has not been created yet, or will
+ * not be created at all.
+ * @class EventTarget
+ * @param opts a configuration object
+ * @config emitFacade {boolean} if true, all events will emit event
+ * facade payloads by default (default false)
+ * @config prefix {string} the prefix to apply to non-prefixed event names
+ * @config chain {boolean} if true, on/after/detach return the host to allow
+ * chaining, otherwise they return an EventHandle (default false)
+ */
+
+var L = Y.Lang,
+ PREFIX_DELIMITER = ':',
+ CATEGORY_DELIMITER = '|',
+ AFTER_PREFIX = '~AFTER~',
+
+ /**
+ * If the instance has a prefix attribute and the
+ * event type is not prefixed, the instance prefix is
+ * applied to the supplied type.
+ * @method _getType
+ * @private
+ */
+ _getType = Y.cached(function(type, pre) {
+
+ if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
+ return type;
+ }
+
+ return pre + PREFIX_DELIMITER + type;
+ }),
+
+ /**
+ * Returns an array with the detach key (if provided),
+ * and the prefixed event name from _getType
+ * Y.on('detachcategory, menu:click', fn)
+ * @method _parseType
+ * @private
+ */
+ _parseType = Y.cached(function(type, pre) {
+
+ var t = type, detachcategory, after, i;
+
+ if (!L.isString(t)) {
+ return t;
+ }
+
+ i = t.indexOf(AFTER_PREFIX);
+
+ if (i > -1) {
+ after = true;
+ t = t.substr(AFTER_PREFIX.length);
+ }
+
+ i = t.indexOf(CATEGORY_DELIMITER);
+
+ if (i > -1) {
+ detachcategory = t.substr(0, (i));
+ t = t.substr(i+1);
+ if (t == '*') {
+ t = null;
+ }
+ }
+
+ // detach category, full type with instance prefix, is this an after listener, short type
+ return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
+ }),
+
+ ET = function(opts) {
+
+
+ var o = (L.isObject(opts)) ? opts : {};
+
+ this._yuievt = this._yuievt || {
+
+ id: Y.guid(),
+
+ events: {},
+
+ targets: {},
+
+ config: o,
+
+ chain: ('chain' in o) ? o.chain : Y.config.chain,
+
+ defaults: {
+ context: o.context || this,
+ host: this,
+ emitFacade: o.emitFacade,
+ fireOnce: o.fireOnce,
+ queuable: o.queuable,
+ broadcast: o.broadcast,
+ bubbles: ('bubbles' in o) ? o.bubbles : true
+ }
+ };
+
+ };
+
+
+ET.prototype = {
+
+ /**
+ * Subscribe to a custom event hosted by this object
+ * @method on
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ on: function(type, fn, context, x) {
+
+ var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
+ detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
+ Node = Y.Node, n, domevent;
+
+ if (L.isObject(type)) {
+
+ if (L.isFunction(type)) {
+ return Y.Do.before.apply(Y.Do, arguments);
+ }
+
+ f = fn;
+ c = context;
+ args = Y.Array(arguments, 0, true);
+ ret = {};
+ after = type._after;
+ delete type._after;
+
+ Y.each(type, function(v, k) {
+
+ if (v) {
+ f = v.fn || ((Y.Lang.isFunction(v)) ? v : f);
+ c = v.context || c;
+ }
+
+ args[0] = (after) ? AFTER_PREFIX + k : k;
+ args[1] = f;
+ args[2] = c;
+
+ ret[k] = this.on.apply(this, args);
+
+ }, this);
+
+ return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
+
+ }
+
+ detachcategory = parts[0];
+ after = parts[2];
+ shorttype = parts[3];
+
+ // extra redirection so we catch adaptor events too. take a look at this.
+ if (Node && (this instanceof Node) && (shorttype in Node.DOM_EVENTS)) {
+ args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, Node.getDOMNode(this));
+ return Y.on.apply(Y, args);
+ }
+
+ type = parts[1];
+
+ if (this instanceof YUI) {
+
+ adapt = Y.Env.evt.plugins[type];
+ args = Y.Array(arguments, 0, true);
+ args[0] = shorttype;
+
+ if (Node) {
+ n = args[2];
+
+ if (n instanceof Y.NodeList) {
+ n = Y.NodeList.getDOMNodes(n);
+ } else if (n instanceof Node) {
+ n = Node.getDOMNode(n);
+ }
+
+ domevent = (shorttype in Node.DOM_EVENTS);
+
+ // Captures both DOM events and event plugins.
+ if (domevent) {
+ args[2] = n;
+ }
+ }
+
+ // check for the existance of an event adaptor
+ if (adapt) {
+ handle = adapt.on.apply(Y, args);
+ } else if ((!type) || domevent) {
+ handle = Y.Event._attach(args);
+ }
+
+ }
+
+ if (!handle) {
+ ce = this._yuievt.events[type] || this.publish(type);
+ handle = ce._on(fn, context, (arguments.length > 3) ? Y.Array(arguments, 3, true) : null, (after) ? 'after' : true);
+ }
+
+ if (detachcategory) {
+ store[detachcategory] = store[detachcategory] || {};
+ store[detachcategory][type] = store[detachcategory][type] || [];
+ store[detachcategory][type].push(handle);
+ }
+
+ return (this._yuievt.chain) ? this : handle;
+
+ },
+
+ /**
+ * subscribe to an event
+ * @method subscribe
+ * @deprecated use on
+ */
+ subscribe: function() {
+ return this.on.apply(this, arguments);
+ },
+
+ /**
+ * Detach one or more listeners the from the specified event
+ * @method detach
+ * @param type {string|Object} Either the handle to the subscriber or the
+ * type of event. If the type
+ * is not specified, it will attempt to remove
+ * the listener from all hosted events.
+ * @param fn {Function} The subscribed function to unsubscribe, if not
+ * supplied, all subscribers will be removed.
+ * @param context {Object} The custom object passed to subscribe. This is
+ * optional, but if supplied will be used to
+ * disambiguate multiple listeners that are the same
+ * (e.g., you subscribe many object using a function
+ * that lives on the prototype)
+ * @return {EventTarget} the host
+ */
+ detach: function(type, fn, context) {
+ var evts = this._yuievt.events, i, ret,
+ Node = Y.Node, isNode = (this instanceof Node);
+
+ // detachAll disabled on the Y instance.
+ if (!type && (this !== Y)) {
+ for (i in evts) {
+ if (evts.hasOwnProperty(i)) {
+ ret = evts[i].detach(fn, context);
+ }
+ }
+ if (isNode) {
+
+ Y.Event.purgeElement(Node.getDOMNode(this));
+ }
+
+ return ret;
+ }
+
+ var parts = _parseType(type, this._yuievt.config.prefix),
+ detachcategory = L.isArray(parts) ? parts[0] : null,
+ shorttype = (parts) ? parts[3] : null,
+ handle, adapt, store = Y.Env.evt.handles, cat, args,
+ ce,
+
+ keyDetacher = function(lcat, ltype) {
+ var handles = lcat[ltype];
+ if (handles) {
+ while (handles.length) {
+ handle = handles.pop();
+ handle.detach();
+ }
+ }
+ };
+
+ if (detachcategory) {
+
+ cat = store[detachcategory];
+ type = parts[1];
+
+ if (cat) {
+ if (type) {
+ keyDetacher(cat, type);
+ } else {
+ for (i in cat) {
+ if (cat.hasOwnProperty(i)) {
+ keyDetacher(cat, i);
+ }
+ }
+ }
+
+ return (this._yuievt.chain) ? this : true;
+ }
+
+ // If this is an event handle, use it to detach
+ } else if (L.isObject(type) && type.detach) {
+ ret = type.detach();
+ return (this._yuievt.chain) ? this : ret;
+ // extra redirection so we catch adaptor events too. take a look at this.
+ } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
+ args = Y.Array(arguments, 0, true);
+ args[2] = Node.getDOMNode(this);
+ return Y.detach.apply(Y, args);
+ }
+
+ adapt = Y.Env.evt.plugins[shorttype];
+
+ // The YUI instance handles DOM events and adaptors
+ if (this instanceof YUI) {
+ args = Y.Array(arguments, 0, true);
+ // use the adaptor specific detach code if
+ if (adapt && adapt.detach) {
+ return adapt.detach.apply(Y, args);
+ // DOM event fork
+ } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
+ args[0] = type;
+ return Y.Event.detach.apply(Y.Event, args);
+ }
+ }
+
+ ce = evts[type];
+ if (ce) {
+ ret = ce.detach(fn, context);
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * detach a listener
+ * @method unsubscribe
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method detachAll
+ * @param type {string} The type, or name of the event
+ */
+ detachAll: function(type) {
+ return this.detach(type);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method unsubscribeAll
+ * @param type {string} The type, or name of the event
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Creates a new custom event of the specified type. If a custom event
+ * by that name already exists, it will not be re-created. In either
+ * case the custom event is returned.
+ *
+ * @method publish
+ *
+ * @param type {string} the type, or name of the event
+ * @param opts {object} optional config params. Valid properties are:
+ *
+ * <ul>
+ * <li>
+ * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
+ * </li>
+ * <li>
+ * 'bubbles': whether or not this event bubbles (true)
+ * </li>
+ * <li>
+ * 'context': the default execution context for the listeners (this)
+ * </li>
+ * <li>
+ * 'defaultFn': the default function to execute when this event fires if preventDefault was not called
+ * </li>
+ * <li>
+ * 'emitFacade': whether or not this event emits a facade (false)
+ * </li>
+ * <li>
+ * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
+ * </li>
+ * <li>
+ * 'fireOnce': if an event is configured to fire once, new subscribers after
+ * the fire will be notified immediately.
+ * </li>
+ * <li>
+ * 'preventable': whether or not preventDefault() has an effect (true)
+ * </li>
+ * <li>
+ * 'preventedFn': a function that is executed when preventDefault is called
+ * </li>
+ * <li>
+ * 'queuable': whether or not this event can be queued during bubbling (false)
+ * </li>
+ * <li>
+ * 'silent': if silent is true, debug messages are not provided for this event.
+ * </li>
+ * <li>
+ * 'stoppedFn': a function that is executed when stopPropagation is called
+ * </li>
+ * <li>
+ * 'type': the event type (valid option if not provided as the first parameter to publish)
+ * </li>
+ * </ul>
+ *
+ * @return {Event.Custom} the custom event
+ *
+ */
+ publish: function(type, opts) {
+ var events, ce, ret, pre = this._yuievt.config.prefix;
+
+ type = (pre) ? _getType(type, pre) : type;
+
+
+ if (L.isObject(type)) {
+ ret = {};
+ Y.each(type, function(v, k) {
+ ret[k] = this.publish(k, v || opts);
+ }, this);
+
+ return ret;
+ }
+
+ events = this._yuievt.events;
+ ce = events[type];
+
+ if (ce) {
+// ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
+ if (opts) {
+ ce.applyConfig(opts, true);
+ }
+ } else {
+ // apply defaults
+ ce = new Y.CustomEvent(type, (opts) ? Y.mix(opts, this._yuievt.defaults) : this._yuievt.defaults);
+ events[type] = ce;
+ }
+
+ // make sure we turn the broadcast flag off if this
+ // event was published as a result of bubbling
+ // if (opts instanceof Y.CustomEvent) {
+ // events[type].broadcast = false;
+ // }
+
+ return events[type];
+ },
+
+ /**
+ * Registers another EventTarget as a bubble target. Bubble order
+ * is determined by the order registered. Multiple targets can
+ * be specified.
+ * @method addTarget
+ * @param o {EventTarget} the target to add
+ */
+ addTarget: function(o) {
+ this._yuievt.targets[Y.stamp(o)] = o;
+ this._yuievt.hasTargets = true;
+ },
+
+ /**
+ * Removes a bubble target
+ * @method removeTarget
+ * @param o {EventTarget} the target to remove
+ */
+ removeTarget: function(o) {
+ delete this._yuievt.targets[Y.stamp(o)];
+ },
+
+ /**
+ * Fire a custom event by name. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters.
+ *
+ * If the custom event object hasn't been created, then the event hasn't
+ * been published and it has no subscribers. For performance sake, we
+ * immediate exit in this case. This means the event won't bubble, so
+ * if the intention is that a bubble target be notified, the event must
+ * be published on this object first.
+ *
+ * The first argument is the event type, and any additional arguments are
+ * passed to the listeners as parameters. If the first of these is an
+ * object literal, and the event is configured to emit an event facade,
+ * that object is mixed into the event facade and the facade is provided
+ * in place of the original object.
+ *
+ * @method fire
+ * @param type {String|Object} The type of the event, or an object that contains
+ * a 'type' property.
+ * @param arguments {Object*} an arbitrary set of parameters to pass to
+ * the handler. If the first of these is an object literal and the event is
+ * configured to emit an event facade, the event facade will replace that
+ * parameter after the properties the object literal contains are copied to
+ * the event facade.
+ * @return {Event.Target} the event host
+ *
+ */
+ fire: function(type) {
+
+ var typeIncluded = L.isString(type),
+ t = (typeIncluded) ? type : (type && type.type),
+ ce, a, ret, pre=this._yuievt.config.prefix;
+
+ t = (pre) ? _getType(t, pre) : t;
+ ce = this.getEvent(t, true);
+
+ // this event has not been published or subscribed to
+ if (!ce) {
+
+ if (this._yuievt.hasTargets) {
+ a = (typeIncluded) ? arguments : Y.Array(arguments, 0, true).unshift(t);
+ return this.bubble(null, a, this);
+ }
+
+ // otherwise there is nothing to be done
+ ret = true;
+
+ } else {
+
+ a = Y.Array(arguments, (typeIncluded) ? 1 : 0, true);
+ ret = ce.fire.apply(ce, a);
+
+ // clear target for next fire()
+ ce.target = null;
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * Returns the custom event of the provided type has been created, a
+ * falsy value otherwise
+ * @method getEvent
+ * @param type {string} the type, or name of the event
+ * @param prefixed {string} if true, the type is prefixed already
+ * @return {Event.Custom} the custom event or null
+ */
+ getEvent: function(type, prefixed) {
+ var pre, e;
+ if (!prefixed) {
+ pre = this._yuievt.config.prefix;
+ type = (pre) ? _getType(type, pre) : type;
+ }
+ e = this._yuievt.events;
+ return (e && type in e) ? e[type] : null;
+ },
+
+ /**
+ * Subscribe to a custom event hosted by this object. The
+ * supplied callback will execute after any listeners add
+ * via the subscribe method, and after the default function,
+ * if configured for the event, has executed.
+ * @method after
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ after: function(type, fn) {
+
+ var a = Y.Array(arguments, 0, true);
+
+ switch (L.type(type)) {
+ case 'function':
+ return Y.Do.after.apply(Y.Do, arguments);
+ case 'object':
+ a[0]._after = true;
+ break;
+ default:
+ a[0] = AFTER_PREFIX + type;
+ }
+
+ return this.on.apply(this, a);
+
+ },
+
+ /**
+ * Executes the callback before a DOM event, custom event
+ * or method. If the first argument is a function, it
+ * is assumed the target is a method. For DOM and custom
+ * events, this is an alias for Y.on.
+ *
+ * For DOM and custom events:
+ * type, callback, context, 0-n arguments
+ *
+ * For methods:
+ * callback, object (method host), methodName, context, 0-n arguments
+ *
+ * @method before
+ * @return detach handle
+ * @deprecated use the on method
+ */
+ before: function() {
+ return this.on.apply(this, arguments);
+ }
+
+};
+
+Y.EventTarget = ET;
+
+// make Y an event target
+Y.mix(Y, ET.prototype, false, false, {
+ bubbles: false
+});
+
+ET.call(Y);
+
+YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
+
+/**
+ * Hosts YUI page level events. This is where events bubble to
+ * when the broadcast config is set to 2. This property is
+ * only available if the custom event module is loaded.
+ * @property Global
+ * @type EventTarget
+ * @for YUI
+ */
+Y.Global = YUI.Env.globalEvents;
+
+// @TODO implement a global namespace function on Y.Global?
+
+})();
+
+
+/**
+ * <code>YUI</code>'s <code>on</code> method is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events, DOM events, and
+ * function events. <code>detach</code> is also provided to remove listeners
+ * serviced by this function.
+ *
+ * The signature that <code>on</code> accepts varies depending on the type
+ * of event being consumed. Refer to the specific methods that will
+ * service a specific request for additional information about subscribing
+ * to that type of event.
+ *
+ * <ul>
+ * <li>Custom events. These events are defined by various
+ * modules in the library. This type of event is delegated to
+ * <code>EventTarget</code>'s <code>on</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('domready', function() { // start work });</code>
+ * </li>
+ * <li>DOM events. These are moments reported by the browser related
+ * to browser functionality and user interaction.
+ * This type of event is delegated to <code>Event</code>'s
+ * <code>attach</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>The specification for the Node(s) to attach the listener
+ * to. This can be a selector, collections, or Node/Element
+ * refereces.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('click', function(e) { // something was clicked }, '#someelement');</code>
+ * </li>
+ * <li>Function events. These events can be used to react before or after a
+ * function is executed. This type of event is delegated to <code>Event.Do</code>'s
+ * <code>before</code> method.
+ * <ul>
+ * <li>The callback to execute</li>
+ * <li>The object that has the function that will be listened for.</li>
+ * <li>The name of the function to listen for.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example <code>Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');</code>
+ * </li>
+ * </ul>
+ *
+ * <code>on</code> corresponds to the moment before any default behavior of
+ * the event. <code>after</code> works the same way, but these listeners
+ * execute after the event's default behavior. <code>before</code> is an
+ * alias for <code>on</code>.
+ *
+ * @method on
+ * @param type** event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target** a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra** 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+/**
+ * after() is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events,
+ * DOM events, and AOP events. This works the same way as
+ * the on() function, only it operates after any default
+ * behavior for the event has executed. @see <code>on</code> for more
+ * information.
+ * @method after
+ * @param type event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+
+}, '3.0.0' ,{requires:['oop']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-custom-complex', function(Y) {
+
+
+/**
+ * Adds event facades, preventable default behavior, and bubbling.
+ * events.
+ * @module event-custom
+ * @submodule event-custom-complex
+ */
+
+(function() {
+
+var FACADE, FACADE_KEYS, CEProto = Y.CustomEvent.prototype;
+
+/**
+ * Wraps and protects a custom event for use when emitFacade is set to true.
+ * Requires the event-custom-complex module
+ * @class EventFacade
+ * @param e {Event} the custom event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ */
+
+Y.EventFacade = function(e, currentTarget) {
+
+ e = e || {};
+
+ /**
+ * The arguments passed to fire
+ * @property details
+ * @type Array
+ */
+ this.details = e.details;
+
+ /**
+ * The event type
+ * @property type
+ * @type string
+ */
+ this.type = e.type;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted eventtarget
+ * @propery target
+ * @type Node
+ */
+ this.target = e.target;
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = currentTarget;
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = e.relatedTarget;
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ e.stopPropagation();
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ e.stopImmediatePropagation();
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ */
+ this.preventDefault = function() {
+ e.preventDefault();
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ e.halt(immediate);
+ };
+
+};
+
+CEProto.fireComplex = function(args) {
+ var es = Y.Env._eventstack, ef, q, queue, ce, ret, events;
+
+ if (es) {
+ // queue this event if the current item in the queue bubbles
+ if (this.queuable && this.type != es.next.type) {
+ this.log('queue ' + this.type);
+ es.queue.push([this, args]);
+ return true;
+ }
+ } else {
+ Y.Env._eventstack = {
+ // id of the first event in the stack
+ id: this.id,
+ next: this,
+ silent: this.silent,
+ stopped: 0,
+ prevented: 0,
+ queue: []
+ };
+ es = Y.Env._eventstack;
+ }
+
+ this.stopped = 0;
+ this.prevented = 0;
+ this.target = this.target || this.host;
+
+ events = new Y.EventTarget({
+ fireOnce: true,
+ context: this.host
+ });
+
+ this.events = events;
+
+ if (this.preventedFn) {
+ events.on('prevented', this.preventedFn);
+ }
+
+ if (this.stoppedFn) {
+ events.on('stopped', this.stoppedFn);
+ }
+
+ this.currentTarget = this.host || this.currentTarget;
+
+ this.details = args.slice(); // original arguments in the details
+
+ // this.log("Firing " + this + ", " + "args: " + args);
+ this.log("Firing " + this.type);
+
+ this._facade = null; // kill facade to eliminate stale properties
+
+ ef = this._getFacade(args);
+
+ if (Y.Lang.isObject(args[0])) {
+ args[0] = ef;
+ } else {
+ args.unshift(ef);
+ }
+
+ if (this.hasSubscribers) {
+ this._procSubs(Y.merge(this.subscribers), args, ef);
+ }
+
+ // bubble if this is hosted in an event target and propagation has not been stopped
+ if (this.bubbles && this.host && this.host.bubble && !this.stopped) {
+ es.stopped = 0;
+ es.prevented = 0;
+ ret = this.host.bubble(this);
+
+ this.stopped = Math.max(this.stopped, es.stopped);
+ this.prevented = Math.max(this.prevented, es.prevented);
+
+ }
+
+ // execute the default behavior if not prevented
+ if (this.defaultFn && !this.prevented) {
+ this.defaultFn.apply(this.host || this, args);
+ }
+
+ // broadcast listeners are fired as discreet events on the
+ // YUI instance and potentially the YUI global.
+ this._broadcast(args);
+
+ // process after listeners. If the default behavior was
+ // prevented, the after events don't fire.
+ if (this.hasAfters && !this.prevented && this.stopped < 2) {
+ this._procSubs(Y.merge(this.afters), args, ef);
+ }
+
+ if (es.id === this.id) {
+ queue = es.queue;
+
+ while (queue.length) {
+ q = queue.pop();
+ ce = q[0];
+ es.stopped = 0;
+ es.prevented = 0;
+ // set up stack to allow the next item to be processed
+ es.next = ce;
+ ce.fire.apply(ce, q[1]);
+ }
+
+ Y.Env._eventstack = null;
+ }
+
+ return this.stopped ? false : true;
+};
+
+CEProto._getFacade = function() {
+
+ var ef = this._facade, o, o2,
+ args = this.details;
+
+ if (!ef) {
+ ef = new Y.EventFacade(this, this.currentTarget);
+ }
+
+ // if the first argument is an object literal, apply the
+ // properties to the event facade
+ o = args && args[0];
+
+ if (Y.Lang.isObject(o, true)) {
+
+ o2 = {};
+
+ // protect the event facade properties
+ Y.mix(o2, ef, true, FACADE_KEYS);
+
+ // mix the data
+ Y.mix(ef, o, true);
+
+ // restore ef
+ Y.mix(ef, o2, true, FACADE_KEYS);
+ }
+
+ // update the details field with the arguments
+ // ef.type = this.type;
+ ef.details = this.details;
+ ef.target = this.target;
+ ef.currentTarget = this.currentTarget;
+ ef.stopped = 0;
+ ef.prevented = 0;
+
+ this._facade = ef;
+
+ return this._facade;
+};
+
+/**
+ * Stop propagation to bubble targets
+ * @for CustomEvent
+ * @method stopPropagation
+ */
+CEProto.stopPropagation = function() {
+ this.stopped = 1;
+ Y.Env._eventstack.stopped = 1;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Stops propagation to bubble targets, and prevents any remaining
+ * subscribers on the current target from executing.
+ * @method stopImmediatePropagation
+ */
+CEProto.stopImmediatePropagation = function() {
+ this.stopped = 2;
+ Y.Env._eventstack.stopped = 2;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Prevents the execution of this event's defaultFn
+ * @method preventDefault
+ */
+CEProto.preventDefault = function() {
+ if (this.preventable) {
+ this.prevented = 1;
+ Y.Env._eventstack.prevented = 1;
+ this.events.fire('prevented', this);
+ }
+};
+
+/**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+CEProto.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ this.preventDefault();
+};
+
+/**
+ * Propagate an event. Requires the event-custom-complex module.
+ * @method bubble
+ * @param evt {Event.Custom} the custom event to propagate
+ * @return {boolean} the aggregated return value from Event.Custom.fire
+ * @for EventTarget
+ */
+Y.EventTarget.prototype.bubble = function(evt, args, target) {
+
+ var targs = this._yuievt.targets, ret = true,
+ t, type, ce, i, bc;
+
+ if (!evt || ((!evt.stopped) && targs)) {
+
+ // Y.log('Bubbling ' + evt.type);
+ for (i in targs) {
+ if (targs.hasOwnProperty(i)) {
+ t = targs[i];
+ type = evt && evt.type;
+ ce = t.getEvent(type, true);
+
+ // if this event was not published on the bubble target,
+ // publish it with sensible default properties
+ if (!ce) {
+
+ if (t._yuievt.hasTargets) {
+ t.bubble.call(t, evt, args, target);
+ }
+
+ } else {
+ ce.target = target || (evt && evt.target) || this;
+ ce.currentTarget = t;
+
+ bc = ce.broadcast;
+ ce.broadcast = false;
+ ret = ret && ce.fire.apply(ce, args || evt.details);
+ ce.broadcast = bc;
+
+ // stopPropagation() was called
+ if (ce.stopped) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+};
+
+FACADE = new Y.EventFacade();
+FACADE_KEYS = Y.Object.keys(FACADE);
+
+})();
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-custom-complex",function(A){(function(){var C,D,B=A.CustomEvent.prototype;A.EventFacade=function(F,E){F=F||{};this.details=F.details;this.type=F.type;this.target=F.target;this.currentTarget=E;this.relatedTarget=F.relatedTarget;this.stopPropagation=function(){F.stopPropagation();};this.stopImmediatePropagation=function(){F.stopImmediatePropagation();};this.preventDefault=function(){F.preventDefault();};this.halt=function(G){F.halt(G);};};B.fireComplex=function(H){var L=A.Env._eventstack,F,J,E,K,G,I;if(L){if(this.queuable&&this.type!=L.next.type){this.log("queue "+this.type);L.queue.push([this,H]);return true;}}else{A.Env._eventstack={id:this.id,next:this,silent:this.silent,stopped:0,prevented:0,queue:[]};L=A.Env._eventstack;}this.stopped=0;this.prevented=0;this.target=this.target||this.host;I=new A.EventTarget({fireOnce:true,context:this.host});this.events=I;if(this.preventedFn){I.on("prevented",this.preventedFn);}if(this.stoppedFn){I.on("stopped",this.stoppedFn);}this.currentTarget=this.host||this.currentTarget;this.details=H.slice();this.log("Firing "+this.type);this._facade=null;F=this._getFacade(H);if(A.Lang.isObject(H[0])){H[0]=F;}else{H.unshift(F);}if(this.hasSubscribers){this._procSubs(A.merge(this.subscribers),H,F);}if(this.bubbles&&this.host&&this.host.bubble&&!this.stopped){L.stopped=0;L.prevented=0;G=this.host.bubble(this);this.stopped=Math.max(this.stopped,L.stopped);this.prevented=Math.max(this.prevented,L.prevented);}if(this.defaultFn&&!this.prevented){this.defaultFn.apply(this.host||this,H);}this._broadcast(H);if(this.hasAfters&&!this.prevented&&this.stopped<2){this._procSubs(A.merge(this.afters),H,F);}if(L.id===this.id){E=L.queue;while(E.length){J=E.pop();K=J[0];L.stopped=0;L.prevented=0;L.next=K;K.fire.apply(K,J[1]);}A.Env._eventstack=null;}return this.stopped?false:true;};B._getFacade=function(){var E=this._facade,H,G,F=this.details;if(!E){E=new A.EventFacade(this,this.currentTarget);}H=F&&F[0];if(A.Lang.isObject(H,true)){G={};A.mix(G,E,true,D);A.mix(E,H,true);A.mix(E,G,true,D);}E.details=this.details;E.target=this.target;E.currentTarget=this.currentTarget;E.stopped=0;E.prevented=0;this._facade=E;return this._facade;};B.stopPropagation=function(){this.stopped=1;A.Env._eventstack.stopped=1;this.events.fire("stopped",this);};B.stopImmediatePropagation=function(){this.stopped=2;A.Env._eventstack.stopped=2;this.events.fire("stopped",this);};B.preventDefault=function(){if(this.preventable){this.prevented=1;A.Env._eventstack.prevented=1;this.events.fire("prevented",this);}};B.halt=function(E){if(E){this.stopImmediatePropagation();}else{this.stopPropagation();}this.preventDefault();};A.EventTarget.prototype.bubble=function(M,K,I){var G=this._yuievt.targets,J=true,N,L,E,F,H;if(!M||((!M.stopped)&&G)){for(F in G){if(G.hasOwnProperty(F)){N=G[F];L=M&&M.type;E=N.getEvent(L,true);if(!E){if(N._yuievt.hasTargets){N.bubble.call(N,M,K,I);}}else{E.target=I||(M&&M.target)||this;E.currentTarget=N;H=E.broadcast;E.broadcast=false;J=J&&E.fire.apply(E,K||M.details);E.broadcast=H;if(E.stopped){break;}}}}}return J;};C=new A.EventFacade();D=A.Object.keys(C);})();},"3.0.0",{requires:["event-custom-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-custom-complex', function(Y) {
+
+
+/**
+ * Adds event facades, preventable default behavior, and bubbling.
+ * events.
+ * @module event-custom
+ * @submodule event-custom-complex
+ */
+
+(function() {
+
+var FACADE, FACADE_KEYS, CEProto = Y.CustomEvent.prototype;
+
+/**
+ * Wraps and protects a custom event for use when emitFacade is set to true.
+ * Requires the event-custom-complex module
+ * @class EventFacade
+ * @param e {Event} the custom event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ */
+
+Y.EventFacade = function(e, currentTarget) {
+
+ e = e || {};
+
+ /**
+ * The arguments passed to fire
+ * @property details
+ * @type Array
+ */
+ this.details = e.details;
+
+ /**
+ * The event type
+ * @property type
+ * @type string
+ */
+ this.type = e.type;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted eventtarget
+ * @propery target
+ * @type Node
+ */
+ this.target = e.target;
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = currentTarget;
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = e.relatedTarget;
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ e.stopPropagation();
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ e.stopImmediatePropagation();
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ */
+ this.preventDefault = function() {
+ e.preventDefault();
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ e.halt(immediate);
+ };
+
+};
+
+CEProto.fireComplex = function(args) {
+ var es = Y.Env._eventstack, ef, q, queue, ce, ret, events;
+
+ if (es) {
+ // queue this event if the current item in the queue bubbles
+ if (this.queuable && this.type != es.next.type) {
+ this.log('queue ' + this.type);
+ es.queue.push([this, args]);
+ return true;
+ }
+ } else {
+ Y.Env._eventstack = {
+ // id of the first event in the stack
+ id: this.id,
+ next: this,
+ silent: this.silent,
+ stopped: 0,
+ prevented: 0,
+ queue: []
+ };
+ es = Y.Env._eventstack;
+ }
+
+ this.stopped = 0;
+ this.prevented = 0;
+ this.target = this.target || this.host;
+
+ events = new Y.EventTarget({
+ fireOnce: true,
+ context: this.host
+ });
+
+ this.events = events;
+
+ if (this.preventedFn) {
+ events.on('prevented', this.preventedFn);
+ }
+
+ if (this.stoppedFn) {
+ events.on('stopped', this.stoppedFn);
+ }
+
+ this.currentTarget = this.host || this.currentTarget;
+
+ this.details = args.slice(); // original arguments in the details
+
+ // this.log("Firing " + this + ", " + "args: " + args);
+ this.log("Firing " + this.type);
+
+ this._facade = null; // kill facade to eliminate stale properties
+
+ ef = this._getFacade(args);
+
+ if (Y.Lang.isObject(args[0])) {
+ args[0] = ef;
+ } else {
+ args.unshift(ef);
+ }
+
+ if (this.hasSubscribers) {
+ this._procSubs(Y.merge(this.subscribers), args, ef);
+ }
+
+ // bubble if this is hosted in an event target and propagation has not been stopped
+ if (this.bubbles && this.host && this.host.bubble && !this.stopped) {
+ es.stopped = 0;
+ es.prevented = 0;
+ ret = this.host.bubble(this);
+
+ this.stopped = Math.max(this.stopped, es.stopped);
+ this.prevented = Math.max(this.prevented, es.prevented);
+
+ }
+
+ // execute the default behavior if not prevented
+ if (this.defaultFn && !this.prevented) {
+ this.defaultFn.apply(this.host || this, args);
+ }
+
+ // broadcast listeners are fired as discreet events on the
+ // YUI instance and potentially the YUI global.
+ this._broadcast(args);
+
+ // process after listeners. If the default behavior was
+ // prevented, the after events don't fire.
+ if (this.hasAfters && !this.prevented && this.stopped < 2) {
+ this._procSubs(Y.merge(this.afters), args, ef);
+ }
+
+ if (es.id === this.id) {
+ queue = es.queue;
+
+ while (queue.length) {
+ q = queue.pop();
+ ce = q[0];
+ es.stopped = 0;
+ es.prevented = 0;
+ // set up stack to allow the next item to be processed
+ es.next = ce;
+ ce.fire.apply(ce, q[1]);
+ }
+
+ Y.Env._eventstack = null;
+ }
+
+ return this.stopped ? false : true;
+};
+
+CEProto._getFacade = function() {
+
+ var ef = this._facade, o, o2,
+ args = this.details;
+
+ if (!ef) {
+ ef = new Y.EventFacade(this, this.currentTarget);
+ }
+
+ // if the first argument is an object literal, apply the
+ // properties to the event facade
+ o = args && args[0];
+
+ if (Y.Lang.isObject(o, true)) {
+
+ o2 = {};
+
+ // protect the event facade properties
+ Y.mix(o2, ef, true, FACADE_KEYS);
+
+ // mix the data
+ Y.mix(ef, o, true);
+
+ // restore ef
+ Y.mix(ef, o2, true, FACADE_KEYS);
+ }
+
+ // update the details field with the arguments
+ // ef.type = this.type;
+ ef.details = this.details;
+ ef.target = this.target;
+ ef.currentTarget = this.currentTarget;
+ ef.stopped = 0;
+ ef.prevented = 0;
+
+ this._facade = ef;
+
+ return this._facade;
+};
+
+/**
+ * Stop propagation to bubble targets
+ * @for CustomEvent
+ * @method stopPropagation
+ */
+CEProto.stopPropagation = function() {
+ this.stopped = 1;
+ Y.Env._eventstack.stopped = 1;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Stops propagation to bubble targets, and prevents any remaining
+ * subscribers on the current target from executing.
+ * @method stopImmediatePropagation
+ */
+CEProto.stopImmediatePropagation = function() {
+ this.stopped = 2;
+ Y.Env._eventstack.stopped = 2;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Prevents the execution of this event's defaultFn
+ * @method preventDefault
+ */
+CEProto.preventDefault = function() {
+ if (this.preventable) {
+ this.prevented = 1;
+ Y.Env._eventstack.prevented = 1;
+ this.events.fire('prevented', this);
+ }
+};
+
+/**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+CEProto.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ this.preventDefault();
+};
+
+/**
+ * Propagate an event. Requires the event-custom-complex module.
+ * @method bubble
+ * @param evt {Event.Custom} the custom event to propagate
+ * @return {boolean} the aggregated return value from Event.Custom.fire
+ * @for EventTarget
+ */
+Y.EventTarget.prototype.bubble = function(evt, args, target) {
+
+ var targs = this._yuievt.targets, ret = true,
+ t, type, ce, i, bc;
+
+ if (!evt || ((!evt.stopped) && targs)) {
+
+ for (i in targs) {
+ if (targs.hasOwnProperty(i)) {
+ t = targs[i];
+ type = evt && evt.type;
+ ce = t.getEvent(type, true);
+
+ // if this event was not published on the bubble target,
+ // publish it with sensible default properties
+ if (!ce) {
+
+ if (t._yuievt.hasTargets) {
+ t.bubble.call(t, evt, args, target);
+ }
+
+ } else {
+ ce.target = target || (evt && evt.target) || this;
+ ce.currentTarget = t;
+
+ bc = ce.broadcast;
+ ce.broadcast = false;
+ ret = ret && ce.fire.apply(ce, args || evt.details);
+ ce.broadcast = bc;
+
+ // stopPropagation() was called
+ if (ce.stopped) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+};
+
+FACADE = new Y.EventFacade();
+FACADE_KEYS = Y.Object.keys(FACADE);
+
+})();
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-custom-base', function(Y) {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ */
+
+Y.Env.evt = {
+ handles: {},
+ plugins: {}
+};
+
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * Allows for the insertion of methods that are executed before or after
+ * a specified method
+ * @class Do
+ * @static
+ */
+
+var BEFORE = 0,
+ AFTER = 1;
+
+Y.Do = {
+
+ /**
+ * Cache of objects touched by the utility
+ * @property objs
+ * @static
+ */
+ objs: {},
+
+ /**
+ * Execute the supplied method before the specified function
+ * @method before
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ before: function(fn, obj, sFn, c) {
+ // Y.log('Do before: ' + sFn, 'info', 'event');
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(BEFORE, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ after: function(fn, obj, sFn, c) {
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(AFTER, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method _inject
+ * @param when {string} before or after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @private
+ * @static
+ */
+ _inject: function(when, fn, obj, sFn) {
+
+ // object id
+ var id = Y.stamp(obj), o, sid;
+
+ if (! this.objs[id]) {
+ // create a map entry for the obj if it doesn't exist
+ this.objs[id] = {};
+ }
+
+ o = this.objs[id];
+
+ if (! o[sFn]) {
+ // create a map entry for the method if it doesn't exist
+ o[sFn] = new Y.Do.Method(obj, sFn);
+
+ // re-route the method to our wrapper
+ obj[sFn] =
+ function() {
+ return o[sFn].exec.apply(o[sFn], arguments);
+ };
+ }
+
+ // subscriber id
+ sid = id + Y.stamp(fn) + sFn;
+
+ // register the callback
+ o[sFn].register(sid, fn, when);
+
+ return new Y.EventHandle(o[sFn], sid);
+
+ },
+
+ /**
+ * Detach a before or after subscription
+ * @method detach
+ * @param handle {string} the subscription handle
+ */
+ detach: function(handle) {
+
+ if (handle.detach) {
+ handle.detach();
+ }
+
+ },
+
+ _unload: function(e, me) {
+
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * Wrapper for a displaced method with aop enabled
+ * @class Do.Method
+ * @constructor
+ * @param obj The object to operate on
+ * @param sFn The name of the method to displace
+ */
+Y.Do.Method = function(obj, sFn) {
+ this.obj = obj;
+ this.methodName = sFn;
+ this.method = obj[sFn];
+ this.before = {};
+ this.after = {};
+};
+
+/**
+ * Register a aop subscriber
+ * @method register
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype.register = function (sid, fn, when) {
+ if (when) {
+ this.after[sid] = fn;
+ } else {
+ this.before[sid] = fn;
+ }
+};
+
+/**
+ * Unregister a aop subscriber
+ * @method delete
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype._delete = function (sid) {
+ // Y.log('Y.Do._delete: ' + sid, 'info', 'Event');
+ delete this.before[sid];
+ delete this.after[sid];
+};
+
+/**
+ * Execute the wrapped method
+ * @method exec
+ */
+Y.Do.Method.prototype.exec = function () {
+
+ var args = Y.Array(arguments, 0, true),
+ i, ret, newRet,
+ bf = this.before,
+ af = this.after,
+ prevented = false;
+
+ // execute before
+ for (i in bf) {
+ if (bf.hasOwnProperty(i)) {
+ ret = bf[i].apply(this.obj, args);
+ if (ret) {
+ switch (ret.constructor) {
+ case Y.Do.Halt:
+ return ret.retVal;
+ case Y.Do.AlterArgs:
+ args = ret.newArgs;
+ break;
+ case Y.Do.Prevent:
+ prevented = true;
+ break;
+ default:
+ }
+ }
+ }
+ }
+
+ // execute method
+ if (!prevented) {
+ ret = this.method.apply(this.obj, args);
+ }
+
+ // execute after methods.
+ for (i in af) {
+ if (af.hasOwnProperty(i)) {
+ newRet = af[i].apply(this.obj, args);
+ // Stop processing if a Halt object is returned
+ if (newRet && newRet.constructor == Y.Do.Halt) {
+ return newRet.retVal;
+ // Check for a new return value
+ } else if (newRet && newRet.constructor == Y.Do.AlterReturn) {
+ ret = newRet.newRetVal;
+ }
+ }
+ }
+
+ return ret;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Return an AlterArgs object when you want to change the arguments that
+ * were passed into the function. An example would be a service that scrubs
+ * out illegal characters prior to executing the core business logic.
+ * @class Do.AlterArgs
+ */
+Y.Do.AlterArgs = function(msg, newArgs) {
+ this.msg = msg;
+ this.newArgs = newArgs;
+};
+
+/**
+ * Return an AlterReturn object when you want to change the result returned
+ * from the core method to the caller
+ * @class Do.AlterReturn
+ */
+Y.Do.AlterReturn = function(msg, newRetVal) {
+ this.msg = msg;
+ this.newRetVal = newRetVal;
+};
+
+/**
+ * Return a Halt object when you want to terminate the execution
+ * of all subsequent subscribers as well as the wrapped method
+ * if it has not exectued yet.
+ * @class Do.Halt
+ */
+Y.Do.Halt = function(msg, retVal) {
+ this.msg = msg;
+ this.retVal = retVal;
+};
+
+/**
+ * Return a Prevent object when you want to prevent the wrapped function
+ * from executing, but want the remaining listeners to execute
+ * @class Do.Prevent
+ */
+Y.Do.Prevent = function(msg) {
+ this.msg = msg;
+};
+
+/**
+ * Return an Error object when you want to terminate the execution
+ * of all subsequent method calls.
+ * @class Do.Error
+ * @deprecated use Y.Do.Halt or Y.Do.Prevent
+ */
+Y.Do.Error = Y.Do.Halt;
+
+//////////////////////////////////////////////////////////////////////////
+
+// Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
+
+})();
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+
+/**
+ * Return value from all subscribe operations
+ * @class EventHandle
+ * @constructor
+ * @param evt {CustomEvent} the custom event
+ * @param sub {Subscriber} the subscriber
+ */
+
+// var onsubscribeType = "_event:onsub",
+var AFTER = 'after',
+ CONFIGS = [
+ 'broadcast',
+ 'bubbles',
+ 'context',
+ 'contextFn',
+ 'currentTarget',
+ 'defaultFn',
+ 'details',
+ 'emitFacade',
+ 'fireOnce',
+ 'host',
+ 'preventable',
+ 'preventedFn',
+ 'queuable',
+ 'silent',
+ 'stoppedFn',
+ 'target',
+ 'type'
+ ],
+
+
+ YUI3_SIGNATURE = 9,
+ YUI_LOG = 'yui:log';
+
+Y.EventHandle = function(evt, sub) {
+
+ /**
+ * The custom event
+ * @type CustomEvent
+ */
+ this.evt = evt;
+
+ /**
+ * The subscriber object
+ * @type Subscriber
+ */
+ this.sub = sub;
+};
+
+Y.EventHandle.prototype = {
+
+ /**
+ * Detaches this subscriber
+ * @method detach
+ */
+ detach: function() {
+ var evt = this.evt, i;
+ if (evt) {
+ // Y.log('EventHandle.detach: ' + this.sub, 'info', 'Event');
+ if (Y.Lang.isArray(evt)) {
+ for (i=0; i<evt.length; i++) {
+ evt[i].detach();
+ }
+ } else {
+ evt._delete(this.sub);
+ }
+ }
+ }
+};
+
+/**
+ * The CustomEvent class lets you define events for your application
+ * that can be subscribed to by one or more independent component.
+ *
+ * @param {String} type The type of event, which is passed to the callback
+ * when the event fires
+ * @param o configuration object
+ * @class CustomEvent
+ * @constructor
+ */
+Y.CustomEvent = function(type, o) {
+
+ // if (arguments.length > 2) {
+// this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
+ // }
+
+ o = o || {};
+
+ this.id = Y.stamp(this);
+
+ /**
+ * The type of event, returned to subscribers when the event fires
+ * @property type
+ * @type string
+ */
+ this.type = type;
+
+ /**
+ * The context the the event will fire from by default. Defaults to the YUI
+ * instance.
+ * @property context
+ * @type object
+ */
+ this.context = Y;
+
+ this.logSystem = (type == YUI_LOG);
+
+ /**
+ * If 0, this event does not broadcast. If 1, the YUI instance is notified
+ * every time this event fires. If 2, the YUI instance and the YUI global
+ * (if event is enabled on the global) are notified every time this event
+ * fires.
+ * @property broadcast
+ * @type int
+ */
+ // this.broadcast = 0;
+
+ /**
+ * By default all custom events are logged in the debug build, set silent
+ * to true to disable debug outpu for this event.
+ * @property silent
+ * @type boolean
+ */
+ this.silent = this.logSystem;
+
+ /**
+ * Specifies whether this event should be queued when the host is actively
+ * processing an event. This will effect exectution order of the callbacks
+ * for the various events.
+ * @property queuable
+ * @type boolean
+ * @default false
+ */
+ // this.queuable = false;
+
+ /**
+ * The subscribers to this event
+ * @property subscribers
+ * @type Subscriber{}
+ */
+ this.subscribers = {};
+
+ /**
+ * 'After' subscribers
+ * @property afters
+ * @type Subscriber{}
+ */
+ this.afters = {};
+
+ /**
+ * This event has fired if true
+ *
+ * @property fired
+ * @type boolean
+ * @default false;
+ */
+ // this.fired = false;
+
+ /**
+ * An array containing the arguments the custom event
+ * was last fired with.
+ * @property firedWith
+ * @type Array
+ */
+ // this.firedWith;
+
+ /**
+ * This event should only fire one time if true, and if
+ * it has fired, any new subscribers should be notified
+ * immediately.
+ *
+ * @property fireOnce
+ * @type boolean
+ * @default false;
+ */
+ // this.fireOnce = false;
+
+ /**
+ * Flag for stopPropagation that is modified during fire()
+ * 1 means to stop propagation to bubble targets. 2 means
+ * to also stop additional subscribers on this target.
+ * @property stopped
+ * @type int
+ */
+ // this.stopped = 0;
+
+ /**
+ * Flag for preventDefault that is modified during fire().
+ * if it is not 0, the default behavior for this event
+ * @property prevented
+ * @type int
+ */
+ // this.prevented = 0;
+
+ /**
+ * Specifies the host for this custom event. This is used
+ * to enable event bubbling
+ * @property host
+ * @type EventTarget
+ */
+ // this.host = null;
+
+ /**
+ * The default function to execute after event listeners
+ * have fire, but only if the default action was not
+ * prevented.
+ * @property defaultFn
+ * @type Function
+ */
+ // this.defaultFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * stopPropagation or stopImmediatePropagation
+ * @property stoppedFn
+ * @type Function
+ */
+ // this.stoppedFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * preventDefault
+ * @property preventedFn
+ * @type Function
+ */
+ // this.preventedFn = null;
+
+ /**
+ * Specifies whether or not this event's default function
+ * can be cancelled by a subscriber by executing preventDefault()
+ * on the event facade
+ * @property preventable
+ * @type boolean
+ * @default true
+ */
+ this.preventable = true;
+
+ /**
+ * Specifies whether or not a subscriber can stop the event propagation
+ * via stopPropagation(), stopImmediatePropagation(), or halt()
+ * @property bubbles
+ * @type boolean
+ * @default true
+ */
+ this.bubbles = true;
+
+ /**
+ * Supports multiple options for listener signatures in order to
+ * port YUI 2 apps.
+ * @property signature
+ * @type int
+ * @default 9
+ */
+ this.signature = YUI3_SIGNATURE;
+
+ // this.hasSubscribers = false;
+
+ // this.hasAfters = false;
+
+ /**
+ * If set to true, the custom event will deliver an EventFacade object
+ * that is similar to a DOM event object.
+ * @property emitFacade
+ * @type boolean
+ * @default false
+ */
+ // this.emitFacade = false;
+
+ this.applyConfig(o, true);
+
+ // this.log("Creating " + this.type);
+
+};
+
+Y.CustomEvent.prototype = {
+
+ /**
+ * Apply configuration properties. Only applies the CONFIG whitelist
+ * @method applyConfig
+ * @param o hash of properties to apply
+ * @param force {boolean} if true, properties that exist on the event
+ * will be overwritten.
+ */
+ applyConfig: function(o, force) {
+ if (o) {
+ Y.mix(this, o, force, CONFIGS);
+ }
+ },
+
+ _on: function(fn, context, args, when) {
+
+ if (!fn) {
+ this.log("Invalid callback for CE: " + this.type);
+ }
+
+ var s = new Y.Subscriber(fn, context, args, when);
+
+ if (this.fireOnce && this.fired) {
+ Y.later(0, this, Y.bind(this._notify, this, s, this.firedWith));
+ }
+
+ if (when == AFTER) {
+ this.afters[s.id] = s;
+ this.hasAfters = true;
+ } else {
+ this.subscribers[s.id] = s;
+ this.hasSubscribers = true;
+ }
+
+ return new Y.EventHandle(this, s);
+
+ },
+
+ /**
+ * Listen for this event
+ * @method subscribe
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ * @deprecated use on
+ */
+ subscribe: function(fn, context) {
+ Y.log('ce.subscribe deprecated, use "on"', 'warn', 'deprecated');
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event
+ * @method on
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ on: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event after the normal subscribers have been notified and
+ * the default behavior has been applied. If a normal subscriber prevents the
+ * default behavior, it also prevents after listeners from firing.
+ * @method after
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ after: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, AFTER);
+ },
+
+ /**
+ * Detach listeners.
+ * @method detach
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {int|EventTarget} returns a chainable event target
+ * or the number of subscribers unsubscribed.
+ */
+ detach: function(fn, context) {
+ // unsubscribe handle
+ if (fn && fn.detach) {
+ return fn.detach();
+ }
+
+ var found = 0, subs = this.subscribers, i, s;
+
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && (!fn || fn === s.fn)) {
+ this._delete(s);
+ found++;
+ }
+ }
+ }
+
+ return found;
+ },
+
+ /**
+ * Detach listeners.
+ * @method unsubscribe
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {boolean|EventTarget} returns a chainable event target
+ * or a boolean for legacy detach support.
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+
+ /**
+ * Notify a single subscriber
+ * @method _notify
+ * @param s {Subscriber} the subscriber
+ * @param args {Array} the arguments array to apply to the listener
+ * @private
+ */
+ _notify: function(s, args, ef) {
+
+ this.log(this.type + "->" + "sub: " + s.id);
+
+ var ret;
+
+ ret = s.notify(args, this);
+
+ if (false === ret || this.stopped > 1) {
+ this.log(this.type + " cancelled by subscriber");
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * Logger abstraction to centralize the application of the silent flag
+ * @method log
+ * @param msg {string} message to log
+ * @param cat {string} log category
+ */
+ log: function(msg, cat) {
+ if (!this.silent) {
+ Y.log(this.id + ': ' + msg, cat || "info", "event");
+ }
+ },
+
+ /**
+ * Notifies the subscribers. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters:
+ * <ul>
+ * <li>The type of event</li>
+ * <li>All of the arguments fire() was executed with as an array</li>
+ * <li>The custom object (if any) that was passed into the subscribe()
+ * method</li>
+ * </ul>
+ * @method fire
+ * @param {Object*} arguments an arbitrary set of parameters to pass to
+ * the handler.
+ * @return {boolean} false if one of the subscribers returned false,
+ * true otherwise
+ *
+ */
+ fire: function() {
+ if (this.fireOnce && this.fired) {
+ this.log('fireOnce event: ' + this.type + ' already fired');
+ return true;
+ } else {
+
+ var args = Y.Array(arguments, 0, true);
+
+ this.fired = true;
+ this.firedWith = args;
+
+ if (this.emitFacade) {
+ return this.fireComplex(args);
+ } else {
+ return this.fireSimple(args);
+ }
+ }
+ },
+
+ fireSimple: function(args) {
+ if (this.hasSubscribers || this.hasAfters) {
+ this._procSubs(Y.merge(this.subscribers, this.afters), args);
+ }
+ this._broadcast(args);
+ return this.stopped ? false : true;
+ },
+
+ // Requires the event-custom-complex module for full funcitonality.
+ fireComplex: function(args) {
+ args[0] = args[0] || {};
+ return this.fireSimple(args);
+ },
+
+ _procSubs: function(subs, args, ef) {
+ var s, i;
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && s.fn) {
+ if (false === this._notify(s, args, ef)) {
+ this.stopped = 2;
+ }
+ if (this.stopped == 2) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ },
+
+ _broadcast: function(args) {
+ if (!this.stopped && this.broadcast) {
+
+ var a = Y.Array(args);
+ a.unshift(this.type);
+
+ if (this.host !== Y) {
+ Y.fire.apply(Y, a);
+ }
+
+ if (this.broadcast == 2) {
+ Y.Global.fire.apply(Y.Global, a);
+ }
+ }
+ },
+
+ /**
+ * Removes all listeners
+ * @method unsubscribeAll
+ * @return {int} The number of listeners unsubscribed
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners
+ * @method detachAll
+ * @return {int} The number of listeners unsubscribed
+ */
+ detachAll: function() {
+ return this.detach();
+ },
+
+ /**
+ * @method _delete
+ * @param subscriber object
+ * @private
+ */
+ _delete: function(s) {
+ if (s) {
+ delete s.fn;
+ delete s.context;
+ delete this.subscribers[s.id];
+ delete this.afters[s.id];
+ }
+ }
+};
+
+/////////////////////////////////////////////////////////////////////
+
+/**
+ * Stores the subscriber information to be used when the event fires.
+ * @param {Function} fn The wrapped function to execute
+ * @param {Object} context The value of the keyword 'this' in the listener
+ * @param {Array} args* 0..n additional arguments to supply the listener
+ *
+ * @class Subscriber
+ * @constructor
+ */
+Y.Subscriber = function(fn, context, args) {
+
+ /**
+ * The callback that will be execute when the event fires
+ * This is wrapped by Y.rbind if obj was supplied.
+ * @property fn
+ * @type Function
+ */
+ this.fn = fn;
+
+ /**
+ * Optional 'this' keyword for the listener
+ * @property context
+ * @type Object
+ */
+ this.context = context;
+
+ /**
+ * Unique subscriber id
+ * @property id
+ * @type String
+ */
+ this.id = Y.stamp(this);
+
+ /**
+ * Additional arguments to propagate to the subscriber
+ * @property args
+ * @type Array
+ */
+ this.args = args;
+
+ /**
+ * Custom events for a given fire transaction.
+ * @property events
+ * @type {EventTarget}
+ */
+ this.events = null;
+
+};
+
+Y.Subscriber.prototype = {
+
+ _notify: function(c, args, ce) {
+ var a = this.args, ret;
+ switch (ce.signature) {
+ case 0:
+ ret = this.fn.call(c, ce.type, args, c);
+ break;
+ case 1:
+ ret = this.fn.call(c, args[0] || null, c);
+ break;
+ default:
+ if (a || args) {
+ args = args || [];
+ a = (a) ? args.concat(a) : args;
+ ret = this.fn.apply(c, a);
+ } else {
+ ret = this.fn.call(c);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Executes the subscriber.
+ * @method notify
+ * @param args {Array} Arguments array for the subscriber
+ * @param ce {CustomEvent} The custom event that sent the notification
+ */
+ notify: function(args, ce) {
+ var c = this.context,
+ ret = true;
+
+ if (!c) {
+ c = (ce.contextFn) ? ce.contextFn() : ce.context;
+ }
+
+ // only catch errors if we will not re-throw them.
+ if (Y.config.throwFail) {
+ ret = this._notify(c, args, ce);
+ } else {
+ try {
+ ret = this._notify(c, args, ce);
+ } catch(e) {
+ Y.error(this + ' failed: ' + e.message, e);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Returns true if the fn and obj match this objects properties.
+ * Used by the unsubscribe method to match the right subscriber.
+ *
+ * @method contains
+ * @param {Function} fn the function to execute
+ * @param {Object} context optional 'this' keyword for the listener
+ * @return {boolean} true if the supplied arguments match this
+ * subscriber's signature.
+ */
+ contains: function(fn, context) {
+ if (context) {
+ return ((this.fn == fn) && this.context == context);
+ } else {
+ return (this.fn == fn);
+ }
+ }
+
+};
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * EventTarget provides the implementation for any object to
+ * publish, subscribe and fire to custom events, and also
+ * alows other EventTargets to target the object with events
+ * sourced from the other object.
+ * EventTarget is designed to be used with Y.augment to wrap
+ * EventCustom in an interface that allows events to be listened to
+ * and fired by name. This makes it possible for implementing code to
+ * subscribe to an event that either has not been created yet, or will
+ * not be created at all.
+ * @class EventTarget
+ * @param opts a configuration object
+ * @config emitFacade {boolean} if true, all events will emit event
+ * facade payloads by default (default false)
+ * @config prefix {string} the prefix to apply to non-prefixed event names
+ * @config chain {boolean} if true, on/after/detach return the host to allow
+ * chaining, otherwise they return an EventHandle (default false)
+ */
+
+var L = Y.Lang,
+ PREFIX_DELIMITER = ':',
+ CATEGORY_DELIMITER = '|',
+ AFTER_PREFIX = '~AFTER~',
+
+ /**
+ * If the instance has a prefix attribute and the
+ * event type is not prefixed, the instance prefix is
+ * applied to the supplied type.
+ * @method _getType
+ * @private
+ */
+ _getType = Y.cached(function(type, pre) {
+
+ if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
+ return type;
+ }
+
+ return pre + PREFIX_DELIMITER + type;
+ }),
+
+ /**
+ * Returns an array with the detach key (if provided),
+ * and the prefixed event name from _getType
+ * Y.on('detachcategory, menu:click', fn)
+ * @method _parseType
+ * @private
+ */
+ _parseType = Y.cached(function(type, pre) {
+
+ var t = type, detachcategory, after, i;
+
+ if (!L.isString(t)) {
+ return t;
+ }
+
+ i = t.indexOf(AFTER_PREFIX);
+
+ if (i > -1) {
+ after = true;
+ t = t.substr(AFTER_PREFIX.length);
+ // Y.log(t);
+ }
+
+ i = t.indexOf(CATEGORY_DELIMITER);
+
+ if (i > -1) {
+ detachcategory = t.substr(0, (i));
+ t = t.substr(i+1);
+ if (t == '*') {
+ t = null;
+ }
+ }
+
+ // detach category, full type with instance prefix, is this an after listener, short type
+ return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
+ }),
+
+ ET = function(opts) {
+
+ // Y.log('EventTarget constructor executed: ' + this._yuid);
+
+ var o = (L.isObject(opts)) ? opts : {};
+
+ this._yuievt = this._yuievt || {
+
+ id: Y.guid(),
+
+ events: {},
+
+ targets: {},
+
+ config: o,
+
+ chain: ('chain' in o) ? o.chain : Y.config.chain,
+
+ defaults: {
+ context: o.context || this,
+ host: this,
+ emitFacade: o.emitFacade,
+ fireOnce: o.fireOnce,
+ queuable: o.queuable,
+ broadcast: o.broadcast,
+ bubbles: ('bubbles' in o) ? o.bubbles : true
+ }
+ };
+
+ };
+
+
+ET.prototype = {
+
+ /**
+ * Subscribe to a custom event hosted by this object
+ * @method on
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ on: function(type, fn, context, x) {
+
+ var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
+ detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
+ Node = Y.Node, n, domevent;
+
+ if (L.isObject(type)) {
+
+ if (L.isFunction(type)) {
+ return Y.Do.before.apply(Y.Do, arguments);
+ }
+
+ f = fn;
+ c = context;
+ args = Y.Array(arguments, 0, true);
+ ret = {};
+ after = type._after;
+ delete type._after;
+
+ Y.each(type, function(v, k) {
+
+ if (v) {
+ f = v.fn || ((Y.Lang.isFunction(v)) ? v : f);
+ c = v.context || c;
+ }
+
+ args[0] = (after) ? AFTER_PREFIX + k : k;
+ args[1] = f;
+ args[2] = c;
+
+ ret[k] = this.on.apply(this, args);
+
+ }, this);
+
+ return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
+
+ }
+
+ detachcategory = parts[0];
+ after = parts[2];
+ shorttype = parts[3];
+
+ // extra redirection so we catch adaptor events too. take a look at this.
+ if (Node && (this instanceof Node) && (shorttype in Node.DOM_EVENTS)) {
+ args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, Node.getDOMNode(this));
+ // Y.log("Node detected, redirecting with these args: " + args);
+ return Y.on.apply(Y, args);
+ }
+
+ type = parts[1];
+
+ if (this instanceof YUI) {
+
+ adapt = Y.Env.evt.plugins[type];
+ args = Y.Array(arguments, 0, true);
+ args[0] = shorttype;
+
+ if (Node) {
+ n = args[2];
+
+ if (n instanceof Y.NodeList) {
+ n = Y.NodeList.getDOMNodes(n);
+ } else if (n instanceof Node) {
+ n = Node.getDOMNode(n);
+ }
+
+ domevent = (shorttype in Node.DOM_EVENTS);
+
+ // Captures both DOM events and event plugins.
+ if (domevent) {
+ args[2] = n;
+ }
+ }
+
+ // check for the existance of an event adaptor
+ if (adapt) {
+ Y.log('Using adaptor for ' + shorttype + ', ' + n, 'info', 'event');
+ handle = adapt.on.apply(Y, args);
+ } else if ((!type) || domevent) {
+ handle = Y.Event._attach(args);
+ }
+
+ }
+
+ if (!handle) {
+ ce = this._yuievt.events[type] || this.publish(type);
+ handle = ce._on(fn, context, (arguments.length > 3) ? Y.Array(arguments, 3, true) : null, (after) ? 'after' : true);
+ }
+
+ if (detachcategory) {
+ store[detachcategory] = store[detachcategory] || {};
+ store[detachcategory][type] = store[detachcategory][type] || [];
+ store[detachcategory][type].push(handle);
+ }
+
+ return (this._yuievt.chain) ? this : handle;
+
+ },
+
+ /**
+ * subscribe to an event
+ * @method subscribe
+ * @deprecated use on
+ */
+ subscribe: function() {
+ Y.log('EventTarget subscribe() is deprecated, use on()', 'warn', 'deprecated');
+ return this.on.apply(this, arguments);
+ },
+
+ /**
+ * Detach one or more listeners the from the specified event
+ * @method detach
+ * @param type {string|Object} Either the handle to the subscriber or the
+ * type of event. If the type
+ * is not specified, it will attempt to remove
+ * the listener from all hosted events.
+ * @param fn {Function} The subscribed function to unsubscribe, if not
+ * supplied, all subscribers will be removed.
+ * @param context {Object} The custom object passed to subscribe. This is
+ * optional, but if supplied will be used to
+ * disambiguate multiple listeners that are the same
+ * (e.g., you subscribe many object using a function
+ * that lives on the prototype)
+ * @return {EventTarget} the host
+ */
+ detach: function(type, fn, context) {
+ var evts = this._yuievt.events, i, ret,
+ Node = Y.Node, isNode = (this instanceof Node);
+
+ // detachAll disabled on the Y instance.
+ if (!type && (this !== Y)) {
+ for (i in evts) {
+ if (evts.hasOwnProperty(i)) {
+ ret = evts[i].detach(fn, context);
+ }
+ }
+ if (isNode) {
+
+ Y.Event.purgeElement(Node.getDOMNode(this));
+ }
+
+ return ret;
+ }
+
+ var parts = _parseType(type, this._yuievt.config.prefix),
+ detachcategory = L.isArray(parts) ? parts[0] : null,
+ shorttype = (parts) ? parts[3] : null,
+ handle, adapt, store = Y.Env.evt.handles, cat, args,
+ ce,
+
+ keyDetacher = function(lcat, ltype) {
+ var handles = lcat[ltype];
+ if (handles) {
+ while (handles.length) {
+ handle = handles.pop();
+ handle.detach();
+ }
+ }
+ };
+
+ if (detachcategory) {
+
+ cat = store[detachcategory];
+ type = parts[1];
+
+ if (cat) {
+ if (type) {
+ keyDetacher(cat, type);
+ } else {
+ for (i in cat) {
+ if (cat.hasOwnProperty(i)) {
+ keyDetacher(cat, i);
+ }
+ }
+ }
+
+ return (this._yuievt.chain) ? this : true;
+ }
+
+ // If this is an event handle, use it to detach
+ } else if (L.isObject(type) && type.detach) {
+ ret = type.detach();
+ return (this._yuievt.chain) ? this : ret;
+ // extra redirection so we catch adaptor events too. take a look at this.
+ } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
+ args = Y.Array(arguments, 0, true);
+ args[2] = Node.getDOMNode(this);
+ return Y.detach.apply(Y, args);
+ }
+
+ adapt = Y.Env.evt.plugins[shorttype];
+
+ // The YUI instance handles DOM events and adaptors
+ if (this instanceof YUI) {
+ args = Y.Array(arguments, 0, true);
+ // use the adaptor specific detach code if
+ if (adapt && adapt.detach) {
+ return adapt.detach.apply(Y, args);
+ // DOM event fork
+ } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
+ args[0] = type;
+ return Y.Event.detach.apply(Y.Event, args);
+ }
+ }
+
+ ce = evts[type];
+ if (ce) {
+ ret = ce.detach(fn, context);
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * detach a listener
+ * @method unsubscribe
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method detachAll
+ * @param type {string} The type, or name of the event
+ */
+ detachAll: function(type) {
+ return this.detach(type);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method unsubscribeAll
+ * @param type {string} The type, or name of the event
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Creates a new custom event of the specified type. If a custom event
+ * by that name already exists, it will not be re-created. In either
+ * case the custom event is returned.
+ *
+ * @method publish
+ *
+ * @param type {string} the type, or name of the event
+ * @param opts {object} optional config params. Valid properties are:
+ *
+ * <ul>
+ * <li>
+ * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
+ * </li>
+ * <li>
+ * 'bubbles': whether or not this event bubbles (true)
+ * </li>
+ * <li>
+ * 'context': the default execution context for the listeners (this)
+ * </li>
+ * <li>
+ * 'defaultFn': the default function to execute when this event fires if preventDefault was not called
+ * </li>
+ * <li>
+ * 'emitFacade': whether or not this event emits a facade (false)
+ * </li>
+ * <li>
+ * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
+ * </li>
+ * <li>
+ * 'fireOnce': if an event is configured to fire once, new subscribers after
+ * the fire will be notified immediately.
+ * </li>
+ * <li>
+ * 'preventable': whether or not preventDefault() has an effect (true)
+ * </li>
+ * <li>
+ * 'preventedFn': a function that is executed when preventDefault is called
+ * </li>
+ * <li>
+ * 'queuable': whether or not this event can be queued during bubbling (false)
+ * </li>
+ * <li>
+ * 'silent': if silent is true, debug messages are not provided for this event.
+ * </li>
+ * <li>
+ * 'stoppedFn': a function that is executed when stopPropagation is called
+ * </li>
+ * <li>
+ * 'type': the event type (valid option if not provided as the first parameter to publish)
+ * </li>
+ * </ul>
+ *
+ * @return {Event.Custom} the custom event
+ *
+ */
+ publish: function(type, opts) {
+ var events, ce, ret, pre = this._yuievt.config.prefix;
+
+ type = (pre) ? _getType(type, pre) : type;
+
+
+ if (L.isObject(type)) {
+ ret = {};
+ Y.each(type, function(v, k) {
+ ret[k] = this.publish(k, v || opts);
+ }, this);
+
+ return ret;
+ }
+
+ events = this._yuievt.events;
+ ce = events[type];
+
+ if (ce) {
+// ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
+ if (opts) {
+ ce.applyConfig(opts, true);
+ }
+ } else {
+ // apply defaults
+ ce = new Y.CustomEvent(type, (opts) ? Y.mix(opts, this._yuievt.defaults) : this._yuievt.defaults);
+ events[type] = ce;
+ }
+
+ // make sure we turn the broadcast flag off if this
+ // event was published as a result of bubbling
+ // if (opts instanceof Y.CustomEvent) {
+ // events[type].broadcast = false;
+ // }
+
+ return events[type];
+ },
+
+ /**
+ * Registers another EventTarget as a bubble target. Bubble order
+ * is determined by the order registered. Multiple targets can
+ * be specified.
+ * @method addTarget
+ * @param o {EventTarget} the target to add
+ */
+ addTarget: function(o) {
+ this._yuievt.targets[Y.stamp(o)] = o;
+ this._yuievt.hasTargets = true;
+ },
+
+ /**
+ * Removes a bubble target
+ * @method removeTarget
+ * @param o {EventTarget} the target to remove
+ */
+ removeTarget: function(o) {
+ delete this._yuievt.targets[Y.stamp(o)];
+ },
+
+ /**
+ * Fire a custom event by name. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters.
+ *
+ * If the custom event object hasn't been created, then the event hasn't
+ * been published and it has no subscribers. For performance sake, we
+ * immediate exit in this case. This means the event won't bubble, so
+ * if the intention is that a bubble target be notified, the event must
+ * be published on this object first.
+ *
+ * The first argument is the event type, and any additional arguments are
+ * passed to the listeners as parameters. If the first of these is an
+ * object literal, and the event is configured to emit an event facade,
+ * that object is mixed into the event facade and the facade is provided
+ * in place of the original object.
+ *
+ * @method fire
+ * @param type {String|Object} The type of the event, or an object that contains
+ * a 'type' property.
+ * @param arguments {Object*} an arbitrary set of parameters to pass to
+ * the handler. If the first of these is an object literal and the event is
+ * configured to emit an event facade, the event facade will replace that
+ * parameter after the properties the object literal contains are copied to
+ * the event facade.
+ * @return {Event.Target} the event host
+ *
+ */
+ fire: function(type) {
+
+ var typeIncluded = L.isString(type),
+ t = (typeIncluded) ? type : (type && type.type),
+ ce, a, ret, pre=this._yuievt.config.prefix;
+
+ t = (pre) ? _getType(t, pre) : t;
+ ce = this.getEvent(t, true);
+
+ // this event has not been published or subscribed to
+ if (!ce) {
+
+ if (this._yuievt.hasTargets) {
+ a = (typeIncluded) ? arguments : Y.Array(arguments, 0, true).unshift(t);
+ return this.bubble(null, a, this);
+ }
+
+ // otherwise there is nothing to be done
+ ret = true;
+
+ } else {
+
+ a = Y.Array(arguments, (typeIncluded) ? 1 : 0, true);
+ ret = ce.fire.apply(ce, a);
+
+ // clear target for next fire()
+ ce.target = null;
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * Returns the custom event of the provided type has been created, a
+ * falsy value otherwise
+ * @method getEvent
+ * @param type {string} the type, or name of the event
+ * @param prefixed {string} if true, the type is prefixed already
+ * @return {Event.Custom} the custom event or null
+ */
+ getEvent: function(type, prefixed) {
+ var pre, e;
+ if (!prefixed) {
+ pre = this._yuievt.config.prefix;
+ type = (pre) ? _getType(type, pre) : type;
+ }
+ e = this._yuievt.events;
+ return (e && type in e) ? e[type] : null;
+ },
+
+ /**
+ * Subscribe to a custom event hosted by this object. The
+ * supplied callback will execute after any listeners add
+ * via the subscribe method, and after the default function,
+ * if configured for the event, has executed.
+ * @method after
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ after: function(type, fn) {
+
+ var a = Y.Array(arguments, 0, true);
+
+ switch (L.type(type)) {
+ case 'function':
+ return Y.Do.after.apply(Y.Do, arguments);
+ case 'object':
+ a[0]._after = true;
+ break;
+ default:
+ a[0] = AFTER_PREFIX + type;
+ }
+
+ return this.on.apply(this, a);
+
+ },
+
+ /**
+ * Executes the callback before a DOM event, custom event
+ * or method. If the first argument is a function, it
+ * is assumed the target is a method. For DOM and custom
+ * events, this is an alias for Y.on.
+ *
+ * For DOM and custom events:
+ * type, callback, context, 0-n arguments
+ *
+ * For methods:
+ * callback, object (method host), methodName, context, 0-n arguments
+ *
+ * @method before
+ * @return detach handle
+ * @deprecated use the on method
+ */
+ before: function() {
+ return this.on.apply(this, arguments);
+ }
+
+};
+
+Y.EventTarget = ET;
+
+// make Y an event target
+Y.mix(Y, ET.prototype, false, false, {
+ bubbles: false
+});
+
+ET.call(Y);
+
+YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
+
+/**
+ * Hosts YUI page level events. This is where events bubble to
+ * when the broadcast config is set to 2. This property is
+ * only available if the custom event module is loaded.
+ * @property Global
+ * @type EventTarget
+ * @for YUI
+ */
+Y.Global = YUI.Env.globalEvents;
+
+// @TODO implement a global namespace function on Y.Global?
+
+})();
+
+
+/**
+ * <code>YUI</code>'s <code>on</code> method is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events, DOM events, and
+ * function events. <code>detach</code> is also provided to remove listeners
+ * serviced by this function.
+ *
+ * The signature that <code>on</code> accepts varies depending on the type
+ * of event being consumed. Refer to the specific methods that will
+ * service a specific request for additional information about subscribing
+ * to that type of event.
+ *
+ * <ul>
+ * <li>Custom events. These events are defined by various
+ * modules in the library. This type of event is delegated to
+ * <code>EventTarget</code>'s <code>on</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('domready', function() { // start work });</code>
+ * </li>
+ * <li>DOM events. These are moments reported by the browser related
+ * to browser functionality and user interaction.
+ * This type of event is delegated to <code>Event</code>'s
+ * <code>attach</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>The specification for the Node(s) to attach the listener
+ * to. This can be a selector, collections, or Node/Element
+ * refereces.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('click', function(e) { // something was clicked }, '#someelement');</code>
+ * </li>
+ * <li>Function events. These events can be used to react before or after a
+ * function is executed. This type of event is delegated to <code>Event.Do</code>'s
+ * <code>before</code> method.
+ * <ul>
+ * <li>The callback to execute</li>
+ * <li>The object that has the function that will be listened for.</li>
+ * <li>The name of the function to listen for.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example <code>Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');</code>
+ * </li>
+ * </ul>
+ *
+ * <code>on</code> corresponds to the moment before any default behavior of
+ * the event. <code>after</code> works the same way, but these listeners
+ * execute after the event's default behavior. <code>before</code> is an
+ * alias for <code>on</code>.
+ *
+ * @method on
+ * @param type** event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target** a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra** 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+/**
+ * after() is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events,
+ * DOM events, and AOP events. This works the same way as
+ * the on() function, only it operates after any default
+ * behavior for the event has executed. @see <code>on</code> for more
+ * information.
+ * @method after
+ * @param type event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+
+}, '3.0.0' ,{requires:['oop']});
+YUI.add('event-custom-complex', function(Y) {
+
+
+/**
+ * Adds event facades, preventable default behavior, and bubbling.
+ * events.
+ * @module event-custom
+ * @submodule event-custom-complex
+ */
+
+(function() {
+
+var FACADE, FACADE_KEYS, CEProto = Y.CustomEvent.prototype;
+
+/**
+ * Wraps and protects a custom event for use when emitFacade is set to true.
+ * Requires the event-custom-complex module
+ * @class EventFacade
+ * @param e {Event} the custom event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ */
+
+Y.EventFacade = function(e, currentTarget) {
+
+ e = e || {};
+
+ /**
+ * The arguments passed to fire
+ * @property details
+ * @type Array
+ */
+ this.details = e.details;
+
+ /**
+ * The event type
+ * @property type
+ * @type string
+ */
+ this.type = e.type;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted eventtarget
+ * @propery target
+ * @type Node
+ */
+ this.target = e.target;
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = currentTarget;
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = e.relatedTarget;
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ e.stopPropagation();
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ e.stopImmediatePropagation();
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ */
+ this.preventDefault = function() {
+ e.preventDefault();
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ e.halt(immediate);
+ };
+
+};
+
+CEProto.fireComplex = function(args) {
+ var es = Y.Env._eventstack, ef, q, queue, ce, ret, events;
+
+ if (es) {
+ // queue this event if the current item in the queue bubbles
+ if (this.queuable && this.type != es.next.type) {
+ this.log('queue ' + this.type);
+ es.queue.push([this, args]);
+ return true;
+ }
+ } else {
+ Y.Env._eventstack = {
+ // id of the first event in the stack
+ id: this.id,
+ next: this,
+ silent: this.silent,
+ stopped: 0,
+ prevented: 0,
+ queue: []
+ };
+ es = Y.Env._eventstack;
+ }
+
+ this.stopped = 0;
+ this.prevented = 0;
+ this.target = this.target || this.host;
+
+ events = new Y.EventTarget({
+ fireOnce: true,
+ context: this.host
+ });
+
+ this.events = events;
+
+ if (this.preventedFn) {
+ events.on('prevented', this.preventedFn);
+ }
+
+ if (this.stoppedFn) {
+ events.on('stopped', this.stoppedFn);
+ }
+
+ this.currentTarget = this.host || this.currentTarget;
+
+ this.details = args.slice(); // original arguments in the details
+
+ // this.log("Firing " + this + ", " + "args: " + args);
+ this.log("Firing " + this.type);
+
+ this._facade = null; // kill facade to eliminate stale properties
+
+ ef = this._getFacade(args);
+
+ if (Y.Lang.isObject(args[0])) {
+ args[0] = ef;
+ } else {
+ args.unshift(ef);
+ }
+
+ if (this.hasSubscribers) {
+ this._procSubs(Y.merge(this.subscribers), args, ef);
+ }
+
+ // bubble if this is hosted in an event target and propagation has not been stopped
+ if (this.bubbles && this.host && this.host.bubble && !this.stopped) {
+ es.stopped = 0;
+ es.prevented = 0;
+ ret = this.host.bubble(this);
+
+ this.stopped = Math.max(this.stopped, es.stopped);
+ this.prevented = Math.max(this.prevented, es.prevented);
+
+ }
+
+ // execute the default behavior if not prevented
+ if (this.defaultFn && !this.prevented) {
+ this.defaultFn.apply(this.host || this, args);
+ }
+
+ // broadcast listeners are fired as discreet events on the
+ // YUI instance and potentially the YUI global.
+ this._broadcast(args);
+
+ // process after listeners. If the default behavior was
+ // prevented, the after events don't fire.
+ if (this.hasAfters && !this.prevented && this.stopped < 2) {
+ this._procSubs(Y.merge(this.afters), args, ef);
+ }
+
+ if (es.id === this.id) {
+ queue = es.queue;
+
+ while (queue.length) {
+ q = queue.pop();
+ ce = q[0];
+ es.stopped = 0;
+ es.prevented = 0;
+ // set up stack to allow the next item to be processed
+ es.next = ce;
+ ce.fire.apply(ce, q[1]);
+ }
+
+ Y.Env._eventstack = null;
+ }
+
+ return this.stopped ? false : true;
+};
+
+CEProto._getFacade = function() {
+
+ var ef = this._facade, o, o2,
+ args = this.details;
+
+ if (!ef) {
+ ef = new Y.EventFacade(this, this.currentTarget);
+ }
+
+ // if the first argument is an object literal, apply the
+ // properties to the event facade
+ o = args && args[0];
+
+ if (Y.Lang.isObject(o, true)) {
+
+ o2 = {};
+
+ // protect the event facade properties
+ Y.mix(o2, ef, true, FACADE_KEYS);
+
+ // mix the data
+ Y.mix(ef, o, true);
+
+ // restore ef
+ Y.mix(ef, o2, true, FACADE_KEYS);
+ }
+
+ // update the details field with the arguments
+ // ef.type = this.type;
+ ef.details = this.details;
+ ef.target = this.target;
+ ef.currentTarget = this.currentTarget;
+ ef.stopped = 0;
+ ef.prevented = 0;
+
+ this._facade = ef;
+
+ return this._facade;
+};
+
+/**
+ * Stop propagation to bubble targets
+ * @for CustomEvent
+ * @method stopPropagation
+ */
+CEProto.stopPropagation = function() {
+ this.stopped = 1;
+ Y.Env._eventstack.stopped = 1;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Stops propagation to bubble targets, and prevents any remaining
+ * subscribers on the current target from executing.
+ * @method stopImmediatePropagation
+ */
+CEProto.stopImmediatePropagation = function() {
+ this.stopped = 2;
+ Y.Env._eventstack.stopped = 2;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Prevents the execution of this event's defaultFn
+ * @method preventDefault
+ */
+CEProto.preventDefault = function() {
+ if (this.preventable) {
+ this.prevented = 1;
+ Y.Env._eventstack.prevented = 1;
+ this.events.fire('prevented', this);
+ }
+};
+
+/**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+CEProto.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ this.preventDefault();
+};
+
+/**
+ * Propagate an event. Requires the event-custom-complex module.
+ * @method bubble
+ * @param evt {Event.Custom} the custom event to propagate
+ * @return {boolean} the aggregated return value from Event.Custom.fire
+ * @for EventTarget
+ */
+Y.EventTarget.prototype.bubble = function(evt, args, target) {
+
+ var targs = this._yuievt.targets, ret = true,
+ t, type, ce, i, bc;
+
+ if (!evt || ((!evt.stopped) && targs)) {
+
+ // Y.log('Bubbling ' + evt.type);
+ for (i in targs) {
+ if (targs.hasOwnProperty(i)) {
+ t = targs[i];
+ type = evt && evt.type;
+ ce = t.getEvent(type, true);
+
+ // if this event was not published on the bubble target,
+ // publish it with sensible default properties
+ if (!ce) {
+
+ if (t._yuievt.hasTargets) {
+ t.bubble.call(t, evt, args, target);
+ }
+
+ } else {
+ ce.target = target || (evt && evt.target) || this;
+ ce.currentTarget = t;
+
+ bc = ce.broadcast;
+ ce.broadcast = false;
+ ret = ret && ce.fire.apply(ce, args || evt.details);
+ ce.broadcast = bc;
+
+ // stopPropagation() was called
+ if (ce.stopped) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+};
+
+FACADE = new Y.EventFacade();
+FACADE_KEYS = Y.Object.keys(FACADE);
+
+})();
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
+
+
+YUI.add('event-custom', function(Y){}, '3.0.0' ,{use:['event-custom-base', 'event-custom-complex']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-custom-base",function(E){E.Env.evt={handles:{},plugins:{}};(function(){var F=0,G=1;E.Do={objs:{},before:function(I,K,L,M){var J=I,H;if(M){H=[I,M].concat(E.Array(arguments,4,true));J=E.rbind.apply(E,H);}return this._inject(F,J,K,L);},after:function(I,K,L,M){var J=I,H;if(M){H=[I,M].concat(E.Array(arguments,4,true));J=E.rbind.apply(E,H);}return this._inject(G,J,K,L);},_inject:function(H,J,K,M){var N=E.stamp(K),L,I;if(!this.objs[N]){this.objs[N]={};}L=this.objs[N];if(!L[M]){L[M]=new E.Do.Method(K,M);K[M]=function(){return L[M].exec.apply(L[M],arguments);};}I=N+E.stamp(J)+M;L[M].register(I,J,H);return new E.EventHandle(L[M],I);},detach:function(H){if(H.detach){H.detach();}},_unload:function(I,H){}};E.Do.Method=function(H,I){this.obj=H;this.methodName=I;this.method=H[I];this.before={};this.after={};};E.Do.Method.prototype.register=function(I,J,H){if(H){this.after[I]=J;}else{this.before[I]=J;}};E.Do.Method.prototype._delete=function(H){delete this.before[H];delete this.after[H];};E.Do.Method.prototype.exec=function(){var J=E.Array(arguments,0,true),K,I,N,L=this.before,H=this.after,M=false;for(K in L){if(L.hasOwnProperty(K)){I=L[K].apply(this.obj,J);if(I){switch(I.constructor){case E.Do.Halt:return I.retVal;case E.Do.AlterArgs:J=I.newArgs;break;case E.Do.Prevent:M=true;break;default:}}}}if(!M){I=this.method.apply(this.obj,J);}for(K in H){if(H.hasOwnProperty(K)){N=H[K].apply(this.obj,J);if(N&&N.constructor==E.Do.Halt){return N.retVal;}else{if(N&&N.constructor==E.Do.AlterReturn){I=N.newRetVal;}}}}return I;};E.Do.AlterArgs=function(I,H){this.msg=I;this.newArgs=H;};E.Do.AlterReturn=function(I,H){this.msg=I;this.newRetVal=H;};E.Do.Halt=function(I,H){this.msg=I;this.retVal=H;};E.Do.Prevent=function(H){this.msg=H;};E.Do.Error=E.Do.Halt;})();var D="after",B=["broadcast","bubbles","context","contextFn","currentTarget","defaultFn","details","emitFacade","fireOnce","host","preventable","preventedFn","queuable","silent","stoppedFn","target","type"],C=9,A="yui:log";E.EventHandle=function(F,G){this.evt=F;this.sub=G;};E.EventHandle.prototype={detach:function(){var F=this.evt,G;if(F){if(E.Lang.isArray(F)){for(G=0;G<F.length;G++){F[G].detach();}}else{F._delete(this.sub);}}}};E.CustomEvent=function(F,G){G=G||{};this.id=E.stamp(this);this.type=F;this.context=E;this.logSystem=(F==A);this.silent=this.logSystem;this.subscribers={};this.afters={};this.preventable=true;this.bubbles=true;this.signature=C;this.applyConfig(G,true);};E.CustomEvent.prototype={applyConfig:function(G,F){if(G){E.mix(this,G,F,B);}},_on:function(J,H,G,F){if(!J){this.log("Invalid callback for CE: "+this.type);}var I=new E.Subscriber(J,H,G,F);if(this.fireOnce&&this.fired){E.later(0,this,E.bind(this._notify,this,I,this.firedWith));}if(F==D){this.afters[I.id]=I;this.hasAfters=true;}else{this.subscribers[I.id]=I;this.hasSubscribers=true;}return new E.EventHandle(this,I);},subscribe:function(H,G){var F=(arguments.length>2)?E.Array(arguments,2,true):null;return this._on(H,G,F,true);},on:function(H,G){var F=(arguments.length>2)?E.Array(arguments,2,true):null;return this._on(H,G,F,true);},after:function(H,G){var F=(arguments.length>2)?E.Array(arguments,2,true):null;return this._on(H,G,F,D);},detach:function(J,H){if(J&&J.detach){return J.detach();}var K=0,G=this.subscribers,F,I;for(F in G){if(G.hasOwnProperty(F)){I=G[F];if(I&&(!J||J===I.fn)){this._delete(I);K++;}}}return K;},unsubscribe:function(){return this.detach.apply(this,arguments);},_notify:function(I,H,F){this.log(this.type+"->"+"sub: "+I.id);var G;G=I.notify(H,this);if(false===G||this.stopped>1){this.log(this.type+" cancelled by subscriber");return false;}return true;},log:function(G,F){if(!this.silent){}},fire:function(){if(this.fireOnce&&this.fired){this.log("fireOnce event: "+this.type+" already fired");return true;}else{var F=E.Array(arguments,0,true);this.fired=true;this.firedWith=F;if(this.emitFacade){return this.fireComplex(F);}else{return this.fireSimple(F);}}},fireSimple:function(F){if(this.hasSubscribers||this.hasAfters){this._procSubs(E.merge(this.subscribers,this.afters),F);}this._broadcast(F);return this.stopped?false:true;},fireComplex:function(F){F[0]=F[0]||{};return this.fireSimple(F);},_procSubs:function(I,G,F){var J,H;for(H in I){if(I.hasOwnProperty(H)){J=I[H];if(J&&J.fn){if(false===this._notify(J,G,F)){this.stopped=2;}if(this.stopped==2){return false;}}}}return true;},_broadcast:function(G){if(!this.stopped&&this.broadcast){var F=E.Array(G);F.unshift(this.type);if(this.host!==E){E.fire.apply(E,F);}if(this.broadcast==2){E.Global.fire.apply(E.Global,F);}}},unsubscribeAll:function(){return this.detachAll.apply(this,arguments);},detachAll:function(){return this.detach();},_delete:function(F){if(F){delete F.fn;delete F.context;delete this.subscribers[F.id];delete this.afters[F.id];}}};E.Subscriber=function(H,G,F){this.fn=H;this.context=G;this.id=E.stamp(this);this.args=F;this.events=null;};E.Subscriber.prototype={_notify:function(J,H,I){var F=this.args,G;switch(I.signature){case 0:G=this.fn.call(J,I.type,H,J);break;case 1:G=this.fn.call(J,H[0]||null,J);break;default:if(F||H){H=H||[];F=(F)?H.concat(F):H;G=this.fn.apply(J,F);}else{G=this.fn.call(J);}}return G;},notify:function(G,I){var J=this.context,F=true;if(!J){J=(I.contextFn)?I.contextFn():I.context;}if(E.config.throwFail){F=this._notify(J,G,I);}else{try{F=this._notify(J,G,I);}catch(H){E.error(this+" failed: "+H.message,H);}}return F;},contains:function(G,F){if(F){return((this.fn==G)&&this.context==F);}else{return(this.fn==G);}}};(function(){var F=E.Lang,H=":",I="|",J="~AFTER~",K=E.cached(function(L,N){if(!N||!F.isString(L)||L.indexOf(H)>-1){return L;}return N+H+L;}),G=E.cached(function(O,Q){var N=O,P,R,L;if(!F.isString(N)){return N;}L=N.indexOf(J);if(L>-1){R=true;N=N.substr(J.length);}L=N.indexOf(I);if(L>-1){P=N.substr(0,(L));N=N.substr(L+1);if(N=="*"){N=null;}}return[P,(Q)?K(N,Q):N,R,N];}),M=function(L){var N=(F.isObject(L))?L:{};this._yuievt=this._yuievt||{id:E.guid(),events:{},targets:{},config:N,chain:("chain" in N)?N.chain:E.config.chain,defaults:{context:N.context||this,host:this,emitFacade:N.emitFacade,fireOnce:N.fireOnce,queuable:N.queuable,broadcast:N.broadcast,bubbles:("bubbles" in N)?N.bubbles:true}};
+};M.prototype={on:function(Q,U,O,V){var Z=G(Q,this._yuievt.config.prefix),a,b,N,g,X,W,d,R=E.Env.evt.handles,P,L,S,e=E.Node,Y,T;if(F.isObject(Q)){if(F.isFunction(Q)){return E.Do.before.apply(E.Do,arguments);}a=U;b=O;N=E.Array(arguments,0,true);g={};P=Q._after;delete Q._after;E.each(Q,function(f,c){if(f){a=f.fn||((E.Lang.isFunction(f))?f:a);b=f.context||b;}N[0]=(P)?J+c:c;N[1]=a;N[2]=b;g[c]=this.on.apply(this,N);},this);return(this._yuievt.chain)?this:new E.EventHandle(g);}W=Z[0];P=Z[2];S=Z[3];if(e&&(this instanceof e)&&(S in e.DOM_EVENTS)){N=E.Array(arguments,0,true);N.splice(2,0,e.getDOMNode(this));return E.on.apply(E,N);}Q=Z[1];if(this instanceof YUI){L=E.Env.evt.plugins[Q];N=E.Array(arguments,0,true);N[0]=S;if(e){Y=N[2];if(Y instanceof E.NodeList){Y=E.NodeList.getDOMNodes(Y);}else{if(Y instanceof e){Y=e.getDOMNode(Y);}}T=(S in e.DOM_EVENTS);if(T){N[2]=Y;}}if(L){d=L.on.apply(E,N);}else{if((!Q)||T){d=E.Event._attach(N);}}}if(!d){X=this._yuievt.events[Q]||this.publish(Q);d=X._on(U,O,(arguments.length>3)?E.Array(arguments,3,true):null,(P)?"after":true);}if(W){R[W]=R[W]||{};R[W][Q]=R[W][Q]||[];R[W][Q].push(d);}return(this._yuievt.chain)?this:d;},subscribe:function(){return this.on.apply(this,arguments);},detach:function(P,U,O){var T=this._yuievt.events,Z,d,c=E.Node,Y=(this instanceof c);if(!P&&(this!==E)){for(Z in T){if(T.hasOwnProperty(Z)){d=T[Z].detach(U,O);}}if(Y){E.Event.purgeElement(c.getDOMNode(this));}return d;}var X=G(P,this._yuievt.config.prefix),V=F.isArray(X)?X[0]:null,R=(X)?X[3]:null,b,L,Q=E.Env.evt.handles,S,N,W,a=function(g,f){var e=g[f];if(e){while(e.length){b=e.pop();b.detach();}}};if(V){S=Q[V];P=X[1];if(S){if(P){a(S,P);}else{for(Z in S){if(S.hasOwnProperty(Z)){a(S,Z);}}}return(this._yuievt.chain)?this:true;}}else{if(F.isObject(P)&&P.detach){d=P.detach();return(this._yuievt.chain)?this:d;}else{if(Y&&((!R)||(R in c.DOM_EVENTS))){N=E.Array(arguments,0,true);N[2]=c.getDOMNode(this);return E.detach.apply(E,N);}}}L=E.Env.evt.plugins[R];if(this instanceof YUI){N=E.Array(arguments,0,true);if(L&&L.detach){return L.detach.apply(E,N);}else{if(!P||(!L&&c&&(P in c.DOM_EVENTS))){N[0]=P;return E.Event.detach.apply(E.Event,N);}}}W=T[P];if(W){d=W.detach(U,O);}return(this._yuievt.chain)?this:d;},unsubscribe:function(){return this.detach.apply(this,arguments);},detachAll:function(L){return this.detach(L);},unsubscribeAll:function(){return this.detachAll.apply(this,arguments);},publish:function(O,P){var N,R,L,Q=this._yuievt.config.prefix;O=(Q)?K(O,Q):O;if(F.isObject(O)){L={};E.each(O,function(T,S){L[S]=this.publish(S,T||P);},this);return L;}N=this._yuievt.events;R=N[O];if(R){if(P){R.applyConfig(P,true);}}else{R=new E.CustomEvent(O,(P)?E.mix(P,this._yuievt.defaults):this._yuievt.defaults);N[O]=R;}return N[O];},addTarget:function(L){this._yuievt.targets[E.stamp(L)]=L;this._yuievt.hasTargets=true;},removeTarget:function(L){delete this._yuievt.targets[E.stamp(L)];},fire:function(P){var S=F.isString(P),O=(S)?P:(P&&P.type),R,L,N,Q=this._yuievt.config.prefix;O=(Q)?K(O,Q):O;R=this.getEvent(O,true);if(!R){if(this._yuievt.hasTargets){L=(S)?arguments:E.Array(arguments,0,true).unshift(O);return this.bubble(null,L,this);}N=true;}else{L=E.Array(arguments,(S)?1:0,true);N=R.fire.apply(R,L);R.target=null;}return(this._yuievt.chain)?this:N;},getEvent:function(N,L){var P,O;if(!L){P=this._yuievt.config.prefix;N=(P)?K(N,P):N;}O=this._yuievt.events;return(O&&N in O)?O[N]:null;},after:function(O,N){var L=E.Array(arguments,0,true);switch(F.type(O)){case"function":return E.Do.after.apply(E.Do,arguments);case"object":L[0]._after=true;break;default:L[0]=J+O;}return this.on.apply(this,L);},before:function(){return this.on.apply(this,arguments);}};E.EventTarget=M;E.mix(E,M.prototype,false,false,{bubbles:false});M.call(E);YUI.Env.globalEvents=YUI.Env.globalEvents||new M();E.Global=YUI.Env.globalEvents;})();},"3.0.0",{requires:["oop"]});YUI.add("event-custom-complex",function(A){(function(){var C,D,B=A.CustomEvent.prototype;A.EventFacade=function(F,E){F=F||{};this.details=F.details;this.type=F.type;this.target=F.target;this.currentTarget=E;this.relatedTarget=F.relatedTarget;this.stopPropagation=function(){F.stopPropagation();};this.stopImmediatePropagation=function(){F.stopImmediatePropagation();};this.preventDefault=function(){F.preventDefault();};this.halt=function(G){F.halt(G);};};B.fireComplex=function(H){var L=A.Env._eventstack,F,J,E,K,G,I;if(L){if(this.queuable&&this.type!=L.next.type){this.log("queue "+this.type);L.queue.push([this,H]);return true;}}else{A.Env._eventstack={id:this.id,next:this,silent:this.silent,stopped:0,prevented:0,queue:[]};L=A.Env._eventstack;}this.stopped=0;this.prevented=0;this.target=this.target||this.host;I=new A.EventTarget({fireOnce:true,context:this.host});this.events=I;if(this.preventedFn){I.on("prevented",this.preventedFn);}if(this.stoppedFn){I.on("stopped",this.stoppedFn);}this.currentTarget=this.host||this.currentTarget;this.details=H.slice();this.log("Firing "+this.type);this._facade=null;F=this._getFacade(H);if(A.Lang.isObject(H[0])){H[0]=F;}else{H.unshift(F);}if(this.hasSubscribers){this._procSubs(A.merge(this.subscribers),H,F);}if(this.bubbles&&this.host&&this.host.bubble&&!this.stopped){L.stopped=0;L.prevented=0;G=this.host.bubble(this);this.stopped=Math.max(this.stopped,L.stopped);this.prevented=Math.max(this.prevented,L.prevented);}if(this.defaultFn&&!this.prevented){this.defaultFn.apply(this.host||this,H);}this._broadcast(H);if(this.hasAfters&&!this.prevented&&this.stopped<2){this._procSubs(A.merge(this.afters),H,F);}if(L.id===this.id){E=L.queue;while(E.length){J=E.pop();K=J[0];L.stopped=0;L.prevented=0;L.next=K;K.fire.apply(K,J[1]);}A.Env._eventstack=null;}return this.stopped?false:true;};B._getFacade=function(){var E=this._facade,H,G,F=this.details;if(!E){E=new A.EventFacade(this,this.currentTarget);}H=F&&F[0];if(A.Lang.isObject(H,true)){G={};A.mix(G,E,true,D);A.mix(E,H,true);A.mix(E,G,true,D);}E.details=this.details;E.target=this.target;E.currentTarget=this.currentTarget;E.stopped=0;
+E.prevented=0;this._facade=E;return this._facade;};B.stopPropagation=function(){this.stopped=1;A.Env._eventstack.stopped=1;this.events.fire("stopped",this);};B.stopImmediatePropagation=function(){this.stopped=2;A.Env._eventstack.stopped=2;this.events.fire("stopped",this);};B.preventDefault=function(){if(this.preventable){this.prevented=1;A.Env._eventstack.prevented=1;this.events.fire("prevented",this);}};B.halt=function(E){if(E){this.stopImmediatePropagation();}else{this.stopPropagation();}this.preventDefault();};A.EventTarget.prototype.bubble=function(M,K,I){var G=this._yuievt.targets,J=true,N,L,E,F,H;if(!M||((!M.stopped)&&G)){for(F in G){if(G.hasOwnProperty(F)){N=G[F];L=M&&M.type;E=N.getEvent(L,true);if(!E){if(N._yuievt.hasTargets){N.bubble.call(N,M,K,I);}}else{E.target=I||(M&&M.target)||this;E.currentTarget=N;H=E.broadcast;E.broadcast=false;J=J&&E.fire.apply(E,K||M.details);E.broadcast=H;if(E.stopped){break;}}}}}return J;};C=new A.EventFacade();D=A.Object.keys(C);})();},"3.0.0",{requires:["event-custom-base"]});YUI.add("event-custom",function(A){},"3.0.0",{use:["event-custom-base","event-custom-complex"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-custom-base', function(Y) {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ */
+
+Y.Env.evt = {
+ handles: {},
+ plugins: {}
+};
+
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * Allows for the insertion of methods that are executed before or after
+ * a specified method
+ * @class Do
+ * @static
+ */
+
+var BEFORE = 0,
+ AFTER = 1;
+
+Y.Do = {
+
+ /**
+ * Cache of objects touched by the utility
+ * @property objs
+ * @static
+ */
+ objs: {},
+
+ /**
+ * Execute the supplied method before the specified function
+ * @method before
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ before: function(fn, obj, sFn, c) {
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(BEFORE, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @static
+ */
+ after: function(fn, obj, sFn, c) {
+ var f = fn, a;
+ if (c) {
+ a = [fn, c].concat(Y.Array(arguments, 4, true));
+ f = Y.rbind.apply(Y, a);
+ }
+
+ return this._inject(AFTER, f, obj, sFn);
+ },
+
+ /**
+ * Execute the supplied method after the specified function
+ * @method _inject
+ * @param when {string} before or after
+ * @param fn {Function} the function to execute
+ * @param obj the object hosting the method to displace
+ * @param sFn {string} the name of the method to displace
+ * @param c The execution context for fn
+ * @return {string} handle for the subscription
+ * @private
+ * @static
+ */
+ _inject: function(when, fn, obj, sFn) {
+
+ // object id
+ var id = Y.stamp(obj), o, sid;
+
+ if (! this.objs[id]) {
+ // create a map entry for the obj if it doesn't exist
+ this.objs[id] = {};
+ }
+
+ o = this.objs[id];
+
+ if (! o[sFn]) {
+ // create a map entry for the method if it doesn't exist
+ o[sFn] = new Y.Do.Method(obj, sFn);
+
+ // re-route the method to our wrapper
+ obj[sFn] =
+ function() {
+ return o[sFn].exec.apply(o[sFn], arguments);
+ };
+ }
+
+ // subscriber id
+ sid = id + Y.stamp(fn) + sFn;
+
+ // register the callback
+ o[sFn].register(sid, fn, when);
+
+ return new Y.EventHandle(o[sFn], sid);
+
+ },
+
+ /**
+ * Detach a before or after subscription
+ * @method detach
+ * @param handle {string} the subscription handle
+ */
+ detach: function(handle) {
+
+ if (handle.detach) {
+ handle.detach();
+ }
+
+ },
+
+ _unload: function(e, me) {
+
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+/**
+ * Wrapper for a displaced method with aop enabled
+ * @class Do.Method
+ * @constructor
+ * @param obj The object to operate on
+ * @param sFn The name of the method to displace
+ */
+Y.Do.Method = function(obj, sFn) {
+ this.obj = obj;
+ this.methodName = sFn;
+ this.method = obj[sFn];
+ this.before = {};
+ this.after = {};
+};
+
+/**
+ * Register a aop subscriber
+ * @method register
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype.register = function (sid, fn, when) {
+ if (when) {
+ this.after[sid] = fn;
+ } else {
+ this.before[sid] = fn;
+ }
+};
+
+/**
+ * Unregister a aop subscriber
+ * @method delete
+ * @param sid {string} the subscriber id
+ * @param fn {Function} the function to execute
+ * @param when {string} when to execute the function
+ */
+Y.Do.Method.prototype._delete = function (sid) {
+ delete this.before[sid];
+ delete this.after[sid];
+};
+
+/**
+ * Execute the wrapped method
+ * @method exec
+ */
+Y.Do.Method.prototype.exec = function () {
+
+ var args = Y.Array(arguments, 0, true),
+ i, ret, newRet,
+ bf = this.before,
+ af = this.after,
+ prevented = false;
+
+ // execute before
+ for (i in bf) {
+ if (bf.hasOwnProperty(i)) {
+ ret = bf[i].apply(this.obj, args);
+ if (ret) {
+ switch (ret.constructor) {
+ case Y.Do.Halt:
+ return ret.retVal;
+ case Y.Do.AlterArgs:
+ args = ret.newArgs;
+ break;
+ case Y.Do.Prevent:
+ prevented = true;
+ break;
+ default:
+ }
+ }
+ }
+ }
+
+ // execute method
+ if (!prevented) {
+ ret = this.method.apply(this.obj, args);
+ }
+
+ // execute after methods.
+ for (i in af) {
+ if (af.hasOwnProperty(i)) {
+ newRet = af[i].apply(this.obj, args);
+ // Stop processing if a Halt object is returned
+ if (newRet && newRet.constructor == Y.Do.Halt) {
+ return newRet.retVal;
+ // Check for a new return value
+ } else if (newRet && newRet.constructor == Y.Do.AlterReturn) {
+ ret = newRet.newRetVal;
+ }
+ }
+ }
+
+ return ret;
+};
+
+//////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Return an AlterArgs object when you want to change the arguments that
+ * were passed into the function. An example would be a service that scrubs
+ * out illegal characters prior to executing the core business logic.
+ * @class Do.AlterArgs
+ */
+Y.Do.AlterArgs = function(msg, newArgs) {
+ this.msg = msg;
+ this.newArgs = newArgs;
+};
+
+/**
+ * Return an AlterReturn object when you want to change the result returned
+ * from the core method to the caller
+ * @class Do.AlterReturn
+ */
+Y.Do.AlterReturn = function(msg, newRetVal) {
+ this.msg = msg;
+ this.newRetVal = newRetVal;
+};
+
+/**
+ * Return a Halt object when you want to terminate the execution
+ * of all subsequent subscribers as well as the wrapped method
+ * if it has not exectued yet.
+ * @class Do.Halt
+ */
+Y.Do.Halt = function(msg, retVal) {
+ this.msg = msg;
+ this.retVal = retVal;
+};
+
+/**
+ * Return a Prevent object when you want to prevent the wrapped function
+ * from executing, but want the remaining listeners to execute
+ * @class Do.Prevent
+ */
+Y.Do.Prevent = function(msg) {
+ this.msg = msg;
+};
+
+/**
+ * Return an Error object when you want to terminate the execution
+ * of all subsequent method calls.
+ * @class Do.Error
+ * @deprecated use Y.Do.Halt or Y.Do.Prevent
+ */
+Y.Do.Error = Y.Do.Halt;
+
+//////////////////////////////////////////////////////////////////////////
+
+// Y["Event"] && Y.Event.addListener(window, "unload", Y.Do._unload, Y.Do);
+
+})();
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+
+/**
+ * Return value from all subscribe operations
+ * @class EventHandle
+ * @constructor
+ * @param evt {CustomEvent} the custom event
+ * @param sub {Subscriber} the subscriber
+ */
+
+// var onsubscribeType = "_event:onsub",
+var AFTER = 'after',
+ CONFIGS = [
+ 'broadcast',
+ 'bubbles',
+ 'context',
+ 'contextFn',
+ 'currentTarget',
+ 'defaultFn',
+ 'details',
+ 'emitFacade',
+ 'fireOnce',
+ 'host',
+ 'preventable',
+ 'preventedFn',
+ 'queuable',
+ 'silent',
+ 'stoppedFn',
+ 'target',
+ 'type'
+ ],
+
+
+ YUI3_SIGNATURE = 9,
+ YUI_LOG = 'yui:log';
+
+Y.EventHandle = function(evt, sub) {
+
+ /**
+ * The custom event
+ * @type CustomEvent
+ */
+ this.evt = evt;
+
+ /**
+ * The subscriber object
+ * @type Subscriber
+ */
+ this.sub = sub;
+};
+
+Y.EventHandle.prototype = {
+
+ /**
+ * Detaches this subscriber
+ * @method detach
+ */
+ detach: function() {
+ var evt = this.evt, i;
+ if (evt) {
+ if (Y.Lang.isArray(evt)) {
+ for (i=0; i<evt.length; i++) {
+ evt[i].detach();
+ }
+ } else {
+ evt._delete(this.sub);
+ }
+ }
+ }
+};
+
+/**
+ * The CustomEvent class lets you define events for your application
+ * that can be subscribed to by one or more independent component.
+ *
+ * @param {String} type The type of event, which is passed to the callback
+ * when the event fires
+ * @param o configuration object
+ * @class CustomEvent
+ * @constructor
+ */
+Y.CustomEvent = function(type, o) {
+
+ // if (arguments.length > 2) {
+// this.log('CustomEvent context and silent are now in the config', 'warn', 'Event');
+ // }
+
+ o = o || {};
+
+ this.id = Y.stamp(this);
+
+ /**
+ * The type of event, returned to subscribers when the event fires
+ * @property type
+ * @type string
+ */
+ this.type = type;
+
+ /**
+ * The context the the event will fire from by default. Defaults to the YUI
+ * instance.
+ * @property context
+ * @type object
+ */
+ this.context = Y;
+
+ this.logSystem = (type == YUI_LOG);
+
+ /**
+ * If 0, this event does not broadcast. If 1, the YUI instance is notified
+ * every time this event fires. If 2, the YUI instance and the YUI global
+ * (if event is enabled on the global) are notified every time this event
+ * fires.
+ * @property broadcast
+ * @type int
+ */
+ // this.broadcast = 0;
+
+ /**
+ * By default all custom events are logged in the debug build, set silent
+ * to true to disable debug outpu for this event.
+ * @property silent
+ * @type boolean
+ */
+ this.silent = this.logSystem;
+
+ /**
+ * Specifies whether this event should be queued when the host is actively
+ * processing an event. This will effect exectution order of the callbacks
+ * for the various events.
+ * @property queuable
+ * @type boolean
+ * @default false
+ */
+ // this.queuable = false;
+
+ /**
+ * The subscribers to this event
+ * @property subscribers
+ * @type Subscriber{}
+ */
+ this.subscribers = {};
+
+ /**
+ * 'After' subscribers
+ * @property afters
+ * @type Subscriber{}
+ */
+ this.afters = {};
+
+ /**
+ * This event has fired if true
+ *
+ * @property fired
+ * @type boolean
+ * @default false;
+ */
+ // this.fired = false;
+
+ /**
+ * An array containing the arguments the custom event
+ * was last fired with.
+ * @property firedWith
+ * @type Array
+ */
+ // this.firedWith;
+
+ /**
+ * This event should only fire one time if true, and if
+ * it has fired, any new subscribers should be notified
+ * immediately.
+ *
+ * @property fireOnce
+ * @type boolean
+ * @default false;
+ */
+ // this.fireOnce = false;
+
+ /**
+ * Flag for stopPropagation that is modified during fire()
+ * 1 means to stop propagation to bubble targets. 2 means
+ * to also stop additional subscribers on this target.
+ * @property stopped
+ * @type int
+ */
+ // this.stopped = 0;
+
+ /**
+ * Flag for preventDefault that is modified during fire().
+ * if it is not 0, the default behavior for this event
+ * @property prevented
+ * @type int
+ */
+ // this.prevented = 0;
+
+ /**
+ * Specifies the host for this custom event. This is used
+ * to enable event bubbling
+ * @property host
+ * @type EventTarget
+ */
+ // this.host = null;
+
+ /**
+ * The default function to execute after event listeners
+ * have fire, but only if the default action was not
+ * prevented.
+ * @property defaultFn
+ * @type Function
+ */
+ // this.defaultFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * stopPropagation or stopImmediatePropagation
+ * @property stoppedFn
+ * @type Function
+ */
+ // this.stoppedFn = null;
+
+ /**
+ * The function to execute if a subscriber calls
+ * preventDefault
+ * @property preventedFn
+ * @type Function
+ */
+ // this.preventedFn = null;
+
+ /**
+ * Specifies whether or not this event's default function
+ * can be cancelled by a subscriber by executing preventDefault()
+ * on the event facade
+ * @property preventable
+ * @type boolean
+ * @default true
+ */
+ this.preventable = true;
+
+ /**
+ * Specifies whether or not a subscriber can stop the event propagation
+ * via stopPropagation(), stopImmediatePropagation(), or halt()
+ * @property bubbles
+ * @type boolean
+ * @default true
+ */
+ this.bubbles = true;
+
+ /**
+ * Supports multiple options for listener signatures in order to
+ * port YUI 2 apps.
+ * @property signature
+ * @type int
+ * @default 9
+ */
+ this.signature = YUI3_SIGNATURE;
+
+ // this.hasSubscribers = false;
+
+ // this.hasAfters = false;
+
+ /**
+ * If set to true, the custom event will deliver an EventFacade object
+ * that is similar to a DOM event object.
+ * @property emitFacade
+ * @type boolean
+ * @default false
+ */
+ // this.emitFacade = false;
+
+ this.applyConfig(o, true);
+
+ // this.log("Creating " + this.type);
+
+};
+
+Y.CustomEvent.prototype = {
+
+ /**
+ * Apply configuration properties. Only applies the CONFIG whitelist
+ * @method applyConfig
+ * @param o hash of properties to apply
+ * @param force {boolean} if true, properties that exist on the event
+ * will be overwritten.
+ */
+ applyConfig: function(o, force) {
+ if (o) {
+ Y.mix(this, o, force, CONFIGS);
+ }
+ },
+
+ _on: function(fn, context, args, when) {
+
+ if (!fn) {
+ this.log("Invalid callback for CE: " + this.type);
+ }
+
+ var s = new Y.Subscriber(fn, context, args, when);
+
+ if (this.fireOnce && this.fired) {
+ Y.later(0, this, Y.bind(this._notify, this, s, this.firedWith));
+ }
+
+ if (when == AFTER) {
+ this.afters[s.id] = s;
+ this.hasAfters = true;
+ } else {
+ this.subscribers[s.id] = s;
+ this.hasSubscribers = true;
+ }
+
+ return new Y.EventHandle(this, s);
+
+ },
+
+ /**
+ * Listen for this event
+ * @method subscribe
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ * @deprecated use on
+ */
+ subscribe: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event
+ * @method on
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ on: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, true);
+ },
+
+ /**
+ * Listen for this event after the normal subscribers have been notified and
+ * the default behavior has been applied. If a normal subscriber prevents the
+ * default behavior, it also prevents after listeners from firing.
+ * @method after
+ * @param {Function} fn The function to execute
+ * @return {EventHandle|EventTarget} unsubscribe handle or a
+ * chainable event target depending on the 'chain' config.
+ */
+ after: function(fn, context) {
+ var a = (arguments.length > 2) ? Y.Array(arguments, 2, true): null;
+ return this._on(fn, context, a, AFTER);
+ },
+
+ /**
+ * Detach listeners.
+ * @method detach
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {int|EventTarget} returns a chainable event target
+ * or the number of subscribers unsubscribed.
+ */
+ detach: function(fn, context) {
+ // unsubscribe handle
+ if (fn && fn.detach) {
+ return fn.detach();
+ }
+
+ var found = 0, subs = this.subscribers, i, s;
+
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && (!fn || fn === s.fn)) {
+ this._delete(s);
+ found++;
+ }
+ }
+ }
+
+ return found;
+ },
+
+ /**
+ * Detach listeners.
+ * @method unsubscribe
+ * @param {Function} fn The subscribed function to remove, if not supplied
+ * all will be removed
+ * @param {Object} context The context object passed to subscribe.
+ * @return {boolean|EventTarget} returns a chainable event target
+ * or a boolean for legacy detach support.
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+
+ /**
+ * Notify a single subscriber
+ * @method _notify
+ * @param s {Subscriber} the subscriber
+ * @param args {Array} the arguments array to apply to the listener
+ * @private
+ */
+ _notify: function(s, args, ef) {
+
+ this.log(this.type + "->" + "sub: " + s.id);
+
+ var ret;
+
+ ret = s.notify(args, this);
+
+ if (false === ret || this.stopped > 1) {
+ this.log(this.type + " cancelled by subscriber");
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
+ * Logger abstraction to centralize the application of the silent flag
+ * @method log
+ * @param msg {string} message to log
+ * @param cat {string} log category
+ */
+ log: function(msg, cat) {
+ if (!this.silent) {
+ }
+ },
+
+ /**
+ * Notifies the subscribers. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters:
+ * <ul>
+ * <li>The type of event</li>
+ * <li>All of the arguments fire() was executed with as an array</li>
+ * <li>The custom object (if any) that was passed into the subscribe()
+ * method</li>
+ * </ul>
+ * @method fire
+ * @param {Object*} arguments an arbitrary set of parameters to pass to
+ * the handler.
+ * @return {boolean} false if one of the subscribers returned false,
+ * true otherwise
+ *
+ */
+ fire: function() {
+ if (this.fireOnce && this.fired) {
+ this.log('fireOnce event: ' + this.type + ' already fired');
+ return true;
+ } else {
+
+ var args = Y.Array(arguments, 0, true);
+
+ this.fired = true;
+ this.firedWith = args;
+
+ if (this.emitFacade) {
+ return this.fireComplex(args);
+ } else {
+ return this.fireSimple(args);
+ }
+ }
+ },
+
+ fireSimple: function(args) {
+ if (this.hasSubscribers || this.hasAfters) {
+ this._procSubs(Y.merge(this.subscribers, this.afters), args);
+ }
+ this._broadcast(args);
+ return this.stopped ? false : true;
+ },
+
+ // Requires the event-custom-complex module for full funcitonality.
+ fireComplex: function(args) {
+ args[0] = args[0] || {};
+ return this.fireSimple(args);
+ },
+
+ _procSubs: function(subs, args, ef) {
+ var s, i;
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ if (s && s.fn) {
+ if (false === this._notify(s, args, ef)) {
+ this.stopped = 2;
+ }
+ if (this.stopped == 2) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ },
+
+ _broadcast: function(args) {
+ if (!this.stopped && this.broadcast) {
+
+ var a = Y.Array(args);
+ a.unshift(this.type);
+
+ if (this.host !== Y) {
+ Y.fire.apply(Y, a);
+ }
+
+ if (this.broadcast == 2) {
+ Y.Global.fire.apply(Y.Global, a);
+ }
+ }
+ },
+
+ /**
+ * Removes all listeners
+ * @method unsubscribeAll
+ * @return {int} The number of listeners unsubscribed
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners
+ * @method detachAll
+ * @return {int} The number of listeners unsubscribed
+ */
+ detachAll: function() {
+ return this.detach();
+ },
+
+ /**
+ * @method _delete
+ * @param subscriber object
+ * @private
+ */
+ _delete: function(s) {
+ if (s) {
+ delete s.fn;
+ delete s.context;
+ delete this.subscribers[s.id];
+ delete this.afters[s.id];
+ }
+ }
+};
+
+/////////////////////////////////////////////////////////////////////
+
+/**
+ * Stores the subscriber information to be used when the event fires.
+ * @param {Function} fn The wrapped function to execute
+ * @param {Object} context The value of the keyword 'this' in the listener
+ * @param {Array} args* 0..n additional arguments to supply the listener
+ *
+ * @class Subscriber
+ * @constructor
+ */
+Y.Subscriber = function(fn, context, args) {
+
+ /**
+ * The callback that will be execute when the event fires
+ * This is wrapped by Y.rbind if obj was supplied.
+ * @property fn
+ * @type Function
+ */
+ this.fn = fn;
+
+ /**
+ * Optional 'this' keyword for the listener
+ * @property context
+ * @type Object
+ */
+ this.context = context;
+
+ /**
+ * Unique subscriber id
+ * @property id
+ * @type String
+ */
+ this.id = Y.stamp(this);
+
+ /**
+ * Additional arguments to propagate to the subscriber
+ * @property args
+ * @type Array
+ */
+ this.args = args;
+
+ /**
+ * Custom events for a given fire transaction.
+ * @property events
+ * @type {EventTarget}
+ */
+ this.events = null;
+
+};
+
+Y.Subscriber.prototype = {
+
+ _notify: function(c, args, ce) {
+ var a = this.args, ret;
+ switch (ce.signature) {
+ case 0:
+ ret = this.fn.call(c, ce.type, args, c);
+ break;
+ case 1:
+ ret = this.fn.call(c, args[0] || null, c);
+ break;
+ default:
+ if (a || args) {
+ args = args || [];
+ a = (a) ? args.concat(a) : args;
+ ret = this.fn.apply(c, a);
+ } else {
+ ret = this.fn.call(c);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Executes the subscriber.
+ * @method notify
+ * @param args {Array} Arguments array for the subscriber
+ * @param ce {CustomEvent} The custom event that sent the notification
+ */
+ notify: function(args, ce) {
+ var c = this.context,
+ ret = true;
+
+ if (!c) {
+ c = (ce.contextFn) ? ce.contextFn() : ce.context;
+ }
+
+ // only catch errors if we will not re-throw them.
+ if (Y.config.throwFail) {
+ ret = this._notify(c, args, ce);
+ } else {
+ try {
+ ret = this._notify(c, args, ce);
+ } catch(e) {
+ Y.error(this + ' failed: ' + e.message, e);
+ }
+ }
+
+ return ret;
+ },
+
+ /**
+ * Returns true if the fn and obj match this objects properties.
+ * Used by the unsubscribe method to match the right subscriber.
+ *
+ * @method contains
+ * @param {Function} fn the function to execute
+ * @param {Object} context optional 'this' keyword for the listener
+ * @return {boolean} true if the supplied arguments match this
+ * subscriber's signature.
+ */
+ contains: function(fn, context) {
+ if (context) {
+ return ((this.fn == fn) && this.context == context);
+ } else {
+ return (this.fn == fn);
+ }
+ }
+
+};
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event-custom
+ * @submodule event-custom-base
+ */
+(function() {
+
+/**
+ * EventTarget provides the implementation for any object to
+ * publish, subscribe and fire to custom events, and also
+ * alows other EventTargets to target the object with events
+ * sourced from the other object.
+ * EventTarget is designed to be used with Y.augment to wrap
+ * EventCustom in an interface that allows events to be listened to
+ * and fired by name. This makes it possible for implementing code to
+ * subscribe to an event that either has not been created yet, or will
+ * not be created at all.
+ * @class EventTarget
+ * @param opts a configuration object
+ * @config emitFacade {boolean} if true, all events will emit event
+ * facade payloads by default (default false)
+ * @config prefix {string} the prefix to apply to non-prefixed event names
+ * @config chain {boolean} if true, on/after/detach return the host to allow
+ * chaining, otherwise they return an EventHandle (default false)
+ */
+
+var L = Y.Lang,
+ PREFIX_DELIMITER = ':',
+ CATEGORY_DELIMITER = '|',
+ AFTER_PREFIX = '~AFTER~',
+
+ /**
+ * If the instance has a prefix attribute and the
+ * event type is not prefixed, the instance prefix is
+ * applied to the supplied type.
+ * @method _getType
+ * @private
+ */
+ _getType = Y.cached(function(type, pre) {
+
+ if (!pre || !L.isString(type) || type.indexOf(PREFIX_DELIMITER) > -1) {
+ return type;
+ }
+
+ return pre + PREFIX_DELIMITER + type;
+ }),
+
+ /**
+ * Returns an array with the detach key (if provided),
+ * and the prefixed event name from _getType
+ * Y.on('detachcategory, menu:click', fn)
+ * @method _parseType
+ * @private
+ */
+ _parseType = Y.cached(function(type, pre) {
+
+ var t = type, detachcategory, after, i;
+
+ if (!L.isString(t)) {
+ return t;
+ }
+
+ i = t.indexOf(AFTER_PREFIX);
+
+ if (i > -1) {
+ after = true;
+ t = t.substr(AFTER_PREFIX.length);
+ }
+
+ i = t.indexOf(CATEGORY_DELIMITER);
+
+ if (i > -1) {
+ detachcategory = t.substr(0, (i));
+ t = t.substr(i+1);
+ if (t == '*') {
+ t = null;
+ }
+ }
+
+ // detach category, full type with instance prefix, is this an after listener, short type
+ return [detachcategory, (pre) ? _getType(t, pre) : t, after, t];
+ }),
+
+ ET = function(opts) {
+
+
+ var o = (L.isObject(opts)) ? opts : {};
+
+ this._yuievt = this._yuievt || {
+
+ id: Y.guid(),
+
+ events: {},
+
+ targets: {},
+
+ config: o,
+
+ chain: ('chain' in o) ? o.chain : Y.config.chain,
+
+ defaults: {
+ context: o.context || this,
+ host: this,
+ emitFacade: o.emitFacade,
+ fireOnce: o.fireOnce,
+ queuable: o.queuable,
+ broadcast: o.broadcast,
+ bubbles: ('bubbles' in o) ? o.bubbles : true
+ }
+ };
+
+ };
+
+
+ET.prototype = {
+
+ /**
+ * Subscribe to a custom event hosted by this object
+ * @method on
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ on: function(type, fn, context, x) {
+
+ var parts = _parseType(type, this._yuievt.config.prefix), f, c, args, ret, ce,
+ detachcategory, handle, store = Y.Env.evt.handles, after, adapt, shorttype,
+ Node = Y.Node, n, domevent;
+
+ if (L.isObject(type)) {
+
+ if (L.isFunction(type)) {
+ return Y.Do.before.apply(Y.Do, arguments);
+ }
+
+ f = fn;
+ c = context;
+ args = Y.Array(arguments, 0, true);
+ ret = {};
+ after = type._after;
+ delete type._after;
+
+ Y.each(type, function(v, k) {
+
+ if (v) {
+ f = v.fn || ((Y.Lang.isFunction(v)) ? v : f);
+ c = v.context || c;
+ }
+
+ args[0] = (after) ? AFTER_PREFIX + k : k;
+ args[1] = f;
+ args[2] = c;
+
+ ret[k] = this.on.apply(this, args);
+
+ }, this);
+
+ return (this._yuievt.chain) ? this : new Y.EventHandle(ret);
+
+ }
+
+ detachcategory = parts[0];
+ after = parts[2];
+ shorttype = parts[3];
+
+ // extra redirection so we catch adaptor events too. take a look at this.
+ if (Node && (this instanceof Node) && (shorttype in Node.DOM_EVENTS)) {
+ args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, Node.getDOMNode(this));
+ return Y.on.apply(Y, args);
+ }
+
+ type = parts[1];
+
+ if (this instanceof YUI) {
+
+ adapt = Y.Env.evt.plugins[type];
+ args = Y.Array(arguments, 0, true);
+ args[0] = shorttype;
+
+ if (Node) {
+ n = args[2];
+
+ if (n instanceof Y.NodeList) {
+ n = Y.NodeList.getDOMNodes(n);
+ } else if (n instanceof Node) {
+ n = Node.getDOMNode(n);
+ }
+
+ domevent = (shorttype in Node.DOM_EVENTS);
+
+ // Captures both DOM events and event plugins.
+ if (domevent) {
+ args[2] = n;
+ }
+ }
+
+ // check for the existance of an event adaptor
+ if (adapt) {
+ handle = adapt.on.apply(Y, args);
+ } else if ((!type) || domevent) {
+ handle = Y.Event._attach(args);
+ }
+
+ }
+
+ if (!handle) {
+ ce = this._yuievt.events[type] || this.publish(type);
+ handle = ce._on(fn, context, (arguments.length > 3) ? Y.Array(arguments, 3, true) : null, (after) ? 'after' : true);
+ }
+
+ if (detachcategory) {
+ store[detachcategory] = store[detachcategory] || {};
+ store[detachcategory][type] = store[detachcategory][type] || [];
+ store[detachcategory][type].push(handle);
+ }
+
+ return (this._yuievt.chain) ? this : handle;
+
+ },
+
+ /**
+ * subscribe to an event
+ * @method subscribe
+ * @deprecated use on
+ */
+ subscribe: function() {
+ return this.on.apply(this, arguments);
+ },
+
+ /**
+ * Detach one or more listeners the from the specified event
+ * @method detach
+ * @param type {string|Object} Either the handle to the subscriber or the
+ * type of event. If the type
+ * is not specified, it will attempt to remove
+ * the listener from all hosted events.
+ * @param fn {Function} The subscribed function to unsubscribe, if not
+ * supplied, all subscribers will be removed.
+ * @param context {Object} The custom object passed to subscribe. This is
+ * optional, but if supplied will be used to
+ * disambiguate multiple listeners that are the same
+ * (e.g., you subscribe many object using a function
+ * that lives on the prototype)
+ * @return {EventTarget} the host
+ */
+ detach: function(type, fn, context) {
+ var evts = this._yuievt.events, i, ret,
+ Node = Y.Node, isNode = (this instanceof Node);
+
+ // detachAll disabled on the Y instance.
+ if (!type && (this !== Y)) {
+ for (i in evts) {
+ if (evts.hasOwnProperty(i)) {
+ ret = evts[i].detach(fn, context);
+ }
+ }
+ if (isNode) {
+
+ Y.Event.purgeElement(Node.getDOMNode(this));
+ }
+
+ return ret;
+ }
+
+ var parts = _parseType(type, this._yuievt.config.prefix),
+ detachcategory = L.isArray(parts) ? parts[0] : null,
+ shorttype = (parts) ? parts[3] : null,
+ handle, adapt, store = Y.Env.evt.handles, cat, args,
+ ce,
+
+ keyDetacher = function(lcat, ltype) {
+ var handles = lcat[ltype];
+ if (handles) {
+ while (handles.length) {
+ handle = handles.pop();
+ handle.detach();
+ }
+ }
+ };
+
+ if (detachcategory) {
+
+ cat = store[detachcategory];
+ type = parts[1];
+
+ if (cat) {
+ if (type) {
+ keyDetacher(cat, type);
+ } else {
+ for (i in cat) {
+ if (cat.hasOwnProperty(i)) {
+ keyDetacher(cat, i);
+ }
+ }
+ }
+
+ return (this._yuievt.chain) ? this : true;
+ }
+
+ // If this is an event handle, use it to detach
+ } else if (L.isObject(type) && type.detach) {
+ ret = type.detach();
+ return (this._yuievt.chain) ? this : ret;
+ // extra redirection so we catch adaptor events too. take a look at this.
+ } else if (isNode && ((!shorttype) || (shorttype in Node.DOM_EVENTS))) {
+ args = Y.Array(arguments, 0, true);
+ args[2] = Node.getDOMNode(this);
+ return Y.detach.apply(Y, args);
+ }
+
+ adapt = Y.Env.evt.plugins[shorttype];
+
+ // The YUI instance handles DOM events and adaptors
+ if (this instanceof YUI) {
+ args = Y.Array(arguments, 0, true);
+ // use the adaptor specific detach code if
+ if (adapt && adapt.detach) {
+ return adapt.detach.apply(Y, args);
+ // DOM event fork
+ } else if (!type || (!adapt && Node && (type in Node.DOM_EVENTS))) {
+ args[0] = type;
+ return Y.Event.detach.apply(Y.Event, args);
+ }
+ }
+
+ ce = evts[type];
+ if (ce) {
+ ret = ce.detach(fn, context);
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * detach a listener
+ * @method unsubscribe
+ * @deprecated use detach
+ */
+ unsubscribe: function() {
+ return this.detach.apply(this, arguments);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method detachAll
+ * @param type {string} The type, or name of the event
+ */
+ detachAll: function(type) {
+ return this.detach(type);
+ },
+
+ /**
+ * Removes all listeners from the specified event. If the event type
+ * is not specified, all listeners from all hosted custom events will
+ * be removed.
+ * @method unsubscribeAll
+ * @param type {string} The type, or name of the event
+ * @deprecated use detachAll
+ */
+ unsubscribeAll: function() {
+ return this.detachAll.apply(this, arguments);
+ },
+
+ /**
+ * Creates a new custom event of the specified type. If a custom event
+ * by that name already exists, it will not be re-created. In either
+ * case the custom event is returned.
+ *
+ * @method publish
+ *
+ * @param type {string} the type, or name of the event
+ * @param opts {object} optional config params. Valid properties are:
+ *
+ * <ul>
+ * <li>
+ * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false)
+ * </li>
+ * <li>
+ * 'bubbles': whether or not this event bubbles (true)
+ * </li>
+ * <li>
+ * 'context': the default execution context for the listeners (this)
+ * </li>
+ * <li>
+ * 'defaultFn': the default function to execute when this event fires if preventDefault was not called
+ * </li>
+ * <li>
+ * 'emitFacade': whether or not this event emits a facade (false)
+ * </li>
+ * <li>
+ * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click'
+ * </li>
+ * <li>
+ * 'fireOnce': if an event is configured to fire once, new subscribers after
+ * the fire will be notified immediately.
+ * </li>
+ * <li>
+ * 'preventable': whether or not preventDefault() has an effect (true)
+ * </li>
+ * <li>
+ * 'preventedFn': a function that is executed when preventDefault is called
+ * </li>
+ * <li>
+ * 'queuable': whether or not this event can be queued during bubbling (false)
+ * </li>
+ * <li>
+ * 'silent': if silent is true, debug messages are not provided for this event.
+ * </li>
+ * <li>
+ * 'stoppedFn': a function that is executed when stopPropagation is called
+ * </li>
+ * <li>
+ * 'type': the event type (valid option if not provided as the first parameter to publish)
+ * </li>
+ * </ul>
+ *
+ * @return {Event.Custom} the custom event
+ *
+ */
+ publish: function(type, opts) {
+ var events, ce, ret, pre = this._yuievt.config.prefix;
+
+ type = (pre) ? _getType(type, pre) : type;
+
+
+ if (L.isObject(type)) {
+ ret = {};
+ Y.each(type, function(v, k) {
+ ret[k] = this.publish(k, v || opts);
+ }, this);
+
+ return ret;
+ }
+
+ events = this._yuievt.events;
+ ce = events[type];
+
+ if (ce) {
+// ce.log("publish applying new config to published event: '"+type+"' exists", 'info', 'event');
+ if (opts) {
+ ce.applyConfig(opts, true);
+ }
+ } else {
+ // apply defaults
+ ce = new Y.CustomEvent(type, (opts) ? Y.mix(opts, this._yuievt.defaults) : this._yuievt.defaults);
+ events[type] = ce;
+ }
+
+ // make sure we turn the broadcast flag off if this
+ // event was published as a result of bubbling
+ // if (opts instanceof Y.CustomEvent) {
+ // events[type].broadcast = false;
+ // }
+
+ return events[type];
+ },
+
+ /**
+ * Registers another EventTarget as a bubble target. Bubble order
+ * is determined by the order registered. Multiple targets can
+ * be specified.
+ * @method addTarget
+ * @param o {EventTarget} the target to add
+ */
+ addTarget: function(o) {
+ this._yuievt.targets[Y.stamp(o)] = o;
+ this._yuievt.hasTargets = true;
+ },
+
+ /**
+ * Removes a bubble target
+ * @method removeTarget
+ * @param o {EventTarget} the target to remove
+ */
+ removeTarget: function(o) {
+ delete this._yuievt.targets[Y.stamp(o)];
+ },
+
+ /**
+ * Fire a custom event by name. The callback functions will be executed
+ * from the context specified when the event was created, and with the
+ * following parameters.
+ *
+ * If the custom event object hasn't been created, then the event hasn't
+ * been published and it has no subscribers. For performance sake, we
+ * immediate exit in this case. This means the event won't bubble, so
+ * if the intention is that a bubble target be notified, the event must
+ * be published on this object first.
+ *
+ * The first argument is the event type, and any additional arguments are
+ * passed to the listeners as parameters. If the first of these is an
+ * object literal, and the event is configured to emit an event facade,
+ * that object is mixed into the event facade and the facade is provided
+ * in place of the original object.
+ *
+ * @method fire
+ * @param type {String|Object} The type of the event, or an object that contains
+ * a 'type' property.
+ * @param arguments {Object*} an arbitrary set of parameters to pass to
+ * the handler. If the first of these is an object literal and the event is
+ * configured to emit an event facade, the event facade will replace that
+ * parameter after the properties the object literal contains are copied to
+ * the event facade.
+ * @return {Event.Target} the event host
+ *
+ */
+ fire: function(type) {
+
+ var typeIncluded = L.isString(type),
+ t = (typeIncluded) ? type : (type && type.type),
+ ce, a, ret, pre=this._yuievt.config.prefix;
+
+ t = (pre) ? _getType(t, pre) : t;
+ ce = this.getEvent(t, true);
+
+ // this event has not been published or subscribed to
+ if (!ce) {
+
+ if (this._yuievt.hasTargets) {
+ a = (typeIncluded) ? arguments : Y.Array(arguments, 0, true).unshift(t);
+ return this.bubble(null, a, this);
+ }
+
+ // otherwise there is nothing to be done
+ ret = true;
+
+ } else {
+
+ a = Y.Array(arguments, (typeIncluded) ? 1 : 0, true);
+ ret = ce.fire.apply(ce, a);
+
+ // clear target for next fire()
+ ce.target = null;
+ }
+
+ return (this._yuievt.chain) ? this : ret;
+ },
+
+ /**
+ * Returns the custom event of the provided type has been created, a
+ * falsy value otherwise
+ * @method getEvent
+ * @param type {string} the type, or name of the event
+ * @param prefixed {string} if true, the type is prefixed already
+ * @return {Event.Custom} the custom event or null
+ */
+ getEvent: function(type, prefixed) {
+ var pre, e;
+ if (!prefixed) {
+ pre = this._yuievt.config.prefix;
+ type = (pre) ? _getType(type, pre) : type;
+ }
+ e = this._yuievt.events;
+ return (e && type in e) ? e[type] : null;
+ },
+
+ /**
+ * Subscribe to a custom event hosted by this object. The
+ * supplied callback will execute after any listeners add
+ * via the subscribe method, and after the default function,
+ * if configured for the event, has executed.
+ * @method after
+ * @param type {string} The type of the event
+ * @param fn {Function} The callback
+ * @return the event target or a detach handle per 'chain' config
+ */
+ after: function(type, fn) {
+
+ var a = Y.Array(arguments, 0, true);
+
+ switch (L.type(type)) {
+ case 'function':
+ return Y.Do.after.apply(Y.Do, arguments);
+ case 'object':
+ a[0]._after = true;
+ break;
+ default:
+ a[0] = AFTER_PREFIX + type;
+ }
+
+ return this.on.apply(this, a);
+
+ },
+
+ /**
+ * Executes the callback before a DOM event, custom event
+ * or method. If the first argument is a function, it
+ * is assumed the target is a method. For DOM and custom
+ * events, this is an alias for Y.on.
+ *
+ * For DOM and custom events:
+ * type, callback, context, 0-n arguments
+ *
+ * For methods:
+ * callback, object (method host), methodName, context, 0-n arguments
+ *
+ * @method before
+ * @return detach handle
+ * @deprecated use the on method
+ */
+ before: function() {
+ return this.on.apply(this, arguments);
+ }
+
+};
+
+Y.EventTarget = ET;
+
+// make Y an event target
+Y.mix(Y, ET.prototype, false, false, {
+ bubbles: false
+});
+
+ET.call(Y);
+
+YUI.Env.globalEvents = YUI.Env.globalEvents || new ET();
+
+/**
+ * Hosts YUI page level events. This is where events bubble to
+ * when the broadcast config is set to 2. This property is
+ * only available if the custom event module is loaded.
+ * @property Global
+ * @type EventTarget
+ * @for YUI
+ */
+Y.Global = YUI.Env.globalEvents;
+
+// @TODO implement a global namespace function on Y.Global?
+
+})();
+
+
+/**
+ * <code>YUI</code>'s <code>on</code> method is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events, DOM events, and
+ * function events. <code>detach</code> is also provided to remove listeners
+ * serviced by this function.
+ *
+ * The signature that <code>on</code> accepts varies depending on the type
+ * of event being consumed. Refer to the specific methods that will
+ * service a specific request for additional information about subscribing
+ * to that type of event.
+ *
+ * <ul>
+ * <li>Custom events. These events are defined by various
+ * modules in the library. This type of event is delegated to
+ * <code>EventTarget</code>'s <code>on</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('domready', function() { // start work });</code>
+ * </li>
+ * <li>DOM events. These are moments reported by the browser related
+ * to browser functionality and user interaction.
+ * This type of event is delegated to <code>Event</code>'s
+ * <code>attach</code> method.
+ * <ul>
+ * <li>The type of the event</li>
+ * <li>The callback to execute</li>
+ * <li>The specification for the Node(s) to attach the listener
+ * to. This can be a selector, collections, or Node/Element
+ * refereces.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example:
+ * <code>Y.on('click', function(e) { // something was clicked }, '#someelement');</code>
+ * </li>
+ * <li>Function events. These events can be used to react before or after a
+ * function is executed. This type of event is delegated to <code>Event.Do</code>'s
+ * <code>before</code> method.
+ * <ul>
+ * <li>The callback to execute</li>
+ * <li>The object that has the function that will be listened for.</li>
+ * <li>The name of the function to listen for.</li>
+ * <li>An optional context object</li>
+ * <li>0..n additional arguments to supply the callback.</li>
+ * </ul>
+ * Example <code>Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname');</code>
+ * </li>
+ * </ul>
+ *
+ * <code>on</code> corresponds to the moment before any default behavior of
+ * the event. <code>after</code> works the same way, but these listeners
+ * execute after the event's default behavior. <code>before</code> is an
+ * alias for <code>on</code>.
+ *
+ * @method on
+ * @param type** event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target** a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra** 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+/**
+ * after() is a unified interface for subscribing to
+ * most events exposed by YUI. This includes custom events,
+ * DOM events, and AOP events. This works the same way as
+ * the on() function, only it operates after any default
+ * behavior for the event has executed. @see <code>on</code> for more
+ * information.
+ * @method after
+ * @param type event type (this parameter does not apply for function events)
+ * @param fn the callback
+ * @param target a descriptor for the target (applies to custom events only).
+ * For function events, this is the object that contains the function to
+ * execute.
+ * @param extra 0..n Extra information a particular event may need. These
+ * will be documented with the event. In the case of function events, this
+ * is the name of the function to execute on the host. In the case of
+ * delegate listeners, this is the event delegation specification.
+ * @param context optionally change the value of 'this' in the callback
+ * @param args* 0..n additional arguments to pass to the callback.
+ * @return the event target or a detach handle per 'chain' config
+ * @for YUI
+ */
+
+
+}, '3.0.0' ,{requires:['oop']});
+YUI.add('event-custom-complex', function(Y) {
+
+
+/**
+ * Adds event facades, preventable default behavior, and bubbling.
+ * events.
+ * @module event-custom
+ * @submodule event-custom-complex
+ */
+
+(function() {
+
+var FACADE, FACADE_KEYS, CEProto = Y.CustomEvent.prototype;
+
+/**
+ * Wraps and protects a custom event for use when emitFacade is set to true.
+ * Requires the event-custom-complex module
+ * @class EventFacade
+ * @param e {Event} the custom event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ */
+
+Y.EventFacade = function(e, currentTarget) {
+
+ e = e || {};
+
+ /**
+ * The arguments passed to fire
+ * @property details
+ * @type Array
+ */
+ this.details = e.details;
+
+ /**
+ * The event type
+ * @property type
+ * @type string
+ */
+ this.type = e.type;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted eventtarget
+ * @propery target
+ * @type Node
+ */
+ this.target = e.target;
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = currentTarget;
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = e.relatedTarget;
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ e.stopPropagation();
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ e.stopImmediatePropagation();
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ */
+ this.preventDefault = function() {
+ e.preventDefault();
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ e.halt(immediate);
+ };
+
+};
+
+CEProto.fireComplex = function(args) {
+ var es = Y.Env._eventstack, ef, q, queue, ce, ret, events;
+
+ if (es) {
+ // queue this event if the current item in the queue bubbles
+ if (this.queuable && this.type != es.next.type) {
+ this.log('queue ' + this.type);
+ es.queue.push([this, args]);
+ return true;
+ }
+ } else {
+ Y.Env._eventstack = {
+ // id of the first event in the stack
+ id: this.id,
+ next: this,
+ silent: this.silent,
+ stopped: 0,
+ prevented: 0,
+ queue: []
+ };
+ es = Y.Env._eventstack;
+ }
+
+ this.stopped = 0;
+ this.prevented = 0;
+ this.target = this.target || this.host;
+
+ events = new Y.EventTarget({
+ fireOnce: true,
+ context: this.host
+ });
+
+ this.events = events;
+
+ if (this.preventedFn) {
+ events.on('prevented', this.preventedFn);
+ }
+
+ if (this.stoppedFn) {
+ events.on('stopped', this.stoppedFn);
+ }
+
+ this.currentTarget = this.host || this.currentTarget;
+
+ this.details = args.slice(); // original arguments in the details
+
+ // this.log("Firing " + this + ", " + "args: " + args);
+ this.log("Firing " + this.type);
+
+ this._facade = null; // kill facade to eliminate stale properties
+
+ ef = this._getFacade(args);
+
+ if (Y.Lang.isObject(args[0])) {
+ args[0] = ef;
+ } else {
+ args.unshift(ef);
+ }
+
+ if (this.hasSubscribers) {
+ this._procSubs(Y.merge(this.subscribers), args, ef);
+ }
+
+ // bubble if this is hosted in an event target and propagation has not been stopped
+ if (this.bubbles && this.host && this.host.bubble && !this.stopped) {
+ es.stopped = 0;
+ es.prevented = 0;
+ ret = this.host.bubble(this);
+
+ this.stopped = Math.max(this.stopped, es.stopped);
+ this.prevented = Math.max(this.prevented, es.prevented);
+
+ }
+
+ // execute the default behavior if not prevented
+ if (this.defaultFn && !this.prevented) {
+ this.defaultFn.apply(this.host || this, args);
+ }
+
+ // broadcast listeners are fired as discreet events on the
+ // YUI instance and potentially the YUI global.
+ this._broadcast(args);
+
+ // process after listeners. If the default behavior was
+ // prevented, the after events don't fire.
+ if (this.hasAfters && !this.prevented && this.stopped < 2) {
+ this._procSubs(Y.merge(this.afters), args, ef);
+ }
+
+ if (es.id === this.id) {
+ queue = es.queue;
+
+ while (queue.length) {
+ q = queue.pop();
+ ce = q[0];
+ es.stopped = 0;
+ es.prevented = 0;
+ // set up stack to allow the next item to be processed
+ es.next = ce;
+ ce.fire.apply(ce, q[1]);
+ }
+
+ Y.Env._eventstack = null;
+ }
+
+ return this.stopped ? false : true;
+};
+
+CEProto._getFacade = function() {
+
+ var ef = this._facade, o, o2,
+ args = this.details;
+
+ if (!ef) {
+ ef = new Y.EventFacade(this, this.currentTarget);
+ }
+
+ // if the first argument is an object literal, apply the
+ // properties to the event facade
+ o = args && args[0];
+
+ if (Y.Lang.isObject(o, true)) {
+
+ o2 = {};
+
+ // protect the event facade properties
+ Y.mix(o2, ef, true, FACADE_KEYS);
+
+ // mix the data
+ Y.mix(ef, o, true);
+
+ // restore ef
+ Y.mix(ef, o2, true, FACADE_KEYS);
+ }
+
+ // update the details field with the arguments
+ // ef.type = this.type;
+ ef.details = this.details;
+ ef.target = this.target;
+ ef.currentTarget = this.currentTarget;
+ ef.stopped = 0;
+ ef.prevented = 0;
+
+ this._facade = ef;
+
+ return this._facade;
+};
+
+/**
+ * Stop propagation to bubble targets
+ * @for CustomEvent
+ * @method stopPropagation
+ */
+CEProto.stopPropagation = function() {
+ this.stopped = 1;
+ Y.Env._eventstack.stopped = 1;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Stops propagation to bubble targets, and prevents any remaining
+ * subscribers on the current target from executing.
+ * @method stopImmediatePropagation
+ */
+CEProto.stopImmediatePropagation = function() {
+ this.stopped = 2;
+ Y.Env._eventstack.stopped = 2;
+ this.events.fire('stopped', this);
+};
+
+/**
+ * Prevents the execution of this event's defaultFn
+ * @method preventDefault
+ */
+CEProto.preventDefault = function() {
+ if (this.preventable) {
+ this.prevented = 1;
+ Y.Env._eventstack.prevented = 1;
+ this.events.fire('prevented', this);
+ }
+};
+
+/**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+CEProto.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ this.preventDefault();
+};
+
+/**
+ * Propagate an event. Requires the event-custom-complex module.
+ * @method bubble
+ * @param evt {Event.Custom} the custom event to propagate
+ * @return {boolean} the aggregated return value from Event.Custom.fire
+ * @for EventTarget
+ */
+Y.EventTarget.prototype.bubble = function(evt, args, target) {
+
+ var targs = this._yuievt.targets, ret = true,
+ t, type, ce, i, bc;
+
+ if (!evt || ((!evt.stopped) && targs)) {
+
+ for (i in targs) {
+ if (targs.hasOwnProperty(i)) {
+ t = targs[i];
+ type = evt && evt.type;
+ ce = t.getEvent(type, true);
+
+ // if this event was not published on the bubble target,
+ // publish it with sensible default properties
+ if (!ce) {
+
+ if (t._yuievt.hasTargets) {
+ t.bubble.call(t, evt, args, target);
+ }
+
+ } else {
+ ce.target = target || (evt && evt.target) || this;
+ ce.currentTarget = t;
+
+ bc = ce.broadcast;
+ ce.broadcast = false;
+ ret = ret && ce.fire.apply(ce, args || evt.details);
+ ce.broadcast = bc;
+
+ // stopPropagation() was called
+ if (ce.stopped) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+};
+
+FACADE = new Y.EventFacade();
+FACADE_KEYS = Y.Object.keys(FACADE);
+
+})();
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
+
+
+YUI.add('event-custom', function(Y){}, '3.0.0' ,{use:['event-custom-base', 'event-custom-complex']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-simulate', function(Y) {
+
+(function() {
+/**
+ * Synthetic DOM events
+ * @module event-simulate
+ * @requires event
+ */
+
+//shortcuts
+var L = Y.Lang,
+ array = Y.Array,
+ isFunction = L.isFunction,
+ isString = L.isString,
+ isBoolean = L.isBoolean,
+ isObject = L.isObject,
+ isNumber = L.isNumber,
+ doc = Y.config.doc,
+
+ //mouse events supported
+ mouseEvents = {
+ click: 1,
+ dblclick: 1,
+ mouseover: 1,
+ mouseout: 1,
+ mousedown: 1,
+ mouseup: 1,
+ mousemove: 1
+ },
+
+ //key events supported
+ keyEvents = {
+ keydown: 1,
+ keyup: 1,
+ keypress: 1
+ };
+
+/*
+ * Note: Intentionally not for YUIDoc generation.
+ * Simulates a key event using the given event information to populate
+ * the generated event object. This method does browser-equalizing
+ * calculations to account for differences in the DOM and IE event models
+ * as well as different browser quirks. Note: keydown causes Safari 2.x to
+ * crash.
+ * @method simulateKeyEvent
+ * @private
+ * @static
+ * @param {HTMLElement} target The target of the given event.
+ * @param {String} type The type of event to fire. This can be any one of
+ * the following: keyup, keydown, and keypress.
+ * @param {Boolean} bubbles (Optional) Indicates if the event can be
+ * bubbled up. DOM Level 3 specifies that all key events bubble by
+ * default. The default is true.
+ * @param {Boolean} cancelable (Optional) Indicates if the event can be
+ * canceled using preventDefault(). DOM Level 3 specifies that all
+ * key events can be cancelled. The default
+ * is true.
+ * @param {Window} view (Optional) The view containing the target. This is
+ * typically the window object. The default is window.
+ * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
+ * is pressed while the event is firing. The default is false.
+ * @param {int} keyCode (Optional) The code for the key that is in use.
+ * The default is 0.
+ * @param {int} charCode (Optional) The Unicode code for the character
+ * associated with the key being used. The default is 0.
+ */
+function simulateKeyEvent(target /*:HTMLElement*/, type /*:String*/,
+ bubbles /*:Boolean*/, cancelable /*:Boolean*/,
+ view /*:Window*/,
+ ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
+ shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
+ keyCode /*:int*/, charCode /*:int*/) /*:Void*/
+{
+ //check target
+ if (!target){
+ Y.error("simulateKeyEvent(): Invalid target.");
+ }
+
+ //check event type
+ if (isString(type)){
+ type = type.toLowerCase();
+ switch(type){
+ case "textevent": //DOM Level 3
+ type = "keypress";
+ break;
+ case "keyup":
+ case "keydown":
+ case "keypress":
+ break;
+ default:
+ Y.error("simulateKeyEvent(): Event type '" + type + "' not supported.");
+ }
+ } else {
+ Y.error("simulateKeyEvent(): Event type must be a string.");
+ }
+
+ //setup default values
+ if (!isBoolean(bubbles)){
+ bubbles = true; //all key events bubble
+ }
+ if (!isBoolean(cancelable)){
+ cancelable = true; //all key events can be cancelled
+ }
+ if (!isObject(view)){
+ view = window; //view is typically window
+ }
+ if (!isBoolean(ctrlKey)){
+ ctrlKey = false;
+ }
+ if (!isBoolean(altKey)){
+ altKey = false;
+ }
+ if (!isBoolean(shiftKey)){
+ shiftKey = false;
+ }
+ if (!isBoolean(metaKey)){
+ metaKey = false;
+ }
+ if (!isNumber(keyCode)){
+ keyCode = 0;
+ }
+ if (!isNumber(charCode)){
+ charCode = 0;
+ }
+
+ //try to create a mouse event
+ var customEvent /*:MouseEvent*/ = null;
+
+ //check for DOM-compliant browsers first
+ if (isFunction(doc.createEvent)){
+
+ try {
+
+ //try to create key event
+ customEvent = doc.createEvent("KeyEvents");
+
+ /*
+ * Interesting problem: Firefox implemented a non-standard
+ * version of initKeyEvent() based on DOM Level 2 specs.
+ * Key event was removed from DOM Level 2 and re-introduced
+ * in DOM Level 3 with a different interface. Firefox is the
+ * only browser with any implementation of Key Events, so for
+ * now, assume it's Firefox if the above line doesn't error.
+ */
+ // @TODO: Decipher between Firefox's implementation and a correct one.
+ customEvent.initKeyEvent(type, bubbles, cancelable, view, ctrlKey,
+ altKey, shiftKey, metaKey, keyCode, charCode);
+
+ } catch (ex /*:Error*/){
+
+ /*
+ * If it got here, that means key events aren't officially supported.
+ * Safari/WebKit is a real problem now. WebKit 522 won't let you
+ * set keyCode, charCode, or other properties if you use a
+ * UIEvent, so we first must try to create a generic event. The
+ * fun part is that this will throw an error on Safari 2.x. The
+ * end result is that we need another try...catch statement just to
+ * deal with this mess.
+ */
+ try {
+
+ //try to create generic event - will fail in Safari 2.x
+ customEvent = doc.createEvent("Events");
+
+ } catch (uierror /*:Error*/){
+
+ //the above failed, so create a UIEvent for Safari 2.x
+ customEvent = doc.createEvent("UIEvents");
+
+ } finally {
+
+ customEvent.initEvent(type, bubbles, cancelable);
+
+ //initialize
+ customEvent.view = view;
+ customEvent.altKey = altKey;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.shiftKey = shiftKey;
+ customEvent.metaKey = metaKey;
+ customEvent.keyCode = keyCode;
+ customEvent.charCode = charCode;
+
+ }
+
+ }
+
+ //fire the event
+ target.dispatchEvent(customEvent);
+
+ } else if (isObject(doc.createEventObject)){ //IE
+
+ //create an IE event object
+ customEvent = doc.createEventObject();
+
+ //assign available properties
+ customEvent.bubbles = bubbles;
+ customEvent.cancelable = cancelable;
+ customEvent.view = view;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.altKey = altKey;
+ customEvent.shiftKey = shiftKey;
+ customEvent.metaKey = metaKey;
+
+ /*
+ * IE doesn't support charCode explicitly. CharCode should
+ * take precedence over any keyCode value for accurate
+ * representation.
+ */
+ customEvent.keyCode = (charCode > 0) ? charCode : keyCode;
+
+ //fire the event
+ target.fireEvent("on" + type, customEvent);
+
+ } else {
+ Y.error("simulateKeyEvent(): No event simulation framework present.");
+ }
+}
+
+/*
+ * Note: Intentionally not for YUIDoc generation.
+ * Simulates a mouse event using the given event information to populate
+ * the generated event object. This method does browser-equalizing
+ * calculations to account for differences in the DOM and IE event models
+ * as well as different browser quirks.
+ * @method simulateMouseEvent
+ * @private
+ * @static
+ * @param {HTMLElement} target The target of the given event.
+ * @param {String} type The type of event to fire. This can be any one of
+ * the following: click, dblclick, mousedown, mouseup, mouseout,
+ * mouseover, and mousemove.
+ * @param {Boolean} bubbles (Optional) Indicates if the event can be
+ * bubbled up. DOM Level 2 specifies that all mouse events bubble by
+ * default. The default is true.
+ * @param {Boolean} cancelable (Optional) Indicates if the event can be
+ * canceled using preventDefault(). DOM Level 2 specifies that all
+ * mouse events except mousemove can be cancelled. The default
+ * is true for all events except mousemove, for which the default
+ * is false.
+ * @param {Window} view (Optional) The view containing the target. This is
+ * typically the window object. The default is window.
+ * @param {int} detail (Optional) The number of times the mouse button has
+ * been used. The default value is 1.
+ * @param {int} screenX (Optional) The x-coordinate on the screen at which
+ * point the event occured. The default is 0.
+ * @param {int} screenY (Optional) The y-coordinate on the screen at which
+ * point the event occured. The default is 0.
+ * @param {int} clientX (Optional) The x-coordinate on the client at which
+ * point the event occured. The default is 0.
+ * @param {int} clientY (Optional) The y-coordinate on the client at which
+ * point the event occured. The default is 0.
+ * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
+ * is pressed while the event is firing. The default is false.
+ * @param {int} button (Optional) The button being pressed while the event
+ * is executing. The value should be 0 for the primary mouse button
+ * (typically the left button), 1 for the terciary mouse button
+ * (typically the middle button), and 2 for the secondary mouse button
+ * (typically the right button). The default is 0.
+ * @param {HTMLElement} relatedTarget (Optional) For mouseout events,
+ * this is the element that the mouse has moved to. For mouseover
+ * events, this is the element that the mouse has moved from. This
+ * argument is ignored for all other events. The default is null.
+ */
+function simulateMouseEvent(target /*:HTMLElement*/, type /*:String*/,
+ bubbles /*:Boolean*/, cancelable /*:Boolean*/,
+ view /*:Window*/, detail /*:int*/,
+ screenX /*:int*/, screenY /*:int*/,
+ clientX /*:int*/, clientY /*:int*/,
+ ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
+ shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
+ button /*:int*/, relatedTarget /*:HTMLElement*/) /*:Void*/
+{
+
+ //check target
+ if (!target){
+ Y.error("simulateMouseEvent(): Invalid target.");
+ }
+
+ //check event type
+ if (isString(type)){
+ type = type.toLowerCase();
+
+ //make sure it's a supported mouse event
+ if (!mouseEvents[type]){
+ Y.error("simulateMouseEvent(): Event type '" + type + "' not supported.");
+ }
+ } else {
+ Y.error("simulateMouseEvent(): Event type must be a string.");
+ }
+
+ //setup default values
+ if (!isBoolean(bubbles)){
+ bubbles = true; //all mouse events bubble
+ }
+ if (!isBoolean(cancelable)){
+ cancelable = (type != "mousemove"); //mousemove is the only one that can't be cancelled
+ }
+ if (!isObject(view)){
+ view = window; //view is typically window
+ }
+ if (!isNumber(detail)){
+ detail = 1; //number of mouse clicks must be at least one
+ }
+ if (!isNumber(screenX)){
+ screenX = 0;
+ }
+ if (!isNumber(screenY)){
+ screenY = 0;
+ }
+ if (!isNumber(clientX)){
+ clientX = 0;
+ }
+ if (!isNumber(clientY)){
+ clientY = 0;
+ }
+ if (!isBoolean(ctrlKey)){
+ ctrlKey = false;
+ }
+ if (!isBoolean(altKey)){
+ altKey = false;
+ }
+ if (!isBoolean(shiftKey)){
+ shiftKey = false;
+ }
+ if (!isBoolean(metaKey)){
+ metaKey = false;
+ }
+ if (!isNumber(button)){
+ button = 0;
+ }
+
+ //try to create a mouse event
+ var customEvent /*:MouseEvent*/ = null;
+
+ //check for DOM-compliant browsers first
+ if (isFunction(doc.createEvent)){
+
+ customEvent = doc.createEvent("MouseEvents");
+
+ //Safari 2.x (WebKit 418) still doesn't implement initMouseEvent()
+ if (customEvent.initMouseEvent){
+ customEvent.initMouseEvent(type, bubbles, cancelable, view, detail,
+ screenX, screenY, clientX, clientY,
+ ctrlKey, altKey, shiftKey, metaKey,
+ button, relatedTarget);
+ } else { //Safari
+
+ //the closest thing available in Safari 2.x is UIEvents
+ customEvent = doc.createEvent("UIEvents");
+ customEvent.initEvent(type, bubbles, cancelable);
+ customEvent.view = view;
+ customEvent.detail = detail;
+ customEvent.screenX = screenX;
+ customEvent.screenY = screenY;
+ customEvent.clientX = clientX;
+ customEvent.clientY = clientY;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.altKey = altKey;
+ customEvent.metaKey = metaKey;
+ customEvent.shiftKey = shiftKey;
+ customEvent.button = button;
+ customEvent.relatedTarget = relatedTarget;
+ }
+
+ /*
+ * Check to see if relatedTarget has been assigned. Firefox
+ * versions less than 2.0 don't allow it to be assigned via
+ * initMouseEvent() and the property is readonly after event
+ * creation, so in order to keep YAHOO.util.getRelatedTarget()
+ * working, assign to the IE proprietary toElement property
+ * for mouseout event and fromElement property for mouseover
+ * event.
+ */
+ if (relatedTarget && !customEvent.relatedTarget){
+ if (type == "mouseout"){
+ customEvent.toElement = relatedTarget;
+ } else if (type == "mouseover"){
+ customEvent.fromElement = relatedTarget;
+ }
+ }
+
+ //fire the event
+ target.dispatchEvent(customEvent);
+
+ } else if (isObject(doc.createEventObject)){ //IE
+
+ //create an IE event object
+ customEvent = doc.createEventObject();
+
+ //assign available properties
+ customEvent.bubbles = bubbles;
+ customEvent.cancelable = cancelable;
+ customEvent.view = view;
+ customEvent.detail = detail;
+ customEvent.screenX = screenX;
+ customEvent.screenY = screenY;
+ customEvent.clientX = clientX;
+ customEvent.clientY = clientY;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.altKey = altKey;
+ customEvent.metaKey = metaKey;
+ customEvent.shiftKey = shiftKey;
+
+ //fix button property for IE's wacky implementation
+ switch(button){
+ case 0:
+ customEvent.button = 1;
+ break;
+ case 1:
+ customEvent.button = 4;
+ break;
+ case 2:
+ //leave as is
+ break;
+ default:
+ customEvent.button = 0;
+ }
+
+ /*
+ * Have to use relatedTarget because IE won't allow assignment
+ * to toElement or fromElement on generic events. This keeps
+ * YAHOO.util.customEvent.getRelatedTarget() functional.
+ */
+ customEvent.relatedTarget = relatedTarget;
+
+ //fire the event
+ target.fireEvent("on" + type, customEvent);
+
+ } else {
+ Y.error("simulateMouseEvent(): No event simulation framework present.");
+ }
+}
+
+
+/**
+ * Simulates the event with the given name on a target.
+ * @param {HTMLElement} target The DOM element that's the target of the event.
+ * @param {String} type The type of event to simulate (i.e., "click").
+ * @param {Object} options (Optional) Extra options to copy onto the event object.
+ * @return {void}
+ * @method simulate
+ * @static
+ */
+Y.Event.simulate = function(target, type, options){
+
+ options = options || {};
+
+ if (mouseEvents[type]){
+ simulateMouseEvent(target, type, options.bubbles,
+ options.cancelable, options.view, options.detail, options.screenX,
+ options.screenY, options.clientX, options.clientY, options.ctrlKey,
+ options.altKey, options.shiftKey, options.metaKey, options.button,
+ options.relatedTarget);
+ } else if (keyEvents[type]){
+ simulateKeyEvent(target, type, options.bubbles,
+ options.cancelable, options.view, options.ctrlKey,
+ options.altKey, options.shiftKey, options.metaKey,
+ options.keyCode, options.charCode);
+ } else {
+ Y.error("simulate(): Event '" + type + "' can't be simulated.");
+ }
+};
+
+/*
+ * @TODO: focus(), blur(), submit()
+ */
+
+})();
+
+
+}, '3.0.0' ,{requires:['event']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-simulate",function(A){(function(){var H=A.Lang,G=A.Array,D=H.isFunction,C=H.isString,E=H.isBoolean,M=H.isObject,K=H.isNumber,J=A.config.doc,N={click:1,dblclick:1,mouseover:1,mouseout:1,mousedown:1,mouseup:1,mousemove:1},I={keydown:1,keyup:1,keypress:1};function F(S,W,R,P,Y,O,L,X,U,a,Z){if(!S){A.error("simulateKeyEvent(): Invalid target.");}if(C(W)){W=W.toLowerCase();switch(W){case"textevent":W="keypress";break;case"keyup":case"keydown":case"keypress":break;default:A.error("simulateKeyEvent(): Event type '"+W+"' not supported.");}}else{A.error("simulateKeyEvent(): Event type must be a string.");}if(!E(R)){R=true;}if(!E(P)){P=true;}if(!M(Y)){Y=window;}if(!E(O)){O=false;}if(!E(L)){L=false;}if(!E(X)){X=false;}if(!E(U)){U=false;}if(!K(a)){a=0;}if(!K(Z)){Z=0;}var V=null;if(D(J.createEvent)){try{V=J.createEvent("KeyEvents");V.initKeyEvent(W,R,P,Y,O,L,X,U,a,Z);}catch(T){try{V=J.createEvent("Events");}catch(Q){V=J.createEvent("UIEvents");}finally{V.initEvent(W,R,P);V.view=Y;V.altKey=L;V.ctrlKey=O;V.shiftKey=X;V.metaKey=U;V.keyCode=a;V.charCode=Z;}}S.dispatchEvent(V);}else{if(M(J.createEventObject)){V=J.createEventObject();V.bubbles=R;V.cancelable=P;V.view=Y;V.ctrlKey=O;V.altKey=L;V.shiftKey=X;V.metaKey=U;V.keyCode=(Z>0)?Z:a;S.fireEvent("on"+W,V);}else{A.error("simulateKeyEvent(): No event simulation framework present.");}}}function B(X,c,U,R,d,W,T,S,Q,O,P,L,b,Z,V,Y){if(!X){A.error("simulateMouseEvent(): Invalid target.");}if(C(c)){c=c.toLowerCase();if(!N[c]){A.error("simulateMouseEvent(): Event type '"+c+"' not supported.");}}else{A.error("simulateMouseEvent(): Event type must be a string.");}if(!E(U)){U=true;}if(!E(R)){R=(c!="mousemove");}if(!M(d)){d=window;}if(!K(W)){W=1;}if(!K(T)){T=0;}if(!K(S)){S=0;}if(!K(Q)){Q=0;}if(!K(O)){O=0;}if(!E(P)){P=false;}if(!E(L)){L=false;}if(!E(b)){b=false;}if(!E(Z)){Z=false;}if(!K(V)){V=0;}var a=null;if(D(J.createEvent)){a=J.createEvent("MouseEvents");if(a.initMouseEvent){a.initMouseEvent(c,U,R,d,W,T,S,Q,O,P,L,b,Z,V,Y);}else{a=J.createEvent("UIEvents");a.initEvent(c,U,R);a.view=d;a.detail=W;a.screenX=T;a.screenY=S;a.clientX=Q;a.clientY=O;a.ctrlKey=P;a.altKey=L;a.metaKey=Z;a.shiftKey=b;a.button=V;a.relatedTarget=Y;}if(Y&&!a.relatedTarget){if(c=="mouseout"){a.toElement=Y;}else{if(c=="mouseover"){a.fromElement=Y;}}}X.dispatchEvent(a);}else{if(M(J.createEventObject)){a=J.createEventObject();a.bubbles=U;a.cancelable=R;a.view=d;a.detail=W;a.screenX=T;a.screenY=S;a.clientX=Q;a.clientY=O;a.ctrlKey=P;a.altKey=L;a.metaKey=Z;a.shiftKey=b;switch(V){case 0:a.button=1;break;case 1:a.button=4;break;case 2:break;default:a.button=0;}a.relatedTarget=Y;X.fireEvent("on"+c,a);}else{A.error("simulateMouseEvent(): No event simulation framework present.");}}}A.Event.simulate=function(P,O,L){L=L||{};if(N[O]){B(P,O,L.bubbles,L.cancelable,L.view,L.detail,L.screenX,L.screenY,L.clientX,L.clientY,L.ctrlKey,L.altKey,L.shiftKey,L.metaKey,L.button,L.relatedTarget);}else{if(I[O]){F(P,O,L.bubbles,L.cancelable,L.view,L.ctrlKey,L.altKey,L.shiftKey,L.metaKey,L.keyCode,L.charCode);}else{A.error("simulate(): Event '"+O+"' can't be simulated.");}}};})();},"3.0.0",{requires:["event"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-simulate', function(Y) {
+
+(function() {
+/**
+ * Synthetic DOM events
+ * @module event-simulate
+ * @requires event
+ */
+
+//shortcuts
+var L = Y.Lang,
+ array = Y.Array,
+ isFunction = L.isFunction,
+ isString = L.isString,
+ isBoolean = L.isBoolean,
+ isObject = L.isObject,
+ isNumber = L.isNumber,
+ doc = Y.config.doc,
+
+ //mouse events supported
+ mouseEvents = {
+ click: 1,
+ dblclick: 1,
+ mouseover: 1,
+ mouseout: 1,
+ mousedown: 1,
+ mouseup: 1,
+ mousemove: 1
+ },
+
+ //key events supported
+ keyEvents = {
+ keydown: 1,
+ keyup: 1,
+ keypress: 1
+ };
+
+/*
+ * Note: Intentionally not for YUIDoc generation.
+ * Simulates a key event using the given event information to populate
+ * the generated event object. This method does browser-equalizing
+ * calculations to account for differences in the DOM and IE event models
+ * as well as different browser quirks. Note: keydown causes Safari 2.x to
+ * crash.
+ * @method simulateKeyEvent
+ * @private
+ * @static
+ * @param {HTMLElement} target The target of the given event.
+ * @param {String} type The type of event to fire. This can be any one of
+ * the following: keyup, keydown, and keypress.
+ * @param {Boolean} bubbles (Optional) Indicates if the event can be
+ * bubbled up. DOM Level 3 specifies that all key events bubble by
+ * default. The default is true.
+ * @param {Boolean} cancelable (Optional) Indicates if the event can be
+ * canceled using preventDefault(). DOM Level 3 specifies that all
+ * key events can be cancelled. The default
+ * is true.
+ * @param {Window} view (Optional) The view containing the target. This is
+ * typically the window object. The default is window.
+ * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
+ * is pressed while the event is firing. The default is false.
+ * @param {int} keyCode (Optional) The code for the key that is in use.
+ * The default is 0.
+ * @param {int} charCode (Optional) The Unicode code for the character
+ * associated with the key being used. The default is 0.
+ */
+function simulateKeyEvent(target /*:HTMLElement*/, type /*:String*/,
+ bubbles /*:Boolean*/, cancelable /*:Boolean*/,
+ view /*:Window*/,
+ ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
+ shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
+ keyCode /*:int*/, charCode /*:int*/) /*:Void*/
+{
+ //check target
+ if (!target){
+ Y.error("simulateKeyEvent(): Invalid target.");
+ }
+
+ //check event type
+ if (isString(type)){
+ type = type.toLowerCase();
+ switch(type){
+ case "textevent": //DOM Level 3
+ type = "keypress";
+ break;
+ case "keyup":
+ case "keydown":
+ case "keypress":
+ break;
+ default:
+ Y.error("simulateKeyEvent(): Event type '" + type + "' not supported.");
+ }
+ } else {
+ Y.error("simulateKeyEvent(): Event type must be a string.");
+ }
+
+ //setup default values
+ if (!isBoolean(bubbles)){
+ bubbles = true; //all key events bubble
+ }
+ if (!isBoolean(cancelable)){
+ cancelable = true; //all key events can be cancelled
+ }
+ if (!isObject(view)){
+ view = window; //view is typically window
+ }
+ if (!isBoolean(ctrlKey)){
+ ctrlKey = false;
+ }
+ if (!isBoolean(altKey)){
+ altKey = false;
+ }
+ if (!isBoolean(shiftKey)){
+ shiftKey = false;
+ }
+ if (!isBoolean(metaKey)){
+ metaKey = false;
+ }
+ if (!isNumber(keyCode)){
+ keyCode = 0;
+ }
+ if (!isNumber(charCode)){
+ charCode = 0;
+ }
+
+ //try to create a mouse event
+ var customEvent /*:MouseEvent*/ = null;
+
+ //check for DOM-compliant browsers first
+ if (isFunction(doc.createEvent)){
+
+ try {
+
+ //try to create key event
+ customEvent = doc.createEvent("KeyEvents");
+
+ /*
+ * Interesting problem: Firefox implemented a non-standard
+ * version of initKeyEvent() based on DOM Level 2 specs.
+ * Key event was removed from DOM Level 2 and re-introduced
+ * in DOM Level 3 with a different interface. Firefox is the
+ * only browser with any implementation of Key Events, so for
+ * now, assume it's Firefox if the above line doesn't error.
+ */
+ // @TODO: Decipher between Firefox's implementation and a correct one.
+ customEvent.initKeyEvent(type, bubbles, cancelable, view, ctrlKey,
+ altKey, shiftKey, metaKey, keyCode, charCode);
+
+ } catch (ex /*:Error*/){
+
+ /*
+ * If it got here, that means key events aren't officially supported.
+ * Safari/WebKit is a real problem now. WebKit 522 won't let you
+ * set keyCode, charCode, or other properties if you use a
+ * UIEvent, so we first must try to create a generic event. The
+ * fun part is that this will throw an error on Safari 2.x. The
+ * end result is that we need another try...catch statement just to
+ * deal with this mess.
+ */
+ try {
+
+ //try to create generic event - will fail in Safari 2.x
+ customEvent = doc.createEvent("Events");
+
+ } catch (uierror /*:Error*/){
+
+ //the above failed, so create a UIEvent for Safari 2.x
+ customEvent = doc.createEvent("UIEvents");
+
+ } finally {
+
+ customEvent.initEvent(type, bubbles, cancelable);
+
+ //initialize
+ customEvent.view = view;
+ customEvent.altKey = altKey;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.shiftKey = shiftKey;
+ customEvent.metaKey = metaKey;
+ customEvent.keyCode = keyCode;
+ customEvent.charCode = charCode;
+
+ }
+
+ }
+
+ //fire the event
+ target.dispatchEvent(customEvent);
+
+ } else if (isObject(doc.createEventObject)){ //IE
+
+ //create an IE event object
+ customEvent = doc.createEventObject();
+
+ //assign available properties
+ customEvent.bubbles = bubbles;
+ customEvent.cancelable = cancelable;
+ customEvent.view = view;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.altKey = altKey;
+ customEvent.shiftKey = shiftKey;
+ customEvent.metaKey = metaKey;
+
+ /*
+ * IE doesn't support charCode explicitly. CharCode should
+ * take precedence over any keyCode value for accurate
+ * representation.
+ */
+ customEvent.keyCode = (charCode > 0) ? charCode : keyCode;
+
+ //fire the event
+ target.fireEvent("on" + type, customEvent);
+
+ } else {
+ Y.error("simulateKeyEvent(): No event simulation framework present.");
+ }
+}
+
+/*
+ * Note: Intentionally not for YUIDoc generation.
+ * Simulates a mouse event using the given event information to populate
+ * the generated event object. This method does browser-equalizing
+ * calculations to account for differences in the DOM and IE event models
+ * as well as different browser quirks.
+ * @method simulateMouseEvent
+ * @private
+ * @static
+ * @param {HTMLElement} target The target of the given event.
+ * @param {String} type The type of event to fire. This can be any one of
+ * the following: click, dblclick, mousedown, mouseup, mouseout,
+ * mouseover, and mousemove.
+ * @param {Boolean} bubbles (Optional) Indicates if the event can be
+ * bubbled up. DOM Level 2 specifies that all mouse events bubble by
+ * default. The default is true.
+ * @param {Boolean} cancelable (Optional) Indicates if the event can be
+ * canceled using preventDefault(). DOM Level 2 specifies that all
+ * mouse events except mousemove can be cancelled. The default
+ * is true for all events except mousemove, for which the default
+ * is false.
+ * @param {Window} view (Optional) The view containing the target. This is
+ * typically the window object. The default is window.
+ * @param {int} detail (Optional) The number of times the mouse button has
+ * been used. The default value is 1.
+ * @param {int} screenX (Optional) The x-coordinate on the screen at which
+ * point the event occured. The default is 0.
+ * @param {int} screenY (Optional) The y-coordinate on the screen at which
+ * point the event occured. The default is 0.
+ * @param {int} clientX (Optional) The x-coordinate on the client at which
+ * point the event occured. The default is 0.
+ * @param {int} clientY (Optional) The y-coordinate on the client at which
+ * point the event occured. The default is 0.
+ * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
+ * is pressed while the event is firing. The default is false.
+ * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
+ * is pressed while the event is firing. The default is false.
+ * @param {int} button (Optional) The button being pressed while the event
+ * is executing. The value should be 0 for the primary mouse button
+ * (typically the left button), 1 for the terciary mouse button
+ * (typically the middle button), and 2 for the secondary mouse button
+ * (typically the right button). The default is 0.
+ * @param {HTMLElement} relatedTarget (Optional) For mouseout events,
+ * this is the element that the mouse has moved to. For mouseover
+ * events, this is the element that the mouse has moved from. This
+ * argument is ignored for all other events. The default is null.
+ */
+function simulateMouseEvent(target /*:HTMLElement*/, type /*:String*/,
+ bubbles /*:Boolean*/, cancelable /*:Boolean*/,
+ view /*:Window*/, detail /*:int*/,
+ screenX /*:int*/, screenY /*:int*/,
+ clientX /*:int*/, clientY /*:int*/,
+ ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
+ shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
+ button /*:int*/, relatedTarget /*:HTMLElement*/) /*:Void*/
+{
+
+ //check target
+ if (!target){
+ Y.error("simulateMouseEvent(): Invalid target.");
+ }
+
+ //check event type
+ if (isString(type)){
+ type = type.toLowerCase();
+
+ //make sure it's a supported mouse event
+ if (!mouseEvents[type]){
+ Y.error("simulateMouseEvent(): Event type '" + type + "' not supported.");
+ }
+ } else {
+ Y.error("simulateMouseEvent(): Event type must be a string.");
+ }
+
+ //setup default values
+ if (!isBoolean(bubbles)){
+ bubbles = true; //all mouse events bubble
+ }
+ if (!isBoolean(cancelable)){
+ cancelable = (type != "mousemove"); //mousemove is the only one that can't be cancelled
+ }
+ if (!isObject(view)){
+ view = window; //view is typically window
+ }
+ if (!isNumber(detail)){
+ detail = 1; //number of mouse clicks must be at least one
+ }
+ if (!isNumber(screenX)){
+ screenX = 0;
+ }
+ if (!isNumber(screenY)){
+ screenY = 0;
+ }
+ if (!isNumber(clientX)){
+ clientX = 0;
+ }
+ if (!isNumber(clientY)){
+ clientY = 0;
+ }
+ if (!isBoolean(ctrlKey)){
+ ctrlKey = false;
+ }
+ if (!isBoolean(altKey)){
+ altKey = false;
+ }
+ if (!isBoolean(shiftKey)){
+ shiftKey = false;
+ }
+ if (!isBoolean(metaKey)){
+ metaKey = false;
+ }
+ if (!isNumber(button)){
+ button = 0;
+ }
+
+ //try to create a mouse event
+ var customEvent /*:MouseEvent*/ = null;
+
+ //check for DOM-compliant browsers first
+ if (isFunction(doc.createEvent)){
+
+ customEvent = doc.createEvent("MouseEvents");
+
+ //Safari 2.x (WebKit 418) still doesn't implement initMouseEvent()
+ if (customEvent.initMouseEvent){
+ customEvent.initMouseEvent(type, bubbles, cancelable, view, detail,
+ screenX, screenY, clientX, clientY,
+ ctrlKey, altKey, shiftKey, metaKey,
+ button, relatedTarget);
+ } else { //Safari
+
+ //the closest thing available in Safari 2.x is UIEvents
+ customEvent = doc.createEvent("UIEvents");
+ customEvent.initEvent(type, bubbles, cancelable);
+ customEvent.view = view;
+ customEvent.detail = detail;
+ customEvent.screenX = screenX;
+ customEvent.screenY = screenY;
+ customEvent.clientX = clientX;
+ customEvent.clientY = clientY;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.altKey = altKey;
+ customEvent.metaKey = metaKey;
+ customEvent.shiftKey = shiftKey;
+ customEvent.button = button;
+ customEvent.relatedTarget = relatedTarget;
+ }
+
+ /*
+ * Check to see if relatedTarget has been assigned. Firefox
+ * versions less than 2.0 don't allow it to be assigned via
+ * initMouseEvent() and the property is readonly after event
+ * creation, so in order to keep YAHOO.util.getRelatedTarget()
+ * working, assign to the IE proprietary toElement property
+ * for mouseout event and fromElement property for mouseover
+ * event.
+ */
+ if (relatedTarget && !customEvent.relatedTarget){
+ if (type == "mouseout"){
+ customEvent.toElement = relatedTarget;
+ } else if (type == "mouseover"){
+ customEvent.fromElement = relatedTarget;
+ }
+ }
+
+ //fire the event
+ target.dispatchEvent(customEvent);
+
+ } else if (isObject(doc.createEventObject)){ //IE
+
+ //create an IE event object
+ customEvent = doc.createEventObject();
+
+ //assign available properties
+ customEvent.bubbles = bubbles;
+ customEvent.cancelable = cancelable;
+ customEvent.view = view;
+ customEvent.detail = detail;
+ customEvent.screenX = screenX;
+ customEvent.screenY = screenY;
+ customEvent.clientX = clientX;
+ customEvent.clientY = clientY;
+ customEvent.ctrlKey = ctrlKey;
+ customEvent.altKey = altKey;
+ customEvent.metaKey = metaKey;
+ customEvent.shiftKey = shiftKey;
+
+ //fix button property for IE's wacky implementation
+ switch(button){
+ case 0:
+ customEvent.button = 1;
+ break;
+ case 1:
+ customEvent.button = 4;
+ break;
+ case 2:
+ //leave as is
+ break;
+ default:
+ customEvent.button = 0;
+ }
+
+ /*
+ * Have to use relatedTarget because IE won't allow assignment
+ * to toElement or fromElement on generic events. This keeps
+ * YAHOO.util.customEvent.getRelatedTarget() functional.
+ */
+ customEvent.relatedTarget = relatedTarget;
+
+ //fire the event
+ target.fireEvent("on" + type, customEvent);
+
+ } else {
+ Y.error("simulateMouseEvent(): No event simulation framework present.");
+ }
+}
+
+
+/**
+ * Simulates the event with the given name on a target.
+ * @param {HTMLElement} target The DOM element that's the target of the event.
+ * @param {String} type The type of event to simulate (i.e., "click").
+ * @param {Object} options (Optional) Extra options to copy onto the event object.
+ * @return {void}
+ * @method simulate
+ * @static
+ */
+Y.Event.simulate = function(target, type, options){
+
+ options = options || {};
+
+ if (mouseEvents[type]){
+ simulateMouseEvent(target, type, options.bubbles,
+ options.cancelable, options.view, options.detail, options.screenX,
+ options.screenY, options.clientX, options.clientY, options.ctrlKey,
+ options.altKey, options.shiftKey, options.metaKey, options.button,
+ options.relatedTarget);
+ } else if (keyEvents[type]){
+ simulateKeyEvent(target, type, options.bubbles,
+ options.cancelable, options.view, options.ctrlKey,
+ options.altKey, options.shiftKey, options.metaKey,
+ options.keyCode, options.charCode);
+ } else {
+ Y.error("simulate(): Event '" + type + "' can't be simulated.");
+ }
+};
+
+/*
+ * @TODO: focus(), blur(), submit()
+ */
+
+})();
+
+
+}, '3.0.0' ,{requires:['event']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+(function() {
+
+
+// Unlike most of the library, this code has to be executed as soon as it is
+// introduced into the page -- and it should only be executed one time
+// regardless of the number of instances that use it.
+
+var GLOBAL_ENV = YUI.Env,
+
+ C = YUI.config,
+
+ D = C.doc,
+
+ POLL_INTERVAL = C.pollInterval || 40,
+
+ _ready = function(e) {
+ GLOBAL_ENV._ready();
+ };
+
+ if (!GLOBAL_ENV._ready) {
+
+ GLOBAL_ENV._ready = function() {
+ if (!GLOBAL_ENV.DOMReady) {
+ GLOBAL_ENV.DOMReady=true;
+
+ // Remove the DOMContentLoaded (FF/Opera/Safari)
+ if (D.removeEventListener) {
+ D.removeEventListener("DOMContentLoaded", _ready, false);
+ }
+ }
+ };
+
+ // create custom event
+
+/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
+
+ // Internet Explorer: use the readyState of a defered script.
+ // This isolates what appears to be a safe moment to manipulate
+ // the DOM prior to when the document's readyState suggests
+ // it is safe to do so.
+ if (navigator.userAgent.match(/MSIE/)) {
+
+ if (self !== self.top) {
+ document.onreadystatechange = function() {
+ if (document.readyState == 'complete') {
+ document.onreadystatechange = null;
+ _ready();
+ }
+ };
+ } else {
+
+ GLOBAL_ENV._dri = setInterval(function() {
+ try {
+ // throws an error if doc is not ready
+ document.documentElement.doScroll('left');
+ clearInterval(GLOBAL_ENV._dri);
+ GLOBAL_ENV._dri = null;
+ _ready();
+ } catch (ex) {
+ }
+ }, POLL_INTERVAL);
+ }
+
+ // FireFox, Opera, Safari 3+: These browsers provide a event for this
+ // moment.
+ } else {
+ D.addEventListener("DOMContentLoaded", _ready, false);
+ }
+
+ /////////////////////////////////////////////////////////////
+ }
+
+})();
+YUI.add('event-base', function(Y) {
+
+(function() {
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+var GLOBAL_ENV = YUI.Env,
+
+ yready = function() {
+ Y.fire('domready');
+ };
+
+Y.publish('domready', {
+ fireOnce: true
+});
+
+if (GLOBAL_ENV.DOMReady) {
+ // console.log('DOMReady already fired', 'info', 'event');
+ yready();
+} else {
+ // console.log('setting up before listener', 'info', 'event');
+ // console.log('env: ' + YUI.Env.windowLoaded, 'info', 'event');
+ Y.before(yready, GLOBAL_ENV, "_ready");
+}
+
+})();
+(function() {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Wraps a DOM event, properties requiring browser abstraction are
+ * fixed here. Provids a security layer when required.
+ * @class DOMEventFacade
+ * @param ev {Event} the DOM event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
+ */
+
+/*
+ * @TODO constants? LEFTBUTTON, MIDDLEBUTTON, RIGHTBUTTON, keys
+ */
+
+/*
+
+var whitelist = {
+ altKey : 1,
+ // "button" : 1, // we supply
+ // "bubbles" : 1, // needed?
+ // "cancelable" : 1, // needed?
+ // "charCode" : 1, // we supply
+ cancelBubble : 1,
+ // "currentTarget" : 1, // we supply
+ ctrlKey : 1,
+ clientX : 1, // needed?
+ clientY : 1, // needed?
+ detail : 1, // not fully implemented
+ // "fromElement" : 1,
+ keyCode : 1,
+ // "height" : 1, // needed?
+ // "initEvent" : 1, // need the init events?
+ // "initMouseEvent" : 1,
+ // "initUIEvent" : 1,
+ // "layerX" : 1, // needed?
+ // "layerY" : 1, // needed?
+ metaKey : 1,
+ // "modifiers" : 1, // needed?
+ // "offsetX" : 1, // needed?
+ // "offsetY" : 1, // needed?
+ // "preventDefault" : 1, // we supply
+ // "reason" : 1, // IE proprietary
+ // "relatedTarget" : 1,
+ // "returnValue" : 1, // needed?
+ shiftKey : 1,
+ // "srcUrn" : 1, // IE proprietary
+ // "srcElement" : 1,
+ // "srcFilter" : 1, IE proprietary
+ // "stopPropagation" : 1, // we supply
+ // "target" : 1,
+ // "timeStamp" : 1, // needed?
+ // "toElement" : 1,
+ type : 1,
+ // "view" : 1,
+ // "which" : 1, // we supply
+ // "width" : 1, // needed?
+ x : 1,
+ y : 1
+},
+
+*/
+
+ var ua = Y.UA,
+
+ /**
+ * webkit key remapping required for Safari < 3.1
+ * @property webkitKeymap
+ * @private
+ */
+ webkitKeymap = {
+ 63232: 38, // up
+ 63233: 40, // down
+ 63234: 37, // left
+ 63235: 39, // right
+ 63276: 33, // page up
+ 63277: 34, // page down
+ 25: 9, // SHIFT-TAB (Safari provides a different key code in
+ // this case, even though the shiftKey modifier is set)
+ 63272: 46, // delete
+ 63273: 36, // home
+ 63275: 35 // end
+ },
+
+ /**
+ * Returns a wrapped node. Intended to be used on event targets,
+ * so it will return the node's parent if the target is a text
+ * node.
+ *
+ * If accessing a property of the node throws an error, this is
+ * probably the anonymous div wrapper Gecko adds inside text
+ * nodes. This likely will only occur when attempting to access
+ * the relatedTarget. In this case, we now return null because
+ * the anonymous div is completely useless and we do not know
+ * what the related target was because we can't even get to
+ * the element's parent node.
+ *
+ * @method resolve
+ * @private
+ */
+ resolve = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ n = n.parentNode;
+ }
+ } catch(e) {
+ return null;
+ }
+
+ return Y.one(n);
+ };
+
+
+// provide a single event with browser abstractions resolved
+//
+// include all properties for both browers?
+// include only DOM2 spec properties?
+// provide browser-specific facade?
+
+Y.DOMEventFacade = function(ev, currentTarget, wrapper) {
+
+ wrapper = wrapper || {};
+
+ var e = ev, ot = currentTarget, d = Y.config.doc, b = d.body,
+ x = e.pageX, y = e.pageY, c, t;
+
+ this.altKey = e.altKey;
+ this.ctrlKey = e.ctrlKey;
+ this.metaKey = e.metaKey;
+ this.shiftKey = e.shiftKey;
+ this.type = e.type;
+ this.clientX = e.clientX;
+ this.clientY = e.clientY;
+
+ //////////////////////////////////////////////////////
+
+ if (!x && 0 !== x) {
+ x = e.clientX || 0;
+ y = e.clientY || 0;
+
+ if (ua.ie) {
+ x += Math.max(d.documentElement.scrollLeft, b.scrollLeft);
+ y += Math.max(d.documentElement.scrollTop, b.scrollTop);
+ }
+ }
+
+ this._yuifacade = true;
+
+ /**
+ * The native event
+ * @property _event
+ */
+ this._event = e;
+
+ /**
+ * The X location of the event on the page (including scroll)
+ * @property pageX
+ * @type int
+ */
+ this.pageX = x;
+
+ /**
+ * The Y location of the event on the page (including scroll)
+ * @property pageY
+ * @type int
+ */
+ this.pageY = y;
+
+ //////////////////////////////////////////////////////
+
+ c = e.keyCode || e.charCode || 0;
+
+ if (ua.webkit && (c in webkitKeymap)) {
+ c = webkitKeymap[c];
+ }
+
+ /**
+ * The keyCode for key events. Uses charCode if keyCode is not available
+ * @property keyCode
+ * @type int
+ */
+ this.keyCode = c;
+
+ /**
+ * The charCode for key events. Same as keyCode
+ * @property charCode
+ * @type int
+ */
+ this.charCode = c;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * The button that was pushed.
+ * @property button
+ * @type int
+ */
+ this.button = e.which || e.button;
+
+ /**
+ * The button that was pushed. Same as button.
+ * @property which
+ * @type int
+ */
+ this.which = this.button;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted element
+ * @propery target
+ * @type Node
+ */
+ this.target = resolve(e.target || e.srcElement);
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = resolve(ot);
+
+ t = e.relatedTarget;
+
+ if (!t) {
+ if (e.type == "mouseout") {
+ t = e.toElement;
+ } else if (e.type == "mouseover") {
+ t = e.fromElement;
+ }
+ }
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = resolve(t);
+
+ /**
+ * Number representing the direction and velocity of the movement of the mousewheel.
+ * Negative is down, the higher the number, the faster. Applies to the mousewheel event.
+ * @property wheelDelta
+ * @type int
+ */
+ if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
+ this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
+ }
+
+ //////////////////////////////////////////////////////
+ // methods
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ } else {
+ e.cancelBubble = true;
+ }
+ wrapper.stopped = 1;
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ if (e.stopImmediatePropagation) {
+ e.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ wrapper.stopped = 2;
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ * @param returnValue {string} sets the returnValue of the event to this value
+ * (rather than the default false value). This can be used to add a customized
+ * confirmation query to the beforeunload event).
+ */
+ this.preventDefault = function(returnValue) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ e.returnValue = returnValue || false;
+ wrapper.prevented = 1;
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+
+ this.preventDefault();
+ };
+
+};
+
+})();
+(function() {
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * The event utility provides functions to add and remove event listeners,
+ * event cleansing. It also tries to automatically remove listeners it
+ * registers during the unload event.
+ *
+ * @class Event
+ * @static
+ */
+
+Y.Env.evt.dom_wrappers = {};
+Y.Env.evt.dom_map = {};
+
+var _eventenv = Y.Env.evt,
+add = YUI.Env.add,
+remove = YUI.Env.remove,
+
+onLoad = function() {
+ YUI.Env.windowLoaded = true;
+ Y.Event._load();
+ remove(window, "load", onLoad);
+},
+
+onUnload = function() {
+ Y.Event._unload();
+ remove(window, "unload", onUnload);
+},
+
+EVENT_READY = 'domready',
+
+COMPAT_ARG = '~yui|2|compat~',
+
+shouldIterate = function(o) {
+ try {
+ return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !o.alert);
+ } catch(ex) {
+ Y.log("collection check failure", "warn", "event");
+ return false;
+ }
+
+},
+
+Event = function() {
+
+ /**
+ * True after the onload event has fired
+ * @property _loadComplete
+ * @type boolean
+ * @static
+ * @private
+ */
+ var _loadComplete = false,
+
+ /**
+ * The number of times to poll after window.onload. This number is
+ * increased if additional late-bound handlers are requested after
+ * the page load.
+ * @property _retryCount
+ * @static
+ * @private
+ */
+ _retryCount = 0,
+
+ /**
+ * onAvailable listeners
+ * @property _avail
+ * @static
+ * @private
+ */
+ _avail = [],
+
+ /**
+ * Custom event wrappers for DOM events. Key is
+ * 'event:' + Element uid stamp + event type
+ * @property _wrappers
+ * @type Y.Event.Custom
+ * @static
+ * @private
+ */
+ _wrappers = _eventenv.dom_wrappers,
+
+ _windowLoadKey = null,
+
+ /**
+ * Custom event wrapper map DOM events. Key is
+ * Element uid stamp. Each item is a hash of custom event
+ * wrappers as provided in the _wrappers collection. This
+ * provides the infrastructure for getListeners.
+ * @property _el_events
+ * @static
+ * @private
+ */
+ _el_events = _eventenv.dom_map;
+
+ return {
+
+ /**
+ * The number of times we should look for elements that are not
+ * in the DOM at the time the event is requested after the document
+ * has been loaded. The default is 1000@amp;40 ms, so it will poll
+ * for 40 seconds or until all outstanding handlers are bound
+ * (whichever comes first).
+ * @property POLL_RETRYS
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_RETRYS: 1000,
+
+ /**
+ * The poll interval in milliseconds
+ * @property POLL_INTERVAL
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_INTERVAL: 40,
+
+ /**
+ * addListener/removeListener can throw errors in unexpected scenarios.
+ * These errors are suppressed, the method returns false, and this property
+ * is set
+ * @property lastError
+ * @static
+ * @type Error
+ */
+ lastError: null,
+
+
+ /**
+ * poll handle
+ * @property _interval
+ * @static
+ * @private
+ */
+ _interval: null,
+
+ /**
+ * document readystate poll handle
+ * @property _dri
+ * @static
+ * @private
+ */
+ _dri: null,
+
+ /**
+ * True when the document is initially usable
+ * @property DOMReady
+ * @type boolean
+ * @static
+ */
+ DOMReady: false,
+
+ /**
+ * @method startInterval
+ * @static
+ * @private
+ */
+ startInterval: function() {
+ var E = Y.Event;
+
+ if (!E._interval) {
+E._interval = setInterval(Y.bind(E._poll, E), E.POLL_INTERVAL);
+ }
+ },
+
+ /**
+ * Executes the supplied callback when the item with the supplied
+ * id is found. This is meant to be used to execute behavior as
+ * soon as possible as the page loads. If you use this after the
+ * initial page load it will poll for a fixed time for the element.
+ * The number of times it will poll and the frequency are
+ * configurable. By default it will poll for 10 seconds.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onAvailable
+ *
+ * @param {string||string[]} id the id of the element, or an array
+ * of ids to look for.
+ * @param {function} fn what to execute when the element is found.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj, if set to an object it
+ * will execute in the context of that object
+ * @param checkContent {boolean} check child node readiness (onContentReady)
+ * @static
+ * @deprecated Use Y.on("available")
+ */
+ // @TODO fix arguments
+ onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
+
+ var a = Y.Array(id), i, availHandle;
+
+ // Y.log('onAvailable registered for: ' + id);
+
+ for (i=0; i<a.length; i=i+1) {
+ _avail.push({
+ id: a[i],
+ fn: fn,
+ obj: p_obj,
+ override: p_override,
+ checkReady: checkContent,
+ compat: compat
+ });
+ }
+ _retryCount = this.POLL_RETRYS;
+
+ // We want the first test to be immediate, but async
+ setTimeout(Y.bind(Y.Event._poll, Y.Event), 0);
+
+ availHandle = new Y.EventHandle({
+
+ _delete: function() {
+ // set by the event system for lazy DOM listeners
+ if (availHandle.handle) {
+ availHandle.handle.detach();
+ return;
+ }
+
+ var i, j;
+
+ // otherwise try to remove the onAvailable listener(s)
+ for (i = 0; i < a.length; i++) {
+ for (j = 0; j < _avail.length; j++) {
+ if (a[i] === _avail[j].id) {
+ _avail.splice(j, 1);
+ }
+ }
+ }
+ }
+
+ });
+
+ return availHandle;
+ },
+
+ /**
+ * Works the same way as onAvailable, but additionally checks the
+ * state of sibling elements to determine if the content of the
+ * available element is safe to modify.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onContentReady
+ *
+ * @param {string} id the id of the element to look for.
+ * @param {function} fn what to execute when the element is ready.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj. If an object, fn will
+ * exectute in the context of that object
+ *
+ * @static
+ * @deprecated Use Y.on("contentready")
+ */
+ // @TODO fix arguments
+ onContentReady: function(id, fn, p_obj, p_override, compat) {
+ return this.onAvailable(id, fn, p_obj, p_override, true, compat);
+ },
+
+ /**
+ * Adds an event listener
+ *
+ * @method attach
+ *
+ * @param {String} type The type of event to append
+ * @param {Function} fn The method the event invokes
+ * @param {String|HTMLElement|Array|NodeList} el An id, an element
+ * reference, or a collection of ids and/or elements to assign the
+ * listener to.
+ * @param {Object} context optional context object
+ * @param {Boolean|object} args 0..n arguments to pass to the callback
+ * @return {EventHandle} an object to that can be used to detach the listener
+ *
+ * @static
+ */
+
+ attach: function(type, fn, el, context) {
+ return Y.Event._attach(Y.Array(arguments, 0, true));
+ },
+
+ _createWrapper: function (el, type, capture, compat, facade) {
+
+ var ek = Y.stamp(el),
+ key = 'event:' + ek + type,
+ cewrapper;
+
+
+ if (false === facade) {
+ key += 'native';
+ }
+ if (capture) {
+ key += 'capture';
+ }
+
+
+ cewrapper = _wrappers[key];
+
+
+ if (!cewrapper) {
+ // create CE wrapper
+ cewrapper = Y.publish(key, {
+ silent: true,
+ bubbles: false,
+ contextFn: function() {
+ cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
+ return cewrapper.nodeRef;
+ }
+ });
+
+ // for later removeListener calls
+ cewrapper.el = el;
+ cewrapper.key = key;
+ cewrapper.domkey = ek;
+ cewrapper.type = type;
+ cewrapper.fn = function(e) {
+ cewrapper.fire(Y.Event.getEvent(e, el, (compat || (false === facade))));
+ };
+ cewrapper.capture = capture;
+
+ if (el == Y.config.win && type == "load") {
+ // window load happens once
+ cewrapper.fireOnce = true;
+ _windowLoadKey = key;
+ }
+
+ _wrappers[key] = cewrapper;
+ _el_events[ek] = _el_events[ek] || {};
+ _el_events[ek][key] = cewrapper;
+
+ add(el, type, cewrapper.fn, capture);
+ }
+
+ return cewrapper;
+
+ },
+
+ _attach: function(args, config) {
+
+ var compat, E=Y.Event,
+ handles, oEl, cewrapper, context,
+ fireNow = false, ret,
+ type = args[0],
+ fn = args[1],
+ el = args[2] || Y.config.win,
+ facade = config && config.facade,
+ capture = config && config.capture;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // trimmedArgs.pop();
+ }
+
+ if (!fn || !fn.call) {
+// throw new TypeError(type + " attach call failed, callback undefined");
+Y.log(type + " attach call failed, invalid callback", "error", "event");
+ return false;
+ }
+
+ // The el argument can be an array of elements or element ids.
+ if (shouldIterate(el)) {
+
+ handles=[];
+
+ Y.each(el, function(v, k) {
+ args[2] = v;
+ handles.push(E._attach(args, config));
+ });
+
+ // return (handles.length === 1) ? handles[0] : handles;
+ return new Y.EventHandle(handles);
+
+ // If the el argument is a string, we assume it is
+ // actually the id of the element. If the page is loaded
+ // we convert el to the actual element, otherwise we
+ // defer attaching the event until the element is
+ // ready
+ } else if (Y.Lang.isString(el)) {
+
+ // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
+
+ if (compat) {
+ oEl = Y.DOM.byId(el);
+ } else {
+
+ oEl = Y.Selector.query(el);
+
+ switch (oEl.length) {
+ case 0:
+ oEl = null;
+ break;
+ case 1:
+ oEl = oEl[0];
+ break;
+ default:
+ args[2] = oEl;
+ return E._attach(args, config);
+ }
+ }
+
+ if (oEl) {
+
+ el = oEl;
+
+ // Not found = defer adding the event until the element is available
+ } else {
+
+ // Y.log(el + ' not found');
+ ret = this.onAvailable(el, function() {
+ // Y.log('lazy attach: ' + args);
+
+ ret.handle = E._attach(args, config);
+
+ }, E, true, false, compat);
+
+ return ret;
+
+ }
+ }
+
+ // Element should be an html element or node
+ if (!el) {
+ Y.log("unable to attach event " + type, "warn", "event");
+ return false;
+ }
+
+ if (Y.Node && el instanceof Y.Node) {
+ el = Y.Node.getDOMNode(el);
+ }
+
+ cewrapper = this._createWrapper(el, type, capture, compat, facade);
+
+ if (el == Y.config.win && type == "load") {
+
+ // if the load is complete, fire immediately.
+ // all subscribers, including the current one
+ // will be notified.
+ if (YUI.Env.windowLoaded) {
+ fireNow = true;
+ }
+ }
+
+ if (compat) {
+ args.pop();
+ }
+
+ context = args[3];
+
+ // set context to the Node if not specified
+ // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
+ ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
+
+ if (fireNow) {
+ cewrapper.fire();
+ }
+
+ return ret;
+
+ },
+
+ /**
+ * Removes an event listener. Supports the signature the event was bound
+ * with, but the preferred way to remove listeners is using the handle
+ * that is returned when using Y.on
+ *
+ * @method detach
+ *
+ * @param {String} type the type of event to remove.
+ * @param {Function} fn the method the event invokes. If fn is
+ * undefined, then all event handlers for the type of event are
+ * removed.
+ * @param {String|HTMLElement|Array|NodeList|EventHandle} el An
+ * event handle, an id, an element reference, or a collection
+ * of ids and/or elements to remove the listener from.
+ * @return {boolean} true if the unbind was successful, false otherwise.
+ * @static
+ */
+ detach: function(type, fn, el, obj) {
+
+ var args=Y.Array(arguments, 0, true), compat, i, l, ok,
+ id, ce;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // args.pop();
+ }
+
+ if (type && type.detach) {
+ return type.detach();
+ }
+
+ // The el argument can be a string
+ if (typeof el == "string") {
+
+ // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
+ if (compat) {
+ el = Y.DOM.byId(el);
+ } else {
+ el = Y.Selector.query(el);
+ l = el.length;
+ if (l < 1) {
+ el = null;
+ } else if (l == 1) {
+ el = el[0];
+ }
+ }
+ // return Y.Event.detach.apply(Y.Event, args);
+
+ // The el argument can be an array of elements or element ids.
+ }
+
+ if (!el) {
+ return false;
+ }
+
+ if (shouldIterate(el)) {
+
+ ok = true;
+ for (i=0, l=el.length; i<l; ++i) {
+ args[2] = el[i];
+ ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
+ }
+
+ return ok;
+
+ }
+
+ if (!type || !fn || !fn.call) {
+ return this.purgeElement(el, false, type);
+ }
+
+ id = 'event:' + Y.stamp(el) + type;
+ ce = _wrappers[id];
+
+ if (ce) {
+ return ce.detach(fn);
+ } else {
+ return false;
+ }
+
+ },
+
+ /**
+ * Finds the event in the window object, the caller's arguments, or
+ * in the arguments of another method in the callstack. This is
+ * executed automatically for events registered through the event
+ * manager, so the implementer should not normally need to execute
+ * this function at all.
+ * @method getEvent
+ * @param {Event} e the event parameter from the handler
+ * @param {HTMLElement} el the element the listener was attached to
+ * @return {Event} the event
+ * @static
+ */
+ getEvent: function(e, el, noFacade) {
+ var ev = e || window.event;
+
+ return (noFacade) ? ev :
+ new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
+ },
+
+ /**
+ * Generates an unique ID for the element if it does not already
+ * have one.
+ * @method generateId
+ * @param el the element to create the id for
+ * @return {string} the resulting id of the element
+ * @static
+ */
+ generateId: function(el) {
+ var id = el.id;
+
+ if (!id) {
+ id = Y.stamp(el);
+ el.id = id;
+ }
+
+ return id;
+ },
+
+ /**
+ * We want to be able to use getElementsByTagName as a collection
+ * to attach a group of events to. Unfortunately, different
+ * browsers return different types of collections. This function
+ * tests to determine if the object is array-like. It will also
+ * fail if the object is an array, but is empty.
+ * @method _isValidCollection
+ * @param o the object to test
+ * @return {boolean} true if the object is array-like and populated
+ * @deprecated was not meant to be used directly
+ * @static
+ * @private
+ */
+ _isValidCollection: shouldIterate,
+
+ /**
+ * hook up any deferred listeners
+ * @method _load
+ * @static
+ * @private
+ */
+ _load: function(e) {
+
+ if (!_loadComplete) {
+
+ // Y.log('Load Complete', 'info', 'event');
+
+ _loadComplete = true;
+
+ // Just in case DOMReady did not go off for some reason
+ // E._ready();
+ if (Y.fire) {
+ Y.fire(EVENT_READY);
+ }
+
+ // Available elements may not have been detected before the
+ // window load event fires. Try to find them now so that the
+ // the user is more likely to get the onAvailable notifications
+ // before the window load notification
+ Y.Event._poll();
+
+ }
+ },
+
+ /**
+ * Polling function that runs before the onload event fires,
+ * attempting to attach to DOM Nodes as soon as they are
+ * available
+ * @method _poll
+ * @static
+ * @private
+ */
+ _poll: function() {
+
+ if (this.locked) {
+ return;
+ }
+
+ if (Y.UA.ie && !YUI.Env.DOMReady) {
+ // Hold off if DOMReady has not fired and check current
+ // readyState to protect against the IE operation aborted
+ // issue.
+ this.startInterval();
+ return;
+ }
+
+ this.locked = true;
+
+ // Y.log.debug("poll");
+
+ // keep trying until after the page is loaded. We need to
+ // check the page load state prior to trying to bind the
+ // elements so that we can be certain all elements have been
+ // tested appropriately
+ var tryAgain = !_loadComplete, notAvail, executeItem,
+ i, len, item, el;
+
+ if (!tryAgain) {
+ tryAgain = (_retryCount > 0);
+ }
+
+ // onAvailable
+ notAvail = [];
+
+ executeItem = function (el, item) {
+
+ var context, ov = item.override;
+
+ if (item.compat) {
+
+ if (item.override) {
+ if (ov === true) {
+ context = item.obj;
+ } else {
+ context = ov;
+ }
+ } else {
+ context = el;
+ }
+
+ item.fn.call(context, item.obj);
+
+ } else {
+ context = item.obj || Y.one(el);
+ item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
+ }
+
+ };
+
+
+ // onAvailable
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && !item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ // Y.log('avail: ' + el);
+ executeItem(el, item);
+ _avail[i] = null;
+ } else {
+ // Y.log('NOT avail: ' + el);
+ notAvail.push(item);
+ }
+ }
+ }
+
+ // onContentReady
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ // The element is available, but not necessarily ready
+ // @todo should we test parentNode.nextSibling?
+ if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
+ executeItem(el, item);
+ _avail[i] = null;
+ }
+ } else {
+ notAvail.push(item);
+ }
+ }
+ }
+
+ _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
+
+ if (tryAgain) {
+ // we may need to strip the nulled out items here
+ this.startInterval();
+ } else {
+ clearInterval(this._interval);
+ this._interval = null;
+ }
+
+ this.locked = false;
+
+ return;
+
+ },
+
+ /**
+ * Removes all listeners attached to the given element via addListener.
+ * Optionally, the node's children can also be purged.
+ * Optionally, you can specify a specific type of event to remove.
+ * @method purgeElement
+ * @param {HTMLElement} el the element to purge
+ * @param {boolean} recurse recursively purge this element's children
+ * as well. Use with caution.
+ * @param {string} type optional type of listener to purge. If
+ * left out, all listeners will be removed
+ * @static
+ */
+ purgeElement: function(el, recurse, type) {
+ // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
+ var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el,
+ lis = this.getListeners(oEl, type), i, len, props;
+ if (lis) {
+ for (i=0,len=lis.length; i<len ; ++i) {
+ props = lis[i];
+ props.detachAll();
+ remove(props.el, props.type, props.fn, props.capture);
+ delete _wrappers[props.key];
+ delete _el_events[props.domkey][props.key];
+ }
+
+ }
+
+ if (recurse && oEl && oEl.childNodes) {
+ for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
+ this.purgeElement(oEl.childNodes[i], recurse, type);
+ }
+ }
+
+ },
+
+ /**
+ * Returns all listeners attached to the given element via addListener.
+ * Optionally, you can specify a specific type of event to return.
+ * @method getListeners
+ * @param el {HTMLElement|string} the element or element id to inspect
+ * @param type {string} optional type of listener to return. If
+ * left out, all listeners will be returned
+ * @return {Y.Custom.Event} the custom event wrapper for the DOM event(s)
+ * @static
+ */
+ getListeners: function(el, type) {
+ var ek = Y.stamp(el, true), evts = _el_events[ek],
+ results=[] , key = (type) ? 'event:' + ek + type : null;
+
+ if (!evts) {
+ return null;
+ }
+
+ if (key) {
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ // get native events as well
+ key += 'native';
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ } else {
+ Y.each(evts, function(v, k) {
+ results.push(v);
+ });
+ }
+
+ return (results.length) ? results : null;
+ },
+
+ /**
+ * Removes all listeners registered by pe.event. Called
+ * automatically during the unload event.
+ * @method _unload
+ * @static
+ * @private
+ */
+ _unload: function(e) {
+ Y.each(_wrappers, function(v, k) {
+ v.detachAll();
+ remove(v.el, v.type, v.fn, v.capture);
+ delete _wrappers[k];
+ delete _el_events[v.domkey][k];
+ });
+ },
+
+
+ /**
+ * Adds a DOM event directly without the caching, cleanup, context adj, etc
+ *
+ * @method nativeAdd
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeAdd: add,
+
+ /**
+ * Basic remove listener
+ *
+ * @method nativeRemove
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeRemove: remove
+ };
+
+}();
+
+Y.Event = Event;
+
+
+if (Y.config.injected || YUI.Env.windowLoaded) {
+ onLoad();
+} else {
+ add(window, "load", onLoad);
+}
+
+// Process onAvailable/onContentReady items when when the DOM is ready in IE
+if (Y.UA.ie) {
+ Y.on(EVENT_READY, Event._poll, Event, true);
+}
+
+Y.on("unload", onUnload);
+
+Event.Custom = Y.CustomEvent;
+Event.Subscriber = Y.Subscriber;
+Event.Target = Y.EventTarget;
+Event.Handle = Y.EventHandle;
+Event.Facade = Y.EventFacade;
+
+Event._poll();
+
+})();
+
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM.
+ * @event available
+ * @param type {string} 'available'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.available = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
+ }
+};
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM with a nextSibling property
+ * (indicating that the element's children are available)
+ * @event contentready
+ * @param type {string} 'contentready'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.contentready = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+(function(){var GLOBAL_ENV=YUI.Env,C=YUI.config,D=C.doc,POLL_INTERVAL=C.pollInterval||40,_ready=function(e){GLOBAL_ENV._ready();};if(!GLOBAL_ENV._ready){GLOBAL_ENV._ready=function(){if(!GLOBAL_ENV.DOMReady){GLOBAL_ENV.DOMReady=true;if(D.removeEventListener){D.removeEventListener("DOMContentLoaded",_ready,false);}}};
+/* DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
+if(navigator.userAgent.match(/MSIE/)){if(self!==self.top){document.onreadystatechange=function(){if(document.readyState=="complete"){document.onreadystatechange=null;_ready();}};}else{GLOBAL_ENV._dri=setInterval(function(){try{document.documentElement.doScroll("left");clearInterval(GLOBAL_ENV._dri);GLOBAL_ENV._dri=null;_ready();}catch(ex){}},POLL_INTERVAL);}}else{D.addEventListener("DOMContentLoaded",_ready,false);}}})();YUI.add("event-base",function(A){(function(){var C=YUI.Env,B=function(){A.fire("domready");};A.publish("domready",{fireOnce:true});if(C.DOMReady){B();}else{A.before(B,C,"_ready");}})();(function(){var C=A.UA,B={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9,63272:46,63273:36,63275:35},D=function(F){try{if(F&&3==F.nodeType){F=F.parentNode;}}catch(E){return null;}return A.one(F);};A.DOMEventFacade=function(L,F,E){E=E||{};var H=L,G=F,I=A.config.doc,M=I.body,N=H.pageX,K=H.pageY,J,O;this.altKey=H.altKey;this.ctrlKey=H.ctrlKey;this.metaKey=H.metaKey;this.shiftKey=H.shiftKey;this.type=H.type;this.clientX=H.clientX;this.clientY=H.clientY;if(!N&&0!==N){N=H.clientX||0;K=H.clientY||0;if(C.ie){N+=Math.max(I.documentElement.scrollLeft,M.scrollLeft);K+=Math.max(I.documentElement.scrollTop,M.scrollTop);}}this._yuifacade=true;this._event=H;this.pageX=N;this.pageY=K;J=H.keyCode||H.charCode||0;if(C.webkit&&(J in B)){J=B[J];}this.keyCode=J;this.charCode=J;this.button=H.which||H.button;this.which=this.button;this.target=D(H.target||H.srcElement);this.currentTarget=D(G);O=H.relatedTarget;if(!O){if(H.type=="mouseout"){O=H.toElement;}else{if(H.type=="mouseover"){O=H.fromElement;}}}this.relatedTarget=D(O);if(H.type=="mousewheel"||H.type=="DOMMouseScroll"){this.wheelDelta=(H.detail)?(H.detail*-1):Math.round(H.wheelDelta/80)||((H.wheelDelta<0)?-1:1);}this.stopPropagation=function(){if(H.stopPropagation){H.stopPropagation();}else{H.cancelBubble=true;}E.stopped=1;};this.stopImmediatePropagation=function(){if(H.stopImmediatePropagation){H.stopImmediatePropagation();}else{this.stopPropagation();}E.stopped=2;};this.preventDefault=function(P){if(H.preventDefault){H.preventDefault();}H.returnValue=P||false;E.prevented=1;};this.halt=function(P){if(P){this.stopImmediatePropagation();}else{this.stopPropagation();}this.preventDefault();};};})();(function(){A.Env.evt.dom_wrappers={};A.Env.evt.dom_map={};var H=A.Env.evt,J=YUI.Env.add,D=YUI.Env.remove,G=function(){YUI.Env.windowLoaded=true;A.Event._load();D(window,"load",G);},B=function(){A.Event._unload();D(window,"unload",B);},C="domready",E="~yui|2|compat~",F=function(L){try{return(L&&typeof L!=="string"&&A.Lang.isNumber(L.length)&&!L.tagName&&!L.alert);}catch(K){return false;}},I=function(){var M=false,N=0,L=[],O=H.dom_wrappers,K=null,P=H.dom_map;return{POLL_RETRYS:1000,POLL_INTERVAL:40,lastError:null,_interval:null,_dri:null,DOMReady:false,startInterval:function(){var Q=A.Event;if(!Q._interval){Q._interval=setInterval(A.bind(Q._poll,Q),Q.POLL_INTERVAL);}},onAvailable:function(Q,U,Y,R,V,X){var W=A.Array(Q),S,T;for(S=0;S<W.length;S=S+1){L.push({id:W[S],fn:U,obj:Y,override:R,checkReady:V,compat:X});}N=this.POLL_RETRYS;setTimeout(A.bind(A.Event._poll,A.Event),0);T=new A.EventHandle({_delete:function(){if(T.handle){T.handle.detach();return;}var a,Z;for(a=0;a<W.length;a++){for(Z=0;Z<L.length;Z++){if(W[a]===L[Z].id){L.splice(Z,1);}}}}});return T;},onContentReady:function(U,R,T,S,Q){return this.onAvailable(U,R,T,S,true,Q);},attach:function(T,S,R,Q){return A.Event._attach(A.Array(arguments,0,true));},_createWrapper:function(W,V,Q,R,U){var X=A.stamp(W),T="event:"+X+V,S;if(false===U){T+="native";}if(Q){T+="capture";}S=O[T];if(!S){S=A.publish(T,{silent:true,bubbles:false,contextFn:function(){S.nodeRef=S.nodeRef||A.one(S.el);return S.nodeRef;}});S.el=W;S.key=T;S.domkey=X;S.type=V;S.fn=function(Y){S.fire(A.Event.getEvent(Y,W,(R||(false===U))));};S.capture=Q;if(W==A.config.win&&V=="load"){S.fireOnce=true;K=T;}O[T]=S;P[X]=P[X]||{};P[X][T]=S;J(W,V,S.fn,Q);}return S;},_attach:function(W,S){var a,e=A.Event,c,U,Z,Q,T=false,V,X=W[0],Y=W[1],R=W[2]||A.config.win,d=S&&S.facade,b=S&&S.capture;if(W[W.length-1]===E){a=true;}if(!Y||!Y.call){return false;}if(F(R)){c=[];A.each(R,function(g,f){W[2]=g;c.push(e._attach(W,S));});return new A.EventHandle(c);}else{if(A.Lang.isString(R)){if(a){U=A.DOM.byId(R);}else{U=A.Selector.query(R);switch(U.length){case 0:U=null;break;case 1:U=U[0];break;default:W[2]=U;return e._attach(W,S);}}if(U){R=U;}else{V=this.onAvailable(R,function(){V.handle=e._attach(W,S);},e,true,false,a);return V;}}}if(!R){return false;}if(A.Node&&R instanceof A.Node){R=A.Node.getDOMNode(R);}Z=this._createWrapper(R,X,b,a,d);if(R==A.config.win&&X=="load"){if(YUI.Env.windowLoaded){T=true;}}if(a){W.pop();}Q=W[3];V=Z._on(Y,Q,(W.length>4)?W.slice(4):null);if(T){Z.fire();}return V;},detach:function(X,Z,S,U){var W=A.Array(arguments,0,true),a,V,T,Y,Q,R;if(W[W.length-1]===E){a=true;}if(X&&X.detach){return X.detach();}if(typeof S=="string"){if(a){S=A.DOM.byId(S);}else{S=A.Selector.query(S);T=S.length;if(T<1){S=null;}else{if(T==1){S=S[0];}}}}if(!S){return false;}if(F(S)){Y=true;for(V=0,T=S.length;V<T;++V){W[2]=S[V];Y=(A.Event.detach.apply(A.Event,W)&&Y);}return Y;}if(!X||!Z||!Z.call){return this.purgeElement(S,false,X);}Q="event:"+A.stamp(S)+X;R=O[Q];if(R){return R.detach(Z);}else{return false;}},getEvent:function(T,R,Q){var S=T||window.event;return(Q)?S:new A.DOMEventFacade(S,R,O["event:"+A.stamp(R)+T.type]);},generateId:function(Q){var R=Q.id;if(!R){R=A.stamp(Q);Q.id=R;}return R;},_isValidCollection:F,_load:function(Q){if(!M){M=true;if(A.fire){A.fire(C);}A.Event._poll();}},_poll:function(){if(this.locked){return;
+}if(A.UA.ie&&!YUI.Env.DOMReady){this.startInterval();return;}this.locked=true;var V=!M,U,W,R,Q,T,S;if(!V){V=(N>0);}U=[];W=function(Z,a){var Y,X=a.override;if(a.compat){if(a.override){if(X===true){Y=a.obj;}else{Y=X;}}else{Y=Z;}a.fn.call(Y,a.obj);}else{Y=a.obj||A.one(Z);a.fn.apply(Y,(A.Lang.isArray(X))?X:[]);}};for(R=0,Q=L.length;R<Q;++R){T=L[R];if(T&&!T.checkReady){S=(T.compat)?A.DOM.byId(T.id):A.Selector.query(T.id,null,true);if(S){W(S,T);L[R]=null;}else{U.push(T);}}}for(R=0,Q=L.length;R<Q;++R){T=L[R];if(T&&T.checkReady){S=(T.compat)?A.DOM.byId(T.id):A.Selector.query(T.id,null,true);if(S){if(M||(S.get&&S.get("nextSibling"))||S.nextSibling){W(S,T);L[R]=null;}}else{U.push(T);}}}N=(U.length===0)?0:N-1;if(V){this.startInterval();}else{clearInterval(this._interval);this._interval=null;}this.locked=false;return;},purgeElement:function(W,X,V){var S=(A.Lang.isString(W))?A.Selector.query(W,null,true):W,R=this.getListeners(S,V),T,Q,U;if(R){for(T=0,Q=R.length;T<Q;++T){U=R[T];U.detachAll();D(U.el,U.type,U.fn,U.capture);delete O[U.key];delete P[U.domkey][U.key];}}if(X&&S&&S.childNodes){for(T=0,Q=S.childNodes.length;T<Q;++T){this.purgeElement(S.childNodes[T],X,V);}}},getListeners:function(U,T){var V=A.stamp(U,true),Q=P[V],S=[],R=(T)?"event:"+V+T:null;if(!Q){return null;}if(R){if(Q[R]){S.push(Q[R]);}R+="native";if(Q[R]){S.push(Q[R]);}}else{A.each(Q,function(X,W){S.push(X);});}return(S.length)?S:null;},_unload:function(Q){A.each(O,function(S,R){S.detachAll();D(S.el,S.type,S.fn,S.capture);delete O[R];delete P[S.domkey][R];});},nativeAdd:J,nativeRemove:D};}();A.Event=I;if(A.config.injected||YUI.Env.windowLoaded){G();}else{J(window,"load",G);}if(A.UA.ie){A.on(C,I._poll,I,true);}A.on("unload",B);I.Custom=A.CustomEvent;I.Subscriber=A.Subscriber;I.Target=A.EventTarget;I.Handle=A.EventHandle;I.Facade=A.EventFacade;I._poll();})();A.Env.evt.plugins.available={on:function(D,C,F,E){var B=arguments.length>4?A.Array(arguments,4,true):[];return A.Event.onAvailable.call(A.Event,F,C,E,B);}};A.Env.evt.plugins.contentready={on:function(D,C,F,E){var B=arguments.length>4?A.Array(arguments,4,true):[];return A.Event.onContentReady.call(A.Event,F,C,E,B);}};},"3.0.0",{requires:["event-custom-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+(function() {
+
+
+// Unlike most of the library, this code has to be executed as soon as it is
+// introduced into the page -- and it should only be executed one time
+// regardless of the number of instances that use it.
+
+var GLOBAL_ENV = YUI.Env,
+
+ C = YUI.config,
+
+ D = C.doc,
+
+ POLL_INTERVAL = C.pollInterval || 40,
+
+ _ready = function(e) {
+ GLOBAL_ENV._ready();
+ };
+
+ if (!GLOBAL_ENV._ready) {
+
+ GLOBAL_ENV._ready = function() {
+ if (!GLOBAL_ENV.DOMReady) {
+ GLOBAL_ENV.DOMReady=true;
+
+ // Remove the DOMContentLoaded (FF/Opera/Safari)
+ if (D.removeEventListener) {
+ D.removeEventListener("DOMContentLoaded", _ready, false);
+ }
+ }
+ };
+
+ // create custom event
+
+/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
+
+ // Internet Explorer: use the readyState of a defered script.
+ // This isolates what appears to be a safe moment to manipulate
+ // the DOM prior to when the document's readyState suggests
+ // it is safe to do so.
+ if (navigator.userAgent.match(/MSIE/)) {
+
+ if (self !== self.top) {
+ document.onreadystatechange = function() {
+ if (document.readyState == 'complete') {
+ document.onreadystatechange = null;
+ _ready();
+ }
+ };
+ } else {
+
+ GLOBAL_ENV._dri = setInterval(function() {
+ try {
+ // throws an error if doc is not ready
+ document.documentElement.doScroll('left');
+ clearInterval(GLOBAL_ENV._dri);
+ GLOBAL_ENV._dri = null;
+ _ready();
+ } catch (ex) {
+ }
+ }, POLL_INTERVAL);
+ }
+
+ // FireFox, Opera, Safari 3+: These browsers provide a event for this
+ // moment.
+ } else {
+ D.addEventListener("DOMContentLoaded", _ready, false);
+ }
+
+ /////////////////////////////////////////////////////////////
+ }
+
+})();
+YUI.add('event-base', function(Y) {
+
+(function() {
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+var GLOBAL_ENV = YUI.Env,
+
+ yready = function() {
+ Y.fire('domready');
+ };
+
+Y.publish('domready', {
+ fireOnce: true
+});
+
+if (GLOBAL_ENV.DOMReady) {
+ // console.log('DOMReady already fired', 'info', 'event');
+ yready();
+} else {
+ // console.log('setting up before listener', 'info', 'event');
+ // console.log('env: ' + YUI.Env.windowLoaded, 'info', 'event');
+ Y.before(yready, GLOBAL_ENV, "_ready");
+}
+
+})();
+(function() {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Wraps a DOM event, properties requiring browser abstraction are
+ * fixed here. Provids a security layer when required.
+ * @class DOMEventFacade
+ * @param ev {Event} the DOM event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
+ */
+
+/*
+ * @TODO constants? LEFTBUTTON, MIDDLEBUTTON, RIGHTBUTTON, keys
+ */
+
+/*
+
+var whitelist = {
+ altKey : 1,
+ // "button" : 1, // we supply
+ // "bubbles" : 1, // needed?
+ // "cancelable" : 1, // needed?
+ // "charCode" : 1, // we supply
+ cancelBubble : 1,
+ // "currentTarget" : 1, // we supply
+ ctrlKey : 1,
+ clientX : 1, // needed?
+ clientY : 1, // needed?
+ detail : 1, // not fully implemented
+ // "fromElement" : 1,
+ keyCode : 1,
+ // "height" : 1, // needed?
+ // "initEvent" : 1, // need the init events?
+ // "initMouseEvent" : 1,
+ // "initUIEvent" : 1,
+ // "layerX" : 1, // needed?
+ // "layerY" : 1, // needed?
+ metaKey : 1,
+ // "modifiers" : 1, // needed?
+ // "offsetX" : 1, // needed?
+ // "offsetY" : 1, // needed?
+ // "preventDefault" : 1, // we supply
+ // "reason" : 1, // IE proprietary
+ // "relatedTarget" : 1,
+ // "returnValue" : 1, // needed?
+ shiftKey : 1,
+ // "srcUrn" : 1, // IE proprietary
+ // "srcElement" : 1,
+ // "srcFilter" : 1, IE proprietary
+ // "stopPropagation" : 1, // we supply
+ // "target" : 1,
+ // "timeStamp" : 1, // needed?
+ // "toElement" : 1,
+ type : 1,
+ // "view" : 1,
+ // "which" : 1, // we supply
+ // "width" : 1, // needed?
+ x : 1,
+ y : 1
+},
+
+*/
+
+ var ua = Y.UA,
+
+ /**
+ * webkit key remapping required for Safari < 3.1
+ * @property webkitKeymap
+ * @private
+ */
+ webkitKeymap = {
+ 63232: 38, // up
+ 63233: 40, // down
+ 63234: 37, // left
+ 63235: 39, // right
+ 63276: 33, // page up
+ 63277: 34, // page down
+ 25: 9, // SHIFT-TAB (Safari provides a different key code in
+ // this case, even though the shiftKey modifier is set)
+ 63272: 46, // delete
+ 63273: 36, // home
+ 63275: 35 // end
+ },
+
+ /**
+ * Returns a wrapped node. Intended to be used on event targets,
+ * so it will return the node's parent if the target is a text
+ * node.
+ *
+ * If accessing a property of the node throws an error, this is
+ * probably the anonymous div wrapper Gecko adds inside text
+ * nodes. This likely will only occur when attempting to access
+ * the relatedTarget. In this case, we now return null because
+ * the anonymous div is completely useless and we do not know
+ * what the related target was because we can't even get to
+ * the element's parent node.
+ *
+ * @method resolve
+ * @private
+ */
+ resolve = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ n = n.parentNode;
+ }
+ } catch(e) {
+ return null;
+ }
+
+ return Y.one(n);
+ };
+
+
+// provide a single event with browser abstractions resolved
+//
+// include all properties for both browers?
+// include only DOM2 spec properties?
+// provide browser-specific facade?
+
+Y.DOMEventFacade = function(ev, currentTarget, wrapper) {
+
+ wrapper = wrapper || {};
+
+ var e = ev, ot = currentTarget, d = Y.config.doc, b = d.body,
+ x = e.pageX, y = e.pageY, c, t;
+
+ this.altKey = e.altKey;
+ this.ctrlKey = e.ctrlKey;
+ this.metaKey = e.metaKey;
+ this.shiftKey = e.shiftKey;
+ this.type = e.type;
+ this.clientX = e.clientX;
+ this.clientY = e.clientY;
+
+ //////////////////////////////////////////////////////
+
+ if (!x && 0 !== x) {
+ x = e.clientX || 0;
+ y = e.clientY || 0;
+
+ if (ua.ie) {
+ x += Math.max(d.documentElement.scrollLeft, b.scrollLeft);
+ y += Math.max(d.documentElement.scrollTop, b.scrollTop);
+ }
+ }
+
+ this._yuifacade = true;
+
+ /**
+ * The native event
+ * @property _event
+ */
+ this._event = e;
+
+ /**
+ * The X location of the event on the page (including scroll)
+ * @property pageX
+ * @type int
+ */
+ this.pageX = x;
+
+ /**
+ * The Y location of the event on the page (including scroll)
+ * @property pageY
+ * @type int
+ */
+ this.pageY = y;
+
+ //////////////////////////////////////////////////////
+
+ c = e.keyCode || e.charCode || 0;
+
+ if (ua.webkit && (c in webkitKeymap)) {
+ c = webkitKeymap[c];
+ }
+
+ /**
+ * The keyCode for key events. Uses charCode if keyCode is not available
+ * @property keyCode
+ * @type int
+ */
+ this.keyCode = c;
+
+ /**
+ * The charCode for key events. Same as keyCode
+ * @property charCode
+ * @type int
+ */
+ this.charCode = c;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * The button that was pushed.
+ * @property button
+ * @type int
+ */
+ this.button = e.which || e.button;
+
+ /**
+ * The button that was pushed. Same as button.
+ * @property which
+ * @type int
+ */
+ this.which = this.button;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted element
+ * @propery target
+ * @type Node
+ */
+ this.target = resolve(e.target || e.srcElement);
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = resolve(ot);
+
+ t = e.relatedTarget;
+
+ if (!t) {
+ if (e.type == "mouseout") {
+ t = e.toElement;
+ } else if (e.type == "mouseover") {
+ t = e.fromElement;
+ }
+ }
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = resolve(t);
+
+ /**
+ * Number representing the direction and velocity of the movement of the mousewheel.
+ * Negative is down, the higher the number, the faster. Applies to the mousewheel event.
+ * @property wheelDelta
+ * @type int
+ */
+ if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
+ this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
+ }
+
+ //////////////////////////////////////////////////////
+ // methods
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ } else {
+ e.cancelBubble = true;
+ }
+ wrapper.stopped = 1;
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ if (e.stopImmediatePropagation) {
+ e.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ wrapper.stopped = 2;
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ * @param returnValue {string} sets the returnValue of the event to this value
+ * (rather than the default false value). This can be used to add a customized
+ * confirmation query to the beforeunload event).
+ */
+ this.preventDefault = function(returnValue) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ e.returnValue = returnValue || false;
+ wrapper.prevented = 1;
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+
+ this.preventDefault();
+ };
+
+};
+
+})();
+(function() {
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * The event utility provides functions to add and remove event listeners,
+ * event cleansing. It also tries to automatically remove listeners it
+ * registers during the unload event.
+ *
+ * @class Event
+ * @static
+ */
+
+Y.Env.evt.dom_wrappers = {};
+Y.Env.evt.dom_map = {};
+
+var _eventenv = Y.Env.evt,
+add = YUI.Env.add,
+remove = YUI.Env.remove,
+
+onLoad = function() {
+ YUI.Env.windowLoaded = true;
+ Y.Event._load();
+ remove(window, "load", onLoad);
+},
+
+onUnload = function() {
+ Y.Event._unload();
+ remove(window, "unload", onUnload);
+},
+
+EVENT_READY = 'domready',
+
+COMPAT_ARG = '~yui|2|compat~',
+
+shouldIterate = function(o) {
+ try {
+ return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !o.alert);
+ } catch(ex) {
+ return false;
+ }
+
+},
+
+Event = function() {
+
+ /**
+ * True after the onload event has fired
+ * @property _loadComplete
+ * @type boolean
+ * @static
+ * @private
+ */
+ var _loadComplete = false,
+
+ /**
+ * The number of times to poll after window.onload. This number is
+ * increased if additional late-bound handlers are requested after
+ * the page load.
+ * @property _retryCount
+ * @static
+ * @private
+ */
+ _retryCount = 0,
+
+ /**
+ * onAvailable listeners
+ * @property _avail
+ * @static
+ * @private
+ */
+ _avail = [],
+
+ /**
+ * Custom event wrappers for DOM events. Key is
+ * 'event:' + Element uid stamp + event type
+ * @property _wrappers
+ * @type Y.Event.Custom
+ * @static
+ * @private
+ */
+ _wrappers = _eventenv.dom_wrappers,
+
+ _windowLoadKey = null,
+
+ /**
+ * Custom event wrapper map DOM events. Key is
+ * Element uid stamp. Each item is a hash of custom event
+ * wrappers as provided in the _wrappers collection. This
+ * provides the infrastructure for getListeners.
+ * @property _el_events
+ * @static
+ * @private
+ */
+ _el_events = _eventenv.dom_map;
+
+ return {
+
+ /**
+ * The number of times we should look for elements that are not
+ * in the DOM at the time the event is requested after the document
+ * has been loaded. The default is 1000@amp;40 ms, so it will poll
+ * for 40 seconds or until all outstanding handlers are bound
+ * (whichever comes first).
+ * @property POLL_RETRYS
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_RETRYS: 1000,
+
+ /**
+ * The poll interval in milliseconds
+ * @property POLL_INTERVAL
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_INTERVAL: 40,
+
+ /**
+ * addListener/removeListener can throw errors in unexpected scenarios.
+ * These errors are suppressed, the method returns false, and this property
+ * is set
+ * @property lastError
+ * @static
+ * @type Error
+ */
+ lastError: null,
+
+
+ /**
+ * poll handle
+ * @property _interval
+ * @static
+ * @private
+ */
+ _interval: null,
+
+ /**
+ * document readystate poll handle
+ * @property _dri
+ * @static
+ * @private
+ */
+ _dri: null,
+
+ /**
+ * True when the document is initially usable
+ * @property DOMReady
+ * @type boolean
+ * @static
+ */
+ DOMReady: false,
+
+ /**
+ * @method startInterval
+ * @static
+ * @private
+ */
+ startInterval: function() {
+ var E = Y.Event;
+
+ if (!E._interval) {
+E._interval = setInterval(Y.bind(E._poll, E), E.POLL_INTERVAL);
+ }
+ },
+
+ /**
+ * Executes the supplied callback when the item with the supplied
+ * id is found. This is meant to be used to execute behavior as
+ * soon as possible as the page loads. If you use this after the
+ * initial page load it will poll for a fixed time for the element.
+ * The number of times it will poll and the frequency are
+ * configurable. By default it will poll for 10 seconds.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onAvailable
+ *
+ * @param {string||string[]} id the id of the element, or an array
+ * of ids to look for.
+ * @param {function} fn what to execute when the element is found.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj, if set to an object it
+ * will execute in the context of that object
+ * @param checkContent {boolean} check child node readiness (onContentReady)
+ * @static
+ * @deprecated Use Y.on("available")
+ */
+ // @TODO fix arguments
+ onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
+
+ var a = Y.Array(id), i, availHandle;
+
+
+ for (i=0; i<a.length; i=i+1) {
+ _avail.push({
+ id: a[i],
+ fn: fn,
+ obj: p_obj,
+ override: p_override,
+ checkReady: checkContent,
+ compat: compat
+ });
+ }
+ _retryCount = this.POLL_RETRYS;
+
+ // We want the first test to be immediate, but async
+ setTimeout(Y.bind(Y.Event._poll, Y.Event), 0);
+
+ availHandle = new Y.EventHandle({
+
+ _delete: function() {
+ // set by the event system for lazy DOM listeners
+ if (availHandle.handle) {
+ availHandle.handle.detach();
+ return;
+ }
+
+ var i, j;
+
+ // otherwise try to remove the onAvailable listener(s)
+ for (i = 0; i < a.length; i++) {
+ for (j = 0; j < _avail.length; j++) {
+ if (a[i] === _avail[j].id) {
+ _avail.splice(j, 1);
+ }
+ }
+ }
+ }
+
+ });
+
+ return availHandle;
+ },
+
+ /**
+ * Works the same way as onAvailable, but additionally checks the
+ * state of sibling elements to determine if the content of the
+ * available element is safe to modify.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onContentReady
+ *
+ * @param {string} id the id of the element to look for.
+ * @param {function} fn what to execute when the element is ready.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj. If an object, fn will
+ * exectute in the context of that object
+ *
+ * @static
+ * @deprecated Use Y.on("contentready")
+ */
+ // @TODO fix arguments
+ onContentReady: function(id, fn, p_obj, p_override, compat) {
+ return this.onAvailable(id, fn, p_obj, p_override, true, compat);
+ },
+
+ /**
+ * Adds an event listener
+ *
+ * @method attach
+ *
+ * @param {String} type The type of event to append
+ * @param {Function} fn The method the event invokes
+ * @param {String|HTMLElement|Array|NodeList} el An id, an element
+ * reference, or a collection of ids and/or elements to assign the
+ * listener to.
+ * @param {Object} context optional context object
+ * @param {Boolean|object} args 0..n arguments to pass to the callback
+ * @return {EventHandle} an object to that can be used to detach the listener
+ *
+ * @static
+ */
+
+ attach: function(type, fn, el, context) {
+ return Y.Event._attach(Y.Array(arguments, 0, true));
+ },
+
+ _createWrapper: function (el, type, capture, compat, facade) {
+
+ var ek = Y.stamp(el),
+ key = 'event:' + ek + type,
+ cewrapper;
+
+
+ if (false === facade) {
+ key += 'native';
+ }
+ if (capture) {
+ key += 'capture';
+ }
+
+
+ cewrapper = _wrappers[key];
+
+
+ if (!cewrapper) {
+ // create CE wrapper
+ cewrapper = Y.publish(key, {
+ silent: true,
+ bubbles: false,
+ contextFn: function() {
+ cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
+ return cewrapper.nodeRef;
+ }
+ });
+
+ // for later removeListener calls
+ cewrapper.el = el;
+ cewrapper.key = key;
+ cewrapper.domkey = ek;
+ cewrapper.type = type;
+ cewrapper.fn = function(e) {
+ cewrapper.fire(Y.Event.getEvent(e, el, (compat || (false === facade))));
+ };
+ cewrapper.capture = capture;
+
+ if (el == Y.config.win && type == "load") {
+ // window load happens once
+ cewrapper.fireOnce = true;
+ _windowLoadKey = key;
+ }
+
+ _wrappers[key] = cewrapper;
+ _el_events[ek] = _el_events[ek] || {};
+ _el_events[ek][key] = cewrapper;
+
+ add(el, type, cewrapper.fn, capture);
+ }
+
+ return cewrapper;
+
+ },
+
+ _attach: function(args, config) {
+
+ var compat, E=Y.Event,
+ handles, oEl, cewrapper, context,
+ fireNow = false, ret,
+ type = args[0],
+ fn = args[1],
+ el = args[2] || Y.config.win,
+ facade = config && config.facade,
+ capture = config && config.capture;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // trimmedArgs.pop();
+ }
+
+ if (!fn || !fn.call) {
+// throw new TypeError(type + " attach call failed, callback undefined");
+ return false;
+ }
+
+ // The el argument can be an array of elements or element ids.
+ if (shouldIterate(el)) {
+
+ handles=[];
+
+ Y.each(el, function(v, k) {
+ args[2] = v;
+ handles.push(E._attach(args, config));
+ });
+
+ // return (handles.length === 1) ? handles[0] : handles;
+ return new Y.EventHandle(handles);
+
+ // If the el argument is a string, we assume it is
+ // actually the id of the element. If the page is loaded
+ // we convert el to the actual element, otherwise we
+ // defer attaching the event until the element is
+ // ready
+ } else if (Y.Lang.isString(el)) {
+
+ // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
+
+ if (compat) {
+ oEl = Y.DOM.byId(el);
+ } else {
+
+ oEl = Y.Selector.query(el);
+
+ switch (oEl.length) {
+ case 0:
+ oEl = null;
+ break;
+ case 1:
+ oEl = oEl[0];
+ break;
+ default:
+ args[2] = oEl;
+ return E._attach(args, config);
+ }
+ }
+
+ if (oEl) {
+
+ el = oEl;
+
+ // Not found = defer adding the event until the element is available
+ } else {
+
+ ret = this.onAvailable(el, function() {
+
+ ret.handle = E._attach(args, config);
+
+ }, E, true, false, compat);
+
+ return ret;
+
+ }
+ }
+
+ // Element should be an html element or node
+ if (!el) {
+ return false;
+ }
+
+ if (Y.Node && el instanceof Y.Node) {
+ el = Y.Node.getDOMNode(el);
+ }
+
+ cewrapper = this._createWrapper(el, type, capture, compat, facade);
+
+ if (el == Y.config.win && type == "load") {
+
+ // if the load is complete, fire immediately.
+ // all subscribers, including the current one
+ // will be notified.
+ if (YUI.Env.windowLoaded) {
+ fireNow = true;
+ }
+ }
+
+ if (compat) {
+ args.pop();
+ }
+
+ context = args[3];
+
+ // set context to the Node if not specified
+ // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
+ ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
+
+ if (fireNow) {
+ cewrapper.fire();
+ }
+
+ return ret;
+
+ },
+
+ /**
+ * Removes an event listener. Supports the signature the event was bound
+ * with, but the preferred way to remove listeners is using the handle
+ * that is returned when using Y.on
+ *
+ * @method detach
+ *
+ * @param {String} type the type of event to remove.
+ * @param {Function} fn the method the event invokes. If fn is
+ * undefined, then all event handlers for the type of event are
+ * removed.
+ * @param {String|HTMLElement|Array|NodeList|EventHandle} el An
+ * event handle, an id, an element reference, or a collection
+ * of ids and/or elements to remove the listener from.
+ * @return {boolean} true if the unbind was successful, false otherwise.
+ * @static
+ */
+ detach: function(type, fn, el, obj) {
+
+ var args=Y.Array(arguments, 0, true), compat, i, l, ok,
+ id, ce;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // args.pop();
+ }
+
+ if (type && type.detach) {
+ return type.detach();
+ }
+
+ // The el argument can be a string
+ if (typeof el == "string") {
+
+ // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
+ if (compat) {
+ el = Y.DOM.byId(el);
+ } else {
+ el = Y.Selector.query(el);
+ l = el.length;
+ if (l < 1) {
+ el = null;
+ } else if (l == 1) {
+ el = el[0];
+ }
+ }
+ // return Y.Event.detach.apply(Y.Event, args);
+
+ // The el argument can be an array of elements or element ids.
+ }
+
+ if (!el) {
+ return false;
+ }
+
+ if (shouldIterate(el)) {
+
+ ok = true;
+ for (i=0, l=el.length; i<l; ++i) {
+ args[2] = el[i];
+ ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
+ }
+
+ return ok;
+
+ }
+
+ if (!type || !fn || !fn.call) {
+ return this.purgeElement(el, false, type);
+ }
+
+ id = 'event:' + Y.stamp(el) + type;
+ ce = _wrappers[id];
+
+ if (ce) {
+ return ce.detach(fn);
+ } else {
+ return false;
+ }
+
+ },
+
+ /**
+ * Finds the event in the window object, the caller's arguments, or
+ * in the arguments of another method in the callstack. This is
+ * executed automatically for events registered through the event
+ * manager, so the implementer should not normally need to execute
+ * this function at all.
+ * @method getEvent
+ * @param {Event} e the event parameter from the handler
+ * @param {HTMLElement} el the element the listener was attached to
+ * @return {Event} the event
+ * @static
+ */
+ getEvent: function(e, el, noFacade) {
+ var ev = e || window.event;
+
+ return (noFacade) ? ev :
+ new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
+ },
+
+ /**
+ * Generates an unique ID for the element if it does not already
+ * have one.
+ * @method generateId
+ * @param el the element to create the id for
+ * @return {string} the resulting id of the element
+ * @static
+ */
+ generateId: function(el) {
+ var id = el.id;
+
+ if (!id) {
+ id = Y.stamp(el);
+ el.id = id;
+ }
+
+ return id;
+ },
+
+ /**
+ * We want to be able to use getElementsByTagName as a collection
+ * to attach a group of events to. Unfortunately, different
+ * browsers return different types of collections. This function
+ * tests to determine if the object is array-like. It will also
+ * fail if the object is an array, but is empty.
+ * @method _isValidCollection
+ * @param o the object to test
+ * @return {boolean} true if the object is array-like and populated
+ * @deprecated was not meant to be used directly
+ * @static
+ * @private
+ */
+ _isValidCollection: shouldIterate,
+
+ /**
+ * hook up any deferred listeners
+ * @method _load
+ * @static
+ * @private
+ */
+ _load: function(e) {
+
+ if (!_loadComplete) {
+
+
+ _loadComplete = true;
+
+ // Just in case DOMReady did not go off for some reason
+ // E._ready();
+ if (Y.fire) {
+ Y.fire(EVENT_READY);
+ }
+
+ // Available elements may not have been detected before the
+ // window load event fires. Try to find them now so that the
+ // the user is more likely to get the onAvailable notifications
+ // before the window load notification
+ Y.Event._poll();
+
+ }
+ },
+
+ /**
+ * Polling function that runs before the onload event fires,
+ * attempting to attach to DOM Nodes as soon as they are
+ * available
+ * @method _poll
+ * @static
+ * @private
+ */
+ _poll: function() {
+
+ if (this.locked) {
+ return;
+ }
+
+ if (Y.UA.ie && !YUI.Env.DOMReady) {
+ // Hold off if DOMReady has not fired and check current
+ // readyState to protect against the IE operation aborted
+ // issue.
+ this.startInterval();
+ return;
+ }
+
+ this.locked = true;
+
+
+ // keep trying until after the page is loaded. We need to
+ // check the page load state prior to trying to bind the
+ // elements so that we can be certain all elements have been
+ // tested appropriately
+ var tryAgain = !_loadComplete, notAvail, executeItem,
+ i, len, item, el;
+
+ if (!tryAgain) {
+ tryAgain = (_retryCount > 0);
+ }
+
+ // onAvailable
+ notAvail = [];
+
+ executeItem = function (el, item) {
+
+ var context, ov = item.override;
+
+ if (item.compat) {
+
+ if (item.override) {
+ if (ov === true) {
+ context = item.obj;
+ } else {
+ context = ov;
+ }
+ } else {
+ context = el;
+ }
+
+ item.fn.call(context, item.obj);
+
+ } else {
+ context = item.obj || Y.one(el);
+ item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
+ }
+
+ };
+
+
+ // onAvailable
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && !item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ executeItem(el, item);
+ _avail[i] = null;
+ } else {
+ notAvail.push(item);
+ }
+ }
+ }
+
+ // onContentReady
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ // The element is available, but not necessarily ready
+ // @todo should we test parentNode.nextSibling?
+ if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
+ executeItem(el, item);
+ _avail[i] = null;
+ }
+ } else {
+ notAvail.push(item);
+ }
+ }
+ }
+
+ _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
+
+ if (tryAgain) {
+ // we may need to strip the nulled out items here
+ this.startInterval();
+ } else {
+ clearInterval(this._interval);
+ this._interval = null;
+ }
+
+ this.locked = false;
+
+ return;
+
+ },
+
+ /**
+ * Removes all listeners attached to the given element via addListener.
+ * Optionally, the node's children can also be purged.
+ * Optionally, you can specify a specific type of event to remove.
+ * @method purgeElement
+ * @param {HTMLElement} el the element to purge
+ * @param {boolean} recurse recursively purge this element's children
+ * as well. Use with caution.
+ * @param {string} type optional type of listener to purge. If
+ * left out, all listeners will be removed
+ * @static
+ */
+ purgeElement: function(el, recurse, type) {
+ // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
+ var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el,
+ lis = this.getListeners(oEl, type), i, len, props;
+ if (lis) {
+ for (i=0,len=lis.length; i<len ; ++i) {
+ props = lis[i];
+ props.detachAll();
+ remove(props.el, props.type, props.fn, props.capture);
+ delete _wrappers[props.key];
+ delete _el_events[props.domkey][props.key];
+ }
+
+ }
+
+ if (recurse && oEl && oEl.childNodes) {
+ for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
+ this.purgeElement(oEl.childNodes[i], recurse, type);
+ }
+ }
+
+ },
+
+ /**
+ * Returns all listeners attached to the given element via addListener.
+ * Optionally, you can specify a specific type of event to return.
+ * @method getListeners
+ * @param el {HTMLElement|string} the element or element id to inspect
+ * @param type {string} optional type of listener to return. If
+ * left out, all listeners will be returned
+ * @return {Y.Custom.Event} the custom event wrapper for the DOM event(s)
+ * @static
+ */
+ getListeners: function(el, type) {
+ var ek = Y.stamp(el, true), evts = _el_events[ek],
+ results=[] , key = (type) ? 'event:' + ek + type : null;
+
+ if (!evts) {
+ return null;
+ }
+
+ if (key) {
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ // get native events as well
+ key += 'native';
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ } else {
+ Y.each(evts, function(v, k) {
+ results.push(v);
+ });
+ }
+
+ return (results.length) ? results : null;
+ },
+
+ /**
+ * Removes all listeners registered by pe.event. Called
+ * automatically during the unload event.
+ * @method _unload
+ * @static
+ * @private
+ */
+ _unload: function(e) {
+ Y.each(_wrappers, function(v, k) {
+ v.detachAll();
+ remove(v.el, v.type, v.fn, v.capture);
+ delete _wrappers[k];
+ delete _el_events[v.domkey][k];
+ });
+ },
+
+
+ /**
+ * Adds a DOM event directly without the caching, cleanup, context adj, etc
+ *
+ * @method nativeAdd
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeAdd: add,
+
+ /**
+ * Basic remove listener
+ *
+ * @method nativeRemove
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeRemove: remove
+ };
+
+}();
+
+Y.Event = Event;
+
+
+if (Y.config.injected || YUI.Env.windowLoaded) {
+ onLoad();
+} else {
+ add(window, "load", onLoad);
+}
+
+// Process onAvailable/onContentReady items when when the DOM is ready in IE
+if (Y.UA.ie) {
+ Y.on(EVENT_READY, Event._poll, Event, true);
+}
+
+Y.on("unload", onUnload);
+
+Event.Custom = Y.CustomEvent;
+Event.Subscriber = Y.Subscriber;
+Event.Target = Y.EventTarget;
+Event.Handle = Y.EventHandle;
+Event.Facade = Y.EventFacade;
+
+Event._poll();
+
+})();
+
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM.
+ * @event available
+ * @param type {string} 'available'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.available = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
+ }
+};
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM with a nextSibling property
+ * (indicating that the element's children are available)
+ * @event contentready
+ * @param type {string} 'contentready'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.contentready = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+(function() {
+
+
+// Unlike most of the library, this code has to be executed as soon as it is
+// introduced into the page -- and it should only be executed one time
+// regardless of the number of instances that use it.
+
+var GLOBAL_ENV = YUI.Env,
+
+ C = YUI.config,
+
+ D = C.doc,
+
+ POLL_INTERVAL = C.pollInterval || 40,
+
+ _ready = function(e) {
+ GLOBAL_ENV._ready();
+ };
+
+ if (!GLOBAL_ENV._ready) {
+
+ GLOBAL_ENV._ready = function() {
+ if (!GLOBAL_ENV.DOMReady) {
+ GLOBAL_ENV.DOMReady=true;
+
+ // Remove the DOMContentLoaded (FF/Opera/Safari)
+ if (D.removeEventListener) {
+ D.removeEventListener("DOMContentLoaded", _ready, false);
+ }
+ }
+ };
+
+ // create custom event
+
+/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
+
+ // Internet Explorer: use the readyState of a defered script.
+ // This isolates what appears to be a safe moment to manipulate
+ // the DOM prior to when the document's readyState suggests
+ // it is safe to do so.
+ if (navigator.userAgent.match(/MSIE/)) {
+
+ if (self !== self.top) {
+ document.onreadystatechange = function() {
+ if (document.readyState == 'complete') {
+ document.onreadystatechange = null;
+ _ready();
+ }
+ };
+ } else {
+
+ GLOBAL_ENV._dri = setInterval(function() {
+ try {
+ // throws an error if doc is not ready
+ document.documentElement.doScroll('left');
+ clearInterval(GLOBAL_ENV._dri);
+ GLOBAL_ENV._dri = null;
+ _ready();
+ } catch (ex) {
+ }
+ }, POLL_INTERVAL);
+ }
+
+ // FireFox, Opera, Safari 3+: These browsers provide a event for this
+ // moment.
+ } else {
+ D.addEventListener("DOMContentLoaded", _ready, false);
+ }
+
+ /////////////////////////////////////////////////////////////
+ }
+
+})();
+YUI.add('event-base', function(Y) {
+
+(function() {
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+var GLOBAL_ENV = YUI.Env,
+
+ yready = function() {
+ Y.fire('domready');
+ };
+
+Y.publish('domready', {
+ fireOnce: true
+});
+
+if (GLOBAL_ENV.DOMReady) {
+ // console.log('DOMReady already fired', 'info', 'event');
+ yready();
+} else {
+ // console.log('setting up before listener', 'info', 'event');
+ // console.log('env: ' + YUI.Env.windowLoaded, 'info', 'event');
+ Y.before(yready, GLOBAL_ENV, "_ready");
+}
+
+})();
+(function() {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Wraps a DOM event, properties requiring browser abstraction are
+ * fixed here. Provids a security layer when required.
+ * @class DOMEventFacade
+ * @param ev {Event} the DOM event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
+ */
+
+/*
+ * @TODO constants? LEFTBUTTON, MIDDLEBUTTON, RIGHTBUTTON, keys
+ */
+
+/*
+
+var whitelist = {
+ altKey : 1,
+ // "button" : 1, // we supply
+ // "bubbles" : 1, // needed?
+ // "cancelable" : 1, // needed?
+ // "charCode" : 1, // we supply
+ cancelBubble : 1,
+ // "currentTarget" : 1, // we supply
+ ctrlKey : 1,
+ clientX : 1, // needed?
+ clientY : 1, // needed?
+ detail : 1, // not fully implemented
+ // "fromElement" : 1,
+ keyCode : 1,
+ // "height" : 1, // needed?
+ // "initEvent" : 1, // need the init events?
+ // "initMouseEvent" : 1,
+ // "initUIEvent" : 1,
+ // "layerX" : 1, // needed?
+ // "layerY" : 1, // needed?
+ metaKey : 1,
+ // "modifiers" : 1, // needed?
+ // "offsetX" : 1, // needed?
+ // "offsetY" : 1, // needed?
+ // "preventDefault" : 1, // we supply
+ // "reason" : 1, // IE proprietary
+ // "relatedTarget" : 1,
+ // "returnValue" : 1, // needed?
+ shiftKey : 1,
+ // "srcUrn" : 1, // IE proprietary
+ // "srcElement" : 1,
+ // "srcFilter" : 1, IE proprietary
+ // "stopPropagation" : 1, // we supply
+ // "target" : 1,
+ // "timeStamp" : 1, // needed?
+ // "toElement" : 1,
+ type : 1,
+ // "view" : 1,
+ // "which" : 1, // we supply
+ // "width" : 1, // needed?
+ x : 1,
+ y : 1
+},
+
+*/
+
+ var ua = Y.UA,
+
+ /**
+ * webkit key remapping required for Safari < 3.1
+ * @property webkitKeymap
+ * @private
+ */
+ webkitKeymap = {
+ 63232: 38, // up
+ 63233: 40, // down
+ 63234: 37, // left
+ 63235: 39, // right
+ 63276: 33, // page up
+ 63277: 34, // page down
+ 25: 9, // SHIFT-TAB (Safari provides a different key code in
+ // this case, even though the shiftKey modifier is set)
+ 63272: 46, // delete
+ 63273: 36, // home
+ 63275: 35 // end
+ },
+
+ /**
+ * Returns a wrapped node. Intended to be used on event targets,
+ * so it will return the node's parent if the target is a text
+ * node.
+ *
+ * If accessing a property of the node throws an error, this is
+ * probably the anonymous div wrapper Gecko adds inside text
+ * nodes. This likely will only occur when attempting to access
+ * the relatedTarget. In this case, we now return null because
+ * the anonymous div is completely useless and we do not know
+ * what the related target was because we can't even get to
+ * the element's parent node.
+ *
+ * @method resolve
+ * @private
+ */
+ resolve = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ n = n.parentNode;
+ }
+ } catch(e) {
+ return null;
+ }
+
+ return Y.one(n);
+ };
+
+
+// provide a single event with browser abstractions resolved
+//
+// include all properties for both browers?
+// include only DOM2 spec properties?
+// provide browser-specific facade?
+
+Y.DOMEventFacade = function(ev, currentTarget, wrapper) {
+
+ wrapper = wrapper || {};
+
+ var e = ev, ot = currentTarget, d = Y.config.doc, b = d.body,
+ x = e.pageX, y = e.pageY, c, t;
+
+ this.altKey = e.altKey;
+ this.ctrlKey = e.ctrlKey;
+ this.metaKey = e.metaKey;
+ this.shiftKey = e.shiftKey;
+ this.type = e.type;
+ this.clientX = e.clientX;
+ this.clientY = e.clientY;
+
+ //////////////////////////////////////////////////////
+
+ if (!x && 0 !== x) {
+ x = e.clientX || 0;
+ y = e.clientY || 0;
+
+ if (ua.ie) {
+ x += Math.max(d.documentElement.scrollLeft, b.scrollLeft);
+ y += Math.max(d.documentElement.scrollTop, b.scrollTop);
+ }
+ }
+
+ this._yuifacade = true;
+
+ /**
+ * The native event
+ * @property _event
+ */
+ this._event = e;
+
+ /**
+ * The X location of the event on the page (including scroll)
+ * @property pageX
+ * @type int
+ */
+ this.pageX = x;
+
+ /**
+ * The Y location of the event on the page (including scroll)
+ * @property pageY
+ * @type int
+ */
+ this.pageY = y;
+
+ //////////////////////////////////////////////////////
+
+ c = e.keyCode || e.charCode || 0;
+
+ if (ua.webkit && (c in webkitKeymap)) {
+ c = webkitKeymap[c];
+ }
+
+ /**
+ * The keyCode for key events. Uses charCode if keyCode is not available
+ * @property keyCode
+ * @type int
+ */
+ this.keyCode = c;
+
+ /**
+ * The charCode for key events. Same as keyCode
+ * @property charCode
+ * @type int
+ */
+ this.charCode = c;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * The button that was pushed.
+ * @property button
+ * @type int
+ */
+ this.button = e.which || e.button;
+
+ /**
+ * The button that was pushed. Same as button.
+ * @property which
+ * @type int
+ */
+ this.which = this.button;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted element
+ * @propery target
+ * @type Node
+ */
+ this.target = resolve(e.target || e.srcElement);
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = resolve(ot);
+
+ t = e.relatedTarget;
+
+ if (!t) {
+ if (e.type == "mouseout") {
+ t = e.toElement;
+ } else if (e.type == "mouseover") {
+ t = e.fromElement;
+ }
+ }
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = resolve(t);
+
+ /**
+ * Number representing the direction and velocity of the movement of the mousewheel.
+ * Negative is down, the higher the number, the faster. Applies to the mousewheel event.
+ * @property wheelDelta
+ * @type int
+ */
+ if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
+ this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
+ }
+
+ //////////////////////////////////////////////////////
+ // methods
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ } else {
+ e.cancelBubble = true;
+ }
+ wrapper.stopped = 1;
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ if (e.stopImmediatePropagation) {
+ e.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ wrapper.stopped = 2;
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ * @param returnValue {string} sets the returnValue of the event to this value
+ * (rather than the default false value). This can be used to add a customized
+ * confirmation query to the beforeunload event).
+ */
+ this.preventDefault = function(returnValue) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ e.returnValue = returnValue || false;
+ wrapper.prevented = 1;
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+
+ this.preventDefault();
+ };
+
+};
+
+})();
+(function() {
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * The event utility provides functions to add and remove event listeners,
+ * event cleansing. It also tries to automatically remove listeners it
+ * registers during the unload event.
+ *
+ * @class Event
+ * @static
+ */
+
+Y.Env.evt.dom_wrappers = {};
+Y.Env.evt.dom_map = {};
+
+var _eventenv = Y.Env.evt,
+add = YUI.Env.add,
+remove = YUI.Env.remove,
+
+onLoad = function() {
+ YUI.Env.windowLoaded = true;
+ Y.Event._load();
+ remove(window, "load", onLoad);
+},
+
+onUnload = function() {
+ Y.Event._unload();
+ remove(window, "unload", onUnload);
+},
+
+EVENT_READY = 'domready',
+
+COMPAT_ARG = '~yui|2|compat~',
+
+shouldIterate = function(o) {
+ try {
+ return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !o.alert);
+ } catch(ex) {
+ Y.log("collection check failure", "warn", "event");
+ return false;
+ }
+
+},
+
+Event = function() {
+
+ /**
+ * True after the onload event has fired
+ * @property _loadComplete
+ * @type boolean
+ * @static
+ * @private
+ */
+ var _loadComplete = false,
+
+ /**
+ * The number of times to poll after window.onload. This number is
+ * increased if additional late-bound handlers are requested after
+ * the page load.
+ * @property _retryCount
+ * @static
+ * @private
+ */
+ _retryCount = 0,
+
+ /**
+ * onAvailable listeners
+ * @property _avail
+ * @static
+ * @private
+ */
+ _avail = [],
+
+ /**
+ * Custom event wrappers for DOM events. Key is
+ * 'event:' + Element uid stamp + event type
+ * @property _wrappers
+ * @type Y.Event.Custom
+ * @static
+ * @private
+ */
+ _wrappers = _eventenv.dom_wrappers,
+
+ _windowLoadKey = null,
+
+ /**
+ * Custom event wrapper map DOM events. Key is
+ * Element uid stamp. Each item is a hash of custom event
+ * wrappers as provided in the _wrappers collection. This
+ * provides the infrastructure for getListeners.
+ * @property _el_events
+ * @static
+ * @private
+ */
+ _el_events = _eventenv.dom_map;
+
+ return {
+
+ /**
+ * The number of times we should look for elements that are not
+ * in the DOM at the time the event is requested after the document
+ * has been loaded. The default is 1000@amp;40 ms, so it will poll
+ * for 40 seconds or until all outstanding handlers are bound
+ * (whichever comes first).
+ * @property POLL_RETRYS
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_RETRYS: 1000,
+
+ /**
+ * The poll interval in milliseconds
+ * @property POLL_INTERVAL
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_INTERVAL: 40,
+
+ /**
+ * addListener/removeListener can throw errors in unexpected scenarios.
+ * These errors are suppressed, the method returns false, and this property
+ * is set
+ * @property lastError
+ * @static
+ * @type Error
+ */
+ lastError: null,
+
+
+ /**
+ * poll handle
+ * @property _interval
+ * @static
+ * @private
+ */
+ _interval: null,
+
+ /**
+ * document readystate poll handle
+ * @property _dri
+ * @static
+ * @private
+ */
+ _dri: null,
+
+ /**
+ * True when the document is initially usable
+ * @property DOMReady
+ * @type boolean
+ * @static
+ */
+ DOMReady: false,
+
+ /**
+ * @method startInterval
+ * @static
+ * @private
+ */
+ startInterval: function() {
+ var E = Y.Event;
+
+ if (!E._interval) {
+E._interval = setInterval(Y.bind(E._poll, E), E.POLL_INTERVAL);
+ }
+ },
+
+ /**
+ * Executes the supplied callback when the item with the supplied
+ * id is found. This is meant to be used to execute behavior as
+ * soon as possible as the page loads. If you use this after the
+ * initial page load it will poll for a fixed time for the element.
+ * The number of times it will poll and the frequency are
+ * configurable. By default it will poll for 10 seconds.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onAvailable
+ *
+ * @param {string||string[]} id the id of the element, or an array
+ * of ids to look for.
+ * @param {function} fn what to execute when the element is found.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj, if set to an object it
+ * will execute in the context of that object
+ * @param checkContent {boolean} check child node readiness (onContentReady)
+ * @static
+ * @deprecated Use Y.on("available")
+ */
+ // @TODO fix arguments
+ onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
+
+ var a = Y.Array(id), i, availHandle;
+
+ // Y.log('onAvailable registered for: ' + id);
+
+ for (i=0; i<a.length; i=i+1) {
+ _avail.push({
+ id: a[i],
+ fn: fn,
+ obj: p_obj,
+ override: p_override,
+ checkReady: checkContent,
+ compat: compat
+ });
+ }
+ _retryCount = this.POLL_RETRYS;
+
+ // We want the first test to be immediate, but async
+ setTimeout(Y.bind(Y.Event._poll, Y.Event), 0);
+
+ availHandle = new Y.EventHandle({
+
+ _delete: function() {
+ // set by the event system for lazy DOM listeners
+ if (availHandle.handle) {
+ availHandle.handle.detach();
+ return;
+ }
+
+ var i, j;
+
+ // otherwise try to remove the onAvailable listener(s)
+ for (i = 0; i < a.length; i++) {
+ for (j = 0; j < _avail.length; j++) {
+ if (a[i] === _avail[j].id) {
+ _avail.splice(j, 1);
+ }
+ }
+ }
+ }
+
+ });
+
+ return availHandle;
+ },
+
+ /**
+ * Works the same way as onAvailable, but additionally checks the
+ * state of sibling elements to determine if the content of the
+ * available element is safe to modify.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onContentReady
+ *
+ * @param {string} id the id of the element to look for.
+ * @param {function} fn what to execute when the element is ready.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj. If an object, fn will
+ * exectute in the context of that object
+ *
+ * @static
+ * @deprecated Use Y.on("contentready")
+ */
+ // @TODO fix arguments
+ onContentReady: function(id, fn, p_obj, p_override, compat) {
+ return this.onAvailable(id, fn, p_obj, p_override, true, compat);
+ },
+
+ /**
+ * Adds an event listener
+ *
+ * @method attach
+ *
+ * @param {String} type The type of event to append
+ * @param {Function} fn The method the event invokes
+ * @param {String|HTMLElement|Array|NodeList} el An id, an element
+ * reference, or a collection of ids and/or elements to assign the
+ * listener to.
+ * @param {Object} context optional context object
+ * @param {Boolean|object} args 0..n arguments to pass to the callback
+ * @return {EventHandle} an object to that can be used to detach the listener
+ *
+ * @static
+ */
+
+ attach: function(type, fn, el, context) {
+ return Y.Event._attach(Y.Array(arguments, 0, true));
+ },
+
+ _createWrapper: function (el, type, capture, compat, facade) {
+
+ var ek = Y.stamp(el),
+ key = 'event:' + ek + type,
+ cewrapper;
+
+
+ if (false === facade) {
+ key += 'native';
+ }
+ if (capture) {
+ key += 'capture';
+ }
+
+
+ cewrapper = _wrappers[key];
+
+
+ if (!cewrapper) {
+ // create CE wrapper
+ cewrapper = Y.publish(key, {
+ silent: true,
+ bubbles: false,
+ contextFn: function() {
+ cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
+ return cewrapper.nodeRef;
+ }
+ });
+
+ // for later removeListener calls
+ cewrapper.el = el;
+ cewrapper.key = key;
+ cewrapper.domkey = ek;
+ cewrapper.type = type;
+ cewrapper.fn = function(e) {
+ cewrapper.fire(Y.Event.getEvent(e, el, (compat || (false === facade))));
+ };
+ cewrapper.capture = capture;
+
+ if (el == Y.config.win && type == "load") {
+ // window load happens once
+ cewrapper.fireOnce = true;
+ _windowLoadKey = key;
+ }
+
+ _wrappers[key] = cewrapper;
+ _el_events[ek] = _el_events[ek] || {};
+ _el_events[ek][key] = cewrapper;
+
+ add(el, type, cewrapper.fn, capture);
+ }
+
+ return cewrapper;
+
+ },
+
+ _attach: function(args, config) {
+
+ var compat, E=Y.Event,
+ handles, oEl, cewrapper, context,
+ fireNow = false, ret,
+ type = args[0],
+ fn = args[1],
+ el = args[2] || Y.config.win,
+ facade = config && config.facade,
+ capture = config && config.capture;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // trimmedArgs.pop();
+ }
+
+ if (!fn || !fn.call) {
+// throw new TypeError(type + " attach call failed, callback undefined");
+Y.log(type + " attach call failed, invalid callback", "error", "event");
+ return false;
+ }
+
+ // The el argument can be an array of elements or element ids.
+ if (shouldIterate(el)) {
+
+ handles=[];
+
+ Y.each(el, function(v, k) {
+ args[2] = v;
+ handles.push(E._attach(args, config));
+ });
+
+ // return (handles.length === 1) ? handles[0] : handles;
+ return new Y.EventHandle(handles);
+
+ // If the el argument is a string, we assume it is
+ // actually the id of the element. If the page is loaded
+ // we convert el to the actual element, otherwise we
+ // defer attaching the event until the element is
+ // ready
+ } else if (Y.Lang.isString(el)) {
+
+ // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
+
+ if (compat) {
+ oEl = Y.DOM.byId(el);
+ } else {
+
+ oEl = Y.Selector.query(el);
+
+ switch (oEl.length) {
+ case 0:
+ oEl = null;
+ break;
+ case 1:
+ oEl = oEl[0];
+ break;
+ default:
+ args[2] = oEl;
+ return E._attach(args, config);
+ }
+ }
+
+ if (oEl) {
+
+ el = oEl;
+
+ // Not found = defer adding the event until the element is available
+ } else {
+
+ // Y.log(el + ' not found');
+ ret = this.onAvailable(el, function() {
+ // Y.log('lazy attach: ' + args);
+
+ ret.handle = E._attach(args, config);
+
+ }, E, true, false, compat);
+
+ return ret;
+
+ }
+ }
+
+ // Element should be an html element or node
+ if (!el) {
+ Y.log("unable to attach event " + type, "warn", "event");
+ return false;
+ }
+
+ if (Y.Node && el instanceof Y.Node) {
+ el = Y.Node.getDOMNode(el);
+ }
+
+ cewrapper = this._createWrapper(el, type, capture, compat, facade);
+
+ if (el == Y.config.win && type == "load") {
+
+ // if the load is complete, fire immediately.
+ // all subscribers, including the current one
+ // will be notified.
+ if (YUI.Env.windowLoaded) {
+ fireNow = true;
+ }
+ }
+
+ if (compat) {
+ args.pop();
+ }
+
+ context = args[3];
+
+ // set context to the Node if not specified
+ // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
+ ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
+
+ if (fireNow) {
+ cewrapper.fire();
+ }
+
+ return ret;
+
+ },
+
+ /**
+ * Removes an event listener. Supports the signature the event was bound
+ * with, but the preferred way to remove listeners is using the handle
+ * that is returned when using Y.on
+ *
+ * @method detach
+ *
+ * @param {String} type the type of event to remove.
+ * @param {Function} fn the method the event invokes. If fn is
+ * undefined, then all event handlers for the type of event are
+ * removed.
+ * @param {String|HTMLElement|Array|NodeList|EventHandle} el An
+ * event handle, an id, an element reference, or a collection
+ * of ids and/or elements to remove the listener from.
+ * @return {boolean} true if the unbind was successful, false otherwise.
+ * @static
+ */
+ detach: function(type, fn, el, obj) {
+
+ var args=Y.Array(arguments, 0, true), compat, i, l, ok,
+ id, ce;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // args.pop();
+ }
+
+ if (type && type.detach) {
+ return type.detach();
+ }
+
+ // The el argument can be a string
+ if (typeof el == "string") {
+
+ // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
+ if (compat) {
+ el = Y.DOM.byId(el);
+ } else {
+ el = Y.Selector.query(el);
+ l = el.length;
+ if (l < 1) {
+ el = null;
+ } else if (l == 1) {
+ el = el[0];
+ }
+ }
+ // return Y.Event.detach.apply(Y.Event, args);
+
+ // The el argument can be an array of elements or element ids.
+ }
+
+ if (!el) {
+ return false;
+ }
+
+ if (shouldIterate(el)) {
+
+ ok = true;
+ for (i=0, l=el.length; i<l; ++i) {
+ args[2] = el[i];
+ ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
+ }
+
+ return ok;
+
+ }
+
+ if (!type || !fn || !fn.call) {
+ return this.purgeElement(el, false, type);
+ }
+
+ id = 'event:' + Y.stamp(el) + type;
+ ce = _wrappers[id];
+
+ if (ce) {
+ return ce.detach(fn);
+ } else {
+ return false;
+ }
+
+ },
+
+ /**
+ * Finds the event in the window object, the caller's arguments, or
+ * in the arguments of another method in the callstack. This is
+ * executed automatically for events registered through the event
+ * manager, so the implementer should not normally need to execute
+ * this function at all.
+ * @method getEvent
+ * @param {Event} e the event parameter from the handler
+ * @param {HTMLElement} el the element the listener was attached to
+ * @return {Event} the event
+ * @static
+ */
+ getEvent: function(e, el, noFacade) {
+ var ev = e || window.event;
+
+ return (noFacade) ? ev :
+ new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
+ },
+
+ /**
+ * Generates an unique ID for the element if it does not already
+ * have one.
+ * @method generateId
+ * @param el the element to create the id for
+ * @return {string} the resulting id of the element
+ * @static
+ */
+ generateId: function(el) {
+ var id = el.id;
+
+ if (!id) {
+ id = Y.stamp(el);
+ el.id = id;
+ }
+
+ return id;
+ },
+
+ /**
+ * We want to be able to use getElementsByTagName as a collection
+ * to attach a group of events to. Unfortunately, different
+ * browsers return different types of collections. This function
+ * tests to determine if the object is array-like. It will also
+ * fail if the object is an array, but is empty.
+ * @method _isValidCollection
+ * @param o the object to test
+ * @return {boolean} true if the object is array-like and populated
+ * @deprecated was not meant to be used directly
+ * @static
+ * @private
+ */
+ _isValidCollection: shouldIterate,
+
+ /**
+ * hook up any deferred listeners
+ * @method _load
+ * @static
+ * @private
+ */
+ _load: function(e) {
+
+ if (!_loadComplete) {
+
+ // Y.log('Load Complete', 'info', 'event');
+
+ _loadComplete = true;
+
+ // Just in case DOMReady did not go off for some reason
+ // E._ready();
+ if (Y.fire) {
+ Y.fire(EVENT_READY);
+ }
+
+ // Available elements may not have been detected before the
+ // window load event fires. Try to find them now so that the
+ // the user is more likely to get the onAvailable notifications
+ // before the window load notification
+ Y.Event._poll();
+
+ }
+ },
+
+ /**
+ * Polling function that runs before the onload event fires,
+ * attempting to attach to DOM Nodes as soon as they are
+ * available
+ * @method _poll
+ * @static
+ * @private
+ */
+ _poll: function() {
+
+ if (this.locked) {
+ return;
+ }
+
+ if (Y.UA.ie && !YUI.Env.DOMReady) {
+ // Hold off if DOMReady has not fired and check current
+ // readyState to protect against the IE operation aborted
+ // issue.
+ this.startInterval();
+ return;
+ }
+
+ this.locked = true;
+
+ // Y.log.debug("poll");
+
+ // keep trying until after the page is loaded. We need to
+ // check the page load state prior to trying to bind the
+ // elements so that we can be certain all elements have been
+ // tested appropriately
+ var tryAgain = !_loadComplete, notAvail, executeItem,
+ i, len, item, el;
+
+ if (!tryAgain) {
+ tryAgain = (_retryCount > 0);
+ }
+
+ // onAvailable
+ notAvail = [];
+
+ executeItem = function (el, item) {
+
+ var context, ov = item.override;
+
+ if (item.compat) {
+
+ if (item.override) {
+ if (ov === true) {
+ context = item.obj;
+ } else {
+ context = ov;
+ }
+ } else {
+ context = el;
+ }
+
+ item.fn.call(context, item.obj);
+
+ } else {
+ context = item.obj || Y.one(el);
+ item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
+ }
+
+ };
+
+
+ // onAvailable
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && !item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ // Y.log('avail: ' + el);
+ executeItem(el, item);
+ _avail[i] = null;
+ } else {
+ // Y.log('NOT avail: ' + el);
+ notAvail.push(item);
+ }
+ }
+ }
+
+ // onContentReady
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ // The element is available, but not necessarily ready
+ // @todo should we test parentNode.nextSibling?
+ if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
+ executeItem(el, item);
+ _avail[i] = null;
+ }
+ } else {
+ notAvail.push(item);
+ }
+ }
+ }
+
+ _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
+
+ if (tryAgain) {
+ // we may need to strip the nulled out items here
+ this.startInterval();
+ } else {
+ clearInterval(this._interval);
+ this._interval = null;
+ }
+
+ this.locked = false;
+
+ return;
+
+ },
+
+ /**
+ * Removes all listeners attached to the given element via addListener.
+ * Optionally, the node's children can also be purged.
+ * Optionally, you can specify a specific type of event to remove.
+ * @method purgeElement
+ * @param {HTMLElement} el the element to purge
+ * @param {boolean} recurse recursively purge this element's children
+ * as well. Use with caution.
+ * @param {string} type optional type of listener to purge. If
+ * left out, all listeners will be removed
+ * @static
+ */
+ purgeElement: function(el, recurse, type) {
+ // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
+ var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el,
+ lis = this.getListeners(oEl, type), i, len, props;
+ if (lis) {
+ for (i=0,len=lis.length; i<len ; ++i) {
+ props = lis[i];
+ props.detachAll();
+ remove(props.el, props.type, props.fn, props.capture);
+ delete _wrappers[props.key];
+ delete _el_events[props.domkey][props.key];
+ }
+
+ }
+
+ if (recurse && oEl && oEl.childNodes) {
+ for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
+ this.purgeElement(oEl.childNodes[i], recurse, type);
+ }
+ }
+
+ },
+
+ /**
+ * Returns all listeners attached to the given element via addListener.
+ * Optionally, you can specify a specific type of event to return.
+ * @method getListeners
+ * @param el {HTMLElement|string} the element or element id to inspect
+ * @param type {string} optional type of listener to return. If
+ * left out, all listeners will be returned
+ * @return {Y.Custom.Event} the custom event wrapper for the DOM event(s)
+ * @static
+ */
+ getListeners: function(el, type) {
+ var ek = Y.stamp(el, true), evts = _el_events[ek],
+ results=[] , key = (type) ? 'event:' + ek + type : null;
+
+ if (!evts) {
+ return null;
+ }
+
+ if (key) {
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ // get native events as well
+ key += 'native';
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ } else {
+ Y.each(evts, function(v, k) {
+ results.push(v);
+ });
+ }
+
+ return (results.length) ? results : null;
+ },
+
+ /**
+ * Removes all listeners registered by pe.event. Called
+ * automatically during the unload event.
+ * @method _unload
+ * @static
+ * @private
+ */
+ _unload: function(e) {
+ Y.each(_wrappers, function(v, k) {
+ v.detachAll();
+ remove(v.el, v.type, v.fn, v.capture);
+ delete _wrappers[k];
+ delete _el_events[v.domkey][k];
+ });
+ },
+
+
+ /**
+ * Adds a DOM event directly without the caching, cleanup, context adj, etc
+ *
+ * @method nativeAdd
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeAdd: add,
+
+ /**
+ * Basic remove listener
+ *
+ * @method nativeRemove
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeRemove: remove
+ };
+
+}();
+
+Y.Event = Event;
+
+
+if (Y.config.injected || YUI.Env.windowLoaded) {
+ onLoad();
+} else {
+ add(window, "load", onLoad);
+}
+
+// Process onAvailable/onContentReady items when when the DOM is ready in IE
+if (Y.UA.ie) {
+ Y.on(EVENT_READY, Event._poll, Event, true);
+}
+
+Y.on("unload", onUnload);
+
+Event.Custom = Y.CustomEvent;
+Event.Subscriber = Y.Subscriber;
+Event.Target = Y.EventTarget;
+Event.Handle = Y.EventHandle;
+Event.Facade = Y.EventFacade;
+
+Event._poll();
+
+})();
+
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM.
+ * @event available
+ * @param type {string} 'available'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.available = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
+ }
+};
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM with a nextSibling property
+ * (indicating that the element's children are available)
+ * @event contentready
+ * @param type {string} 'contentready'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.contentready = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
+YUI.add('event-delegate', function(Y) {
+
+/**
+ * Adds event delegation support to the library.
+ *
+ * @module event
+ * @submodule event-delegate
+ */
+
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ delegates = {},
+
+ specialTypes = {
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+ },
+
+ resolveTextNode = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ return n.parentNode;
+ }
+ } catch(e) { }
+ return n;
+ },
+
+ delegateHandler = function(delegateKey, e, el) {
+
+ var target = resolveTextNode((e.target || e.srcElement)),
+ tests = delegates[delegateKey],
+ spec,
+ ename,
+ matched,
+ fn,
+ ev;
+
+
+ var getMatch = function(el, selector, container) {
+
+ var returnVal;
+
+ if (!el || el === container) {
+ returnVal = false;
+ }
+ else {
+ returnVal = Y.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
+ }
+
+ return returnVal;
+
+ };
+
+
+ for (spec in tests) {
+
+ if (tests.hasOwnProperty(spec)) {
+
+ ename = tests[spec];
+ fn = tests.fn;
+ matched = null;
+
+
+ if (Y.Selector.test(target, spec, el)) {
+ matched = target;
+ }
+ else if (Y.Selector.test(target, ((spec.replace(/,/gi, " *,")) + " *"), el)) {
+
+ // The target is a descendant of an element matching
+ // the selector, so crawl up to find the ancestor that
+ // matches the selector
+
+ matched = getMatch(target, spec, el);
+
+ }
+
+
+ if (matched) {
+
+ if (!ev) {
+ ev = new Y.DOMEventFacade(e, el);
+ ev.container = ev.currentTarget;
+ }
+
+ ev.currentTarget = Y.Node.get(matched);
+
+ Y.publish(ename, {
+ contextFn: function() {
+ return ev.currentTarget;
+ }
+ });
+
+ if (fn) {
+ fn(ev, ename);
+ }
+ else {
+ Y.fire(ename, ev);
+ }
+
+ }
+
+ }
+ }
+
+ },
+
+ attach = function (type, key, element) {
+
+ var focusMethods = {
+ focus: Event._attachFocus,
+ blur: Event._attachBlur
+ },
+
+ attachFn = focusMethods[type],
+
+ args = [type,
+ function (e) {
+ delegateHandler(key, (e || window.event), element);
+ },
+ element];
+
+
+ if (attachFn) {
+ return attachFn(args, { capture: true, facade: false });
+ }
+ else {
+ return Event._attach(args, { facade: false });
+ }
+
+ },
+
+ sanitize = Y.cached(function(str) {
+ return str.replace(/[|,:]/g, '~');
+ });
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @event delegate
+ * @param type {string} 'delegate'
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param delegateType {string} the event type to delegate
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ * @deprecated use Y.delegate
+ */
+Y.Env.evt.plugins.delegate = {
+
+ on: function(type, fn, el, delegateType, spec) {
+
+ Y.log('delegate event is deprecated, use Y.delegate()', 'warn', 'deprecated');
+
+ var args = Y.Array(arguments, 0, true);
+
+ args.splice(3, 1);
+
+ args[0] = delegateType;
+
+ return Y.delegate.apply(Y, args);
+
+ }
+
+};
+
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @method delegate
+ * @param type {string} the event type to delegate
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Event.delegate = function (type, fn, el, spec) {
+
+ if (!spec) {
+ Y.log('delegate: no spec, nothing to do', 'warn', 'event');
+ return false;
+ }
+
+
+ var args = Y.Array(arguments, 0, true),
+ element = el, // HTML element serving as the delegation container
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Y.Selector.query returns an array of matches unless specified
+ // to return just the first match. Since the primary use case for
+ // event delegation is to use a single event handler on a container,
+ // Y.delegate doesn't currently support being able to bind a
+ // single listener to multiple containers.
+
+ element = Y.Selector.query(el, null, true);
+
+ if (!element) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Event.delegate.apply(Event, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ element = Y.Node.getDOMNode(element);
+
+
+ var guid = Y.stamp(element),
+
+ // The Custom Event for the delegation spec
+ ename = 'delegate:' + guid + type + sanitize(spec),
+
+ // The key to the listener for the event type and container
+ delegateKey = type + guid,
+
+ delegate = delegates[delegateKey],
+
+ domEventHandle,
+
+ ceHandle,
+
+ listeners;
+
+
+ if (!delegate) {
+
+ delegate = {};
+
+ if (specialTypes[type]) {
+
+ if (!Event._fireMouseEnter) {
+ Y.log("Delegating a " + type + " event requires the event-mouseenter submodule.", "error", "Event");
+ return false;
+ }
+
+ type = specialTypes[type];
+ delegate.fn = Event._fireMouseEnter;
+
+ }
+
+ // Create the DOM Event wrapper that will fire the Custom Event
+
+ domEventHandle = attach(type, delegateKey, element);
+
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'delegates' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known delegates
+ delete delegates[delegateKey];
+
+ Y.log("DOM event listener associated with the " + ename + " Custom Event removed. Removing all " + ename + " listeners.", "info", "Event");
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(ename);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+ delegate.handle = domEventHandle;
+
+ delegates[delegateKey] = delegate;
+
+ }
+
+
+ listeners = delegate.listeners;
+
+ delegate.listeners = listeners ? (listeners + 1) : 1;
+ delegate[spec] = ename;
+
+
+ args[0] = ename;
+
+ // Remove element, delegation spec
+ args.splice(2, 2);
+
+
+ // Subscribe to the Custom Event for the delegation spec
+
+ ceHandle = Y.on.apply(Y, args);
+
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'delegates' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ delegate.listeners = (delegate.listeners - 1);
+
+ if (delegate.listeners === 0) {
+ Y.log("No more listeners for the " + ename + " Custom Event. Removing its associated DOM event listener.", "info", "Event");
+ delegate.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+ return ceHandle;
+
+};
+
+Y.delegate = Event.delegate;
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-mousewheel', function(Y) {
+
+/**
+ * Adds mousewheel event support
+ * @module event
+ * @submodule event-mousewheel
+ */
+var DOM_MOUSE_SCROLL = 'DOMMouseScroll',
+ fixArgs = function(args) {
+ var a = Y.Array(args, 0, true), target;
+ if (Y.UA.gecko) {
+ a[0] = DOM_MOUSE_SCROLL;
+ target = Y.config.win;
+ } else {
+ target = Y.config.doc;
+ }
+
+ if (a.length < 3) {
+ a[2] = target;
+ } else {
+ a.splice(2, 0, target);
+ }
+
+ return a;
+ };
+
+/**
+ * Mousewheel event. This listener is automatically attached to the
+ * correct target, so one should not be supplied. Mouse wheel
+ * direction and velocity is stored in the 'mouseDelta' field.
+ * @event mousewheel
+ * @param type {string} 'mousewheel'
+ * @param fn {function} the callback to execute
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.mousewheel = {
+ on: function() {
+ return Y.Event._attach(fixArgs(arguments));
+ },
+
+ detach: function() {
+ return Y.Event.detach.apply(Y.Event, fixArgs(arguments));
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-mouseenter', function(Y) {
+
+/**
+ * Adds support for mouseenter/mouseleave events
+ * @module event
+ * @submodule event-mouseenter
+ */
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ plugins = Y.Env.evt.plugins,
+
+ listeners = {},
+
+ eventConfig = {
+
+ on: function(type, fn, el) {
+
+ var args = Y.Array(arguments, 0, true),
+ element = el,
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Need to use Y.all because if el is a string it could be a
+ // selector that returns a NodeList
+
+ element = Y.all(el);
+
+ if (element.size() === 0) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Y.on.apply(Y, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ var sDOMEvent = (type === "mouseenter") ? "mouseover" : "mouseout",
+
+ // The name of the custom event
+ sEventName = type + ":" + Y.stamp(element) + sDOMEvent,
+
+ listener = listeners[sEventName],
+
+ domEventHandle,
+
+ ceHandle,
+
+ nListeners;
+
+
+ // Bind an actual DOM event listener that will call the
+ // the custom event
+ if (!listener) {
+
+ domEventHandle = Y.on(sDOMEvent, Y.rbind(Event._fireMouseEnter, Y, sEventName), element);
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'listeners' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known mouseenter
+ // and mouseleave listeners
+ delete listeners[sEventName];
+
+ Y.log("DOM event listener associated with the " + sEventName + " Custom Event removed. Removing all " + sEventName + " listeners.", "info", "Event");
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(sEventName);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+
+ listener = {};
+ listener.handle = domEventHandle;
+
+ listeners[sEventName] = listener;
+
+ }
+
+ nListeners = listener.count;
+
+ listener.count = nListeners ? (nListeners + 1) : 1;
+
+ args[0] = sEventName;
+
+ // Remove the element from the args
+ args.splice(2, 1);
+
+ // Subscribe to the custom event
+ ceHandle = Y.on.apply(Y, args);
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'listeners' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ listener.count = (listener.count - 1);
+
+ if (listener.count === 0) {
+ Y.log("No more listeners for the " + sEventName + " Custom Event. Removing its associated DOM event listener.", "info", "Event");
+ listener.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+
+ return ceHandle;
+
+ }
+
+ };
+
+
+Event._fireMouseEnter = function (e, eventName) {
+
+ var relatedTarget = e.relatedTarget,
+ currentTarget = e.currentTarget;
+
+ if (currentTarget !== relatedTarget &&
+ !currentTarget.contains(relatedTarget)) {
+
+ Y.publish(eventName, {
+ contextFn: function() {
+ return currentTarget;
+ }
+ });
+
+ Y.fire(eventName, e);
+
+ }
+
+};
+
+
+/**
+ * Sets up a "mouseenter" listener—a listener that is called the first time
+ * the user's mouse enters the specified element(s).
+ *
+ * @event mouseenter
+ * @param type {string} "mouseenter"
+ * @param fn {function} The method the event invokes.
+ * @param el {string|node} The element(s) to assign the listener to.
+ * @param spec {string} Optional. String representing a selector that must
+ * match the target of the event in order for the listener to be called.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+plugins.mouseenter = eventConfig;
+
+/**
+* Sets up a "mouseleave" listener—a listener that is called the first time
+* the user's mouse leaves the specified element(s).
+*
+* @event mouseleave
+* @param type {string} "mouseleave"
+* @param fn {function} The method the event invokes.
+* @param el {string|node} The element(s) to assign the listener to.
+* @param spec {string} Optional. String representing a selector that must
+* match the target of the event in order for the listener to be called.
+* @return {EventHandle} the detach handle
+* @for YUI
+ */
+plugins.mouseleave = eventConfig;
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-key', function(Y) {
+
+/**
+ * Functionality to listen for one or more specific key combinations.
+ * @module event
+ * @submodule event-key
+ */
+
+/**
+ * Add a key listener. The listener will only be notified if the
+ * keystroke detected meets the supplied specification. The
+ * spec consists of the key event type, followed by a colon,
+ * followed by zero or more comma separated key codes, followed
+ * by zero or more modifiers delimited by a plus sign. Ex:
+ * press:12,65+shift+ctrl
+ * @event key
+ * @for YUI
+ * @param type {string} 'key'
+ * @param fn {function} the function to execute
+ * @param id {string|HTMLElement|collection} the element(s) to bind
+ * @param spec {string} the keyCode and modifier specification
+ * @param o optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {Event.Handle} the detach handle
+ */
+Y.Env.evt.plugins.key = {
+
+ on: function(type, fn, id, spec, o) {
+ var a = Y.Array(arguments, 0, true), parsed, etype, criteria, ename;
+
+ parsed = spec && spec.split(':');
+
+ if (!spec || spec.indexOf(':') == -1 || !parsed[1]) {
+Y.log('Illegal key spec, creating a regular keypress listener instead.', 'info', 'event');
+ a[0] = 'key' + ((parsed && parsed[0]) || 'press');
+ return Y.on.apply(Y, a);
+ }
+
+ // key event type: 'down', 'up', or 'press'
+ etype = parsed[0];
+
+ // list of key codes optionally followed by modifiers
+ criteria = (parsed[1]) ? parsed[1].split(/,|\+/) : null;
+
+ // the name of the custom event that will be created for the spec
+ ename = (Y.Lang.isString(id) ? id : Y.stamp(id)) + spec;
+
+ ename = ename.replace(/,/g, '_');
+
+ if (!Y.getEvent(ename)) {
+
+ // subscribe spec validator to the DOM event
+ Y.on(type + etype, function(e) {
+
+ // Y.log('keylistener: ' + e.keyCode);
+
+ var passed = false, failed = false, i, crit, critInt;
+
+ for (i=0; i<criteria.length; i=i+1) {
+ crit = criteria[i];
+ critInt = parseInt(crit, 10);
+
+ // pass this section if any supplied keyCode
+ // is found
+ if (Y.Lang.isNumber(critInt)) {
+
+ if (e.charCode === critInt) {
+ // Y.log('passed: ' + crit);
+ passed = true;
+ } else {
+ failed = true;
+ // Y.log('failed: ' + crit);
+ }
+
+ // only check modifier if no keyCode was specified
+ // or the keyCode check was successful. pass only
+ // if every modifier passes
+ } else if (passed || !failed) {
+ passed = (e[crit + 'Key']);
+ failed = !passed;
+ // Y.log(crit + ": " + passed);
+ }
+ }
+
+ // fire spec custom event if spec if met
+ if (passed) {
+ Y.fire(ename, e);
+ }
+
+ }, id);
+
+ }
+
+ // subscribe supplied listener to custom event for spec validator
+ // remove element and spec.
+ a.splice(2, 2);
+ a[0] = ename;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-focus', function(Y) {
+
+/**
+ * Adds focus and blur event listener support. These events normally
+ * do not bubble, so this adds support for that so these events
+ * can be used in event delegation scenarios.
+ *
+ * @module event
+ * @submodule event-focus
+ */
+(function() {
+
+var UA = Y.UA,
+ Event = Y.Event,
+ plugins = Y.Env.evt.plugins,
+ ie = UA.ie,
+ bUseMutation = (UA.opera || UA.webkit),
+ eventNames = {
+ focus: (ie ? 'focusin' : (bUseMutation ? 'DOMFocusIn' : 'focus')),
+ blur: (ie ? 'focusout' : (bUseMutation ? 'DOMFocusOut' : 'blur'))
+ },
+
+ // Only need to use capture phase for Gecko since it doesn't support
+ // focusin, focusout, DOMFocusIn, or DOMFocusOut
+ CAPTURE_CONFIG = { capture: (UA.gecko ? true : false) },
+
+
+ attach = function (args, config) {
+
+ var a = Y.Array(args, 0, true);
+ a[0] = eventNames[a[0]];
+ return Event._attach(a, config);
+
+ },
+
+ eventAdapter = {
+
+ on: function () {
+ return attach(arguments, CAPTURE_CONFIG);
+ }
+
+ };
+
+
+Event._attachFocus = attach;
+Event._attachBlur = attach;
+
+/**
+ * Adds a DOM focus listener. Uses the focusin event in IE,
+ * DOMFocusIn for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event focus
+ * @param type {string} 'focus'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.focus = eventAdapter;
+
+/**
+ * Adds a DOM blur listener. Uses the focusout event in IE,
+ * DOMFocusOut for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event blur
+ * @param type {string} 'blur'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.blur = eventAdapter;
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-resize', function(Y) {
+
+/**
+ * Adds a window resize event that has its behavior normalized to fire at the
+ * end of the resize rather than constantly during the resize.
+ * @module event
+ * @submodule event-resize
+ */
+(function() {
+
+var detachHandle,
+
+ timerHandle,
+
+ CE_NAME = 'window:resize',
+
+ handler = function(e) {
+
+ if (Y.UA.gecko) {
+
+ Y.fire(CE_NAME, e);
+
+ } else {
+
+ if (timerHandle) {
+ timerHandle.cancel();
+ }
+
+ timerHandle = Y.later(Y.config.windowResizeDelay || 40, Y, function() {
+ Y.fire(CE_NAME, e);
+ });
+ }
+
+ };
+
+
+/**
+ * Firefox fires the window resize event once when the resize action
+ * finishes, other browsers fire the event periodically during the
+ * resize. This code uses timeout logic to simulate the Firefox
+ * behavior in other browsers.
+ * @event windowresize
+ * @for YUI
+ */
+Y.Env.evt.plugins.windowresize = {
+
+ on: function(type, fn) {
+
+ // check for single window listener and add if needed
+ if (!detachHandle) {
+ detachHandle = Y.Event._attach(['resize', handler]);
+ }
+
+ var a = Y.Array(arguments, 0, true);
+ a[0] = CE_NAME;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
+
+
+YUI.add('event', function(Y){}, '3.0.0' ,{use:['event-base', 'event-delegate', 'event-mousewheel', 'event-mouseenter', 'event-key', 'event-focus', 'event-resize']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-delegate', function(Y) {
+
+/**
+ * Adds event delegation support to the library.
+ *
+ * @module event
+ * @submodule event-delegate
+ */
+
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ delegates = {},
+
+ specialTypes = {
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+ },
+
+ resolveTextNode = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ return n.parentNode;
+ }
+ } catch(e) { }
+ return n;
+ },
+
+ delegateHandler = function(delegateKey, e, el) {
+
+ var target = resolveTextNode((e.target || e.srcElement)),
+ tests = delegates[delegateKey],
+ spec,
+ ename,
+ matched,
+ fn,
+ ev;
+
+
+ var getMatch = function(el, selector, container) {
+
+ var returnVal;
+
+ if (!el || el === container) {
+ returnVal = false;
+ }
+ else {
+ returnVal = Y.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
+ }
+
+ return returnVal;
+
+ };
+
+
+ for (spec in tests) {
+
+ if (tests.hasOwnProperty(spec)) {
+
+ ename = tests[spec];
+ fn = tests.fn;
+ matched = null;
+
+
+ if (Y.Selector.test(target, spec, el)) {
+ matched = target;
+ }
+ else if (Y.Selector.test(target, ((spec.replace(/,/gi, " *,")) + " *"), el)) {
+
+ // The target is a descendant of an element matching
+ // the selector, so crawl up to find the ancestor that
+ // matches the selector
+
+ matched = getMatch(target, spec, el);
+
+ }
+
+
+ if (matched) {
+
+ if (!ev) {
+ ev = new Y.DOMEventFacade(e, el);
+ ev.container = ev.currentTarget;
+ }
+
+ ev.currentTarget = Y.Node.get(matched);
+
+ Y.publish(ename, {
+ contextFn: function() {
+ return ev.currentTarget;
+ }
+ });
+
+ if (fn) {
+ fn(ev, ename);
+ }
+ else {
+ Y.fire(ename, ev);
+ }
+
+ }
+
+ }
+ }
+
+ },
+
+ attach = function (type, key, element) {
+
+ var focusMethods = {
+ focus: Event._attachFocus,
+ blur: Event._attachBlur
+ },
+
+ attachFn = focusMethods[type],
+
+ args = [type,
+ function (e) {
+ delegateHandler(key, (e || window.event), element);
+ },
+ element];
+
+
+ if (attachFn) {
+ return attachFn(args, { capture: true, facade: false });
+ }
+ else {
+ return Event._attach(args, { facade: false });
+ }
+
+ },
+
+ sanitize = Y.cached(function(str) {
+ return str.replace(/[|,:]/g, '~');
+ });
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @event delegate
+ * @param type {string} 'delegate'
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param delegateType {string} the event type to delegate
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ * @deprecated use Y.delegate
+ */
+Y.Env.evt.plugins.delegate = {
+
+ on: function(type, fn, el, delegateType, spec) {
+
+ Y.log('delegate event is deprecated, use Y.delegate()', 'warn', 'deprecated');
+
+ var args = Y.Array(arguments, 0, true);
+
+ args.splice(3, 1);
+
+ args[0] = delegateType;
+
+ return Y.delegate.apply(Y, args);
+
+ }
+
+};
+
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @method delegate
+ * @param type {string} the event type to delegate
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Event.delegate = function (type, fn, el, spec) {
+
+ if (!spec) {
+ Y.log('delegate: no spec, nothing to do', 'warn', 'event');
+ return false;
+ }
+
+
+ var args = Y.Array(arguments, 0, true),
+ element = el, // HTML element serving as the delegation container
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Y.Selector.query returns an array of matches unless specified
+ // to return just the first match. Since the primary use case for
+ // event delegation is to use a single event handler on a container,
+ // Y.delegate doesn't currently support being able to bind a
+ // single listener to multiple containers.
+
+ element = Y.Selector.query(el, null, true);
+
+ if (!element) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Event.delegate.apply(Event, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ element = Y.Node.getDOMNode(element);
+
+
+ var guid = Y.stamp(element),
+
+ // The Custom Event for the delegation spec
+ ename = 'delegate:' + guid + type + sanitize(spec),
+
+ // The key to the listener for the event type and container
+ delegateKey = type + guid,
+
+ delegate = delegates[delegateKey],
+
+ domEventHandle,
+
+ ceHandle,
+
+ listeners;
+
+
+ if (!delegate) {
+
+ delegate = {};
+
+ if (specialTypes[type]) {
+
+ if (!Event._fireMouseEnter) {
+ Y.log("Delegating a " + type + " event requires the event-mouseenter submodule.", "error", "Event");
+ return false;
+ }
+
+ type = specialTypes[type];
+ delegate.fn = Event._fireMouseEnter;
+
+ }
+
+ // Create the DOM Event wrapper that will fire the Custom Event
+
+ domEventHandle = attach(type, delegateKey, element);
+
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'delegates' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known delegates
+ delete delegates[delegateKey];
+
+ Y.log("DOM event listener associated with the " + ename + " Custom Event removed. Removing all " + ename + " listeners.", "info", "Event");
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(ename);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+ delegate.handle = domEventHandle;
+
+ delegates[delegateKey] = delegate;
+
+ }
+
+
+ listeners = delegate.listeners;
+
+ delegate.listeners = listeners ? (listeners + 1) : 1;
+ delegate[spec] = ename;
+
+
+ args[0] = ename;
+
+ // Remove element, delegation spec
+ args.splice(2, 2);
+
+
+ // Subscribe to the Custom Event for the delegation spec
+
+ ceHandle = Y.on.apply(Y, args);
+
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'delegates' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ delegate.listeners = (delegate.listeners - 1);
+
+ if (delegate.listeners === 0) {
+ Y.log("No more listeners for the " + ename + " Custom Event. Removing its associated DOM event listener.", "info", "Event");
+ delegate.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+ return ceHandle;
+
+};
+
+Y.delegate = Event.delegate;
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-delegate",function(B){var I=B.Event,F=B.Lang,E={},A={mouseenter:"mouseover",mouseleave:"mouseout"},H=function(K){try{if(K&&3==K.nodeType){return K.parentNode;}}catch(J){}return K;},D=function(K,P,M){var Q=H((P.target||P.srcElement)),N=E[K],T,O,L,S,R;var J=function(X,U,V){var W;if(!X||X===V){W=false;}else{W=B.Selector.test(X,U)?X:J(X.parentNode,U,V);}return W;};for(T in N){if(N.hasOwnProperty(T)){O=N[T];S=N.fn;L=null;if(B.Selector.test(Q,T,M)){L=Q;}else{if(B.Selector.test(Q,((T.replace(/,/gi," *,"))+" *"),M)){L=J(Q,T,M);}}if(L){if(!R){R=new B.DOMEventFacade(P,M);R.container=R.currentTarget;}R.currentTarget=B.Node.get(L);B.publish(O,{contextFn:function(){return R.currentTarget;}});if(S){S(R,O);}else{B.fire(O,R);}}}}},G=function(M,L,K){var O={focus:I._attachFocus,blur:I._attachBlur},N=O[M],J=[M,function(P){D(L,(P||window.event),K);},K];if(N){return N(J,{capture:true,facade:false});}else{return I._attach(J,{facade:false});}},C=B.cached(function(J){return J.replace(/[|,:]/g,"~");});B.Env.evt.plugins.delegate={on:function(O,N,M,J,K){var L=B.Array(arguments,0,true);L.splice(3,1);L[0]=J;return B.delegate.apply(B,L);}};I.delegate=function(R,U,K,W){if(!W){return false;}var O=B.Array(arguments,0,true),M=K,N;if(F.isString(K)){M=B.Selector.query(K,null,true);if(!M){N=I.onAvailable(K,function(){N.handle=I.delegate.apply(I,O);},I,true,false);return N;}}M=B.Node.getDOMNode(M);var S=B.stamp(M),L="delegate:"+S+R+C(W),J=R+S,Q=E[J],T,V,P;if(!Q){Q={};if(A[R]){if(!I._fireMouseEnter){return false;}R=A[R];Q.fn=I._fireMouseEnter;}T=G(R,J,M);B.after(function(X){if(T.sub==X){delete E[J];B.detachAll(L);}},T.evt,"_delete");Q.handle=T;E[J]=Q;}P=Q.listeners;Q.listeners=P?(P+1):1;Q[W]=L;O[0]=L;O.splice(2,2);V=B.on.apply(B,O);B.after(function(){Q.listeners=(Q.listeners-1);if(Q.listeners===0){Q.handle.detach();}},V,"detach");return V;};B.delegate=I.delegate;},"3.0.0",{requires:["node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-delegate', function(Y) {
+
+/**
+ * Adds event delegation support to the library.
+ *
+ * @module event
+ * @submodule event-delegate
+ */
+
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ delegates = {},
+
+ specialTypes = {
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+ },
+
+ resolveTextNode = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ return n.parentNode;
+ }
+ } catch(e) { }
+ return n;
+ },
+
+ delegateHandler = function(delegateKey, e, el) {
+
+ var target = resolveTextNode((e.target || e.srcElement)),
+ tests = delegates[delegateKey],
+ spec,
+ ename,
+ matched,
+ fn,
+ ev;
+
+
+ var getMatch = function(el, selector, container) {
+
+ var returnVal;
+
+ if (!el || el === container) {
+ returnVal = false;
+ }
+ else {
+ returnVal = Y.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
+ }
+
+ return returnVal;
+
+ };
+
+
+ for (spec in tests) {
+
+ if (tests.hasOwnProperty(spec)) {
+
+ ename = tests[spec];
+ fn = tests.fn;
+ matched = null;
+
+
+ if (Y.Selector.test(target, spec, el)) {
+ matched = target;
+ }
+ else if (Y.Selector.test(target, ((spec.replace(/,/gi, " *,")) + " *"), el)) {
+
+ // The target is a descendant of an element matching
+ // the selector, so crawl up to find the ancestor that
+ // matches the selector
+
+ matched = getMatch(target, spec, el);
+
+ }
+
+
+ if (matched) {
+
+ if (!ev) {
+ ev = new Y.DOMEventFacade(e, el);
+ ev.container = ev.currentTarget;
+ }
+
+ ev.currentTarget = Y.Node.get(matched);
+
+ Y.publish(ename, {
+ contextFn: function() {
+ return ev.currentTarget;
+ }
+ });
+
+ if (fn) {
+ fn(ev, ename);
+ }
+ else {
+ Y.fire(ename, ev);
+ }
+
+ }
+
+ }
+ }
+
+ },
+
+ attach = function (type, key, element) {
+
+ var focusMethods = {
+ focus: Event._attachFocus,
+ blur: Event._attachBlur
+ },
+
+ attachFn = focusMethods[type],
+
+ args = [type,
+ function (e) {
+ delegateHandler(key, (e || window.event), element);
+ },
+ element];
+
+
+ if (attachFn) {
+ return attachFn(args, { capture: true, facade: false });
+ }
+ else {
+ return Event._attach(args, { facade: false });
+ }
+
+ },
+
+ sanitize = Y.cached(function(str) {
+ return str.replace(/[|,:]/g, '~');
+ });
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @event delegate
+ * @param type {string} 'delegate'
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param delegateType {string} the event type to delegate
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ * @deprecated use Y.delegate
+ */
+Y.Env.evt.plugins.delegate = {
+
+ on: function(type, fn, el, delegateType, spec) {
+
+
+ var args = Y.Array(arguments, 0, true);
+
+ args.splice(3, 1);
+
+ args[0] = delegateType;
+
+ return Y.delegate.apply(Y, args);
+
+ }
+
+};
+
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @method delegate
+ * @param type {string} the event type to delegate
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Event.delegate = function (type, fn, el, spec) {
+
+ if (!spec) {
+ return false;
+ }
+
+
+ var args = Y.Array(arguments, 0, true),
+ element = el, // HTML element serving as the delegation container
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Y.Selector.query returns an array of matches unless specified
+ // to return just the first match. Since the primary use case for
+ // event delegation is to use a single event handler on a container,
+ // Y.delegate doesn't currently support being able to bind a
+ // single listener to multiple containers.
+
+ element = Y.Selector.query(el, null, true);
+
+ if (!element) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Event.delegate.apply(Event, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ element = Y.Node.getDOMNode(element);
+
+
+ var guid = Y.stamp(element),
+
+ // The Custom Event for the delegation spec
+ ename = 'delegate:' + guid + type + sanitize(spec),
+
+ // The key to the listener for the event type and container
+ delegateKey = type + guid,
+
+ delegate = delegates[delegateKey],
+
+ domEventHandle,
+
+ ceHandle,
+
+ listeners;
+
+
+ if (!delegate) {
+
+ delegate = {};
+
+ if (specialTypes[type]) {
+
+ if (!Event._fireMouseEnter) {
+ return false;
+ }
+
+ type = specialTypes[type];
+ delegate.fn = Event._fireMouseEnter;
+
+ }
+
+ // Create the DOM Event wrapper that will fire the Custom Event
+
+ domEventHandle = attach(type, delegateKey, element);
+
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'delegates' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known delegates
+ delete delegates[delegateKey];
+
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(ename);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+ delegate.handle = domEventHandle;
+
+ delegates[delegateKey] = delegate;
+
+ }
+
+
+ listeners = delegate.listeners;
+
+ delegate.listeners = listeners ? (listeners + 1) : 1;
+ delegate[spec] = ename;
+
+
+ args[0] = ename;
+
+ // Remove element, delegation spec
+ args.splice(2, 2);
+
+
+ // Subscribe to the Custom Event for the delegation spec
+
+ ceHandle = Y.on.apply(Y, args);
+
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'delegates' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ delegate.listeners = (delegate.listeners - 1);
+
+ if (delegate.listeners === 0) {
+ delegate.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+ return ceHandle;
+
+};
+
+Y.delegate = Event.delegate;
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-focus', function(Y) {
+
+/**
+ * Adds focus and blur event listener support. These events normally
+ * do not bubble, so this adds support for that so these events
+ * can be used in event delegation scenarios.
+ *
+ * @module event
+ * @submodule event-focus
+ */
+(function() {
+
+var UA = Y.UA,
+ Event = Y.Event,
+ plugins = Y.Env.evt.plugins,
+ ie = UA.ie,
+ bUseMutation = (UA.opera || UA.webkit),
+ eventNames = {
+ focus: (ie ? 'focusin' : (bUseMutation ? 'DOMFocusIn' : 'focus')),
+ blur: (ie ? 'focusout' : (bUseMutation ? 'DOMFocusOut' : 'blur'))
+ },
+
+ // Only need to use capture phase for Gecko since it doesn't support
+ // focusin, focusout, DOMFocusIn, or DOMFocusOut
+ CAPTURE_CONFIG = { capture: (UA.gecko ? true : false) },
+
+
+ attach = function (args, config) {
+
+ var a = Y.Array(args, 0, true);
+ a[0] = eventNames[a[0]];
+ return Event._attach(a, config);
+
+ },
+
+ eventAdapter = {
+
+ on: function () {
+ return attach(arguments, CAPTURE_CONFIG);
+ }
+
+ };
+
+
+Event._attachFocus = attach;
+Event._attachBlur = attach;
+
+/**
+ * Adds a DOM focus listener. Uses the focusin event in IE,
+ * DOMFocusIn for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event focus
+ * @param type {string} 'focus'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.focus = eventAdapter;
+
+/**
+ * Adds a DOM blur listener. Uses the focusout event in IE,
+ * DOMFocusOut for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event blur
+ * @param type {string} 'blur'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.blur = eventAdapter;
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-focus",function(A){(function(){var I=A.UA,J=A.Event,E=A.Env.evt.plugins,C=I.ie,F=(I.opera||I.webkit),D={focus:(C?"focusin":(F?"DOMFocusIn":"focus")),blur:(C?"focusout":(F?"DOMFocusOut":"blur"))},G={capture:(I.gecko?true:false)},H=function(M,L){var K=A.Array(M,0,true);K[0]=D[K[0]];return J._attach(K,L);},B={on:function(){return H(arguments,G);}};J._attachFocus=H;J._attachBlur=H;E.focus=B;E.blur=B;})();},"3.0.0",{requires:["node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-focus', function(Y) {
+
+/**
+ * Adds focus and blur event listener support. These events normally
+ * do not bubble, so this adds support for that so these events
+ * can be used in event delegation scenarios.
+ *
+ * @module event
+ * @submodule event-focus
+ */
+(function() {
+
+var UA = Y.UA,
+ Event = Y.Event,
+ plugins = Y.Env.evt.plugins,
+ ie = UA.ie,
+ bUseMutation = (UA.opera || UA.webkit),
+ eventNames = {
+ focus: (ie ? 'focusin' : (bUseMutation ? 'DOMFocusIn' : 'focus')),
+ blur: (ie ? 'focusout' : (bUseMutation ? 'DOMFocusOut' : 'blur'))
+ },
+
+ // Only need to use capture phase for Gecko since it doesn't support
+ // focusin, focusout, DOMFocusIn, or DOMFocusOut
+ CAPTURE_CONFIG = { capture: (UA.gecko ? true : false) },
+
+
+ attach = function (args, config) {
+
+ var a = Y.Array(args, 0, true);
+ a[0] = eventNames[a[0]];
+ return Event._attach(a, config);
+
+ },
+
+ eventAdapter = {
+
+ on: function () {
+ return attach(arguments, CAPTURE_CONFIG);
+ }
+
+ };
+
+
+Event._attachFocus = attach;
+Event._attachBlur = attach;
+
+/**
+ * Adds a DOM focus listener. Uses the focusin event in IE,
+ * DOMFocusIn for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event focus
+ * @param type {string} 'focus'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.focus = eventAdapter;
+
+/**
+ * Adds a DOM blur listener. Uses the focusout event in IE,
+ * DOMFocusOut for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event blur
+ * @param type {string} 'blur'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.blur = eventAdapter;
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-key', function(Y) {
+
+/**
+ * Functionality to listen for one or more specific key combinations.
+ * @module event
+ * @submodule event-key
+ */
+
+/**
+ * Add a key listener. The listener will only be notified if the
+ * keystroke detected meets the supplied specification. The
+ * spec consists of the key event type, followed by a colon,
+ * followed by zero or more comma separated key codes, followed
+ * by zero or more modifiers delimited by a plus sign. Ex:
+ * press:12,65+shift+ctrl
+ * @event key
+ * @for YUI
+ * @param type {string} 'key'
+ * @param fn {function} the function to execute
+ * @param id {string|HTMLElement|collection} the element(s) to bind
+ * @param spec {string} the keyCode and modifier specification
+ * @param o optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {Event.Handle} the detach handle
+ */
+Y.Env.evt.plugins.key = {
+
+ on: function(type, fn, id, spec, o) {
+ var a = Y.Array(arguments, 0, true), parsed, etype, criteria, ename;
+
+ parsed = spec && spec.split(':');
+
+ if (!spec || spec.indexOf(':') == -1 || !parsed[1]) {
+Y.log('Illegal key spec, creating a regular keypress listener instead.', 'info', 'event');
+ a[0] = 'key' + ((parsed && parsed[0]) || 'press');
+ return Y.on.apply(Y, a);
+ }
+
+ // key event type: 'down', 'up', or 'press'
+ etype = parsed[0];
+
+ // list of key codes optionally followed by modifiers
+ criteria = (parsed[1]) ? parsed[1].split(/,|\+/) : null;
+
+ // the name of the custom event that will be created for the spec
+ ename = (Y.Lang.isString(id) ? id : Y.stamp(id)) + spec;
+
+ ename = ename.replace(/,/g, '_');
+
+ if (!Y.getEvent(ename)) {
+
+ // subscribe spec validator to the DOM event
+ Y.on(type + etype, function(e) {
+
+ // Y.log('keylistener: ' + e.keyCode);
+
+ var passed = false, failed = false, i, crit, critInt;
+
+ for (i=0; i<criteria.length; i=i+1) {
+ crit = criteria[i];
+ critInt = parseInt(crit, 10);
+
+ // pass this section if any supplied keyCode
+ // is found
+ if (Y.Lang.isNumber(critInt)) {
+
+ if (e.charCode === critInt) {
+ // Y.log('passed: ' + crit);
+ passed = true;
+ } else {
+ failed = true;
+ // Y.log('failed: ' + crit);
+ }
+
+ // only check modifier if no keyCode was specified
+ // or the keyCode check was successful. pass only
+ // if every modifier passes
+ } else if (passed || !failed) {
+ passed = (e[crit + 'Key']);
+ failed = !passed;
+ // Y.log(crit + ": " + passed);
+ }
+ }
+
+ // fire spec custom event if spec if met
+ if (passed) {
+ Y.fire(ename, e);
+ }
+
+ }, id);
+
+ }
+
+ // subscribe supplied listener to custom event for spec validator
+ // remove element and spec.
+ a.splice(2, 2);
+ a[0] = ename;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-key",function(A){A.Env.evt.plugins.key={on:function(E,G,B,K,C){var I=A.Array(arguments,0,true),F,J,H,D;F=K&&K.split(":");if(!K||K.indexOf(":")==-1||!F[1]){I[0]="key"+((F&&F[0])||"press");return A.on.apply(A,I);}J=F[0];H=(F[1])?F[1].split(/,|\+/):null;D=(A.Lang.isString(B)?B:A.stamp(B))+K;D=D.replace(/,/g,"_");if(!A.getEvent(D)){A.on(E+J,function(P){var Q=false,M=false,N,L,O;for(N=0;N<H.length;N=N+1){L=H[N];O=parseInt(L,10);if(A.Lang.isNumber(O)){if(P.charCode===O){Q=true;}else{M=true;}}else{if(Q||!M){Q=(P[L+"Key"]);M=!Q;}}}if(Q){A.fire(D,P);}},B);}I.splice(2,2);I[0]=D;return A.on.apply(A,I);}};},"3.0.0",{requires:["node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-key', function(Y) {
+
+/**
+ * Functionality to listen for one or more specific key combinations.
+ * @module event
+ * @submodule event-key
+ */
+
+/**
+ * Add a key listener. The listener will only be notified if the
+ * keystroke detected meets the supplied specification. The
+ * spec consists of the key event type, followed by a colon,
+ * followed by zero or more comma separated key codes, followed
+ * by zero or more modifiers delimited by a plus sign. Ex:
+ * press:12,65+shift+ctrl
+ * @event key
+ * @for YUI
+ * @param type {string} 'key'
+ * @param fn {function} the function to execute
+ * @param id {string|HTMLElement|collection} the element(s) to bind
+ * @param spec {string} the keyCode and modifier specification
+ * @param o optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {Event.Handle} the detach handle
+ */
+Y.Env.evt.plugins.key = {
+
+ on: function(type, fn, id, spec, o) {
+ var a = Y.Array(arguments, 0, true), parsed, etype, criteria, ename;
+
+ parsed = spec && spec.split(':');
+
+ if (!spec || spec.indexOf(':') == -1 || !parsed[1]) {
+ a[0] = 'key' + ((parsed && parsed[0]) || 'press');
+ return Y.on.apply(Y, a);
+ }
+
+ // key event type: 'down', 'up', or 'press'
+ etype = parsed[0];
+
+ // list of key codes optionally followed by modifiers
+ criteria = (parsed[1]) ? parsed[1].split(/,|\+/) : null;
+
+ // the name of the custom event that will be created for the spec
+ ename = (Y.Lang.isString(id) ? id : Y.stamp(id)) + spec;
+
+ ename = ename.replace(/,/g, '_');
+
+ if (!Y.getEvent(ename)) {
+
+ // subscribe spec validator to the DOM event
+ Y.on(type + etype, function(e) {
+
+
+ var passed = false, failed = false, i, crit, critInt;
+
+ for (i=0; i<criteria.length; i=i+1) {
+ crit = criteria[i];
+ critInt = parseInt(crit, 10);
+
+ // pass this section if any supplied keyCode
+ // is found
+ if (Y.Lang.isNumber(critInt)) {
+
+ if (e.charCode === critInt) {
+ passed = true;
+ } else {
+ failed = true;
+ }
+
+ // only check modifier if no keyCode was specified
+ // or the keyCode check was successful. pass only
+ // if every modifier passes
+ } else if (passed || !failed) {
+ passed = (e[crit + 'Key']);
+ failed = !passed;
+ }
+ }
+
+ // fire spec custom event if spec if met
+ if (passed) {
+ Y.fire(ename, e);
+ }
+
+ }, id);
+
+ }
+
+ // subscribe supplied listener to custom event for spec validator
+ // remove element and spec.
+ a.splice(2, 2);
+ a[0] = ename;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+(function(){var GLOBAL_ENV=YUI.Env,C=YUI.config,D=C.doc,POLL_INTERVAL=C.pollInterval||40,_ready=function(e){GLOBAL_ENV._ready();};if(!GLOBAL_ENV._ready){GLOBAL_ENV._ready=function(){if(!GLOBAL_ENV.DOMReady){GLOBAL_ENV.DOMReady=true;if(D.removeEventListener){D.removeEventListener("DOMContentLoaded",_ready,false);}}};
+/* DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
+if(navigator.userAgent.match(/MSIE/)){if(self!==self.top){document.onreadystatechange=function(){if(document.readyState=="complete"){document.onreadystatechange=null;_ready();}};}else{GLOBAL_ENV._dri=setInterval(function(){try{document.documentElement.doScroll("left");clearInterval(GLOBAL_ENV._dri);GLOBAL_ENV._dri=null;_ready();}catch(ex){}},POLL_INTERVAL);}}else{D.addEventListener("DOMContentLoaded",_ready,false);}}})();YUI.add("event-base",function(A){(function(){var C=YUI.Env,B=function(){A.fire("domready");};A.publish("domready",{fireOnce:true});if(C.DOMReady){B();}else{A.before(B,C,"_ready");}})();(function(){var C=A.UA,B={63232:38,63233:40,63234:37,63235:39,63276:33,63277:34,25:9,63272:46,63273:36,63275:35},D=function(F){try{if(F&&3==F.nodeType){F=F.parentNode;}}catch(E){return null;}return A.one(F);};A.DOMEventFacade=function(L,F,E){E=E||{};var H=L,G=F,I=A.config.doc,M=I.body,N=H.pageX,K=H.pageY,J,O;this.altKey=H.altKey;this.ctrlKey=H.ctrlKey;this.metaKey=H.metaKey;this.shiftKey=H.shiftKey;this.type=H.type;this.clientX=H.clientX;this.clientY=H.clientY;if(!N&&0!==N){N=H.clientX||0;K=H.clientY||0;if(C.ie){N+=Math.max(I.documentElement.scrollLeft,M.scrollLeft);K+=Math.max(I.documentElement.scrollTop,M.scrollTop);}}this._yuifacade=true;this._event=H;this.pageX=N;this.pageY=K;J=H.keyCode||H.charCode||0;if(C.webkit&&(J in B)){J=B[J];}this.keyCode=J;this.charCode=J;this.button=H.which||H.button;this.which=this.button;this.target=D(H.target||H.srcElement);this.currentTarget=D(G);O=H.relatedTarget;if(!O){if(H.type=="mouseout"){O=H.toElement;}else{if(H.type=="mouseover"){O=H.fromElement;}}}this.relatedTarget=D(O);if(H.type=="mousewheel"||H.type=="DOMMouseScroll"){this.wheelDelta=(H.detail)?(H.detail*-1):Math.round(H.wheelDelta/80)||((H.wheelDelta<0)?-1:1);}this.stopPropagation=function(){if(H.stopPropagation){H.stopPropagation();}else{H.cancelBubble=true;}E.stopped=1;};this.stopImmediatePropagation=function(){if(H.stopImmediatePropagation){H.stopImmediatePropagation();}else{this.stopPropagation();}E.stopped=2;};this.preventDefault=function(P){if(H.preventDefault){H.preventDefault();}H.returnValue=P||false;E.prevented=1;};this.halt=function(P){if(P){this.stopImmediatePropagation();}else{this.stopPropagation();}this.preventDefault();};};})();(function(){A.Env.evt.dom_wrappers={};A.Env.evt.dom_map={};var H=A.Env.evt,J=YUI.Env.add,D=YUI.Env.remove,G=function(){YUI.Env.windowLoaded=true;A.Event._load();D(window,"load",G);},B=function(){A.Event._unload();D(window,"unload",B);},C="domready",E="~yui|2|compat~",F=function(L){try{return(L&&typeof L!=="string"&&A.Lang.isNumber(L.length)&&!L.tagName&&!L.alert);}catch(K){return false;}},I=function(){var M=false,N=0,L=[],O=H.dom_wrappers,K=null,P=H.dom_map;return{POLL_RETRYS:1000,POLL_INTERVAL:40,lastError:null,_interval:null,_dri:null,DOMReady:false,startInterval:function(){var Q=A.Event;if(!Q._interval){Q._interval=setInterval(A.bind(Q._poll,Q),Q.POLL_INTERVAL);}},onAvailable:function(Q,U,Y,R,V,X){var W=A.Array(Q),S,T;for(S=0;S<W.length;S=S+1){L.push({id:W[S],fn:U,obj:Y,override:R,checkReady:V,compat:X});}N=this.POLL_RETRYS;setTimeout(A.bind(A.Event._poll,A.Event),0);T=new A.EventHandle({_delete:function(){if(T.handle){T.handle.detach();return;}var a,Z;for(a=0;a<W.length;a++){for(Z=0;Z<L.length;Z++){if(W[a]===L[Z].id){L.splice(Z,1);}}}}});return T;},onContentReady:function(U,R,T,S,Q){return this.onAvailable(U,R,T,S,true,Q);},attach:function(T,S,R,Q){return A.Event._attach(A.Array(arguments,0,true));},_createWrapper:function(W,V,Q,R,U){var X=A.stamp(W),T="event:"+X+V,S;if(false===U){T+="native";}if(Q){T+="capture";}S=O[T];if(!S){S=A.publish(T,{silent:true,bubbles:false,contextFn:function(){S.nodeRef=S.nodeRef||A.one(S.el);return S.nodeRef;}});S.el=W;S.key=T;S.domkey=X;S.type=V;S.fn=function(Y){S.fire(A.Event.getEvent(Y,W,(R||(false===U))));};S.capture=Q;if(W==A.config.win&&V=="load"){S.fireOnce=true;K=T;}O[T]=S;P[X]=P[X]||{};P[X][T]=S;J(W,V,S.fn,Q);}return S;},_attach:function(W,S){var a,e=A.Event,c,U,Z,Q,T=false,V,X=W[0],Y=W[1],R=W[2]||A.config.win,d=S&&S.facade,b=S&&S.capture;if(W[W.length-1]===E){a=true;}if(!Y||!Y.call){return false;}if(F(R)){c=[];A.each(R,function(g,f){W[2]=g;c.push(e._attach(W,S));});return new A.EventHandle(c);}else{if(A.Lang.isString(R)){if(a){U=A.DOM.byId(R);}else{U=A.Selector.query(R);switch(U.length){case 0:U=null;break;case 1:U=U[0];break;default:W[2]=U;return e._attach(W,S);}}if(U){R=U;}else{V=this.onAvailable(R,function(){V.handle=e._attach(W,S);},e,true,false,a);return V;}}}if(!R){return false;}if(A.Node&&R instanceof A.Node){R=A.Node.getDOMNode(R);}Z=this._createWrapper(R,X,b,a,d);if(R==A.config.win&&X=="load"){if(YUI.Env.windowLoaded){T=true;}}if(a){W.pop();}Q=W[3];V=Z._on(Y,Q,(W.length>4)?W.slice(4):null);if(T){Z.fire();}return V;},detach:function(X,Z,S,U){var W=A.Array(arguments,0,true),a,V,T,Y,Q,R;if(W[W.length-1]===E){a=true;}if(X&&X.detach){return X.detach();}if(typeof S=="string"){if(a){S=A.DOM.byId(S);}else{S=A.Selector.query(S);T=S.length;if(T<1){S=null;}else{if(T==1){S=S[0];}}}}if(!S){return false;}if(F(S)){Y=true;for(V=0,T=S.length;V<T;++V){W[2]=S[V];Y=(A.Event.detach.apply(A.Event,W)&&Y);}return Y;}if(!X||!Z||!Z.call){return this.purgeElement(S,false,X);}Q="event:"+A.stamp(S)+X;R=O[Q];if(R){return R.detach(Z);}else{return false;}},getEvent:function(T,R,Q){var S=T||window.event;return(Q)?S:new A.DOMEventFacade(S,R,O["event:"+A.stamp(R)+T.type]);},generateId:function(Q){var R=Q.id;if(!R){R=A.stamp(Q);Q.id=R;}return R;},_isValidCollection:F,_load:function(Q){if(!M){M=true;if(A.fire){A.fire(C);}A.Event._poll();}},_poll:function(){if(this.locked){return;
+}if(A.UA.ie&&!YUI.Env.DOMReady){this.startInterval();return;}this.locked=true;var V=!M,U,W,R,Q,T,S;if(!V){V=(N>0);}U=[];W=function(Z,a){var Y,X=a.override;if(a.compat){if(a.override){if(X===true){Y=a.obj;}else{Y=X;}}else{Y=Z;}a.fn.call(Y,a.obj);}else{Y=a.obj||A.one(Z);a.fn.apply(Y,(A.Lang.isArray(X))?X:[]);}};for(R=0,Q=L.length;R<Q;++R){T=L[R];if(T&&!T.checkReady){S=(T.compat)?A.DOM.byId(T.id):A.Selector.query(T.id,null,true);if(S){W(S,T);L[R]=null;}else{U.push(T);}}}for(R=0,Q=L.length;R<Q;++R){T=L[R];if(T&&T.checkReady){S=(T.compat)?A.DOM.byId(T.id):A.Selector.query(T.id,null,true);if(S){if(M||(S.get&&S.get("nextSibling"))||S.nextSibling){W(S,T);L[R]=null;}}else{U.push(T);}}}N=(U.length===0)?0:N-1;if(V){this.startInterval();}else{clearInterval(this._interval);this._interval=null;}this.locked=false;return;},purgeElement:function(W,X,V){var S=(A.Lang.isString(W))?A.Selector.query(W,null,true):W,R=this.getListeners(S,V),T,Q,U;if(R){for(T=0,Q=R.length;T<Q;++T){U=R[T];U.detachAll();D(U.el,U.type,U.fn,U.capture);delete O[U.key];delete P[U.domkey][U.key];}}if(X&&S&&S.childNodes){for(T=0,Q=S.childNodes.length;T<Q;++T){this.purgeElement(S.childNodes[T],X,V);}}},getListeners:function(U,T){var V=A.stamp(U,true),Q=P[V],S=[],R=(T)?"event:"+V+T:null;if(!Q){return null;}if(R){if(Q[R]){S.push(Q[R]);}R+="native";if(Q[R]){S.push(Q[R]);}}else{A.each(Q,function(X,W){S.push(X);});}return(S.length)?S:null;},_unload:function(Q){A.each(O,function(S,R){S.detachAll();D(S.el,S.type,S.fn,S.capture);delete O[R];delete P[S.domkey][R];});},nativeAdd:J,nativeRemove:D};}();A.Event=I;if(A.config.injected||YUI.Env.windowLoaded){G();}else{J(window,"load",G);}if(A.UA.ie){A.on(C,I._poll,I,true);}A.on("unload",B);I.Custom=A.CustomEvent;I.Subscriber=A.Subscriber;I.Target=A.EventTarget;I.Handle=A.EventHandle;I.Facade=A.EventFacade;I._poll();})();A.Env.evt.plugins.available={on:function(D,C,F,E){var B=arguments.length>4?A.Array(arguments,4,true):[];return A.Event.onAvailable.call(A.Event,F,C,E,B);}};A.Env.evt.plugins.contentready={on:function(D,C,F,E){var B=arguments.length>4?A.Array(arguments,4,true):[];return A.Event.onContentReady.call(A.Event,F,C,E,B);}};},"3.0.0",{requires:["event-custom-base"]});YUI.add("event-delegate",function(B){var I=B.Event,F=B.Lang,E={},A={mouseenter:"mouseover",mouseleave:"mouseout"},H=function(K){try{if(K&&3==K.nodeType){return K.parentNode;}}catch(J){}return K;},D=function(K,P,M){var Q=H((P.target||P.srcElement)),N=E[K],T,O,L,S,R;var J=function(X,U,V){var W;if(!X||X===V){W=false;}else{W=B.Selector.test(X,U)?X:J(X.parentNode,U,V);}return W;};for(T in N){if(N.hasOwnProperty(T)){O=N[T];S=N.fn;L=null;if(B.Selector.test(Q,T,M)){L=Q;}else{if(B.Selector.test(Q,((T.replace(/,/gi," *,"))+" *"),M)){L=J(Q,T,M);}}if(L){if(!R){R=new B.DOMEventFacade(P,M);R.container=R.currentTarget;}R.currentTarget=B.Node.get(L);B.publish(O,{contextFn:function(){return R.currentTarget;}});if(S){S(R,O);}else{B.fire(O,R);}}}}},G=function(M,L,K){var O={focus:I._attachFocus,blur:I._attachBlur},N=O[M],J=[M,function(P){D(L,(P||window.event),K);},K];if(N){return N(J,{capture:true,facade:false});}else{return I._attach(J,{facade:false});}},C=B.cached(function(J){return J.replace(/[|,:]/g,"~");});B.Env.evt.plugins.delegate={on:function(O,N,M,J,K){var L=B.Array(arguments,0,true);L.splice(3,1);L[0]=J;return B.delegate.apply(B,L);}};I.delegate=function(R,U,K,W){if(!W){return false;}var O=B.Array(arguments,0,true),M=K,N;if(F.isString(K)){M=B.Selector.query(K,null,true);if(!M){N=I.onAvailable(K,function(){N.handle=I.delegate.apply(I,O);},I,true,false);return N;}}M=B.Node.getDOMNode(M);var S=B.stamp(M),L="delegate:"+S+R+C(W),J=R+S,Q=E[J],T,V,P;if(!Q){Q={};if(A[R]){if(!I._fireMouseEnter){return false;}R=A[R];Q.fn=I._fireMouseEnter;}T=G(R,J,M);B.after(function(X){if(T.sub==X){delete E[J];B.detachAll(L);}},T.evt,"_delete");Q.handle=T;E[J]=Q;}P=Q.listeners;Q.listeners=P?(P+1):1;Q[W]=L;O[0]=L;O.splice(2,2);V=B.on.apply(B,O);B.after(function(){Q.listeners=(Q.listeners-1);if(Q.listeners===0){Q.handle.detach();}},V,"detach");return V;};B.delegate=I.delegate;},"3.0.0",{requires:["node-base"]});YUI.add("event-mousewheel",function(C){var B="DOMMouseScroll",A=function(E){var D=C.Array(E,0,true),F;if(C.UA.gecko){D[0]=B;F=C.config.win;}else{F=C.config.doc;}if(D.length<3){D[2]=F;}else{D.splice(2,0,F);}return D;};C.Env.evt.plugins.mousewheel={on:function(){return C.Event._attach(A(arguments));},detach:function(){return C.Event.detach.apply(C.Event,A(arguments));}};},"3.0.0",{requires:["node-base"]});YUI.add("event-mouseenter",function(F){var C=F.Event,E=F.Lang,B=F.Env.evt.plugins,D={},A={on:function(M,O,H){var L=F.Array(arguments,0,true),J=H,K;if(E.isString(H)){J=F.all(H);if(J.size()===0){K=C.onAvailable(H,function(){K.handle=F.on.apply(F,L);},C,true,false);return K;}}var R=(M==="mouseenter")?"mouseover":"mouseout",Q=M+":"+F.stamp(J)+R,I=D[Q],N,P,G;if(!I){N=F.on(R,F.rbind(C._fireMouseEnter,F,Q),J);F.after(function(S){if(N.sub==S){delete D[Q];F.detachAll(Q);}},N.evt,"_delete");I={};I.handle=N;D[Q]=I;}G=I.count;I.count=G?(G+1):1;L[0]=Q;L.splice(2,1);P=F.on.apply(F,L);F.after(function(){I.count=(I.count-1);if(I.count===0){I.handle.detach();}},P,"detach");return P;}};C._fireMouseEnter=function(J,H){var G=J.relatedTarget,I=J.currentTarget;if(I!==G&&!I.contains(G)){F.publish(H,{contextFn:function(){return I;}});F.fire(H,J);}};B.mouseenter=A;B.mouseleave=A;},"3.0.0",{requires:["node-base"]});YUI.add("event-key",function(A){A.Env.evt.plugins.key={on:function(E,G,B,K,C){var I=A.Array(arguments,0,true),F,J,H,D;F=K&&K.split(":");if(!K||K.indexOf(":")==-1||!F[1]){I[0]="key"+((F&&F[0])||"press");return A.on.apply(A,I);}J=F[0];H=(F[1])?F[1].split(/,|\+/):null;D=(A.Lang.isString(B)?B:A.stamp(B))+K;D=D.replace(/,/g,"_");if(!A.getEvent(D)){A.on(E+J,function(P){var Q=false,M=false,N,L,O;for(N=0;N<H.length;N=N+1){L=H[N];O=parseInt(L,10);if(A.Lang.isNumber(O)){if(P.charCode===O){Q=true;}else{M=true;}}else{if(Q||!M){Q=(P[L+"Key"]);M=!Q;}}}if(Q){A.fire(D,P);}},B);
+}I.splice(2,2);I[0]=D;return A.on.apply(A,I);}};},"3.0.0",{requires:["node-base"]});YUI.add("event-focus",function(A){(function(){var I=A.UA,J=A.Event,E=A.Env.evt.plugins,C=I.ie,F=(I.opera||I.webkit),D={focus:(C?"focusin":(F?"DOMFocusIn":"focus")),blur:(C?"focusout":(F?"DOMFocusOut":"blur"))},G={capture:(I.gecko?true:false)},H=function(M,L){var K=A.Array(M,0,true);K[0]=D[K[0]];return J._attach(K,L);},B={on:function(){return H(arguments,G);}};J._attachFocus=H;J._attachBlur=H;E.focus=B;E.blur=B;})();},"3.0.0",{requires:["node-base"]});YUI.add("event-resize",function(A){(function(){var C,B,E="window:resize",D=function(F){if(A.UA.gecko){A.fire(E,F);}else{if(B){B.cancel();}B=A.later(A.config.windowResizeDelay||40,A,function(){A.fire(E,F);});}};A.Env.evt.plugins.windowresize={on:function(H,G){if(!C){C=A.Event._attach(["resize",D]);}var F=A.Array(arguments,0,true);F[0]=E;return A.on.apply(A,F);}};})();},"3.0.0",{requires:["node-base"]});YUI.add("event",function(A){},"3.0.0",{use:["event-base","event-delegate","event-mousewheel","event-mouseenter","event-key","event-focus","event-resize"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-mouseenter', function(Y) {
+
+/**
+ * Adds support for mouseenter/mouseleave events
+ * @module event
+ * @submodule event-mouseenter
+ */
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ plugins = Y.Env.evt.plugins,
+
+ listeners = {},
+
+ eventConfig = {
+
+ on: function(type, fn, el) {
+
+ var args = Y.Array(arguments, 0, true),
+ element = el,
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Need to use Y.all because if el is a string it could be a
+ // selector that returns a NodeList
+
+ element = Y.all(el);
+
+ if (element.size() === 0) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Y.on.apply(Y, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ var sDOMEvent = (type === "mouseenter") ? "mouseover" : "mouseout",
+
+ // The name of the custom event
+ sEventName = type + ":" + Y.stamp(element) + sDOMEvent,
+
+ listener = listeners[sEventName],
+
+ domEventHandle,
+
+ ceHandle,
+
+ nListeners;
+
+
+ // Bind an actual DOM event listener that will call the
+ // the custom event
+ if (!listener) {
+
+ domEventHandle = Y.on(sDOMEvent, Y.rbind(Event._fireMouseEnter, Y, sEventName), element);
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'listeners' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known mouseenter
+ // and mouseleave listeners
+ delete listeners[sEventName];
+
+ Y.log("DOM event listener associated with the " + sEventName + " Custom Event removed. Removing all " + sEventName + " listeners.", "info", "Event");
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(sEventName);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+
+ listener = {};
+ listener.handle = domEventHandle;
+
+ listeners[sEventName] = listener;
+
+ }
+
+ nListeners = listener.count;
+
+ listener.count = nListeners ? (nListeners + 1) : 1;
+
+ args[0] = sEventName;
+
+ // Remove the element from the args
+ args.splice(2, 1);
+
+ // Subscribe to the custom event
+ ceHandle = Y.on.apply(Y, args);
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'listeners' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ listener.count = (listener.count - 1);
+
+ if (listener.count === 0) {
+ Y.log("No more listeners for the " + sEventName + " Custom Event. Removing its associated DOM event listener.", "info", "Event");
+ listener.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+
+ return ceHandle;
+
+ }
+
+ };
+
+
+Event._fireMouseEnter = function (e, eventName) {
+
+ var relatedTarget = e.relatedTarget,
+ currentTarget = e.currentTarget;
+
+ if (currentTarget !== relatedTarget &&
+ !currentTarget.contains(relatedTarget)) {
+
+ Y.publish(eventName, {
+ contextFn: function() {
+ return currentTarget;
+ }
+ });
+
+ Y.fire(eventName, e);
+
+ }
+
+};
+
+
+/**
+ * Sets up a "mouseenter" listener—a listener that is called the first time
+ * the user's mouse enters the specified element(s).
+ *
+ * @event mouseenter
+ * @param type {string} "mouseenter"
+ * @param fn {function} The method the event invokes.
+ * @param el {string|node} The element(s) to assign the listener to.
+ * @param spec {string} Optional. String representing a selector that must
+ * match the target of the event in order for the listener to be called.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+plugins.mouseenter = eventConfig;
+
+/**
+* Sets up a "mouseleave" listener—a listener that is called the first time
+* the user's mouse leaves the specified element(s).
+*
+* @event mouseleave
+* @param type {string} "mouseleave"
+* @param fn {function} The method the event invokes.
+* @param el {string|node} The element(s) to assign the listener to.
+* @param spec {string} Optional. String representing a selector that must
+* match the target of the event in order for the listener to be called.
+* @return {EventHandle} the detach handle
+* @for YUI
+ */
+plugins.mouseleave = eventConfig;
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-mouseenter",function(F){var C=F.Event,E=F.Lang,B=F.Env.evt.plugins,D={},A={on:function(M,O,H){var L=F.Array(arguments,0,true),J=H,K;if(E.isString(H)){J=F.all(H);if(J.size()===0){K=C.onAvailable(H,function(){K.handle=F.on.apply(F,L);},C,true,false);return K;}}var R=(M==="mouseenter")?"mouseover":"mouseout",Q=M+":"+F.stamp(J)+R,I=D[Q],N,P,G;if(!I){N=F.on(R,F.rbind(C._fireMouseEnter,F,Q),J);F.after(function(S){if(N.sub==S){delete D[Q];F.detachAll(Q);}},N.evt,"_delete");I={};I.handle=N;D[Q]=I;}G=I.count;I.count=G?(G+1):1;L[0]=Q;L.splice(2,1);P=F.on.apply(F,L);F.after(function(){I.count=(I.count-1);if(I.count===0){I.handle.detach();}},P,"detach");return P;}};C._fireMouseEnter=function(J,H){var G=J.relatedTarget,I=J.currentTarget;if(I!==G&&!I.contains(G)){F.publish(H,{contextFn:function(){return I;}});F.fire(H,J);}};B.mouseenter=A;B.mouseleave=A;},"3.0.0",{requires:["node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-mouseenter', function(Y) {
+
+/**
+ * Adds support for mouseenter/mouseleave events
+ * @module event
+ * @submodule event-mouseenter
+ */
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ plugins = Y.Env.evt.plugins,
+
+ listeners = {},
+
+ eventConfig = {
+
+ on: function(type, fn, el) {
+
+ var args = Y.Array(arguments, 0, true),
+ element = el,
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Need to use Y.all because if el is a string it could be a
+ // selector that returns a NodeList
+
+ element = Y.all(el);
+
+ if (element.size() === 0) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Y.on.apply(Y, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ var sDOMEvent = (type === "mouseenter") ? "mouseover" : "mouseout",
+
+ // The name of the custom event
+ sEventName = type + ":" + Y.stamp(element) + sDOMEvent,
+
+ listener = listeners[sEventName],
+
+ domEventHandle,
+
+ ceHandle,
+
+ nListeners;
+
+
+ // Bind an actual DOM event listener that will call the
+ // the custom event
+ if (!listener) {
+
+ domEventHandle = Y.on(sDOMEvent, Y.rbind(Event._fireMouseEnter, Y, sEventName), element);
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'listeners' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known mouseenter
+ // and mouseleave listeners
+ delete listeners[sEventName];
+
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(sEventName);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+
+ listener = {};
+ listener.handle = domEventHandle;
+
+ listeners[sEventName] = listener;
+
+ }
+
+ nListeners = listener.count;
+
+ listener.count = nListeners ? (nListeners + 1) : 1;
+
+ args[0] = sEventName;
+
+ // Remove the element from the args
+ args.splice(2, 1);
+
+ // Subscribe to the custom event
+ ceHandle = Y.on.apply(Y, args);
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'listeners' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ listener.count = (listener.count - 1);
+
+ if (listener.count === 0) {
+ listener.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+
+ return ceHandle;
+
+ }
+
+ };
+
+
+Event._fireMouseEnter = function (e, eventName) {
+
+ var relatedTarget = e.relatedTarget,
+ currentTarget = e.currentTarget;
+
+ if (currentTarget !== relatedTarget &&
+ !currentTarget.contains(relatedTarget)) {
+
+ Y.publish(eventName, {
+ contextFn: function() {
+ return currentTarget;
+ }
+ });
+
+ Y.fire(eventName, e);
+
+ }
+
+};
+
+
+/**
+ * Sets up a "mouseenter" listener—a listener that is called the first time
+ * the user's mouse enters the specified element(s).
+ *
+ * @event mouseenter
+ * @param type {string} "mouseenter"
+ * @param fn {function} The method the event invokes.
+ * @param el {string|node} The element(s) to assign the listener to.
+ * @param spec {string} Optional. String representing a selector that must
+ * match the target of the event in order for the listener to be called.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+plugins.mouseenter = eventConfig;
+
+/**
+* Sets up a "mouseleave" listener—a listener that is called the first time
+* the user's mouse leaves the specified element(s).
+*
+* @event mouseleave
+* @param type {string} "mouseleave"
+* @param fn {function} The method the event invokes.
+* @param el {string|node} The element(s) to assign the listener to.
+* @param spec {string} Optional. String representing a selector that must
+* match the target of the event in order for the listener to be called.
+* @return {EventHandle} the detach handle
+* @for YUI
+ */
+plugins.mouseleave = eventConfig;
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-mousewheel', function(Y) {
+
+/**
+ * Adds mousewheel event support
+ * @module event
+ * @submodule event-mousewheel
+ */
+var DOM_MOUSE_SCROLL = 'DOMMouseScroll',
+ fixArgs = function(args) {
+ var a = Y.Array(args, 0, true), target;
+ if (Y.UA.gecko) {
+ a[0] = DOM_MOUSE_SCROLL;
+ target = Y.config.win;
+ } else {
+ target = Y.config.doc;
+ }
+
+ if (a.length < 3) {
+ a[2] = target;
+ } else {
+ a.splice(2, 0, target);
+ }
+
+ return a;
+ };
+
+/**
+ * Mousewheel event. This listener is automatically attached to the
+ * correct target, so one should not be supplied. Mouse wheel
+ * direction and velocity is stored in the 'mouseDelta' field.
+ * @event mousewheel
+ * @param type {string} 'mousewheel'
+ * @param fn {function} the callback to execute
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.mousewheel = {
+ on: function() {
+ return Y.Event._attach(fixArgs(arguments));
+ },
+
+ detach: function() {
+ return Y.Event.detach.apply(Y.Event, fixArgs(arguments));
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-mousewheel",function(C){var B="DOMMouseScroll",A=function(E){var D=C.Array(E,0,true),F;if(C.UA.gecko){D[0]=B;F=C.config.win;}else{F=C.config.doc;}if(D.length<3){D[2]=F;}else{D.splice(2,0,F);}return D;};C.Env.evt.plugins.mousewheel={on:function(){return C.Event._attach(A(arguments));},detach:function(){return C.Event.detach.apply(C.Event,A(arguments));}};},"3.0.0",{requires:["node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-mousewheel', function(Y) {
+
+/**
+ * Adds mousewheel event support
+ * @module event
+ * @submodule event-mousewheel
+ */
+var DOM_MOUSE_SCROLL = 'DOMMouseScroll',
+ fixArgs = function(args) {
+ var a = Y.Array(args, 0, true), target;
+ if (Y.UA.gecko) {
+ a[0] = DOM_MOUSE_SCROLL;
+ target = Y.config.win;
+ } else {
+ target = Y.config.doc;
+ }
+
+ if (a.length < 3) {
+ a[2] = target;
+ } else {
+ a.splice(2, 0, target);
+ }
+
+ return a;
+ };
+
+/**
+ * Mousewheel event. This listener is automatically attached to the
+ * correct target, so one should not be supplied. Mouse wheel
+ * direction and velocity is stored in the 'mouseDelta' field.
+ * @event mousewheel
+ * @param type {string} 'mousewheel'
+ * @param fn {function} the callback to execute
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.mousewheel = {
+ on: function() {
+ return Y.Event._attach(fixArgs(arguments));
+ },
+
+ detach: function() {
+ return Y.Event.detach.apply(Y.Event, fixArgs(arguments));
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-resize', function(Y) {
+
+/**
+ * Adds a window resize event that has its behavior normalized to fire at the
+ * end of the resize rather than constantly during the resize.
+ * @module event
+ * @submodule event-resize
+ */
+(function() {
+
+var detachHandle,
+
+ timerHandle,
+
+ CE_NAME = 'window:resize',
+
+ handler = function(e) {
+
+ if (Y.UA.gecko) {
+
+ Y.fire(CE_NAME, e);
+
+ } else {
+
+ if (timerHandle) {
+ timerHandle.cancel();
+ }
+
+ timerHandle = Y.later(Y.config.windowResizeDelay || 40, Y, function() {
+ Y.fire(CE_NAME, e);
+ });
+ }
+
+ };
+
+
+/**
+ * Firefox fires the window resize event once when the resize action
+ * finishes, other browsers fire the event periodically during the
+ * resize. This code uses timeout logic to simulate the Firefox
+ * behavior in other browsers.
+ * @event windowresize
+ * @for YUI
+ */
+Y.Env.evt.plugins.windowresize = {
+
+ on: function(type, fn) {
+
+ // check for single window listener and add if needed
+ if (!detachHandle) {
+ detachHandle = Y.Event._attach(['resize', handler]);
+ }
+
+ var a = Y.Array(arguments, 0, true);
+ a[0] = CE_NAME;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("event-resize",function(A){(function(){var C,B,E="window:resize",D=function(F){if(A.UA.gecko){A.fire(E,F);}else{if(B){B.cancel();}B=A.later(A.config.windowResizeDelay||40,A,function(){A.fire(E,F);});}};A.Env.evt.plugins.windowresize={on:function(H,G){if(!C){C=A.Event._attach(["resize",D]);}var F=A.Array(arguments,0,true);F[0]=E;return A.on.apply(A,F);}};})();},"3.0.0",{requires:["node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('event-resize', function(Y) {
+
+/**
+ * Adds a window resize event that has its behavior normalized to fire at the
+ * end of the resize rather than constantly during the resize.
+ * @module event
+ * @submodule event-resize
+ */
+(function() {
+
+var detachHandle,
+
+ timerHandle,
+
+ CE_NAME = 'window:resize',
+
+ handler = function(e) {
+
+ if (Y.UA.gecko) {
+
+ Y.fire(CE_NAME, e);
+
+ } else {
+
+ if (timerHandle) {
+ timerHandle.cancel();
+ }
+
+ timerHandle = Y.later(Y.config.windowResizeDelay || 40, Y, function() {
+ Y.fire(CE_NAME, e);
+ });
+ }
+
+ };
+
+
+/**
+ * Firefox fires the window resize event once when the resize action
+ * finishes, other browsers fire the event periodically during the
+ * resize. This code uses timeout logic to simulate the Firefox
+ * behavior in other browsers.
+ * @event windowresize
+ * @for YUI
+ */
+Y.Env.evt.plugins.windowresize = {
+
+ on: function(type, fn) {
+
+ // check for single window listener and add if needed
+ if (!detachHandle) {
+ detachHandle = Y.Event._attach(['resize', handler]);
+ }
+
+ var a = Y.Array(arguments, 0, true);
+ a[0] = CE_NAME;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+(function() {
+
+
+// Unlike most of the library, this code has to be executed as soon as it is
+// introduced into the page -- and it should only be executed one time
+// regardless of the number of instances that use it.
+
+var GLOBAL_ENV = YUI.Env,
+
+ C = YUI.config,
+
+ D = C.doc,
+
+ POLL_INTERVAL = C.pollInterval || 40,
+
+ _ready = function(e) {
+ GLOBAL_ENV._ready();
+ };
+
+ if (!GLOBAL_ENV._ready) {
+
+ GLOBAL_ENV._ready = function() {
+ if (!GLOBAL_ENV.DOMReady) {
+ GLOBAL_ENV.DOMReady=true;
+
+ // Remove the DOMContentLoaded (FF/Opera/Safari)
+ if (D.removeEventListener) {
+ D.removeEventListener("DOMContentLoaded", _ready, false);
+ }
+ }
+ };
+
+ // create custom event
+
+/*! DOMReady: based on work by: Dean Edwards/John Resig/Matthias Miller/Diego Perini */
+
+ // Internet Explorer: use the readyState of a defered script.
+ // This isolates what appears to be a safe moment to manipulate
+ // the DOM prior to when the document's readyState suggests
+ // it is safe to do so.
+ if (navigator.userAgent.match(/MSIE/)) {
+
+ if (self !== self.top) {
+ document.onreadystatechange = function() {
+ if (document.readyState == 'complete') {
+ document.onreadystatechange = null;
+ _ready();
+ }
+ };
+ } else {
+
+ GLOBAL_ENV._dri = setInterval(function() {
+ try {
+ // throws an error if doc is not ready
+ document.documentElement.doScroll('left');
+ clearInterval(GLOBAL_ENV._dri);
+ GLOBAL_ENV._dri = null;
+ _ready();
+ } catch (ex) {
+ }
+ }, POLL_INTERVAL);
+ }
+
+ // FireFox, Opera, Safari 3+: These browsers provide a event for this
+ // moment.
+ } else {
+ D.addEventListener("DOMContentLoaded", _ready, false);
+ }
+
+ /////////////////////////////////////////////////////////////
+ }
+
+})();
+YUI.add('event-base', function(Y) {
+
+(function() {
+/*
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+var GLOBAL_ENV = YUI.Env,
+
+ yready = function() {
+ Y.fire('domready');
+ };
+
+Y.publish('domready', {
+ fireOnce: true
+});
+
+if (GLOBAL_ENV.DOMReady) {
+ // console.log('DOMReady already fired', 'info', 'event');
+ yready();
+} else {
+ // console.log('setting up before listener', 'info', 'event');
+ // console.log('env: ' + YUI.Env.windowLoaded, 'info', 'event');
+ Y.before(yready, GLOBAL_ENV, "_ready");
+}
+
+})();
+(function() {
+
+/**
+ * Custom event engine, DOM event listener abstraction layer, synthetic DOM
+ * events.
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Wraps a DOM event, properties requiring browser abstraction are
+ * fixed here. Provids a security layer when required.
+ * @class DOMEventFacade
+ * @param ev {Event} the DOM event
+ * @param currentTarget {HTMLElement} the element the listener was attached to
+ * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
+ */
+
+/*
+ * @TODO constants? LEFTBUTTON, MIDDLEBUTTON, RIGHTBUTTON, keys
+ */
+
+/*
+
+var whitelist = {
+ altKey : 1,
+ // "button" : 1, // we supply
+ // "bubbles" : 1, // needed?
+ // "cancelable" : 1, // needed?
+ // "charCode" : 1, // we supply
+ cancelBubble : 1,
+ // "currentTarget" : 1, // we supply
+ ctrlKey : 1,
+ clientX : 1, // needed?
+ clientY : 1, // needed?
+ detail : 1, // not fully implemented
+ // "fromElement" : 1,
+ keyCode : 1,
+ // "height" : 1, // needed?
+ // "initEvent" : 1, // need the init events?
+ // "initMouseEvent" : 1,
+ // "initUIEvent" : 1,
+ // "layerX" : 1, // needed?
+ // "layerY" : 1, // needed?
+ metaKey : 1,
+ // "modifiers" : 1, // needed?
+ // "offsetX" : 1, // needed?
+ // "offsetY" : 1, // needed?
+ // "preventDefault" : 1, // we supply
+ // "reason" : 1, // IE proprietary
+ // "relatedTarget" : 1,
+ // "returnValue" : 1, // needed?
+ shiftKey : 1,
+ // "srcUrn" : 1, // IE proprietary
+ // "srcElement" : 1,
+ // "srcFilter" : 1, IE proprietary
+ // "stopPropagation" : 1, // we supply
+ // "target" : 1,
+ // "timeStamp" : 1, // needed?
+ // "toElement" : 1,
+ type : 1,
+ // "view" : 1,
+ // "which" : 1, // we supply
+ // "width" : 1, // needed?
+ x : 1,
+ y : 1
+},
+
+*/
+
+ var ua = Y.UA,
+
+ /**
+ * webkit key remapping required for Safari < 3.1
+ * @property webkitKeymap
+ * @private
+ */
+ webkitKeymap = {
+ 63232: 38, // up
+ 63233: 40, // down
+ 63234: 37, // left
+ 63235: 39, // right
+ 63276: 33, // page up
+ 63277: 34, // page down
+ 25: 9, // SHIFT-TAB (Safari provides a different key code in
+ // this case, even though the shiftKey modifier is set)
+ 63272: 46, // delete
+ 63273: 36, // home
+ 63275: 35 // end
+ },
+
+ /**
+ * Returns a wrapped node. Intended to be used on event targets,
+ * so it will return the node's parent if the target is a text
+ * node.
+ *
+ * If accessing a property of the node throws an error, this is
+ * probably the anonymous div wrapper Gecko adds inside text
+ * nodes. This likely will only occur when attempting to access
+ * the relatedTarget. In this case, we now return null because
+ * the anonymous div is completely useless and we do not know
+ * what the related target was because we can't even get to
+ * the element's parent node.
+ *
+ * @method resolve
+ * @private
+ */
+ resolve = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ n = n.parentNode;
+ }
+ } catch(e) {
+ return null;
+ }
+
+ return Y.one(n);
+ };
+
+
+// provide a single event with browser abstractions resolved
+//
+// include all properties for both browers?
+// include only DOM2 spec properties?
+// provide browser-specific facade?
+
+Y.DOMEventFacade = function(ev, currentTarget, wrapper) {
+
+ wrapper = wrapper || {};
+
+ var e = ev, ot = currentTarget, d = Y.config.doc, b = d.body,
+ x = e.pageX, y = e.pageY, c, t;
+
+ this.altKey = e.altKey;
+ this.ctrlKey = e.ctrlKey;
+ this.metaKey = e.metaKey;
+ this.shiftKey = e.shiftKey;
+ this.type = e.type;
+ this.clientX = e.clientX;
+ this.clientY = e.clientY;
+
+ //////////////////////////////////////////////////////
+
+ if (!x && 0 !== x) {
+ x = e.clientX || 0;
+ y = e.clientY || 0;
+
+ if (ua.ie) {
+ x += Math.max(d.documentElement.scrollLeft, b.scrollLeft);
+ y += Math.max(d.documentElement.scrollTop, b.scrollTop);
+ }
+ }
+
+ this._yuifacade = true;
+
+ /**
+ * The native event
+ * @property _event
+ */
+ this._event = e;
+
+ /**
+ * The X location of the event on the page (including scroll)
+ * @property pageX
+ * @type int
+ */
+ this.pageX = x;
+
+ /**
+ * The Y location of the event on the page (including scroll)
+ * @property pageY
+ * @type int
+ */
+ this.pageY = y;
+
+ //////////////////////////////////////////////////////
+
+ c = e.keyCode || e.charCode || 0;
+
+ if (ua.webkit && (c in webkitKeymap)) {
+ c = webkitKeymap[c];
+ }
+
+ /**
+ * The keyCode for key events. Uses charCode if keyCode is not available
+ * @property keyCode
+ * @type int
+ */
+ this.keyCode = c;
+
+ /**
+ * The charCode for key events. Same as keyCode
+ * @property charCode
+ * @type int
+ */
+ this.charCode = c;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * The button that was pushed.
+ * @property button
+ * @type int
+ */
+ this.button = e.which || e.button;
+
+ /**
+ * The button that was pushed. Same as button.
+ * @property which
+ * @type int
+ */
+ this.which = this.button;
+
+ //////////////////////////////////////////////////////
+
+ /**
+ * Node reference for the targeted element
+ * @propery target
+ * @type Node
+ */
+ this.target = resolve(e.target || e.srcElement);
+
+ /**
+ * Node reference for the element that the listener was attached to.
+ * @propery currentTarget
+ * @type Node
+ */
+ this.currentTarget = resolve(ot);
+
+ t = e.relatedTarget;
+
+ if (!t) {
+ if (e.type == "mouseout") {
+ t = e.toElement;
+ } else if (e.type == "mouseover") {
+ t = e.fromElement;
+ }
+ }
+
+ /**
+ * Node reference to the relatedTarget
+ * @propery relatedTarget
+ * @type Node
+ */
+ this.relatedTarget = resolve(t);
+
+ /**
+ * Number representing the direction and velocity of the movement of the mousewheel.
+ * Negative is down, the higher the number, the faster. Applies to the mousewheel event.
+ * @property wheelDelta
+ * @type int
+ */
+ if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
+ this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
+ }
+
+ //////////////////////////////////////////////////////
+ // methods
+
+ /**
+ * Stops the propagation to the next bubble target
+ * @method stopPropagation
+ */
+ this.stopPropagation = function() {
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ } else {
+ e.cancelBubble = true;
+ }
+ wrapper.stopped = 1;
+ };
+
+ /**
+ * Stops the propagation to the next bubble target and
+ * prevents any additional listeners from being exectued
+ * on the current target.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function() {
+ if (e.stopImmediatePropagation) {
+ e.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+ wrapper.stopped = 2;
+ };
+
+ /**
+ * Prevents the event's default behavior
+ * @method preventDefault
+ * @param returnValue {string} sets the returnValue of the event to this value
+ * (rather than the default false value). This can be used to add a customized
+ * confirmation query to the beforeunload event).
+ */
+ this.preventDefault = function(returnValue) {
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ e.returnValue = returnValue || false;
+ wrapper.prevented = 1;
+ };
+
+ /**
+ * Stops the event propagation and prevents the default
+ * event behavior.
+ * @method halt
+ * @param immediate {boolean} if true additional listeners
+ * on the current target will not be executed
+ */
+ this.halt = function(immediate) {
+ if (immediate) {
+ this.stopImmediatePropagation();
+ } else {
+ this.stopPropagation();
+ }
+
+ this.preventDefault();
+ };
+
+};
+
+})();
+(function() {
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * The event utility provides functions to add and remove event listeners,
+ * event cleansing. It also tries to automatically remove listeners it
+ * registers during the unload event.
+ *
+ * @class Event
+ * @static
+ */
+
+Y.Env.evt.dom_wrappers = {};
+Y.Env.evt.dom_map = {};
+
+var _eventenv = Y.Env.evt,
+add = YUI.Env.add,
+remove = YUI.Env.remove,
+
+onLoad = function() {
+ YUI.Env.windowLoaded = true;
+ Y.Event._load();
+ remove(window, "load", onLoad);
+},
+
+onUnload = function() {
+ Y.Event._unload();
+ remove(window, "unload", onUnload);
+},
+
+EVENT_READY = 'domready',
+
+COMPAT_ARG = '~yui|2|compat~',
+
+shouldIterate = function(o) {
+ try {
+ return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) && !o.tagName && !o.alert);
+ } catch(ex) {
+ return false;
+ }
+
+},
+
+Event = function() {
+
+ /**
+ * True after the onload event has fired
+ * @property _loadComplete
+ * @type boolean
+ * @static
+ * @private
+ */
+ var _loadComplete = false,
+
+ /**
+ * The number of times to poll after window.onload. This number is
+ * increased if additional late-bound handlers are requested after
+ * the page load.
+ * @property _retryCount
+ * @static
+ * @private
+ */
+ _retryCount = 0,
+
+ /**
+ * onAvailable listeners
+ * @property _avail
+ * @static
+ * @private
+ */
+ _avail = [],
+
+ /**
+ * Custom event wrappers for DOM events. Key is
+ * 'event:' + Element uid stamp + event type
+ * @property _wrappers
+ * @type Y.Event.Custom
+ * @static
+ * @private
+ */
+ _wrappers = _eventenv.dom_wrappers,
+
+ _windowLoadKey = null,
+
+ /**
+ * Custom event wrapper map DOM events. Key is
+ * Element uid stamp. Each item is a hash of custom event
+ * wrappers as provided in the _wrappers collection. This
+ * provides the infrastructure for getListeners.
+ * @property _el_events
+ * @static
+ * @private
+ */
+ _el_events = _eventenv.dom_map;
+
+ return {
+
+ /**
+ * The number of times we should look for elements that are not
+ * in the DOM at the time the event is requested after the document
+ * has been loaded. The default is 1000@amp;40 ms, so it will poll
+ * for 40 seconds or until all outstanding handlers are bound
+ * (whichever comes first).
+ * @property POLL_RETRYS
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_RETRYS: 1000,
+
+ /**
+ * The poll interval in milliseconds
+ * @property POLL_INTERVAL
+ * @type int
+ * @static
+ * @final
+ */
+ POLL_INTERVAL: 40,
+
+ /**
+ * addListener/removeListener can throw errors in unexpected scenarios.
+ * These errors are suppressed, the method returns false, and this property
+ * is set
+ * @property lastError
+ * @static
+ * @type Error
+ */
+ lastError: null,
+
+
+ /**
+ * poll handle
+ * @property _interval
+ * @static
+ * @private
+ */
+ _interval: null,
+
+ /**
+ * document readystate poll handle
+ * @property _dri
+ * @static
+ * @private
+ */
+ _dri: null,
+
+ /**
+ * True when the document is initially usable
+ * @property DOMReady
+ * @type boolean
+ * @static
+ */
+ DOMReady: false,
+
+ /**
+ * @method startInterval
+ * @static
+ * @private
+ */
+ startInterval: function() {
+ var E = Y.Event;
+
+ if (!E._interval) {
+E._interval = setInterval(Y.bind(E._poll, E), E.POLL_INTERVAL);
+ }
+ },
+
+ /**
+ * Executes the supplied callback when the item with the supplied
+ * id is found. This is meant to be used to execute behavior as
+ * soon as possible as the page loads. If you use this after the
+ * initial page load it will poll for a fixed time for the element.
+ * The number of times it will poll and the frequency are
+ * configurable. By default it will poll for 10 seconds.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onAvailable
+ *
+ * @param {string||string[]} id the id of the element, or an array
+ * of ids to look for.
+ * @param {function} fn what to execute when the element is found.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj, if set to an object it
+ * will execute in the context of that object
+ * @param checkContent {boolean} check child node readiness (onContentReady)
+ * @static
+ * @deprecated Use Y.on("available")
+ */
+ // @TODO fix arguments
+ onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
+
+ var a = Y.Array(id), i, availHandle;
+
+
+ for (i=0; i<a.length; i=i+1) {
+ _avail.push({
+ id: a[i],
+ fn: fn,
+ obj: p_obj,
+ override: p_override,
+ checkReady: checkContent,
+ compat: compat
+ });
+ }
+ _retryCount = this.POLL_RETRYS;
+
+ // We want the first test to be immediate, but async
+ setTimeout(Y.bind(Y.Event._poll, Y.Event), 0);
+
+ availHandle = new Y.EventHandle({
+
+ _delete: function() {
+ // set by the event system for lazy DOM listeners
+ if (availHandle.handle) {
+ availHandle.handle.detach();
+ return;
+ }
+
+ var i, j;
+
+ // otherwise try to remove the onAvailable listener(s)
+ for (i = 0; i < a.length; i++) {
+ for (j = 0; j < _avail.length; j++) {
+ if (a[i] === _avail[j].id) {
+ _avail.splice(j, 1);
+ }
+ }
+ }
+ }
+
+ });
+
+ return availHandle;
+ },
+
+ /**
+ * Works the same way as onAvailable, but additionally checks the
+ * state of sibling elements to determine if the content of the
+ * available element is safe to modify.
+ *
+ * <p>The callback is executed with a single parameter:
+ * the custom object parameter, if provided.</p>
+ *
+ * @method onContentReady
+ *
+ * @param {string} id the id of the element to look for.
+ * @param {function} fn what to execute when the element is ready.
+ * @param {object} p_obj an optional object to be passed back as
+ * a parameter to fn.
+ * @param {boolean|object} p_override If set to true, fn will execute
+ * in the context of p_obj. If an object, fn will
+ * exectute in the context of that object
+ *
+ * @static
+ * @deprecated Use Y.on("contentready")
+ */
+ // @TODO fix arguments
+ onContentReady: function(id, fn, p_obj, p_override, compat) {
+ return this.onAvailable(id, fn, p_obj, p_override, true, compat);
+ },
+
+ /**
+ * Adds an event listener
+ *
+ * @method attach
+ *
+ * @param {String} type The type of event to append
+ * @param {Function} fn The method the event invokes
+ * @param {String|HTMLElement|Array|NodeList} el An id, an element
+ * reference, or a collection of ids and/or elements to assign the
+ * listener to.
+ * @param {Object} context optional context object
+ * @param {Boolean|object} args 0..n arguments to pass to the callback
+ * @return {EventHandle} an object to that can be used to detach the listener
+ *
+ * @static
+ */
+
+ attach: function(type, fn, el, context) {
+ return Y.Event._attach(Y.Array(arguments, 0, true));
+ },
+
+ _createWrapper: function (el, type, capture, compat, facade) {
+
+ var ek = Y.stamp(el),
+ key = 'event:' + ek + type,
+ cewrapper;
+
+
+ if (false === facade) {
+ key += 'native';
+ }
+ if (capture) {
+ key += 'capture';
+ }
+
+
+ cewrapper = _wrappers[key];
+
+
+ if (!cewrapper) {
+ // create CE wrapper
+ cewrapper = Y.publish(key, {
+ silent: true,
+ bubbles: false,
+ contextFn: function() {
+ cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
+ return cewrapper.nodeRef;
+ }
+ });
+
+ // for later removeListener calls
+ cewrapper.el = el;
+ cewrapper.key = key;
+ cewrapper.domkey = ek;
+ cewrapper.type = type;
+ cewrapper.fn = function(e) {
+ cewrapper.fire(Y.Event.getEvent(e, el, (compat || (false === facade))));
+ };
+ cewrapper.capture = capture;
+
+ if (el == Y.config.win && type == "load") {
+ // window load happens once
+ cewrapper.fireOnce = true;
+ _windowLoadKey = key;
+ }
+
+ _wrappers[key] = cewrapper;
+ _el_events[ek] = _el_events[ek] || {};
+ _el_events[ek][key] = cewrapper;
+
+ add(el, type, cewrapper.fn, capture);
+ }
+
+ return cewrapper;
+
+ },
+
+ _attach: function(args, config) {
+
+ var compat, E=Y.Event,
+ handles, oEl, cewrapper, context,
+ fireNow = false, ret,
+ type = args[0],
+ fn = args[1],
+ el = args[2] || Y.config.win,
+ facade = config && config.facade,
+ capture = config && config.capture;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // trimmedArgs.pop();
+ }
+
+ if (!fn || !fn.call) {
+// throw new TypeError(type + " attach call failed, callback undefined");
+ return false;
+ }
+
+ // The el argument can be an array of elements or element ids.
+ if (shouldIterate(el)) {
+
+ handles=[];
+
+ Y.each(el, function(v, k) {
+ args[2] = v;
+ handles.push(E._attach(args, config));
+ });
+
+ // return (handles.length === 1) ? handles[0] : handles;
+ return new Y.EventHandle(handles);
+
+ // If the el argument is a string, we assume it is
+ // actually the id of the element. If the page is loaded
+ // we convert el to the actual element, otherwise we
+ // defer attaching the event until the element is
+ // ready
+ } else if (Y.Lang.isString(el)) {
+
+ // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
+
+ if (compat) {
+ oEl = Y.DOM.byId(el);
+ } else {
+
+ oEl = Y.Selector.query(el);
+
+ switch (oEl.length) {
+ case 0:
+ oEl = null;
+ break;
+ case 1:
+ oEl = oEl[0];
+ break;
+ default:
+ args[2] = oEl;
+ return E._attach(args, config);
+ }
+ }
+
+ if (oEl) {
+
+ el = oEl;
+
+ // Not found = defer adding the event until the element is available
+ } else {
+
+ ret = this.onAvailable(el, function() {
+
+ ret.handle = E._attach(args, config);
+
+ }, E, true, false, compat);
+
+ return ret;
+
+ }
+ }
+
+ // Element should be an html element or node
+ if (!el) {
+ return false;
+ }
+
+ if (Y.Node && el instanceof Y.Node) {
+ el = Y.Node.getDOMNode(el);
+ }
+
+ cewrapper = this._createWrapper(el, type, capture, compat, facade);
+
+ if (el == Y.config.win && type == "load") {
+
+ // if the load is complete, fire immediately.
+ // all subscribers, including the current one
+ // will be notified.
+ if (YUI.Env.windowLoaded) {
+ fireNow = true;
+ }
+ }
+
+ if (compat) {
+ args.pop();
+ }
+
+ context = args[3];
+
+ // set context to the Node if not specified
+ // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
+ ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
+
+ if (fireNow) {
+ cewrapper.fire();
+ }
+
+ return ret;
+
+ },
+
+ /**
+ * Removes an event listener. Supports the signature the event was bound
+ * with, but the preferred way to remove listeners is using the handle
+ * that is returned when using Y.on
+ *
+ * @method detach
+ *
+ * @param {String} type the type of event to remove.
+ * @param {Function} fn the method the event invokes. If fn is
+ * undefined, then all event handlers for the type of event are
+ * removed.
+ * @param {String|HTMLElement|Array|NodeList|EventHandle} el An
+ * event handle, an id, an element reference, or a collection
+ * of ids and/or elements to remove the listener from.
+ * @return {boolean} true if the unbind was successful, false otherwise.
+ * @static
+ */
+ detach: function(type, fn, el, obj) {
+
+ var args=Y.Array(arguments, 0, true), compat, i, l, ok,
+ id, ce;
+
+ if (args[args.length-1] === COMPAT_ARG) {
+ compat = true;
+ // args.pop();
+ }
+
+ if (type && type.detach) {
+ return type.detach();
+ }
+
+ // The el argument can be a string
+ if (typeof el == "string") {
+
+ // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
+ if (compat) {
+ el = Y.DOM.byId(el);
+ } else {
+ el = Y.Selector.query(el);
+ l = el.length;
+ if (l < 1) {
+ el = null;
+ } else if (l == 1) {
+ el = el[0];
+ }
+ }
+ // return Y.Event.detach.apply(Y.Event, args);
+
+ // The el argument can be an array of elements or element ids.
+ }
+
+ if (!el) {
+ return false;
+ }
+
+ if (shouldIterate(el)) {
+
+ ok = true;
+ for (i=0, l=el.length; i<l; ++i) {
+ args[2] = el[i];
+ ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
+ }
+
+ return ok;
+
+ }
+
+ if (!type || !fn || !fn.call) {
+ return this.purgeElement(el, false, type);
+ }
+
+ id = 'event:' + Y.stamp(el) + type;
+ ce = _wrappers[id];
+
+ if (ce) {
+ return ce.detach(fn);
+ } else {
+ return false;
+ }
+
+ },
+
+ /**
+ * Finds the event in the window object, the caller's arguments, or
+ * in the arguments of another method in the callstack. This is
+ * executed automatically for events registered through the event
+ * manager, so the implementer should not normally need to execute
+ * this function at all.
+ * @method getEvent
+ * @param {Event} e the event parameter from the handler
+ * @param {HTMLElement} el the element the listener was attached to
+ * @return {Event} the event
+ * @static
+ */
+ getEvent: function(e, el, noFacade) {
+ var ev = e || window.event;
+
+ return (noFacade) ? ev :
+ new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
+ },
+
+ /**
+ * Generates an unique ID for the element if it does not already
+ * have one.
+ * @method generateId
+ * @param el the element to create the id for
+ * @return {string} the resulting id of the element
+ * @static
+ */
+ generateId: function(el) {
+ var id = el.id;
+
+ if (!id) {
+ id = Y.stamp(el);
+ el.id = id;
+ }
+
+ return id;
+ },
+
+ /**
+ * We want to be able to use getElementsByTagName as a collection
+ * to attach a group of events to. Unfortunately, different
+ * browsers return different types of collections. This function
+ * tests to determine if the object is array-like. It will also
+ * fail if the object is an array, but is empty.
+ * @method _isValidCollection
+ * @param o the object to test
+ * @return {boolean} true if the object is array-like and populated
+ * @deprecated was not meant to be used directly
+ * @static
+ * @private
+ */
+ _isValidCollection: shouldIterate,
+
+ /**
+ * hook up any deferred listeners
+ * @method _load
+ * @static
+ * @private
+ */
+ _load: function(e) {
+
+ if (!_loadComplete) {
+
+
+ _loadComplete = true;
+
+ // Just in case DOMReady did not go off for some reason
+ // E._ready();
+ if (Y.fire) {
+ Y.fire(EVENT_READY);
+ }
+
+ // Available elements may not have been detected before the
+ // window load event fires. Try to find them now so that the
+ // the user is more likely to get the onAvailable notifications
+ // before the window load notification
+ Y.Event._poll();
+
+ }
+ },
+
+ /**
+ * Polling function that runs before the onload event fires,
+ * attempting to attach to DOM Nodes as soon as they are
+ * available
+ * @method _poll
+ * @static
+ * @private
+ */
+ _poll: function() {
+
+ if (this.locked) {
+ return;
+ }
+
+ if (Y.UA.ie && !YUI.Env.DOMReady) {
+ // Hold off if DOMReady has not fired and check current
+ // readyState to protect against the IE operation aborted
+ // issue.
+ this.startInterval();
+ return;
+ }
+
+ this.locked = true;
+
+
+ // keep trying until after the page is loaded. We need to
+ // check the page load state prior to trying to bind the
+ // elements so that we can be certain all elements have been
+ // tested appropriately
+ var tryAgain = !_loadComplete, notAvail, executeItem,
+ i, len, item, el;
+
+ if (!tryAgain) {
+ tryAgain = (_retryCount > 0);
+ }
+
+ // onAvailable
+ notAvail = [];
+
+ executeItem = function (el, item) {
+
+ var context, ov = item.override;
+
+ if (item.compat) {
+
+ if (item.override) {
+ if (ov === true) {
+ context = item.obj;
+ } else {
+ context = ov;
+ }
+ } else {
+ context = el;
+ }
+
+ item.fn.call(context, item.obj);
+
+ } else {
+ context = item.obj || Y.one(el);
+ item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
+ }
+
+ };
+
+
+ // onAvailable
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && !item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ executeItem(el, item);
+ _avail[i] = null;
+ } else {
+ notAvail.push(item);
+ }
+ }
+ }
+
+ // onContentReady
+ for (i=0,len=_avail.length; i<len; ++i) {
+ item = _avail[i];
+ if (item && item.checkReady) {
+
+ // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
+ el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
+
+ if (el) {
+ // The element is available, but not necessarily ready
+ // @todo should we test parentNode.nextSibling?
+ if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
+ executeItem(el, item);
+ _avail[i] = null;
+ }
+ } else {
+ notAvail.push(item);
+ }
+ }
+ }
+
+ _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
+
+ if (tryAgain) {
+ // we may need to strip the nulled out items here
+ this.startInterval();
+ } else {
+ clearInterval(this._interval);
+ this._interval = null;
+ }
+
+ this.locked = false;
+
+ return;
+
+ },
+
+ /**
+ * Removes all listeners attached to the given element via addListener.
+ * Optionally, the node's children can also be purged.
+ * Optionally, you can specify a specific type of event to remove.
+ * @method purgeElement
+ * @param {HTMLElement} el the element to purge
+ * @param {boolean} recurse recursively purge this element's children
+ * as well. Use with caution.
+ * @param {string} type optional type of listener to purge. If
+ * left out, all listeners will be removed
+ * @static
+ */
+ purgeElement: function(el, recurse, type) {
+ // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
+ var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el,
+ lis = this.getListeners(oEl, type), i, len, props;
+ if (lis) {
+ for (i=0,len=lis.length; i<len ; ++i) {
+ props = lis[i];
+ props.detachAll();
+ remove(props.el, props.type, props.fn, props.capture);
+ delete _wrappers[props.key];
+ delete _el_events[props.domkey][props.key];
+ }
+
+ }
+
+ if (recurse && oEl && oEl.childNodes) {
+ for (i=0,len=oEl.childNodes.length; i<len ; ++i) {
+ this.purgeElement(oEl.childNodes[i], recurse, type);
+ }
+ }
+
+ },
+
+ /**
+ * Returns all listeners attached to the given element via addListener.
+ * Optionally, you can specify a specific type of event to return.
+ * @method getListeners
+ * @param el {HTMLElement|string} the element or element id to inspect
+ * @param type {string} optional type of listener to return. If
+ * left out, all listeners will be returned
+ * @return {Y.Custom.Event} the custom event wrapper for the DOM event(s)
+ * @static
+ */
+ getListeners: function(el, type) {
+ var ek = Y.stamp(el, true), evts = _el_events[ek],
+ results=[] , key = (type) ? 'event:' + ek + type : null;
+
+ if (!evts) {
+ return null;
+ }
+
+ if (key) {
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ // get native events as well
+ key += 'native';
+ if (evts[key]) {
+ results.push(evts[key]);
+ }
+
+ } else {
+ Y.each(evts, function(v, k) {
+ results.push(v);
+ });
+ }
+
+ return (results.length) ? results : null;
+ },
+
+ /**
+ * Removes all listeners registered by pe.event. Called
+ * automatically during the unload event.
+ * @method _unload
+ * @static
+ * @private
+ */
+ _unload: function(e) {
+ Y.each(_wrappers, function(v, k) {
+ v.detachAll();
+ remove(v.el, v.type, v.fn, v.capture);
+ delete _wrappers[k];
+ delete _el_events[v.domkey][k];
+ });
+ },
+
+
+ /**
+ * Adds a DOM event directly without the caching, cleanup, context adj, etc
+ *
+ * @method nativeAdd
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeAdd: add,
+
+ /**
+ * Basic remove listener
+ *
+ * @method nativeRemove
+ * @param {HTMLElement} el the element to bind the handler to
+ * @param {string} type the type of event handler
+ * @param {function} fn the callback to invoke
+ * @param {boolen} capture capture or bubble phase
+ * @static
+ * @private
+ */
+ nativeRemove: remove
+ };
+
+}();
+
+Y.Event = Event;
+
+
+if (Y.config.injected || YUI.Env.windowLoaded) {
+ onLoad();
+} else {
+ add(window, "load", onLoad);
+}
+
+// Process onAvailable/onContentReady items when when the DOM is ready in IE
+if (Y.UA.ie) {
+ Y.on(EVENT_READY, Event._poll, Event, true);
+}
+
+Y.on("unload", onUnload);
+
+Event.Custom = Y.CustomEvent;
+Event.Subscriber = Y.Subscriber;
+Event.Target = Y.EventTarget;
+Event.Handle = Y.EventHandle;
+Event.Facade = Y.EventFacade;
+
+Event._poll();
+
+})();
+
+/**
+ * DOM event listener abstraction layer
+ * @module event
+ * @submodule event-base
+ */
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM.
+ * @event available
+ * @param type {string} 'available'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.available = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
+ }
+};
+
+/**
+ * Executes the callback as soon as the specified element
+ * is detected in the DOM with a nextSibling property
+ * (indicating that the element's children are available)
+ * @event contentready
+ * @param type {string} 'contentready'
+ * @param fn {function} the callback function to execute.
+ * @param el {string|HTMLElement|collection} the element(s) to attach
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.contentready = {
+ on: function(type, fn, id, o) {
+ var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : [];
+ return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
+YUI.add('event-delegate', function(Y) {
+
+/**
+ * Adds event delegation support to the library.
+ *
+ * @module event
+ * @submodule event-delegate
+ */
+
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ delegates = {},
+
+ specialTypes = {
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+ },
+
+ resolveTextNode = function(n) {
+ try {
+ if (n && 3 == n.nodeType) {
+ return n.parentNode;
+ }
+ } catch(e) { }
+ return n;
+ },
+
+ delegateHandler = function(delegateKey, e, el) {
+
+ var target = resolveTextNode((e.target || e.srcElement)),
+ tests = delegates[delegateKey],
+ spec,
+ ename,
+ matched,
+ fn,
+ ev;
+
+
+ var getMatch = function(el, selector, container) {
+
+ var returnVal;
+
+ if (!el || el === container) {
+ returnVal = false;
+ }
+ else {
+ returnVal = Y.Selector.test(el, selector) ? el: getMatch(el.parentNode, selector, container);
+ }
+
+ return returnVal;
+
+ };
+
+
+ for (spec in tests) {
+
+ if (tests.hasOwnProperty(spec)) {
+
+ ename = tests[spec];
+ fn = tests.fn;
+ matched = null;
+
+
+ if (Y.Selector.test(target, spec, el)) {
+ matched = target;
+ }
+ else if (Y.Selector.test(target, ((spec.replace(/,/gi, " *,")) + " *"), el)) {
+
+ // The target is a descendant of an element matching
+ // the selector, so crawl up to find the ancestor that
+ // matches the selector
+
+ matched = getMatch(target, spec, el);
+
+ }
+
+
+ if (matched) {
+
+ if (!ev) {
+ ev = new Y.DOMEventFacade(e, el);
+ ev.container = ev.currentTarget;
+ }
+
+ ev.currentTarget = Y.Node.get(matched);
+
+ Y.publish(ename, {
+ contextFn: function() {
+ return ev.currentTarget;
+ }
+ });
+
+ if (fn) {
+ fn(ev, ename);
+ }
+ else {
+ Y.fire(ename, ev);
+ }
+
+ }
+
+ }
+ }
+
+ },
+
+ attach = function (type, key, element) {
+
+ var focusMethods = {
+ focus: Event._attachFocus,
+ blur: Event._attachBlur
+ },
+
+ attachFn = focusMethods[type],
+
+ args = [type,
+ function (e) {
+ delegateHandler(key, (e || window.event), element);
+ },
+ element];
+
+
+ if (attachFn) {
+ return attachFn(args, { capture: true, facade: false });
+ }
+ else {
+ return Event._attach(args, { facade: false });
+ }
+
+ },
+
+ sanitize = Y.cached(function(str) {
+ return str.replace(/[|,:]/g, '~');
+ });
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @event delegate
+ * @param type {string} 'delegate'
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param delegateType {string} the event type to delegate
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ * @deprecated use Y.delegate
+ */
+Y.Env.evt.plugins.delegate = {
+
+ on: function(type, fn, el, delegateType, spec) {
+
+
+ var args = Y.Array(arguments, 0, true);
+
+ args.splice(3, 1);
+
+ args[0] = delegateType;
+
+ return Y.delegate.apply(Y, args);
+
+ }
+
+};
+
+
+/**
+ * Sets up event delegation on a container element. The delegated event
+ * will use a supplied selector to test if the target or one of the
+ * descendants of the target match it. The supplied callback function
+ * will only be executed if a match was encountered, and, in fact,
+ * will be executed for each element that matches if you supply an
+ * ambiguous selector.
+ *
+ * The event object for the delegated event is supplied to the callback
+ * function. It is modified slightly in order to support all properties
+ * that may be needed for event delegation. 'currentTarget' is set to
+ * the element that matched the delegation specifcation. 'container' is
+ * set to the element that the listener is bound to (this normally would
+ * be the 'currentTarget').
+ *
+ * @method delegate
+ * @param type {string} the event type to delegate
+ * @param fn {function} the callback function to execute. This function
+ * will be provided the event object for the delegated event.
+ * @param el {string|node} the element that is the delegation container
+ * @param spec {string} a selector that must match the target of the
+ * event.
+ * @param context optional argument that specifies what 'this' refers to.
+ * @param args* 0..n additional arguments to pass on to the callback function.
+ * These arguments will be added after the event object.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Event.delegate = function (type, fn, el, spec) {
+
+ if (!spec) {
+ return false;
+ }
+
+
+ var args = Y.Array(arguments, 0, true),
+ element = el, // HTML element serving as the delegation container
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Y.Selector.query returns an array of matches unless specified
+ // to return just the first match. Since the primary use case for
+ // event delegation is to use a single event handler on a container,
+ // Y.delegate doesn't currently support being able to bind a
+ // single listener to multiple containers.
+
+ element = Y.Selector.query(el, null, true);
+
+ if (!element) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Event.delegate.apply(Event, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ element = Y.Node.getDOMNode(element);
+
+
+ var guid = Y.stamp(element),
+
+ // The Custom Event for the delegation spec
+ ename = 'delegate:' + guid + type + sanitize(spec),
+
+ // The key to the listener for the event type and container
+ delegateKey = type + guid,
+
+ delegate = delegates[delegateKey],
+
+ domEventHandle,
+
+ ceHandle,
+
+ listeners;
+
+
+ if (!delegate) {
+
+ delegate = {};
+
+ if (specialTypes[type]) {
+
+ if (!Event._fireMouseEnter) {
+ return false;
+ }
+
+ type = specialTypes[type];
+ delegate.fn = Event._fireMouseEnter;
+
+ }
+
+ // Create the DOM Event wrapper that will fire the Custom Event
+
+ domEventHandle = attach(type, delegateKey, element);
+
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'delegates' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known delegates
+ delete delegates[delegateKey];
+
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(ename);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+ delegate.handle = domEventHandle;
+
+ delegates[delegateKey] = delegate;
+
+ }
+
+
+ listeners = delegate.listeners;
+
+ delegate.listeners = listeners ? (listeners + 1) : 1;
+ delegate[spec] = ename;
+
+
+ args[0] = ename;
+
+ // Remove element, delegation spec
+ args.splice(2, 2);
+
+
+ // Subscribe to the Custom Event for the delegation spec
+
+ ceHandle = Y.on.apply(Y, args);
+
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'delegates' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ delegate.listeners = (delegate.listeners - 1);
+
+ if (delegate.listeners === 0) {
+ delegate.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+ return ceHandle;
+
+};
+
+Y.delegate = Event.delegate;
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-mousewheel', function(Y) {
+
+/**
+ * Adds mousewheel event support
+ * @module event
+ * @submodule event-mousewheel
+ */
+var DOM_MOUSE_SCROLL = 'DOMMouseScroll',
+ fixArgs = function(args) {
+ var a = Y.Array(args, 0, true), target;
+ if (Y.UA.gecko) {
+ a[0] = DOM_MOUSE_SCROLL;
+ target = Y.config.win;
+ } else {
+ target = Y.config.doc;
+ }
+
+ if (a.length < 3) {
+ a[2] = target;
+ } else {
+ a.splice(2, 0, target);
+ }
+
+ return a;
+ };
+
+/**
+ * Mousewheel event. This listener is automatically attached to the
+ * correct target, so one should not be supplied. Mouse wheel
+ * direction and velocity is stored in the 'mouseDelta' field.
+ * @event mousewheel
+ * @param type {string} 'mousewheel'
+ * @param fn {function} the callback to execute
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+Y.Env.evt.plugins.mousewheel = {
+ on: function() {
+ return Y.Event._attach(fixArgs(arguments));
+ },
+
+ detach: function() {
+ return Y.Event.detach.apply(Y.Event, fixArgs(arguments));
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-mouseenter', function(Y) {
+
+/**
+ * Adds support for mouseenter/mouseleave events
+ * @module event
+ * @submodule event-mouseenter
+ */
+var Event = Y.Event,
+ Lang = Y.Lang,
+
+ plugins = Y.Env.evt.plugins,
+
+ listeners = {},
+
+ eventConfig = {
+
+ on: function(type, fn, el) {
+
+ var args = Y.Array(arguments, 0, true),
+ element = el,
+ availHandle;
+
+
+ if (Lang.isString(el)) {
+
+ // Need to use Y.all because if el is a string it could be a
+ // selector that returns a NodeList
+
+ element = Y.all(el);
+
+ if (element.size() === 0) { // Not found, check using onAvailable
+
+ availHandle = Event.onAvailable(el, function() {
+
+ availHandle.handle = Y.on.apply(Y, args);
+
+ }, Event, true, false);
+
+ return availHandle;
+
+ }
+
+ }
+
+
+ var sDOMEvent = (type === "mouseenter") ? "mouseover" : "mouseout",
+
+ // The name of the custom event
+ sEventName = type + ":" + Y.stamp(element) + sDOMEvent,
+
+ listener = listeners[sEventName],
+
+ domEventHandle,
+
+ ceHandle,
+
+ nListeners;
+
+
+ // Bind an actual DOM event listener that will call the
+ // the custom event
+ if (!listener) {
+
+ domEventHandle = Y.on(sDOMEvent, Y.rbind(Event._fireMouseEnter, Y, sEventName), element);
+
+ // Hook into the _delete method for the Custom Event wrapper of this
+ // DOM Event in order to clean up the 'listeners' map and unsubscribe
+ // the associated Custom Event listeners fired by this DOM event
+ // listener if/when the user calls "purgeElement" OR removes all
+ // listeners of the Custom Event.
+
+ Y.after(function (sub) {
+
+ if (domEventHandle.sub == sub) {
+
+ // Delete this event from the map of known mouseenter
+ // and mouseleave listeners
+ delete listeners[sEventName];
+
+
+ // Unsubscribe all listeners of the Custom Event fired
+ // by this DOM event.
+ Y.detachAll(sEventName);
+
+ }
+
+ }, domEventHandle.evt, "_delete");
+
+
+ listener = {};
+ listener.handle = domEventHandle;
+
+ listeners[sEventName] = listener;
+
+ }
+
+ nListeners = listener.count;
+
+ listener.count = nListeners ? (nListeners + 1) : 1;
+
+ args[0] = sEventName;
+
+ // Remove the element from the args
+ args.splice(2, 1);
+
+ // Subscribe to the custom event
+ ceHandle = Y.on.apply(Y, args);
+
+ // Hook into the detach method of the handle in order to clean up the
+ // 'listeners' map and remove the associated DOM event handler
+ // responsible for firing this Custom Event if all listener for this
+ // event have been removed.
+
+ Y.after(function () {
+
+ listener.count = (listener.count - 1);
+
+ if (listener.count === 0) {
+ listener.handle.detach();
+ }
+
+ }, ceHandle, "detach");
+
+
+ return ceHandle;
+
+ }
+
+ };
+
+
+Event._fireMouseEnter = function (e, eventName) {
+
+ var relatedTarget = e.relatedTarget,
+ currentTarget = e.currentTarget;
+
+ if (currentTarget !== relatedTarget &&
+ !currentTarget.contains(relatedTarget)) {
+
+ Y.publish(eventName, {
+ contextFn: function() {
+ return currentTarget;
+ }
+ });
+
+ Y.fire(eventName, e);
+
+ }
+
+};
+
+
+/**
+ * Sets up a "mouseenter" listener—a listener that is called the first time
+ * the user's mouse enters the specified element(s).
+ *
+ * @event mouseenter
+ * @param type {string} "mouseenter"
+ * @param fn {function} The method the event invokes.
+ * @param el {string|node} The element(s) to assign the listener to.
+ * @param spec {string} Optional. String representing a selector that must
+ * match the target of the event in order for the listener to be called.
+ * @return {EventHandle} the detach handle
+ * @for YUI
+ */
+plugins.mouseenter = eventConfig;
+
+/**
+* Sets up a "mouseleave" listener—a listener that is called the first time
+* the user's mouse leaves the specified element(s).
+*
+* @event mouseleave
+* @param type {string} "mouseleave"
+* @param fn {function} The method the event invokes.
+* @param el {string|node} The element(s) to assign the listener to.
+* @param spec {string} Optional. String representing a selector that must
+* match the target of the event in order for the listener to be called.
+* @return {EventHandle} the detach handle
+* @for YUI
+ */
+plugins.mouseleave = eventConfig;
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-key', function(Y) {
+
+/**
+ * Functionality to listen for one or more specific key combinations.
+ * @module event
+ * @submodule event-key
+ */
+
+/**
+ * Add a key listener. The listener will only be notified if the
+ * keystroke detected meets the supplied specification. The
+ * spec consists of the key event type, followed by a colon,
+ * followed by zero or more comma separated key codes, followed
+ * by zero or more modifiers delimited by a plus sign. Ex:
+ * press:12,65+shift+ctrl
+ * @event key
+ * @for YUI
+ * @param type {string} 'key'
+ * @param fn {function} the function to execute
+ * @param id {string|HTMLElement|collection} the element(s) to bind
+ * @param spec {string} the keyCode and modifier specification
+ * @param o optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {Event.Handle} the detach handle
+ */
+Y.Env.evt.plugins.key = {
+
+ on: function(type, fn, id, spec, o) {
+ var a = Y.Array(arguments, 0, true), parsed, etype, criteria, ename;
+
+ parsed = spec && spec.split(':');
+
+ if (!spec || spec.indexOf(':') == -1 || !parsed[1]) {
+ a[0] = 'key' + ((parsed && parsed[0]) || 'press');
+ return Y.on.apply(Y, a);
+ }
+
+ // key event type: 'down', 'up', or 'press'
+ etype = parsed[0];
+
+ // list of key codes optionally followed by modifiers
+ criteria = (parsed[1]) ? parsed[1].split(/,|\+/) : null;
+
+ // the name of the custom event that will be created for the spec
+ ename = (Y.Lang.isString(id) ? id : Y.stamp(id)) + spec;
+
+ ename = ename.replace(/,/g, '_');
+
+ if (!Y.getEvent(ename)) {
+
+ // subscribe spec validator to the DOM event
+ Y.on(type + etype, function(e) {
+
+
+ var passed = false, failed = false, i, crit, critInt;
+
+ for (i=0; i<criteria.length; i=i+1) {
+ crit = criteria[i];
+ critInt = parseInt(crit, 10);
+
+ // pass this section if any supplied keyCode
+ // is found
+ if (Y.Lang.isNumber(critInt)) {
+
+ if (e.charCode === critInt) {
+ passed = true;
+ } else {
+ failed = true;
+ }
+
+ // only check modifier if no keyCode was specified
+ // or the keyCode check was successful. pass only
+ // if every modifier passes
+ } else if (passed || !failed) {
+ passed = (e[crit + 'Key']);
+ failed = !passed;
+ }
+ }
+
+ // fire spec custom event if spec if met
+ if (passed) {
+ Y.fire(ename, e);
+ }
+
+ }, id);
+
+ }
+
+ // subscribe supplied listener to custom event for spec validator
+ // remove element and spec.
+ a.splice(2, 2);
+ a[0] = ename;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-focus', function(Y) {
+
+/**
+ * Adds focus and blur event listener support. These events normally
+ * do not bubble, so this adds support for that so these events
+ * can be used in event delegation scenarios.
+ *
+ * @module event
+ * @submodule event-focus
+ */
+(function() {
+
+var UA = Y.UA,
+ Event = Y.Event,
+ plugins = Y.Env.evt.plugins,
+ ie = UA.ie,
+ bUseMutation = (UA.opera || UA.webkit),
+ eventNames = {
+ focus: (ie ? 'focusin' : (bUseMutation ? 'DOMFocusIn' : 'focus')),
+ blur: (ie ? 'focusout' : (bUseMutation ? 'DOMFocusOut' : 'blur'))
+ },
+
+ // Only need to use capture phase for Gecko since it doesn't support
+ // focusin, focusout, DOMFocusIn, or DOMFocusOut
+ CAPTURE_CONFIG = { capture: (UA.gecko ? true : false) },
+
+
+ attach = function (args, config) {
+
+ var a = Y.Array(args, 0, true);
+ a[0] = eventNames[a[0]];
+ return Event._attach(a, config);
+
+ },
+
+ eventAdapter = {
+
+ on: function () {
+ return attach(arguments, CAPTURE_CONFIG);
+ }
+
+ };
+
+
+Event._attachFocus = attach;
+Event._attachBlur = attach;
+
+/**
+ * Adds a DOM focus listener. Uses the focusin event in IE,
+ * DOMFocusIn for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event focus
+ * @param type {string} 'focus'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.focus = eventAdapter;
+
+/**
+ * Adds a DOM blur listener. Uses the focusout event in IE,
+ * DOMFocusOut for Opera and Webkit, and the capture phase for Gecko so that
+ * the event propagates in a way that enables event delegation.
+ *
+ * @for YUI
+ * @event blur
+ * @param type {string} 'blur'
+ * @param fn {function} the callback function to execute
+ * @param o {string|HTMLElement|collection} the element(s) to bind
+ * @param context optional context object
+ * @param args 0..n additional arguments to provide to the listener.
+ * @return {EventHandle} the detach handle
+ */
+plugins.blur = eventAdapter;
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
+YUI.add('event-resize', function(Y) {
+
+/**
+ * Adds a window resize event that has its behavior normalized to fire at the
+ * end of the resize rather than constantly during the resize.
+ * @module event
+ * @submodule event-resize
+ */
+(function() {
+
+var detachHandle,
+
+ timerHandle,
+
+ CE_NAME = 'window:resize',
+
+ handler = function(e) {
+
+ if (Y.UA.gecko) {
+
+ Y.fire(CE_NAME, e);
+
+ } else {
+
+ if (timerHandle) {
+ timerHandle.cancel();
+ }
+
+ timerHandle = Y.later(Y.config.windowResizeDelay || 40, Y, function() {
+ Y.fire(CE_NAME, e);
+ });
+ }
+
+ };
+
+
+/**
+ * Firefox fires the window resize event once when the resize action
+ * finishes, other browsers fire the event periodically during the
+ * resize. This code uses timeout logic to simulate the Firefox
+ * behavior in other browsers.
+ * @event windowresize
+ * @for YUI
+ */
+Y.Env.evt.plugins.windowresize = {
+
+ on: function(type, fn) {
+
+ // check for single window listener and add if needed
+ if (!detachHandle) {
+ detachHandle = Y.Event._attach(['resize', handler]);
+ }
+
+ var a = Y.Array(arguments, 0, true);
+ a[0] = CE_NAME;
+
+ return Y.on.apply(Y, a);
+ }
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['node-base']});
+
+
+YUI.add('event', function(Y){}, '3.0.0' ,{use:['event-base', 'event-delegate', 'event-mousewheel', 'event-mouseenter', 'event-key', 'event-focus', 'event-resize']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('get', function(Y) {
+
+(function() {
+
+/**
+ * Provides a mechanism to fetch remote resources and
+ * insert them into a document.
+ * @module yui
+ * @submodule get
+ */
+
+var ua = Y.UA,
+ L = Y.Lang,
+ // PREFIX = Y.guid(),
+ TYPE_JS = "text/javascript",
+ TYPE_CSS = "text/css",
+ STYLESHEET = "stylesheet";
+
+/**
+ * Fetches and inserts one or more script or link nodes into the document
+ * @class Get
+ * @static
+ */
+Y.Get = function() {
+
+ /**
+ * hash of queues to manage multiple requests
+ * @property queues
+ * @private
+ */
+ var queues={},
+
+ /**
+ * queue index used to generate transaction ids
+ * @property qidx
+ * @type int
+ * @private
+ */
+ qidx=0,
+
+ /**
+ * interal property used to prevent multiple simultaneous purge
+ * processes
+ * @property purging
+ * @type boolean
+ * @private
+ */
+ purging=false,
+
+
+ /**
+ * Generates an HTML element, this is not appended to a document
+ * @method _node
+ * @param type {string} the type of element
+ * @param attr {string} the attributes
+ * @param win {Window} optional window to create the element in
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _node = function(type, attr, win) {
+ var w = win || Y.config.win, d=w.document, n=d.createElement(type),
+ i;
+
+ for (i in attr) {
+ if (attr[i] && attr.hasOwnProperty(i)) {
+ n.setAttribute(i, attr[i]);
+ }
+ }
+
+ return n;
+ },
+
+ /**
+ * Generates a link node
+ * @method _linkNode
+ * @param url {string} the url for the css file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _linkNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_CSS,
+ rel: STYLESHEET,
+ href: url
+ };
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+ return _node("link", o, win);
+ },
+
+ /**
+ * Generates a script node
+ * @method _scriptNode
+ * @param url {string} the url for the script file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _scriptNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_JS,
+ src: url
+ };
+
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+
+ return _node("script", o, win);
+ },
+
+ /**
+ * Removes the nodes for the specified queue
+ * @method _purge
+ * @private
+ */
+ _purge = function(tId) {
+ var q=queues[tId], n, l, d, h, s, i, node, attr;
+ if (q) {
+ n = q.nodes;
+ l = n.length;
+ d = q.win.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, tId);
+ if (s) {
+ h = s.parentNode;
+ }
+ }
+
+ for (i=0; i<l; i=i+1) {
+ node = n[i];
+ if (node.clearAttributes) {
+ node.clearAttributes();
+ } else {
+ // This is a hostile delete
+ // operation attempting to improve
+ // memory performance. As such, the
+ // hasOwnProperty check is intentionally
+ // ommitted.
+ for (attr in node) {
+ delete node[attr];
+ }
+ }
+
+ h.removeChild(node);
+ }
+ }
+ q.nodes = [];
+ },
+
+ /**
+ * Returns the data payload for callback functions
+ * @method _returnData
+ * @private
+ */
+ _returnData = function(q, msg, result) {
+ return {
+ tId: q.tId,
+ win: q.win,
+ data: q.data,
+ nodes: q.nodes,
+ msg: msg,
+ statusText: result,
+ purge: function() {
+ _purge(this.tId);
+ }
+ };
+ },
+
+ /**
+ * The transaction is finished
+ * @method _end
+ * @param id {string} the id of the request
+ * @private
+ */
+ _end = function(id, msg, result) {
+ var q = queues[id], sc;
+ if (q && q.onEnd) {
+ sc = q.context || q;
+ q.onEnd.call(sc, _returnData(q, msg, result));
+ }
+ },
+
+ /*
+ * The request failed, execute fail handler with whatever
+ * was accomplished. There isn't a failure case at the
+ * moment unless you count aborted transactions
+ * @method _fail
+ * @param id {string} the id of the request
+ * @private
+ */
+ _fail = function(id, msg) {
+
+ Y.log("get failure: " + msg, "warn", "get");
+
+ var q = queues[id], sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ // execute failure callback
+ if (q.onFailure) {
+ sc = q.context || q;
+ q.onFailure.call(sc, _returnData(q, msg));
+ }
+
+ _end(id, msg, 'failure');
+ },
+
+ _get = function(nId, tId) {
+ var q = queues[tId],
+ n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
+ if (!n) {
+ _fail(tId, "target node not found: " + nId);
+ }
+
+ return n;
+ },
+
+ /**
+ * The request is complete, so executing the requester's callback
+ * @method _finish
+ * @param id {string} the id of the request
+ * @private
+ */
+ _finish = function(id) {
+ Y.log("Finishing transaction " + id, "info", "get");
+ var q = queues[id], msg, sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+ q.finished = true;
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ // execute success callback
+ if (q.onSuccess) {
+ sc = q.context || q;
+ q.onSuccess.call(sc, _returnData(q));
+ }
+
+ _end(id, msg, 'OK');
+ },
+
+ /**
+ * Timeout detected
+ * @method _timeout
+ * @param id {string} the id of the request
+ * @private
+ */
+ _timeout = function(id) {
+ Y.log("Timeout " + id, "info", "get");
+ var q = queues[id], sc;
+ if (q.onTimeout) {
+ sc = q.context || q;
+ q.onTimeout.call(sc, _returnData(q));
+ }
+
+ _end(id, 'timeout', 'timeout');
+ },
+
+
+ /**
+ * Loads the next item for a given request
+ * @method _next
+ * @param id {string} the id of the request
+ * @param loaded {string} the url that was just loaded, if any
+ * @private
+ */
+ _next = function(id, loaded) {
+
+ Y.log("_next: " + id + ", loaded: " + (loaded || "nothing"), "info", "get");
+
+ var q = queues[id], msg, w, d, h, n, url, s;
+
+ if (q.timer) {
+ // Y.log('cancel timer');
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ if (loaded) {
+ q.url.shift();
+ if (q.varName) {
+ q.varName.shift();
+ }
+ } else {
+ // This is the first pass: make sure the url is an array
+ q.url = (L.isString(q.url)) ? [q.url] : q.url;
+ if (q.varName) {
+ q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
+ }
+ }
+
+ w = q.win;
+ d = w.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.url.length === 0) {
+ _finish(id);
+ return;
+ }
+
+ url = q.url[0];
+
+ // if the url is undefined, this is probably a trailing comma problem in IE
+ if (!url) {
+ q.url.shift();
+ Y.log('skipping empty url');
+ return _next(id);
+ }
+
+ Y.log("attempting to load " + url, "info", "get");
+
+ if (q.timeout) {
+ // Y.log('create timer');
+ // q.timer = L.later(q.timeout, q, _timeout, id);
+ q.timer = setTimeout(function() {
+ _timeout(id);
+ }, q.timeout);
+ }
+
+ if (q.type === "script") {
+ n = _scriptNode(url, w, q.attributes);
+ } else {
+ n = _linkNode(url, w, q.attributes);
+ }
+
+ // track this node's load progress
+ _track(q.type, n, id, url, w, q.url.length);
+
+ // add the node to the queue so we can return it to the user supplied callback
+ q.nodes.push(n);
+
+ // add it to the head or insert it before 'insertBefore'
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, id);
+ if (s) {
+ s.parentNode.insertBefore(n, s);
+ }
+ } else {
+ h.appendChild(n);
+ }
+
+ Y.log("Appending node: " + url, "info", "get");
+
+ // FireFox does not support the onload event for link nodes, so there is
+ // no way to make the css requests synchronous. This means that the css
+ // rules in multiple files could be applied out of order in this browser
+ // if a later request returns before an earlier one. Safari too.
+ if ((ua.webkit || ua.gecko) && q.type === "css") {
+ _next(id, url);
+ }
+ },
+
+ /**
+ * Removes processed queues and corresponding nodes
+ * @method _autoPurge
+ * @private
+ */
+ _autoPurge = function() {
+
+ if (purging) {
+ return;
+ }
+
+ purging = true;
+
+ var i, q;
+
+ for (i in queues) {
+ if (queues.hasOwnProperty(i)) {
+ q = queues[i];
+ if (q.autopurge && q.finished) {
+ _purge(q.tId);
+ delete queues[i];
+ }
+ }
+ }
+
+ purging = false;
+ },
+
+ /**
+ * Saves the state for the request and begins loading
+ * the requested urls
+ * @method queue
+ * @param type {string} the type of node to insert
+ * @param url {string} the url to load
+ * @param opts the hash of options for this request
+ * @private
+ */
+ _queue = function(type, url, opts) {
+
+ opts = opts || {};
+
+ var id = "q" + (qidx++), q,
+ thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
+
+ if (qidx % thresh === 0) {
+ _autoPurge();
+ }
+
+ queues[id] = Y.merge(opts, {
+ tId: id,
+ type: type,
+ url: url,
+ finished: false,
+ nodes: []
+ });
+
+ q = queues[id];
+ q.win = q.win || Y.config.win;
+ q.context = q.context || q;
+ q.autopurge = ("autopurge" in q) ? q.autopurge :
+ (type === "script") ? true : false;
+
+ if (opts.charset) {
+ q.attributes = q.attributes || {};
+ q.attributes.charset = opts.charset;
+ }
+
+ // L.later(0, q, _next, id);
+ setTimeout(function() {
+ _next(id);
+ }, 0);
+
+ return {
+ tId: id
+ };
+ },
+
+ /**
+ * Detects when a node has been loaded. In the case of
+ * script nodes, this does not guarantee that contained
+ * script is ready to use.
+ * @method _track
+ * @param type {string} the type of node to track
+ * @param n {HTMLElement} the node to track
+ * @param id {string} the id of the request
+ * @param url {string} the url that is being loaded
+ * @param win {Window} the targeted window
+ * @param qlength the number of remaining items in the queue,
+ * including this one
+ * @param trackfn {Function} function to execute when finished
+ * the default is _next
+ * @private
+ */
+ _track = function(type, n, id, url, win, qlength, trackfn) {
+ var f = trackfn || _next;
+
+ // IE supports the readystatechange event for script and css nodes
+ // Opera only for script nodes. Opera support onload for script
+ // nodes, but this doesn't fire when there is a load failure.
+ // The onreadystatechange appears to be a better way to respond
+ // to both success and failure.
+ if (ua.ie) {
+ n.onreadystatechange = function() {
+ var rs = this.readyState;
+ if ("loaded" === rs || "complete" === rs) {
+ Y.log(id + " onreadstatechange " + url, "info", "get");
+ n.onreadystatechange = null;
+ f(id, url);
+ }
+ };
+
+ // webkit prior to 3.x is no longer supported
+ } else if (ua.webkit) {
+
+ if (type === "script") {
+ // Safari 3.x supports the load event for script nodes (DOM2)
+ n.addEventListener("load", function() {
+ Y.log(id + " DOM2 onload " + url, "info", "get");
+ f(id, url);
+ });
+ }
+
+ // FireFox and Opera support onload (but not DOM2 in FF) handlers for
+ // script nodes. Opera, but not FF, supports the onload event for link
+ // nodes.
+ } else {
+
+ n.onload = function() {
+ Y.log(id + " onload " + url, "info", "get");
+ f(id, url);
+ };
+
+ n.onerror = function(e) {
+ _fail(id, e + ": " + url);
+ };
+ }
+ };
+
+ return {
+
+ /**
+ * The number of request required before an automatic purge.
+ * Can be configured via the 'purgethreshold' config
+ * property PURGE_THRESH
+ * @static
+ * @type int
+ * @default 20
+ * @private
+ */
+ PURGE_THRESH: 20,
+
+ /**
+ * Called by the the helper for detecting script load in Safari
+ * @method _finalize
+ * @static
+ * @param id {string} the transaction id
+ * @private
+ */
+ _finalize: function(id) {
+ Y.log(id + " finalized ", "info", "get");
+ // L.later(0, null, _finish, id);
+ setTimeout(function() {
+ _finish(id);
+ }, 0);
+ },
+
+ /**
+ * Abort a transaction
+ * @method abort
+ * @static
+ * @param o {string|object} Either the tId or the object returned from
+ * script() or css()
+ */
+ abort: function(o) {
+ var id = (L.isString(o)) ? o : o.tId,
+ q = queues[id];
+ if (q) {
+ Y.log("Aborting " + id, "info", "get");
+ q.aborted = true;
+ }
+ },
+
+ /**
+ * Fetches and inserts one or more script nodes into the head
+ * of the current document or the document in a specified window.
+ *
+ * @method script
+ * @static
+ * @param url {string|string[]} the url or urls to the script(s)
+ * @param opts {object} Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the script(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onTimeout</dt>
+ * <dd>
+ * callback to execute when a timeout occurs.
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onEnd</dt>
+ * <dd>a function that executes when the transaction finishes, regardless of the exit path</dd>
+ * <dt>onFailure</dt>
+ * <dd>
+ * callback to execute when the script load operation fails
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted successfully</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove any nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>autopurge</dt>
+ * <dd>
+ * setting to true will let the utilities cleanup routine purge
+ * the script once loaded
+ * </dd>
+ * <dt>purgethreshold</dt>
+ * <dd>
+ * The number of transaction before autopurge should be initiated
+ * </dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callback when the script(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * </dl>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * <dt>timeout</dt>
+ * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd>
+ * <pre>
+ * Y.Get.script(
+ * ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
+ * "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"], {
+ * onSuccess: function(o) {
+ * this.log("won't cause error because Y is the context");
+ * Y.log(o.data); // foo
+ * Y.log(o.nodes.length === 2) // true
+ * // o.purge(); // optionally remove the script nodes immediately
+ * },
+ * onFailure: function(o) {
+ * Y.log("transaction failed");
+ * },
+ * onTimeout: function(o) {
+ * Y.log("transaction timed out");
+ * },
+ * data: "foo",
+ * timeout: 10000, // 10 second timeout
+ * context: Y, // make the YUI instance
+ * // win: otherframe // target another window/frame
+ * autopurge: true // allow the utility to choose when to remove the nodes
+ * purgetheshold: 1 // purge previous transaction before next transaction
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ script: function(url, opts) {
+ return _queue("script", url, opts);
+ },
+
+ /**
+ * Fetches and inserts one or more css link nodes into the
+ * head of the current document or the document in a specified
+ * window.
+ * @method css
+ * @static
+ * @param url {string} the url or urls to the css file(s)
+ * @param opts Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the css file(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>win</dl>
+ * <dd>the window the link nodes(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callbacks when the nodes(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * </dl>
+ * <pre>
+ * Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+ * </pre>
+ * <pre>
+ * Y.Get.css(
+ * ["http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css",
+ * "http://yui.yahooapis.com/2.3.1/build/logger/assets/skins/sam/logger.css"], {
+ * insertBefore: 'custom-styles' // nodes will be inserted before the specified node
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ css: function(url, opts) {
+ return _queue("css", url, opts);
+ }
+ };
+}();
+
+})();
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("get",function(A){(function(){var C=A.UA,B=A.Lang,E="text/javascript",F="text/css",D="stylesheet";A.Get=function(){var M={},K=0,U=false,W=function(a,X,b){var Y=b||A.config.win,c=Y.document,e=c.createElement(a),Z;for(Z in X){if(X[Z]&&X.hasOwnProperty(Z)){e.setAttribute(Z,X[Z]);}}return e;},T=function(Y,Z,X){var a={id:A.guid(),type:F,rel:D,href:Y};if(X){A.mix(a,X);}return W("link",a,Z);},S=function(Y,Z,X){var a={id:A.guid(),type:E,src:Y};if(X){A.mix(a,X);}return W("script",a,Z);},N=function(c){var X=M[c],Y,a,g,e,j,b,Z,f;if(X){Y=X.nodes;a=Y.length;g=X.win.document;e=g.getElementsByTagName("head")[0];if(X.insertBefore){j=L(X.insertBefore,c);if(j){e=j.parentNode;}}for(b=0;b<a;b=b+1){Z=Y[b];if(Z.clearAttributes){Z.clearAttributes();}else{for(f in Z){delete Z[f];}}e.removeChild(Z);}}X.nodes=[];},P=function(Y,Z,X){return{tId:Y.tId,win:Y.win,data:Y.data,nodes:Y.nodes,msg:Z,statusText:X,purge:function(){N(this.tId);}};},O=function(b,a,X){var Y=M[b],Z;if(Y&&Y.onEnd){Z=Y.context||Y;Y.onEnd.call(Z,P(Y,a,X));}},V=function(a,Z){var X=M[a],Y;if(X.timer){clearTimeout(X.timer);}if(X.onFailure){Y=X.context||X;X.onFailure.call(Y,P(X,Z));}O(a,Z,"failure");},L=function(X,a){var Y=M[a],Z=(B.isString(X))?Y.win.document.getElementById(X):X;if(!Z){V(a,"target node not found: "+X);}return Z;},I=function(a){var X=M[a],Z,Y;if(X.timer){clearTimeout(X.timer);}X.finished=true;if(X.aborted){Z="transaction "+a+" was aborted";V(a,Z);return;}if(X.onSuccess){Y=X.context||X;X.onSuccess.call(Y,P(X));}O(a,Z,"OK");},Q=function(Z){var X=M[Z],Y;if(X.onTimeout){Y=X.context||X;X.onTimeout.call(Y,P(X));}O(Z,"timeout","timeout");},H=function(Z,c){var Y=M[Z],b,g,f,e,a,X,i;if(Y.timer){clearTimeout(Y.timer);}if(Y.aborted){b="transaction "+Z+" was aborted";V(Z,b);return;}if(c){Y.url.shift();if(Y.varName){Y.varName.shift();}}else{Y.url=(B.isString(Y.url))?[Y.url]:Y.url;if(Y.varName){Y.varName=(B.isString(Y.varName))?[Y.varName]:Y.varName;}}g=Y.win;f=g.document;e=f.getElementsByTagName("head")[0];if(Y.url.length===0){I(Z);return;}X=Y.url[0];if(!X){Y.url.shift();return H(Z);}if(Y.timeout){Y.timer=setTimeout(function(){Q(Z);},Y.timeout);}if(Y.type==="script"){a=S(X,g,Y.attributes);}else{a=T(X,g,Y.attributes);}J(Y.type,a,Z,X,g,Y.url.length);Y.nodes.push(a);if(Y.insertBefore){i=L(Y.insertBefore,Z);if(i){i.parentNode.insertBefore(a,i);}}else{e.appendChild(a);}if((C.webkit||C.gecko)&&Y.type==="css"){H(Z,X);}},G=function(){if(U){return;}U=true;var X,Y;for(X in M){if(M.hasOwnProperty(X)){Y=M[X];if(Y.autopurge&&Y.finished){N(Y.tId);delete M[X];}}}U=false;},R=function(Y,X,Z){Z=Z||{};var c="q"+(K++),a,b=Z.purgethreshold||A.Get.PURGE_THRESH;if(K%b===0){G();}M[c]=A.merge(Z,{tId:c,type:Y,url:X,finished:false,nodes:[]});a=M[c];a.win=a.win||A.config.win;a.context=a.context||a;a.autopurge=("autopurge" in a)?a.autopurge:(Y==="script")?true:false;if(Z.charset){a.attributes=a.attributes||{};a.attributes.charset=Z.charset;}setTimeout(function(){H(c);},0);return{tId:c};},J=function(Z,e,d,Y,c,b,X){var a=X||H;if(C.ie){e.onreadystatechange=function(){var f=this.readyState;if("loaded"===f||"complete"===f){e.onreadystatechange=null;a(d,Y);}};}else{if(C.webkit){if(Z==="script"){e.addEventListener("load",function(){a(d,Y);});}}else{e.onload=function(){a(d,Y);};e.onerror=function(f){V(d,f+": "+Y);};}}};return{PURGE_THRESH:20,_finalize:function(X){setTimeout(function(){I(X);},0);},abort:function(Y){var Z=(B.isString(Y))?Y:Y.tId,X=M[Z];if(X){X.aborted=true;}},script:function(X,Y){return R("script",X,Y);},css:function(X,Y){return R("css",X,Y);}};}();})();},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('get', function(Y) {
+
+(function() {
+
+/**
+ * Provides a mechanism to fetch remote resources and
+ * insert them into a document.
+ * @module yui
+ * @submodule get
+ */
+
+var ua = Y.UA,
+ L = Y.Lang,
+ // PREFIX = Y.guid(),
+ TYPE_JS = "text/javascript",
+ TYPE_CSS = "text/css",
+ STYLESHEET = "stylesheet";
+
+/**
+ * Fetches and inserts one or more script or link nodes into the document
+ * @class Get
+ * @static
+ */
+Y.Get = function() {
+
+ /**
+ * hash of queues to manage multiple requests
+ * @property queues
+ * @private
+ */
+ var queues={},
+
+ /**
+ * queue index used to generate transaction ids
+ * @property qidx
+ * @type int
+ * @private
+ */
+ qidx=0,
+
+ /**
+ * interal property used to prevent multiple simultaneous purge
+ * processes
+ * @property purging
+ * @type boolean
+ * @private
+ */
+ purging=false,
+
+
+ /**
+ * Generates an HTML element, this is not appended to a document
+ * @method _node
+ * @param type {string} the type of element
+ * @param attr {string} the attributes
+ * @param win {Window} optional window to create the element in
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _node = function(type, attr, win) {
+ var w = win || Y.config.win, d=w.document, n=d.createElement(type),
+ i;
+
+ for (i in attr) {
+ if (attr[i] && attr.hasOwnProperty(i)) {
+ n.setAttribute(i, attr[i]);
+ }
+ }
+
+ return n;
+ },
+
+ /**
+ * Generates a link node
+ * @method _linkNode
+ * @param url {string} the url for the css file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _linkNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_CSS,
+ rel: STYLESHEET,
+ href: url
+ };
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+ return _node("link", o, win);
+ },
+
+ /**
+ * Generates a script node
+ * @method _scriptNode
+ * @param url {string} the url for the script file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _scriptNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_JS,
+ src: url
+ };
+
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+
+ return _node("script", o, win);
+ },
+
+ /**
+ * Removes the nodes for the specified queue
+ * @method _purge
+ * @private
+ */
+ _purge = function(tId) {
+ var q=queues[tId], n, l, d, h, s, i, node, attr;
+ if (q) {
+ n = q.nodes;
+ l = n.length;
+ d = q.win.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, tId);
+ if (s) {
+ h = s.parentNode;
+ }
+ }
+
+ for (i=0; i<l; i=i+1) {
+ node = n[i];
+ if (node.clearAttributes) {
+ node.clearAttributes();
+ } else {
+ // This is a hostile delete
+ // operation attempting to improve
+ // memory performance. As such, the
+ // hasOwnProperty check is intentionally
+ // ommitted.
+ for (attr in node) {
+ delete node[attr];
+ }
+ }
+
+ h.removeChild(node);
+ }
+ }
+ q.nodes = [];
+ },
+
+ /**
+ * Returns the data payload for callback functions
+ * @method _returnData
+ * @private
+ */
+ _returnData = function(q, msg, result) {
+ return {
+ tId: q.tId,
+ win: q.win,
+ data: q.data,
+ nodes: q.nodes,
+ msg: msg,
+ statusText: result,
+ purge: function() {
+ _purge(this.tId);
+ }
+ };
+ },
+
+ /**
+ * The transaction is finished
+ * @method _end
+ * @param id {string} the id of the request
+ * @private
+ */
+ _end = function(id, msg, result) {
+ var q = queues[id], sc;
+ if (q && q.onEnd) {
+ sc = q.context || q;
+ q.onEnd.call(sc, _returnData(q, msg, result));
+ }
+ },
+
+ /*
+ * The request failed, execute fail handler with whatever
+ * was accomplished. There isn't a failure case at the
+ * moment unless you count aborted transactions
+ * @method _fail
+ * @param id {string} the id of the request
+ * @private
+ */
+ _fail = function(id, msg) {
+
+
+ var q = queues[id], sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ // execute failure callback
+ if (q.onFailure) {
+ sc = q.context || q;
+ q.onFailure.call(sc, _returnData(q, msg));
+ }
+
+ _end(id, msg, 'failure');
+ },
+
+ _get = function(nId, tId) {
+ var q = queues[tId],
+ n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
+ if (!n) {
+ _fail(tId, "target node not found: " + nId);
+ }
+
+ return n;
+ },
+
+ /**
+ * The request is complete, so executing the requester's callback
+ * @method _finish
+ * @param id {string} the id of the request
+ * @private
+ */
+ _finish = function(id) {
+ var q = queues[id], msg, sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+ q.finished = true;
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ // execute success callback
+ if (q.onSuccess) {
+ sc = q.context || q;
+ q.onSuccess.call(sc, _returnData(q));
+ }
+
+ _end(id, msg, 'OK');
+ },
+
+ /**
+ * Timeout detected
+ * @method _timeout
+ * @param id {string} the id of the request
+ * @private
+ */
+ _timeout = function(id) {
+ var q = queues[id], sc;
+ if (q.onTimeout) {
+ sc = q.context || q;
+ q.onTimeout.call(sc, _returnData(q));
+ }
+
+ _end(id, 'timeout', 'timeout');
+ },
+
+
+ /**
+ * Loads the next item for a given request
+ * @method _next
+ * @param id {string} the id of the request
+ * @param loaded {string} the url that was just loaded, if any
+ * @private
+ */
+ _next = function(id, loaded) {
+
+
+ var q = queues[id], msg, w, d, h, n, url, s;
+
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ if (loaded) {
+ q.url.shift();
+ if (q.varName) {
+ q.varName.shift();
+ }
+ } else {
+ // This is the first pass: make sure the url is an array
+ q.url = (L.isString(q.url)) ? [q.url] : q.url;
+ if (q.varName) {
+ q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
+ }
+ }
+
+ w = q.win;
+ d = w.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.url.length === 0) {
+ _finish(id);
+ return;
+ }
+
+ url = q.url[0];
+
+ // if the url is undefined, this is probably a trailing comma problem in IE
+ if (!url) {
+ q.url.shift();
+ return _next(id);
+ }
+
+
+ if (q.timeout) {
+ // q.timer = L.later(q.timeout, q, _timeout, id);
+ q.timer = setTimeout(function() {
+ _timeout(id);
+ }, q.timeout);
+ }
+
+ if (q.type === "script") {
+ n = _scriptNode(url, w, q.attributes);
+ } else {
+ n = _linkNode(url, w, q.attributes);
+ }
+
+ // track this node's load progress
+ _track(q.type, n, id, url, w, q.url.length);
+
+ // add the node to the queue so we can return it to the user supplied callback
+ q.nodes.push(n);
+
+ // add it to the head or insert it before 'insertBefore'
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, id);
+ if (s) {
+ s.parentNode.insertBefore(n, s);
+ }
+ } else {
+ h.appendChild(n);
+ }
+
+
+ // FireFox does not support the onload event for link nodes, so there is
+ // no way to make the css requests synchronous. This means that the css
+ // rules in multiple files could be applied out of order in this browser
+ // if a later request returns before an earlier one. Safari too.
+ if ((ua.webkit || ua.gecko) && q.type === "css") {
+ _next(id, url);
+ }
+ },
+
+ /**
+ * Removes processed queues and corresponding nodes
+ * @method _autoPurge
+ * @private
+ */
+ _autoPurge = function() {
+
+ if (purging) {
+ return;
+ }
+
+ purging = true;
+
+ var i, q;
+
+ for (i in queues) {
+ if (queues.hasOwnProperty(i)) {
+ q = queues[i];
+ if (q.autopurge && q.finished) {
+ _purge(q.tId);
+ delete queues[i];
+ }
+ }
+ }
+
+ purging = false;
+ },
+
+ /**
+ * Saves the state for the request and begins loading
+ * the requested urls
+ * @method queue
+ * @param type {string} the type of node to insert
+ * @param url {string} the url to load
+ * @param opts the hash of options for this request
+ * @private
+ */
+ _queue = function(type, url, opts) {
+
+ opts = opts || {};
+
+ var id = "q" + (qidx++), q,
+ thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
+
+ if (qidx % thresh === 0) {
+ _autoPurge();
+ }
+
+ queues[id] = Y.merge(opts, {
+ tId: id,
+ type: type,
+ url: url,
+ finished: false,
+ nodes: []
+ });
+
+ q = queues[id];
+ q.win = q.win || Y.config.win;
+ q.context = q.context || q;
+ q.autopurge = ("autopurge" in q) ? q.autopurge :
+ (type === "script") ? true : false;
+
+ if (opts.charset) {
+ q.attributes = q.attributes || {};
+ q.attributes.charset = opts.charset;
+ }
+
+ // L.later(0, q, _next, id);
+ setTimeout(function() {
+ _next(id);
+ }, 0);
+
+ return {
+ tId: id
+ };
+ },
+
+ /**
+ * Detects when a node has been loaded. In the case of
+ * script nodes, this does not guarantee that contained
+ * script is ready to use.
+ * @method _track
+ * @param type {string} the type of node to track
+ * @param n {HTMLElement} the node to track
+ * @param id {string} the id of the request
+ * @param url {string} the url that is being loaded
+ * @param win {Window} the targeted window
+ * @param qlength the number of remaining items in the queue,
+ * including this one
+ * @param trackfn {Function} function to execute when finished
+ * the default is _next
+ * @private
+ */
+ _track = function(type, n, id, url, win, qlength, trackfn) {
+ var f = trackfn || _next;
+
+ // IE supports the readystatechange event for script and css nodes
+ // Opera only for script nodes. Opera support onload for script
+ // nodes, but this doesn't fire when there is a load failure.
+ // The onreadystatechange appears to be a better way to respond
+ // to both success and failure.
+ if (ua.ie) {
+ n.onreadystatechange = function() {
+ var rs = this.readyState;
+ if ("loaded" === rs || "complete" === rs) {
+ n.onreadystatechange = null;
+ f(id, url);
+ }
+ };
+
+ // webkit prior to 3.x is no longer supported
+ } else if (ua.webkit) {
+
+ if (type === "script") {
+ // Safari 3.x supports the load event for script nodes (DOM2)
+ n.addEventListener("load", function() {
+ f(id, url);
+ });
+ }
+
+ // FireFox and Opera support onload (but not DOM2 in FF) handlers for
+ // script nodes. Opera, but not FF, supports the onload event for link
+ // nodes.
+ } else {
+
+ n.onload = function() {
+ f(id, url);
+ };
+
+ n.onerror = function(e) {
+ _fail(id, e + ": " + url);
+ };
+ }
+ };
+
+ return {
+
+ /**
+ * The number of request required before an automatic purge.
+ * Can be configured via the 'purgethreshold' config
+ * property PURGE_THRESH
+ * @static
+ * @type int
+ * @default 20
+ * @private
+ */
+ PURGE_THRESH: 20,
+
+ /**
+ * Called by the the helper for detecting script load in Safari
+ * @method _finalize
+ * @static
+ * @param id {string} the transaction id
+ * @private
+ */
+ _finalize: function(id) {
+ // L.later(0, null, _finish, id);
+ setTimeout(function() {
+ _finish(id);
+ }, 0);
+ },
+
+ /**
+ * Abort a transaction
+ * @method abort
+ * @static
+ * @param o {string|object} Either the tId or the object returned from
+ * script() or css()
+ */
+ abort: function(o) {
+ var id = (L.isString(o)) ? o : o.tId,
+ q = queues[id];
+ if (q) {
+ q.aborted = true;
+ }
+ },
+
+ /**
+ * Fetches and inserts one or more script nodes into the head
+ * of the current document or the document in a specified window.
+ *
+ * @method script
+ * @static
+ * @param url {string|string[]} the url or urls to the script(s)
+ * @param opts {object} Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the script(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onTimeout</dt>
+ * <dd>
+ * callback to execute when a timeout occurs.
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onEnd</dt>
+ * <dd>a function that executes when the transaction finishes, regardless of the exit path</dd>
+ * <dt>onFailure</dt>
+ * <dd>
+ * callback to execute when the script load operation fails
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted successfully</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove any nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>autopurge</dt>
+ * <dd>
+ * setting to true will let the utilities cleanup routine purge
+ * the script once loaded
+ * </dd>
+ * <dt>purgethreshold</dt>
+ * <dd>
+ * The number of transaction before autopurge should be initiated
+ * </dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callback when the script(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * </dl>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * <dt>timeout</dt>
+ * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd>
+ * <pre>
+ * Y.Get.script(
+ * ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
+ * "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"], {
+ * onSuccess: function(o) {
+ * this.log("won't cause error because Y is the context");
+ * },
+ * onFailure: function(o) {
+ * },
+ * onTimeout: function(o) {
+ * },
+ * data: "foo",
+ * timeout: 10000, // 10 second timeout
+ * context: Y, // make the YUI instance
+ * // win: otherframe // target another window/frame
+ * autopurge: true // allow the utility to choose when to remove the nodes
+ * purgetheshold: 1 // purge previous transaction before next transaction
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ script: function(url, opts) {
+ return _queue("script", url, opts);
+ },
+
+ /**
+ * Fetches and inserts one or more css link nodes into the
+ * head of the current document or the document in a specified
+ * window.
+ * @method css
+ * @static
+ * @param url {string} the url or urls to the css file(s)
+ * @param opts Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the css file(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>win</dl>
+ * <dd>the window the link nodes(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callbacks when the nodes(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * </dl>
+ * <pre>
+ * Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+ * </pre>
+ * <pre>
+ * Y.Get.css(
+ * ["http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css",
+ * insertBefore: 'custom-styles' // nodes will be inserted before the specified node
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ css: function(url, opts) {
+ return _queue("css", url, opts);
+ }
+ };
+}();
+
+})();
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('history', function(Y) {
+
+/*global YUI */
+
+
+/**
+ * The Browser History Utility provides the ability to use the back/forward
+ * navigation buttons in a DHTML application. It also allows a DHTML
+ * application to be bookmarked in a specific state.
+ *
+ * This utility requires the following static markup:
+ *
+ * <iframe id="yui-history-iframe" src="path-to-real-asset-in-same-domain"></iframe>
+ * <input id="yui-history-field" type="hidden">
+ *
+ * @module history
+ */
+
+/**
+ * This class represents an instance of the browser history utility.
+ * @class History
+ * @constructor
+ */
+
+ // Shortcuts, etc.
+ var win = Y.config.win,
+ doc = Y.config.doc,
+
+ encode = encodeURIComponent,
+ decode = decodeURIComponent,
+
+ H, G,
+
+ // YUI Compressor helper...
+ E_MISSING_OR_INVALID_ARG = 'Missing or invalid argument',
+
+ // Regular expression used to parse query strings and such.
+ REGEXP = /([^=&]+)=([^&]*)/g,
+
+ // A few private variables...
+ _useIFrame = false,
+ _getHash,
+
+ /**
+ * @event history:ready
+ * @description Fires when the browser history utility is ready
+ * @type Event.Custom
+ */
+ EV_HISTORY_READY = 'history:ready',
+
+ /**
+ * @event history:globalStateChange
+ * @description Fires when the global state of the page has changed (that is,
+ * when the state of at least one browser history module has changed)
+ * @type Event.Custom
+ */
+ EV_HISTORY_GLOBAL_STATE_CHANGE = 'history:globalStateChange',
+
+ /**
+ * @event history:moduleStateChange
+ * @description Fires when the state of a history module object has changed
+ * @type Event.Custom
+ */
+ EV_HISTORY_MODULE_STATE_CHANGE = 'history:moduleStateChange';
+
+
+ if (!YUI.Env.history) {
+
+ YUI.Env.history = G = {
+
+ // Flag used to tell whether the history utility is ready to be used.
+ ready: false,
+
+ // List of registered modules.
+ _modules: [],
+
+ // INPUT field (with type="hidden" or type="text") or TEXTAREA.
+ // This field keeps the value of the initial state, current state
+ // the list of all states across pages within a single browser session.
+ _stateField: null,
+
+ // Hidden IFrame used to store the browsing history on IE6/7.
+ _historyIFrame: null
+ };
+
+ }
+
+ /**
+ * Returns the portion of the hash after the '#' symbol.
+ * @method _getHash
+ * @return {string} The hash portion of the document's location
+ * @private
+ */
+ if (Y.UA.gecko) {
+ // We branch at runtime for Gecko since window.location.hash in Gecko
+ // returns a decoded string, and we want all encoding untouched.
+ _getHash = function () {
+ var m = /#(.*)$/.exec(win.location.href);
+ return m && m[1] ? m[1] : '';
+ };
+ } else {
+ _getHash = function () {
+ return win.location.hash.substr(1);
+ };
+ }
+
+ /**
+ * Stores the initial state and current state for all registered modules
+ * in the (hidden) form field specified during initialization.
+ * @method _storeStates
+ * @private
+ */
+ function _storeStates() {
+ var initialStates = [], currentStates = [];
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ initialStates.push(moduleId + '=' + module.initialState);
+ currentStates.push(moduleId + '=' + module.currentState);
+ });
+
+ G._stateField.set('value', initialStates.join('&') + '|' + currentStates.join('&'));
+ }
+
+ /**
+ * Sets the new currentState attribute of all modules depending on the new fully
+ * qualified state. Also notifies the modules which current state has changed.
+ * @method _handleFQStateChange
+ * @param {string} fqstate fully qualified state
+ * @private
+ */
+ function _handleFQStateChange(fqstate) {
+ var m, states = [], globalStateChanged = false;
+
+ if (fqstate) {
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(fqstate))) {
+ states[m[1]] = m[2];
+ }
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ var currentState = states[moduleId];
+
+ if (!currentState || module.currentState !== currentState) {
+ module.currentState = currentState || module.initialState;
+ module.fire(EV_HISTORY_MODULE_STATE_CHANGE, decode(module.currentState));
+ globalStateChanged = true;
+ }
+ });
+
+ } else {
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ if (module.currentState !== module.initialState) {
+ module.currentState = module.initialState;
+ module.fire(EV_HISTORY_MODULE_STATE_CHANGE, decode(module.currentState));
+ globalStateChanged = true;
+ }
+ });
+ }
+
+ if (globalStateChanged) {
+ H.fire(EV_HISTORY_GLOBAL_STATE_CHANGE);
+ }
+ }
+
+ /**
+ * Update the IFrame with our new state.
+ * @method _updateIFrame
+ * @private
+ * @return {boolean} true if successful. false otherwise.
+ */
+ function _updateIFrame(fqstate) {
+ var html, doc;
+
+ html = '<html><body>' + fqstate + '</body></html>';
+
+ try {
+ doc = G._historyIFrame.get('contentWindow.document');
+ // TODO: The Node API should expose these methods in the very near future...
+ doc.invoke('open');
+ doc.invoke('write', html, '', '', '', ''); // see bug #2447937
+ doc.invoke('close');
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ /**
+ * Periodically checks whether our internal IFrame is ready to be used
+ * @method _checkIframeLoaded
+ * @private
+ */
+ function _checkIframeLoaded() {
+ var elem, fqstate, hash;
+
+ if (!G._historyIFrame.get('contentWindow.document')) {
+ // Check again in 10 msec...
+ setTimeout(_checkIframeLoaded, 10);
+ return;
+ }
+
+ // Periodically check whether a navigate operation has been
+ // requested on the main window. This will happen when
+ // History.navigate has been called or after the user
+ // has hit the back/forward button.
+ elem = G._historyIFrame.get('contentWindow.document.body');
+ // We must use innerText, and not innerHTML because our string contains
+ // the "&" character (which would end up being escaped as "&") and
+ // the string comparison would fail...
+ fqstate = elem ? elem.get('innerText') : null;
+
+ hash = _getHash();
+
+ setInterval(function () {
+ var newfqstate, states, newHash;
+
+ elem = G._historyIFrame.get('contentWindow.document.body');
+ // See my comment above about using innerText instead of innerHTML...
+ newfqstate = elem ? elem.get('innerText') : null;
+
+ newHash = _getHash();
+
+ if (newfqstate !== fqstate) {
+
+ fqstate = newfqstate;
+ _handleFQStateChange(fqstate);
+
+ if (!fqstate) {
+ states = [];
+ Y.Object.each(G._modules, function (module, moduleId) {
+ states.push(moduleId + '=' + module.initialState);
+ });
+ newHash = states.join('&');
+ } else {
+ newHash = fqstate;
+ }
+
+ // Allow the state to be bookmarked by setting the top window's
+ // URL fragment identifier. Note that here, we are on IE < 8
+ // which does not touch the browser history when changing the
+ // hash (unlike all the other browsers).
+ win.location.hash = hash = newHash;
+
+ _storeStates();
+
+ } else if (newHash !== hash) {
+
+ // The hash has changed. The user might have clicked on a link,
+ // or modified the URL directly, or opened the same application
+ // bookmarked in a specific state using a bookmark. However, we
+ // know the hash change was not caused by a hit on the back or
+ // forward buttons, or by a call to navigate() (because it would
+ // have been handled above) We must handle these cases, which is
+ // why we also need to keep track of hash changes on IE!
+
+ // Note that IE6 has some major issues with this kind of user
+ // interaction (the history stack gets completely messed up)
+ // but it seems to work fine on IE7.
+
+ hash = newHash;
+
+ // Now, store a new history entry. The following will cause the
+ // code above to execute, doing all the dirty work for us...
+ _updateIFrame(newHash);
+ }
+
+ }, 50);
+
+ G.ready = true;
+ H.fire(EV_HISTORY_READY);
+ }
+
+ /**
+ * Finish up the initialization of the browser utility library.
+ * @method _initialize
+ * @private
+ */
+ function _initialize() {
+ var m, parts, moduleId, module, initialState, currentState, hash;
+
+ // Decode the content of our storage field...
+ parts = G._stateField.get('value').split('|');
+
+ if (parts.length > 1) {
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(parts[0]))) {
+ moduleId = m[1];
+ initialState = m[2];
+ module = G._modules[moduleId];
+ if (module) {
+ module.initialState = initialState;
+ }
+ }
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(parts[1]))) {
+ moduleId = m[1];
+ currentState = m[2];
+ module = G._modules[moduleId];
+ if (module) {
+ module.currentState = currentState;
+ }
+ }
+ }
+
+ // IE8 in IE7 mode defines window.onhashchange, but never fires it...
+ if (!Y.Lang.isUndefined(win.onhashchange) &&
+ (Y.Lang.isUndefined(doc.documentMode) || doc.documentMode > 7)) {
+
+ // The HTML5 way of handling DHTML history...
+ win.onhashchange = function () {
+ var hash = _getHash();
+ _handleFQStateChange(hash);
+ _storeStates();
+ };
+
+ G.ready = true;
+ H.fire(EV_HISTORY_READY);
+
+ } else if (_useIFrame) {
+
+ // IE < 8 or IE8 in quirks mode or IE7 standards mode
+ _checkIframeLoaded();
+
+ } else {
+
+ // Periodically check whether a navigate operation has been
+ // requested on the main window. This will happen when
+ // History.navigate has been called, or after the user
+ // has hit the back/forward button.
+
+ // On Gecko and Opera, we just need to watch the hash...
+ hash = _getHash();
+
+ setInterval(function () {
+ var newHash = _getHash();
+ if (newHash !== hash) {
+ hash = newHash;
+ _handleFQStateChange(hash);
+ _storeStates();
+ }
+ }, 50);
+
+ G.ready = true;
+ H.fire(EV_HISTORY_READY);
+ }
+ }
+
+
+ H = {
+
+ /**
+ * Registers a new module.
+ * @method register
+ * @param {string} moduleId Non-empty string uniquely identifying the
+ * module you wish to register.
+ * @param {string} initialState The initial state of the specified
+ * module corresponding to its earliest history entry.
+ * @return {History.Module} The newly registered module
+ */
+ register: function (moduleId, initialState) {
+ var module;
+
+ if (!Y.Lang.isString(moduleId) || Y.Lang.trim(moduleId) === '' || !Y.Lang.isString(initialState)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ moduleId = encode(moduleId);
+ initialState = encode(initialState);
+
+ if (G._modules[moduleId]) {
+ // The module seems to have already been registered.
+ return;
+ }
+
+ // Note: A module CANNOT be registered once the browser history
+ // utility has been initialized. This is related to reading and
+ // writing state values from/to the input field. Relaxing this
+ // rule would potentially create situations rather complicated
+ // to deal with.
+ if (G.ready) {
+ return null;
+ }
+
+ module = new H.Module(moduleId, initialState);
+ G._modules[moduleId] = module;
+ return module;
+ },
+
+ /**
+ * Initializes the Browser History Manager. Call this method
+ * from a script block located right after the opening body tag.
+ * @method initialize
+ * @param {string|HTML Element} stateField <input type="hidden"> used
+ * to store application states. Must be in the static markup.
+ * @param {string|HTML Element} historyIFrame IFrame used to store
+ * the history (only required for IE6/7)
+ * @public
+ */
+ initialize: function (stateField, historyIFrame) {
+ var tagName, type;
+
+ if (G.ready) {
+ // The browser history utility has already been initialized.
+ return true;
+ }
+
+ stateField = Y.get(stateField);
+ if (!stateField) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ tagName = stateField.get('tagName').toUpperCase();
+ type = stateField.get('type');
+
+ if (tagName !== 'TEXTAREA' && (tagName !== 'INPUT' || type !== 'hidden' && type !== 'text')) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ // IE < 8 or IE8 in quirks mode or IE7 standards mode
+ if (Y.UA.ie && (Y.Lang.isUndefined(doc.documentMode) || doc.documentMode < 8)) {
+ _useIFrame = true;
+ historyIFrame = Y.get(historyIFrame);
+ if (!historyIFrame || historyIFrame.get('tagName').toUpperCase() !== 'IFRAME') {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+ }
+
+ if (Y.UA.opera && !Y.Lang.isUndefined(win.history.navigationMode)) {
+ // Disable Opera's fast back/forward navigation mode and put
+ // it in compatible mode. This makes anchor-based history
+ // navigation work after the page has been navigated away
+ // from and re-activated, at the cost of slowing down
+ // back/forward navigation to and from that page.
+ win.history.navigationMode = 'compatible';
+ }
+
+ G._stateField = stateField;
+ G._historyIFrame = historyIFrame;
+
+ Y.on('domready', _initialize);
+ return true;
+ },
+
+ /**
+ * Stores a new entry in the browser history by changing the state of a registered module.
+ * @method navigate
+ * @param {string} module Non-empty string representing your module.
+ * @param {string} state String representing the new state of the specified module.
+ * @return {boolean} Indicates whether the new state was successfully added to the history.
+ * @public
+ */
+ navigate: function (moduleId, state) {
+ var states;
+
+ if (!Y.Lang.isString(moduleId) || !Y.Lang.isString(state)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ // The ncoding of module id and state takes place in mutiNavigate.
+ states = {};
+ states[moduleId] = state;
+
+ return H.multiNavigate(states);
+ },
+
+ /**
+ * Stores a new entry in the browser history by changing the state
+ * of several registered modules in one atomic operation.
+ * @method multiNavigate
+ * @param {object} states Associative array of module-state pairs to set simultaneously.
+ * @return {boolean} Indicates whether the new state was successfully added to the history.
+ * @public
+ */
+ multiNavigate: function (states) {
+ var newStates = [], fqstate, globalStateChanged = false;
+
+ if (!G.ready) {
+ return false;
+ }
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ var state, decodedModuleId = decode(moduleId);
+
+ if (!states.hasOwnProperty(decodedModuleId)) {
+ // The caller did not wish to modify the state of this
+ // module. We must however include it in fqstate!
+ state = module.currentState;
+ } else {
+ state = encode(states[decodedModuleId]);
+ if (state !== module.upcomingState) {
+ module.upcomingState = state;
+ globalStateChanged = true;
+ }
+ }
+
+ newStates.push(moduleId + '=' + state);
+ });
+
+ if (!globalStateChanged) {
+ // Nothing changed, so don't do anything.
+ return false;
+ }
+
+ fqstate = newStates.join('&');
+
+ if (_useIFrame) {
+ return _updateIFrame(fqstate);
+ } else {
+ win.location.hash = fqstate;
+ return true;
+ }
+ },
+
+ /**
+ * Returns the current state of the specified module.
+ * @method getCurrentState
+ * @param {string} moduleId Non-empty string representing your module.
+ * @return {string} The current state of the specified module.
+ * @public
+ */
+ getCurrentState: function (moduleId) {
+ var module;
+
+ if (!Y.Lang.isString(moduleId)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ if (!G.ready) {
+ return null;
+ }
+
+ moduleId = encode(moduleId);
+ module = G._modules[moduleId];
+ if (!module) {
+ return null;
+ }
+
+ return decode(module.currentState);
+ },
+
+ /**
+ * Returns the state of a module according to the URL fragment
+ * identifier. This method is useful to initialize your modules
+ * if your application was bookmarked from a particular state.
+ * @method getBookmarkedState
+ * @param {string} moduleId Non-empty string representing your module.
+ * @return {string} The bookmarked state of the specified module.
+ * @public
+ */
+ getBookmarkedState: function (moduleId) {
+ var m, i, h;
+
+ if (!Y.Lang.isString(moduleId)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ moduleId = encode(moduleId);
+
+ // Use location.href instead of location.hash which is already
+ // URL-decoded, which creates problems if the state value
+ // contained special characters...
+ h = win.location.href;
+ i = h.indexOf('#');
+
+ if (i >= 0) {
+ h = h.substr(i + 1);
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(h))) {
+ if (m[1] === moduleId) {
+ return decode(m[2]);
+ }
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Returns the value of the specified query string parameter.
+ * This method is not used internally by the Browser History Manager.
+ * However, it is provided here as a helper since many applications
+ * using the Browser History Manager will want to read the value of
+ * url parameters to initialize themselves.
+ * @method getQueryStringParameter
+ * @param {string} paramName Name of the parameter we want to look up.
+ * @param {string} queryString Optional URL to look at. If not specified,
+ * this method uses the URL in the address bar.
+ * @return {string} The value of the specified parameter, or null.
+ * @public
+ */
+ getQueryStringParameter: function (paramName, url) {
+ var m, q, i;
+
+ url = url || win.location.href;
+
+ i = url.indexOf('?');
+ q = i >= 0 ? url.substr(i + 1) : url;
+
+ // Remove the hash if any
+ i = q.lastIndexOf('#');
+ q = i >= 0 ? q.substr(0, i) : q;
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(q))) {
+ if (m[1] === paramName) {
+ return decode(m[2]);
+ }
+ }
+
+ return null;
+ }
+ };
+
+
+ // Make Y.History an event target
+ Y.mix(H, Y.Event.Target.prototype);
+ Y.Event.Target.call(H);
+
+
+ /**
+ * This class represents a browser history module.
+ * @class History.Module
+ * @constructor
+ * @param id {String} the module identifier
+ * @param initialState {String} the module's initial state
+ */
+ H.Module = function (id, initialState) {
+
+ Y.Event.Target.call(this);
+
+ /**
+ * The module identifier
+ * @type String
+ * @final
+ */
+ this.id = id;
+
+ /**
+ * The module's initial state
+ * @type String
+ * @final
+ */
+ this.initialState = initialState;
+
+ /**
+ * The module's current state
+ * @type String
+ * @final
+ */
+ this.currentState = initialState;
+
+ /**
+ * The module's upcoming state. There can be a slight delay between the
+ * time a state is changed, and the time a state change is detected.
+ * This property allows us to not fire the module state changed event
+ * multiple times, making client code simpler.
+ * @type String
+ * @private
+ * @final
+ */
+ this.upcomingState = initialState;
+ };
+
+ Y.mix(H.Module, Y.Event.Target, false, null, 1);
+
+ Y.History = H;
+
+
+}, '3.0.0' ,{skinnable:false, use:['event', 'node']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("history",function(A){var C=A.config.win,U=A.config.doc,D=encodeURIComponent,O=decodeURIComponent,J,K,R="Missing or invalid argument",M=/([^=&]+)=([^&]*)/g,B=false,N,L="history:ready",Q="history:globalStateChange",F="history:moduleStateChange";if(!YUI.Env.history){YUI.Env.history=K={ready:false,_modules:[],_stateField:null,_historyIFrame:null};}if(A.UA.gecko){N=function(){var G=/#(.*)$/.exec(C.location.href);return G&&G[1]?G[1]:"";};}else{N=function(){return C.location.hash.substr(1);};}function S(){var H=[],G=[];A.Object.each(K._modules,function(V,W){H.push(W+"="+V.initialState);G.push(W+"="+V.currentState);});K._stateField.set("value",H.join("&")+"|"+G.join("&"));}function P(W){var G,V=[],H=false;if(W){M.lastIndex=0;while((G=M.exec(W))){V[G[1]]=G[2];}A.Object.each(K._modules,function(X,Z){var Y=V[Z];if(!Y||X.currentState!==Y){X.currentState=Y||X.initialState;X.fire(F,O(X.currentState));H=true;}});}else{A.Object.each(K._modules,function(X,Y){if(X.currentState!==X.initialState){X.currentState=X.initialState;X.fire(F,O(X.currentState));H=true;}});}if(H){J.fire(Q);}}function I(W){var G,V;G="<html><body>"+W+"</body></html>";try{V=K._historyIFrame.get("contentWindow.document");V.invoke("open");V.invoke("write",G,"","","","");V.invoke("close");return true;}catch(H){return false;}}function E(){var G,V,H;if(!K._historyIFrame.get("contentWindow.document")){setTimeout(E,10);return;}G=K._historyIFrame.get("contentWindow.document.body");V=G?G.get("innerText"):null;H=N();setInterval(function(){var Y,W,X;G=K._historyIFrame.get("contentWindow.document.body");Y=G?G.get("innerText"):null;X=N();if(Y!==V){V=Y;P(V);if(!V){W=[];A.Object.each(K._modules,function(Z,a){W.push(a+"="+Z.initialState);});X=W.join("&");}else{X=V;}C.location.hash=H=X;S();}else{if(X!==H){H=X;I(X);}}},50);K.ready=true;J.fire(L);}function T(){var H,Z,X,V,G,W,Y;Z=K._stateField.get("value").split("|");if(Z.length>1){M.lastIndex=0;while((H=M.exec(Z[0]))){X=H[1];G=H[2];V=K._modules[X];if(V){V.initialState=G;}}M.lastIndex=0;while((H=M.exec(Z[1]))){X=H[1];W=H[2];V=K._modules[X];if(V){V.currentState=W;}}}if(!A.Lang.isUndefined(C.onhashchange)&&(A.Lang.isUndefined(U.documentMode)||U.documentMode>7)){C.onhashchange=function(){var a=N();P(a);S();};K.ready=true;J.fire(L);}else{if(B){E();}else{Y=N();setInterval(function(){var a=N();if(a!==Y){Y=a;P(Y);S();}},50);K.ready=true;J.fire(L);}}}J={register:function(V,G){var H;if(!A.Lang.isString(V)||A.Lang.trim(V)===""||!A.Lang.isString(G)){throw new Error(R);}V=D(V);G=D(G);if(K._modules[V]){return;}if(K.ready){return null;}H=new J.Module(V,G);K._modules[V]=H;return H;},initialize:function(G,W){var H,V;if(K.ready){return true;}G=A.get(G);if(!G){throw new Error(R);}H=G.get("tagName").toUpperCase();V=G.get("type");if(H!=="TEXTAREA"&&(H!=="INPUT"||V!=="hidden"&&V!=="text")){throw new Error(R);}if(A.UA.ie&&(A.Lang.isUndefined(U.documentMode)||U.documentMode<8)){B=true;W=A.get(W);if(!W||W.get("tagName").toUpperCase()!=="IFRAME"){throw new Error(R);}}if(A.UA.opera&&!A.Lang.isUndefined(C.history.navigationMode)){C.history.navigationMode="compatible";}K._stateField=G;K._historyIFrame=W;A.on("domready",T);return true;},navigate:function(H,V){var G;if(!A.Lang.isString(H)||!A.Lang.isString(V)){throw new Error(R);}G={};G[H]=V;return J.multiNavigate(G);},multiNavigate:function(H){var V=[],W,G=false;if(!K.ready){return false;}A.Object.each(K._modules,function(Y,Z){var a,X=O(Z);if(!H.hasOwnProperty(X)){a=Y.currentState;}else{a=D(H[X]);if(a!==Y.upcomingState){Y.upcomingState=a;G=true;}}V.push(Z+"="+a);});if(!G){return false;}W=V.join("&");if(B){return I(W);}else{C.location.hash=W;return true;}},getCurrentState:function(H){var G;if(!A.Lang.isString(H)){throw new Error(R);}if(!K.ready){return null;}H=D(H);G=K._modules[H];if(!G){return null;}return O(G.currentState);},getBookmarkedState:function(W){var G,H,V;if(!A.Lang.isString(W)){throw new Error(R);}W=D(W);V=C.location.href;H=V.indexOf("#");if(H>=0){V=V.substr(H+1);M.lastIndex=0;while((G=M.exec(V))){if(G[1]===W){return O(G[2]);}}}return null;},getQueryStringParameter:function(X,H){var G,W,V;H=H||C.location.href;V=H.indexOf("?");W=V>=0?H.substr(V+1):H;V=W.lastIndexOf("#");W=V>=0?W.substr(0,V):W;M.lastIndex=0;while((G=M.exec(W))){if(G[1]===X){return O(G[2]);}}return null;}};A.mix(J,A.Event.Target.prototype);A.Event.Target.call(J);J.Module=function(H,G){A.Event.Target.call(this);this.id=H;this.initialState=G;this.currentState=G;this.upcomingState=G;};A.mix(J.Module,A.Event.Target,false,null,1);A.History=J;},"3.0.0",{skinnable:false,use:["event","node"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('history', function(Y) {
+
+/*global YUI */
+
+
+/**
+ * The Browser History Utility provides the ability to use the back/forward
+ * navigation buttons in a DHTML application. It also allows a DHTML
+ * application to be bookmarked in a specific state.
+ *
+ * This utility requires the following static markup:
+ *
+ * <iframe id="yui-history-iframe" src="path-to-real-asset-in-same-domain"></iframe>
+ * <input id="yui-history-field" type="hidden">
+ *
+ * @module history
+ */
+
+/**
+ * This class represents an instance of the browser history utility.
+ * @class History
+ * @constructor
+ */
+
+ // Shortcuts, etc.
+ var win = Y.config.win,
+ doc = Y.config.doc,
+
+ encode = encodeURIComponent,
+ decode = decodeURIComponent,
+
+ H, G,
+
+ // YUI Compressor helper...
+ E_MISSING_OR_INVALID_ARG = 'Missing or invalid argument',
+
+ // Regular expression used to parse query strings and such.
+ REGEXP = /([^=&]+)=([^&]*)/g,
+
+ // A few private variables...
+ _useIFrame = false,
+ _getHash,
+
+ /**
+ * @event history:ready
+ * @description Fires when the browser history utility is ready
+ * @type Event.Custom
+ */
+ EV_HISTORY_READY = 'history:ready',
+
+ /**
+ * @event history:globalStateChange
+ * @description Fires when the global state of the page has changed (that is,
+ * when the state of at least one browser history module has changed)
+ * @type Event.Custom
+ */
+ EV_HISTORY_GLOBAL_STATE_CHANGE = 'history:globalStateChange',
+
+ /**
+ * @event history:moduleStateChange
+ * @description Fires when the state of a history module object has changed
+ * @type Event.Custom
+ */
+ EV_HISTORY_MODULE_STATE_CHANGE = 'history:moduleStateChange';
+
+
+ if (!YUI.Env.history) {
+
+ YUI.Env.history = G = {
+
+ // Flag used to tell whether the history utility is ready to be used.
+ ready: false,
+
+ // List of registered modules.
+ _modules: [],
+
+ // INPUT field (with type="hidden" or type="text") or TEXTAREA.
+ // This field keeps the value of the initial state, current state
+ // the list of all states across pages within a single browser session.
+ _stateField: null,
+
+ // Hidden IFrame used to store the browsing history on IE6/7.
+ _historyIFrame: null
+ };
+
+ }
+
+ /**
+ * Returns the portion of the hash after the '#' symbol.
+ * @method _getHash
+ * @return {string} The hash portion of the document's location
+ * @private
+ */
+ if (Y.UA.gecko) {
+ // We branch at runtime for Gecko since window.location.hash in Gecko
+ // returns a decoded string, and we want all encoding untouched.
+ _getHash = function () {
+ var m = /#(.*)$/.exec(win.location.href);
+ return m && m[1] ? m[1] : '';
+ };
+ } else {
+ _getHash = function () {
+ return win.location.hash.substr(1);
+ };
+ }
+
+ /**
+ * Stores the initial state and current state for all registered modules
+ * in the (hidden) form field specified during initialization.
+ * @method _storeStates
+ * @private
+ */
+ function _storeStates() {
+ var initialStates = [], currentStates = [];
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ initialStates.push(moduleId + '=' + module.initialState);
+ currentStates.push(moduleId + '=' + module.currentState);
+ });
+
+ G._stateField.set('value', initialStates.join('&') + '|' + currentStates.join('&'));
+ }
+
+ /**
+ * Sets the new currentState attribute of all modules depending on the new fully
+ * qualified state. Also notifies the modules which current state has changed.
+ * @method _handleFQStateChange
+ * @param {string} fqstate fully qualified state
+ * @private
+ */
+ function _handleFQStateChange(fqstate) {
+ var m, states = [], globalStateChanged = false;
+
+ if (fqstate) {
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(fqstate))) {
+ states[m[1]] = m[2];
+ }
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ var currentState = states[moduleId];
+
+ if (!currentState || module.currentState !== currentState) {
+ module.currentState = currentState || module.initialState;
+ module.fire(EV_HISTORY_MODULE_STATE_CHANGE, decode(module.currentState));
+ globalStateChanged = true;
+ }
+ });
+
+ } else {
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ if (module.currentState !== module.initialState) {
+ module.currentState = module.initialState;
+ module.fire(EV_HISTORY_MODULE_STATE_CHANGE, decode(module.currentState));
+ globalStateChanged = true;
+ }
+ });
+ }
+
+ if (globalStateChanged) {
+ H.fire(EV_HISTORY_GLOBAL_STATE_CHANGE);
+ }
+ }
+
+ /**
+ * Update the IFrame with our new state.
+ * @method _updateIFrame
+ * @private
+ * @return {boolean} true if successful. false otherwise.
+ */
+ function _updateIFrame(fqstate) {
+ var html, doc;
+
+ html = '<html><body>' + fqstate + '</body></html>';
+
+ try {
+ doc = G._historyIFrame.get('contentWindow.document');
+ // TODO: The Node API should expose these methods in the very near future...
+ doc.invoke('open');
+ doc.invoke('write', html, '', '', '', ''); // see bug #2447937
+ doc.invoke('close');
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ /**
+ * Periodically checks whether our internal IFrame is ready to be used
+ * @method _checkIframeLoaded
+ * @private
+ */
+ function _checkIframeLoaded() {
+ var elem, fqstate, hash;
+
+ if (!G._historyIFrame.get('contentWindow.document')) {
+ // Check again in 10 msec...
+ setTimeout(_checkIframeLoaded, 10);
+ return;
+ }
+
+ // Periodically check whether a navigate operation has been
+ // requested on the main window. This will happen when
+ // History.navigate has been called or after the user
+ // has hit the back/forward button.
+ elem = G._historyIFrame.get('contentWindow.document.body');
+ // We must use innerText, and not innerHTML because our string contains
+ // the "&" character (which would end up being escaped as "&") and
+ // the string comparison would fail...
+ fqstate = elem ? elem.get('innerText') : null;
+
+ hash = _getHash();
+
+ setInterval(function () {
+ var newfqstate, states, newHash;
+
+ elem = G._historyIFrame.get('contentWindow.document.body');
+ // See my comment above about using innerText instead of innerHTML...
+ newfqstate = elem ? elem.get('innerText') : null;
+
+ newHash = _getHash();
+
+ if (newfqstate !== fqstate) {
+
+ fqstate = newfqstate;
+ _handleFQStateChange(fqstate);
+
+ if (!fqstate) {
+ states = [];
+ Y.Object.each(G._modules, function (module, moduleId) {
+ states.push(moduleId + '=' + module.initialState);
+ });
+ newHash = states.join('&');
+ } else {
+ newHash = fqstate;
+ }
+
+ // Allow the state to be bookmarked by setting the top window's
+ // URL fragment identifier. Note that here, we are on IE < 8
+ // which does not touch the browser history when changing the
+ // hash (unlike all the other browsers).
+ win.location.hash = hash = newHash;
+
+ _storeStates();
+
+ } else if (newHash !== hash) {
+
+ // The hash has changed. The user might have clicked on a link,
+ // or modified the URL directly, or opened the same application
+ // bookmarked in a specific state using a bookmark. However, we
+ // know the hash change was not caused by a hit on the back or
+ // forward buttons, or by a call to navigate() (because it would
+ // have been handled above) We must handle these cases, which is
+ // why we also need to keep track of hash changes on IE!
+
+ // Note that IE6 has some major issues with this kind of user
+ // interaction (the history stack gets completely messed up)
+ // but it seems to work fine on IE7.
+
+ hash = newHash;
+
+ // Now, store a new history entry. The following will cause the
+ // code above to execute, doing all the dirty work for us...
+ _updateIFrame(newHash);
+ }
+
+ }, 50);
+
+ G.ready = true;
+ H.fire(EV_HISTORY_READY);
+ }
+
+ /**
+ * Finish up the initialization of the browser utility library.
+ * @method _initialize
+ * @private
+ */
+ function _initialize() {
+ var m, parts, moduleId, module, initialState, currentState, hash;
+
+ // Decode the content of our storage field...
+ parts = G._stateField.get('value').split('|');
+
+ if (parts.length > 1) {
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(parts[0]))) {
+ moduleId = m[1];
+ initialState = m[2];
+ module = G._modules[moduleId];
+ if (module) {
+ module.initialState = initialState;
+ }
+ }
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(parts[1]))) {
+ moduleId = m[1];
+ currentState = m[2];
+ module = G._modules[moduleId];
+ if (module) {
+ module.currentState = currentState;
+ }
+ }
+ }
+
+ // IE8 in IE7 mode defines window.onhashchange, but never fires it...
+ if (!Y.Lang.isUndefined(win.onhashchange) &&
+ (Y.Lang.isUndefined(doc.documentMode) || doc.documentMode > 7)) {
+
+ // The HTML5 way of handling DHTML history...
+ win.onhashchange = function () {
+ var hash = _getHash();
+ _handleFQStateChange(hash);
+ _storeStates();
+ };
+
+ G.ready = true;
+ H.fire(EV_HISTORY_READY);
+
+ } else if (_useIFrame) {
+
+ // IE < 8 or IE8 in quirks mode or IE7 standards mode
+ _checkIframeLoaded();
+
+ } else {
+
+ // Periodically check whether a navigate operation has been
+ // requested on the main window. This will happen when
+ // History.navigate has been called, or after the user
+ // has hit the back/forward button.
+
+ // On Gecko and Opera, we just need to watch the hash...
+ hash = _getHash();
+
+ setInterval(function () {
+ var newHash = _getHash();
+ if (newHash !== hash) {
+ hash = newHash;
+ _handleFQStateChange(hash);
+ _storeStates();
+ }
+ }, 50);
+
+ G.ready = true;
+ H.fire(EV_HISTORY_READY);
+ }
+ }
+
+
+ H = {
+
+ /**
+ * Registers a new module.
+ * @method register
+ * @param {string} moduleId Non-empty string uniquely identifying the
+ * module you wish to register.
+ * @param {string} initialState The initial state of the specified
+ * module corresponding to its earliest history entry.
+ * @return {History.Module} The newly registered module
+ */
+ register: function (moduleId, initialState) {
+ var module;
+
+ if (!Y.Lang.isString(moduleId) || Y.Lang.trim(moduleId) === '' || !Y.Lang.isString(initialState)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ moduleId = encode(moduleId);
+ initialState = encode(initialState);
+
+ if (G._modules[moduleId]) {
+ // The module seems to have already been registered.
+ return;
+ }
+
+ // Note: A module CANNOT be registered once the browser history
+ // utility has been initialized. This is related to reading and
+ // writing state values from/to the input field. Relaxing this
+ // rule would potentially create situations rather complicated
+ // to deal with.
+ if (G.ready) {
+ return null;
+ }
+
+ module = new H.Module(moduleId, initialState);
+ G._modules[moduleId] = module;
+ return module;
+ },
+
+ /**
+ * Initializes the Browser History Manager. Call this method
+ * from a script block located right after the opening body tag.
+ * @method initialize
+ * @param {string|HTML Element} stateField <input type="hidden"> used
+ * to store application states. Must be in the static markup.
+ * @param {string|HTML Element} historyIFrame IFrame used to store
+ * the history (only required for IE6/7)
+ * @public
+ */
+ initialize: function (stateField, historyIFrame) {
+ var tagName, type;
+
+ if (G.ready) {
+ // The browser history utility has already been initialized.
+ return true;
+ }
+
+ stateField = Y.get(stateField);
+ if (!stateField) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ tagName = stateField.get('tagName').toUpperCase();
+ type = stateField.get('type');
+
+ if (tagName !== 'TEXTAREA' && (tagName !== 'INPUT' || type !== 'hidden' && type !== 'text')) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ // IE < 8 or IE8 in quirks mode or IE7 standards mode
+ if (Y.UA.ie && (Y.Lang.isUndefined(doc.documentMode) || doc.documentMode < 8)) {
+ _useIFrame = true;
+ historyIFrame = Y.get(historyIFrame);
+ if (!historyIFrame || historyIFrame.get('tagName').toUpperCase() !== 'IFRAME') {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+ }
+
+ if (Y.UA.opera && !Y.Lang.isUndefined(win.history.navigationMode)) {
+ // Disable Opera's fast back/forward navigation mode and put
+ // it in compatible mode. This makes anchor-based history
+ // navigation work after the page has been navigated away
+ // from and re-activated, at the cost of slowing down
+ // back/forward navigation to and from that page.
+ win.history.navigationMode = 'compatible';
+ }
+
+ G._stateField = stateField;
+ G._historyIFrame = historyIFrame;
+
+ Y.on('domready', _initialize);
+ return true;
+ },
+
+ /**
+ * Stores a new entry in the browser history by changing the state of a registered module.
+ * @method navigate
+ * @param {string} module Non-empty string representing your module.
+ * @param {string} state String representing the new state of the specified module.
+ * @return {boolean} Indicates whether the new state was successfully added to the history.
+ * @public
+ */
+ navigate: function (moduleId, state) {
+ var states;
+
+ if (!Y.Lang.isString(moduleId) || !Y.Lang.isString(state)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ // The ncoding of module id and state takes place in mutiNavigate.
+ states = {};
+ states[moduleId] = state;
+
+ return H.multiNavigate(states);
+ },
+
+ /**
+ * Stores a new entry in the browser history by changing the state
+ * of several registered modules in one atomic operation.
+ * @method multiNavigate
+ * @param {object} states Associative array of module-state pairs to set simultaneously.
+ * @return {boolean} Indicates whether the new state was successfully added to the history.
+ * @public
+ */
+ multiNavigate: function (states) {
+ var newStates = [], fqstate, globalStateChanged = false;
+
+ if (!G.ready) {
+ return false;
+ }
+
+ Y.Object.each(G._modules, function (module, moduleId) {
+ var state, decodedModuleId = decode(moduleId);
+
+ if (!states.hasOwnProperty(decodedModuleId)) {
+ // The caller did not wish to modify the state of this
+ // module. We must however include it in fqstate!
+ state = module.currentState;
+ } else {
+ state = encode(states[decodedModuleId]);
+ if (state !== module.upcomingState) {
+ module.upcomingState = state;
+ globalStateChanged = true;
+ }
+ }
+
+ newStates.push(moduleId + '=' + state);
+ });
+
+ if (!globalStateChanged) {
+ // Nothing changed, so don't do anything.
+ return false;
+ }
+
+ fqstate = newStates.join('&');
+
+ if (_useIFrame) {
+ return _updateIFrame(fqstate);
+ } else {
+ win.location.hash = fqstate;
+ return true;
+ }
+ },
+
+ /**
+ * Returns the current state of the specified module.
+ * @method getCurrentState
+ * @param {string} moduleId Non-empty string representing your module.
+ * @return {string} The current state of the specified module.
+ * @public
+ */
+ getCurrentState: function (moduleId) {
+ var module;
+
+ if (!Y.Lang.isString(moduleId)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ if (!G.ready) {
+ return null;
+ }
+
+ moduleId = encode(moduleId);
+ module = G._modules[moduleId];
+ if (!module) {
+ return null;
+ }
+
+ return decode(module.currentState);
+ },
+
+ /**
+ * Returns the state of a module according to the URL fragment
+ * identifier. This method is useful to initialize your modules
+ * if your application was bookmarked from a particular state.
+ * @method getBookmarkedState
+ * @param {string} moduleId Non-empty string representing your module.
+ * @return {string} The bookmarked state of the specified module.
+ * @public
+ */
+ getBookmarkedState: function (moduleId) {
+ var m, i, h;
+
+ if (!Y.Lang.isString(moduleId)) {
+ throw new Error(E_MISSING_OR_INVALID_ARG);
+ }
+
+ moduleId = encode(moduleId);
+
+ // Use location.href instead of location.hash which is already
+ // URL-decoded, which creates problems if the state value
+ // contained special characters...
+ h = win.location.href;
+ i = h.indexOf('#');
+
+ if (i >= 0) {
+ h = h.substr(i + 1);
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(h))) {
+ if (m[1] === moduleId) {
+ return decode(m[2]);
+ }
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Returns the value of the specified query string parameter.
+ * This method is not used internally by the Browser History Manager.
+ * However, it is provided here as a helper since many applications
+ * using the Browser History Manager will want to read the value of
+ * url parameters to initialize themselves.
+ * @method getQueryStringParameter
+ * @param {string} paramName Name of the parameter we want to look up.
+ * @param {string} queryString Optional URL to look at. If not specified,
+ * this method uses the URL in the address bar.
+ * @return {string} The value of the specified parameter, or null.
+ * @public
+ */
+ getQueryStringParameter: function (paramName, url) {
+ var m, q, i;
+
+ url = url || win.location.href;
+
+ i = url.indexOf('?');
+ q = i >= 0 ? url.substr(i + 1) : url;
+
+ // Remove the hash if any
+ i = q.lastIndexOf('#');
+ q = i >= 0 ? q.substr(0, i) : q;
+
+ REGEXP.lastIndex = 0;
+ while ((m = REGEXP.exec(q))) {
+ if (m[1] === paramName) {
+ return decode(m[2]);
+ }
+ }
+
+ return null;
+ }
+ };
+
+
+ // Make Y.History an event target
+ Y.mix(H, Y.Event.Target.prototype);
+ Y.Event.Target.call(H);
+
+
+ /**
+ * This class represents a browser history module.
+ * @class History.Module
+ * @constructor
+ * @param id {String} the module identifier
+ * @param initialState {String} the module's initial state
+ */
+ H.Module = function (id, initialState) {
+
+ Y.Event.Target.call(this);
+
+ /**
+ * The module identifier
+ * @type String
+ * @final
+ */
+ this.id = id;
+
+ /**
+ * The module's initial state
+ * @type String
+ * @final
+ */
+ this.initialState = initialState;
+
+ /**
+ * The module's current state
+ * @type String
+ * @final
+ */
+ this.currentState = initialState;
+
+ /**
+ * The module's upcoming state. There can be a slight delay between the
+ * time a state is changed, and the time a state change is detected.
+ * This property allows us to not fire the module state changed event
+ * multiple times, making client code simpler.
+ * @type String
+ * @private
+ * @final
+ */
+ this.upcomingState = initialState;
+ };
+
+ Y.mix(H.Module, Y.Event.Target, false, null, 1);
+
+ Y.History = H;
+
+
+}, '3.0.0' ,{skinnable:false, use:['event', 'node']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('imageloader', function(Y) {
+
+/**
+ * The ImageLoader Utility is a framework to dynamically load images according to certain triggers,
+ * enabling faster load times and a more responsive UI.
+ *
+ * @module imageloader
+ * @requires base-base, node-style, node-screen
+ */
+
+
+ /**
+ * A group for images. A group can have one time limit and a series of triggers. Thus the images belonging to this group must share these constraints.
+ * @class ImgLoadGroup
+ * @extends Base
+ * @constructor
+ */
+ Y.ImgLoadGroup = function() {
+ // call init first, because it sets up local vars for storing attribute-related info
+ this._init();
+ Y.ImgLoadGroup.superclass.constructor.apply(this, arguments);
+ };
+
+ Y.ImgLoadGroup.NAME = 'imgLoadGroup';
+
+ Y.ImgLoadGroup.ATTRS = {
+
+ /**
+ * Name for the group. Only used to identify the group in logging statements.
+ * @attribute name
+ * @type String
+ */
+ name: {
+ value: ''
+ },
+
+ /**
+ * Time limit, in seconds, after which images are fetched regardless of trigger events.
+ * @attribute timeLimit
+ * @type Number
+ */
+ timeLimit: {
+ value: null
+ },
+
+ /**
+ * Distance below the fold for which images are loaded. Images are not loaded until they are at most this distance away from (or above) the fold.
+ * This check is performed at page load (domready) and after any window scroll or window resize event (until all images are loaded).
+ * @attribute foldDistance
+ * @type Number
+ */
+ foldDistance: {
+ validator: Y.Lang.isNumber,
+ setter: function(val) { this._setFoldTriggers(); return val; },
+ lazyAdd: false
+ },
+
+ /**
+ * Class name that will identify images belonging to the group. This class name will be removed from each element in order to fetch images.
+ * This class should have, in its CSS style definition, "<code>background:none !important;</code>".
+ * @attribute className
+ * @type String
+ */
+ className: {
+ value: null,
+ setter: function(name) { this._className = name; return name; },
+ lazyAdd: false
+ }
+
+ };
+
+ var groupProto = {
+
+ /**
+ * Initialize all private members needed for the group.
+ * @method _init
+ * @private
+ */
+ _init: function() {
+
+ /**
+ * Collection of triggers for this group.
+ * Keeps track of each trigger's event handle, as returned from <code>Y.on</code>.
+ * @property _triggers
+ * @private
+ * @type Array
+ */
+ this._triggers = [];
+
+ /**
+ * Collection of images (<code>Y.ImgLoadImgObj</code> objects) registered with this group, keyed by DOM id.
+ * @property _imgObjs
+ * @private
+ * @type Object
+ */
+ this._imgObjs = {};
+
+ /**
+ * Timeout object to keep a handle on the time limit.
+ * @property _timeout
+ * @private
+ * @type Object
+ */
+ this._timeout = null;
+
+ /**
+ * DOM elements having the class name that is associated with this group.
+ * Elements are stored during the <code>_foldCheck</code> function and reused later during any subsequent <code>_foldCheck</code> calls - gives a slight performance improvement when the page fold is repeatedly checked.
+ * @property _classImageEls
+ * @private
+ * @type Array
+ */
+ this._classImageEls = null;
+
+ /**
+ * Keep the CSS class name in a member variable for ease and speed.
+ * @property _className
+ * @private
+ * @type String
+ */
+ this._className = null;
+
+ /**
+ * Boolean tracking whether the window scroll and window resize triggers have been set if this is a fold group.
+ * @property _areFoldTriggersSet
+ * @private
+ * @type Boolean
+ */
+ this._areFoldTriggersSet = false;
+
+ /**
+ * The maximum pixel height of the document that has been made visible.
+ * During fold checks, if the user scrolls up then there's no need to check for newly exposed images.
+ * @property _maxKnownHLimit
+ * @private
+ * @type Int
+ */
+ this._maxKnownHLimit = 0;
+
+ // add a listener to domready that will start the time limit
+ Y.on('domready', this._onloadTasks, this);
+ },
+
+ /**
+ * Adds a trigger to the group. Arguments are passed to <code>Y.on</code>.
+ * @method addTrigger
+ * @chainable
+ * @param {Object} obj The DOM object to attach the trigger event to
+ * @param {String} type The event type
+ */
+ addTrigger: function(obj, type) {
+ if (! obj || ! type) {
+ return this;
+ }
+
+ Y.log('adding trigger to group: ' + this.get('name'), 'info', 'imageloader');
+
+ /* Need to wrap the fetch function. Event Util can't distinguish prototyped functions of different instantiations.
+ * Leads to this scenario: groupA and groupZ both have window-scroll triggers. groupZ also has a 2-sec timeout (groupA has no timeout).
+ * groupZ's timeout fires; we remove the triggers. The detach call finds the first window-scroll event with Y.ILG.p.fetch, which is groupA's.
+ * groupA's trigger is removed and never fires, leaving images unfetched.
+ */
+ var wrappedFetch = function() {
+ this.fetch();
+ };
+ this._triggers.push( Y.on(type, wrappedFetch, obj, this) );
+
+ return this;
+ },
+
+ /**
+ * Adds a custom event trigger to the group.
+ * @method addCustomTrigger
+ * @chainable
+ * @param {String} name The name of the event
+ * @param {Object} obj The object on which to attach the event. <code>obj</code> is optional - by default the event is attached to the <code>Y</code> instance
+ */
+ addCustomTrigger: function(name, obj) {
+ if (! name) {
+ return this;
+ }
+
+ Y.log('adding custom trigger to group: ' + this.get('name'), 'info', 'imageloader');
+
+ // see comment in addTrigger()
+ var wrappedFetch = function() {
+ this.fetch();
+ };
+ if (Y.Lang.isUndefined(obj)) {
+ this._triggers.push( Y.on(name, wrappedFetch, this) );
+ }
+ else {
+ this._triggers.push( obj.on(name, wrappedFetch, this) );
+ }
+
+ return this;
+ },
+
+ /**
+ * Sets the window scroll and window resize triggers for any group that is fold-conditional (i.e., has a fold distance set).
+ * @method _setFoldTriggers
+ * @private
+ */
+ _setFoldTriggers: function() {
+ if (this._areFoldTriggersSet) {
+ return;
+ }
+
+ Y.log('setting window scroll and resize events for group: ' + this.get('name'), 'info', 'imageloader');
+
+ var wrappedFoldCheck = function() {
+ this._foldCheck();
+ };
+ this._triggers.push( Y.on('scroll', wrappedFoldCheck, window, this) );
+ this._triggers.push( Y.on('resize', wrappedFoldCheck, window, this) );
+ this._areFoldTriggersSet = true;
+ },
+
+ /**
+ * Performs necessary setup at domready time.
+ * Initiates time limit for group; executes the fold check for the images.
+ * @method _onloadTasks
+ * @private
+ */
+ _onloadTasks: function() {
+ var timeLim = this.get('timeLimit');
+ if (timeLim && timeLim > 0) {
+ Y.log('setting time limit of ' + timeLim + ' seconds for group: ' + this.get('name'), 'info', 'imageloader');
+ this._timeout = setTimeout(this._getFetchTimeout(), timeLim * 1000);
+ }
+
+ if (! Y.Lang.isUndefined(this.get('foldDistance'))) {
+ this._foldCheck();
+ }
+ },
+
+ /**
+ * Returns the group's <code>fetch</code> method, with the proper closure, for use with <code>setTimeout</code>.
+ * @method _getFetchTimeout
+ * @return {Function} group's <code>fetch</code> method
+ * @private
+ */
+ _getFetchTimeout: function() {
+ var self = this;
+ return function() { self.fetch(); };
+ },
+
+ /**
+ * Registers an image with the group.
+ * Arguments are passed through to a <code>Y.ImgLoadImgObj</code> constructor; see that class' attribute documentation for detailed information. "<code>domId</code>" is a required attribute.
+ * @method registerImage
+ * @param {Object} * A configuration object literal with attribute name/value pairs (passed through to a <code>Y.ImgLoadImgObj</code> constructor)
+ * @return {Object} <code>Y.ImgLoadImgObj</code> that was registered
+ */
+ registerImage: function() {
+ var domId = arguments[0].domId;
+ if (! domId) {
+ return null;
+ }
+
+ Y.log('adding image with id: ' + domId + ' to group: ' + this.get('name'), 'info', 'imageloader');
+
+ this._imgObjs[domId] = new Y.ImgLoadImgObj(arguments[0]);
+ return this._imgObjs[domId];
+ },
+
+ /**
+ * Displays the images in the group.
+ * This method is called when a trigger fires or the time limit expires; it shouldn't be called externally, but is not private in the rare event that it needs to be called immediately.
+ * @method fetch
+ */
+ fetch: function() {
+ Y.log('Fetching images in group: "' + this.get('name') + '".', 'info', 'imageloader');
+
+ // done with the triggers
+ this._clearTriggers();
+
+ // fetch whatever we need to by className
+ this._fetchByClass();
+
+ // fetch registered images
+ for (var id in this._imgObjs) {
+ if (this._imgObjs.hasOwnProperty(id)) {
+ this._imgObjs[id].fetch();
+ }
+ }
+ },
+
+ /**
+ * Clears the timeout and all triggers associated with the group.
+ * @method _clearTriggers
+ * @private
+ */
+ _clearTriggers: function() {
+ clearTimeout(this._timeout);
+ // detach all listeners
+ for (var i=0, len = this._triggers.length; i < len; i++) {
+ this._triggers[i].detach();
+ }
+ },
+
+ /**
+ * Checks the position of each image in the group. If any part of the image is within the specified distance (<code>foldDistance</code>) of the client viewport, the image is fetched immediately.
+ * @method _foldCheck
+ * @private
+ */
+ _foldCheck: function() {
+ Y.log('Checking for images above the fold in group: "' + this.get('name') + '"', 'info', 'imageloader');
+
+ var allFetched = true,
+ viewReg = Y.DOM.viewportRegion(),
+ hLimit = viewReg.bottom + this.get('foldDistance'),
+ id, imgFetched, els, i, len;
+
+ // unless we've uncovered new frontiers, there's no need to continue
+ if (hLimit <= this._maxKnownHLimit) {
+ return;
+ }
+ this._maxKnownHLimit = hLimit;
+
+ for (id in this._imgObjs) {
+ if (this._imgObjs.hasOwnProperty(id)) {
+ imgFetched = this._imgObjs[id].fetch(hLimit);
+ allFetched = allFetched && imgFetched;
+ }
+ }
+
+ // and by class
+ if (this._className) {
+ if (this._classImageEls === null) {
+ // get all the relevant elements and store them
+ this._classImageEls = [];
+ els = Y.all('.' + this._className);
+ els.each( function(node) { this._classImageEls.push( { el: node, y: node.getY(), fetched: false } ); }, this);
+ }
+ els = this._classImageEls;
+ for (i=0, len = els.length; i < len; i++) {
+ if (els[i].fetched) {
+ continue;
+ }
+ if (els[i].y && els[i].y <= hLimit) {
+ els[i].el.removeClass(this._className);
+ els[i].fetched = true;
+ Y.log('Image with id "' + els[i].el.get('id') + '" is within distance of the fold. Fetching image. (Image registered by class name with the group - may not have an id.)', 'info', 'imageloader');
+ }
+ else {
+ allFetched = false;
+ }
+ }
+ }
+
+ // if allFetched, remove listeners
+ if (allFetched) {
+ Y.log('All images fetched; removing listeners for group: "' + this.get('name') + '"', 'info', 'imageloader');
+ this._clearTriggers();
+ }
+ },
+
+ /**
+ * Finds all elements in the DOM with the class name specified in the group. Removes the class from the element in order to let the style definitions trigger the image fetching.
+ * @method _fetchByClass
+ * @private
+ */
+ _fetchByClass: function() {
+ if (! this._className) {
+ return;
+ }
+
+ Y.log('Fetching all images with class "' + this._className + '" in group "' + this.get('name') + '".', 'info', 'imageloader');
+
+ Y.all('.' + this._className).removeClass(this._className);
+ }
+
+ };
+
+
+ Y.extend(Y.ImgLoadGroup, Y.Base, groupProto);
+
+
+ //------------------------------------------------
+
+
+ /**
+ * Image objects to be registered with the groups
+ * @class ImgLoadImgObj
+ * @extends Base
+ * @constructor
+ */
+ Y.ImgLoadImgObj = function() {
+ Y.ImgLoadImgObj.superclass.constructor.apply(this, arguments);
+ this._init();
+ };
+
+ Y.ImgLoadImgObj.NAME = 'imgLoadImgObj';
+
+ Y.ImgLoadImgObj.ATTRS = {
+ /**
+ * HTML DOM id of the image element.
+ * @attribute domId
+ * @type String
+ */
+ domId: {
+ value: null,
+ writeOnce: true
+ },
+
+ /**
+ * Background URL for the image.
+ * For an image whose URL is specified by "<code>background-image</code>" in the element's style.
+ * @attribute bgUrl
+ * @type String
+ */
+ bgUrl: {
+ value: null
+ },
+
+ /**
+ * Source URL for the image.
+ * For an image whose URL is specified by a "<code>src</code>" attribute in the DOM element.
+ * @attribute srcUrl
+ * @type String
+ */
+ srcUrl: {
+ value: null
+ },
+
+ /**
+ * Pixel width of the image. Will be set as a <code>width</code> attribute on the DOM element after the image is fetched.
+ * Defaults to the natural width of the image (no <code>width</code> attribute will be set).
+ * Usually only used with src images.
+ * @attribute width
+ * @type Int
+ */
+ width: {
+ value: null
+ },
+
+ /**
+ * Pixel height of the image. Will be set as a <code>height</code> attribute on the DOM element after the image is fetched.
+ * Defaults to the natural height of the image (no <code>height</code> attribute will be set).
+ * Usually only used with src images.
+ * @attribute height
+ * @type Int
+ */
+ height: {
+ value: null
+ },
+
+ /**
+ * Whether the image's <code>style.visibility</code> should be set to <code>visible</code> after the image is fetched.
+ * Used when setting images as <code>visibility:hidden</code> prior to image fetching.
+ * @attribute setVisible
+ * @type Boolean
+ */
+ setVisible: {
+ value: false
+ },
+
+ /**
+ * Whether the image is a PNG.
+ * PNG images get special treatment in that the URL is specified through AlphaImageLoader for IE, versions 6 and earlier.
+ * Only used with background images.
+ * @attribute isPng
+ * @type Boolean
+ */
+ isPng: {
+ value: false
+ },
+
+ /**
+ * AlphaImageLoader <code>sizingMethod</code> property to be set for the image.
+ * Only set if <code>isPng</code> value for this image is set to <code>true</code>.
+ * Defaults to <code>scale</code>.
+ * @attribute sizingMethod
+ * @type String
+ */
+ sizingMethod: {
+ value: 'scale'
+ },
+
+ /**
+ * AlphaImageLoader <code>enabled</code> property to be set for the image.
+ * Only set if <code>isPng</code> value for this image is set to <code>true</code>.
+ * Defaults to <code>true</code>.
+ * @attribute enabled
+ * @type String
+ */
+ enabled: {
+ value: 'true'
+ }
+
+ };
+
+ var imgProto = {
+
+ /**
+ * Initialize all private members needed for the group.
+ * @method _init
+ * @private
+ */
+ _init: function() {
+
+ /**
+ * Whether this image has already been fetched.
+ * In the case of fold-conditional groups, images won't be fetched twice.
+ * @property _fetched
+ * @private
+ * @type Boolean
+ */
+ this._fetched = false;
+
+ /**
+ * The Node object returned from <code>Y.get</code>, to avoid repeat calls to access the DOM.
+ * @property _imgEl
+ * @private
+ * @type Object
+ */
+ this._imgEl = null;
+
+ /**
+ * The vertical position returned from <code>getY</code>, to avoid repeat calls to access the DOM.
+ * The Y position is checked only for images registered with fold-conditional groups. The position is checked first at page load (domready)
+ * and this caching enhancement assumes that the image's vertical position won't change after that first check.
+ * @property _yPos
+ * @private
+ * @type Int
+ */
+ this._yPos = null;
+ },
+
+ /**
+ * Displays the image; puts the URL into the DOM.
+ * This method shouldn't be called externally, but is not private in the rare event that it needs to be called immediately.
+ * @method fetch
+ * @param {Int} withinY The pixel distance from the top of the page, for which if the image lies within, it will be fetched. Undefined indicates that no check should be made, and the image should always be fetched
+ * @return {Boolean} Whether the image has been fetched (either during this execution or previously)
+ */
+ fetch: function(withinY) {
+ if (this._fetched) {
+ return true;
+ }
+
+ var el = this._getImgEl(),
+ yPos;
+ if (! el) {
+ return false;
+ }
+
+ if (withinY) {
+ // need a distance check
+ yPos = this._getYPos();
+ if (! yPos || yPos > withinY) {
+ return false;
+ }
+ Y.log('Image with id "' + this.get('domId') + '" is within distance of the fold. Fetching image.', 'info', 'imageloader');
+ }
+
+ Y.log('Fetching image with id "' + this.get('domId') + '".', 'info', 'imageloader');
+
+ // apply url
+ if (this.get('bgUrl') !== null) {
+ // bg url
+ if (this.get('isPng') && Y.UA.ie && Y.UA.ie <= 6) {
+ // png for which to apply AlphaImageLoader
+ el.setStyle('filter', 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.get('url') + '", sizingMethod="' + this.get('sizingMethod') + '", enabled="' + this.get('enabled') + '")');
+ }
+ else {
+ // regular bg image
+ el.setStyle('backgroundImage', "url('" + this.get('bgUrl') + "')");
+ }
+ }
+ else if (this.get('srcUrl') !== null) {
+ // regular src image
+ el.setAttribute('src', this.get('srcUrl'));
+ }
+
+ // apply attributes
+ if (this.get('setVisible')) {
+ el.setStyle('visibility', 'visible');
+ }
+ if (this.get('width')) {
+ el.setAttribute('width', this.get('width'));
+ }
+ if (this.get('height')) {
+ el.setAttribute('height', this.get('height'));
+ }
+
+ this._fetched = true;
+
+ return true;
+ },
+
+ /**
+ * Gets the object (as a <code>Y.Node</code>) of the DOM element indicated by "<code>domId</code>".
+ * @method _getImgEl
+ * @returns {Object} DOM element of the image as a <code>Y.Node</code> object
+ * @private
+ */
+ _getImgEl: function() {
+ if (this._imgEl === null) {
+ this._imgEl = Y.get('#' + this.get('domId'));
+ }
+ return this._imgEl;
+ },
+
+ /**
+ * Gets the Y position of the node in page coordinates.
+ * Expects that the page-coordinate position of the image won't change.
+ * @method _getYPos
+ * @returns {Object} The Y position of the image
+ * @private
+ */
+ _getYPos: function() {
+ if (this._yPos === null) {
+ this._yPos = this._getImgEl().getY();
+ }
+ return this._yPos;
+ }
+
+ };
+
+
+ Y.extend(Y.ImgLoadImgObj, Y.Base, imgProto);
+
+
+
+
+}, '3.0.0' ,{requires:['base-base', 'node-style', 'node-screen']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("imageloader",function(B){B.ImgLoadGroup=function(){this._init();B.ImgLoadGroup.superclass.constructor.apply(this,arguments);};B.ImgLoadGroup.NAME="imgLoadGroup";B.ImgLoadGroup.ATTRS={name:{value:""},timeLimit:{value:null},foldDistance:{validator:B.Lang.isNumber,setter:function(D){this._setFoldTriggers();return D;},lazyAdd:false},className:{value:null,setter:function(D){this._className=D;return D;},lazyAdd:false}};var C={_init:function(){this._triggers=[];this._imgObjs={};this._timeout=null;this._classImageEls=null;this._className=null;this._areFoldTriggersSet=false;this._maxKnownHLimit=0;B.on("domready",this._onloadTasks,this);},addTrigger:function(F,E){if(!F||!E){return this;}var D=function(){this.fetch();};this._triggers.push(B.on(E,D,F,this));return this;},addCustomTrigger:function(D,F){if(!D){return this;}var E=function(){this.fetch();};if(B.Lang.isUndefined(F)){this._triggers.push(B.on(D,E,this));}else{this._triggers.push(F.on(D,E,this));}return this;},_setFoldTriggers:function(){if(this._areFoldTriggersSet){return;}var D=function(){this._foldCheck();};this._triggers.push(B.on("scroll",D,window,this));this._triggers.push(B.on("resize",D,window,this));this._areFoldTriggersSet=true;},_onloadTasks:function(){var D=this.get("timeLimit");if(D&&D>0){this._timeout=setTimeout(this._getFetchTimeout(),D*1000);}if(!B.Lang.isUndefined(this.get("foldDistance"))){this._foldCheck();}},_getFetchTimeout:function(){var D=this;return function(){D.fetch();};},registerImage:function(){var D=arguments[0].domId;if(!D){return null;}this._imgObjs[D]=new B.ImgLoadImgObj(arguments[0]);return this._imgObjs[D];},fetch:function(){this._clearTriggers();this._fetchByClass();for(var D in this._imgObjs){if(this._imgObjs.hasOwnProperty(D)){this._imgObjs[D].fetch();}}},_clearTriggers:function(){clearTimeout(this._timeout);for(var E=0,D=this._triggers.length;E<D;E++){this._triggers[E].detach();}},_foldCheck:function(){var I=true,J=B.DOM.viewportRegion(),H=J.bottom+this.get("foldDistance"),K,E,G,F,D;if(H<=this._maxKnownHLimit){return;}this._maxKnownHLimit=H;for(K in this._imgObjs){if(this._imgObjs.hasOwnProperty(K)){E=this._imgObjs[K].fetch(H);I=I&&E;}}if(this._className){if(this._classImageEls===null){this._classImageEls=[];G=B.all("."+this._className);G.each(function(L){this._classImageEls.push({el:L,y:L.getY(),fetched:false});},this);}G=this._classImageEls;for(F=0,D=G.length;F<D;F++){if(G[F].fetched){continue;}if(G[F].y&&G[F].y<=H){G[F].el.removeClass(this._className);G[F].fetched=true;}else{I=false;}}}if(I){this._clearTriggers();}},_fetchByClass:function(){if(!this._className){return;}B.all("."+this._className).removeClass(this._className);}};B.extend(B.ImgLoadGroup,B.Base,C);B.ImgLoadImgObj=function(){B.ImgLoadImgObj.superclass.constructor.apply(this,arguments);this._init();};B.ImgLoadImgObj.NAME="imgLoadImgObj";B.ImgLoadImgObj.ATTRS={domId:{value:null,writeOnce:true},bgUrl:{value:null},srcUrl:{value:null},width:{value:null},height:{value:null},setVisible:{value:false},isPng:{value:false},sizingMethod:{value:"scale"},enabled:{value:"true"}};var A={_init:function(){this._fetched=false;this._imgEl=null;this._yPos=null;},fetch:function(F){if(this._fetched){return true;}var D=this._getImgEl(),E;if(!D){return false;}if(F){E=this._getYPos();if(!E||E>F){return false;}}if(this.get("bgUrl")!==null){if(this.get("isPng")&&B.UA.ie&&B.UA.ie<=6){D.setStyle("filter",'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+this.get("url")+'", sizingMethod="'+this.get("sizingMethod")+'", enabled="'+this.get("enabled")+'")');}else{D.setStyle("backgroundImage","url('"+this.get("bgUrl")+"')");}}else{if(this.get("srcUrl")!==null){D.setAttribute("src",this.get("srcUrl"));}}if(this.get("setVisible")){D.setStyle("visibility","visible");}if(this.get("width")){D.setAttribute("width",this.get("width"));}if(this.get("height")){D.setAttribute("height",this.get("height"));}this._fetched=true;return true;},_getImgEl:function(){if(this._imgEl===null){this._imgEl=B.get("#"+this.get("domId"));}return this._imgEl;},_getYPos:function(){if(this._yPos===null){this._yPos=this._getImgEl().getY();}return this._yPos;}};B.extend(B.ImgLoadImgObj,B.Base,A);},"3.0.0",{requires:["base-base","node-style","node-screen"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('imageloader', function(Y) {
+
+/**
+ * The ImageLoader Utility is a framework to dynamically load images according to certain triggers,
+ * enabling faster load times and a more responsive UI.
+ *
+ * @module imageloader
+ * @requires base-base, node-style, node-screen
+ */
+
+
+ /**
+ * A group for images. A group can have one time limit and a series of triggers. Thus the images belonging to this group must share these constraints.
+ * @class ImgLoadGroup
+ * @extends Base
+ * @constructor
+ */
+ Y.ImgLoadGroup = function() {
+ // call init first, because it sets up local vars for storing attribute-related info
+ this._init();
+ Y.ImgLoadGroup.superclass.constructor.apply(this, arguments);
+ };
+
+ Y.ImgLoadGroup.NAME = 'imgLoadGroup';
+
+ Y.ImgLoadGroup.ATTRS = {
+
+ /**
+ * Name for the group. Only used to identify the group in logging statements.
+ * @attribute name
+ * @type String
+ */
+ name: {
+ value: ''
+ },
+
+ /**
+ * Time limit, in seconds, after which images are fetched regardless of trigger events.
+ * @attribute timeLimit
+ * @type Number
+ */
+ timeLimit: {
+ value: null
+ },
+
+ /**
+ * Distance below the fold for which images are loaded. Images are not loaded until they are at most this distance away from (or above) the fold.
+ * This check is performed at page load (domready) and after any window scroll or window resize event (until all images are loaded).
+ * @attribute foldDistance
+ * @type Number
+ */
+ foldDistance: {
+ validator: Y.Lang.isNumber,
+ setter: function(val) { this._setFoldTriggers(); return val; },
+ lazyAdd: false
+ },
+
+ /**
+ * Class name that will identify images belonging to the group. This class name will be removed from each element in order to fetch images.
+ * This class should have, in its CSS style definition, "<code>background:none !important;</code>".
+ * @attribute className
+ * @type String
+ */
+ className: {
+ value: null,
+ setter: function(name) { this._className = name; return name; },
+ lazyAdd: false
+ }
+
+ };
+
+ var groupProto = {
+
+ /**
+ * Initialize all private members needed for the group.
+ * @method _init
+ * @private
+ */
+ _init: function() {
+
+ /**
+ * Collection of triggers for this group.
+ * Keeps track of each trigger's event handle, as returned from <code>Y.on</code>.
+ * @property _triggers
+ * @private
+ * @type Array
+ */
+ this._triggers = [];
+
+ /**
+ * Collection of images (<code>Y.ImgLoadImgObj</code> objects) registered with this group, keyed by DOM id.
+ * @property _imgObjs
+ * @private
+ * @type Object
+ */
+ this._imgObjs = {};
+
+ /**
+ * Timeout object to keep a handle on the time limit.
+ * @property _timeout
+ * @private
+ * @type Object
+ */
+ this._timeout = null;
+
+ /**
+ * DOM elements having the class name that is associated with this group.
+ * Elements are stored during the <code>_foldCheck</code> function and reused later during any subsequent <code>_foldCheck</code> calls - gives a slight performance improvement when the page fold is repeatedly checked.
+ * @property _classImageEls
+ * @private
+ * @type Array
+ */
+ this._classImageEls = null;
+
+ /**
+ * Keep the CSS class name in a member variable for ease and speed.
+ * @property _className
+ * @private
+ * @type String
+ */
+ this._className = null;
+
+ /**
+ * Boolean tracking whether the window scroll and window resize triggers have been set if this is a fold group.
+ * @property _areFoldTriggersSet
+ * @private
+ * @type Boolean
+ */
+ this._areFoldTriggersSet = false;
+
+ /**
+ * The maximum pixel height of the document that has been made visible.
+ * During fold checks, if the user scrolls up then there's no need to check for newly exposed images.
+ * @property _maxKnownHLimit
+ * @private
+ * @type Int
+ */
+ this._maxKnownHLimit = 0;
+
+ // add a listener to domready that will start the time limit
+ Y.on('domready', this._onloadTasks, this);
+ },
+
+ /**
+ * Adds a trigger to the group. Arguments are passed to <code>Y.on</code>.
+ * @method addTrigger
+ * @chainable
+ * @param {Object} obj The DOM object to attach the trigger event to
+ * @param {String} type The event type
+ */
+ addTrigger: function(obj, type) {
+ if (! obj || ! type) {
+ return this;
+ }
+
+
+ /* Need to wrap the fetch function. Event Util can't distinguish prototyped functions of different instantiations.
+ * Leads to this scenario: groupA and groupZ both have window-scroll triggers. groupZ also has a 2-sec timeout (groupA has no timeout).
+ * groupZ's timeout fires; we remove the triggers. The detach call finds the first window-scroll event with Y.ILG.p.fetch, which is groupA's.
+ * groupA's trigger is removed and never fires, leaving images unfetched.
+ */
+ var wrappedFetch = function() {
+ this.fetch();
+ };
+ this._triggers.push( Y.on(type, wrappedFetch, obj, this) );
+
+ return this;
+ },
+
+ /**
+ * Adds a custom event trigger to the group.
+ * @method addCustomTrigger
+ * @chainable
+ * @param {String} name The name of the event
+ * @param {Object} obj The object on which to attach the event. <code>obj</code> is optional - by default the event is attached to the <code>Y</code> instance
+ */
+ addCustomTrigger: function(name, obj) {
+ if (! name) {
+ return this;
+ }
+
+
+ // see comment in addTrigger()
+ var wrappedFetch = function() {
+ this.fetch();
+ };
+ if (Y.Lang.isUndefined(obj)) {
+ this._triggers.push( Y.on(name, wrappedFetch, this) );
+ }
+ else {
+ this._triggers.push( obj.on(name, wrappedFetch, this) );
+ }
+
+ return this;
+ },
+
+ /**
+ * Sets the window scroll and window resize triggers for any group that is fold-conditional (i.e., has a fold distance set).
+ * @method _setFoldTriggers
+ * @private
+ */
+ _setFoldTriggers: function() {
+ if (this._areFoldTriggersSet) {
+ return;
+ }
+
+
+ var wrappedFoldCheck = function() {
+ this._foldCheck();
+ };
+ this._triggers.push( Y.on('scroll', wrappedFoldCheck, window, this) );
+ this._triggers.push( Y.on('resize', wrappedFoldCheck, window, this) );
+ this._areFoldTriggersSet = true;
+ },
+
+ /**
+ * Performs necessary setup at domready time.
+ * Initiates time limit for group; executes the fold check for the images.
+ * @method _onloadTasks
+ * @private
+ */
+ _onloadTasks: function() {
+ var timeLim = this.get('timeLimit');
+ if (timeLim && timeLim > 0) {
+ this._timeout = setTimeout(this._getFetchTimeout(), timeLim * 1000);
+ }
+
+ if (! Y.Lang.isUndefined(this.get('foldDistance'))) {
+ this._foldCheck();
+ }
+ },
+
+ /**
+ * Returns the group's <code>fetch</code> method, with the proper closure, for use with <code>setTimeout</code>.
+ * @method _getFetchTimeout
+ * @return {Function} group's <code>fetch</code> method
+ * @private
+ */
+ _getFetchTimeout: function() {
+ var self = this;
+ return function() { self.fetch(); };
+ },
+
+ /**
+ * Registers an image with the group.
+ * Arguments are passed through to a <code>Y.ImgLoadImgObj</code> constructor; see that class' attribute documentation for detailed information. "<code>domId</code>" is a required attribute.
+ * @method registerImage
+ * @param {Object} * A configuration object literal with attribute name/value pairs (passed through to a <code>Y.ImgLoadImgObj</code> constructor)
+ * @return {Object} <code>Y.ImgLoadImgObj</code> that was registered
+ */
+ registerImage: function() {
+ var domId = arguments[0].domId;
+ if (! domId) {
+ return null;
+ }
+
+
+ this._imgObjs[domId] = new Y.ImgLoadImgObj(arguments[0]);
+ return this._imgObjs[domId];
+ },
+
+ /**
+ * Displays the images in the group.
+ * This method is called when a trigger fires or the time limit expires; it shouldn't be called externally, but is not private in the rare event that it needs to be called immediately.
+ * @method fetch
+ */
+ fetch: function() {
+
+ // done with the triggers
+ this._clearTriggers();
+
+ // fetch whatever we need to by className
+ this._fetchByClass();
+
+ // fetch registered images
+ for (var id in this._imgObjs) {
+ if (this._imgObjs.hasOwnProperty(id)) {
+ this._imgObjs[id].fetch();
+ }
+ }
+ },
+
+ /**
+ * Clears the timeout and all triggers associated with the group.
+ * @method _clearTriggers
+ * @private
+ */
+ _clearTriggers: function() {
+ clearTimeout(this._timeout);
+ // detach all listeners
+ for (var i=0, len = this._triggers.length; i < len; i++) {
+ this._triggers[i].detach();
+ }
+ },
+
+ /**
+ * Checks the position of each image in the group. If any part of the image is within the specified distance (<code>foldDistance</code>) of the client viewport, the image is fetched immediately.
+ * @method _foldCheck
+ * @private
+ */
+ _foldCheck: function() {
+
+ var allFetched = true,
+ viewReg = Y.DOM.viewportRegion(),
+ hLimit = viewReg.bottom + this.get('foldDistance'),
+ id, imgFetched, els, i, len;
+
+ // unless we've uncovered new frontiers, there's no need to continue
+ if (hLimit <= this._maxKnownHLimit) {
+ return;
+ }
+ this._maxKnownHLimit = hLimit;
+
+ for (id in this._imgObjs) {
+ if (this._imgObjs.hasOwnProperty(id)) {
+ imgFetched = this._imgObjs[id].fetch(hLimit);
+ allFetched = allFetched && imgFetched;
+ }
+ }
+
+ // and by class
+ if (this._className) {
+ if (this._classImageEls === null) {
+ // get all the relevant elements and store them
+ this._classImageEls = [];
+ els = Y.all('.' + this._className);
+ els.each( function(node) { this._classImageEls.push( { el: node, y: node.getY(), fetched: false } ); }, this);
+ }
+ els = this._classImageEls;
+ for (i=0, len = els.length; i < len; i++) {
+ if (els[i].fetched) {
+ continue;
+ }
+ if (els[i].y && els[i].y <= hLimit) {
+ els[i].el.removeClass(this._className);
+ els[i].fetched = true;
+ }
+ else {
+ allFetched = false;
+ }
+ }
+ }
+
+ // if allFetched, remove listeners
+ if (allFetched) {
+ this._clearTriggers();
+ }
+ },
+
+ /**
+ * Finds all elements in the DOM with the class name specified in the group. Removes the class from the element in order to let the style definitions trigger the image fetching.
+ * @method _fetchByClass
+ * @private
+ */
+ _fetchByClass: function() {
+ if (! this._className) {
+ return;
+ }
+
+
+ Y.all('.' + this._className).removeClass(this._className);
+ }
+
+ };
+
+
+ Y.extend(Y.ImgLoadGroup, Y.Base, groupProto);
+
+
+ //------------------------------------------------
+
+
+ /**
+ * Image objects to be registered with the groups
+ * @class ImgLoadImgObj
+ * @extends Base
+ * @constructor
+ */
+ Y.ImgLoadImgObj = function() {
+ Y.ImgLoadImgObj.superclass.constructor.apply(this, arguments);
+ this._init();
+ };
+
+ Y.ImgLoadImgObj.NAME = 'imgLoadImgObj';
+
+ Y.ImgLoadImgObj.ATTRS = {
+ /**
+ * HTML DOM id of the image element.
+ * @attribute domId
+ * @type String
+ */
+ domId: {
+ value: null,
+ writeOnce: true
+ },
+
+ /**
+ * Background URL for the image.
+ * For an image whose URL is specified by "<code>background-image</code>" in the element's style.
+ * @attribute bgUrl
+ * @type String
+ */
+ bgUrl: {
+ value: null
+ },
+
+ /**
+ * Source URL for the image.
+ * For an image whose URL is specified by a "<code>src</code>" attribute in the DOM element.
+ * @attribute srcUrl
+ * @type String
+ */
+ srcUrl: {
+ value: null
+ },
+
+ /**
+ * Pixel width of the image. Will be set as a <code>width</code> attribute on the DOM element after the image is fetched.
+ * Defaults to the natural width of the image (no <code>width</code> attribute will be set).
+ * Usually only used with src images.
+ * @attribute width
+ * @type Int
+ */
+ width: {
+ value: null
+ },
+
+ /**
+ * Pixel height of the image. Will be set as a <code>height</code> attribute on the DOM element after the image is fetched.
+ * Defaults to the natural height of the image (no <code>height</code> attribute will be set).
+ * Usually only used with src images.
+ * @attribute height
+ * @type Int
+ */
+ height: {
+ value: null
+ },
+
+ /**
+ * Whether the image's <code>style.visibility</code> should be set to <code>visible</code> after the image is fetched.
+ * Used when setting images as <code>visibility:hidden</code> prior to image fetching.
+ * @attribute setVisible
+ * @type Boolean
+ */
+ setVisible: {
+ value: false
+ },
+
+ /**
+ * Whether the image is a PNG.
+ * PNG images get special treatment in that the URL is specified through AlphaImageLoader for IE, versions 6 and earlier.
+ * Only used with background images.
+ * @attribute isPng
+ * @type Boolean
+ */
+ isPng: {
+ value: false
+ },
+
+ /**
+ * AlphaImageLoader <code>sizingMethod</code> property to be set for the image.
+ * Only set if <code>isPng</code> value for this image is set to <code>true</code>.
+ * Defaults to <code>scale</code>.
+ * @attribute sizingMethod
+ * @type String
+ */
+ sizingMethod: {
+ value: 'scale'
+ },
+
+ /**
+ * AlphaImageLoader <code>enabled</code> property to be set for the image.
+ * Only set if <code>isPng</code> value for this image is set to <code>true</code>.
+ * Defaults to <code>true</code>.
+ * @attribute enabled
+ * @type String
+ */
+ enabled: {
+ value: 'true'
+ }
+
+ };
+
+ var imgProto = {
+
+ /**
+ * Initialize all private members needed for the group.
+ * @method _init
+ * @private
+ */
+ _init: function() {
+
+ /**
+ * Whether this image has already been fetched.
+ * In the case of fold-conditional groups, images won't be fetched twice.
+ * @property _fetched
+ * @private
+ * @type Boolean
+ */
+ this._fetched = false;
+
+ /**
+ * The Node object returned from <code>Y.get</code>, to avoid repeat calls to access the DOM.
+ * @property _imgEl
+ * @private
+ * @type Object
+ */
+ this._imgEl = null;
+
+ /**
+ * The vertical position returned from <code>getY</code>, to avoid repeat calls to access the DOM.
+ * The Y position is checked only for images registered with fold-conditional groups. The position is checked first at page load (domready)
+ * and this caching enhancement assumes that the image's vertical position won't change after that first check.
+ * @property _yPos
+ * @private
+ * @type Int
+ */
+ this._yPos = null;
+ },
+
+ /**
+ * Displays the image; puts the URL into the DOM.
+ * This method shouldn't be called externally, but is not private in the rare event that it needs to be called immediately.
+ * @method fetch
+ * @param {Int} withinY The pixel distance from the top of the page, for which if the image lies within, it will be fetched. Undefined indicates that no check should be made, and the image should always be fetched
+ * @return {Boolean} Whether the image has been fetched (either during this execution or previously)
+ */
+ fetch: function(withinY) {
+ if (this._fetched) {
+ return true;
+ }
+
+ var el = this._getImgEl(),
+ yPos;
+ if (! el) {
+ return false;
+ }
+
+ if (withinY) {
+ // need a distance check
+ yPos = this._getYPos();
+ if (! yPos || yPos > withinY) {
+ return false;
+ }
+ }
+
+
+ // apply url
+ if (this.get('bgUrl') !== null) {
+ // bg url
+ if (this.get('isPng') && Y.UA.ie && Y.UA.ie <= 6) {
+ // png for which to apply AlphaImageLoader
+ el.setStyle('filter', 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + this.get('url') + '", sizingMethod="' + this.get('sizingMethod') + '", enabled="' + this.get('enabled') + '")');
+ }
+ else {
+ // regular bg image
+ el.setStyle('backgroundImage', "url('" + this.get('bgUrl') + "')");
+ }
+ }
+ else if (this.get('srcUrl') !== null) {
+ // regular src image
+ el.setAttribute('src', this.get('srcUrl'));
+ }
+
+ // apply attributes
+ if (this.get('setVisible')) {
+ el.setStyle('visibility', 'visible');
+ }
+ if (this.get('width')) {
+ el.setAttribute('width', this.get('width'));
+ }
+ if (this.get('height')) {
+ el.setAttribute('height', this.get('height'));
+ }
+
+ this._fetched = true;
+
+ return true;
+ },
+
+ /**
+ * Gets the object (as a <code>Y.Node</code>) of the DOM element indicated by "<code>domId</code>".
+ * @method _getImgEl
+ * @returns {Object} DOM element of the image as a <code>Y.Node</code> object
+ * @private
+ */
+ _getImgEl: function() {
+ if (this._imgEl === null) {
+ this._imgEl = Y.get('#' + this.get('domId'));
+ }
+ return this._imgEl;
+ },
+
+ /**
+ * Gets the Y position of the node in page coordinates.
+ * Expects that the page-coordinate position of the image won't change.
+ * @method _getYPos
+ * @returns {Object} The Y position of the image
+ * @private
+ */
+ _getYPos: function() {
+ if (this._yPos === null) {
+ this._yPos = this._getImgEl().getY();
+ }
+ return this._yPos;
+ }
+
+ };
+
+
+ Y.extend(Y.ImgLoadImgObj, Y.Base, imgProto);
+
+
+
+
+}, '3.0.0' ,{requires:['base-base', 'node-style', 'node-screen']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-base', function(Y) {
+
+ /**
+ * Base IO functionality. Provides basic XHR transport support.
+ * @module io
+ * @submodule io-base
+ */
+
+ /**
+ * The io class is a utility that brokers HTTP requests through a simplified
+ * interface. Specifically, it allows JavaScript to make HTTP requests to
+ * a resource without a page reload. The underlying transport for making
+ * same-domain requests is the XMLHttpRequest object. YUI.io can also use
+ * Flash, if specified as a transport, for cross-domain requests.
+ *
+ * @class io
+ */
+
+ /**
+ * @event io:start
+ * @description This event is fired by YUI.io when a transaction is initiated.
+ * @type Event Custom
+ */
+ var E_START = 'io:start',
+
+ /**
+ * @event io:complete
+ * @description This event is fired by YUI.io when a transaction is complete.
+ * Response status and data are accessible, if available.
+ * @type Event Custom
+ */
+ E_COMPLETE = 'io:complete',
+
+ /**
+ * @event io:success
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP2xx.
+ * @type Event Custom
+ */
+ E_SUCCESS = 'io:success',
+
+ /**
+ * @event io:failure
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP4xx, 5xx and above.
+ * @type Event Custom
+ */
+ E_FAILURE = 'io:failure',
+
+ /**
+ * @event io:end
+ * @description This event signifies the end of the transaction lifecycle. The
+ * transaction transport is destroyed.
+ * @type Event Custom
+ */
+ E_END = 'io:end',
+
+ //--------------------------------------
+ // Properties
+ //--------------------------------------
+ /**
+ * @description A transaction counter that increments for each transaction.
+ *
+ * @property transactionId
+ * @private
+ * @static
+ * @type int
+ */
+ transactionId = 0,
+
+ /**
+ * @description Object of default HTTP headers to be initialized and sent
+ * for all transactions.
+ *
+ * @property _headers
+ * @private
+ * @static
+ * @type object
+ */
+ _headers = {
+ 'X-Requested-With' : 'XMLHttpRequest'
+ },
+
+ /**
+ * @description Object that stores timeout values for any transaction with
+ * a defined "timeout" configuration property.
+ *
+ * @property _timeout
+ * @private
+ * @static
+ * @type object
+ */
+ _timeout = {},
+
+ // Window reference
+ w = Y.config.win;
+
+ //--------------------------------------
+ // Methods
+ //--------------------------------------
+ /**
+ * @description Method for requesting a transaction. _io() is implemented as
+ * yui.io(). Each transaction may include a configuration object. Its
+ * properties are:
+ *
+ * method: HTTP method verb (e.g., GET or POST). If this property is not
+ * not defined, the default value will be GET.
+ *
+ * data: This is the name-value string that will be sent as the transaction
+ * data. If the request is HTTP GET, the data become part of
+ * querystring. If HTTP POST, the data are sent in the message body.
+ *
+ * xdr: Defines the transport to be used for cross-domain requests. By
+ * setting this property, the transaction will use the specified
+ * transport instead of XMLHttpRequest. Currently, the only alternate
+ * transport supported is Flash (e.g., { xdr: 'flash' }).
+ *
+ * form: This is a defined object used to process HTML form as data. The
+ * properties are:
+ * {
+ * id: object, //HTML form object or id of HTML form
+ * useDisabled: boolean, //Allow disabled HTML form field values
+ * to be sent as part of the data.
+ * }
+ *
+ * on: This is a defined object used to create and handle specific
+ * events during a transaction lifecycle. These events will fire in
+ * addition to the global io events. The events are:
+ * start - This event is fired when a request is sent to a resource.
+ * complete - This event fires when the transaction is complete.
+ * success - This event fires when the response status resolves to
+ * HTTP 2xx.
+ * failure - This event fires when the response status resolves to
+ * HTTP 4xx, 5xx; and, for all transaction exceptions,
+ * including aborted transactions and transaction timeouts.
+ * end - This even is fired at the conclusion of the transaction
+ * lifecycle, after a success or failure resolution.
+ *
+ * The properties are:
+ * {
+ * start: function(id, args){},
+ * complete: function(id, responseobject, args){},
+ * success: function(id, responseobject, args){},
+ * failure: function(id, responseobject, args){},
+ * end: function(id, args){}
+ * }
+ * Each property can reference a function or be written as an
+ * inline function.
+ *
+ * context: Object reference for an event handler when it is implemented
+ * as a method of a base object. Defining "context" will preserve
+ * the proper reference of "this" used in the event handler.
+ * headers: This is a defined object of client headers, as many as.
+ * desired for the transaction. These headers are sentThe object
+ * pattern is:
+ * {
+ * header: value
+ * }
+ *
+ * timeout: This value, defined as milliseconds, is a time threshold for the
+ * transaction. When this threshold is reached, and the transaction's
+ * Complete event has not yet fired, the transaction will be aborted.
+ * arguments: Object, array, string, or number passed to all registered
+ * event handlers. This value is available as the second
+ * argument in the "start" and "abort" event handlers; and, it is
+ * the third argument in the "complete", "success", and "failure"
+ * event handlers.
+ *
+ * @method _io
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @param {number} i - transaction id, if already set by queue.
+ * @return object
+ */
+ function _io(uri, c, i) {
+ var f, o, m;
+ c = c || {};
+ o = _create(c.xdr || c.form, i);
+ m = c.method ? c.method.toUpperCase() : 'GET';
+
+ if (c.form) {
+ if (c.form.upload) {
+ return Y.io._upload(o, uri, c);
+ }
+ else {
+ f = Y.io._serialize(c.form, c.data);
+ if (m === 'POST') {
+ c.data = f;
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded');
+ }
+ else if (m === 'GET') {
+ uri = _concat(uri, f);
+ }
+ }
+ }
+ else if (c.data && m === 'GET') {
+ uri = _concat(uri, c.data);
+ }
+
+ if (c.xdr) {
+ if (c.xdr.use === 'native' && window.XDomainRequest || c.xdr.use === 'flash') {
+ return Y.io.xdr(uri, o, c);
+ }
+ if (c.xdr.credentials) {
+ o.c.withCredentials = true;
+ }
+ }
+
+ o.c.onreadystatechange = function() { _readyState(o, c); };
+ try {
+ o.c.open(m, uri, true);
+ }
+ catch(e0){
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ if (c.data && m === 'POST') {
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
+ }
+
+ _setHeaders(o.c, c.headers || {});
+ try {
+ // Using "null" will result in a POST request with
+ // no Content-Length defined.
+ o.c.send(c.data || '');
+ }
+ catch(e1) {
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ _ioStart(o.id, c);
+ // If config.timeout is defined, and the request is standard XHR,
+ // initialize timeout polling.
+ if (c.timeout) {
+ _startTimeout(o, c.timeout);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _ioCancel(o, 'abort') : false;
+ },
+ isInProgress: function() {
+ return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
+ }
+ }
+ }
+
+ /**
+ * @description Method for creating and subscribing transaction events.
+ *
+ * @method _subscribe
+ * @private
+ * @static
+ * @param {string} e - event to be published
+ * @param {object} c - configuration data subset for event subscription.
+ *
+ * @return void
+ */
+ function _subscribe(e, c){
+ var evt = new Y.EventTarget().publish('transaction:' + e);
+ evt.subscribe(c.on[e], (c.context || Y), c.arguments);
+
+ return evt;
+ }
+
+ /**
+ * @description Fires event "io:start" and creates, fires a
+ * transaction-specific start event, if config.on.start is
+ * defined.
+ *
+ * @method _ioStart
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioStart(id, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_START, id);
+ if (c.on.start) {
+ evt = _subscribe('start', c);
+ evt.fire(id);
+ }
+ }
+
+
+ /**
+ * @description Fires event "io:complete" and creates, fires a
+ * transaction-specific "complete" event, if config.on.complete is
+ * defined.
+ *
+ * @method _ioComplete
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioComplete(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_COMPLETE, o.id, r);
+ if (c.on.complete) {
+ evt = _subscribe('complete', c);
+ evt.fire(o.id, r);
+ }
+ }
+
+ /**
+ * @description Fires event "io:success" and creates, fires a
+ * transaction-specific "success" event, if config.on.success is
+ * defined.
+ *
+ * @method _ioSuccess
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioSuccess(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_SUCCESS, o.id, o.c);
+ if (c.on.success) {
+ evt = _subscribe('success', c);
+ evt.fire(o.id, o.c);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:failure" and creates, fires a
+ * transaction-specific "failure" event, if config.on.failure is
+ * defined.
+ *
+ * @method _ioFailure
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioFailure(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_FAILURE, o.id, r);
+ if (c.on.failure) {
+ evt = _subscribe('failure', c);
+ evt.fire(o.id, r);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:end" and creates, fires a
+ * transaction-specific "end" event, if config.on.end is
+ * defined.
+ *
+ * @method _ioEnd
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioEnd(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_END, o.id);
+ if (c.on.end) {
+ evt = _subscribe('end', c);
+ evt.fire(o.id);
+ }
+
+ _destroy(o, c.xdr ? true : false );
+ }
+
+ /**
+ * @description Terminates a transaction due to an explicit abort or
+ * timeout.
+ *
+ * @method _ioCancel
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} s - Identifies timed out or aborted transaction.
+ *
+ * @return void
+ */
+ function _ioCancel(o, s) {
+ if (o && o.c) {
+ o.status = s;
+ o.c.abort();
+ }
+ }
+
+ /**
+ * @description Resends an XDR transaction, using the Flash tranport,
+ * if the native transport fails.
+ *
+ * @method _resend
+ * @private
+ * @static
+
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _resend(o, uri, c) {
+ var id = parseInt(o.id);
+
+ _destroy(o);
+ c.xdr.use = 'flash';
+
+ return Y.io(uri, c, id);
+ }
+
+ /**
+ * @description Method that increments _transactionId for each transaction.
+ *
+ * @method _id
+ * @private
+ * @static
+ * @return int
+ */
+ function _id() {
+ var id = transactionId;
+
+ transactionId++;
+
+ return id;
+ }
+
+ /**
+ * @description Method that creates a unique transaction object for each
+ * request.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {number} c - configuration object subset to determine if
+ * the transaction is an XDR or file upload,
+ * requiring an alternate transport.
+ * @param {number} i - transaction id
+ * @return object
+ */
+ function _create(c, i) {
+ var o = {};
+ o.id = Y.Lang.isNumber(i) ? i : _id();
+ c = c || {};
+
+ if (!c.use && !c.upload) {
+ o.c = _xhr();
+ }
+ else if (c.use) {
+ if (c.use === 'flash') {
+ o.c = Y.io._transport[c.use];
+ }
+ else if (c.use === 'native' && window.XDomainRequest) {
+ o.c = new XDomainRequest();
+ }
+ else {
+ o.c = _xhr();
+ }
+ }
+ else {
+ o.c = {};
+ }
+
+ return o;
+ };
+
+ /**
+ * @description Method that creates the XMLHttpRequest transport
+ *
+ * @method _xhr
+ * @private
+ * @static
+ * @return object
+ */
+ function _xhr() {
+ return w.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
+ }
+
+ /**
+ * @description Method that concatenates string data for HTTP GET transactions.
+ *
+ * @method _concat
+ * @private
+ * @static
+ * @param {string} s - URI or root data.
+ * @param {string} d - data to be concatenated onto URI.
+ * @return int
+ */
+ function _concat(s, d) {
+ s += ((s.indexOf('?') == -1) ? '?' : '&') + d;
+ return s;
+ }
+
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ *
+ * @method _setHeader
+ * @private
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ function _setHeader(l, v) {
+ if (v) {
+ _headers[l] = v;
+ }
+ else {
+ delete _headers[l];
+ }
+ }
+
+ /**
+ * @description Method that sets all HTTP headers to be sent in a transaction.
+ *
+ * @method _setHeaders
+ * @private
+ * @static
+ * @param {object} o - XHR instance for the specific transaction.
+ * @param {object} h - HTTP headers for the specific transaction, as defined
+ * in the configuration object passed to YUI.io().
+ * @return void
+ */
+ function _setHeaders(o, h) {
+ var p;
+
+ for (p in _headers) {
+ if (_headers.hasOwnProperty(p)) {
+ if (h[p]) {
+ // Configuration headers will supersede IO preset headers,
+ // if headers match.
+ break;
+ }
+ else {
+ h[p] = _headers[p];
+ }
+ }
+ }
+
+ for (p in h) {
+ if (h.hasOwnProperty(p)) {
+ o.setRequestHeader(p, h[p]);
+ }
+ }
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _startTimeout(o, timeout) {
+ _timeout[o.id] = w.setTimeout(function() { _ioCancel(o, 'timeout'); }, timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ *
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction id.
+ * @return void
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(_timeout[id]);
+ delete _timeout[id];
+ }
+
+ /**
+ * @description Event handler bound to onreadystatechange.
+ *
+ * @method _readyState
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _readyState(o, c) {
+ if (o.c.readyState === 4) {
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ w.setTimeout(
+ function() {
+ _ioComplete(o, c);
+ _handleResponse(o, c);
+ }, 0);
+ }
+ }
+
+ /**
+ * @description Method that determines if a transaction response qualifies
+ * as success or failure, based on the response HTTP status code, and
+ * fires the appropriate success or failure events.
+ *
+ * @method _handleResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to io().
+ * @return void
+ */
+ function _handleResponse(o, c) {
+ var status;
+ try{
+ if (o.c.status && o.c.status !== 0) {
+ status = o.c.status;
+ }
+ else {
+ status = 0;
+ }
+ }
+ catch(e) {
+ status = 0;
+ }
+
+ // IE reports HTTP 204 as HTTP 1223.
+ if (status >= 200 && status < 300 || status === 1223) {
+ _ioSuccess(o, c);
+ }
+ else {
+ _ioFailure(o, c);
+ }
+ }
+
+ function _destroy(o, transport) {
+ // IE, when using XMLHttpRequest as an ActiveX Object, will throw
+ // a "Type Mismatch" error if the event handler is set to "null".
+ if(w.XMLHttpRequest && !transport) {
+ if (o.c) {
+ o.c.onreadystatechange = null;
+ }
+ }
+
+ o.c = null;
+ o = null;
+ }
+
+ _io.start = _ioStart;
+ _io.complete = _ioComplete;
+ _io.success = _ioSuccess;
+ _io.failure = _ioFailure;
+ _io.end = _ioEnd;
+ _io._id = _id;
+ _io._timeout = _timeout;
+
+ //--------------------------------------
+ // Begin public interface definition
+ //--------------------------------------
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ * This is the interface for _setHeader().
+ *
+ * @method header
+ * @public
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ _io.header = _setHeader;
+
+ /**
+ * @description Method for requesting a transaction. This
+ * is the interface for _io().
+ *
+ * @method io
+ * @public
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @return object
+ */
+ Y.io = _io;
+ Y.io.http = _io;
+
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("io-base",function(D){var d="io:start",P="io:complete",B="io:success",F="io:failure",e="io:end",X=0,O={"X-Requested-With":"XMLHttpRequest"},Z={},K=D.config.win;function b(h,p,g){var j,l,Y;p=p||{};l=W(p.xdr||p.form,g);Y=p.method?p.method.toUpperCase():"GET";if(p.form){if(p.form.upload){return D.io._upload(l,h,p);}else{j=D.io._serialize(p.form,p.data);if(Y==="POST"){p.data=j;V("Content-Type","application/x-www-form-urlencoded");}else{if(Y==="GET"){h=Q(h,j);}}}}else{if(p.data&&Y==="GET"){h=Q(h,p.data);}}if(p.xdr){if(p.xdr.use==="native"&&window.XDomainRequest||p.xdr.use==="flash"){return D.io.xdr(h,l,p);}if(p.xdr.credentials){l.c.withCredentials=true;}}l.c.onreadystatechange=function(){c(l,p);};try{l.c.open(Y,h,true);}catch(n){if(p.xdr){return A(l,h,p);}}if(p.data&&Y==="POST"){V("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");}C(l.c,p.headers||{});try{l.c.send(p.data||"");}catch(k){if(p.xdr){return A(l,h,p);}}S(l.id,p);if(p.timeout){R(l,p.timeout);}return{id:l.id,abort:function(){return l.c?N(l,"abort"):false;},isInProgress:function(){return l.c?l.c.readyState!==4&&l.c.readyState!==0:false;}};}function U(f,g){var Y=new D.EventTarget().publish("transaction:"+f);Y.subscribe(g.on[f],(g.context||D),g.arguments);return Y;}function S(g,f){var Y;f.on=f.on||{};D.fire(d,g);if(f.on.start){Y=U("start",f);Y.fire(g);}}function G(g,h){var Y,f=g.status?{status:0,statusText:g.status}:g.c;h.on=h.on||{};D.fire(P,g.id,f);if(h.on.complete){Y=U("complete",h);Y.fire(g.id,f);}}function T(f,g){var Y;g.on=g.on||{};D.fire(B,f.id,f.c);if(g.on.success){Y=U("success",g);Y.fire(f.id,f.c);}J(f,g);}function I(g,h){var Y,f=g.status?{status:0,statusText:g.status}:g.c;h.on=h.on||{};D.fire(F,g.id,f);if(h.on.failure){Y=U("failure",h);Y.fire(g.id,f);}J(g,h);}function J(f,g){var Y;g.on=g.on||{};D.fire(e,f.id);if(g.on.end){Y=U("end",g);Y.fire(f.id);}H(f,g.xdr?true:false);}function N(f,Y){if(f&&f.c){f.status=Y;f.c.abort();}}function A(f,Y,h){var g=parseInt(f.id);H(f);h.xdr.use="flash";return D.io(Y,h,g);}function E(){var Y=X;X++;return Y;}function W(g,Y){var f={};f.id=D.Lang.isNumber(Y)?Y:E();g=g||{};if(!g.use&&!g.upload){f.c=L();}else{if(g.use){if(g.use==="flash"){f.c=D.io._transport[g.use];}else{if(g.use==="native"&&window.XDomainRequest){f.c=new XDomainRequest();}else{f.c=L();}}}else{f.c={};}}return f;}function L(){return K.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP");}function Q(Y,f){Y+=((Y.indexOf("?")==-1)?"?":"&")+f;return Y;}function V(Y,f){if(f){O[Y]=f;}else{delete O[Y];}}function C(g,Y){var f;for(f in O){if(O.hasOwnProperty(f)){if(Y[f]){break;}else{Y[f]=O[f];}}}for(f in Y){if(Y.hasOwnProperty(f)){g.setRequestHeader(f,Y[f]);}}}function R(f,Y){Z[f.id]=K.setTimeout(function(){N(f,"timeout");},Y);}function M(Y){K.clearTimeout(Z[Y]);delete Z[Y];}function c(Y,f){if(Y.c.readyState===4){if(f.timeout){M(Y.id);}K.setTimeout(function(){G(Y,f);a(Y,f);},0);}}function a(g,h){var Y;try{if(g.c.status&&g.c.status!==0){Y=g.c.status;}else{Y=0;}}catch(f){Y=0;}if(Y>=200&&Y<300||Y===1223){T(g,h);}else{I(g,h);}}function H(Y,f){if(K.XMLHttpRequest&&!f){if(Y.c){Y.c.onreadystatechange=null;}}Y.c=null;Y=null;}b.start=S;b.complete=G;b.success=T;b.failure=I;b.end=J;b._id=E;b._timeout=Z;b.header=V;D.io=b;D.io.http=b;},"3.0.0",{requires:["event-custom-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-base', function(Y) {
+
+ /**
+ * Base IO functionality. Provides basic XHR transport support.
+ * @module io
+ * @submodule io-base
+ */
+
+ /**
+ * The io class is a utility that brokers HTTP requests through a simplified
+ * interface. Specifically, it allows JavaScript to make HTTP requests to
+ * a resource without a page reload. The underlying transport for making
+ * same-domain requests is the XMLHttpRequest object. YUI.io can also use
+ * Flash, if specified as a transport, for cross-domain requests.
+ *
+ * @class io
+ */
+
+ /**
+ * @event io:start
+ * @description This event is fired by YUI.io when a transaction is initiated.
+ * @type Event Custom
+ */
+ var E_START = 'io:start',
+
+ /**
+ * @event io:complete
+ * @description This event is fired by YUI.io when a transaction is complete.
+ * Response status and data are accessible, if available.
+ * @type Event Custom
+ */
+ E_COMPLETE = 'io:complete',
+
+ /**
+ * @event io:success
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP2xx.
+ * @type Event Custom
+ */
+ E_SUCCESS = 'io:success',
+
+ /**
+ * @event io:failure
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP4xx, 5xx and above.
+ * @type Event Custom
+ */
+ E_FAILURE = 'io:failure',
+
+ /**
+ * @event io:end
+ * @description This event signifies the end of the transaction lifecycle. The
+ * transaction transport is destroyed.
+ * @type Event Custom
+ */
+ E_END = 'io:end',
+
+ //--------------------------------------
+ // Properties
+ //--------------------------------------
+ /**
+ * @description A transaction counter that increments for each transaction.
+ *
+ * @property transactionId
+ * @private
+ * @static
+ * @type int
+ */
+ transactionId = 0,
+
+ /**
+ * @description Object of default HTTP headers to be initialized and sent
+ * for all transactions.
+ *
+ * @property _headers
+ * @private
+ * @static
+ * @type object
+ */
+ _headers = {
+ 'X-Requested-With' : 'XMLHttpRequest'
+ },
+
+ /**
+ * @description Object that stores timeout values for any transaction with
+ * a defined "timeout" configuration property.
+ *
+ * @property _timeout
+ * @private
+ * @static
+ * @type object
+ */
+ _timeout = {},
+
+ // Window reference
+ w = Y.config.win;
+
+ //--------------------------------------
+ // Methods
+ //--------------------------------------
+ /**
+ * @description Method for requesting a transaction. _io() is implemented as
+ * yui.io(). Each transaction may include a configuration object. Its
+ * properties are:
+ *
+ * method: HTTP method verb (e.g., GET or POST). If this property is not
+ * not defined, the default value will be GET.
+ *
+ * data: This is the name-value string that will be sent as the transaction
+ * data. If the request is HTTP GET, the data become part of
+ * querystring. If HTTP POST, the data are sent in the message body.
+ *
+ * xdr: Defines the transport to be used for cross-domain requests. By
+ * setting this property, the transaction will use the specified
+ * transport instead of XMLHttpRequest. Currently, the only alternate
+ * transport supported is Flash (e.g., { xdr: 'flash' }).
+ *
+ * form: This is a defined object used to process HTML form as data. The
+ * properties are:
+ * {
+ * id: object, //HTML form object or id of HTML form
+ * useDisabled: boolean, //Allow disabled HTML form field values
+ * to be sent as part of the data.
+ * }
+ *
+ * on: This is a defined object used to create and handle specific
+ * events during a transaction lifecycle. These events will fire in
+ * addition to the global io events. The events are:
+ * start - This event is fired when a request is sent to a resource.
+ * complete - This event fires when the transaction is complete.
+ * success - This event fires when the response status resolves to
+ * HTTP 2xx.
+ * failure - This event fires when the response status resolves to
+ * HTTP 4xx, 5xx; and, for all transaction exceptions,
+ * including aborted transactions and transaction timeouts.
+ * end - This even is fired at the conclusion of the transaction
+ * lifecycle, after a success or failure resolution.
+ *
+ * The properties are:
+ * {
+ * start: function(id, args){},
+ * complete: function(id, responseobject, args){},
+ * success: function(id, responseobject, args){},
+ * failure: function(id, responseobject, args){},
+ * end: function(id, args){}
+ * }
+ * Each property can reference a function or be written as an
+ * inline function.
+ *
+ * context: Object reference for an event handler when it is implemented
+ * as a method of a base object. Defining "context" will preserve
+ * the proper reference of "this" used in the event handler.
+ * headers: This is a defined object of client headers, as many as.
+ * desired for the transaction. These headers are sentThe object
+ * pattern is:
+ * {
+ * header: value
+ * }
+ *
+ * timeout: This value, defined as milliseconds, is a time threshold for the
+ * transaction. When this threshold is reached, and the transaction's
+ * Complete event has not yet fired, the transaction will be aborted.
+ * arguments: Object, array, string, or number passed to all registered
+ * event handlers. This value is available as the second
+ * argument in the "start" and "abort" event handlers; and, it is
+ * the third argument in the "complete", "success", and "failure"
+ * event handlers.
+ *
+ * @method _io
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @param {number} i - transaction id, if already set by queue.
+ * @return object
+ */
+ function _io(uri, c, i) {
+ var f, o, m;
+ c = c || {};
+ o = _create(c.xdr || c.form, i);
+ m = c.method ? c.method.toUpperCase() : 'GET';
+
+ if (c.form) {
+ if (c.form.upload) {
+ return Y.io._upload(o, uri, c);
+ }
+ else {
+ f = Y.io._serialize(c.form, c.data);
+ if (m === 'POST') {
+ c.data = f;
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded');
+ }
+ else if (m === 'GET') {
+ uri = _concat(uri, f);
+ }
+ }
+ }
+ else if (c.data && m === 'GET') {
+ uri = _concat(uri, c.data);
+ }
+
+ if (c.xdr) {
+ if (c.xdr.use === 'native' && window.XDomainRequest || c.xdr.use === 'flash') {
+ return Y.io.xdr(uri, o, c);
+ }
+ if (c.xdr.credentials) {
+ o.c.withCredentials = true;
+ }
+ }
+
+ o.c.onreadystatechange = function() { _readyState(o, c); };
+ try {
+ o.c.open(m, uri, true);
+ }
+ catch(e0){
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ if (c.data && m === 'POST') {
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
+ }
+
+ _setHeaders(o.c, c.headers || {});
+ try {
+ // Using "null" will result in a POST request with
+ // no Content-Length defined.
+ o.c.send(c.data || '');
+ }
+ catch(e1) {
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ _ioStart(o.id, c);
+ // If config.timeout is defined, and the request is standard XHR,
+ // initialize timeout polling.
+ if (c.timeout) {
+ _startTimeout(o, c.timeout);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _ioCancel(o, 'abort') : false;
+ },
+ isInProgress: function() {
+ return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
+ }
+ }
+ }
+
+ /**
+ * @description Method for creating and subscribing transaction events.
+ *
+ * @method _subscribe
+ * @private
+ * @static
+ * @param {string} e - event to be published
+ * @param {object} c - configuration data subset for event subscription.
+ *
+ * @return void
+ */
+ function _subscribe(e, c){
+ var evt = new Y.EventTarget().publish('transaction:' + e);
+ evt.subscribe(c.on[e], (c.context || Y), c.arguments);
+
+ return evt;
+ }
+
+ /**
+ * @description Fires event "io:start" and creates, fires a
+ * transaction-specific start event, if config.on.start is
+ * defined.
+ *
+ * @method _ioStart
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioStart(id, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_START, id);
+ if (c.on.start) {
+ evt = _subscribe('start', c);
+ evt.fire(id);
+ }
+ }
+
+
+ /**
+ * @description Fires event "io:complete" and creates, fires a
+ * transaction-specific "complete" event, if config.on.complete is
+ * defined.
+ *
+ * @method _ioComplete
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioComplete(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_COMPLETE, o.id, r);
+ if (c.on.complete) {
+ evt = _subscribe('complete', c);
+ evt.fire(o.id, r);
+ }
+ }
+
+ /**
+ * @description Fires event "io:success" and creates, fires a
+ * transaction-specific "success" event, if config.on.success is
+ * defined.
+ *
+ * @method _ioSuccess
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioSuccess(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_SUCCESS, o.id, o.c);
+ if (c.on.success) {
+ evt = _subscribe('success', c);
+ evt.fire(o.id, o.c);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:failure" and creates, fires a
+ * transaction-specific "failure" event, if config.on.failure is
+ * defined.
+ *
+ * @method _ioFailure
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioFailure(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_FAILURE, o.id, r);
+ if (c.on.failure) {
+ evt = _subscribe('failure', c);
+ evt.fire(o.id, r);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:end" and creates, fires a
+ * transaction-specific "end" event, if config.on.end is
+ * defined.
+ *
+ * @method _ioEnd
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioEnd(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_END, o.id);
+ if (c.on.end) {
+ evt = _subscribe('end', c);
+ evt.fire(o.id);
+ }
+
+ _destroy(o, c.xdr ? true : false );
+ }
+
+ /**
+ * @description Terminates a transaction due to an explicit abort or
+ * timeout.
+ *
+ * @method _ioCancel
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} s - Identifies timed out or aborted transaction.
+ *
+ * @return void
+ */
+ function _ioCancel(o, s) {
+ if (o && o.c) {
+ o.status = s;
+ o.c.abort();
+ }
+ }
+
+ /**
+ * @description Resends an XDR transaction, using the Flash tranport,
+ * if the native transport fails.
+ *
+ * @method _resend
+ * @private
+ * @static
+
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _resend(o, uri, c) {
+ var id = parseInt(o.id);
+
+ _destroy(o);
+ c.xdr.use = 'flash';
+
+ return Y.io(uri, c, id);
+ }
+
+ /**
+ * @description Method that increments _transactionId for each transaction.
+ *
+ * @method _id
+ * @private
+ * @static
+ * @return int
+ */
+ function _id() {
+ var id = transactionId;
+
+ transactionId++;
+
+ return id;
+ }
+
+ /**
+ * @description Method that creates a unique transaction object for each
+ * request.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {number} c - configuration object subset to determine if
+ * the transaction is an XDR or file upload,
+ * requiring an alternate transport.
+ * @param {number} i - transaction id
+ * @return object
+ */
+ function _create(c, i) {
+ var o = {};
+ o.id = Y.Lang.isNumber(i) ? i : _id();
+ c = c || {};
+
+ if (!c.use && !c.upload) {
+ o.c = _xhr();
+ }
+ else if (c.use) {
+ if (c.use === 'flash') {
+ o.c = Y.io._transport[c.use];
+ }
+ else if (c.use === 'native' && window.XDomainRequest) {
+ o.c = new XDomainRequest();
+ }
+ else {
+ o.c = _xhr();
+ }
+ }
+ else {
+ o.c = {};
+ }
+
+ return o;
+ };
+
+ /**
+ * @description Method that creates the XMLHttpRequest transport
+ *
+ * @method _xhr
+ * @private
+ * @static
+ * @return object
+ */
+ function _xhr() {
+ return w.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
+ }
+
+ /**
+ * @description Method that concatenates string data for HTTP GET transactions.
+ *
+ * @method _concat
+ * @private
+ * @static
+ * @param {string} s - URI or root data.
+ * @param {string} d - data to be concatenated onto URI.
+ * @return int
+ */
+ function _concat(s, d) {
+ s += ((s.indexOf('?') == -1) ? '?' : '&') + d;
+ return s;
+ }
+
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ *
+ * @method _setHeader
+ * @private
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ function _setHeader(l, v) {
+ if (v) {
+ _headers[l] = v;
+ }
+ else {
+ delete _headers[l];
+ }
+ }
+
+ /**
+ * @description Method that sets all HTTP headers to be sent in a transaction.
+ *
+ * @method _setHeaders
+ * @private
+ * @static
+ * @param {object} o - XHR instance for the specific transaction.
+ * @param {object} h - HTTP headers for the specific transaction, as defined
+ * in the configuration object passed to YUI.io().
+ * @return void
+ */
+ function _setHeaders(o, h) {
+ var p;
+
+ for (p in _headers) {
+ if (_headers.hasOwnProperty(p)) {
+ if (h[p]) {
+ // Configuration headers will supersede IO preset headers,
+ // if headers match.
+ break;
+ }
+ else {
+ h[p] = _headers[p];
+ }
+ }
+ }
+
+ for (p in h) {
+ if (h.hasOwnProperty(p)) {
+ o.setRequestHeader(p, h[p]);
+ }
+ }
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _startTimeout(o, timeout) {
+ _timeout[o.id] = w.setTimeout(function() { _ioCancel(o, 'timeout'); }, timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ *
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction id.
+ * @return void
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(_timeout[id]);
+ delete _timeout[id];
+ }
+
+ /**
+ * @description Event handler bound to onreadystatechange.
+ *
+ * @method _readyState
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _readyState(o, c) {
+ if (o.c.readyState === 4) {
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ w.setTimeout(
+ function() {
+ _ioComplete(o, c);
+ _handleResponse(o, c);
+ }, 0);
+ }
+ }
+
+ /**
+ * @description Method that determines if a transaction response qualifies
+ * as success or failure, based on the response HTTP status code, and
+ * fires the appropriate success or failure events.
+ *
+ * @method _handleResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to io().
+ * @return void
+ */
+ function _handleResponse(o, c) {
+ var status;
+ try{
+ if (o.c.status && o.c.status !== 0) {
+ status = o.c.status;
+ }
+ else {
+ status = 0;
+ }
+ }
+ catch(e) {
+ status = 0;
+ }
+
+ // IE reports HTTP 204 as HTTP 1223.
+ if (status >= 200 && status < 300 || status === 1223) {
+ _ioSuccess(o, c);
+ }
+ else {
+ _ioFailure(o, c);
+ }
+ }
+
+ function _destroy(o, transport) {
+ // IE, when using XMLHttpRequest as an ActiveX Object, will throw
+ // a "Type Mismatch" error if the event handler is set to "null".
+ if(w.XMLHttpRequest && !transport) {
+ if (o.c) {
+ o.c.onreadystatechange = null;
+ }
+ }
+
+ o.c = null;
+ o = null;
+ }
+
+ _io.start = _ioStart;
+ _io.complete = _ioComplete;
+ _io.success = _ioSuccess;
+ _io.failure = _ioFailure;
+ _io.end = _ioEnd;
+ _io._id = _id;
+ _io._timeout = _timeout;
+
+ //--------------------------------------
+ // Begin public interface definition
+ //--------------------------------------
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ * This is the interface for _setHeader().
+ *
+ * @method header
+ * @public
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ _io.header = _setHeader;
+
+ /**
+ * @description Method for requesting a transaction. This
+ * is the interface for _io().
+ *
+ * @method io
+ * @public
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @return object
+ */
+ Y.io = _io;
+ Y.io.http = _io;
+
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-base', function(Y) {
+
+ /**
+ * Base IO functionality. Provides basic XHR transport support.
+ * @module io
+ * @submodule io-base
+ */
+
+ /**
+ * The io class is a utility that brokers HTTP requests through a simplified
+ * interface. Specifically, it allows JavaScript to make HTTP requests to
+ * a resource without a page reload. The underlying transport for making
+ * same-domain requests is the XMLHttpRequest object. YUI.io can also use
+ * Flash, if specified as a transport, for cross-domain requests.
+ *
+ * @class io
+ */
+
+ /**
+ * @event io:start
+ * @description This event is fired by YUI.io when a transaction is initiated.
+ * @type Event Custom
+ */
+ var E_START = 'io:start',
+
+ /**
+ * @event io:complete
+ * @description This event is fired by YUI.io when a transaction is complete.
+ * Response status and data are accessible, if available.
+ * @type Event Custom
+ */
+ E_COMPLETE = 'io:complete',
+
+ /**
+ * @event io:success
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP2xx.
+ * @type Event Custom
+ */
+ E_SUCCESS = 'io:success',
+
+ /**
+ * @event io:failure
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP4xx, 5xx and above.
+ * @type Event Custom
+ */
+ E_FAILURE = 'io:failure',
+
+ /**
+ * @event io:end
+ * @description This event signifies the end of the transaction lifecycle. The
+ * transaction transport is destroyed.
+ * @type Event Custom
+ */
+ E_END = 'io:end',
+
+ //--------------------------------------
+ // Properties
+ //--------------------------------------
+ /**
+ * @description A transaction counter that increments for each transaction.
+ *
+ * @property transactionId
+ * @private
+ * @static
+ * @type int
+ */
+ transactionId = 0,
+
+ /**
+ * @description Object of default HTTP headers to be initialized and sent
+ * for all transactions.
+ *
+ * @property _headers
+ * @private
+ * @static
+ * @type object
+ */
+ _headers = {
+ 'X-Requested-With' : 'XMLHttpRequest'
+ },
+
+ /**
+ * @description Object that stores timeout values for any transaction with
+ * a defined "timeout" configuration property.
+ *
+ * @property _timeout
+ * @private
+ * @static
+ * @type object
+ */
+ _timeout = {},
+
+ // Window reference
+ w = Y.config.win;
+
+ //--------------------------------------
+ // Methods
+ //--------------------------------------
+ /**
+ * @description Method for requesting a transaction. _io() is implemented as
+ * yui.io(). Each transaction may include a configuration object. Its
+ * properties are:
+ *
+ * method: HTTP method verb (e.g., GET or POST). If this property is not
+ * not defined, the default value will be GET.
+ *
+ * data: This is the name-value string that will be sent as the transaction
+ * data. If the request is HTTP GET, the data become part of
+ * querystring. If HTTP POST, the data are sent in the message body.
+ *
+ * xdr: Defines the transport to be used for cross-domain requests. By
+ * setting this property, the transaction will use the specified
+ * transport instead of XMLHttpRequest. Currently, the only alternate
+ * transport supported is Flash (e.g., { xdr: 'flash' }).
+ *
+ * form: This is a defined object used to process HTML form as data. The
+ * properties are:
+ * {
+ * id: object, //HTML form object or id of HTML form
+ * useDisabled: boolean, //Allow disabled HTML form field values
+ * to be sent as part of the data.
+ * }
+ *
+ * on: This is a defined object used to create and handle specific
+ * events during a transaction lifecycle. These events will fire in
+ * addition to the global io events. The events are:
+ * start - This event is fired when a request is sent to a resource.
+ * complete - This event fires when the transaction is complete.
+ * success - This event fires when the response status resolves to
+ * HTTP 2xx.
+ * failure - This event fires when the response status resolves to
+ * HTTP 4xx, 5xx; and, for all transaction exceptions,
+ * including aborted transactions and transaction timeouts.
+ * end - This even is fired at the conclusion of the transaction
+ * lifecycle, after a success or failure resolution.
+ *
+ * The properties are:
+ * {
+ * start: function(id, args){},
+ * complete: function(id, responseobject, args){},
+ * success: function(id, responseobject, args){},
+ * failure: function(id, responseobject, args){},
+ * end: function(id, args){}
+ * }
+ * Each property can reference a function or be written as an
+ * inline function.
+ *
+ * context: Object reference for an event handler when it is implemented
+ * as a method of a base object. Defining "context" will preserve
+ * the proper reference of "this" used in the event handler.
+ * headers: This is a defined object of client headers, as many as.
+ * desired for the transaction. These headers are sentThe object
+ * pattern is:
+ * {
+ * header: value
+ * }
+ *
+ * timeout: This value, defined as milliseconds, is a time threshold for the
+ * transaction. When this threshold is reached, and the transaction's
+ * Complete event has not yet fired, the transaction will be aborted.
+ * arguments: Object, array, string, or number passed to all registered
+ * event handlers. This value is available as the second
+ * argument in the "start" and "abort" event handlers; and, it is
+ * the third argument in the "complete", "success", and "failure"
+ * event handlers.
+ *
+ * @method _io
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @param {number} i - transaction id, if already set by queue.
+ * @return object
+ */
+ function _io(uri, c, i) {
+ var f, o, m;
+ c = c || {};
+ o = _create(c.xdr || c.form, i);
+ m = c.method ? c.method.toUpperCase() : 'GET';
+
+ if (c.form) {
+ if (c.form.upload) {
+ return Y.io._upload(o, uri, c);
+ }
+ else {
+ f = Y.io._serialize(c.form, c.data);
+ if (m === 'POST') {
+ c.data = f;
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded');
+ }
+ else if (m === 'GET') {
+ uri = _concat(uri, f);
+ }
+ }
+ }
+ else if (c.data && m === 'GET') {
+ uri = _concat(uri, c.data);
+ }
+
+ if (c.xdr) {
+ if (c.xdr.use === 'native' && window.XDomainRequest || c.xdr.use === 'flash') {
+ return Y.io.xdr(uri, o, c);
+ }
+ if (c.xdr.credentials) {
+ o.c.withCredentials = true;
+ }
+ }
+
+ o.c.onreadystatechange = function() { _readyState(o, c); };
+ try {
+ o.c.open(m, uri, true);
+ }
+ catch(e0){
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ if (c.data && m === 'POST') {
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
+ }
+
+ _setHeaders(o.c, c.headers || {});
+ try {
+ // Using "null" will result in a POST request with
+ // no Content-Length defined.
+ o.c.send(c.data || '');
+ }
+ catch(e1) {
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ _ioStart(o.id, c);
+ // If config.timeout is defined, and the request is standard XHR,
+ // initialize timeout polling.
+ if (c.timeout) {
+ _startTimeout(o, c.timeout);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _ioCancel(o, 'abort') : false;
+ },
+ isInProgress: function() {
+ return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
+ }
+ }
+ }
+
+ /**
+ * @description Method for creating and subscribing transaction events.
+ *
+ * @method _subscribe
+ * @private
+ * @static
+ * @param {string} e - event to be published
+ * @param {object} c - configuration data subset for event subscription.
+ *
+ * @return void
+ */
+ function _subscribe(e, c){
+ var evt = new Y.EventTarget().publish('transaction:' + e);
+ evt.subscribe(c.on[e], (c.context || Y), c.arguments);
+
+ return evt;
+ }
+
+ /**
+ * @description Fires event "io:start" and creates, fires a
+ * transaction-specific start event, if config.on.start is
+ * defined.
+ *
+ * @method _ioStart
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioStart(id, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_START, id);
+ if (c.on.start) {
+ evt = _subscribe('start', c);
+ evt.fire(id);
+ }
+ }
+
+
+ /**
+ * @description Fires event "io:complete" and creates, fires a
+ * transaction-specific "complete" event, if config.on.complete is
+ * defined.
+ *
+ * @method _ioComplete
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioComplete(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_COMPLETE, o.id, r);
+ if (c.on.complete) {
+ evt = _subscribe('complete', c);
+ evt.fire(o.id, r);
+ }
+ }
+
+ /**
+ * @description Fires event "io:success" and creates, fires a
+ * transaction-specific "success" event, if config.on.success is
+ * defined.
+ *
+ * @method _ioSuccess
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioSuccess(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_SUCCESS, o.id, o.c);
+ if (c.on.success) {
+ evt = _subscribe('success', c);
+ evt.fire(o.id, o.c);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:failure" and creates, fires a
+ * transaction-specific "failure" event, if config.on.failure is
+ * defined.
+ *
+ * @method _ioFailure
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioFailure(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_FAILURE, o.id, r);
+ if (c.on.failure) {
+ evt = _subscribe('failure', c);
+ evt.fire(o.id, r);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:end" and creates, fires a
+ * transaction-specific "end" event, if config.on.end is
+ * defined.
+ *
+ * @method _ioEnd
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioEnd(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_END, o.id);
+ if (c.on.end) {
+ evt = _subscribe('end', c);
+ evt.fire(o.id);
+ }
+
+ _destroy(o, c.xdr ? true : false );
+ }
+
+ /**
+ * @description Terminates a transaction due to an explicit abort or
+ * timeout.
+ *
+ * @method _ioCancel
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} s - Identifies timed out or aborted transaction.
+ *
+ * @return void
+ */
+ function _ioCancel(o, s) {
+ if (o && o.c) {
+ o.status = s;
+ o.c.abort();
+ }
+ }
+
+ /**
+ * @description Resends an XDR transaction, using the Flash tranport,
+ * if the native transport fails.
+ *
+ * @method _resend
+ * @private
+ * @static
+
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _resend(o, uri, c) {
+ var id = parseInt(o.id);
+
+ _destroy(o);
+ c.xdr.use = 'flash';
+
+ return Y.io(uri, c, id);
+ }
+
+ /**
+ * @description Method that increments _transactionId for each transaction.
+ *
+ * @method _id
+ * @private
+ * @static
+ * @return int
+ */
+ function _id() {
+ var id = transactionId;
+
+ transactionId++;
+
+ return id;
+ }
+
+ /**
+ * @description Method that creates a unique transaction object for each
+ * request.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {number} c - configuration object subset to determine if
+ * the transaction is an XDR or file upload,
+ * requiring an alternate transport.
+ * @param {number} i - transaction id
+ * @return object
+ */
+ function _create(c, i) {
+ var o = {};
+ o.id = Y.Lang.isNumber(i) ? i : _id();
+ c = c || {};
+
+ if (!c.use && !c.upload) {
+ o.c = _xhr();
+ }
+ else if (c.use) {
+ if (c.use === 'flash') {
+ o.c = Y.io._transport[c.use];
+ }
+ else if (c.use === 'native' && window.XDomainRequest) {
+ o.c = new XDomainRequest();
+ }
+ else {
+ o.c = _xhr();
+ }
+ }
+ else {
+ o.c = {};
+ }
+
+ return o;
+ };
+
+ /**
+ * @description Method that creates the XMLHttpRequest transport
+ *
+ * @method _xhr
+ * @private
+ * @static
+ * @return object
+ */
+ function _xhr() {
+ return w.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
+ }
+
+ /**
+ * @description Method that concatenates string data for HTTP GET transactions.
+ *
+ * @method _concat
+ * @private
+ * @static
+ * @param {string} s - URI or root data.
+ * @param {string} d - data to be concatenated onto URI.
+ * @return int
+ */
+ function _concat(s, d) {
+ s += ((s.indexOf('?') == -1) ? '?' : '&') + d;
+ return s;
+ }
+
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ *
+ * @method _setHeader
+ * @private
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ function _setHeader(l, v) {
+ if (v) {
+ _headers[l] = v;
+ }
+ else {
+ delete _headers[l];
+ }
+ }
+
+ /**
+ * @description Method that sets all HTTP headers to be sent in a transaction.
+ *
+ * @method _setHeaders
+ * @private
+ * @static
+ * @param {object} o - XHR instance for the specific transaction.
+ * @param {object} h - HTTP headers for the specific transaction, as defined
+ * in the configuration object passed to YUI.io().
+ * @return void
+ */
+ function _setHeaders(o, h) {
+ var p;
+
+ for (p in _headers) {
+ if (_headers.hasOwnProperty(p)) {
+ if (h[p]) {
+ // Configuration headers will supersede IO preset headers,
+ // if headers match.
+ break;
+ }
+ else {
+ h[p] = _headers[p];
+ }
+ }
+ }
+
+ for (p in h) {
+ if (h.hasOwnProperty(p)) {
+ o.setRequestHeader(p, h[p]);
+ }
+ }
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _startTimeout(o, timeout) {
+ _timeout[o.id] = w.setTimeout(function() { _ioCancel(o, 'timeout'); }, timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ *
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction id.
+ * @return void
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(_timeout[id]);
+ delete _timeout[id];
+ }
+
+ /**
+ * @description Event handler bound to onreadystatechange.
+ *
+ * @method _readyState
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _readyState(o, c) {
+ if (o.c.readyState === 4) {
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ w.setTimeout(
+ function() {
+ _ioComplete(o, c);
+ _handleResponse(o, c);
+ }, 0);
+ }
+ }
+
+ /**
+ * @description Method that determines if a transaction response qualifies
+ * as success or failure, based on the response HTTP status code, and
+ * fires the appropriate success or failure events.
+ *
+ * @method _handleResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to io().
+ * @return void
+ */
+ function _handleResponse(o, c) {
+ var status;
+ try{
+ if (o.c.status && o.c.status !== 0) {
+ status = o.c.status;
+ }
+ else {
+ status = 0;
+ }
+ }
+ catch(e) {
+ status = 0;
+ }
+
+ // IE reports HTTP 204 as HTTP 1223.
+ if (status >= 200 && status < 300 || status === 1223) {
+ _ioSuccess(o, c);
+ }
+ else {
+ _ioFailure(o, c);
+ }
+ }
+
+ function _destroy(o, transport) {
+ // IE, when using XMLHttpRequest as an ActiveX Object, will throw
+ // a "Type Mismatch" error if the event handler is set to "null".
+ if(w.XMLHttpRequest && !transport) {
+ if (o.c) {
+ o.c.onreadystatechange = null;
+ }
+ }
+
+ o.c = null;
+ o = null;
+ }
+
+ _io.start = _ioStart;
+ _io.complete = _ioComplete;
+ _io.success = _ioSuccess;
+ _io.failure = _ioFailure;
+ _io.end = _ioEnd;
+ _io._id = _id;
+ _io._timeout = _timeout;
+
+ //--------------------------------------
+ // Begin public interface definition
+ //--------------------------------------
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ * This is the interface for _setHeader().
+ *
+ * @method header
+ * @public
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ _io.header = _setHeader;
+
+ /**
+ * @description Method for requesting a transaction. This
+ * is the interface for _io().
+ *
+ * @method io
+ * @public
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @return object
+ */
+ Y.io = _io;
+ Y.io.http = _io;
+
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
+
+YUI.add('io-form', function(Y) {
+
+ /**
+ * Extends the IO base class to enable HTML form data serialization, when specified
+ * in the transaction's configuration object.
+ * @module io
+ * @submodule io-form
+ */
+
+ Y.mix(Y.io, {
+ /**
+ * @description Method to enumerate through an HTML form's elements collection
+ * and return a string comprised of key-value pairs.
+ *
+ * @method _serialize
+ * @private
+ * @static
+ * @param {object} c - YUI form node or HTML form id.
+ * @param {string} s - Transaction data defined in the configuration.
+ * @return string
+ */
+ _serialize: function(c, s) {
+ var eUC = encodeURIComponent,
+ data = [],
+ useDf = c.useDisabled || false,
+ item = 0,
+ id = (typeof c.id === 'string') ? c.id : c.id.getAttribute('id'),
+ e, f, n, v, d, i, il, j, jl, o;
+
+ if (!id) {
+ id = Y.guid('io:');
+ c.id.setAttribute('id', id);
+ }
+
+ f = Y.config.doc.getElementById(id);
+
+ // Iterate over the form elements collection to construct the
+ // label-value pairs.
+ for (i = 0, il = f.elements.length; i < il; ++i) {
+ e = f.elements[i];
+ d = e.disabled;
+ n = e.name;
+
+ if ((useDf) ? n : (n && !d)) {
+ n = encodeURIComponent(n) + '=';
+ v = encodeURIComponent(e.value);
+
+ switch (e.type) {
+ // Safari, Opera, FF all default options.value from .text if
+ // value attribute not specified in markup
+ case 'select-one':
+ if (e.selectedIndex > -1) {
+ o = e.options[e.selectedIndex];
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ break;
+ case 'select-multiple':
+ if (e.selectedIndex > -1) {
+ for (j = e.selectedIndex, jl = e.options.length; j < jl; ++j) {
+ o = e.options[j];
+ if (o.selected) {
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ }
+ }
+ break;
+ case 'radio':
+ case 'checkbox':
+ if(e.checked){
+ data[item++] = n + v;
+ }
+ break;
+ case 'file':
+ // stub case as XMLHttpRequest will only send the file path as a string.
+ case undefined:
+ // stub case for fieldset element which returns undefined.
+ case 'reset':
+ // stub case for input type reset button.
+ case 'button':
+ // stub case for input type button elements.
+ break;
+ case 'submit':
+ default:
+ data[item++] = n + v;
+ }
+ }
+ }
+ Y.log('HTML form serialized. The value is: ' + data.join('&'), 'info', 'io');
+ return s ? data.join('&') + "&" + s : data.join('&');
+ }
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','node-style']});
+
+YUI.add('io-xdr', function(Y) {
+
+ /**
+ * Extends the IO base class to provide an alternate, Flash transport, for making
+ * cross-domain requests.
+ * @module io
+ * @submodule io-xdr
+ */
+
+ /**
+ * @event io:xdrReady
+ * @description This event is fired by YUI.io when the specified transport is
+ * ready for use.
+ * @type Event Custom
+ */
+ var E_XDR_READY = 'io:xdrReady',
+
+
+ /**
+ * @description Object that stores callback handlers for cross-domain requests
+ * when using Flash as the transport.
+ *
+ * @property _fn
+ * @private
+ * @static
+ * @type object
+ */
+ _fn = {},
+
+ /**
+ * @description Map of transaction state used when XDomainRequest is the
+ * XDR transport.
+ *
+ * @property _rS
+ * @private
+ * @static
+ * @type object
+ */
+ _rS = {};
+
+ /**
+ * @description Method that creates the Flash transport swf.
+ *
+ * @method _swf
+ * @private
+ * @static
+ * @param {string} uri - location of io.swf.
+ * @param {string} yid - YUI instance id.
+ * @return void
+ */
+ function _swf(uri, yid) {
+ var o = '<object id="yuiIoSwf" type="application/x-shockwave-flash" data="' +
+ uri + '" width="0" height="0">' +
+ '<param name="movie" value="' + uri + '">' +
+ '<param name="FlashVars" value="yid=' + yid + '">' +
+ '<param name="allowScriptAccess" value="always">' +
+ '</object>',
+ c = document.createElement('div');
+
+ document.body.appendChild(c);
+ c.innerHTML = o;
+ }
+
+ /**
+ * @description Sets event handlers for XDomainRequest transactions.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @return void
+ */
+ function _xdr(o, c) {
+ o.c.onprogress = function() { _rS[o.id] = 3; }
+ o.c.onload = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'success');
+ };
+ o.c.onerror = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'failure');
+ };
+ if (c.timeout) {
+ o.c.ontimeout = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'timeout');
+ };
+ o.c.timeout = c.timeout;
+ }
+ }
+
+ /**
+ * @description Creates a response object for XDR transactions, for success
+ * and failure cases.
+ *
+ * @method _data
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {boolean} isFlash - True if Flash was used as the transport.
+ * @param {boolean} isXML - True if the response data are XML.
+ *
+ * @return object
+ */
+ function _data(o, isFlash, isXML) {
+ var text, xml;
+
+ if (!o.status) {
+ text = isFlash ? decodeURI(o.c.responseText) : o.c.responseText;
+ xml = isXML ? Y.DataType.XML.parse(text) : null;
+
+ return { id: o.id, c: { responseText: text, responseXML: xml } };
+ }
+ else {
+ return { id: o.id, status: o.status };
+ }
+
+ }
+
+ /**
+ * @description Method for intiating an XDR transaction abort.
+ *
+ * @method _abort
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _abort(o, c) {
+ return c.xdr.use === 'flash' ? o.c.abort(o.id, c) : o.c.abort();
+ }
+
+ /**
+ * @description Method for determining if an XDR transaction has completed
+ * and all data are received.
+ *
+ * @method _isInProgress.
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _isInProgress(o, t) {
+ return (t === 'flash' && o.c) ? o.c.isInProgress(o.id) : _rS[o.id] !== 4;
+ }
+
+ Y.mix(Y.io, {
+
+ /**
+ * @description Map of io transports.
+ *
+ * @property _transport
+ * @private
+ * @static
+ * @type object
+ */
+ _transport: {},
+
+ /**
+ * @description Method for accessing the transport's interface for making a
+ * cross-domain transaction.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ xdr: function(uri, o, c) {
+ if (c.on && c.xdr.use === 'flash') {
+ _fn[o.id] = {
+ on: c.on,
+ context: c.context,
+ arguments: c.arguments
+ };
+ // These nodes do not need to be serialized across Flash's
+ // ExternalInterface. Doing so will result in exceptions.
+ c.context = null;
+ c.form = null;
+
+ o.c.send(uri, c, o.id);
+ }
+ else if (window.XDomainRequest) {
+ _xdr(o, c);
+ o.c.open(c.method || 'GET', uri);
+ o.c.send(c.data);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _abort(o, c) : false;
+ },
+ isInProgress: function() {
+ return o.c ? _isInProgress(o, c.xdr.use) : false;
+ }
+ }
+ },
+
+ /**
+ * @description Response controller for cross-domain requests when using the
+ * Flash transport or IE8's XDomainRequest object.
+ *
+ * @method xdrResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @param {string} e - Event name
+ * @return object
+ */
+ xdrResponse: function(o, c, e) {
+ var m, fn,
+ isFlash = c.xdr.use === 'flash' ? true : false,
+ isXML = c.xdr.dataType === 'xml' ? true : false;
+ c.on = c.on || {};
+
+ if (isFlash) {
+ m = _fn || {};
+ fn = m[o.id] ? m[o.id] : null;
+ if (fn) {
+ c.on = fn.on;
+ c.context = fn.context;
+ c.arguments = fn.arguments;
+ }
+ }
+ if (e === ('abort' || 'timeout')) {
+ o.status = e;
+ }
+
+ switch (e) {
+ case 'start':
+ Y.io.start(o.id, c);
+ break;
+ case 'success':
+ Y.io.success(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ case 'timeout':
+ case 'abort':
+ case 'failure':
+ Y.io.failure(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ }
+ },
+
+ /**
+ * @description Fires event "io:xdrReady"
+ *
+ * @method xdrReady
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ xdrReady: function(id) {
+ Y.fire(E_XDR_READY, id);
+ },
+
+ /**
+ * @description Method to initialize the desired transport.
+ *
+ * @method transport
+ * @public
+ * @static
+ * @param {object} o - object of transport configurations.
+ * @return void
+ */
+ transport: function(o) {
+ var id = o.yid ? o.yid : Y.id;
+
+ _swf(o.src, id);
+ this._transport.flash = Y.config.doc.getElementById('yuiIoSwf');
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','datatype-xml']});
+
+YUI.add('io-upload-iframe', function(Y) {
+
+ /**
+ * Extends the IO base class to enable file uploads, with HTML forms,
+ * using an iframe as the transport medium.
+ * @module io
+ * @submodule io-upload-iframe
+ */
+
+ var w = Y.config.win;
+ /**
+ * @description Parses the POST data object and creates hidden form elements
+ * for each key-value, and appends them to the HTML form object.
+ * @method appendData
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {string} s The key-value POST data.
+ * @return {array} o Array of created fields.
+ */
+ function _addData(f, s) {
+ var o = [],
+ m = s.split('='),
+ i, l;
+
+ for (i = 0, l = m.length - 1; i < l; i++) {
+ o[i] = document.createElement('input');
+ o[i].type = 'hidden';
+ o[i].name = m[i].substring(m[i].lastIndexOf('&') + 1);
+ o[i].value = (i + 1 === l) ? m[i + 1] : m[i + 1].substring(0, (m[i + 1].lastIndexOf('&')));
+ f.appendChild(o[i]);
+ Y.log('key: ' + o[i].name + ' and value: ' + o[i].value + ' added as form data.', 'info', 'io');
+ }
+
+ return o;
+ }
+
+ /**
+ * @description Removes the custom fields created to pass additional POST
+ * data, along with the HTML form fields.
+ * @method f
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} o HTML form fields created from configuration.data.
+ * @return {void}
+ */
+ function _removeData(f, o) {
+ var i, l;
+
+ for(i = 0, l = o.length; i < l; i++){
+ f.removeChild(o[i]);
+ }
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _setAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} id The Transaction ID.
+ * @param {object} uri Qualified path to transaction resource.
+ * @return {void}
+ */
+ function _setAttrs(f, id, uri) {
+ var ie8 = (document.documentMode && document.documentMode === 8) ? true : false;
+
+ f.setAttribute('action', uri);
+ f.setAttribute('method', 'POST');
+ f.setAttribute('target', 'ioupload' + id );
+ f.setAttribute(Y.UA.ie && !ie8 ? 'encoding' : 'enctype', 'multipart/form-data');
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _resetAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} a Object of original attributes.
+ * @return {void}
+ */
+ function _resetAttrs(f, a){
+ var p;
+
+ for (p in a) {
+ if (a.hasOwnProperty(a, p)) {
+ if (a[p]) {
+ f.setAttribute(p, f[p]);
+ }
+ else {
+ f.removeAttribute(p);
+ }
+ }
+ }
+ }
+
+ /**
+ * @description Creates the iframe transported used in file upload
+ * transactions, and binds the response event handler.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _create(o, c) {
+ var i = Y.Node.create('<iframe id="ioupload' + o.id + '" name="ioupload' + o.id + '" />');
+ i._node.style.position = 'absolute';
+ i._node.style.top = '-1000px';
+ i._node.style.left = '-1000px';
+
+ Y.one('body').appendChild(i);
+ // Bind the onload handler to the iframe to detect the file upload response.
+ Y.on("load", function() { _handle(o, c) }, '#ioupload' + o.id);
+ }
+
+ /**
+ * @description Bound to the iframe's Load event and processes
+ * the response data.
+ * @method _handle
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _handle(o, c) {
+ var d = Y.one('#ioupload' + o.id).get('contentWindow.document'),
+ b = d.one('body'),
+ xml = (d._node.nodeType === 9),
+ p;
+
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ if (b) {
+ // When a response Content-Type of "text/plain" is used, Firefox and Safari
+ // will wrap the response string with <pre></pre>.
+ p = b.query('pre:first-child');
+ o.c.responseText = p ? p.get('innerHTML') : b.get('innerHTML');
+ Y.log('The responseText value for transaction ' + o.id + ' is: ' + o.c.responseText + '.', 'info', 'io');
+ }
+ else if (xml) {
+ o.c.responseXML = d._node;
+ Y.log('The response for transaction ' + o.id + ' is an XML document.', 'info', 'io');
+ }
+
+ Y.io.complete(o, c);
+ Y.io.end(o, c);
+ // The transaction is complete, so call _destroy to remove
+ // the event listener bound to the iframe transport, and then
+ // destroy the iframe.
+ w.setTimeout( function() { _destroy(o.id); }, 0);
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _startTimeout(o, c) {
+ Y.io._timeout[o.id] = w.setTimeout(
+ function() {
+ var r = { id: o.id, status: 'timeout' };
+
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ Y.log('Transaction ' + o.id + ' timeout.', 'info', 'io');
+ }, c.timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction ID.
+ * @return {void}
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(Y.io._timeout[id]);
+ delete Y.io._timeout[id];
+ }
+
+ /**
+ * @description
+ * @method _destroy
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _destroy(id) {
+ Y.Event.purgeElement('#ioupload' + id, false);
+ Y.one('body').removeChild(Y.one('#ioupload' + id));
+ Y.log('The iframe transport for transaction ' + id + ' has been destroyed.', 'info', 'io');
+ }
+
+ Y.mix(Y.io, {
+ /**
+ * @description Uploads HTML form data, inclusive of files/attachments,
+ * using the iframe created in _create to facilitate the transaction.
+ * @method _upload
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ _upload: function(o, uri, c) {
+ var f = (typeof c.form.id === 'string') ? Y.config.doc.getElementById(c.form.id) : c.form.id,
+ fields,
+ // Track original HTML form attribute values.
+ attr = {
+ action: f.getAttribute('action'),
+ target: f.getAttribute('target')
+ };
+
+ _create(o, c);
+ // Initialize the HTML form properties in case they are
+ // not defined in the HTML form.
+ _setAttrs(f, o.id, uri);
+ if (c.data) {
+ fields = _addData(f, c.data);
+ }
+
+ // Start polling if a callback is present and the timeout
+ // property has been defined.
+ if (c.timeout) {
+ _startTimeout(o, c);
+ Y.log('Transaction timeout started for transaction ' + o.id + '.', 'info', 'io');
+ }
+
+ // Start file upload.
+ f.submit();
+ Y.io.start(o.id, c);
+ if (c.data) {
+ _removeData(f, fields);
+ }
+ // Restore HTML form attributes to their original values.
+ _resetAttrs(f, attr);
+
+ return {
+ id: o.id,
+ abort: function() {
+ var r = { id: o.id, status: 'abort' };
+
+ if (Y.one('#ioupload' + o.id)) {
+ _destroy(o.id);
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ Y.log('Transaction ' + o.id + ' aborted.', 'info', 'io');
+ }
+ else {
+ Y.log('Attempted to abort transaction ' + o.id + ' but transaction has completed.', 'info', 'io');
+ return false;
+ }
+ },
+ isInProgress: function() {
+ return Y.one('#ioupload' + o.id) ? true : false;
+ }
+ }
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','event-base']});
+
+YUI.add('io-queue', function(Y) {
+
+ /**
+ * Extends the IO base class to implement Queue for synchronous
+ * transaction processing.
+ * @module io
+ * @submodule io-queue
+ */
+
+ /**
+ * @description Array of transactions queued for processing
+ *
+ * @property _yQ
+ * @private
+ * @static
+ * @type Object
+ */
+ var _q = new Y.Queue(),
+
+ /**
+ * @description Reference to "io:complete" event handler.
+ *
+ * @property _e
+ * @private
+ * @static
+ * @type Object
+ */
+ _e,
+
+ _activeId,
+ /**
+ * @description Property to determine whether the queue is set to
+ * 1 (active) or 0 (inactive). When inactive, transactions
+ * will be stored in the queue until the queue is set to active.
+ *
+ * @property _qState
+ * @private
+ * @static
+ * @type int
+ */
+ _qState = 1;
+
+ /**
+ * @description Method for requesting a transaction, and queueing the
+ * request before it is sent to the resource.
+ *
+ * @method _queue
+ * @private
+ * @static
+ * @return Object
+ */
+ function _queue(uri, c) {
+ var o = { uri: uri, id: Y.io._id(), cfg:c };
+
+ _q.add(o);
+ if (_qState === 1) {
+ _shift();
+ }
+
+ Y.log('Object queued. Transaction id is ' + o.id, 'info', 'io');
+ return o;
+ }
+
+ /**
+ * @description Method Process the first transaction from the
+ * queue in FIFO order.
+ *
+ * @method _shift
+ * @private
+ * @static
+ * @return void
+ */
+ function _shift() {
+ var o = _q.next();
+
+ _activeId = o.id;
+ _qState = 0;
+ Y.io(o.uri, o.cfg, o.id);
+ }
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ *
+ * @method _unshift
+ * @private
+ * @static
+ * @return void
+ */
+ function _unshift(o) {
+ _q.promote(o);
+ }
+
+ function _next(id) {
+ _qState = 1;
+ if (_activeId === id && _q.size() > 0) {
+ _shift();
+ }
+ }
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue.
+ *
+ * @method _remove
+ * @private
+ * @static
+ * @return void
+ */
+ function _remove(o) {
+ _q.remove(o);
+ }
+
+ function _start() {
+ _qState = 1;
+
+ if (_q.size() > 0) {
+ _shift();
+ }
+ Y.log('Queue started.', 'info', 'io');
+ }
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is reset to "active".
+ *
+ * @method _stop
+ * @private
+ * @static
+ * @return void
+ */
+ function _stop() {
+ _qState = 0;
+ Y.log('Queue stopped.', 'info', 'io');
+ };
+
+ /**
+ * @description Method to query the current size of the queue.
+ *
+ * @method _size
+ * @private
+ * @static
+ * @return int
+ */
+ function _size() {
+ return _q.size();
+ };
+
+ _e = Y.on('io:complete', function(id) { _next(id); }, Y.io);
+
+ /**
+ * @description Method to query the current size of the queue, or to
+ * set a maximum queue size. This is the interface for _size().
+ *
+ * @method size
+ * @public
+ * @static
+ * @param {number} i - Specified maximum size of queue.
+ * @return number
+ */
+ _queue.size = _size;
+
+ /**
+ * @description Method for setting the queue to active. If there are
+ * transactions pending in the queue, they will be processed from the
+ * queue in FIFO order. This is the interface for _start().
+ *
+ * @method start
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.start = _start;
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is restarted. This is the
+ * interface for _stop().
+ *
+ * @method stop
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.stop = _stop;
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ * This is the interface for _unshift().
+ *
+ * @method promote
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.promote = _unshift;
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue. This is the interface for _remove().
+ *
+ * @method remove
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.remove = _remove;
+
+ Y.mix(Y.io, {
+ queue: _queue
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','queue-promote']});
+
+
+
+YUI.add('io', function(Y){}, '3.0.0' ,{use:['io-base', 'io-form', 'io-xdr', 'io-upload-iframe', 'io-queue']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-form', function(Y) {
+
+ /**
+ * Extends the IO base class to enable HTML form data serialization, when specified
+ * in the transaction's configuration object.
+ * @module io
+ * @submodule io-form
+ */
+
+ Y.mix(Y.io, {
+ /**
+ * @description Method to enumerate through an HTML form's elements collection
+ * and return a string comprised of key-value pairs.
+ *
+ * @method _serialize
+ * @private
+ * @static
+ * @param {object} c - YUI form node or HTML form id.
+ * @param {string} s - Transaction data defined in the configuration.
+ * @return string
+ */
+ _serialize: function(c, s) {
+ var eUC = encodeURIComponent,
+ data = [],
+ useDf = c.useDisabled || false,
+ item = 0,
+ id = (typeof c.id === 'string') ? c.id : c.id.getAttribute('id'),
+ e, f, n, v, d, i, il, j, jl, o;
+
+ if (!id) {
+ id = Y.guid('io:');
+ c.id.setAttribute('id', id);
+ }
+
+ f = Y.config.doc.getElementById(id);
+
+ // Iterate over the form elements collection to construct the
+ // label-value pairs.
+ for (i = 0, il = f.elements.length; i < il; ++i) {
+ e = f.elements[i];
+ d = e.disabled;
+ n = e.name;
+
+ if ((useDf) ? n : (n && !d)) {
+ n = encodeURIComponent(n) + '=';
+ v = encodeURIComponent(e.value);
+
+ switch (e.type) {
+ // Safari, Opera, FF all default options.value from .text if
+ // value attribute not specified in markup
+ case 'select-one':
+ if (e.selectedIndex > -1) {
+ o = e.options[e.selectedIndex];
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ break;
+ case 'select-multiple':
+ if (e.selectedIndex > -1) {
+ for (j = e.selectedIndex, jl = e.options.length; j < jl; ++j) {
+ o = e.options[j];
+ if (o.selected) {
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ }
+ }
+ break;
+ case 'radio':
+ case 'checkbox':
+ if(e.checked){
+ data[item++] = n + v;
+ }
+ break;
+ case 'file':
+ // stub case as XMLHttpRequest will only send the file path as a string.
+ case undefined:
+ // stub case for fieldset element which returns undefined.
+ case 'reset':
+ // stub case for input type reset button.
+ case 'button':
+ // stub case for input type button elements.
+ break;
+ case 'submit':
+ default:
+ data[item++] = n + v;
+ }
+ }
+ }
+ Y.log('HTML form serialized. The value is: ' + data.join('&'), 'info', 'io');
+ return s ? data.join('&') + "&" + s : data.join('&');
+ }
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','node-style']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("io-form",function(A){A.mix(A.io,{_serialize:function(M,R){var I=encodeURIComponent,H=[],N=M.useDisabled||false,Q=0,B=(typeof M.id==="string")?M.id:M.id.getAttribute("id"),K,J,D,P,L,G,O,E,F,C;if(!B){B=A.guid("io:");M.id.setAttribute("id",B);}J=A.config.doc.getElementById(B);for(G=0,O=J.elements.length;G<O;++G){K=J.elements[G];L=K.disabled;D=K.name;if((N)?D:(D&&!L)){D=encodeURIComponent(D)+"=";P=encodeURIComponent(K.value);switch(K.type){case"select-one":if(K.selectedIndex>-1){C=K.options[K.selectedIndex];H[Q++]=D+I((C.attributes.value&&C.attributes.value.specified)?C.value:C.text);}break;case"select-multiple":if(K.selectedIndex>-1){for(E=K.selectedIndex,F=K.options.length;E<F;++E){C=K.options[E];if(C.selected){H[Q++]=D+I((C.attributes.value&&C.attributes.value.specified)?C.value:C.text);}}}break;case"radio":case"checkbox":if(K.checked){H[Q++]=D+P;}break;case"file":case undefined:case"reset":case"button":break;case"submit":default:H[Q++]=D+P;}}}return R?H.join("&")+"&"+R:H.join("&");}},true);},"3.0.0",{requires:["io-base","node-base","node-style"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-form', function(Y) {
+
+ /**
+ * Extends the IO base class to enable HTML form data serialization, when specified
+ * in the transaction's configuration object.
+ * @module io
+ * @submodule io-form
+ */
+
+ Y.mix(Y.io, {
+ /**
+ * @description Method to enumerate through an HTML form's elements collection
+ * and return a string comprised of key-value pairs.
+ *
+ * @method _serialize
+ * @private
+ * @static
+ * @param {object} c - YUI form node or HTML form id.
+ * @param {string} s - Transaction data defined in the configuration.
+ * @return string
+ */
+ _serialize: function(c, s) {
+ var eUC = encodeURIComponent,
+ data = [],
+ useDf = c.useDisabled || false,
+ item = 0,
+ id = (typeof c.id === 'string') ? c.id : c.id.getAttribute('id'),
+ e, f, n, v, d, i, il, j, jl, o;
+
+ if (!id) {
+ id = Y.guid('io:');
+ c.id.setAttribute('id', id);
+ }
+
+ f = Y.config.doc.getElementById(id);
+
+ // Iterate over the form elements collection to construct the
+ // label-value pairs.
+ for (i = 0, il = f.elements.length; i < il; ++i) {
+ e = f.elements[i];
+ d = e.disabled;
+ n = e.name;
+
+ if ((useDf) ? n : (n && !d)) {
+ n = encodeURIComponent(n) + '=';
+ v = encodeURIComponent(e.value);
+
+ switch (e.type) {
+ // Safari, Opera, FF all default options.value from .text if
+ // value attribute not specified in markup
+ case 'select-one':
+ if (e.selectedIndex > -1) {
+ o = e.options[e.selectedIndex];
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ break;
+ case 'select-multiple':
+ if (e.selectedIndex > -1) {
+ for (j = e.selectedIndex, jl = e.options.length; j < jl; ++j) {
+ o = e.options[j];
+ if (o.selected) {
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ }
+ }
+ break;
+ case 'radio':
+ case 'checkbox':
+ if(e.checked){
+ data[item++] = n + v;
+ }
+ break;
+ case 'file':
+ // stub case as XMLHttpRequest will only send the file path as a string.
+ case undefined:
+ // stub case for fieldset element which returns undefined.
+ case 'reset':
+ // stub case for input type reset button.
+ case 'button':
+ // stub case for input type button elements.
+ break;
+ case 'submit':
+ default:
+ data[item++] = n + v;
+ }
+ }
+ }
+ return s ? data.join('&') + "&" + s : data.join('&');
+ }
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','node-style']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("io-base",function(D){var d="io:start",P="io:complete",B="io:success",F="io:failure",e="io:end",X=0,O={"X-Requested-With":"XMLHttpRequest"},Z={},K=D.config.win;function b(h,p,g){var j,l,Y;p=p||{};l=W(p.xdr||p.form,g);Y=p.method?p.method.toUpperCase():"GET";if(p.form){if(p.form.upload){return D.io._upload(l,h,p);}else{j=D.io._serialize(p.form,p.data);if(Y==="POST"){p.data=j;V("Content-Type","application/x-www-form-urlencoded");}else{if(Y==="GET"){h=Q(h,j);}}}}else{if(p.data&&Y==="GET"){h=Q(h,p.data);}}if(p.xdr){if(p.xdr.use==="native"&&window.XDomainRequest||p.xdr.use==="flash"){return D.io.xdr(h,l,p);}if(p.xdr.credentials){l.c.withCredentials=true;}}l.c.onreadystatechange=function(){c(l,p);};try{l.c.open(Y,h,true);}catch(n){if(p.xdr){return A(l,h,p);}}if(p.data&&Y==="POST"){V("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");}C(l.c,p.headers||{});try{l.c.send(p.data||"");}catch(k){if(p.xdr){return A(l,h,p);}}S(l.id,p);if(p.timeout){R(l,p.timeout);}return{id:l.id,abort:function(){return l.c?N(l,"abort"):false;},isInProgress:function(){return l.c?l.c.readyState!==4&&l.c.readyState!==0:false;}};}function U(f,g){var Y=new D.EventTarget().publish("transaction:"+f);Y.subscribe(g.on[f],(g.context||D),g.arguments);return Y;}function S(g,f){var Y;f.on=f.on||{};D.fire(d,g);if(f.on.start){Y=U("start",f);Y.fire(g);}}function G(g,h){var Y,f=g.status?{status:0,statusText:g.status}:g.c;h.on=h.on||{};D.fire(P,g.id,f);if(h.on.complete){Y=U("complete",h);Y.fire(g.id,f);}}function T(f,g){var Y;g.on=g.on||{};D.fire(B,f.id,f.c);if(g.on.success){Y=U("success",g);Y.fire(f.id,f.c);}J(f,g);}function I(g,h){var Y,f=g.status?{status:0,statusText:g.status}:g.c;h.on=h.on||{};D.fire(F,g.id,f);if(h.on.failure){Y=U("failure",h);Y.fire(g.id,f);}J(g,h);}function J(f,g){var Y;g.on=g.on||{};D.fire(e,f.id);if(g.on.end){Y=U("end",g);Y.fire(f.id);}H(f,g.xdr?true:false);}function N(f,Y){if(f&&f.c){f.status=Y;f.c.abort();}}function A(f,Y,h){var g=parseInt(f.id);H(f);h.xdr.use="flash";return D.io(Y,h,g);}function E(){var Y=X;X++;return Y;}function W(g,Y){var f={};f.id=D.Lang.isNumber(Y)?Y:E();g=g||{};if(!g.use&&!g.upload){f.c=L();}else{if(g.use){if(g.use==="flash"){f.c=D.io._transport[g.use];}else{if(g.use==="native"&&window.XDomainRequest){f.c=new XDomainRequest();}else{f.c=L();}}}else{f.c={};}}return f;}function L(){return K.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP");}function Q(Y,f){Y+=((Y.indexOf("?")==-1)?"?":"&")+f;return Y;}function V(Y,f){if(f){O[Y]=f;}else{delete O[Y];}}function C(g,Y){var f;for(f in O){if(O.hasOwnProperty(f)){if(Y[f]){break;}else{Y[f]=O[f];}}}for(f in Y){if(Y.hasOwnProperty(f)){g.setRequestHeader(f,Y[f]);}}}function R(f,Y){Z[f.id]=K.setTimeout(function(){N(f,"timeout");},Y);}function M(Y){K.clearTimeout(Z[Y]);delete Z[Y];}function c(Y,f){if(Y.c.readyState===4){if(f.timeout){M(Y.id);}K.setTimeout(function(){G(Y,f);a(Y,f);},0);}}function a(g,h){var Y;try{if(g.c.status&&g.c.status!==0){Y=g.c.status;}else{Y=0;}}catch(f){Y=0;}if(Y>=200&&Y<300||Y===1223){T(g,h);}else{I(g,h);}}function H(Y,f){if(K.XMLHttpRequest&&!f){if(Y.c){Y.c.onreadystatechange=null;}}Y.c=null;Y=null;}b.start=S;b.complete=G;b.success=T;b.failure=I;b.end=J;b._id=E;b._timeout=Z;b.header=V;D.io=b;D.io.http=b;},"3.0.0",{requires:["event-custom-base"]});YUI.add("io-form",function(A){A.mix(A.io,{_serialize:function(M,R){var I=encodeURIComponent,H=[],N=M.useDisabled||false,Q=0,B=(typeof M.id==="string")?M.id:M.id.getAttribute("id"),K,J,D,P,L,G,O,E,F,C;if(!B){B=A.guid("io:");M.id.setAttribute("id",B);}J=A.config.doc.getElementById(B);for(G=0,O=J.elements.length;G<O;++G){K=J.elements[G];L=K.disabled;D=K.name;if((N)?D:(D&&!L)){D=encodeURIComponent(D)+"=";P=encodeURIComponent(K.value);switch(K.type){case"select-one":if(K.selectedIndex>-1){C=K.options[K.selectedIndex];H[Q++]=D+I((C.attributes.value&&C.attributes.value.specified)?C.value:C.text);}break;case"select-multiple":if(K.selectedIndex>-1){for(E=K.selectedIndex,F=K.options.length;E<F;++E){C=K.options[E];if(C.selected){H[Q++]=D+I((C.attributes.value&&C.attributes.value.specified)?C.value:C.text);}}}break;case"radio":case"checkbox":if(K.checked){H[Q++]=D+P;}break;case"file":case undefined:case"reset":case"button":break;case"submit":default:H[Q++]=D+P;}}}return R?H.join("&")+"&"+R:H.join("&");}},true);},"3.0.0",{requires:["io-base","node-base","node-style"]});YUI.add("io-xdr",function(A){var I="io:xdrReady",D={},E={};function F(J,M){var K='<object id="yuiIoSwf" type="application/x-shockwave-flash" data="'+J+'" width="0" height="0">'+'<param name="movie" value="'+J+'">'+'<param name="FlashVars" value="yid='+M+'">'+'<param name="allowScriptAccess" value="always">'+"</object>",L=document.createElement("div");document.body.appendChild(L);L.innerHTML=K;}function G(J,K){J.c.onprogress=function(){E[J.id]=3;};J.c.onload=function(){E[J.id]=4;A.io.xdrResponse(J,K,"success");};J.c.onerror=function(){E[J.id]=4;A.io.xdrResponse(J,K,"failure");};if(K.timeout){J.c.ontimeout=function(){E[J.id]=4;A.io.xdrResponse(J,K,"timeout");};J.c.timeout=K.timeout;}}function B(M,K,N){var L,J;if(!M.status){L=K?decodeURI(M.c.responseText):M.c.responseText;J=N?A.DataType.XML.parse(L):null;return{id:M.id,c:{responseText:L,responseXML:J}};}else{return{id:M.id,status:M.status};}}function H(J,K){return K.xdr.use==="flash"?J.c.abort(J.id,K):J.c.abort();}function C(K,J){return(J==="flash"&&K.c)?K.c.isInProgress(K.id):E[K.id]!==4;}A.mix(A.io,{_transport:{},xdr:function(J,K,L){if(L.on&&L.xdr.use==="flash"){D[K.id]={on:L.on,context:L.context,arguments:L.arguments};L.context=null;L.form=null;K.c.send(J,L,K.id);}else{if(window.XDomainRequest){G(K,L);K.c.open(L.method||"GET",J);K.c.send(L.data);}}return{id:K.id,abort:function(){return K.c?H(K,L):false;},isInProgress:function(){return K.c?C(K,L.xdr.use):false;}};},xdrResponse:function(N,P,M){var J,L,K=P.xdr.use==="flash"?true:false,O=P.xdr.dataType==="xml"?true:false;P.on=P.on||{};if(K){J=D||{};L=J[N.id]?J[N.id]:null;if(L){P.on=L.on;
+P.context=L.context;P.arguments=L.arguments;}}if(M===("abort"||"timeout")){N.status=M;}switch(M){case"start":A.io.start(N.id,P);break;case"success":A.io.success(B(N,K,O),P);K?delete J[N.id]:delete E[N.id];break;case"timeout":case"abort":case"failure":A.io.failure(B(N,K,O),P);K?delete J[N.id]:delete E[N.id];break;}},xdrReady:function(J){A.fire(I,J);},transport:function(J){var K=J.yid?J.yid:A.id;F(J.src,K);this._transport.flash=A.config.doc.getElementById("yuiIoSwf");}});},"3.0.0",{requires:["io-base","datatype-xml"]});YUI.add("io-upload-iframe",function(B){var I=B.config.win;function D(P,O){var Q=[],L=O.split("="),N,M;for(N=0,M=L.length-1;N<M;N++){Q[N]=document.createElement("input");Q[N].type="hidden";Q[N].name=L[N].substring(L[N].lastIndexOf("&")+1);Q[N].value=(N+1===M)?L[N+1]:L[N+1].substring(0,(L[N+1].lastIndexOf("&")));P.appendChild(Q[N]);}return Q;}function F(N,O){var M,L;for(M=0,L=O.length;M<L;M++){N.removeChild(O[M]);}}function E(N,O,M){var L=(document.documentMode&&document.documentMode===8)?true:false;N.setAttribute("action",M);N.setAttribute("method","POST");N.setAttribute("target","ioupload"+O);N.setAttribute(B.UA.ie&&!L?"encoding":"enctype","multipart/form-data");}function K(M,L){var N;for(N in L){if(L.hasOwnProperty(L,N)){if(L[N]){M.setAttribute(N,M[N]);}else{M.removeAttribute(N);}}}}function J(M,N){var L=B.Node.create('<iframe id="ioupload'+M.id+'" name="ioupload'+M.id+'" />');L._node.style.position="absolute";L._node.style.top="-1000px";L._node.style.left="-1000px";B.one("body").appendChild(L);B.on("load",function(){A(M,N);},"#ioupload"+M.id);}function A(P,Q){var O=B.one("#ioupload"+P.id).get("contentWindow.document"),L=O.one("body"),M=(O._node.nodeType===9),N;if(Q.timeout){H(P.id);}if(L){N=L.query("pre:first-child");P.c.responseText=N?N.get("innerHTML"):L.get("innerHTML");}else{if(M){P.c.responseXML=O._node;}}B.io.complete(P,Q);B.io.end(P,Q);I.setTimeout(function(){G(P.id);},0);}function C(L,M){B.io._timeout[L.id]=I.setTimeout(function(){var N={id:L.id,status:"timeout"};B.io.complete(N,M);B.io.end(N,M);},M.timeout);}function H(L){I.clearTimeout(B.io._timeout[L]);delete B.io._timeout[L];}function G(L){B.Event.purgeElement("#ioupload"+L,false);B.one("body").removeChild(B.one("#ioupload"+L));}B.mix(B.io,{_upload:function(P,N,Q){var O=(typeof Q.form.id==="string")?B.config.doc.getElementById(Q.form.id):Q.form.id,M,L={action:O.getAttribute("action"),target:O.getAttribute("target")};J(P,Q);E(O,P.id,N);if(Q.data){M=D(O,Q.data);}if(Q.timeout){C(P,Q);}O.submit();B.io.start(P.id,Q);if(Q.data){F(O,M);}K(O,L);return{id:P.id,abort:function(){var R={id:P.id,status:"abort"};if(B.one("#ioupload"+P.id)){G(P.id);B.io.complete(R,Q);B.io.end(R,Q);}else{return false;}},isInProgress:function(){return B.one("#ioupload"+P.id)?true:false;}};}});},"3.0.0",{requires:["io-base","node-base","event-base"]});YUI.add("io-queue",function(B){var A=new B.Queue(),I,G,M=1;function J(N,P){var O={uri:N,id:B.io._id(),cfg:P};A.add(O);if(M===1){F();}return O;}function F(){var N=A.next();G=N.id;M=0;B.io(N.uri,N.cfg,N.id);}function D(N){A.promote(N);}function C(N){M=1;if(G===N&&A.size()>0){F();}}function L(N){A.remove(N);}function E(){M=1;if(A.size()>0){F();}}function H(){M=0;}function K(){return A.size();}I=B.on("io:complete",function(N){C(N);},B.io);J.size=K;J.start=E;J.stop=H;J.promote=D;J.remove=L;B.mix(B.io,{queue:J},true);},"3.0.0",{requires:["io-base","queue-promote"]});YUI.add("io",function(A){},"3.0.0",{use:["io-base","io-form","io-xdr","io-upload-iframe","io-queue"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-queue', function(Y) {
+
+ /**
+ * Extends the IO base class to implement Queue for synchronous
+ * transaction processing.
+ * @module io
+ * @submodule io-queue
+ */
+
+ /**
+ * @description Array of transactions queued for processing
+ *
+ * @property _yQ
+ * @private
+ * @static
+ * @type Object
+ */
+ var _q = new Y.Queue(),
+
+ /**
+ * @description Reference to "io:complete" event handler.
+ *
+ * @property _e
+ * @private
+ * @static
+ * @type Object
+ */
+ _e,
+
+ _activeId,
+ /**
+ * @description Property to determine whether the queue is set to
+ * 1 (active) or 0 (inactive). When inactive, transactions
+ * will be stored in the queue until the queue is set to active.
+ *
+ * @property _qState
+ * @private
+ * @static
+ * @type int
+ */
+ _qState = 1;
+
+ /**
+ * @description Method for requesting a transaction, and queueing the
+ * request before it is sent to the resource.
+ *
+ * @method _queue
+ * @private
+ * @static
+ * @return Object
+ */
+ function _queue(uri, c) {
+ var o = { uri: uri, id: Y.io._id(), cfg:c };
+
+ _q.add(o);
+ if (_qState === 1) {
+ _shift();
+ }
+
+ Y.log('Object queued. Transaction id is' + o.id, 'info', 'io');
+ return o;
+ }
+
+ /**
+ * @description Method Process the first transaction from the
+ * queue in FIFO order.
+ *
+ * @method _shift
+ * @private
+ * @static
+ * @return void
+ */
+ function _shift() {
+ var o = _q.next();
+
+ _activeId = o.id;
+ _qState = 0;
+ Y.io(o.uri, o.cfg, o.id);
+ }
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ *
+ * @method _unshift
+ * @private
+ * @static
+ * @return void
+ */
+ function _unshift(o) {
+ _q.promote(o);
+ }
+
+ function _next(id) {
+ _qState = 1;
+ if (_activeId === id && _q.size() > 0) {
+ _shift();
+ }
+ }
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue.
+ *
+ * @method _remove
+ * @private
+ * @static
+ * @return void
+ */
+ function _remove(o) {
+ _q.remove(o);
+ }
+
+ function _start() {
+ _qState = 1;
+
+ if (_q.size() > 0) {
+ _shift();
+ }
+ Y.log('Queue started.', 'info', 'io');
+ }
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is reset to "active".
+ *
+ * @method _stop
+ * @private
+ * @static
+ * @return void
+ */
+ function _stop() {
+ _qState = 0;
+ Y.log('Queue stopped.', 'info', 'io');
+ };
+
+ /**
+ * @description Method to query the current size of the queue.
+ *
+ * @method _size
+ * @private
+ * @static
+ * @return int
+ */
+ function _size() {
+ return _q.size();
+ };
+
+ _e = Y.on('io:complete', function(id) { _next(id); }, Y.io);
+
+ /**
+ * @description Method to query the current size of the queue, or to
+ * set a maximum queue size. This is the interface for _size().
+ *
+ * @method size
+ * @public
+ * @static
+ * @param {number} i - Specified maximum size of queue.
+ * @return number
+ */
+ _queue.size = _size;
+
+ /**
+ * @description Method for setting the queue to active. If there are
+ * transactions pending in the queue, they will be processed from the
+ * queue in FIFO order. This is the interface for _start().
+ *
+ * @method start
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.start = _start;
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is restarted. This is the
+ * interface for _stop().
+ *
+ * @method stop
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.stop = _stop;
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ * This is the interface for _unshift().
+ *
+ * @method promote
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.promote = _unshift;
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue. This is the interface for _remove().
+ *
+ * @method remove
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.remove = _remove;
+
+ Y.mix(Y.io, {
+ queue: _queue
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','queue-promote']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("io-queue",function(B){var A=new B.Queue(),I,G,M=1;function J(N,P){var O={uri:N,id:B.io._id(),cfg:P};A.add(O);if(M===1){F();}return O;}function F(){var N=A.next();G=N.id;M=0;B.io(N.uri,N.cfg,N.id);}function D(N){A.promote(N);}function C(N){M=1;if(G===N&&A.size()>0){F();}}function L(N){A.remove(N);}function E(){M=1;if(A.size()>0){F();}}function H(){M=0;}function K(){return A.size();}I=B.on("io:complete",function(N){C(N);},B.io);J.size=K;J.start=E;J.stop=H;J.promote=D;J.remove=L;B.mix(B.io,{queue:J},true);},"3.0.0",{requires:["io-base","queue-promote"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-queue', function(Y) {
+
+ /**
+ * Extends the IO base class to implement Queue for synchronous
+ * transaction processing.
+ * @module io
+ * @submodule io-queue
+ */
+
+ /**
+ * @description Array of transactions queued for processing
+ *
+ * @property _yQ
+ * @private
+ * @static
+ * @type Object
+ */
+ var _q = new Y.Queue(),
+
+ /**
+ * @description Reference to "io:complete" event handler.
+ *
+ * @property _e
+ * @private
+ * @static
+ * @type Object
+ */
+ _e,
+
+ _activeId,
+ /**
+ * @description Property to determine whether the queue is set to
+ * 1 (active) or 0 (inactive). When inactive, transactions
+ * will be stored in the queue until the queue is set to active.
+ *
+ * @property _qState
+ * @private
+ * @static
+ * @type int
+ */
+ _qState = 1;
+
+ /**
+ * @description Method for requesting a transaction, and queueing the
+ * request before it is sent to the resource.
+ *
+ * @method _queue
+ * @private
+ * @static
+ * @return Object
+ */
+ function _queue(uri, c) {
+ var o = { uri: uri, id: Y.io._id(), cfg:c };
+
+ _q.add(o);
+ if (_qState === 1) {
+ _shift();
+ }
+
+ return o;
+ }
+
+ /**
+ * @description Method Process the first transaction from the
+ * queue in FIFO order.
+ *
+ * @method _shift
+ * @private
+ * @static
+ * @return void
+ */
+ function _shift() {
+ var o = _q.next();
+
+ _activeId = o.id;
+ _qState = 0;
+ Y.io(o.uri, o.cfg, o.id);
+ }
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ *
+ * @method _unshift
+ * @private
+ * @static
+ * @return void
+ */
+ function _unshift(o) {
+ _q.promote(o);
+ }
+
+ function _next(id) {
+ _qState = 1;
+ if (_activeId === id && _q.size() > 0) {
+ _shift();
+ }
+ }
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue.
+ *
+ * @method _remove
+ * @private
+ * @static
+ * @return void
+ */
+ function _remove(o) {
+ _q.remove(o);
+ }
+
+ function _start() {
+ _qState = 1;
+
+ if (_q.size() > 0) {
+ _shift();
+ }
+ }
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is reset to "active".
+ *
+ * @method _stop
+ * @private
+ * @static
+ * @return void
+ */
+ function _stop() {
+ _qState = 0;
+ };
+
+ /**
+ * @description Method to query the current size of the queue.
+ *
+ * @method _size
+ * @private
+ * @static
+ * @return int
+ */
+ function _size() {
+ return _q.size();
+ };
+
+ _e = Y.on('io:complete', function(id) { _next(id); }, Y.io);
+
+ /**
+ * @description Method to query the current size of the queue, or to
+ * set a maximum queue size. This is the interface for _size().
+ *
+ * @method size
+ * @public
+ * @static
+ * @param {number} i - Specified maximum size of queue.
+ * @return number
+ */
+ _queue.size = _size;
+
+ /**
+ * @description Method for setting the queue to active. If there are
+ * transactions pending in the queue, they will be processed from the
+ * queue in FIFO order. This is the interface for _start().
+ *
+ * @method start
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.start = _start;
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is restarted. This is the
+ * interface for _stop().
+ *
+ * @method stop
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.stop = _stop;
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ * This is the interface for _unshift().
+ *
+ * @method promote
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.promote = _unshift;
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue. This is the interface for _remove().
+ *
+ * @method remove
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.remove = _remove;
+
+ Y.mix(Y.io, {
+ queue: _queue
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','queue-promote']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-upload-iframe', function(Y) {
+
+ /**
+ * Extends the IO base class to enable file uploads, with HTML forms,
+ * using an iframe as the transport medium.
+ * @module io
+ * @submodule io-upload-iframe
+ */
+
+ var w = Y.config.win;
+ /**
+ * @description Parses the POST data object and creates hidden form elements
+ * for each key-value, and appends them to the HTML form object.
+ * @method appendData
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {string} s The key-value POST data.
+ * @return {array} e Array of created fields.
+ */
+ function _addData(f, s) {
+ var o = [],
+ m = s.split('='),
+ i, l;
+
+ for (i = 0, l = m.length - 1; i < l; i++) {
+ o[i] = document.createElement('input');
+ o[i].type = 'hidden';
+ o[i].name = m[i].substring(m[i].lastIndexOf('&') + 1);
+ o[i].value = (i + 1 === l) ? m[i + 1] : m[i + 1].substring(0, (m[i + 1].lastIndexOf('&')));
+ f.appendChild(o[i]);
+ Y.log('key: ' + o[i].name + ' and value: ' + o[i].value + ' added as form data.', 'info', 'io');
+ }
+
+ return o;
+ }
+
+ /**
+ * @description Removes the custom fields created to pass additional POST
+ * data, along with the HTML form fields.
+ * @method f
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} o HTML form fields created from configuration.data.
+ * @return {void}
+ */
+ function _removeData(f, o) {
+ var i, l;
+
+ for(i = 0, l = o.length; i < l; i++){
+ f.removeChild(o[i]);
+ }
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _setAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} id The Transaction ID.
+ * @param {object} uri Qualified path to transaction resource.
+ * @return {void}
+ */
+ function _setAttrs(f, id, uri) {
+ var ie8 = (document.documentMode && document.documentMode === 8) ? true : false;
+
+ f.setAttribute('action', uri);
+ f.setAttribute('method', 'POST');
+ f.setAttribute('target', 'ioupload' + id );
+ f.setAttribute(Y.UA.ie && !ie8 ? 'encoding' : 'enctype', 'multipart/form-data');
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _resetAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} a Object of original attributes.
+ * @return {void}
+ */
+ function _resetAttrs(f, a){
+ var p;
+
+ for (p in a) {
+ if (a.hasOwnProperty(a, p)) {
+ if (a[p]) {
+ f.setAttribute(p, f[p]);
+ }
+ else {
+ f.removeAttribute(p);
+ }
+ }
+ }
+ }
+
+ /**
+ * @description Creates the iframe transported used in file upload
+ * transactions, and binds the response event handler.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _create(o, c) {
+ var i = Y.Node.create('<iframe id="ioupload' + o.id + '" name="ioupload' + o.id + '" />');
+ i._node.style.position = 'absolute';
+ i._node.style.top = '-1000px';
+ i._node.style.left = '-1000px';
+
+ Y.one('body').appendChild(i);
+ // Bind the onload handler to the iframe to detect the file upload response.
+ Y.on("load", function() { _handle(o, c) }, '#ioupload' + o.id);
+ }
+
+ /**
+ * @description Bound to the iframe's Load event and processes
+ * the response data.
+ * @method _handle
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _handle(o, c) {
+ var d = Y.one('#ioupload' + o.id).get('contentWindow.document'),
+ b = d.one('body'),
+ xml = (d._node.nodeType === 9),
+ p;
+
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ if (b) {
+ // When a response Content-Type of "text/plain" is used, Firefox and Safari
+ // will wrap the response string with <pre></pre>.
+ p = b.query('pre:first-child');
+ o.c.responseText = p ? p.get('innerHTML') : b.get('innerHTML');
+ Y.log('The responseText value for transaction ' + o.id + ' is: ' + o.c.responseText + '.', 'info', 'io');
+ }
+ else if (xml) {
+ o.c.responseXML = d._node;
+ Y.log('The response for transaction ' + o.id + ' is an XML document.', 'info', 'io');
+ }
+
+ Y.io.complete(o, c);
+ Y.io.end(o, c);
+ // The transaction is complete, so call _destroy to remove
+ // the event listener bound to the iframe transport, and then
+ // destroy the iframe.
+ w.setTimeout( function() { _destroy(o.id); }, 0);
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _startTimeout(o, c) {
+ Y.io._timeout[o.id] = w.setTimeout(
+ function() {
+ var r = { id: o.id, status: 'timeout' };
+
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ Y.log('Transaction ' + o.id + ' timeout.', 'info', 'io');
+ }, c.timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction ID.
+ * @return {void}
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(Y.io._timeout[id]);
+ delete Y.io._timeout[id];
+ }
+
+ /**
+ * @description
+ * @method _destroy
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _destroy(id) {
+ Y.Event.purgeElement('#ioupload' + id, false);
+ Y.one('body').removeChild(Y.one('#ioupload' + id));
+ Y.log('The iframe transport for transaction ' + id + ' has been destroyed.', 'info', 'io');
+ }
+
+ Y.mix(Y.io, {
+ /**
+ * @description Uploads HTML form data, inclusive of files/attachments,
+ * using the iframe created in _create to facilitate the transaction.
+ * @method _upload
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ _upload: function(o, uri, c) {
+ var f = (typeof c.form.id === 'string') ? Y.config.doc.getElementById(c.form.id) : c.form.id,
+ fields,
+ // Track original HTML form attribute values.
+ attr = {
+ action: f.getAttribute('action'),
+ target: f.getAttribute('target')
+ };
+
+ _create(o, c);
+ // Initialize the HTML form properties in case they are
+ // not defined in the HTML form.
+ _setAttrs(f, o.id, uri);
+ if (c.data) {
+ fields = _addData(f, c.data);
+ }
+
+ // Start polling if a callback is present and the timeout
+ // property has been defined.
+ if (c.timeout) {
+ _startTimeout(o, c);
+ Y.log('Transaction timeout started for transaction ' + o.id + '.', 'info', 'io');
+ }
+
+ // Start file upload.
+ f.submit();
+ Y.io.start(o.id, c);
+ if (c.data) {
+ _removeData(f, fields);
+ }
+ // Restore HTML form attributes to their original values.
+ _resetAttrs(f, attr);
+
+ return {
+ id: o.id,
+ abort: function() {
+ var r = { id: o.id, status: 'abort' };
+
+ if (Y.one('#ioupload' + o.id)) {
+ _destroy(o.id);
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ Y.log('Transaction ' + o.id + ' aborted.', 'info', 'io');
+ }
+ else {
+ Y.log('Attempted to abort transaction ' + o.id + ' but transaction has completed.', 'info', 'io');
+ return false;
+ }
+ },
+ isInProgress: function() {
+ return Y.one('#ioupload' + o.id) ? true : false;
+ }
+ }
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','event-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("io-upload-iframe",function(B){var I=B.config.win;function D(P,O){var Q=[],L=O.split("="),N,M;for(N=0,M=L.length-1;N<M;N++){Q[N]=document.createElement("input");Q[N].type="hidden";Q[N].name=L[N].substring(L[N].lastIndexOf("&")+1);Q[N].value=(N+1===M)?L[N+1]:L[N+1].substring(0,(L[N+1].lastIndexOf("&")));P.appendChild(Q[N]);}return Q;}function F(N,O){var M,L;for(M=0,L=O.length;M<L;M++){N.removeChild(O[M]);}}function E(N,O,M){var L=(document.documentMode&&document.documentMode===8)?true:false;N.setAttribute("action",M);N.setAttribute("method","POST");N.setAttribute("target","ioupload"+O);N.setAttribute(B.UA.ie&&!L?"encoding":"enctype","multipart/form-data");}function K(M,L){var N;for(N in L){if(L.hasOwnProperty(L,N)){if(L[N]){M.setAttribute(N,M[N]);}else{M.removeAttribute(N);}}}}function J(M,N){var L=B.Node.create('<iframe id="ioupload'+M.id+'" name="ioupload'+M.id+'" />');L._node.style.position="absolute";L._node.style.top="-1000px";L._node.style.left="-1000px";B.one("body").appendChild(L);B.on("load",function(){A(M,N);},"#ioupload"+M.id);}function A(P,Q){var O=B.one("#ioupload"+P.id).get("contentWindow.document"),L=O.one("body"),M=(O._node.nodeType===9),N;if(Q.timeout){H(P.id);}if(L){N=L.query("pre:first-child");P.c.responseText=N?N.get("innerHTML"):L.get("innerHTML");}else{if(M){P.c.responseXML=O._node;}}B.io.complete(P,Q);B.io.end(P,Q);I.setTimeout(function(){G(P.id);},0);}function C(L,M){B.io._timeout[L.id]=I.setTimeout(function(){var N={id:L.id,status:"timeout"};B.io.complete(N,M);B.io.end(N,M);},M.timeout);}function H(L){I.clearTimeout(B.io._timeout[L]);delete B.io._timeout[L];}function G(L){B.Event.purgeElement("#ioupload"+L,false);B.one("body").removeChild(B.one("#ioupload"+L));}B.mix(B.io,{_upload:function(P,N,Q){var O=(typeof Q.form.id==="string")?B.config.doc.getElementById(Q.form.id):Q.form.id,M,L={action:O.getAttribute("action"),target:O.getAttribute("target")};J(P,Q);E(O,P.id,N);if(Q.data){M=D(O,Q.data);}if(Q.timeout){C(P,Q);}O.submit();B.io.start(P.id,Q);if(Q.data){F(O,M);}K(O,L);return{id:P.id,abort:function(){var R={id:P.id,status:"abort"};if(B.one("#ioupload"+P.id)){G(P.id);B.io.complete(R,Q);B.io.end(R,Q);}else{return false;}},isInProgress:function(){return B.one("#ioupload"+P.id)?true:false;}};}});},"3.0.0",{requires:["io-base","node-base","event-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-upload-iframe', function(Y) {
+
+ /**
+ * Extends the IO base class to enable file uploads, with HTML forms,
+ * using an iframe as the transport medium.
+ * @module io
+ * @submodule io-upload-iframe
+ */
+
+ var w = Y.config.win;
+ /**
+ * @description Parses the POST data object and creates hidden form elements
+ * for each key-value, and appends them to the HTML form object.
+ * @method appendData
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {string} s The key-value POST data.
+ * @return {array} e Array of created fields.
+ */
+ function _addData(f, s) {
+ var o = [],
+ m = s.split('='),
+ i, l;
+
+ for (i = 0, l = m.length - 1; i < l; i++) {
+ o[i] = document.createElement('input');
+ o[i].type = 'hidden';
+ o[i].name = m[i].substring(m[i].lastIndexOf('&') + 1);
+ o[i].value = (i + 1 === l) ? m[i + 1] : m[i + 1].substring(0, (m[i + 1].lastIndexOf('&')));
+ f.appendChild(o[i]);
+ }
+
+ return o;
+ }
+
+ /**
+ * @description Removes the custom fields created to pass additional POST
+ * data, along with the HTML form fields.
+ * @method f
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} o HTML form fields created from configuration.data.
+ * @return {void}
+ */
+ function _removeData(f, o) {
+ var i, l;
+
+ for(i = 0, l = o.length; i < l; i++){
+ f.removeChild(o[i]);
+ }
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _setAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} id The Transaction ID.
+ * @param {object} uri Qualified path to transaction resource.
+ * @return {void}
+ */
+ function _setAttrs(f, id, uri) {
+ var ie8 = (document.documentMode && document.documentMode === 8) ? true : false;
+
+ f.setAttribute('action', uri);
+ f.setAttribute('method', 'POST');
+ f.setAttribute('target', 'ioupload' + id );
+ f.setAttribute(Y.UA.ie && !ie8 ? 'encoding' : 'enctype', 'multipart/form-data');
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _resetAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} a Object of original attributes.
+ * @return {void}
+ */
+ function _resetAttrs(f, a){
+ var p;
+
+ for (p in a) {
+ if (a.hasOwnProperty(a, p)) {
+ if (a[p]) {
+ f.setAttribute(p, f[p]);
+ }
+ else {
+ f.removeAttribute(p);
+ }
+ }
+ }
+ }
+
+ /**
+ * @description Creates the iframe transported used in file upload
+ * transactions, and binds the response event handler.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _create(o, c) {
+ var i = Y.Node.create('<iframe id="ioupload' + o.id + '" name="ioupload' + o.id + '" />');
+ i._node.style.position = 'absolute';
+ i._node.style.top = '-1000px';
+ i._node.style.left = '-1000px';
+
+ Y.one('body').appendChild(i);
+ // Bind the onload handler to the iframe to detect the file upload response.
+ Y.on("load", function() { _handle(o, c) }, '#ioupload' + o.id);
+ }
+
+ /**
+ * @description Bound to the iframe's Load event and processes
+ * the response data.
+ * @method _handle
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _handle(o, c) {
+ var d = Y.one('#ioupload' + o.id).get('contentWindow.document'),
+ b = d.one('body'),
+ xml = (d._node.nodeType === 9),
+ p;
+
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ if (b) {
+ // When a response Content-Type of "text/plain" is used, Firefox and Safari
+ // will wrap the response string with <pre></pre>.
+ p = b.query('pre:first-child');
+ o.c.responseText = p ? p.get('innerHTML') : b.get('innerHTML');
+ }
+ else if (xml) {
+ o.c.responseXML = d._node;
+ }
+
+ Y.io.complete(o, c);
+ Y.io.end(o, c);
+ // The transaction is complete, so call _destroy to remove
+ // the event listener bound to the iframe transport, and then
+ // destroy the iframe.
+ w.setTimeout( function() { _destroy(o.id); }, 0);
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _startTimeout(o, c) {
+ Y.io._timeout[o.id] = w.setTimeout(
+ function() {
+ var r = { id: o.id, status: 'timeout' };
+
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ }, c.timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction ID.
+ * @return {void}
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(Y.io._timeout[id]);
+ delete Y.io._timeout[id];
+ }
+
+ /**
+ * @description
+ * @method _destroy
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _destroy(id) {
+ Y.Event.purgeElement('#ioupload' + id, false);
+ Y.one('body').removeChild(Y.one('#ioupload' + id));
+ }
+
+ Y.mix(Y.io, {
+ /**
+ * @description Uploads HTML form data, inclusive of files/attachments,
+ * using the iframe created in _create to facilitate the transaction.
+ * @method _upload
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ _upload: function(o, uri, c) {
+ var f = (typeof c.form.id === 'string') ? Y.config.doc.getElementById(c.form.id) : c.form.id,
+ fields,
+ // Track original HTML form attribute values.
+ attr = {
+ action: f.getAttribute('action'),
+ target: f.getAttribute('target')
+ };
+
+ _create(o, c);
+ // Initialize the HTML form properties in case they are
+ // not defined in the HTML form.
+ _setAttrs(f, o.id, uri);
+ if (c.data) {
+ fields = _addData(f, c.data);
+ }
+
+ // Start polling if a callback is present and the timeout
+ // property has been defined.
+ if (c.timeout) {
+ _startTimeout(o, c);
+ }
+
+ // Start file upload.
+ f.submit();
+ Y.io.start(o.id, c);
+ if (c.data) {
+ _removeData(f, fields);
+ }
+ // Restore HTML form attributes to their original values.
+ _resetAttrs(f, attr);
+
+ return {
+ id: o.id,
+ abort: function() {
+ var r = { id: o.id, status: 'abort' };
+
+ if (Y.one('#ioupload' + o.id)) {
+ _destroy(o.id);
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ }
+ else {
+ return false;
+ }
+ },
+ isInProgress: function() {
+ return Y.one('#ioupload' + o.id) ? true : false;
+ }
+ }
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','event-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-xdr', function(Y) {
+
+ /**
+ * Extends the IO base class to provide an alternate, Flash transport, for making
+ * cross-domain requests.
+ * @module io
+ * @submodule io-xdr
+ */
+
+ /**
+ * @event io:xdrReady
+ * @description This event is fired by YUI.io when the specified transport is
+ * ready for use.
+ * @type Event Custom
+ */
+ var E_XDR_READY = 'io:xdrReady',
+
+
+ /**
+ * @description Object that stores callback handlers for cross-domain requests
+ * when using Flash as the transport.
+ *
+ * @property _fn
+ * @private
+ * @static
+ * @type object
+ */
+ _fn = {},
+
+ /**
+ * @description Map of transaction state used when XDomainRequest is the
+ * XDR transport.
+ *
+ * @property _rS
+ * @private
+ * @static
+ * @type object
+ */
+ _rS = {};
+
+ /**
+ * @description Method that creates the Flash transport swf.
+ *
+ * @method _swf
+ * @private
+ * @static
+ * @param {string} uri - location of io.swf.
+ * @param {string} yid - YUI instance id.
+ * @return void
+ */
+ function _swf(uri, yid) {
+ var o = '<object id="yuiIoSwf" type="application/x-shockwave-flash" data="' +
+ uri + '" width="0" height="0">' +
+ '<param name="movie" value="' + uri + '">' +
+ '<param name="FlashVars" value="yid=' + yid + '">' +
+ '<param name="allowScriptAccess" value="always">' +
+ '</object>',
+ c = document.createElement('div');
+
+ document.body.appendChild(c);
+ c.innerHTML = o;
+ }
+
+ /**
+ * @description Sets event handlers for XDomainRequest transactions.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @return void
+ */
+ function _xdr(o, c) {
+ o.c.onprogress = function() { _rS[o.id] = 3; }
+ o.c.onload = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'success');
+ };
+ o.c.onerror = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'failure');
+ };
+ if (c.timeout) {
+ o.c.ontimeout = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'timeout');
+ };
+ o.c.timeout = c.timeout;
+ }
+ }
+
+ /**
+ * @description Creates a response object for XDR transactions, for success
+ * and failure cases.
+ *
+ * @method _data
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {boolean} isFlash - True if Flash was used as the transport.
+ * @param {boolean} isXML - True if the response data are XML.
+ *
+ * @return object
+ */
+ function _data(o, isFlash, isXML) {
+ var text, xml;
+
+ if (!o.status) {
+ text = isFlash ? decodeURI(o.c.responseText) : o.c.responseText;
+ xml = isXML ? Y.DataType.XML.parse(text) : null;
+
+ return { id: o.id, c: { responseText: text, responseXML: xml } };
+ }
+ else {
+ return { id: o.id, status: o.status };
+ }
+
+ }
+
+ /**
+ * @description Method for intiating an XDR transaction abort.
+ *
+ * @method _abort
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _abort(o, c) {
+ return c.xdr.use === 'flash' ? o.c.abort(o.id, c) : o.c.abort();
+ }
+
+ /**
+ * @description Method for determining if an XDR transaction has completed
+ * and all data are received.
+ *
+ * @method _isInProgress.
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _isInProgress(o, t) {
+ return (t === 'flash' && o.c) ? o.c.isInProgress(o.id) : _rS[o.id] !== 4;
+ }
+
+ Y.mix(Y.io, {
+
+ /**
+ * @description Map of io transports.
+ *
+ * @property _transport
+ * @private
+ * @static
+ * @type object
+ */
+ _transport: {},
+
+ /**
+ * @description Method for accessing the transport's interface for making a
+ * cross-domain transaction.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ xdr: function(uri, o, c) {
+ if (c.on && c.xdr.use === 'flash') {
+ _fn[o.id] = {
+ on: c.on,
+ context: c.context,
+ arguments: c.arguments
+ };
+ // These nodes do not need to be serialized across Flash's
+ // ExternalInterface. Doing so will result in exceptions.
+ c.context = null;
+ c.form = null;
+
+ o.c.send(uri, c, o.id);
+ }
+ else if (window.XDomainRequest) {
+ _xdr(o, c);
+ o.c.open(c.method || 'GET', uri);
+ o.c.send(c.data);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _abort(o, c) : false;
+ },
+ isInProgress: function() {
+ return o.c ? _isInProgress(o, c.xdr.use) : false;
+ }
+ }
+ },
+
+ /**
+ * @description Response controller for cross-domain requests when using the
+ * Flash transport or IE8's XDomainRequest object.
+ *
+ * @method xdrResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @param {string} e - Event name
+ * @return object
+ */
+ xdrResponse: function(o, c, e) {
+ var m, fn,
+ isFlash = c.xdr.use === 'flash' ? true : false,
+ isXML = c.xdr.dataType === 'xml' ? true : false;
+ c.on = c.on || {};
+
+ if (isFlash) {
+ m = _fn || {};
+ fn = m[o.id] ? m[o.id] : null;
+ if (fn) {
+ c.on = fn.on;
+ c.context = fn.context;
+ c.arguments = fn.arguments;
+ }
+ }
+ if (e === ('abort' || 'timeout')) {
+ o.status = e;
+ }
+
+ switch (e) {
+ case 'start':
+ Y.io.start(o.id, c);
+ break;
+ case 'success':
+ Y.io.success(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ case 'timeout':
+ case 'abort':
+ case 'failure':
+ Y.io.failure(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ }
+ },
+
+ /**
+ * @description Fires event "io:xdrReady"
+ *
+ * @method xdrReady
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ xdrReady: function(id) {
+ Y.fire(E_XDR_READY, id);
+ },
+
+ /**
+ * @description Method to initialize the desired transport.
+ *
+ * @method transport
+ * @public
+ * @static
+ * @param {object} o - object of transport configurations.
+ * @return void
+ */
+ transport: function(o) {
+ var id = o.yid ? o.yid : Y.id;
+
+ _swf(o.src, id);
+ this._transport.flash = Y.config.doc.getElementById('yuiIoSwf');
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','datatype-xml']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("io-xdr",function(A){var I="io:xdrReady",D={},E={};function F(J,M){var K='<object id="yuiIoSwf" type="application/x-shockwave-flash" data="'+J+'" width="0" height="0">'+'<param name="movie" value="'+J+'">'+'<param name="FlashVars" value="yid='+M+'">'+'<param name="allowScriptAccess" value="always">'+"</object>",L=document.createElement("div");document.body.appendChild(L);L.innerHTML=K;}function G(J,K){J.c.onprogress=function(){E[J.id]=3;};J.c.onload=function(){E[J.id]=4;A.io.xdrResponse(J,K,"success");};J.c.onerror=function(){E[J.id]=4;A.io.xdrResponse(J,K,"failure");};if(K.timeout){J.c.ontimeout=function(){E[J.id]=4;A.io.xdrResponse(J,K,"timeout");};J.c.timeout=K.timeout;}}function B(M,K,N){var L,J;if(!M.status){L=K?decodeURI(M.c.responseText):M.c.responseText;J=N?A.DataType.XML.parse(L):null;return{id:M.id,c:{responseText:L,responseXML:J}};}else{return{id:M.id,status:M.status};}}function H(J,K){return K.xdr.use==="flash"?J.c.abort(J.id,K):J.c.abort();}function C(K,J){return(J==="flash"&&K.c)?K.c.isInProgress(K.id):E[K.id]!==4;}A.mix(A.io,{_transport:{},xdr:function(J,K,L){if(L.on&&L.xdr.use==="flash"){D[K.id]={on:L.on,context:L.context,arguments:L.arguments};L.context=null;L.form=null;K.c.send(J,L,K.id);}else{if(window.XDomainRequest){G(K,L);K.c.open(L.method||"GET",J);K.c.send(L.data);}}return{id:K.id,abort:function(){return K.c?H(K,L):false;},isInProgress:function(){return K.c?C(K,L.xdr.use):false;}};},xdrResponse:function(N,P,M){var J,L,K=P.xdr.use==="flash"?true:false,O=P.xdr.dataType==="xml"?true:false;P.on=P.on||{};if(K){J=D||{};L=J[N.id]?J[N.id]:null;if(L){P.on=L.on;P.context=L.context;P.arguments=L.arguments;}}if(M===("abort"||"timeout")){N.status=M;}switch(M){case"start":A.io.start(N.id,P);break;case"success":A.io.success(B(N,K,O),P);K?delete J[N.id]:delete E[N.id];break;case"timeout":case"abort":case"failure":A.io.failure(B(N,K,O),P);K?delete J[N.id]:delete E[N.id];break;}},xdrReady:function(J){A.fire(I,J);},transport:function(J){var K=J.yid?J.yid:A.id;F(J.src,K);this._transport.flash=A.config.doc.getElementById("yuiIoSwf");}});},"3.0.0",{requires:["io-base","datatype-xml"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-xdr', function(Y) {
+
+ /**
+ * Extends the IO base class to provide an alternate, Flash transport, for making
+ * cross-domain requests.
+ * @module io
+ * @submodule io-xdr
+ */
+
+ /**
+ * @event io:xdrReady
+ * @description This event is fired by YUI.io when the specified transport is
+ * ready for use.
+ * @type Event Custom
+ */
+ var E_XDR_READY = 'io:xdrReady',
+
+
+ /**
+ * @description Object that stores callback handlers for cross-domain requests
+ * when using Flash as the transport.
+ *
+ * @property _fn
+ * @private
+ * @static
+ * @type object
+ */
+ _fn = {},
+
+ /**
+ * @description Map of transaction state used when XDomainRequest is the
+ * XDR transport.
+ *
+ * @property _rS
+ * @private
+ * @static
+ * @type object
+ */
+ _rS = {};
+
+ /**
+ * @description Method that creates the Flash transport swf.
+ *
+ * @method _swf
+ * @private
+ * @static
+ * @param {string} uri - location of io.swf.
+ * @param {string} yid - YUI instance id.
+ * @return void
+ */
+ function _swf(uri, yid) {
+ var o = '<object id="yuiIoSwf" type="application/x-shockwave-flash" data="' +
+ uri + '" width="0" height="0">' +
+ '<param name="movie" value="' + uri + '">' +
+ '<param name="FlashVars" value="yid=' + yid + '">' +
+ '<param name="allowScriptAccess" value="always">' +
+ '</object>',
+ c = document.createElement('div');
+
+ document.body.appendChild(c);
+ c.innerHTML = o;
+ }
+
+ /**
+ * @description Sets event handlers for XDomainRequest transactions.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @return void
+ */
+ function _xdr(o, c) {
+ o.c.onprogress = function() { _rS[o.id] = 3; }
+ o.c.onload = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'success');
+ };
+ o.c.onerror = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'failure');
+ };
+ if (c.timeout) {
+ o.c.ontimeout = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'timeout');
+ };
+ o.c.timeout = c.timeout;
+ }
+ }
+
+ /**
+ * @description Creates a response object for XDR transactions, for success
+ * and failure cases.
+ *
+ * @method _data
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {boolean} isFlash - True if Flash was used as the transport.
+ * @param {boolean} isXML - True if the response data are XML.
+ *
+ * @return object
+ */
+ function _data(o, isFlash, isXML) {
+ var text, xml;
+
+ if (!o.status) {
+ text = isFlash ? decodeURI(o.c.responseText) : o.c.responseText;
+ xml = isXML ? Y.DataType.XML.parse(text) : null;
+
+ return { id: o.id, c: { responseText: text, responseXML: xml } };
+ }
+ else {
+ return { id: o.id, status: o.status };
+ }
+
+ }
+
+ /**
+ * @description Method for intiating an XDR transaction abort.
+ *
+ * @method _abort
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _abort(o, c) {
+ return c.xdr.use === 'flash' ? o.c.abort(o.id, c) : o.c.abort();
+ }
+
+ /**
+ * @description Method for determining if an XDR transaction has completed
+ * and all data are received.
+ *
+ * @method _isInProgress.
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _isInProgress(o, t) {
+ return (t === 'flash' && o.c) ? o.c.isInProgress(o.id) : _rS[o.id] !== 4;
+ }
+
+ Y.mix(Y.io, {
+
+ /**
+ * @description Map of io transports.
+ *
+ * @property _transport
+ * @private
+ * @static
+ * @type object
+ */
+ _transport: {},
+
+ /**
+ * @description Method for accessing the transport's interface for making a
+ * cross-domain transaction.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ xdr: function(uri, o, c) {
+ if (c.on && c.xdr.use === 'flash') {
+ _fn[o.id] = {
+ on: c.on,
+ context: c.context,
+ arguments: c.arguments
+ };
+ // These nodes do not need to be serialized across Flash's
+ // ExternalInterface. Doing so will result in exceptions.
+ c.context = null;
+ c.form = null;
+
+ o.c.send(uri, c, o.id);
+ }
+ else if (window.XDomainRequest) {
+ _xdr(o, c);
+ o.c.open(c.method || 'GET', uri);
+ o.c.send(c.data);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _abort(o, c) : false;
+ },
+ isInProgress: function() {
+ return o.c ? _isInProgress(o, c.xdr.use) : false;
+ }
+ }
+ },
+
+ /**
+ * @description Response controller for cross-domain requests when using the
+ * Flash transport or IE8's XDomainRequest object.
+ *
+ * @method xdrResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @param {string} e - Event name
+ * @return object
+ */
+ xdrResponse: function(o, c, e) {
+ var m, fn,
+ isFlash = c.xdr.use === 'flash' ? true : false,
+ isXML = c.xdr.dataType === 'xml' ? true : false;
+ c.on = c.on || {};
+
+ if (isFlash) {
+ m = _fn || {};
+ fn = m[o.id] ? m[o.id] : null;
+ if (fn) {
+ c.on = fn.on;
+ c.context = fn.context;
+ c.arguments = fn.arguments;
+ }
+ }
+ if (e === ('abort' || 'timeout')) {
+ o.status = e;
+ }
+
+ switch (e) {
+ case 'start':
+ Y.io.start(o.id, c);
+ break;
+ case 'success':
+ Y.io.success(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ case 'timeout':
+ case 'abort':
+ case 'failure':
+ Y.io.failure(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ }
+ },
+
+ /**
+ * @description Fires event "io:xdrReady"
+ *
+ * @method xdrReady
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ xdrReady: function(id) {
+ Y.fire(E_XDR_READY, id);
+ },
+
+ /**
+ * @description Method to initialize the desired transport.
+ *
+ * @method transport
+ * @public
+ * @static
+ * @param {object} o - object of transport configurations.
+ * @return void
+ */
+ transport: function(o) {
+ var id = o.yid ? o.yid : Y.id;
+
+ _swf(o.src, id);
+ this._transport.flash = Y.config.doc.getElementById('yuiIoSwf');
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','datatype-xml']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('io-base', function(Y) {
+
+ /**
+ * Base IO functionality. Provides basic XHR transport support.
+ * @module io
+ * @submodule io-base
+ */
+
+ /**
+ * The io class is a utility that brokers HTTP requests through a simplified
+ * interface. Specifically, it allows JavaScript to make HTTP requests to
+ * a resource without a page reload. The underlying transport for making
+ * same-domain requests is the XMLHttpRequest object. YUI.io can also use
+ * Flash, if specified as a transport, for cross-domain requests.
+ *
+ * @class io
+ */
+
+ /**
+ * @event io:start
+ * @description This event is fired by YUI.io when a transaction is initiated.
+ * @type Event Custom
+ */
+ var E_START = 'io:start',
+
+ /**
+ * @event io:complete
+ * @description This event is fired by YUI.io when a transaction is complete.
+ * Response status and data are accessible, if available.
+ * @type Event Custom
+ */
+ E_COMPLETE = 'io:complete',
+
+ /**
+ * @event io:success
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP2xx.
+ * @type Event Custom
+ */
+ E_SUCCESS = 'io:success',
+
+ /**
+ * @event io:failure
+ * @description This event is fired by YUI.io when a transaction is complete, and
+ * the HTTP status resolves to HTTP4xx, 5xx and above.
+ * @type Event Custom
+ */
+ E_FAILURE = 'io:failure',
+
+ /**
+ * @event io:end
+ * @description This event signifies the end of the transaction lifecycle. The
+ * transaction transport is destroyed.
+ * @type Event Custom
+ */
+ E_END = 'io:end',
+
+ //--------------------------------------
+ // Properties
+ //--------------------------------------
+ /**
+ * @description A transaction counter that increments for each transaction.
+ *
+ * @property transactionId
+ * @private
+ * @static
+ * @type int
+ */
+ transactionId = 0,
+
+ /**
+ * @description Object of default HTTP headers to be initialized and sent
+ * for all transactions.
+ *
+ * @property _headers
+ * @private
+ * @static
+ * @type object
+ */
+ _headers = {
+ 'X-Requested-With' : 'XMLHttpRequest'
+ },
+
+ /**
+ * @description Object that stores timeout values for any transaction with
+ * a defined "timeout" configuration property.
+ *
+ * @property _timeout
+ * @private
+ * @static
+ * @type object
+ */
+ _timeout = {},
+
+ // Window reference
+ w = Y.config.win;
+
+ //--------------------------------------
+ // Methods
+ //--------------------------------------
+ /**
+ * @description Method for requesting a transaction. _io() is implemented as
+ * yui.io(). Each transaction may include a configuration object. Its
+ * properties are:
+ *
+ * method: HTTP method verb (e.g., GET or POST). If this property is not
+ * not defined, the default value will be GET.
+ *
+ * data: This is the name-value string that will be sent as the transaction
+ * data. If the request is HTTP GET, the data become part of
+ * querystring. If HTTP POST, the data are sent in the message body.
+ *
+ * xdr: Defines the transport to be used for cross-domain requests. By
+ * setting this property, the transaction will use the specified
+ * transport instead of XMLHttpRequest. Currently, the only alternate
+ * transport supported is Flash (e.g., { xdr: 'flash' }).
+ *
+ * form: This is a defined object used to process HTML form as data. The
+ * properties are:
+ * {
+ * id: object, //HTML form object or id of HTML form
+ * useDisabled: boolean, //Allow disabled HTML form field values
+ * to be sent as part of the data.
+ * }
+ *
+ * on: This is a defined object used to create and handle specific
+ * events during a transaction lifecycle. These events will fire in
+ * addition to the global io events. The events are:
+ * start - This event is fired when a request is sent to a resource.
+ * complete - This event fires when the transaction is complete.
+ * success - This event fires when the response status resolves to
+ * HTTP 2xx.
+ * failure - This event fires when the response status resolves to
+ * HTTP 4xx, 5xx; and, for all transaction exceptions,
+ * including aborted transactions and transaction timeouts.
+ * end - This even is fired at the conclusion of the transaction
+ * lifecycle, after a success or failure resolution.
+ *
+ * The properties are:
+ * {
+ * start: function(id, args){},
+ * complete: function(id, responseobject, args){},
+ * success: function(id, responseobject, args){},
+ * failure: function(id, responseobject, args){},
+ * end: function(id, args){}
+ * }
+ * Each property can reference a function or be written as an
+ * inline function.
+ *
+ * context: Object reference for an event handler when it is implemented
+ * as a method of a base object. Defining "context" will preserve
+ * the proper reference of "this" used in the event handler.
+ * headers: This is a defined object of client headers, as many as.
+ * desired for the transaction. These headers are sentThe object
+ * pattern is:
+ * {
+ * header: value
+ * }
+ *
+ * timeout: This value, defined as milliseconds, is a time threshold for the
+ * transaction. When this threshold is reached, and the transaction's
+ * Complete event has not yet fired, the transaction will be aborted.
+ * arguments: Object, array, string, or number passed to all registered
+ * event handlers. This value is available as the second
+ * argument in the "start" and "abort" event handlers; and, it is
+ * the third argument in the "complete", "success", and "failure"
+ * event handlers.
+ *
+ * @method _io
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @param {number} i - transaction id, if already set by queue.
+ * @return object
+ */
+ function _io(uri, c, i) {
+ var f, o, m;
+ c = c || {};
+ o = _create(c.xdr || c.form, i);
+ m = c.method ? c.method.toUpperCase() : 'GET';
+
+ if (c.form) {
+ if (c.form.upload) {
+ return Y.io._upload(o, uri, c);
+ }
+ else {
+ f = Y.io._serialize(c.form, c.data);
+ if (m === 'POST') {
+ c.data = f;
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded');
+ }
+ else if (m === 'GET') {
+ uri = _concat(uri, f);
+ }
+ }
+ }
+ else if (c.data && m === 'GET') {
+ uri = _concat(uri, c.data);
+ }
+
+ if (c.xdr) {
+ if (c.xdr.use === 'native' && window.XDomainRequest || c.xdr.use === 'flash') {
+ return Y.io.xdr(uri, o, c);
+ }
+ if (c.xdr.credentials) {
+ o.c.withCredentials = true;
+ }
+ }
+
+ o.c.onreadystatechange = function() { _readyState(o, c); };
+ try {
+ o.c.open(m, uri, true);
+ }
+ catch(e0){
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ if (c.data && m === 'POST') {
+ _setHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
+ }
+
+ _setHeaders(o.c, c.headers || {});
+ try {
+ // Using "null" will result in a POST request with
+ // no Content-Length defined.
+ o.c.send(c.data || '');
+ }
+ catch(e1) {
+ if (c.xdr) {
+ // This exception is usually thrown by browsers
+ // that do not support native XDR transactions.
+ return _resend(o, uri, c);
+ }
+ }
+
+ _ioStart(o.id, c);
+ // If config.timeout is defined, and the request is standard XHR,
+ // initialize timeout polling.
+ if (c.timeout) {
+ _startTimeout(o, c.timeout);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _ioCancel(o, 'abort') : false;
+ },
+ isInProgress: function() {
+ return o.c ? o.c.readyState !== 4 && o.c.readyState !== 0 : false;
+ }
+ }
+ }
+
+ /**
+ * @description Method for creating and subscribing transaction events.
+ *
+ * @method _subscribe
+ * @private
+ * @static
+ * @param {string} e - event to be published
+ * @param {object} c - configuration data subset for event subscription.
+ *
+ * @return void
+ */
+ function _subscribe(e, c){
+ var evt = new Y.EventTarget().publish('transaction:' + e);
+ evt.subscribe(c.on[e], (c.context || Y), c.arguments);
+
+ return evt;
+ }
+
+ /**
+ * @description Fires event "io:start" and creates, fires a
+ * transaction-specific start event, if config.on.start is
+ * defined.
+ *
+ * @method _ioStart
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioStart(id, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_START, id);
+ if (c.on.start) {
+ evt = _subscribe('start', c);
+ evt.fire(id);
+ }
+ }
+
+
+ /**
+ * @description Fires event "io:complete" and creates, fires a
+ * transaction-specific "complete" event, if config.on.complete is
+ * defined.
+ *
+ * @method _ioComplete
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioComplete(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_COMPLETE, o.id, r);
+ if (c.on.complete) {
+ evt = _subscribe('complete', c);
+ evt.fire(o.id, r);
+ }
+ }
+
+ /**
+ * @description Fires event "io:success" and creates, fires a
+ * transaction-specific "success" event, if config.on.success is
+ * defined.
+ *
+ * @method _ioSuccess
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioSuccess(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_SUCCESS, o.id, o.c);
+ if (c.on.success) {
+ evt = _subscribe('success', c);
+ evt.fire(o.id, o.c);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:failure" and creates, fires a
+ * transaction-specific "failure" event, if config.on.failure is
+ * defined.
+ *
+ * @method _ioFailure
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioFailure(o, c) {
+ var evt,
+ r = o.status ? { status: 0, statusText: o.status } : o.c;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_FAILURE, o.id, r);
+ if (c.on.failure) {
+ evt = _subscribe('failure', c);
+ evt.fire(o.id, r);
+ }
+
+ _ioEnd(o, c);
+ }
+
+ /**
+ * @description Fires event "io:end" and creates, fires a
+ * transaction-specific "end" event, if config.on.end is
+ * defined.
+ *
+ * @method _ioEnd
+ * @private
+ * @static
+ * @param {object} o - transaction object.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _ioEnd(o, c) {
+ var evt;
+ // Set default value of argument c, property "on" to Object if
+ // the property is null or undefined.
+ c.on = c.on || {};
+
+ Y.fire(E_END, o.id);
+ if (c.on.end) {
+ evt = _subscribe('end', c);
+ evt.fire(o.id);
+ }
+
+ _destroy(o, c.xdr ? true : false );
+ }
+
+ /**
+ * @description Terminates a transaction due to an explicit abort or
+ * timeout.
+ *
+ * @method _ioCancel
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} s - Identifies timed out or aborted transaction.
+ *
+ * @return void
+ */
+ function _ioCancel(o, s) {
+ if (o && o.c) {
+ o.status = s;
+ o.c.abort();
+ }
+ }
+
+ /**
+ * @description Resends an XDR transaction, using the Flash tranport,
+ * if the native transport fails.
+ *
+ * @method _resend
+ * @private
+ * @static
+
+ * @param {object} o - Transaction object generated by _create().
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ function _resend(o, uri, c) {
+ var id = parseInt(o.id);
+
+ _destroy(o);
+ c.xdr.use = 'flash';
+
+ return Y.io(uri, c, id);
+ }
+
+ /**
+ * @description Method that increments _transactionId for each transaction.
+ *
+ * @method _id
+ * @private
+ * @static
+ * @return int
+ */
+ function _id() {
+ var id = transactionId;
+
+ transactionId++;
+
+ return id;
+ }
+
+ /**
+ * @description Method that creates a unique transaction object for each
+ * request.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {number} c - configuration object subset to determine if
+ * the transaction is an XDR or file upload,
+ * requiring an alternate transport.
+ * @param {number} i - transaction id
+ * @return object
+ */
+ function _create(c, i) {
+ var o = {};
+ o.id = Y.Lang.isNumber(i) ? i : _id();
+ c = c || {};
+
+ if (!c.use && !c.upload) {
+ o.c = _xhr();
+ }
+ else if (c.use) {
+ if (c.use === 'flash') {
+ o.c = Y.io._transport[c.use];
+ }
+ else if (c.use === 'native' && window.XDomainRequest) {
+ o.c = new XDomainRequest();
+ }
+ else {
+ o.c = _xhr();
+ }
+ }
+ else {
+ o.c = {};
+ }
+
+ return o;
+ };
+
+ /**
+ * @description Method that creates the XMLHttpRequest transport
+ *
+ * @method _xhr
+ * @private
+ * @static
+ * @return object
+ */
+ function _xhr() {
+ return w.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
+ }
+
+ /**
+ * @description Method that concatenates string data for HTTP GET transactions.
+ *
+ * @method _concat
+ * @private
+ * @static
+ * @param {string} s - URI or root data.
+ * @param {string} d - data to be concatenated onto URI.
+ * @return int
+ */
+ function _concat(s, d) {
+ s += ((s.indexOf('?') == -1) ? '?' : '&') + d;
+ return s;
+ }
+
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ *
+ * @method _setHeader
+ * @private
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ function _setHeader(l, v) {
+ if (v) {
+ _headers[l] = v;
+ }
+ else {
+ delete _headers[l];
+ }
+ }
+
+ /**
+ * @description Method that sets all HTTP headers to be sent in a transaction.
+ *
+ * @method _setHeaders
+ * @private
+ * @static
+ * @param {object} o - XHR instance for the specific transaction.
+ * @param {object} h - HTTP headers for the specific transaction, as defined
+ * in the configuration object passed to YUI.io().
+ * @return void
+ */
+ function _setHeaders(o, h) {
+ var p;
+
+ for (p in _headers) {
+ if (_headers.hasOwnProperty(p)) {
+ if (h[p]) {
+ // Configuration headers will supersede IO preset headers,
+ // if headers match.
+ break;
+ }
+ else {
+ h[p] = _headers[p];
+ }
+ }
+ }
+
+ for (p in h) {
+ if (h.hasOwnProperty(p)) {
+ o.setRequestHeader(p, h[p]);
+ }
+ }
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _startTimeout(o, timeout) {
+ _timeout[o.id] = w.setTimeout(function() { _ioCancel(o, 'timeout'); }, timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ *
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction id.
+ * @return void
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(_timeout[id]);
+ delete _timeout[id];
+ }
+
+ /**
+ * @description Event handler bound to onreadystatechange.
+ *
+ * @method _readyState
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to YUI.io().
+ * @return void
+ */
+ function _readyState(o, c) {
+ if (o.c.readyState === 4) {
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ w.setTimeout(
+ function() {
+ _ioComplete(o, c);
+ _handleResponse(o, c);
+ }, 0);
+ }
+ }
+
+ /**
+ * @description Method that determines if a transaction response qualifies
+ * as success or failure, based on the response HTTP status code, and
+ * fires the appropriate success or failure events.
+ *
+ * @method _handleResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create().
+ * @param {object} c - Configuration object passed to io().
+ * @return void
+ */
+ function _handleResponse(o, c) {
+ var status;
+ try{
+ if (o.c.status && o.c.status !== 0) {
+ status = o.c.status;
+ }
+ else {
+ status = 0;
+ }
+ }
+ catch(e) {
+ status = 0;
+ }
+
+ // IE reports HTTP 204 as HTTP 1223.
+ if (status >= 200 && status < 300 || status === 1223) {
+ _ioSuccess(o, c);
+ }
+ else {
+ _ioFailure(o, c);
+ }
+ }
+
+ function _destroy(o, transport) {
+ // IE, when using XMLHttpRequest as an ActiveX Object, will throw
+ // a "Type Mismatch" error if the event handler is set to "null".
+ if(w.XMLHttpRequest && !transport) {
+ if (o.c) {
+ o.c.onreadystatechange = null;
+ }
+ }
+
+ o.c = null;
+ o = null;
+ }
+
+ _io.start = _ioStart;
+ _io.complete = _ioComplete;
+ _io.success = _ioSuccess;
+ _io.failure = _ioFailure;
+ _io.end = _ioEnd;
+ _io._id = _id;
+ _io._timeout = _timeout;
+
+ //--------------------------------------
+ // Begin public interface definition
+ //--------------------------------------
+ /**
+ * @description Method that stores default client headers for all transactions.
+ * If a label is passed with no value argument, the header will be deleted.
+ * This is the interface for _setHeader().
+ *
+ * @method header
+ * @public
+ * @static
+ * @param {string} l - HTTP header
+ * @param {string} v - HTTP header value
+ * @return int
+ */
+ _io.header = _setHeader;
+
+ /**
+ * @description Method for requesting a transaction. This
+ * is the interface for _io().
+ *
+ * @method io
+ * @public
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} c - configuration object for the transaction.
+ * @return object
+ */
+ Y.io = _io;
+ Y.io.http = _io;
+
+
+
+}, '3.0.0' ,{requires:['event-custom-base']});
+
+YUI.add('io-form', function(Y) {
+
+ /**
+ * Extends the IO base class to enable HTML form data serialization, when specified
+ * in the transaction's configuration object.
+ * @module io
+ * @submodule io-form
+ */
+
+ Y.mix(Y.io, {
+ /**
+ * @description Method to enumerate through an HTML form's elements collection
+ * and return a string comprised of key-value pairs.
+ *
+ * @method _serialize
+ * @private
+ * @static
+ * @param {object} c - YUI form node or HTML form id.
+ * @param {string} s - Transaction data defined in the configuration.
+ * @return string
+ */
+ _serialize: function(c, s) {
+ var eUC = encodeURIComponent,
+ data = [],
+ useDf = c.useDisabled || false,
+ item = 0,
+ id = (typeof c.id === 'string') ? c.id : c.id.getAttribute('id'),
+ e, f, n, v, d, i, il, j, jl, o;
+
+ if (!id) {
+ id = Y.guid('io:');
+ c.id.setAttribute('id', id);
+ }
+
+ f = Y.config.doc.getElementById(id);
+
+ // Iterate over the form elements collection to construct the
+ // label-value pairs.
+ for (i = 0, il = f.elements.length; i < il; ++i) {
+ e = f.elements[i];
+ d = e.disabled;
+ n = e.name;
+
+ if ((useDf) ? n : (n && !d)) {
+ n = encodeURIComponent(n) + '=';
+ v = encodeURIComponent(e.value);
+
+ switch (e.type) {
+ // Safari, Opera, FF all default options.value from .text if
+ // value attribute not specified in markup
+ case 'select-one':
+ if (e.selectedIndex > -1) {
+ o = e.options[e.selectedIndex];
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ break;
+ case 'select-multiple':
+ if (e.selectedIndex > -1) {
+ for (j = e.selectedIndex, jl = e.options.length; j < jl; ++j) {
+ o = e.options[j];
+ if (o.selected) {
+ data[item++] = n + eUC((o.attributes.value && o.attributes.value.specified) ? o.value : o.text);
+ }
+ }
+ }
+ break;
+ case 'radio':
+ case 'checkbox':
+ if(e.checked){
+ data[item++] = n + v;
+ }
+ break;
+ case 'file':
+ // stub case as XMLHttpRequest will only send the file path as a string.
+ case undefined:
+ // stub case for fieldset element which returns undefined.
+ case 'reset':
+ // stub case for input type reset button.
+ case 'button':
+ // stub case for input type button elements.
+ break;
+ case 'submit':
+ default:
+ data[item++] = n + v;
+ }
+ }
+ }
+ return s ? data.join('&') + "&" + s : data.join('&');
+ }
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','node-style']});
+
+YUI.add('io-xdr', function(Y) {
+
+ /**
+ * Extends the IO base class to provide an alternate, Flash transport, for making
+ * cross-domain requests.
+ * @module io
+ * @submodule io-xdr
+ */
+
+ /**
+ * @event io:xdrReady
+ * @description This event is fired by YUI.io when the specified transport is
+ * ready for use.
+ * @type Event Custom
+ */
+ var E_XDR_READY = 'io:xdrReady',
+
+
+ /**
+ * @description Object that stores callback handlers for cross-domain requests
+ * when using Flash as the transport.
+ *
+ * @property _fn
+ * @private
+ * @static
+ * @type object
+ */
+ _fn = {},
+
+ /**
+ * @description Map of transaction state used when XDomainRequest is the
+ * XDR transport.
+ *
+ * @property _rS
+ * @private
+ * @static
+ * @type object
+ */
+ _rS = {};
+
+ /**
+ * @description Method that creates the Flash transport swf.
+ *
+ * @method _swf
+ * @private
+ * @static
+ * @param {string} uri - location of io.swf.
+ * @param {string} yid - YUI instance id.
+ * @return void
+ */
+ function _swf(uri, yid) {
+ var o = '<object id="yuiIoSwf" type="application/x-shockwave-flash" data="' +
+ uri + '" width="0" height="0">' +
+ '<param name="movie" value="' + uri + '">' +
+ '<param name="FlashVars" value="yid=' + yid + '">' +
+ '<param name="allowScriptAccess" value="always">' +
+ '</object>',
+ c = document.createElement('div');
+
+ document.body.appendChild(c);
+ c.innerHTML = o;
+ }
+
+ /**
+ * @description Sets event handlers for XDomainRequest transactions.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @return void
+ */
+ function _xdr(o, c) {
+ o.c.onprogress = function() { _rS[o.id] = 3; }
+ o.c.onload = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'success');
+ };
+ o.c.onerror = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'failure');
+ };
+ if (c.timeout) {
+ o.c.ontimeout = function() {
+ _rS[o.id] = 4;
+ Y.io.xdrResponse(o, c, 'timeout');
+ };
+ o.c.timeout = c.timeout;
+ }
+ }
+
+ /**
+ * @description Creates a response object for XDR transactions, for success
+ * and failure cases.
+ *
+ * @method _data
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {boolean} isFlash - True if Flash was used as the transport.
+ * @param {boolean} isXML - True if the response data are XML.
+ *
+ * @return object
+ */
+ function _data(o, isFlash, isXML) {
+ var text, xml;
+
+ if (!o.status) {
+ text = isFlash ? decodeURI(o.c.responseText) : o.c.responseText;
+ xml = isXML ? Y.DataType.XML.parse(text) : null;
+
+ return { id: o.id, c: { responseText: text, responseXML: xml } };
+ }
+ else {
+ return { id: o.id, status: o.status };
+ }
+
+ }
+
+ /**
+ * @description Method for intiating an XDR transaction abort.
+ *
+ * @method _abort
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _abort(o, c) {
+ return c.xdr.use === 'flash' ? o.c.abort(o.id, c) : o.c.abort();
+ }
+
+ /**
+ * @description Method for determining if an XDR transaction has completed
+ * and all data are received.
+ *
+ * @method _isInProgress.
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ function _isInProgress(o, t) {
+ return (t === 'flash' && o.c) ? o.c.isInProgress(o.id) : _rS[o.id] !== 4;
+ }
+
+ Y.mix(Y.io, {
+
+ /**
+ * @description Map of io transports.
+ *
+ * @property _transport
+ * @private
+ * @static
+ * @type object
+ */
+ _transport: {},
+
+ /**
+ * @description Method for accessing the transport's interface for making a
+ * cross-domain transaction.
+ *
+ * @method _xdr
+ * @private
+ * @static
+ * @param {string} uri - qualified path to transaction resource.
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ */
+ xdr: function(uri, o, c) {
+ if (c.on && c.xdr.use === 'flash') {
+ _fn[o.id] = {
+ on: c.on,
+ context: c.context,
+ arguments: c.arguments
+ };
+ // These nodes do not need to be serialized across Flash's
+ // ExternalInterface. Doing so will result in exceptions.
+ c.context = null;
+ c.form = null;
+
+ o.c.send(uri, c, o.id);
+ }
+ else if (window.XDomainRequest) {
+ _xdr(o, c);
+ o.c.open(c.method || 'GET', uri);
+ o.c.send(c.data);
+ }
+
+ return {
+ id: o.id,
+ abort: function() {
+ return o.c ? _abort(o, c) : false;
+ },
+ isInProgress: function() {
+ return o.c ? _isInProgress(o, c.xdr.use) : false;
+ }
+ }
+ },
+
+ /**
+ * @description Response controller for cross-domain requests when using the
+ * Flash transport or IE8's XDomainRequest object.
+ *
+ * @method xdrResponse
+ * @private
+ * @static
+ * @param {object} o - Transaction object generated by _create() in io-base.
+ * @param {object} c - configuration object for the transaction.
+ * @param {string} e - Event name
+ * @return object
+ */
+ xdrResponse: function(o, c, e) {
+ var m, fn,
+ isFlash = c.xdr.use === 'flash' ? true : false,
+ isXML = c.xdr.dataType === 'xml' ? true : false;
+ c.on = c.on || {};
+
+ if (isFlash) {
+ m = _fn || {};
+ fn = m[o.id] ? m[o.id] : null;
+ if (fn) {
+ c.on = fn.on;
+ c.context = fn.context;
+ c.arguments = fn.arguments;
+ }
+ }
+ if (e === ('abort' || 'timeout')) {
+ o.status = e;
+ }
+
+ switch (e) {
+ case 'start':
+ Y.io.start(o.id, c);
+ break;
+ case 'success':
+ Y.io.success(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ case 'timeout':
+ case 'abort':
+ case 'failure':
+ Y.io.failure(_data(o, isFlash, isXML), c);
+ isFlash ? delete m[o.id] : delete _rS[o.id];
+ break;
+ }
+ },
+
+ /**
+ * @description Fires event "io:xdrReady"
+ *
+ * @method xdrReady
+ * @private
+ * @static
+ * @param {number} id - transaction id
+ * @param {object} c - configuration object for the transaction.
+ *
+ * @return void
+ */
+ xdrReady: function(id) {
+ Y.fire(E_XDR_READY, id);
+ },
+
+ /**
+ * @description Method to initialize the desired transport.
+ *
+ * @method transport
+ * @public
+ * @static
+ * @param {object} o - object of transport configurations.
+ * @return void
+ */
+ transport: function(o) {
+ var id = o.yid ? o.yid : Y.id;
+
+ _swf(o.src, id);
+ this._transport.flash = Y.config.doc.getElementById('yuiIoSwf');
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','datatype-xml']});
+
+YUI.add('io-upload-iframe', function(Y) {
+
+ /**
+ * Extends the IO base class to enable file uploads, with HTML forms,
+ * using an iframe as the transport medium.
+ * @module io
+ * @submodule io-upload-iframe
+ */
+
+ var w = Y.config.win;
+ /**
+ * @description Parses the POST data object and creates hidden form elements
+ * for each key-value, and appends them to the HTML form object.
+ * @method appendData
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {string} s The key-value POST data.
+ * @return {array} o Array of created fields.
+ */
+ function _addData(f, s) {
+ var o = [],
+ m = s.split('='),
+ i, l;
+
+ for (i = 0, l = m.length - 1; i < l; i++) {
+ o[i] = document.createElement('input');
+ o[i].type = 'hidden';
+ o[i].name = m[i].substring(m[i].lastIndexOf('&') + 1);
+ o[i].value = (i + 1 === l) ? m[i + 1] : m[i + 1].substring(0, (m[i + 1].lastIndexOf('&')));
+ f.appendChild(o[i]);
+ }
+
+ return o;
+ }
+
+ /**
+ * @description Removes the custom fields created to pass additional POST
+ * data, along with the HTML form fields.
+ * @method f
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} o HTML form fields created from configuration.data.
+ * @return {void}
+ */
+ function _removeData(f, o) {
+ var i, l;
+
+ for(i = 0, l = o.length; i < l; i++){
+ f.removeChild(o[i]);
+ }
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _setAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} id The Transaction ID.
+ * @param {object} uri Qualified path to transaction resource.
+ * @return {void}
+ */
+ function _setAttrs(f, id, uri) {
+ var ie8 = (document.documentMode && document.documentMode === 8) ? true : false;
+
+ f.setAttribute('action', uri);
+ f.setAttribute('method', 'POST');
+ f.setAttribute('target', 'ioupload' + id );
+ f.setAttribute(Y.UA.ie && !ie8 ? 'encoding' : 'enctype', 'multipart/form-data');
+ }
+
+ /**
+ * @description Sets the appropriate attributes and values to the HTML
+ * form, in preparation of a file upload transaction.
+ * @method _resetAttrs
+ * @private
+ * @static
+ * @param {object} f HTML form object.
+ * @param {object} a Object of original attributes.
+ * @return {void}
+ */
+ function _resetAttrs(f, a){
+ var p;
+
+ for (p in a) {
+ if (a.hasOwnProperty(a, p)) {
+ if (a[p]) {
+ f.setAttribute(p, f[p]);
+ }
+ else {
+ f.removeAttribute(p);
+ }
+ }
+ }
+ }
+
+ /**
+ * @description Creates the iframe transported used in file upload
+ * transactions, and binds the response event handler.
+ *
+ * @method _create
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _create(o, c) {
+ var i = Y.Node.create('<iframe id="ioupload' + o.id + '" name="ioupload' + o.id + '" />');
+ i._node.style.position = 'absolute';
+ i._node.style.top = '-1000px';
+ i._node.style.left = '-1000px';
+
+ Y.one('body').appendChild(i);
+ // Bind the onload handler to the iframe to detect the file upload response.
+ Y.on("load", function() { _handle(o, c) }, '#ioupload' + o.id);
+ }
+
+ /**
+ * @description Bound to the iframe's Load event and processes
+ * the response data.
+ * @method _handle
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _handle(o, c) {
+ var d = Y.one('#ioupload' + o.id).get('contentWindow.document'),
+ b = d.one('body'),
+ xml = (d._node.nodeType === 9),
+ p;
+
+ if (c.timeout) {
+ _clearTimeout(o.id);
+ }
+
+ if (b) {
+ // When a response Content-Type of "text/plain" is used, Firefox and Safari
+ // will wrap the response string with <pre></pre>.
+ p = b.query('pre:first-child');
+ o.c.responseText = p ? p.get('innerHTML') : b.get('innerHTML');
+ }
+ else if (xml) {
+ o.c.responseXML = d._node;
+ }
+
+ Y.io.complete(o, c);
+ Y.io.end(o, c);
+ // The transaction is complete, so call _destroy to remove
+ // the event listener bound to the iframe transport, and then
+ // destroy the iframe.
+ w.setTimeout( function() { _destroy(o.id); }, 0);
+ }
+
+ /**
+ * @description Starts timeout count if the configuration object
+ * has a defined timeout property.
+ *
+ * @method _startTimeout
+ * @private
+ * @static
+ * @param {object} o Transaction object generated by _create().
+ * @param {object} c Configuration object passed to YUI.io().
+ * @return {void}
+ */
+ function _startTimeout(o, c) {
+ Y.io._timeout[o.id] = w.setTimeout(
+ function() {
+ var r = { id: o.id, status: 'timeout' };
+
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ }, c.timeout);
+ }
+
+ /**
+ * @description Clears the timeout interval started by _startTimeout().
+ * @method _clearTimeout
+ * @private
+ * @static
+ * @param {number} id - Transaction ID.
+ * @return {void}
+ */
+ function _clearTimeout(id) {
+ w.clearTimeout(Y.io._timeout[id]);
+ delete Y.io._timeout[id];
+ }
+
+ /**
+ * @description
+ * @method _destroy
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ function _destroy(id) {
+ Y.Event.purgeElement('#ioupload' + id, false);
+ Y.one('body').removeChild(Y.one('#ioupload' + id));
+ }
+
+ Y.mix(Y.io, {
+ /**
+ * @description Uploads HTML form data, inclusive of files/attachments,
+ * using the iframe created in _create to facilitate the transaction.
+ * @method _upload
+ * @private
+ * @static
+ * @param {o} o The transaction object
+ * @param {object} uri Qualified path to transaction resource.
+ * @param {object} c Configuration object for the transaction.
+ * @return {void}
+ */
+ _upload: function(o, uri, c) {
+ var f = (typeof c.form.id === 'string') ? Y.config.doc.getElementById(c.form.id) : c.form.id,
+ fields,
+ // Track original HTML form attribute values.
+ attr = {
+ action: f.getAttribute('action'),
+ target: f.getAttribute('target')
+ };
+
+ _create(o, c);
+ // Initialize the HTML form properties in case they are
+ // not defined in the HTML form.
+ _setAttrs(f, o.id, uri);
+ if (c.data) {
+ fields = _addData(f, c.data);
+ }
+
+ // Start polling if a callback is present and the timeout
+ // property has been defined.
+ if (c.timeout) {
+ _startTimeout(o, c);
+ }
+
+ // Start file upload.
+ f.submit();
+ Y.io.start(o.id, c);
+ if (c.data) {
+ _removeData(f, fields);
+ }
+ // Restore HTML form attributes to their original values.
+ _resetAttrs(f, attr);
+
+ return {
+ id: o.id,
+ abort: function() {
+ var r = { id: o.id, status: 'abort' };
+
+ if (Y.one('#ioupload' + o.id)) {
+ _destroy(o.id);
+ Y.io.complete(r, c);
+ Y.io.end(r, c);
+ }
+ else {
+ return false;
+ }
+ },
+ isInProgress: function() {
+ return Y.one('#ioupload' + o.id) ? true : false;
+ }
+ }
+ }
+ });
+
+
+
+}, '3.0.0' ,{requires:['io-base','node-base','event-base']});
+
+YUI.add('io-queue', function(Y) {
+
+ /**
+ * Extends the IO base class to implement Queue for synchronous
+ * transaction processing.
+ * @module io
+ * @submodule io-queue
+ */
+
+ /**
+ * @description Array of transactions queued for processing
+ *
+ * @property _yQ
+ * @private
+ * @static
+ * @type Object
+ */
+ var _q = new Y.Queue(),
+
+ /**
+ * @description Reference to "io:complete" event handler.
+ *
+ * @property _e
+ * @private
+ * @static
+ * @type Object
+ */
+ _e,
+
+ _activeId,
+ /**
+ * @description Property to determine whether the queue is set to
+ * 1 (active) or 0 (inactive). When inactive, transactions
+ * will be stored in the queue until the queue is set to active.
+ *
+ * @property _qState
+ * @private
+ * @static
+ * @type int
+ */
+ _qState = 1;
+
+ /**
+ * @description Method for requesting a transaction, and queueing the
+ * request before it is sent to the resource.
+ *
+ * @method _queue
+ * @private
+ * @static
+ * @return Object
+ */
+ function _queue(uri, c) {
+ var o = { uri: uri, id: Y.io._id(), cfg:c };
+
+ _q.add(o);
+ if (_qState === 1) {
+ _shift();
+ }
+
+ return o;
+ }
+
+ /**
+ * @description Method Process the first transaction from the
+ * queue in FIFO order.
+ *
+ * @method _shift
+ * @private
+ * @static
+ * @return void
+ */
+ function _shift() {
+ var o = _q.next();
+
+ _activeId = o.id;
+ _qState = 0;
+ Y.io(o.uri, o.cfg, o.id);
+ }
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ *
+ * @method _unshift
+ * @private
+ * @static
+ * @return void
+ */
+ function _unshift(o) {
+ _q.promote(o);
+ }
+
+ function _next(id) {
+ _qState = 1;
+ if (_activeId === id && _q.size() > 0) {
+ _shift();
+ }
+ }
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue.
+ *
+ * @method _remove
+ * @private
+ * @static
+ * @return void
+ */
+ function _remove(o) {
+ _q.remove(o);
+ }
+
+ function _start() {
+ _qState = 1;
+
+ if (_q.size() > 0) {
+ _shift();
+ }
+ }
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is reset to "active".
+ *
+ * @method _stop
+ * @private
+ * @static
+ * @return void
+ */
+ function _stop() {
+ _qState = 0;
+ };
+
+ /**
+ * @description Method to query the current size of the queue.
+ *
+ * @method _size
+ * @private
+ * @static
+ * @return int
+ */
+ function _size() {
+ return _q.size();
+ };
+
+ _e = Y.on('io:complete', function(id) { _next(id); }, Y.io);
+
+ /**
+ * @description Method to query the current size of the queue, or to
+ * set a maximum queue size. This is the interface for _size().
+ *
+ * @method size
+ * @public
+ * @static
+ * @param {number} i - Specified maximum size of queue.
+ * @return number
+ */
+ _queue.size = _size;
+
+ /**
+ * @description Method for setting the queue to active. If there are
+ * transactions pending in the queue, they will be processed from the
+ * queue in FIFO order. This is the interface for _start().
+ *
+ * @method start
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.start = _start;
+
+ /**
+ * @description Method for setting queue processing to inactive.
+ * Transaction requests to YUI.io.queue() will be stored in the queue, but
+ * not processed until the queue is restarted. This is the
+ * interface for _stop().
+ *
+ * @method stop
+ * @public
+ * @static
+ * @return void
+ */
+ _queue.stop = _stop;
+
+ /**
+ * @description Method for promoting a transaction to the top of the queue.
+ * This is the interface for _unshift().
+ *
+ * @method promote
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.promote = _unshift;
+
+ /**
+ * @description Method for removing a specific, pending transaction from
+ * the queue. This is the interface for _remove().
+ *
+ * @method remove
+ * @public
+ * @static
+ * @param {Object} o - Reference to queued transaction.
+ * @return void
+ */
+ _queue.remove = _remove;
+
+ Y.mix(Y.io, {
+ queue: _queue
+ }, true);
+
+
+
+}, '3.0.0' ,{requires:['io-base','queue-promote']});
+
+
+
+YUI.add('io', function(Y){}, '3.0.0' ,{use:['io-base', 'io-form', 'io-xdr', 'io-upload-iframe', 'io-queue']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('json-parse', function(Y) {
+
+/**
+ * <p>The JSON module adds support for serializing JavaScript objects into
+ * JSON strings and parsing JavaScript objects from strings in JSON format.</p>
+ *
+ * <p>The JSON namespace is added to your YUI instance including static methods
+ * Y.JSON.parse(..) and Y.JSON.stringify(..).</p>
+ *
+ * <p>The functionality and method signatures follow the ECMAScript 5
+ * specification. In browsers with native JSON support, the native
+ * implementation is used.</p>
+ *
+ * <p>The <code>json</code> module is a rollup of <code>json-parse</code> and
+ * <code>json-stringify</code>.</p>
+ *
+ * <p>As their names suggest, <code>json-parse</code> adds support for parsing
+ * JSON data (Y.JSON.parse) and <code>json-stringify</code> for serializing
+ * JavaScript data into JSON strings (Y.JSON.stringify). You may choose to
+ * include either of the submodules individually if you don't need the
+ * complementary functionality, or include the rollup for both.</p>
+ *
+ * @module json
+ * @class JSON
+ * @static
+ */
+
+/**
+ * Provides Y.JSON.parse method to accept JSON strings and return native
+ * JavaScript objects.
+ *
+ * @module json
+ * @submodule json-parse
+ * @for JSON
+ * @static
+ */
+
+
+// All internals kept private for security reasons
+
+
+ /**
+ * Alias to native browser implementation of the JSON object if available.
+ *
+ * @property Native
+ * @type {Object}
+ * @private
+ */
+var _JSON = Y.config.win.JSON,
+ Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
+
+ /**
+ * Replace certain Unicode characters that JavaScript may handle incorrectly
+ * during eval--either by deleting them or treating them as line
+ * endings--with escape sequences.
+ * IMPORTANT NOTE: This regex will be used to modify the input if a match is
+ * found.
+ *
+ * @property _UNICODE_EXCEPTIONS
+ * @type {RegExp}
+ * @private
+ */
+ _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+
+
+ /**
+ * First step in the safety evaluation. Regex used to replace all escape
+ * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
+ *
+ * @property _ESCAPES
+ * @type {RegExp}
+ * @private
+ */
+ _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+
+ /**
+ * Second step in the safety evaluation. Regex used to replace all simple
+ * values with ']' characters.
+ *
+ * @property _VALUES
+ * @type {RegExp}
+ * @private
+ */
+ _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+
+ /**
+ * Third step in the safety evaluation. Regex used to remove all open
+ * square brackets following a colon, comma, or at the beginning of the
+ * string.
+ *
+ * @property _BRACKETS
+ * @type {RegExp}
+ * @private
+ */
+ _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
+
+ /**
+ * Final step in the safety evaluation. Regex used to test the string left
+ * after all previous replacements for invalid characters.
+ *
+ * @property _UNSAFE
+ * @type {RegExp}
+ * @private
+ */
+ _UNSAFE = /[^\],:{}\s]/,
+
+ /**
+ * Replaces specific unicode characters with their appropriate \unnnn
+ * format. Some browsers ignore certain characters during eval.
+ *
+ * @method escapeException
+ * @param c {String} Unicode character
+ * @return {String} the \unnnn escapement of the character
+ * @private
+ */
+ _escapeException = function (c) {
+ return '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ },
+
+ /**
+ * Traverses nested objects, applying a reviver function to each (key,value)
+ * from the scope if the key:value's containing object. The value returned
+ * from the function will replace the original value in the key:value pair.
+ * If the value returned is undefined, the key will be omitted from the
+ * returned object.
+ *
+ * @method _revive
+ * @param data {MIXED} Any JavaScript data
+ * @param reviver {Function} filter or mutation function
+ * @return {MIXED} The results of the filtered data
+ * @private
+ */
+ _revive = function (data, reviver) {
+ var walk = function (o,key) {
+ var k,v,value = o[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (value.hasOwnProperty(k)) {
+ v = walk(value, k);
+ if (v === undefined) {
+ delete value[k];
+ } else {
+ value[k] = v;
+ }
+ }
+ }
+ }
+ return reviver.call(o,key,value);
+ };
+
+ return typeof reviver === 'function' ? walk({'':data},'') : data;
+ },
+
+ /**
+ * Parse a JSON string, returning the native JavaScript representation.
+ *
+ * @param s {string} JSON string data
+ * @param reviver {function} (optional) function(k,v) passed each key value
+ * pair of object literals, allowing pruning or altering values
+ * @return {MIXED} the native JavaScript representation of the JSON string
+ * @throws SyntaxError
+ * @method parse
+ * @static
+ */
+ // JavaScript implementation in lieu of native browser support. Based on
+ // the json2.js library from http://json.org
+ _parse = function (s,reviver) {
+ if (typeof s === 'string') {
+ // Replace certain Unicode characters that are otherwise handled
+ // incorrectly by some browser implementations.
+ // NOTE: This modifies the input if such characters are found!
+ s = s.replace(_UNICODE_EXCEPTIONS, _escapeException);
+
+ // Test for any remaining invalid characters
+ if (!_UNSAFE.test(s.replace(_ESCAPES,'@').
+ replace(_VALUES,']').
+ replace(_BRACKETS,''))) {
+
+ // Eval the text into a JavaScript data structure, apply any
+ // reviver function, and return
+ return _revive( eval('(' + s + ')'), reviver );
+ }
+ }
+
+ throw new SyntaxError('JSON.parse');
+ };
+
+Y.namespace('JSON').parse = function (s,reviver) {
+ return Native && Y.JSON.useNativeParse ?
+ Native.parse(s,reviver) : _parse(s,reviver);
+};
+
+/**
+ * Leverage native JSON parse if the browser has a native implementation.
+ * In general, this is a good idea. See the Known Issues section in the
+ * JSON user guide for caveats. The default value is true for browsers with
+ * native JSON support.
+ *
+ * @property useNativeParse
+ * @type Boolean
+ * @default true
+ * @static
+ */
+Y.JSON.useNativeParse = !!Native;
+
+
+}, '3.0.0' );
+YUI.add('json-stringify', function(Y) {
+
+/**
+ * Provides Y.JSON.stringify method for converting objects to JSON strings.
+ *
+ * @module json
+ * @submodule json-stringify
+ * @for JSON
+ * @static
+ */
+var _JSON = Y.config.win.JSON,
+ Lang = Y.Lang,
+ isFunction= Lang.isFunction,
+ isObject = Lang.isObject,
+ isArray = Lang.isArray,
+ _toStr = Object.prototype.toString,
+ Native = (_toStr.call(_JSON) === '[object JSON]' && _JSON),
+ UNDEFINED = 'undefined',
+ OBJECT = 'object',
+ NULL = 'null',
+ STRING = 'string',
+ NUMBER = 'number',
+ BOOLEAN = 'boolean',
+ DATE = 'date',
+ _allowable= {
+ 'undefined' : UNDEFINED,
+ 'string' : STRING,
+ '[object String]' : STRING,
+ 'number' : NUMBER,
+ '[object Number]' : NUMBER,
+ 'boolean' : BOOLEAN,
+ '[object Boolean]' : BOOLEAN,
+ '[object Date]' : DATE,
+ '[object RegExp]' : OBJECT
+ },
+ EMPTY = '',
+ OPEN_O = '{',
+ CLOSE_O = '}',
+ OPEN_A = '[',
+ CLOSE_A = ']',
+ COMMA = ',',
+ COMMA_CR = ",\n",
+ CR = "\n",
+ COLON = ':',
+ COLON_SP = ': ',
+ QUOTE = '"',
+ // Regex used to capture characters that need escaping before enclosing
+ // their containing string in quotes.
+ _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ // Character substitution map for common escapes and special characters.
+ _CHARS = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+
+
+// Utility function used to determine how to serialize a variable.
+function _type(o) {
+ var t = typeof o;
+ return _allowable[t] || // number, string, boolean, undefined
+ _allowable[_toStr.call(o)] || // Number, String, Boolean, Date
+ (t === OBJECT ?
+ (o ? OBJECT : NULL) : // object, array, null, misc natives
+ UNDEFINED); // function, unknown
+}
+
+// Escapes a special character to a safe Unicode representation
+function _char(c) {
+ if (!_CHARS[c]) {
+ _CHARS[c] = '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ }
+ return _CHARS[c];
+}
+
+// Enclose escaped strings in quotes
+function _string(s) {
+ return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE;
+}
+
+// Adds the provided space to the beginning of every line in the input string
+function _indent(s,space) {
+ return s.replace(/^/gm, space);
+}
+
+// JavaScript implementation of stringify (see API declaration of stringify)
+function _stringify(o,w,space) {
+ if (o === undefined) {
+ return undefined;
+ }
+
+ var replacer = isFunction(w) ? w : null,
+ format = _toStr.call(space).match(/String|Number/) || [],
+ _date = Y.JSON.dateToString,
+ stack = [],
+ tmp,i,len;
+
+ if (replacer || !isArray(w)) {
+ w = undefined;
+ }
+
+ // Ensure whitelist keys are unique (bug 2110391)
+ if (w) {
+ tmp = {};
+ for (i = 0, len = w.length; i < len; ++i) {
+ tmp[w[i]] = true;
+ }
+ w = tmp;
+ }
+
+ // Per the spec, strings are truncated to 10 characters and numbers
+ // are converted to that number of spaces (max 10)
+ space = format[0] === 'Number' ?
+ new Array(Math.min(Math.max(0,space),10)+1).join(" ") :
+ (space || EMPTY).slice(0,10);
+
+ function _serialize(h,key) {
+ var value = h[key],
+ t = _type(value),
+ a = [],
+ colon = space ? COLON_SP : COLON,
+ arr, i, keys, k, v;
+
+ // Per the ECMA 5 spec, toJSON is applied before the replacer is
+ // called. Also per the spec, Date.prototype.toJSON has been added, so
+ // Date instances should be serialized prior to exposure to the
+ // replacer. I disagree with this decision, but the spec is the spec.
+ if (isObject(value) && isFunction(value.toJSON)) {
+ value = value.toJSON(key);
+ } else if (t === DATE) {
+ value = _date(value);
+ }
+
+ if (isFunction(replacer)) {
+ value = replacer.call(h,key,value);
+ }
+
+ if (value !== h[key]) {
+ t = _type(value);
+ }
+
+ switch (t) {
+ case DATE : // intentional fallthrough. Pre-replacer Dates are
+ // serialized in the toJSON stage. Dates here would
+ // have been produced by the replacer.
+ case OBJECT : break;
+ case STRING : return _string(value);
+ case NUMBER : return isFinite(value) ? value+EMPTY : NULL;
+ case BOOLEAN : return value+EMPTY;
+ case NULL : return NULL;
+ default : return undefined;
+ }
+
+ // Check for cyclical references in nested objects
+ for (i = stack.length - 1; i >= 0; --i) {
+ if (stack[i] === value) {
+ throw new Error("JSON.stringify. Cyclical reference");
+ }
+ }
+
+ arr = isArray(value);
+
+ // Add the object to the processing stack
+ stack.push(value);
+
+ if (arr) { // Array
+ for (i = value.length - 1; i >= 0; --i) {
+ a[i] = _serialize(value, i) || NULL;
+ }
+ } else { // Object
+ // If whitelist provided, take only those keys
+ keys = w || value;
+ i = 0;
+
+ for (k in keys) {
+ if (keys.hasOwnProperty(k)) {
+ v = _serialize(value, k);
+ if (v) {
+ a[i++] = _string(k) + colon + v;
+ }
+ }
+ }
+ }
+
+ // remove the array from the stack
+ stack.pop();
+
+ if (space && a.length) {
+ return arr ?
+ OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A :
+ OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O;
+ } else {
+ return arr ?
+ OPEN_A + a.join(COMMA) + CLOSE_A :
+ OPEN_O + a.join(COMMA) + CLOSE_O;
+ }
+ }
+
+ // process the input
+ return _serialize({'':o},'');
+}
+
+Y.mix(Y.namespace('JSON'),{
+ /**
+ * Leverage native JSON stringify if the browser has a native
+ * implementation. In general, this is a good idea. See the Known Issues
+ * section in the JSON user guide for caveats. The default value is true
+ * for browsers with native JSON support.
+ *
+ * @property JSON.useNativeStringify
+ * @type Boolean
+ * @default true
+ * @static
+ */
+ useNativeStringify : !!Native,
+
+ /**
+ * Serializes a Date instance as a UTC date string. Used internally by
+ * stringify. Override this method if you need Dates serialized in a
+ * different format.
+ *
+ * @method dateToString
+ * @param d {Date} The Date to serialize
+ * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
+ * @static
+ */
+ dateToString : function (d) {
+ function _zeroPad(v) {
+ return v < 10 ? '0' + v : v;
+ }
+
+ return d.getUTCFullYear() + '-' +
+ _zeroPad(d.getUTCMonth() + 1) + '-' +
+ _zeroPad(d.getUTCDate()) + 'T' +
+ _zeroPad(d.getUTCHours()) + COLON +
+ _zeroPad(d.getUTCMinutes()) + COLON +
+ _zeroPad(d.getUTCSeconds()) + 'Z';
+ },
+
+ /**
+ * <p>Converts an arbitrary value to a JSON string representation.</p>
+ *
+ * <p>Objects with cyclical references will trigger an exception.</p>
+ *
+ * <p>If a whitelist is provided, only matching object keys will be
+ * included. Alternately, a replacer function may be passed as the
+ * second parameter. This function is executed on every value in the
+ * input, and its return value will be used in place of the original value.
+ * This is useful to serialize specialized objects or class instances.</p>
+ *
+ * <p>If a positive integer or non-empty string is passed as the third
+ * parameter, the output will be formatted with carriage returns and
+ * indentation for readability. If a String is passed (such as "\t") it
+ * will be used once for each indentation level. If a number is passed,
+ * that number of spaces will be used.</p>
+ *
+ * @method stringify
+ * @param o {MIXED} any arbitrary value to convert to JSON string
+ * @param w {Array|Function} (optional) whitelist of acceptable object
+ * keys to include, or a replacer function to modify the
+ * raw value before serialization
+ * @param ind {Number|String} (optional) indentation character or depth of
+ * spaces to format the output.
+ * @return {string} JSON string representation of the input
+ * @static
+ */
+ stringify : function (o,w,ind) {
+ return Native && Y.JSON.useNativeStringify ?
+ Native.stringify(o,w,ind) : _stringify(o,w,ind);
+ }
+});
+
+
+}, '3.0.0' );
+
+
+YUI.add('json', function(Y){}, '3.0.0' ,{use:['json-parse', 'json-stringify']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("json-parse",function(Y){var _JSON=Y.config.win.JSON,Native=(Object.prototype.toString.call(_JSON)==="[object JSON]"&&_JSON),_UNICODE_EXCEPTIONS=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,_ESCAPES=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,_VALUES=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,_BRACKETS=/(?:^|:|,)(?:\s*\[)+/g,_UNSAFE=/[^\],:{}\s]/,_escapeException=function(c){return"\\u"+("0000"+(+(c.charCodeAt(0))).toString(16)).slice(-4);},_revive=function(data,reviver){var walk=function(o,key){var k,v,value=o[key];if(value&&typeof value==="object"){for(k in value){if(value.hasOwnProperty(k)){v=walk(value,k);if(v===undefined){delete value[k];}else{value[k]=v;}}}}return reviver.call(o,key,value);};return typeof reviver==="function"?walk({"":data},""):data;},_parse=function(s,reviver){if(typeof s==="string"){s=s.replace(_UNICODE_EXCEPTIONS,_escapeException);if(!_UNSAFE.test(s.replace(_ESCAPES,"@").replace(_VALUES,"]").replace(_BRACKETS,""))){return _revive(eval("("+s+")"),reviver);}}throw new SyntaxError("JSON.parse");};Y.namespace("JSON").parse=function(s,reviver){return Native&&Y.JSON.useNativeParse?Native.parse(s,reviver):_parse(s,reviver);};Y.JSON.useNativeParse=!!Native;},"3.0.0");YUI.add("json-stringify",function(C){var b=C.config.win.JSON,E=C.Lang,A=E.isFunction,L=E.isObject,N=E.isArray,X=Object.prototype.toString,W=(X.call(b)==="[object JSON]"&&b),h="undefined",O="object",e="null",R="string",U="number",Q="boolean",D="date",H={"undefined":h,"string":R,"[object String]":R,"number":U,"[object Number]":U,"boolean":Q,"[object Boolean]":Q,"[object Date]":D,"[object RegExp]":O},i="",g="{",G="}",F="[",V="]",T=",",K=",\n",B="\n",I=":",f=": ",M='"',Z=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,a={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};function c(j){var Y=typeof j;return H[Y]||H[X.call(j)]||(Y===O?(j?O:e):h);}function P(Y){if(!a[Y]){a[Y]="\\u"+("0000"+(+(Y.charCodeAt(0))).toString(16)).slice(-4);}return a[Y];}function J(Y){return M+Y.replace(Z,P)+M;}function d(Y,j){return Y.replace(/^/gm,j);}function S(j,s,Y){if(j===undefined){return undefined;}var l=A(s)?s:null,r=X.call(Y).match(/String|Number/)||[],t=C.JSON.dateToString,q=[],n,m,p;if(l||!N(s)){s=undefined;}if(s){n={};for(m=0,p=s.length;m<p;++m){n[s[m]]=true;}s=n;}Y=r[0]==="Number"?new Array(Math.min(Math.max(0,Y),10)+1).join(" "):(Y||i).slice(0,10);function k(w,AC){var AA=w[AC],AE=c(AA),z=[],y=Y?f:I,x,u,AD,o,AB;if(L(AA)&&A(AA.toJSON)){AA=AA.toJSON(AC);}else{if(AE===D){AA=t(AA);}}if(A(l)){AA=l.call(w,AC,AA);}if(AA!==w[AC]){AE=c(AA);}switch(AE){case D:case O:break;case R:return J(AA);case U:return isFinite(AA)?AA+i:e;case Q:return AA+i;case e:return e;default:return undefined;}for(u=q.length-1;u>=0;--u){if(q[u]===AA){throw new Error("JSON.stringify. Cyclical reference");}}x=N(AA);q.push(AA);if(x){for(u=AA.length-1;u>=0;--u){z[u]=k(AA,u)||e;}}else{AD=s||AA;u=0;for(o in AD){if(AD.hasOwnProperty(o)){AB=k(AA,o);if(AB){z[u++]=J(o)+y+AB;}}}}q.pop();if(Y&&z.length){return x?F+B+d(z.join(K),Y)+B+V:g+B+d(z.join(K),Y)+B+G;}else{return x?F+z.join(T)+V:g+z.join(T)+G;}}return k({"":j},"");}C.mix(C.namespace("JSON"),{useNativeStringify:!!W,dateToString:function(j){function Y(k){return k<10?"0"+k:k;}return j.getUTCFullYear()+"-"+Y(j.getUTCMonth()+1)+"-"+Y(j.getUTCDate())+"T"+Y(j.getUTCHours())+I+Y(j.getUTCMinutes())+I+Y(j.getUTCSeconds())+"Z";},stringify:function(k,Y,j){return W&&C.JSON.useNativeStringify?W.stringify(k,Y,j):S(k,Y,j);}});},"3.0.0");YUI.add("json",function(A){},"3.0.0",{use:["json-parse","json-stringify"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('json-parse', function(Y) {
+
+/**
+ * <p>The JSON module adds support for serializing JavaScript objects into
+ * JSON strings and parsing JavaScript objects from strings in JSON format.</p>
+ *
+ * <p>The JSON namespace is added to your YUI instance including static methods
+ * Y.JSON.parse(..) and Y.JSON.stringify(..).</p>
+ *
+ * <p>The functionality and method signatures follow the ECMAScript 5
+ * specification. In browsers with native JSON support, the native
+ * implementation is used.</p>
+ *
+ * <p>The <code>json</code> module is a rollup of <code>json-parse</code> and
+ * <code>json-stringify</code>.</p>
+ *
+ * <p>As their names suggest, <code>json-parse</code> adds support for parsing
+ * JSON data (Y.JSON.parse) and <code>json-stringify</code> for serializing
+ * JavaScript data into JSON strings (Y.JSON.stringify). You may choose to
+ * include either of the submodules individually if you don't need the
+ * complementary functionality, or include the rollup for both.</p>
+ *
+ * @module json
+ * @class JSON
+ * @static
+ */
+
+/**
+ * Provides Y.JSON.parse method to accept JSON strings and return native
+ * JavaScript objects.
+ *
+ * @module json
+ * @submodule json-parse
+ * @for JSON
+ * @static
+ */
+
+
+// All internals kept private for security reasons
+
+
+ /**
+ * Alias to native browser implementation of the JSON object if available.
+ *
+ * @property Native
+ * @type {Object}
+ * @private
+ */
+var _JSON = Y.config.win.JSON,
+ Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
+
+ /**
+ * Replace certain Unicode characters that JavaScript may handle incorrectly
+ * during eval--either by deleting them or treating them as line
+ * endings--with escape sequences.
+ * IMPORTANT NOTE: This regex will be used to modify the input if a match is
+ * found.
+ *
+ * @property _UNICODE_EXCEPTIONS
+ * @type {RegExp}
+ * @private
+ */
+ _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+
+
+ /**
+ * First step in the safety evaluation. Regex used to replace all escape
+ * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
+ *
+ * @property _ESCAPES
+ * @type {RegExp}
+ * @private
+ */
+ _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+
+ /**
+ * Second step in the safety evaluation. Regex used to replace all simple
+ * values with ']' characters.
+ *
+ * @property _VALUES
+ * @type {RegExp}
+ * @private
+ */
+ _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+
+ /**
+ * Third step in the safety evaluation. Regex used to remove all open
+ * square brackets following a colon, comma, or at the beginning of the
+ * string.
+ *
+ * @property _BRACKETS
+ * @type {RegExp}
+ * @private
+ */
+ _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
+
+ /**
+ * Final step in the safety evaluation. Regex used to test the string left
+ * after all previous replacements for invalid characters.
+ *
+ * @property _UNSAFE
+ * @type {RegExp}
+ * @private
+ */
+ _UNSAFE = /[^\],:{}\s]/,
+
+ /**
+ * Replaces specific unicode characters with their appropriate \unnnn
+ * format. Some browsers ignore certain characters during eval.
+ *
+ * @method escapeException
+ * @param c {String} Unicode character
+ * @return {String} the \unnnn escapement of the character
+ * @private
+ */
+ _escapeException = function (c) {
+ return '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ },
+
+ /**
+ * Traverses nested objects, applying a reviver function to each (key,value)
+ * from the scope if the key:value's containing object. The value returned
+ * from the function will replace the original value in the key:value pair.
+ * If the value returned is undefined, the key will be omitted from the
+ * returned object.
+ *
+ * @method _revive
+ * @param data {MIXED} Any JavaScript data
+ * @param reviver {Function} filter or mutation function
+ * @return {MIXED} The results of the filtered data
+ * @private
+ */
+ _revive = function (data, reviver) {
+ var walk = function (o,key) {
+ var k,v,value = o[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (value.hasOwnProperty(k)) {
+ v = walk(value, k);
+ if (v === undefined) {
+ delete value[k];
+ } else {
+ value[k] = v;
+ }
+ }
+ }
+ }
+ return reviver.call(o,key,value);
+ };
+
+ return typeof reviver === 'function' ? walk({'':data},'') : data;
+ },
+
+ /**
+ * Parse a JSON string, returning the native JavaScript representation.
+ *
+ * @param s {string} JSON string data
+ * @param reviver {function} (optional) function(k,v) passed each key value
+ * pair of object literals, allowing pruning or altering values
+ * @return {MIXED} the native JavaScript representation of the JSON string
+ * @throws SyntaxError
+ * @method parse
+ * @static
+ */
+ // JavaScript implementation in lieu of native browser support. Based on
+ // the json2.js library from http://json.org
+ _parse = function (s,reviver) {
+ if (typeof s === 'string') {
+ // Replace certain Unicode characters that are otherwise handled
+ // incorrectly by some browser implementations.
+ // NOTE: This modifies the input if such characters are found!
+ s = s.replace(_UNICODE_EXCEPTIONS, _escapeException);
+
+ // Test for any remaining invalid characters
+ if (!_UNSAFE.test(s.replace(_ESCAPES,'@').
+ replace(_VALUES,']').
+ replace(_BRACKETS,''))) {
+
+ // Eval the text into a JavaScript data structure, apply any
+ // reviver function, and return
+ return _revive( eval('(' + s + ')'), reviver );
+ }
+ }
+
+ throw new SyntaxError('JSON.parse');
+ };
+
+Y.namespace('JSON').parse = function (s,reviver) {
+ return Native && Y.JSON.useNativeParse ?
+ Native.parse(s,reviver) : _parse(s,reviver);
+};
+
+/**
+ * Leverage native JSON parse if the browser has a native implementation.
+ * In general, this is a good idea. See the Known Issues section in the
+ * JSON user guide for caveats. The default value is true for browsers with
+ * native JSON support.
+ *
+ * @property useNativeParse
+ * @type Boolean
+ * @default true
+ * @static
+ */
+Y.JSON.useNativeParse = !!Native;
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("json-parse",function(Y){var _JSON=Y.config.win.JSON,Native=(Object.prototype.toString.call(_JSON)==="[object JSON]"&&_JSON),_UNICODE_EXCEPTIONS=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,_ESCAPES=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,_VALUES=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,_BRACKETS=/(?:^|:|,)(?:\s*\[)+/g,_UNSAFE=/[^\],:{}\s]/,_escapeException=function(c){return"\\u"+("0000"+(+(c.charCodeAt(0))).toString(16)).slice(-4);},_revive=function(data,reviver){var walk=function(o,key){var k,v,value=o[key];if(value&&typeof value==="object"){for(k in value){if(value.hasOwnProperty(k)){v=walk(value,k);if(v===undefined){delete value[k];}else{value[k]=v;}}}}return reviver.call(o,key,value);};return typeof reviver==="function"?walk({"":data},""):data;},_parse=function(s,reviver){if(typeof s==="string"){s=s.replace(_UNICODE_EXCEPTIONS,_escapeException);if(!_UNSAFE.test(s.replace(_ESCAPES,"@").replace(_VALUES,"]").replace(_BRACKETS,""))){return _revive(eval("("+s+")"),reviver);}}throw new SyntaxError("JSON.parse");};Y.namespace("JSON").parse=function(s,reviver){return Native&&Y.JSON.useNativeParse?Native.parse(s,reviver):_parse(s,reviver);};Y.JSON.useNativeParse=!!Native;},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('json-parse', function(Y) {
+
+/**
+ * <p>The JSON module adds support for serializing JavaScript objects into
+ * JSON strings and parsing JavaScript objects from strings in JSON format.</p>
+ *
+ * <p>The JSON namespace is added to your YUI instance including static methods
+ * Y.JSON.parse(..) and Y.JSON.stringify(..).</p>
+ *
+ * <p>The functionality and method signatures follow the ECMAScript 5
+ * specification. In browsers with native JSON support, the native
+ * implementation is used.</p>
+ *
+ * <p>The <code>json</code> module is a rollup of <code>json-parse</code> and
+ * <code>json-stringify</code>.</p>
+ *
+ * <p>As their names suggest, <code>json-parse</code> adds support for parsing
+ * JSON data (Y.JSON.parse) and <code>json-stringify</code> for serializing
+ * JavaScript data into JSON strings (Y.JSON.stringify). You may choose to
+ * include either of the submodules individually if you don't need the
+ * complementary functionality, or include the rollup for both.</p>
+ *
+ * @module json
+ * @class JSON
+ * @static
+ */
+
+/**
+ * Provides Y.JSON.parse method to accept JSON strings and return native
+ * JavaScript objects.
+ *
+ * @module json
+ * @submodule json-parse
+ * @for JSON
+ * @static
+ */
+
+
+// All internals kept private for security reasons
+
+
+ /**
+ * Alias to native browser implementation of the JSON object if available.
+ *
+ * @property Native
+ * @type {Object}
+ * @private
+ */
+var _JSON = Y.config.win.JSON,
+ Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
+
+ /**
+ * Replace certain Unicode characters that JavaScript may handle incorrectly
+ * during eval--either by deleting them or treating them as line
+ * endings--with escape sequences.
+ * IMPORTANT NOTE: This regex will be used to modify the input if a match is
+ * found.
+ *
+ * @property _UNICODE_EXCEPTIONS
+ * @type {RegExp}
+ * @private
+ */
+ _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+
+
+ /**
+ * First step in the safety evaluation. Regex used to replace all escape
+ * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
+ *
+ * @property _ESCAPES
+ * @type {RegExp}
+ * @private
+ */
+ _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+
+ /**
+ * Second step in the safety evaluation. Regex used to replace all simple
+ * values with ']' characters.
+ *
+ * @property _VALUES
+ * @type {RegExp}
+ * @private
+ */
+ _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+
+ /**
+ * Third step in the safety evaluation. Regex used to remove all open
+ * square brackets following a colon, comma, or at the beginning of the
+ * string.
+ *
+ * @property _BRACKETS
+ * @type {RegExp}
+ * @private
+ */
+ _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
+
+ /**
+ * Final step in the safety evaluation. Regex used to test the string left
+ * after all previous replacements for invalid characters.
+ *
+ * @property _UNSAFE
+ * @type {RegExp}
+ * @private
+ */
+ _UNSAFE = /[^\],:{}\s]/,
+
+ /**
+ * Replaces specific unicode characters with their appropriate \unnnn
+ * format. Some browsers ignore certain characters during eval.
+ *
+ * @method escapeException
+ * @param c {String} Unicode character
+ * @return {String} the \unnnn escapement of the character
+ * @private
+ */
+ _escapeException = function (c) {
+ return '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ },
+
+ /**
+ * Traverses nested objects, applying a reviver function to each (key,value)
+ * from the scope if the key:value's containing object. The value returned
+ * from the function will replace the original value in the key:value pair.
+ * If the value returned is undefined, the key will be omitted from the
+ * returned object.
+ *
+ * @method _revive
+ * @param data {MIXED} Any JavaScript data
+ * @param reviver {Function} filter or mutation function
+ * @return {MIXED} The results of the filtered data
+ * @private
+ */
+ _revive = function (data, reviver) {
+ var walk = function (o,key) {
+ var k,v,value = o[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (value.hasOwnProperty(k)) {
+ v = walk(value, k);
+ if (v === undefined) {
+ delete value[k];
+ } else {
+ value[k] = v;
+ }
+ }
+ }
+ }
+ return reviver.call(o,key,value);
+ };
+
+ return typeof reviver === 'function' ? walk({'':data},'') : data;
+ },
+
+ /**
+ * Parse a JSON string, returning the native JavaScript representation.
+ *
+ * @param s {string} JSON string data
+ * @param reviver {function} (optional) function(k,v) passed each key value
+ * pair of object literals, allowing pruning or altering values
+ * @return {MIXED} the native JavaScript representation of the JSON string
+ * @throws SyntaxError
+ * @method parse
+ * @static
+ */
+ // JavaScript implementation in lieu of native browser support. Based on
+ // the json2.js library from http://json.org
+ _parse = function (s,reviver) {
+ if (typeof s === 'string') {
+ // Replace certain Unicode characters that are otherwise handled
+ // incorrectly by some browser implementations.
+ // NOTE: This modifies the input if such characters are found!
+ s = s.replace(_UNICODE_EXCEPTIONS, _escapeException);
+
+ // Test for any remaining invalid characters
+ if (!_UNSAFE.test(s.replace(_ESCAPES,'@').
+ replace(_VALUES,']').
+ replace(_BRACKETS,''))) {
+
+ // Eval the text into a JavaScript data structure, apply any
+ // reviver function, and return
+ return _revive( eval('(' + s + ')'), reviver );
+ }
+ }
+
+ throw new SyntaxError('JSON.parse');
+ };
+
+Y.namespace('JSON').parse = function (s,reviver) {
+ return Native && Y.JSON.useNativeParse ?
+ Native.parse(s,reviver) : _parse(s,reviver);
+};
+
+/**
+ * Leverage native JSON parse if the browser has a native implementation.
+ * In general, this is a good idea. See the Known Issues section in the
+ * JSON user guide for caveats. The default value is true for browsers with
+ * native JSON support.
+ *
+ * @property useNativeParse
+ * @type Boolean
+ * @default true
+ * @static
+ */
+Y.JSON.useNativeParse = !!Native;
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('json-stringify', function(Y) {
+
+/**
+ * Provides Y.JSON.stringify method for converting objects to JSON strings.
+ *
+ * @module json
+ * @submodule json-stringify
+ * @for JSON
+ * @static
+ */
+var _JSON = Y.config.win.JSON,
+ Lang = Y.Lang,
+ isFunction= Lang.isFunction,
+ isObject = Lang.isObject,
+ isArray = Lang.isArray,
+ _toStr = Object.prototype.toString,
+ Native = (_toStr.call(_JSON) === '[object JSON]' && _JSON),
+ UNDEFINED = 'undefined',
+ OBJECT = 'object',
+ NULL = 'null',
+ STRING = 'string',
+ NUMBER = 'number',
+ BOOLEAN = 'boolean',
+ DATE = 'date',
+ _allowable= {
+ 'undefined' : UNDEFINED,
+ 'string' : STRING,
+ '[object String]' : STRING,
+ 'number' : NUMBER,
+ '[object Number]' : NUMBER,
+ 'boolean' : BOOLEAN,
+ '[object Boolean]' : BOOLEAN,
+ '[object Date]' : DATE,
+ '[object RegExp]' : OBJECT
+ },
+ EMPTY = '',
+ OPEN_O = '{',
+ CLOSE_O = '}',
+ OPEN_A = '[',
+ CLOSE_A = ']',
+ COMMA = ',',
+ COMMA_CR = ",\n",
+ CR = "\n",
+ COLON = ':',
+ COLON_SP = ': ',
+ QUOTE = '"',
+ // Regex used to capture characters that need escaping before enclosing
+ // their containing string in quotes.
+ _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ // Character substitution map for common escapes and special characters.
+ _CHARS = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+
+
+// Utility function used to determine how to serialize a variable.
+function _type(o) {
+ var t = typeof o;
+ return _allowable[t] || // number, string, boolean, undefined
+ _allowable[_toStr.call(o)] || // Number, String, Boolean, Date
+ (t === OBJECT ?
+ (o ? OBJECT : NULL) : // object, array, null, misc natives
+ UNDEFINED); // function, unknown
+}
+
+// Escapes a special character to a safe Unicode representation
+function _char(c) {
+ if (!_CHARS[c]) {
+ _CHARS[c] = '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ }
+ return _CHARS[c];
+}
+
+// Enclose escaped strings in quotes
+function _string(s) {
+ return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE;
+}
+
+// Adds the provided space to the beginning of every line in the input string
+function _indent(s,space) {
+ return s.replace(/^/gm, space);
+}
+
+// JavaScript implementation of stringify (see API declaration of stringify)
+function _stringify(o,w,space) {
+ if (o === undefined) {
+ return undefined;
+ }
+
+ var replacer = isFunction(w) ? w : null,
+ format = _toStr.call(space).match(/String|Number/) || [],
+ _date = Y.JSON.dateToString,
+ stack = [],
+ tmp,i,len;
+
+ if (replacer || !isArray(w)) {
+ w = undefined;
+ }
+
+ // Ensure whitelist keys are unique (bug 2110391)
+ if (w) {
+ tmp = {};
+ for (i = 0, len = w.length; i < len; ++i) {
+ tmp[w[i]] = true;
+ }
+ w = tmp;
+ }
+
+ // Per the spec, strings are truncated to 10 characters and numbers
+ // are converted to that number of spaces (max 10)
+ space = format[0] === 'Number' ?
+ new Array(Math.min(Math.max(0,space),10)+1).join(" ") :
+ (space || EMPTY).slice(0,10);
+
+ function _serialize(h,key) {
+ var value = h[key],
+ t = _type(value),
+ a = [],
+ colon = space ? COLON_SP : COLON,
+ arr, i, keys, k, v;
+
+ // Per the ECMA 5 spec, toJSON is applied before the replacer is
+ // called. Also per the spec, Date.prototype.toJSON has been added, so
+ // Date instances should be serialized prior to exposure to the
+ // replacer. I disagree with this decision, but the spec is the spec.
+ if (isObject(value) && isFunction(value.toJSON)) {
+ value = value.toJSON(key);
+ } else if (t === DATE) {
+ value = _date(value);
+ }
+
+ if (isFunction(replacer)) {
+ value = replacer.call(h,key,value);
+ }
+
+ if (value !== h[key]) {
+ t = _type(value);
+ }
+
+ switch (t) {
+ case DATE : // intentional fallthrough. Pre-replacer Dates are
+ // serialized in the toJSON stage. Dates here would
+ // have been produced by the replacer.
+ case OBJECT : break;
+ case STRING : return _string(value);
+ case NUMBER : return isFinite(value) ? value+EMPTY : NULL;
+ case BOOLEAN : return value+EMPTY;
+ case NULL : return NULL;
+ default : return undefined;
+ }
+
+ // Check for cyclical references in nested objects
+ for (i = stack.length - 1; i >= 0; --i) {
+ if (stack[i] === value) {
+ throw new Error("JSON.stringify. Cyclical reference");
+ }
+ }
+
+ arr = isArray(value);
+
+ // Add the object to the processing stack
+ stack.push(value);
+
+ if (arr) { // Array
+ for (i = value.length - 1; i >= 0; --i) {
+ a[i] = _serialize(value, i) || NULL;
+ }
+ } else { // Object
+ // If whitelist provided, take only those keys
+ keys = w || value;
+ i = 0;
+
+ for (k in keys) {
+ if (keys.hasOwnProperty(k)) {
+ v = _serialize(value, k);
+ if (v) {
+ a[i++] = _string(k) + colon + v;
+ }
+ }
+ }
+ }
+
+ // remove the array from the stack
+ stack.pop();
+
+ if (space && a.length) {
+ return arr ?
+ OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A :
+ OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O;
+ } else {
+ return arr ?
+ OPEN_A + a.join(COMMA) + CLOSE_A :
+ OPEN_O + a.join(COMMA) + CLOSE_O;
+ }
+ }
+
+ // process the input
+ return _serialize({'':o},'');
+}
+
+Y.mix(Y.namespace('JSON'),{
+ /**
+ * Leverage native JSON stringify if the browser has a native
+ * implementation. In general, this is a good idea. See the Known Issues
+ * section in the JSON user guide for caveats. The default value is true
+ * for browsers with native JSON support.
+ *
+ * @property JSON.useNativeStringify
+ * @type Boolean
+ * @default true
+ * @static
+ */
+ useNativeStringify : !!Native,
+
+ /**
+ * Serializes a Date instance as a UTC date string. Used internally by
+ * stringify. Override this method if you need Dates serialized in a
+ * different format.
+ *
+ * @method dateToString
+ * @param d {Date} The Date to serialize
+ * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
+ * @static
+ */
+ dateToString : function (d) {
+ function _zeroPad(v) {
+ return v < 10 ? '0' + v : v;
+ }
+
+ return d.getUTCFullYear() + '-' +
+ _zeroPad(d.getUTCMonth() + 1) + '-' +
+ _zeroPad(d.getUTCDate()) + 'T' +
+ _zeroPad(d.getUTCHours()) + COLON +
+ _zeroPad(d.getUTCMinutes()) + COLON +
+ _zeroPad(d.getUTCSeconds()) + 'Z';
+ },
+
+ /**
+ * <p>Converts an arbitrary value to a JSON string representation.</p>
+ *
+ * <p>Objects with cyclical references will trigger an exception.</p>
+ *
+ * <p>If a whitelist is provided, only matching object keys will be
+ * included. Alternately, a replacer function may be passed as the
+ * second parameter. This function is executed on every value in the
+ * input, and its return value will be used in place of the original value.
+ * This is useful to serialize specialized objects or class instances.</p>
+ *
+ * <p>If a positive integer or non-empty string is passed as the third
+ * parameter, the output will be formatted with carriage returns and
+ * indentation for readability. If a String is passed (such as "\t") it
+ * will be used once for each indentation level. If a number is passed,
+ * that number of spaces will be used.</p>
+ *
+ * @method stringify
+ * @param o {MIXED} any arbitrary value to convert to JSON string
+ * @param w {Array|Function} (optional) whitelist of acceptable object
+ * keys to include, or a replacer function to modify the
+ * raw value before serialization
+ * @param ind {Number|String} (optional) indentation character or depth of
+ * spaces to format the output.
+ * @return {string} JSON string representation of the input
+ * @static
+ */
+ stringify : function (o,w,ind) {
+ return Native && Y.JSON.useNativeStringify ?
+ Native.stringify(o,w,ind) : _stringify(o,w,ind);
+ }
+});
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("json-stringify",function(C){var b=C.config.win.JSON,E=C.Lang,A=E.isFunction,L=E.isObject,N=E.isArray,X=Object.prototype.toString,W=(X.call(b)==="[object JSON]"&&b),h="undefined",O="object",e="null",R="string",U="number",Q="boolean",D="date",H={"undefined":h,"string":R,"[object String]":R,"number":U,"[object Number]":U,"boolean":Q,"[object Boolean]":Q,"[object Date]":D,"[object RegExp]":O},i="",g="{",G="}",F="[",V="]",T=",",K=",\n",B="\n",I=":",f=": ",M='"',Z=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,a={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};function c(j){var Y=typeof j;return H[Y]||H[X.call(j)]||(Y===O?(j?O:e):h);}function P(Y){if(!a[Y]){a[Y]="\\u"+("0000"+(+(Y.charCodeAt(0))).toString(16)).slice(-4);}return a[Y];}function J(Y){return M+Y.replace(Z,P)+M;}function d(Y,j){return Y.replace(/^/gm,j);}function S(j,s,Y){if(j===undefined){return undefined;}var l=A(s)?s:null,r=X.call(Y).match(/String|Number/)||[],t=C.JSON.dateToString,q=[],n,m,p;if(l||!N(s)){s=undefined;}if(s){n={};for(m=0,p=s.length;m<p;++m){n[s[m]]=true;}s=n;}Y=r[0]==="Number"?new Array(Math.min(Math.max(0,Y),10)+1).join(" "):(Y||i).slice(0,10);function k(w,AC){var AA=w[AC],AE=c(AA),z=[],y=Y?f:I,x,u,AD,o,AB;if(L(AA)&&A(AA.toJSON)){AA=AA.toJSON(AC);}else{if(AE===D){AA=t(AA);}}if(A(l)){AA=l.call(w,AC,AA);}if(AA!==w[AC]){AE=c(AA);}switch(AE){case D:case O:break;case R:return J(AA);case U:return isFinite(AA)?AA+i:e;case Q:return AA+i;case e:return e;default:return undefined;}for(u=q.length-1;u>=0;--u){if(q[u]===AA){throw new Error("JSON.stringify. Cyclical reference");}}x=N(AA);q.push(AA);if(x){for(u=AA.length-1;u>=0;--u){z[u]=k(AA,u)||e;}}else{AD=s||AA;u=0;for(o in AD){if(AD.hasOwnProperty(o)){AB=k(AA,o);if(AB){z[u++]=J(o)+y+AB;}}}}q.pop();if(Y&&z.length){return x?F+B+d(z.join(K),Y)+B+V:g+B+d(z.join(K),Y)+B+G;}else{return x?F+z.join(T)+V:g+z.join(T)+G;}}return k({"":j},"");}C.mix(C.namespace("JSON"),{useNativeStringify:!!W,dateToString:function(j){function Y(k){return k<10?"0"+k:k;}return j.getUTCFullYear()+"-"+Y(j.getUTCMonth()+1)+"-"+Y(j.getUTCDate())+"T"+Y(j.getUTCHours())+I+Y(j.getUTCMinutes())+I+Y(j.getUTCSeconds())+"Z";},stringify:function(k,Y,j){return W&&C.JSON.useNativeStringify?W.stringify(k,Y,j):S(k,Y,j);}});},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('json-stringify', function(Y) {
+
+/**
+ * Provides Y.JSON.stringify method for converting objects to JSON strings.
+ *
+ * @module json
+ * @submodule json-stringify
+ * @for JSON
+ * @static
+ */
+var _JSON = Y.config.win.JSON,
+ Lang = Y.Lang,
+ isFunction= Lang.isFunction,
+ isObject = Lang.isObject,
+ isArray = Lang.isArray,
+ _toStr = Object.prototype.toString,
+ Native = (_toStr.call(_JSON) === '[object JSON]' && _JSON),
+ UNDEFINED = 'undefined',
+ OBJECT = 'object',
+ NULL = 'null',
+ STRING = 'string',
+ NUMBER = 'number',
+ BOOLEAN = 'boolean',
+ DATE = 'date',
+ _allowable= {
+ 'undefined' : UNDEFINED,
+ 'string' : STRING,
+ '[object String]' : STRING,
+ 'number' : NUMBER,
+ '[object Number]' : NUMBER,
+ 'boolean' : BOOLEAN,
+ '[object Boolean]' : BOOLEAN,
+ '[object Date]' : DATE,
+ '[object RegExp]' : OBJECT
+ },
+ EMPTY = '',
+ OPEN_O = '{',
+ CLOSE_O = '}',
+ OPEN_A = '[',
+ CLOSE_A = ']',
+ COMMA = ',',
+ COMMA_CR = ",\n",
+ CR = "\n",
+ COLON = ':',
+ COLON_SP = ': ',
+ QUOTE = '"',
+ // Regex used to capture characters that need escaping before enclosing
+ // their containing string in quotes.
+ _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ // Character substitution map for common escapes and special characters.
+ _CHARS = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+
+
+// Utility function used to determine how to serialize a variable.
+function _type(o) {
+ var t = typeof o;
+ return _allowable[t] || // number, string, boolean, undefined
+ _allowable[_toStr.call(o)] || // Number, String, Boolean, Date
+ (t === OBJECT ?
+ (o ? OBJECT : NULL) : // object, array, null, misc natives
+ UNDEFINED); // function, unknown
+}
+
+// Escapes a special character to a safe Unicode representation
+function _char(c) {
+ if (!_CHARS[c]) {
+ _CHARS[c] = '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ }
+ return _CHARS[c];
+}
+
+// Enclose escaped strings in quotes
+function _string(s) {
+ return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE;
+}
+
+// Adds the provided space to the beginning of every line in the input string
+function _indent(s,space) {
+ return s.replace(/^/gm, space);
+}
+
+// JavaScript implementation of stringify (see API declaration of stringify)
+function _stringify(o,w,space) {
+ if (o === undefined) {
+ return undefined;
+ }
+
+ var replacer = isFunction(w) ? w : null,
+ format = _toStr.call(space).match(/String|Number/) || [],
+ _date = Y.JSON.dateToString,
+ stack = [],
+ tmp,i,len;
+
+ if (replacer || !isArray(w)) {
+ w = undefined;
+ }
+
+ // Ensure whitelist keys are unique (bug 2110391)
+ if (w) {
+ tmp = {};
+ for (i = 0, len = w.length; i < len; ++i) {
+ tmp[w[i]] = true;
+ }
+ w = tmp;
+ }
+
+ // Per the spec, strings are truncated to 10 characters and numbers
+ // are converted to that number of spaces (max 10)
+ space = format[0] === 'Number' ?
+ new Array(Math.min(Math.max(0,space),10)+1).join(" ") :
+ (space || EMPTY).slice(0,10);
+
+ function _serialize(h,key) {
+ var value = h[key],
+ t = _type(value),
+ a = [],
+ colon = space ? COLON_SP : COLON,
+ arr, i, keys, k, v;
+
+ // Per the ECMA 5 spec, toJSON is applied before the replacer is
+ // called. Also per the spec, Date.prototype.toJSON has been added, so
+ // Date instances should be serialized prior to exposure to the
+ // replacer. I disagree with this decision, but the spec is the spec.
+ if (isObject(value) && isFunction(value.toJSON)) {
+ value = value.toJSON(key);
+ } else if (t === DATE) {
+ value = _date(value);
+ }
+
+ if (isFunction(replacer)) {
+ value = replacer.call(h,key,value);
+ }
+
+ if (value !== h[key]) {
+ t = _type(value);
+ }
+
+ switch (t) {
+ case DATE : // intentional fallthrough. Pre-replacer Dates are
+ // serialized in the toJSON stage. Dates here would
+ // have been produced by the replacer.
+ case OBJECT : break;
+ case STRING : return _string(value);
+ case NUMBER : return isFinite(value) ? value+EMPTY : NULL;
+ case BOOLEAN : return value+EMPTY;
+ case NULL : return NULL;
+ default : return undefined;
+ }
+
+ // Check for cyclical references in nested objects
+ for (i = stack.length - 1; i >= 0; --i) {
+ if (stack[i] === value) {
+ throw new Error("JSON.stringify. Cyclical reference");
+ }
+ }
+
+ arr = isArray(value);
+
+ // Add the object to the processing stack
+ stack.push(value);
+
+ if (arr) { // Array
+ for (i = value.length - 1; i >= 0; --i) {
+ a[i] = _serialize(value, i) || NULL;
+ }
+ } else { // Object
+ // If whitelist provided, take only those keys
+ keys = w || value;
+ i = 0;
+
+ for (k in keys) {
+ if (keys.hasOwnProperty(k)) {
+ v = _serialize(value, k);
+ if (v) {
+ a[i++] = _string(k) + colon + v;
+ }
+ }
+ }
+ }
+
+ // remove the array from the stack
+ stack.pop();
+
+ if (space && a.length) {
+ return arr ?
+ OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A :
+ OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O;
+ } else {
+ return arr ?
+ OPEN_A + a.join(COMMA) + CLOSE_A :
+ OPEN_O + a.join(COMMA) + CLOSE_O;
+ }
+ }
+
+ // process the input
+ return _serialize({'':o},'');
+}
+
+Y.mix(Y.namespace('JSON'),{
+ /**
+ * Leverage native JSON stringify if the browser has a native
+ * implementation. In general, this is a good idea. See the Known Issues
+ * section in the JSON user guide for caveats. The default value is true
+ * for browsers with native JSON support.
+ *
+ * @property JSON.useNativeStringify
+ * @type Boolean
+ * @default true
+ * @static
+ */
+ useNativeStringify : !!Native,
+
+ /**
+ * Serializes a Date instance as a UTC date string. Used internally by
+ * stringify. Override this method if you need Dates serialized in a
+ * different format.
+ *
+ * @method dateToString
+ * @param d {Date} The Date to serialize
+ * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
+ * @static
+ */
+ dateToString : function (d) {
+ function _zeroPad(v) {
+ return v < 10 ? '0' + v : v;
+ }
+
+ return d.getUTCFullYear() + '-' +
+ _zeroPad(d.getUTCMonth() + 1) + '-' +
+ _zeroPad(d.getUTCDate()) + 'T' +
+ _zeroPad(d.getUTCHours()) + COLON +
+ _zeroPad(d.getUTCMinutes()) + COLON +
+ _zeroPad(d.getUTCSeconds()) + 'Z';
+ },
+
+ /**
+ * <p>Converts an arbitrary value to a JSON string representation.</p>
+ *
+ * <p>Objects with cyclical references will trigger an exception.</p>
+ *
+ * <p>If a whitelist is provided, only matching object keys will be
+ * included. Alternately, a replacer function may be passed as the
+ * second parameter. This function is executed on every value in the
+ * input, and its return value will be used in place of the original value.
+ * This is useful to serialize specialized objects or class instances.</p>
+ *
+ * <p>If a positive integer or non-empty string is passed as the third
+ * parameter, the output will be formatted with carriage returns and
+ * indentation for readability. If a String is passed (such as "\t") it
+ * will be used once for each indentation level. If a number is passed,
+ * that number of spaces will be used.</p>
+ *
+ * @method stringify
+ * @param o {MIXED} any arbitrary value to convert to JSON string
+ * @param w {Array|Function} (optional) whitelist of acceptable object
+ * keys to include, or a replacer function to modify the
+ * raw value before serialization
+ * @param ind {Number|String} (optional) indentation character or depth of
+ * spaces to format the output.
+ * @return {string} JSON string representation of the input
+ * @static
+ */
+ stringify : function (o,w,ind) {
+ return Native && Y.JSON.useNativeStringify ?
+ Native.stringify(o,w,ind) : _stringify(o,w,ind);
+ }
+});
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('json-parse', function(Y) {
+
+/**
+ * <p>The JSON module adds support for serializing JavaScript objects into
+ * JSON strings and parsing JavaScript objects from strings in JSON format.</p>
+ *
+ * <p>The JSON namespace is added to your YUI instance including static methods
+ * Y.JSON.parse(..) and Y.JSON.stringify(..).</p>
+ *
+ * <p>The functionality and method signatures follow the ECMAScript 5
+ * specification. In browsers with native JSON support, the native
+ * implementation is used.</p>
+ *
+ * <p>The <code>json</code> module is a rollup of <code>json-parse</code> and
+ * <code>json-stringify</code>.</p>
+ *
+ * <p>As their names suggest, <code>json-parse</code> adds support for parsing
+ * JSON data (Y.JSON.parse) and <code>json-stringify</code> for serializing
+ * JavaScript data into JSON strings (Y.JSON.stringify). You may choose to
+ * include either of the submodules individually if you don't need the
+ * complementary functionality, or include the rollup for both.</p>
+ *
+ * @module json
+ * @class JSON
+ * @static
+ */
+
+/**
+ * Provides Y.JSON.parse method to accept JSON strings and return native
+ * JavaScript objects.
+ *
+ * @module json
+ * @submodule json-parse
+ * @for JSON
+ * @static
+ */
+
+
+// All internals kept private for security reasons
+
+
+ /**
+ * Alias to native browser implementation of the JSON object if available.
+ *
+ * @property Native
+ * @type {Object}
+ * @private
+ */
+var _JSON = Y.config.win.JSON,
+ Native = (Object.prototype.toString.call(_JSON) === '[object JSON]' && _JSON),
+
+ /**
+ * Replace certain Unicode characters that JavaScript may handle incorrectly
+ * during eval--either by deleting them or treating them as line
+ * endings--with escape sequences.
+ * IMPORTANT NOTE: This regex will be used to modify the input if a match is
+ * found.
+ *
+ * @property _UNICODE_EXCEPTIONS
+ * @type {RegExp}
+ * @private
+ */
+ _UNICODE_EXCEPTIONS = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+
+
+ /**
+ * First step in the safety evaluation. Regex used to replace all escape
+ * sequences (i.e. "\\", etc) with '@' characters (a non-JSON character).
+ *
+ * @property _ESCAPES
+ * @type {RegExp}
+ * @private
+ */
+ _ESCAPES = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+
+ /**
+ * Second step in the safety evaluation. Regex used to replace all simple
+ * values with ']' characters.
+ *
+ * @property _VALUES
+ * @type {RegExp}
+ * @private
+ */
+ _VALUES = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+
+ /**
+ * Third step in the safety evaluation. Regex used to remove all open
+ * square brackets following a colon, comma, or at the beginning of the
+ * string.
+ *
+ * @property _BRACKETS
+ * @type {RegExp}
+ * @private
+ */
+ _BRACKETS = /(?:^|:|,)(?:\s*\[)+/g,
+
+ /**
+ * Final step in the safety evaluation. Regex used to test the string left
+ * after all previous replacements for invalid characters.
+ *
+ * @property _UNSAFE
+ * @type {RegExp}
+ * @private
+ */
+ _UNSAFE = /[^\],:{}\s]/,
+
+ /**
+ * Replaces specific unicode characters with their appropriate \unnnn
+ * format. Some browsers ignore certain characters during eval.
+ *
+ * @method escapeException
+ * @param c {String} Unicode character
+ * @return {String} the \unnnn escapement of the character
+ * @private
+ */
+ _escapeException = function (c) {
+ return '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ },
+
+ /**
+ * Traverses nested objects, applying a reviver function to each (key,value)
+ * from the scope if the key:value's containing object. The value returned
+ * from the function will replace the original value in the key:value pair.
+ * If the value returned is undefined, the key will be omitted from the
+ * returned object.
+ *
+ * @method _revive
+ * @param data {MIXED} Any JavaScript data
+ * @param reviver {Function} filter or mutation function
+ * @return {MIXED} The results of the filtered data
+ * @private
+ */
+ _revive = function (data, reviver) {
+ var walk = function (o,key) {
+ var k,v,value = o[key];
+ if (value && typeof value === 'object') {
+ for (k in value) {
+ if (value.hasOwnProperty(k)) {
+ v = walk(value, k);
+ if (v === undefined) {
+ delete value[k];
+ } else {
+ value[k] = v;
+ }
+ }
+ }
+ }
+ return reviver.call(o,key,value);
+ };
+
+ return typeof reviver === 'function' ? walk({'':data},'') : data;
+ },
+
+ /**
+ * Parse a JSON string, returning the native JavaScript representation.
+ *
+ * @param s {string} JSON string data
+ * @param reviver {function} (optional) function(k,v) passed each key value
+ * pair of object literals, allowing pruning or altering values
+ * @return {MIXED} the native JavaScript representation of the JSON string
+ * @throws SyntaxError
+ * @method parse
+ * @static
+ */
+ // JavaScript implementation in lieu of native browser support. Based on
+ // the json2.js library from http://json.org
+ _parse = function (s,reviver) {
+ if (typeof s === 'string') {
+ // Replace certain Unicode characters that are otherwise handled
+ // incorrectly by some browser implementations.
+ // NOTE: This modifies the input if such characters are found!
+ s = s.replace(_UNICODE_EXCEPTIONS, _escapeException);
+
+ // Test for any remaining invalid characters
+ if (!_UNSAFE.test(s.replace(_ESCAPES,'@').
+ replace(_VALUES,']').
+ replace(_BRACKETS,''))) {
+
+ // Eval the text into a JavaScript data structure, apply any
+ // reviver function, and return
+ return _revive( eval('(' + s + ')'), reviver );
+ }
+ }
+
+ throw new SyntaxError('JSON.parse');
+ };
+
+Y.namespace('JSON').parse = function (s,reviver) {
+ return Native && Y.JSON.useNativeParse ?
+ Native.parse(s,reviver) : _parse(s,reviver);
+};
+
+/**
+ * Leverage native JSON parse if the browser has a native implementation.
+ * In general, this is a good idea. See the Known Issues section in the
+ * JSON user guide for caveats. The default value is true for browsers with
+ * native JSON support.
+ *
+ * @property useNativeParse
+ * @type Boolean
+ * @default true
+ * @static
+ */
+Y.JSON.useNativeParse = !!Native;
+
+
+}, '3.0.0' );
+YUI.add('json-stringify', function(Y) {
+
+/**
+ * Provides Y.JSON.stringify method for converting objects to JSON strings.
+ *
+ * @module json
+ * @submodule json-stringify
+ * @for JSON
+ * @static
+ */
+var _JSON = Y.config.win.JSON,
+ Lang = Y.Lang,
+ isFunction= Lang.isFunction,
+ isObject = Lang.isObject,
+ isArray = Lang.isArray,
+ _toStr = Object.prototype.toString,
+ Native = (_toStr.call(_JSON) === '[object JSON]' && _JSON),
+ UNDEFINED = 'undefined',
+ OBJECT = 'object',
+ NULL = 'null',
+ STRING = 'string',
+ NUMBER = 'number',
+ BOOLEAN = 'boolean',
+ DATE = 'date',
+ _allowable= {
+ 'undefined' : UNDEFINED,
+ 'string' : STRING,
+ '[object String]' : STRING,
+ 'number' : NUMBER,
+ '[object Number]' : NUMBER,
+ 'boolean' : BOOLEAN,
+ '[object Boolean]' : BOOLEAN,
+ '[object Date]' : DATE,
+ '[object RegExp]' : OBJECT
+ },
+ EMPTY = '',
+ OPEN_O = '{',
+ CLOSE_O = '}',
+ OPEN_A = '[',
+ CLOSE_A = ']',
+ COMMA = ',',
+ COMMA_CR = ",\n",
+ CR = "\n",
+ COLON = ':',
+ COLON_SP = ': ',
+ QUOTE = '"',
+ // Regex used to capture characters that need escaping before enclosing
+ // their containing string in quotes.
+ _SPECIAL_CHARS = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
+ // Character substitution map for common escapes and special characters.
+ _CHARS = {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '"' : '\\"',
+ '\\': '\\\\'
+ };
+
+
+// Utility function used to determine how to serialize a variable.
+function _type(o) {
+ var t = typeof o;
+ return _allowable[t] || // number, string, boolean, undefined
+ _allowable[_toStr.call(o)] || // Number, String, Boolean, Date
+ (t === OBJECT ?
+ (o ? OBJECT : NULL) : // object, array, null, misc natives
+ UNDEFINED); // function, unknown
+}
+
+// Escapes a special character to a safe Unicode representation
+function _char(c) {
+ if (!_CHARS[c]) {
+ _CHARS[c] = '\\u'+('0000'+(+(c.charCodeAt(0))).toString(16)).slice(-4);
+ }
+ return _CHARS[c];
+}
+
+// Enclose escaped strings in quotes
+function _string(s) {
+ return QUOTE + s.replace(_SPECIAL_CHARS, _char) + QUOTE;
+}
+
+// Adds the provided space to the beginning of every line in the input string
+function _indent(s,space) {
+ return s.replace(/^/gm, space);
+}
+
+// JavaScript implementation of stringify (see API declaration of stringify)
+function _stringify(o,w,space) {
+ if (o === undefined) {
+ return undefined;
+ }
+
+ var replacer = isFunction(w) ? w : null,
+ format = _toStr.call(space).match(/String|Number/) || [],
+ _date = Y.JSON.dateToString,
+ stack = [],
+ tmp,i,len;
+
+ if (replacer || !isArray(w)) {
+ w = undefined;
+ }
+
+ // Ensure whitelist keys are unique (bug 2110391)
+ if (w) {
+ tmp = {};
+ for (i = 0, len = w.length; i < len; ++i) {
+ tmp[w[i]] = true;
+ }
+ w = tmp;
+ }
+
+ // Per the spec, strings are truncated to 10 characters and numbers
+ // are converted to that number of spaces (max 10)
+ space = format[0] === 'Number' ?
+ new Array(Math.min(Math.max(0,space),10)+1).join(" ") :
+ (space || EMPTY).slice(0,10);
+
+ function _serialize(h,key) {
+ var value = h[key],
+ t = _type(value),
+ a = [],
+ colon = space ? COLON_SP : COLON,
+ arr, i, keys, k, v;
+
+ // Per the ECMA 5 spec, toJSON is applied before the replacer is
+ // called. Also per the spec, Date.prototype.toJSON has been added, so
+ // Date instances should be serialized prior to exposure to the
+ // replacer. I disagree with this decision, but the spec is the spec.
+ if (isObject(value) && isFunction(value.toJSON)) {
+ value = value.toJSON(key);
+ } else if (t === DATE) {
+ value = _date(value);
+ }
+
+ if (isFunction(replacer)) {
+ value = replacer.call(h,key,value);
+ }
+
+ if (value !== h[key]) {
+ t = _type(value);
+ }
+
+ switch (t) {
+ case DATE : // intentional fallthrough. Pre-replacer Dates are
+ // serialized in the toJSON stage. Dates here would
+ // have been produced by the replacer.
+ case OBJECT : break;
+ case STRING : return _string(value);
+ case NUMBER : return isFinite(value) ? value+EMPTY : NULL;
+ case BOOLEAN : return value+EMPTY;
+ case NULL : return NULL;
+ default : return undefined;
+ }
+
+ // Check for cyclical references in nested objects
+ for (i = stack.length - 1; i >= 0; --i) {
+ if (stack[i] === value) {
+ throw new Error("JSON.stringify. Cyclical reference");
+ }
+ }
+
+ arr = isArray(value);
+
+ // Add the object to the processing stack
+ stack.push(value);
+
+ if (arr) { // Array
+ for (i = value.length - 1; i >= 0; --i) {
+ a[i] = _serialize(value, i) || NULL;
+ }
+ } else { // Object
+ // If whitelist provided, take only those keys
+ keys = w || value;
+ i = 0;
+
+ for (k in keys) {
+ if (keys.hasOwnProperty(k)) {
+ v = _serialize(value, k);
+ if (v) {
+ a[i++] = _string(k) + colon + v;
+ }
+ }
+ }
+ }
+
+ // remove the array from the stack
+ stack.pop();
+
+ if (space && a.length) {
+ return arr ?
+ OPEN_A + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_A :
+ OPEN_O + CR + _indent(a.join(COMMA_CR), space) + CR + CLOSE_O;
+ } else {
+ return arr ?
+ OPEN_A + a.join(COMMA) + CLOSE_A :
+ OPEN_O + a.join(COMMA) + CLOSE_O;
+ }
+ }
+
+ // process the input
+ return _serialize({'':o},'');
+}
+
+Y.mix(Y.namespace('JSON'),{
+ /**
+ * Leverage native JSON stringify if the browser has a native
+ * implementation. In general, this is a good idea. See the Known Issues
+ * section in the JSON user guide for caveats. The default value is true
+ * for browsers with native JSON support.
+ *
+ * @property JSON.useNativeStringify
+ * @type Boolean
+ * @default true
+ * @static
+ */
+ useNativeStringify : !!Native,
+
+ /**
+ * Serializes a Date instance as a UTC date string. Used internally by
+ * stringify. Override this method if you need Dates serialized in a
+ * different format.
+ *
+ * @method dateToString
+ * @param d {Date} The Date to serialize
+ * @return {String} stringified Date in UTC format YYYY-MM-DDTHH:mm:SSZ
+ * @static
+ */
+ dateToString : function (d) {
+ function _zeroPad(v) {
+ return v < 10 ? '0' + v : v;
+ }
+
+ return d.getUTCFullYear() + '-' +
+ _zeroPad(d.getUTCMonth() + 1) + '-' +
+ _zeroPad(d.getUTCDate()) + 'T' +
+ _zeroPad(d.getUTCHours()) + COLON +
+ _zeroPad(d.getUTCMinutes()) + COLON +
+ _zeroPad(d.getUTCSeconds()) + 'Z';
+ },
+
+ /**
+ * <p>Converts an arbitrary value to a JSON string representation.</p>
+ *
+ * <p>Objects with cyclical references will trigger an exception.</p>
+ *
+ * <p>If a whitelist is provided, only matching object keys will be
+ * included. Alternately, a replacer function may be passed as the
+ * second parameter. This function is executed on every value in the
+ * input, and its return value will be used in place of the original value.
+ * This is useful to serialize specialized objects or class instances.</p>
+ *
+ * <p>If a positive integer or non-empty string is passed as the third
+ * parameter, the output will be formatted with carriage returns and
+ * indentation for readability. If a String is passed (such as "\t") it
+ * will be used once for each indentation level. If a number is passed,
+ * that number of spaces will be used.</p>
+ *
+ * @method stringify
+ * @param o {MIXED} any arbitrary value to convert to JSON string
+ * @param w {Array|Function} (optional) whitelist of acceptable object
+ * keys to include, or a replacer function to modify the
+ * raw value before serialization
+ * @param ind {Number|String} (optional) indentation character or depth of
+ * spaces to format the output.
+ * @return {string} JSON string representation of the input
+ * @static
+ */
+ stringify : function (o,w,ind) {
+ return Native && Y.JSON.useNativeStringify ?
+ Native.stringify(o,w,ind) : _stringify(o,w,ind);
+ }
+});
+
+
+}, '3.0.0' );
+
+
+YUI.add('json', function(Y){}, '3.0.0' ,{use:['json-parse', 'json-stringify']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('loader', function(Y) {
+
+(function() {
+/**
+ * Loader dynamically loads script and css files. It includes the dependency
+ * info for the version of the library in use, and will automatically pull in
+ * dependencies for the modules requested. It supports rollup files and will
+ * automatically use these when appropriate in order to minimize the number of
+ * http connections required to load all of the dependencies. It can load the
+ * files from the Yahoo! CDN, and it can utilize the combo service provided on
+ * this network to reduce the number of http connections required to download
+ * YUI files.
+ *
+ * @module loader
+ */
+
+/**
+ * Loader dynamically loads script and css files. It includes the dependency
+ * info for the version of the library in use, and will automatically pull in
+ * dependencies for the modules requested. It supports rollup files and will
+ * automatically use these when appropriate in order to minimize the number of
+ * http connections required to load all of the dependencies. It can load the
+ * files from the Yahoo! CDN, and it can utilize the combo service provided on
+ * this network to reduce the number of http connections required to download
+ * YUI files.
+ *
+ * While the loader can be instantiated by the end user, it normally is not.
+ * @see YUI.use for the normal use case. The use function automatically will
+ * pull in missing dependencies.
+ *
+ * @class Loader
+ * @constructor
+ * @param o an optional set of configuration options. Valid options:
+ * <ul>
+ * <li>base:
+ * The base dir</li>
+ * <li>secureBase:
+ * The secure base dir (not implemented)</li>
+ * <li>comboBase:
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?</li>
+ * <li>root:
+ * The root path to prepend to module names for the combo service. Ex: 2.5.2/build/</li>
+ * <li>filter:
+ *
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * </li>
+ * <li>filters: per-component filter specification. If specified for a given component, this overrides the filter config</li>
+ * <li>combine:
+ * Use the YUI combo service to reduce the number of http connections required to load your dependencies</li>
+ * <li>ignore:
+ * A list of modules that should never be dynamically loaded</li>
+ * <li>force:
+ * A list of modules that should always be loaded when required, even if already present on the page</li>
+ * <li>insertBefore:
+ * Node or id for a node that should be used as the insertion point for new nodes</li>
+ * <li>charset:
+ * charset for dynamic nodes (deprecated, use jsAttributes or cssAttributes)</li>
+ * <li>jsAttributes: object literal containing attributes to add to script nodes</li>
+ * <li>cssAttributes: object literal containing attributes to add to link nodes</li>
+ * <li>timeout:
+ * number of milliseconds before a timeout occurs when dynamically loading nodes. in not set, there is no timeout</li>
+ * <li>context:
+ * execution context for all callbacks</li>
+ * <li>onSuccess:
+ * callback for the 'success' event</li>
+ * <li>onFailure: callback for the 'failure' event</li>
+ * <li>onCSS: callback for the 'CSSComplete' event. When loading YUI components with CSS
+ * the CSS is loaded first, then the script. This provides a moment you can tie into to improve
+ * the presentation of the page while the script is loading.</li>
+ * <li>onTimeout:
+ * callback for the 'timeout' event</li>
+ * <li>onProgress:
+ * callback executed each time a script or css file is loaded</li>
+ * <li>modules:
+ * A list of module definitions. See Loader.addModule for the supported module metadata</li>
+ * </ul>
+ */
+
+/*
+ * Global loader queue
+ * @property _loaderQueue
+ * @type Queue
+ * @private
+ */
+YUI.Env._loaderQueue = YUI.Env._loaderQueue || new Y.Queue();
+
+var NOT_FOUND = {},
+ GLOBAL_ENV = YUI.Env,
+ GLOBAL_LOADED,
+ BASE = 'base',
+ CSS = 'css',
+ JS = 'js',
+ CSSRESET = 'cssreset',
+ CSSFONTS = 'cssfonts',
+ CSSGRIDS = 'cssgrids',
+ CSSBASE = 'cssbase',
+ CSS_AFTER = [CSSRESET, CSSFONTS, CSSGRIDS,
+ 'cssreset-context', 'cssfonts-context', 'cssgrids-context'],
+ YUI_CSS = ['reset', 'fonts', 'grids', BASE],
+ VERSION = Y.version,
+ ROOT = VERSION + '/build/',
+ CONTEXT = '-context',
+
+ ANIMBASE = 'anim-base',
+ ATTRIBUTE = 'attribute',
+ ATTRIBUTEBASE = ATTRIBUTE + '-base',
+ BASEBASE = 'base-base',
+ DDDRAG = 'dd-drag',
+ DOM = 'dom',
+ DATASCHEMABASE = 'dataschema-base',
+ DATASOURCELOCAL = 'datasource-local',
+ DOMBASE = 'dom-base',
+ DOMSTYLE = 'dom-style',
+ DOMSCREEN = 'dom-screen',
+ DUMP = 'dump',
+ GET = 'get',
+ EVENTBASE = 'event-base',
+ EVENTCUSTOM = 'event-custom',
+ EVENTCUSTOMBASE = 'event-custom-base',
+ IOBASE = 'io-base',
+ NODE = 'node',
+ NODEBASE = 'node-base',
+ NODESTYLE = 'node-style',
+ NODESCREEN = 'node-screen',
+ OOP = 'oop',
+ PLUGINHOST = 'pluginhost',
+ SELECTORCSS2 = 'selector-css2',
+ SUBSTITUTE = 'substitute',
+ WIDGET = 'widget',
+ WIDGETPOSITION = 'widget-position',
+ YUIBASE = 'yui-base',
+
+ PLUGIN = 'plugin',
+
+ META = {
+
+ version: VERSION,
+
+ root: ROOT,
+
+ base: 'http://yui.yahooapis.com/' + ROOT,
+
+ comboBase: 'http://yui.yahooapis.com/combo?',
+
+ skin: {
+ defaultSkin: 'sam',
+ base: 'assets/skins/',
+ path: 'skin.css',
+ after: CSS_AFTER
+ //rollup: 3
+ },
+
+ modules: {
+
+ dom: {
+ requires: [OOP],
+ submodules: {
+
+ 'dom-base': {
+ requires: [OOP]
+ },
+
+ 'dom-style': {
+ requires: [DOMBASE]
+ },
+
+ 'dom-screen': {
+ requires: [DOMBASE, DOMSTYLE]
+ },
+
+ 'selector-native': {
+ requires: [DOMBASE]
+ },
+
+ 'selector-css2': {
+ requires: ['selector-native']
+ },
+
+ 'selector': {
+ requires: [DOMBASE]
+ }
+
+ },
+
+ plugins: {
+ 'selector-css3': {
+ requires: [SELECTORCSS2]
+ }
+ }
+ },
+
+ node: {
+ requires: [DOM, EVENTBASE],
+ // expound: EVENT,
+
+ submodules: {
+ 'node-base': {
+ requires: [DOMBASE, SELECTORCSS2, EVENTBASE]
+ },
+
+ 'node-style': {
+ requires: [DOMSTYLE, NODEBASE]
+ },
+
+ 'node-screen': {
+ requires: [DOMSCREEN, NODEBASE]
+ },
+
+ 'node-pluginhost': {
+ requires: [NODEBASE, PLUGINHOST]
+ },
+
+
+ 'node-event-delegate': {
+ requires: [NODEBASE, 'event-delegate']
+ }
+ },
+
+ plugins: {
+ 'node-event-simulate': {
+ requires: [NODEBASE, 'event-simulate']
+ }
+ }
+ },
+
+ anim: {
+ submodules: {
+
+ 'anim-base': {
+ requires: [BASEBASE, NODESTYLE]
+ },
+
+ 'anim-color': {
+ requires: [ANIMBASE]
+ },
+
+ 'anim-easing': {
+ requires: [ANIMBASE]
+ },
+
+ 'anim-scroll': {
+ requires: [ANIMBASE]
+ },
+
+ 'anim-xy': {
+ requires: [ANIMBASE, NODESCREEN]
+ },
+
+ 'anim-curve': {
+ requires: ['anim-xy']
+ },
+
+ 'anim-node-plugin': {
+ requires: ['node-pluginhost', ANIMBASE]
+ }
+ }
+ },
+
+ attribute: {
+ submodules: {
+ 'attribute-base': {
+ requires: [EVENTCUSTOM]
+ },
+
+ 'attribute-complex': {
+ requires: [ATTRIBUTEBASE]
+ }
+ }
+ },
+
+ base: {
+ submodules: {
+ 'base-base': {
+ requires: [ATTRIBUTEBASE]
+ },
+
+ 'base-build': {
+ requires: [BASEBASE]
+ },
+
+ 'base-pluginhost': {
+ requires: [BASEBASE, PLUGINHOST]
+ }
+ }
+ },
+
+ cache: {
+ requires: [PLUGIN]
+ },
+
+ compat: {
+ requires: [NODE, DUMP, SUBSTITUTE]
+ },
+
+ classnamemanager: {
+ requires: [YUIBASE]
+ },
+
+ collection: {
+ requires: [OOP]
+ },
+
+ console: {
+ requires: ['yui-log', WIDGET, SUBSTITUTE],
+ skinnable: true,
+ plugins: {
+ 'console-filters': {
+ requires: [PLUGIN, 'console'],
+ skinnable: true
+ }
+ }
+ },
+
+ cookie: {
+ requires: [YUIBASE]
+ },
+
+ dataschema:{
+ submodules: {
+ 'dataschema-base': {
+ requires: [BASE]
+ },
+ 'dataschema-array': {
+ requires: [DATASCHEMABASE]
+ },
+ 'dataschema-json': {
+ requires: [DATASCHEMABASE, 'json']
+ },
+ 'dataschema-text': {
+ requires: [DATASCHEMABASE]
+ },
+ 'dataschema-xml': {
+ requires: [DATASCHEMABASE]
+ }
+ }
+ },
+
+ datasource:{
+ submodules: {
+ 'datasource-local': {
+ requires: [BASE]
+ },
+ 'datasource-arrayschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-array']
+ },
+ 'datasource-cache': {
+ requires: [DATASOURCELOCAL, 'cache']
+ },
+ 'datasource-function': {
+ requires: [DATASOURCELOCAL]
+ },
+ 'datasource-jsonschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-json']
+ },
+ 'datasource-polling': {
+ requires: [DATASOURCELOCAL]
+ },
+ 'datasource-get': {
+ requires: [DATASOURCELOCAL, GET]
+ },
+ 'datasource-textschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-text']
+ },
+ 'datasource-io': {
+ requires: [DATASOURCELOCAL, IOBASE]
+ },
+ 'datasource-xmlschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-xml']
+ }
+ }
+ },
+
+ datatype:{
+ submodules: {
+ 'datatype-date': {
+ requires: [YUIBASE]
+ },
+ 'datatype-number': {
+ requires: [YUIBASE]
+ },
+ 'datatype-xml': {
+ requires: [YUIBASE]
+ }
+ }
+ },
+
+ dd:{
+ submodules: {
+ 'dd-ddm-base': {
+ requires: [NODE, BASE]
+ },
+ 'dd-ddm':{
+ requires: ['dd-ddm-base', 'event-resize']
+ },
+ 'dd-ddm-drop':{
+ requires: ['dd-ddm']
+ },
+ 'dd-drag':{
+ requires: ['dd-ddm-base']
+ },
+ 'dd-drop':{
+ requires: ['dd-ddm-drop']
+ },
+ 'dd-proxy':{
+ requires: [DDDRAG]
+ },
+ 'dd-constrain':{
+ requires: [DDDRAG]
+ },
+ 'dd-scroll':{
+ requires: [DDDRAG]
+ },
+ 'dd-plugin':{
+ requires: [DDDRAG],
+ optional: ['dd-constrain', 'dd-proxy']
+ },
+ 'dd-drop-plugin':{
+ requires: ['dd-drop']
+ }
+ }
+ },
+
+ dump: {
+ requires: [YUIBASE]
+ },
+
+ event: {
+ expound: NODEBASE,
+ submodules: {
+ 'event-base': {
+ expound: NODEBASE,
+ requires: [EVENTCUSTOMBASE]
+ },
+ 'event-delegate': {
+ requires: [NODEBASE]
+ },
+ 'event-focus': {
+ requires: [NODEBASE]
+ },
+ 'event-key': {
+ requires: [NODEBASE]
+ },
+ 'event-mouseenter': {
+ requires: [NODEBASE]
+ },
+ 'event-mousewheel': {
+ requires: [NODEBASE]
+ },
+ 'event-resize': {
+ requires: [NODEBASE]
+ }
+ }
+ },
+
+ 'event-custom': {
+ submodules: {
+ 'event-custom-base': {
+ requires: [OOP, 'yui-later']
+ },
+ 'event-custom-complex': {
+ requires: [EVENTCUSTOMBASE]
+ }
+ }
+ },
+
+ 'event-simulate': {
+ requires: [EVENTBASE]
+ },
+
+ 'node-focusmanager': {
+ requires: [ATTRIBUTE, NODE, PLUGIN, 'node-event-simulate', 'event-key', 'event-focus']
+ },
+
+ history: {
+ requires: [NODE]
+ },
+
+ imageloader: {
+ requires: [BASEBASE, NODESTYLE, NODESCREEN]
+ },
+
+ io:{
+ submodules: {
+
+ 'io-base': {
+ requires: [EVENTCUSTOMBASE]
+ },
+
+ 'io-xdr': {
+ requires: [IOBASE, 'datatype-xml']
+ },
+
+ 'io-form': {
+ requires: [IOBASE, NODEBASE, NODESTYLE]
+ },
+
+ 'io-upload-iframe': {
+ requires: [IOBASE, NODEBASE]
+ },
+
+ 'io-queue': {
+ requires: [IOBASE, 'queue-promote']
+ }
+ }
+ },
+
+ json: {
+ submodules: {
+ 'json-parse': {
+ requires: [YUIBASE]
+ },
+
+ 'json-stringify': {
+ requires: [YUIBASE]
+ }
+ }
+ },
+
+ loader: {
+ requires: [GET]
+ },
+
+ 'node-menunav': {
+ requires: [NODE, 'classnamemanager', PLUGIN, 'node-focusmanager'],
+ skinnable: true
+ },
+
+ oop: {
+ requires: [YUIBASE]
+ },
+
+ overlay: {
+ requires: [WIDGET, WIDGETPOSITION, 'widget-position-ext', 'widget-stack', 'widget-stdmod'],
+ skinnable: true
+ },
+
+ plugin: {
+ requires: [BASEBASE]
+ },
+
+ pluginhost: {
+ requires: [YUIBASE]
+ },
+
+ profiler: {
+ requires: [YUIBASE]
+ },
+
+ 'queue-promote': {
+ requires: [YUIBASE]
+ },
+
+ // deprecated package, replaced with async-queue
+ 'queue-run': {
+ requires: [EVENTCUSTOM],
+ path: 'async-queue/async-queue-min.js'
+ },
+
+ 'async-queue': {
+ requires: [EVENTCUSTOM],
+ supersedes: ['queue-run']
+ },
+
+ slider: {
+ requires: [WIDGET, 'dd-constrain'],
+ skinnable: true
+ },
+
+ stylesheet: {
+ requires: [YUIBASE]
+ },
+
+ substitute: {
+ optional: [DUMP]
+ },
+
+ widget: {
+ requires: [ATTRIBUTE, 'event-focus', BASE, NODE, 'classnamemanager'],
+ plugins: {
+ 'widget-position': { },
+ 'widget-position-ext': {
+ requires: [WIDGETPOSITION]
+ },
+ 'widget-stack': {
+ skinnable: true
+ },
+ 'widget-stdmod': { }
+ },
+ skinnable: true
+ },
+
+ yui: {
+ submodules: {
+ 'yui-base': {},
+ get: {},
+ 'yui-log': {},
+ 'yui-later': {}
+ }
+ },
+
+ test: {
+ requires: [SUBSTITUTE, NODE, 'json', 'event-simulate']
+ }
+
+ }
+},
+
+_path = Y.cached(function(dir, file, type) {
+ return dir + '/' + file + '-min.' + (type || CSS);
+}),
+
+_queue = YUI.Env._loaderQueue,
+
+mods = META.modules, i, bname, mname, contextname,
+L = Y.Lang;
+
+// Create the metadata for both the regular and context-aware
+// versions of the YUI CSS foundation.
+for (i=0; i<YUI_CSS.length; i=i+1) {
+ bname = YUI_CSS[i];
+ mname = CSS + bname;
+
+ mods[mname] = {
+ type: CSS,
+ path: _path(mname, bname)
+ };
+
+ // define -context module
+ contextname = mname + CONTEXT;
+ bname = bname + CONTEXT;
+
+ mods[contextname] = {
+ type: CSS,
+ path: _path(mname, bname)
+ };
+
+ if (mname == CSSGRIDS) {
+ mods[mname].requires = [CSSFONTS];
+ mods[mname].optional = [CSSRESET];
+ mods[contextname].requires = [CSSFONTS + CONTEXT];
+ mods[contextname].optional = [CSSRESET + CONTEXT];
+ } else if (mname == CSSBASE) {
+ mods[mname].after = CSS_AFTER;
+ mods[contextname].after = CSS_AFTER;
+ }
+}
+
+Y.Env.meta = META;
+
+GLOBAL_LOADED = GLOBAL_ENV._loaded;
+
+Y.Loader = function(o) {
+
+ /**
+ * Internal callback to handle multiple internal insert() calls
+ * so that css is inserted prior to js
+ * @property _internalCallback
+ * @private
+ */
+ // this._internalCallback = null;
+
+ /**
+ * Callback that will be executed when the loader is finished
+ * with an insert
+ * @method onSuccess
+ * @type function
+ */
+ // this.onSuccess = null;
+
+ /**
+ * Callback that will be executed if there is a failure
+ * @method onFailure
+ * @type function
+ */
+ // this.onFailure = null;
+
+ /**
+ * Callback for the 'CSSComplete' event. When loading YUI components with CSS
+ * the CSS is loaded first, then the script. This provides a moment you can tie into to improve
+ * the presentation of the page while the script is loading.
+ * @method onCSS
+ * @type function
+ */
+ // this.onCSS = null;
+
+ /**
+ * Callback executed each time a script or css file is loaded
+ * @method onProgress
+ * @type function
+ */
+ // this.onProgress = null;
+
+ /**
+ * Callback that will be executed if a timeout occurs
+ * @method onTimeout
+ * @type function
+ */
+ // this.onTimeout = null;
+
+ /**
+ * The execution context for all callbacks
+ * @property context
+ * @default {YUI} the YUI instance
+ */
+ this.context = Y;
+
+ /**
+ * Data that is passed to all callbacks
+ * @property data
+ */
+ // this.data = null;
+
+ /**
+ * Node reference or id where new nodes should be inserted before
+ * @property insertBefore
+ * @type string|HTMLElement
+ */
+ // this.insertBefore = null;
+
+ /**
+ * The charset attribute for inserted nodes
+ * @property charset
+ * @type string
+ * @deprecated, use cssAttributes or jsAttributes
+ */
+ // this.charset = null;
+
+ /**
+ * An object literal containing attributes to add to link nodes
+ * @property cssAttributes
+ * @type object
+ */
+ // this.cssAttributes = null;
+
+ /**
+ * An object literal containing attributes to add to script nodes
+ * @property jsAttributes
+ * @type object
+ */
+ // this.jsAttributes = null;
+
+ /**
+ * The base directory.
+ * @property base
+ * @type string
+ * @default http://yui.yahooapis.com/[YUI VERSION]/build/
+ */
+ this.base = Y.Env.meta.base;
+
+ /**
+ * Base path for the combo service
+ * @property comboBase
+ * @type string
+ * @default http://yui.yahooapis.com/combo?
+ */
+ this.comboBase = Y.Env.meta.comboBase;
+
+ /**
+ * If configured, YUI JS resources will use the combo
+ * handler
+ * @property combine
+ * @type boolean
+ * @default true if a base dir isn't in the config
+ */
+ this.combine = o.base && (o.base.indexOf( this.comboBase.substr(0, 20)) > -1);
+
+ /**
+ * Ignore modules registered on the YUI global
+ * @property ignoreRegistered
+ * @default false
+ */
+ // this.ignoreRegistered = false;
+
+ /**
+ * Root path to prepend to module path for the combo
+ * service
+ * @property root
+ * @type string
+ * @default [YUI VERSION]/build/
+ */
+ this.root = Y.Env.meta.root;
+
+ /**
+ * Timeout value in milliseconds. If set, this value will be used by
+ * the get utility. the timeout event will fire if
+ * a timeout occurs.
+ * @property timeout
+ * @type int
+ */
+ this.timeout = 0;
+
+ /**
+ * A list of modules that should not be loaded, even if
+ * they turn up in the dependency tree
+ * @property ignore
+ * @type string[]
+ */
+ // this.ignore = null;
+
+ /**
+ * A list of modules that should always be loaded, even
+ * if they have already been inserted into the page.
+ * @property force
+ * @type string[]
+ */
+ // this.force = null;
+
+ this.forceMap = {};
+
+ /**
+ * Should we allow rollups
+ * @property allowRollup
+ * @type boolean
+ * @default true
+ */
+ // this.allowRollup = true;
+
+ /**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ * @property filter
+ * @type string|{searchExp: string, replaceStr: string}
+ */
+ // this.filter = null;
+
+ /**
+ * per-component filter specification. If specified for a given component, this
+ * overrides the filter config.
+ * @property filters
+ * @type object
+ */
+ this.filters = {};
+
+ /**
+ * The list of requested modules
+ * @property required
+ * @type {string: boolean}
+ */
+ this.required = {};
+
+ /**
+ * The library metadata
+ * @property moduleInfo
+ */
+ // this.moduleInfo = Y.merge(Y.Env.meta.moduleInfo);
+ this.moduleInfo = {};
+
+ /**
+ * Provides the information used to skin the skinnable components.
+ * The following skin definition would result in 'skin1' and 'skin2'
+ * being loaded for calendar (if calendar was requested), and
+ * 'sam' for all other skinnable components:
+ *
+ * <code>
+ * skin: {
+ *
+ * // The default skin, which is automatically applied if not
+ * // overriden by a component-specific skin definition.
+ * // Change this in to apply a different skin globally
+ * defaultSkin: 'sam',
+ *
+ * // This is combined with the loader base property to get
+ * // the default root directory for a skin. ex:
+ * // http://yui.yahooapis.com/2.3.0/build/assets/skins/sam/
+ * base: 'assets/skins/',
+ *
+ * // The name of the rollup css file for the skin
+ * path: 'skin.css',
+ *
+ * // The number of skinnable components requested that are
+ * // required before using the rollup file rather than the
+ * // individual component css files
+ * rollup: 3,
+ *
+ * // Any component-specific overrides can be specified here,
+ * // making it possible to load different skins for different
+ * // components. It is possible to load more than one skin
+ * // for a given component as well.
+ * overrides: {
+ * calendar: ['skin1', 'skin2']
+ * }
+ * }
+ * </code>
+ * @property skin
+ */
+ this.skin = Y.merge(Y.Env.meta.skin);
+
+ var defaults = Y.Env.meta.modules, i, onPage = YUI.Env.mods;
+
+ this._internal = true;
+ for (i in defaults) {
+ if (defaults.hasOwnProperty(i)) {
+ this.addModule(defaults[i], i);
+ }
+ }
+
+ for (i in onPage) {
+ if (onPage.hasOwnProperty(i) && !this.moduleInfo[i] && onPage[i].details) {
+ this.addModule(onPage[i].details, i);
+ }
+ }
+ this._internal = false;
+
+ /**
+ * List of rollup files found in the library metadata
+ * @property rollups
+ */
+ // this.rollups = null;
+
+ /**
+ * Whether or not to load optional dependencies for
+ * the requested modules
+ * @property loadOptional
+ * @type boolean
+ * @default false
+ */
+ // this.loadOptional = false;
+
+ /**
+ * All of the derived dependencies in sorted order, which
+ * will be populated when either calculate() or insert()
+ * is called
+ * @property sorted
+ * @type string[]
+ */
+ this.sorted = [];
+
+ /**
+ * Set when beginning to compute the dependency tree.
+ * Composed of what YUI reports to be loaded combined
+ * with what has been loaded by any instance on the page
+ * with the version number specified in the metadata.
+ * @propery loaded
+ * @type {string: boolean}
+ */
+ this.loaded = GLOBAL_LOADED[VERSION];
+
+ /**
+ * A list of modules to attach to the YUI instance when complete.
+ * If not supplied, the sorted list of dependencies are applied.
+ * @property attaching
+ */
+ // this.attaching = null;
+
+ /**
+ * Flag to indicate the dependency tree needs to be recomputed
+ * if insert is called again.
+ * @property dirty
+ * @type boolean
+ * @default true
+ */
+ this.dirty = true;
+
+ /**
+ * List of modules inserted by the utility
+ * @property inserted
+ * @type {string: boolean}
+ */
+ this.inserted = {};
+
+ /**
+ * List of skipped modules during insert() because the module
+ * was not defined
+ * @property skipped
+ */
+ this.skipped = {};
+
+
+ // Y.on('yui:load', this.loadNext, this);
+
+ this._config(o);
+
+};
+
+Y.Loader.prototype = {
+
+ FILTER_DEFS: {
+ RAW: {
+ 'searchExp': "-min\\.js",
+ 'replaceStr': ".js"
+ },
+ DEBUG: {
+ 'searchExp': "-min\\.js",
+ 'replaceStr': "-debug.js"
+ }
+ },
+
+ SKIN_PREFIX: "skin-",
+
+ _config: function(o) {
+
+ var i, j, val, f;
+
+ // apply config values
+ if (o) {
+ for (i in o) {
+ if (o.hasOwnProperty(i)) {
+ val = o[i];
+ if (i == 'require') {
+ this.require(val);
+ } else if (i == 'modules') {
+
+ // add a hash of module definitions
+ for (j in val) {
+ if (val.hasOwnProperty(j)) {
+ this.addModule(val[j], j);
+ }
+ }
+
+ } else {
+ this[i] = val;
+ }
+ }
+ }
+ }
+
+ // fix filter
+ f = this.filter;
+
+ if (L.isString(f)) {
+ f = f.toUpperCase();
+ this.filterName = f;
+ this.filter = this.FILTER_DEFS[f];
+ if (f == 'DEBUG') {
+ this.require('yui-log', 'dump');
+ }
+ }
+
+ },
+
+ /**
+ * Returns the skin module name for the specified skin name. If a
+ * module name is supplied, the returned skin module name is
+ * specific to the module passed in.
+ * @method formatSkin
+ * @param skin {string} the name of the skin
+ * @param mod {string} optional: the name of a module to skin
+ * @return {string} the full skin module name
+ */
+ formatSkin: function(skin, mod) {
+ var s = this.SKIN_PREFIX + skin;
+ if (mod) {
+ s = s + "-" + mod;
+ }
+
+ return s;
+ },
+
+ /*
+ * Reverses <code>formatSkin</code>, providing the skin name and
+ * module name if the string matches the pattern for skins.
+ * @method parseSkin
+ * @param mod {string} the module name to parse
+ * @return {skin: string, module: string} the parsed skin name
+ * and module name, or null if the supplied string does not match
+ * the skin pattern
+ *
+ * This isn't being used at the moment
+ *
+ */
+ // parseSkin: function(mod) {
+ //
+ // if (mod.indexOf(this.SKIN_PREFIX) === 0) {
+ // var a = mod.split("-");
+ // return {skin: a[1], module: a[2]};
+ // }
+ // return null;
+ // },
+
+ /**
+ * Adds the skin def to the module info
+ * @method _addSkin
+ * @param skin {string} the name of the skin
+ * @param mod {string} the name of the module
+ * @param parent {string} parent module if this is a skin of a
+ * submodule or plugin
+ * @return {string} the module name for the skin
+ * @private
+ */
+ _addSkin: function(skin, mod, parent) {
+
+ var name = this.formatSkin(skin),
+ info = this.moduleInfo,
+ sinf = this.skin,
+ ext = info[mod] && info[mod].ext,
+ mdef, pkg;
+
+ /*
+ // Add a module definition for the skin rollup css
+ // Y.log('ext? ' + mod + ": " + ext);
+ if (!info[name]) {
+ // Y.log('adding skin ' + name);
+ this.addModule({
+ 'name': name,
+ 'type': 'css',
+ 'path': sinf.base + skin + '/' + sinf.path,
+ //'supersedes': '*',
+ 'after': sinf.after,
+ 'rollup': sinf.rollup,
+ 'ext': ext
+ });
+ }
+ */
+
+ // Add a module definition for the module-specific skin css
+ if (mod) {
+ name = this.formatSkin(skin, mod);
+ if (!info[name]) {
+ mdef = info[mod];
+ pkg = mdef.pkg || mod;
+ // Y.log('adding skin ' + name);
+ this.addModule({
+ 'name': name,
+ 'type': 'css',
+ 'after': sinf.after,
+ 'path': (parent || pkg) + '/' + sinf.base + skin + '/' + mod + '.css',
+ 'ext': ext
+ });
+ }
+ }
+
+ return name;
+ },
+
+ /** Add a new module to the component metadata.
+ * <dl>
+ * <dt>name:</dt> <dd>required, the component name</dd>
+ * <dt>type:</dt> <dd>required, the component type (js or css)</dd>
+ * <dt>path:</dt> <dd>required, the path to the script from "base"</dd>
+ * <dt>requires:</dt> <dd>array of modules required by this component</dd>
+ * <dt>optional:</dt> <dd>array of optional modules for this component</dd>
+ * <dt>supersedes:</dt> <dd>array of the modules this component replaces</dd>
+ * <dt>after:</dt> <dd>array of modules the components which, if present, should be sorted above this one</dd>
+ * <dt>rollup:</dt> <dd>the number of superseded modules required for automatic rollup</dd>
+ * <dt>fullpath:</dt> <dd>If fullpath is specified, this is used instead of the configured base + path</dd>
+ * <dt>skinnable:</dt> <dd>flag to determine if skin assets should automatically be pulled in</dd>
+ * <dt>submodules:</dt> <dd>a has of submodules</dd>
+ * </dl>
+ * @method addModule
+ * @param o An object containing the module data
+ * @param name the module name (optional), required if not in the module data
+ * @return {boolean} true if the module was added, false if
+ * the object passed in did not provide all required attributes
+ */
+ addModule: function(o, name) {
+
+ name = name || o.name;
+ o.name = name;
+
+ if (!o || !o.name) {
+ return false;
+ }
+
+ if (!o.type) {
+ o.type = JS;
+ }
+
+ if (!o.path && !o.fullpath) {
+ // o.path = name + "/" + name + "-min." + o.type;
+ o.path = _path(name, name, o.type);
+ }
+
+ o.ext = ('ext' in o) ? o.ext : (this._internal) ? false : true;
+ o.requires = o.requires || [];
+
+ // Y.log('New module ' + name);
+
+ this.moduleInfo[name] = o;
+
+ // Handle submodule logic
+ var subs = o.submodules, i, l, sup, s, smod, plugins, plug;
+ if (subs) {
+ sup = [];
+ l = 0;
+
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ s.path = _path(name, i, o.type);
+ this.addModule(s, i);
+ sup.push(i);
+
+ if (o.skinnable) {
+ smod = this._addSkin(this.skin.defaultSkin, i, name);
+ sup.push(smod.name);
+ }
+
+ l++;
+ }
+ }
+
+ o.supersedes = sup;
+ o.rollup = (l<4) ? l : Math.min(l-1, 4);
+ }
+
+ plugins = o.plugins;
+ if (plugins) {
+ for (i in plugins) {
+ if (plugins.hasOwnProperty(i)) {
+ plug = plugins[i];
+ plug.path = _path(name, i, o.type);
+ plug.requires = plug.requires || [];
+ // plug.requires.push(name);
+ this.addModule(plug, i);
+ if (o.skinnable) {
+ this._addSkin(this.skin.defaultSkin, i, name);
+ }
+ }
+ }
+ }
+
+ this.dirty = true;
+
+ return o;
+ },
+
+ /**
+ * Add a requirement for one or more module
+ * @method require
+ * @param what {string[] | string*} the modules to load
+ */
+ require: function(what) {
+ var a = (typeof what === "string") ? arguments : what;
+ this.dirty = true;
+ Y.mix(this.required, Y.Array.hash(a));
+ },
+
+ /**
+ * Returns an object containing properties for all modules required
+ * in order to load the requested module
+ * @method getRequires
+ * @param mod The module definition from moduleInfo
+ */
+ getRequires: function(mod) {
+
+ if (!mod) {
+ // Y.log('getRequires, no module');
+ return [];
+ }
+
+ if (!this.dirty && mod.expanded) {
+ // Y.log('already expanded');
+ return mod.expanded;
+ }
+
+ var i, d=[], r=mod.requires, o=mod.optional,
+ info=this.moduleInfo, m, j, add;
+
+ for (i=0; i<r.length; i=i+1) {
+ // Y.log(mod.name + ' requiring ' + r[i]);
+ d.push(r[i]);
+ m = this.getModule(r[i]);
+ add = this.getRequires(m);
+ for (j=0;j<add.length;j=j+1) {
+ d.push(add[j]);
+ }
+ }
+
+ // get the requirements from superseded modules, if any
+ r=mod.supersedes;
+ if (r) {
+ for (i=0; i<r.length; i=i+1) {
+ // Y.log(mod.name + ' requiring ' + r[i]);
+ d.push(r[i]);
+ m = this.getModule(r[i]);
+ add = this.getRequires(m);
+ for (j=0;j<add.length;j=j+1) {
+ d.push(add[j]);
+ }
+ }
+ }
+
+ if (o && this.loadOptional) {
+ for (i=0; i<o.length; i=i+1) {
+ d.push(o[i]);
+ add = this.getRequires(info[o[i]]);
+ for (j=0;j<add.length;j=j+1) {
+ d.push(add[j]);
+ }
+ }
+ }
+
+ mod.expanded = Y.Object.keys(Y.Array.hash(d));
+ return mod.expanded;
+ },
+
+
+ /**
+ * Returns a hash of module names the supplied module satisfies.
+ * @method getProvides
+ * @param name {string} The name of the module
+ * @return what this module provides
+ */
+ getProvides: function(name) {
+ var m = this.getModule(name), o, s;
+
+ if (!m) {
+ return NOT_FOUND;
+ }
+
+ if (m && !m.provides) {
+ o = {};
+ s = m.supersedes;
+
+ if (s) {
+ Y.Array.each(s, function(v) {
+ Y.mix(o, this.getProvides(v));
+ }, this);
+ }
+
+ o[name] = true;
+ m.provides = o;
+ }
+
+ return m.provides;
+ },
+
+
+ /**
+ * Calculates the dependency tree, the result is stored in the sorted
+ * property
+ * @method calculate
+ * @param o optional options object
+ * @param type optional argument to prune modules
+ */
+ calculate: function(o, type) {
+ if (o || type || this.dirty) {
+ this._config(o);
+ this._setup();
+ this._explode();
+ if (this.allowRollup && !this.combine) {
+ this._rollup();
+ }
+ this._reduce();
+ this._sort();
+
+ // Y.log("after calculate: " + this.sorted);
+
+ this.dirty = false;
+ }
+ },
+
+ /**
+ * Investigates the current YUI configuration on the page. By default,
+ * modules already detected will not be loaded again unless a force
+ * option is encountered. Called by calculate()
+ * @method _setup
+ * @private
+ */
+ _setup: function() {
+
+ var info = this.moduleInfo, name, i, j, m, o, l, smod;
+
+ // Create skin modules
+ for (name in info) {
+ if (info.hasOwnProperty(name)) {
+ m = info[name];
+ if (m && m.skinnable) {
+ // Y.log("skinning: " + name);
+ o = this.skin.overrides;
+ if (o && o[name]) {
+ for (i=0; i<o[name].length; i=i+1) {
+ smod = this._addSkin(o[name][i], name);
+ }
+ } else {
+ smod = this._addSkin(this.skin.defaultSkin, name);
+ }
+
+ m.requires.push(smod);
+ }
+ }
+ }
+
+ l = Y.merge(this.inserted); // shallow clone
+
+ // available modules
+ if (!this.ignoreRegistered) {
+ Y.mix(l, GLOBAL_ENV.mods);
+ }
+
+ // Y.log("Already loaded stuff: " + L.dump(l, 0));
+
+ // add the ignore list to the list of loaded packages
+ if (this.ignore) {
+ // OU.appendArray(l, this.ignore);
+ Y.mix(l, Y.Array.hash(this.ignore));
+ }
+
+ // expand the list to include superseded modules
+ for (j in l) {
+ // Y.log("expanding: " + j);
+ if (l.hasOwnProperty(j)) {
+ Y.mix(l, this.getProvides(j));
+ }
+ }
+
+ // remove modules on the force list from the loaded list
+ if (this.force) {
+ for (i=0; i<this.force.length; i=i+1) {
+ if (this.force[i] in l) {
+ delete l[this.force[i]];
+ }
+ }
+ }
+
+ // Y.log("loaded expanded: " + L.dump(l, 0));
+
+ Y.mix(this.loaded, l);
+
+ // this.loaded = l;
+
+ },
+
+
+ /**
+ * Inspects the required modules list looking for additional
+ * dependencies. Expands the required list to include all
+ * required modules. Called by calculate()
+ * @method _explode
+ * @private
+ */
+ _explode: function() {
+
+ var r = this.required, m, reqs;
+
+ Y.Object.each(r, function(v, name) {
+
+ m = this.getModule(name);
+
+ var expound = m && m.expound;
+ // Y.log('exploding ' + i);
+
+ if (m) {
+
+ if (expound) {
+ r[expound] = this.getModule(expound);
+ reqs = this.getRequires(r[expound]);
+ Y.mix(r, Y.Array.hash(reqs));
+ }
+
+ reqs = this.getRequires(m);
+
+ // Y.log('via explode: ' + reqs);
+ Y.mix(r, Y.Array.hash(reqs));
+ }
+
+ }, this);
+ },
+
+ getModule: function(name) {
+
+ var m = this.moduleInfo[name];
+
+ // create the default module
+ // if (!m) {
+ // Y.log('Module does not exist: ' + name + ', creating with defaults');
+ // m = this.addModule({ext: false}, name);
+ // }
+
+ return m;
+ },
+
+ /**
+ * Look for rollup packages to determine if all of the modules a
+ * rollup supersedes are required. If so, include the rollup to
+ * help reduce the total number of connections required. Called
+ * by calculate()
+ * @method _rollup
+ * @private
+ */
+ _rollup: function() {
+ var i, j, m, s, rollups={}, r=this.required, roll,
+ info = this.moduleInfo, rolled, c;
+
+ // find and cache rollup modules
+ if (this.dirty || !this.rollups) {
+ for (i in info) {
+ if (info.hasOwnProperty(i)) {
+ m = this.getModule(i);
+ // if (m && m.rollup && m.supersedes) {
+ if (m && m.rollup) {
+ rollups[i] = m;
+ }
+ }
+ }
+
+ this.rollups = rollups;
+ this.forceMap = (this.force) ? Y.Array.hash(this.force) : {};
+ }
+
+ // make as many passes as needed to pick up rollup rollups
+ for (;;) {
+ rolled = false;
+
+ // go through the rollup candidates
+ for (i in rollups) {
+
+ if (rollups.hasOwnProperty(i)) {
+
+ // there can be only one, unless forced
+ if (!r[i] && ((!this.loaded[i]) || this.forceMap[i])) {
+ m = this.getModule(i);
+ s = m.supersedes || [];
+ roll = false;
+
+ // @TODO remove continue
+ if (!m.rollup) {
+ continue;
+ }
+
+ c = 0;
+
+ // check the threshold
+ for (j=0;j<s.length;j=j+1) {
+
+
+ // if the superseded module is loaded, we can't load the rollup
+ // unless it has been forced
+ if (this.loaded[s[j]] && !this.forceMap[s[j]]) {
+ roll = false;
+ break;
+ // increment the counter if this module is required. if we are
+ // beyond the rollup threshold, we will use the rollup module
+ } else if (r[s[j]]) {
+ c++;
+ // Y.log("adding to thresh: " + c + ", " + s[j]);
+ roll = (c >= m.rollup);
+ if (roll) {
+ // Y.log("over thresh " + c + ", " + s[j]);
+ break;
+ }
+ }
+ }
+
+ if (roll) {
+ // Y.log("adding rollup: " + i);
+ // add the rollup
+ r[i] = true;
+ rolled = true;
+
+ // expand the rollup's dependencies
+ this.getRequires(m);
+ }
+ }
+ }
+ }
+
+ // if we made it here w/o rolling up something, we are done
+ if (!rolled) {
+ break;
+ }
+ }
+ },
+
+ /**
+ * Remove superceded modules and loaded modules. Called by
+ * calculate() after we have the mega list of all dependencies
+ * @method _reduce
+ * @private
+ */
+ _reduce: function() {
+ var i, j, s, m, r=this.required, type = this.loadType;
+ for (i in r) {
+ if (r.hasOwnProperty(i)) {
+ m = this.getModule(i);
+ // remove if already loaded
+ if ((this.loaded[i] && (!this.forceMap[i]) && !this.ignoreRegistered) || (type && m && m.type != type)) {
+ delete r[i];
+ // remove anything this module supersedes
+ } else {
+
+ s = m && m.supersedes;
+ if (s) {
+ for (j=0; j<s.length; j=j+1) {
+ if (s[j] in r) {
+ delete r[s[j]];
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ _attach: function() {
+ // this is the full list of items the YUI needs attached,
+ // which is needed if some dependencies are already on
+ // the page without their dependencies.
+ if (this.attaching) {
+ Y.log('attaching Y supplied deps: ' + this.attaching, "info", "loader");
+ Y._attach(this.attaching);
+ } else {
+ Y.log('attaching sorted list: ' + this.sorted, "info", "loader");
+ Y._attach(this.sorted);
+ }
+
+ // this._pushEvents();
+
+ },
+
+ _finish: function() {
+ _queue.running = false;
+ this._continue();
+ },
+
+ _onSuccess: function() {
+
+ Y.log('loader successful: ' + Y.id, "info", "loader");
+
+ this._attach();
+
+ var skipped = this.skipped, i, f;
+
+ for (i in skipped) {
+ if (skipped.hasOwnProperty(i)) {
+ delete this.inserted[i];
+ }
+ }
+
+ this.skipped = {};
+
+ f = this.onSuccess;
+
+ if (f) {
+ f.call(this.context, {
+ msg: 'success',
+ data: this.data,
+ success: true
+ });
+ }
+
+ this._finish();
+
+ },
+
+ _onFailure: function(o) {
+
+ Y.log('load error: ' + o.msg + ', ' + Y.id, "error", "loader");
+
+ this._attach();
+
+ var f = this.onFailure;
+ if (f) {
+ f.call(this.context, {
+ msg: 'failure: ' + o.msg,
+ data: this.data,
+ success: false
+ });
+ }
+
+ this._finish();
+ },
+
+ _onTimeout: function() {
+
+ Y.log('loader timeout: ' + Y.id, "error", "loader");
+
+ this._attach();
+
+ var f = this.onTimeout;
+ if (f) {
+ f.call(this.context, {
+ msg: 'timeout',
+ data: this.data,
+ success: false
+ });
+ }
+
+ this._finish();
+ },
+
+ /**
+ * Sorts the dependency tree. The last step of calculate()
+ * @method _sort
+ * @private
+ */
+ _sort: function() {
+
+ // create an indexed list
+ var s = Y.Object.keys(this.required),
+ info = this.moduleInfo,
+ loaded = this.loaded,
+ done = {},
+ p=0, l, a, b, j, k, moved, doneKey,
+
+ // returns true if b is not loaded, and is required
+ // directly or by means of modules it supersedes.
+ requires = Y.cached(function(mod1, mod2) {
+
+ var m = info[mod1], i, r, after, other = info[mod2], s;
+
+ if (loaded[mod2] || !m || !other) {
+ return false;
+ }
+
+ r = m.expanded;
+ after = m.after;
+
+ // check if this module requires the other directly
+ if (r && Y.Array.indexOf(r, mod2) > -1) {
+ return true;
+ }
+
+ // check if this module should be sorted after the other
+ if (after && Y.Array.indexOf(after, mod2) > -1) {
+ return true;
+ }
+
+ // check if this module requires one the other supersedes
+ s = info[mod2] && info[mod2].supersedes;
+ if (s) {
+ for (i=0; i<s.length; i=i+1) {
+ if (requires(mod1, s[i])) {
+ return true;
+ }
+ }
+ }
+
+ // external css files should be sorted below yui css
+ if (m.ext && m.type == CSS && !other.ext && other.type == CSS) {
+ return true;
+ }
+
+ return false;
+ });
+
+ // keep going until we make a pass without moving anything
+ for (;;) {
+
+ l = s.length;
+ moved = false;
+
+ // start the loop after items that are already sorted
+ for (j=p; j<l; j=j+1) {
+
+ // check the next module on the list to see if its
+ // dependencies have been met
+ a = s[j];
+
+ // check everything below current item and move if we
+ // find a requirement for the current item
+ for (k=j+1; k<l; k=k+1) {
+ doneKey = a + s[k];
+ if (!done[doneKey] && requires(a, s[k])) {
+
+ // extract the dependency so we can move it up
+ b = s.splice(k, 1);
+
+ // insert the dependency above the item that
+ // requires it
+ s.splice(j, 0, b[0]);
+
+ // only swap two dependencies once to short circut
+ // circular dependencies
+ done[doneKey] = true;
+
+ // keep working
+ moved = true;
+
+ break;
+ }
+ }
+
+ // jump out of loop if we moved something
+ if (moved) {
+ break;
+ // this item is sorted, move our pointer and keep going
+ } else {
+ p = p + 1;
+ }
+ }
+
+ // when we make it here and moved is false, we are
+ // finished sorting
+ if (!moved) {
+ break;
+ }
+
+ }
+
+ this.sorted = s;
+ },
+
+ _insert: function(source, o, type) {
+
+ // Y.log('private _insert() ' + (type || '') + ', ' + Y.id, "info", "loader");
+
+ // restore the state at the time of the request
+ if (source) {
+ this._config(source);
+ }
+
+ // build the dependency list
+ this.calculate(o); // don't include type so we can process CSS and script in
+ // one pass when the type is not specified.
+ this.loadType = type;
+
+ if (!type) {
+
+ var self = this;
+
+ // Y.log("trying to load css first");
+ this._internalCallback = function() {
+ var f = self.onCSS;
+ if (f) {
+ f.call(self.context, Y);
+ }
+ self._internalCallback = null;
+ self._insert(null, null, JS);
+ };
+
+ // _queue.running = false;
+ this._insert(null, null, CSS);
+
+ return;
+ }
+
+
+ // set a flag to indicate the load has started
+ this._loading = true;
+
+ // flag to indicate we are done with the combo service
+ // and any additional files will need to be loaded
+ // individually
+ this._combineComplete = {};
+
+
+ // start the load
+ this.loadNext();
+
+ },
+
+ _continue: function() {
+ if (!(_queue.running) && _queue.size() > 0) {
+ _queue.running = true;
+ _queue.next()();
+ }
+ },
+
+ /**
+ * inserts the requested modules and their dependencies.
+ * <code>type</code> can be "js" or "css". Both script and
+ * css are inserted if type is not provided.
+ * @method insert
+ * @param o optional options object
+ * @param type {string} the type of dependency to insert
+ */
+ insert: function(o, type) {
+
+ Y.log('public insert() ' + (type || '') + ', ' + Y.id, "info", "loader");
+
+ var self = this, copy = Y.merge(this, true);
+
+ delete copy.require;
+ delete copy.dirty;
+
+ _queue.add(function() {
+ self._insert(copy, o, type);
+ });
+
+ this._continue();
+
+ },
+
+ /**
+ * Executed every time a module is loaded, and if we are in a load
+ * cycle, we attempt to load the next script. Public so that it
+ * is possible to call this if using a method other than
+ * Y.register to determine when scripts are fully loaded
+ * @method loadNext
+ * @param mname {string} optional the name of the module that has
+ * been loaded (which is usually why it is time to load the next
+ * one)
+ */
+ loadNext: function(mname) {
+
+ // It is possible that this function is executed due to something
+ // else one the page loading a YUI module. Only react when we
+ // are actively loading something
+ if (!this._loading) {
+ return;
+ }
+
+ var s, len, i, m, url, self=this, type=this.loadType, fn, msg, attr,
+ callback=function(o) {
+ Y.log('Combo complete: ' + o.data, "info", "loader");
+ this._combineComplete[type] = true;
+
+ var c=this._combining, len=c.length, i;
+
+ for (i=0; i<len; i=i+1) {
+ this.inserted[c[i]] = true;
+ }
+
+ this.loadNext(o.data);
+ },
+ onsuccess=function(o) {
+ // Y.log('loading next, just loaded' + o.data);
+ self.loadNext(o.data);
+ };
+
+ // @TODO this will need to handle the two phase insert when
+ // CSS support is added
+ if (this.combine && (!this._combineComplete[type])) {
+
+ this._combining = [];
+ s=this.sorted;
+ len=s.length;
+ url=this.comboBase;
+
+
+ for (i=0; i<len; i=i+1) {
+ m = this.getModule(s[i]);
+ // Do not try to combine non-yui JS
+ if (m && (m.type === type) && !m.ext) {
+ url += this.root + m.path;
+ if (i < len-1) {
+ url += '&';
+ }
+
+ this._combining.push(s[i]);
+ }
+ }
+
+ if (this._combining.length) {
+
+Y.log('Attempting to use combo: ' + this._combining, "info", "loader");
+
+ // if (m.type === CSS) {
+ if (type === CSS) {
+ fn = Y.Get.css;
+ attr = this.cssAttributes;
+ } else {
+ fn = Y.Get.script;
+ attr = this.jsAttributes;
+ }
+
+ // @TODO get rid of the redundant Get code
+ fn(this._filter(url), {
+ data: this._loading,
+ onSuccess: callback,
+ onFailure: this._onFailure,
+ onTimeout: this._onTimeout,
+ insertBefore: this.insertBefore,
+ charset: this.charset,
+ attributes: attr,
+ timeout: this.timeout,
+ autopurge: false,
+ context: self
+ });
+
+ return;
+
+ } else {
+ this._combineComplete[type] = true;
+ }
+ }
+
+ if (mname) {
+
+ // if the module that was just loaded isn't what we were expecting,
+ // continue to wait
+ if (mname !== this._loading) {
+ return;
+ }
+
+Y.log("loadNext executing, just loaded " + mname + ", " + Y.id, "info", "loader");
+
+ // The global handler that is called when each module is loaded
+ // will pass that module name to this function. Storing this
+ // data to avoid loading the same module multiple times
+ this.inserted[mname] = true;
+ this.loaded[mname] = true;
+
+ if (this.onProgress) {
+ this.onProgress.call(this.context, {
+ name: mname,
+ data: this.data
+ });
+ }
+
+
+ }
+
+ s=this.sorted;
+ len=s.length;
+
+ for (i=0; i<len; i=i+1) {
+
+ // this.inserted keeps track of what the loader has loaded.
+ // move on if this item is done.
+ if (s[i] in this.inserted) {
+ // Y.log(s[i] + " alread loaded ");
+ continue;
+ }
+
+ // Because rollups will cause multiple load notifications
+ // from Y, loadNext may be called multiple times for
+ // the same module when loading a rollup. We can safely
+ // skip the subsequent requests
+ if (s[i] === this._loading) {
+ Y.log("still loading " + s[i] + ", waiting", "info", "loader");
+ return;
+ }
+
+ // log("inserting " + s[i]);
+ m = this.getModule(s[i]);
+
+ if (!m) {
+
+ msg = "Undefined module " + s[i] + " skipped";
+ Y.log(msg, 'warn', 'loader');
+ this.inserted[s[i]] = true;
+ this.skipped[s[i]] = true;
+ continue;
+
+ }
+
+
+ // The load type is stored to offer the possibility to load
+ // the css separately from the script.
+ if (!type || type === m.type) {
+ this._loading = s[i];
+ Y.log("attempting to load " + s[i] + ", " + this.base, "info", "loader");
+
+ if (m.type === CSS) {
+ fn = Y.Get.css;
+ attr = this.cssAttributes;
+ } else {
+ fn = Y.Get.script;
+ attr = this.jsAttributes;
+ }
+
+ url = (m.fullpath) ? this._filter(m.fullpath, s[i]) : this._url(m.path, s[i]);
+
+ fn(url, {
+ data: s[i],
+ onSuccess: onsuccess,
+ insertBefore: this.insertBefore,
+ charset: this.charset,
+ attributes: attr,
+ onFailure: this._onFailure,
+ onTimeout: this._onTimeout,
+ timeout: this.timeout,
+ autopurge: false,
+ context: self
+ });
+
+ return;
+ }
+ }
+
+ // we are finished
+ this._loading = null;
+
+ fn = this._internalCallback;
+
+ // internal callback for loading css first
+ if (fn) {
+ // Y.log('loader internal');
+ this._internalCallback = null;
+ fn.call(this);
+
+ // } else if (this.onSuccess) {
+ } else {
+ // Y.log('loader complete');
+ // call Y.use passing this instance. Y will use the sorted
+ // dependency list.
+ this._onSuccess();
+ }
+
+ },
+
+ /**
+ * Apply filter defined for this instance to a url/path
+ * method _filter
+ * @param u {string} the string to filter
+ * @param name {string} the name of the module, if we are processing
+ * a single module as opposed to a combined url
+ * @return {string} the filtered string
+ * @private
+ */
+ _filter: function(u, name) {
+
+ var f = this.filter,
+ hasFilter = name && (name in this.filters),
+ modFilter = hasFilter && this.filters[name];
+
+ if (u) {
+
+ if (hasFilter) {
+ f = (L.isString(modFilter)) ? this.FILTER_DEFS[modFilter.toUpperCase()] || null : modFilter;
+ }
+
+ if (f) {
+ u = u.replace(new RegExp(f.searchExp, 'g'), f.replaceStr);
+ }
+ }
+
+ return u;
+
+ },
+
+ /**
+ * Generates the full url for a module
+ * method _url
+ * @param path {string} the path fragment
+ * @return {string} the full url
+ * @private
+ */
+ _url: function(path, name) {
+ return this._filter((this.base || "") + path, name);
+ }
+
+};
+
+})();
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("loader",function(A){(function(){YUI.Env._loaderQueue=YUI.Env._loaderQueue||new A.Queue();var w={},t=YUI.Env,AC,n="base",X="css",AB="js",K="cssreset",U="cssfonts",AD="cssgrids",C="cssbase",I=[K,U,AD,"cssreset-context","cssfonts-context","cssgrids-context"],a=["reset","fonts","grids",n],b=A.version,u=b+"/build/",e="-context",k="anim-base",y="attribute",S=y+"-base",B="base-base",x="dd-drag",h="dom",E="dataschema-base",q="datasource-local",l="dom-base",N="dom-style",M="dom-screen",G="dump",Z="get",Y="event-base",o="event-custom",W="event-custom-base",r="io-base",AA="node",V="node-base",J="node-style",O="node-screen",T="oop",j="pluginhost",F="selector-css2",m="substitute",R="widget",H="widget-position",s="yui-base",g="plugin",f={version:b,root:u,base:"http://yui.yahooapis.com/"+u,comboBase:"http://yui.yahooapis.com/combo?",skin:{defaultSkin:"sam",base:"assets/skins/",path:"skin.css",after:I},modules:{dom:{requires:[T],submodules:{"dom-base":{requires:[T]},"dom-style":{requires:[l]},"dom-screen":{requires:[l,N]},"selector-native":{requires:[l]},"selector-css2":{requires:["selector-native"]},"selector":{requires:[l]}},plugins:{"selector-css3":{requires:[F]}}},node:{requires:[h,Y],submodules:{"node-base":{requires:[l,F,Y]},"node-style":{requires:[N,V]},"node-screen":{requires:[M,V]},"node-pluginhost":{requires:[V,j]},"node-event-delegate":{requires:[V,"event-delegate"]}},plugins:{"node-event-simulate":{requires:[V,"event-simulate"]}}},anim:{submodules:{"anim-base":{requires:[B,J]},"anim-color":{requires:[k]},"anim-easing":{requires:[k]},"anim-scroll":{requires:[k]},"anim-xy":{requires:[k,O]},"anim-curve":{requires:["anim-xy"]},"anim-node-plugin":{requires:["node-pluginhost",k]}}},attribute:{submodules:{"attribute-base":{requires:[o]},"attribute-complex":{requires:[S]}}},base:{submodules:{"base-base":{requires:[S]},"base-build":{requires:[B]},"base-pluginhost":{requires:[B,j]}}},cache:{requires:[g]},compat:{requires:[AA,G,m]},classnamemanager:{requires:[s]},collection:{requires:[T]},console:{requires:["yui-log",R,m],skinnable:true,plugins:{"console-filters":{requires:[g,"console"],skinnable:true}}},cookie:{requires:[s]},dataschema:{submodules:{"dataschema-base":{requires:[n]},"dataschema-array":{requires:[E]},"dataschema-json":{requires:[E,"json"]},"dataschema-text":{requires:[E]},"dataschema-xml":{requires:[E]}}},datasource:{submodules:{"datasource-local":{requires:[n]},"datasource-arrayschema":{requires:[q,g,"dataschema-array"]},"datasource-cache":{requires:[q,"cache"]},"datasource-function":{requires:[q]},"datasource-jsonschema":{requires:[q,g,"dataschema-json"]},"datasource-polling":{requires:[q]},"datasource-get":{requires:[q,Z]},"datasource-textschema":{requires:[q,g,"dataschema-text"]},"datasource-io":{requires:[q,r]},"datasource-xmlschema":{requires:[q,g,"dataschema-xml"]}}},datatype:{submodules:{"datatype-date":{requires:[s]},"datatype-number":{requires:[s]},"datatype-xml":{requires:[s]}}},dd:{submodules:{"dd-ddm-base":{requires:[AA,n]},"dd-ddm":{requires:["dd-ddm-base","event-resize"]},"dd-ddm-drop":{requires:["dd-ddm"]},"dd-drag":{requires:["dd-ddm-base"]},"dd-drop":{requires:["dd-ddm-drop"]},"dd-proxy":{requires:[x]},"dd-constrain":{requires:[x]},"dd-scroll":{requires:[x]},"dd-plugin":{requires:[x],optional:["dd-constrain","dd-proxy"]},"dd-drop-plugin":{requires:["dd-drop"]}}},dump:{requires:[s]},event:{expound:V,submodules:{"event-base":{expound:V,requires:[W]},"event-delegate":{requires:[V]},"event-focus":{requires:[V]},"event-key":{requires:[V]},"event-mouseenter":{requires:[V]},"event-mousewheel":{requires:[V]},"event-resize":{requires:[V]}}},"event-custom":{submodules:{"event-custom-base":{requires:[T,"yui-later"]},"event-custom-complex":{requires:[W]}}},"event-simulate":{requires:[Y]},"node-focusmanager":{requires:[y,AA,g,"node-event-simulate","event-key","event-focus"]},history:{requires:[AA]},imageloader:{requires:[B,J,O]},io:{submodules:{"io-base":{requires:[W]},"io-xdr":{requires:[r,"datatype-xml"]},"io-form":{requires:[r,V,J]},"io-upload-iframe":{requires:[r,V]},"io-queue":{requires:[r,"queue-promote"]}}},json:{submodules:{"json-parse":{requires:[s]},"json-stringify":{requires:[s]}}},loader:{requires:[Z]},"node-menunav":{requires:[AA,"classnamemanager",g,"node-focusmanager"],skinnable:true},oop:{requires:[s]},overlay:{requires:[R,H,"widget-position-ext","widget-stack","widget-stdmod"],skinnable:true},plugin:{requires:[B]},pluginhost:{requires:[s]},profiler:{requires:[s]},"queue-promote":{requires:[s]},"queue-run":{requires:[o],path:"async-queue/async-queue-min.js"},"async-queue":{requires:[o],supersedes:["queue-run"]},slider:{requires:[R,"dd-constrain"],skinnable:true},stylesheet:{requires:[s]},substitute:{optional:[G]},widget:{requires:[y,"event-focus",n,AA,"classnamemanager"],plugins:{"widget-position":{},"widget-position-ext":{requires:[H]},"widget-stack":{skinnable:true},"widget-stdmod":{}},skinnable:true},yui:{submodules:{"yui-base":{},get:{},"yui-log":{},"yui-later":{}}},test:{requires:[m,AA,"json","event-simulate"]}}},p=A.cached(function(L,i,AE){return L+"/"+i+"-min."+(AE||X);}),Q=YUI.Env._loaderQueue,D=f.modules,v,d,c,z,P=A.Lang;for(v=0;v<a.length;v=v+1){d=a[v];c=X+d;D[c]={type:X,path:p(c,d)};z=c+e;d=d+e;D[z]={type:X,path:p(c,d)};if(c==AD){D[c].requires=[U];D[c].optional=[K];D[z].requires=[U+e];D[z].optional=[K+e];}else{if(c==C){D[c].after=I;D[z].after=I;}}}A.Env.meta=f;AC=t._loaded;A.Loader=function(AG){this.context=A;this.base=A.Env.meta.base;this.comboBase=A.Env.meta.comboBase;this.combine=AG.base&&(AG.base.indexOf(this.comboBase.substr(0,20))>-1);this.root=A.Env.meta.root;this.timeout=0;this.forceMap={};this.filters={};this.required={};this.moduleInfo={};this.skin=A.merge(A.Env.meta.skin);var AF=A.Env.meta.modules,L,AE=YUI.Env.mods;this._internal=true;for(L in AF){if(AF.hasOwnProperty(L)){this.addModule(AF[L],L);}}for(L in AE){if(AE.hasOwnProperty(L)&&!this.moduleInfo[L]&&AE[L].details){this.addModule(AE[L].details,L);}}this._internal=false;this.sorted=[];
+this.loaded=AC[b];this.dirty=true;this.inserted={};this.skipped={};this._config(AG);};A.Loader.prototype={FILTER_DEFS:{RAW:{"searchExp":"-min\\.js","replaceStr":".js"},DEBUG:{"searchExp":"-min\\.js","replaceStr":"-debug.js"}},SKIN_PREFIX:"skin-",_config:function(AH){var AE,L,AG,AF;if(AH){for(AE in AH){if(AH.hasOwnProperty(AE)){AG=AH[AE];if(AE=="require"){this.require(AG);}else{if(AE=="modules"){for(L in AG){if(AG.hasOwnProperty(L)){this.addModule(AG[L],L);}}}else{this[AE]=AG;}}}}}AF=this.filter;if(P.isString(AF)){AF=AF.toUpperCase();this.filterName=AF;this.filter=this.FILTER_DEFS[AF];if(AF=="DEBUG"){this.require("yui-log","dump");}}},formatSkin:function(AE,L){var i=this.SKIN_PREFIX+AE;if(L){i=i+"-"+L;}return i;},_addSkin:function(AK,AI,AJ){var L=this.formatSkin(AK),AF=this.moduleInfo,i=this.skin,AE=AF[AI]&&AF[AI].ext,AH,AG;if(AI){L=this.formatSkin(AK,AI);if(!AF[L]){AH=AF[AI];AG=AH.pkg||AI;this.addModule({"name":L,"type":"css","after":i.after,"path":(AJ||AG)+"/"+i.base+AK+"/"+AI+".css","ext":AE});}}return L;},addModule:function(AF,AE){AE=AE||AF.name;AF.name=AE;if(!AF||!AF.name){return false;}if(!AF.type){AF.type=AB;}if(!AF.path&&!AF.fullpath){AF.path=p(AE,AE,AF.type);}AF.ext=("ext" in AF)?AF.ext:(this._internal)?false:true;AF.requires=AF.requires||[];this.moduleInfo[AE]=AF;var AI=AF.submodules,AJ,AG,AK,AM,AL,AH,L;if(AI){AK=[];AG=0;for(AJ in AI){if(AI.hasOwnProperty(AJ)){AM=AI[AJ];AM.path=p(AE,AJ,AF.type);this.addModule(AM,AJ);AK.push(AJ);if(AF.skinnable){AL=this._addSkin(this.skin.defaultSkin,AJ,AE);AK.push(AL.name);}AG++;}}AF.supersedes=AK;AF.rollup=(AG<4)?AG:Math.min(AG-1,4);}AH=AF.plugins;if(AH){for(AJ in AH){if(AH.hasOwnProperty(AJ)){L=AH[AJ];L.path=p(AE,AJ,AF.type);L.requires=L.requires||[];this.addModule(L,AJ);if(AF.skinnable){this._addSkin(this.skin.defaultSkin,AJ,AE);}}}}this.dirty=true;return AF;},require:function(i){var L=(typeof i==="string")?arguments:i;this.dirty=true;A.mix(this.required,A.Array.hash(L));},getRequires:function(AK){if(!AK){return[];}if(!this.dirty&&AK.expanded){return AK.expanded;}var AI,AJ=[],L=AK.requires,AE=AK.optional,AF=this.moduleInfo,AG,AH,AL;for(AI=0;AI<L.length;AI=AI+1){AJ.push(L[AI]);AG=this.getModule(L[AI]);AL=this.getRequires(AG);for(AH=0;AH<AL.length;AH=AH+1){AJ.push(AL[AH]);}}L=AK.supersedes;if(L){for(AI=0;AI<L.length;AI=AI+1){AJ.push(L[AI]);AG=this.getModule(L[AI]);AL=this.getRequires(AG);for(AH=0;AH<AL.length;AH=AH+1){AJ.push(AL[AH]);}}}if(AE&&this.loadOptional){for(AI=0;AI<AE.length;AI=AI+1){AJ.push(AE[AI]);AL=this.getRequires(AF[AE[AI]]);for(AH=0;AH<AL.length;AH=AH+1){AJ.push(AL[AH]);}}}AK.expanded=A.Object.keys(A.Array.hash(AJ));return AK.expanded;},getProvides:function(i){var L=this.getModule(i),AF,AE;if(!L){return w;}if(L&&!L.provides){AF={};AE=L.supersedes;if(AE){A.Array.each(AE,function(AG){A.mix(AF,this.getProvides(AG));},this);}AF[i]=true;L.provides=AF;}return L.provides;},calculate:function(i,L){if(i||L||this.dirty){this._config(i);this._setup();this._explode();if(this.allowRollup&&!this.combine){this._rollup();}this._reduce();this._sort();this.dirty=false;}},_setup:function(){var AJ=this.moduleInfo,AH,AI,AG,AE,AK,AF,L;for(AH in AJ){if(AJ.hasOwnProperty(AH)){AE=AJ[AH];if(AE&&AE.skinnable){AK=this.skin.overrides;if(AK&&AK[AH]){for(AI=0;AI<AK[AH].length;AI=AI+1){L=this._addSkin(AK[AH][AI],AH);}}else{L=this._addSkin(this.skin.defaultSkin,AH);}AE.requires.push(L);}}}AF=A.merge(this.inserted);if(!this.ignoreRegistered){A.mix(AF,t.mods);}if(this.ignore){A.mix(AF,A.Array.hash(this.ignore));}for(AG in AF){if(AF.hasOwnProperty(AG)){A.mix(AF,this.getProvides(AG));}}if(this.force){for(AI=0;AI<this.force.length;AI=AI+1){if(this.force[AI] in AF){delete AF[this.force[AI]];}}}A.mix(this.loaded,AF);},_explode:function(){var AE=this.required,L,i;A.Object.each(AE,function(AF,AG){L=this.getModule(AG);var AH=L&&L.expound;if(L){if(AH){AE[AH]=this.getModule(AH);i=this.getRequires(AE[AH]);A.mix(AE,A.Array.hash(i));}i=this.getRequires(L);A.mix(AE,A.Array.hash(i));}},this);},getModule:function(i){var L=this.moduleInfo[i];return L;},_rollup:function(){var AJ,AI,AH,AM,AL={},L=this.required,AF,AG=this.moduleInfo,AE,AK;if(this.dirty||!this.rollups){for(AJ in AG){if(AG.hasOwnProperty(AJ)){AH=this.getModule(AJ);if(AH&&AH.rollup){AL[AJ]=AH;}}}this.rollups=AL;this.forceMap=(this.force)?A.Array.hash(this.force):{};}for(;;){AE=false;for(AJ in AL){if(AL.hasOwnProperty(AJ)){if(!L[AJ]&&((!this.loaded[AJ])||this.forceMap[AJ])){AH=this.getModule(AJ);AM=AH.supersedes||[];AF=false;if(!AH.rollup){continue;}AK=0;for(AI=0;AI<AM.length;AI=AI+1){if(this.loaded[AM[AI]]&&!this.forceMap[AM[AI]]){AF=false;break;}else{if(L[AM[AI]]){AK++;AF=(AK>=AH.rollup);if(AF){break;}}}}if(AF){L[AJ]=true;AE=true;this.getRequires(AH);}}}}if(!AE){break;}}},_reduce:function(){var AF,AE,AH,L,AI=this.required,AG=this.loadType;for(AF in AI){if(AI.hasOwnProperty(AF)){L=this.getModule(AF);if((this.loaded[AF]&&(!this.forceMap[AF])&&!this.ignoreRegistered)||(AG&&L&&L.type!=AG)){delete AI[AF];}else{AH=L&&L.supersedes;if(AH){for(AE=0;AE<AH.length;AE=AE+1){if(AH[AE] in AI){delete AI[AH[AE]];}}}}}}},_attach:function(){if(this.attaching){A._attach(this.attaching);}else{A._attach(this.sorted);}},_finish:function(){Q.running=false;this._continue();},_onSuccess:function(){this._attach();var L=this.skipped,AE,AF;for(AE in L){if(L.hasOwnProperty(AE)){delete this.inserted[AE];}}this.skipped={};AF=this.onSuccess;if(AF){AF.call(this.context,{msg:"success",data:this.data,success:true});}this._finish();},_onFailure:function(i){this._attach();var L=this.onFailure;if(L){L.call(this.context,{msg:"failure: "+i.msg,data:this.data,success:false});}this._finish();},_onTimeout:function(){this._attach();var L=this.onTimeout;if(L){L.call(this.context,{msg:"timeout",data:this.data,success:false});}this._finish();},_sort:function(){var AO=A.Object.keys(this.required),AE=this.moduleInfo,AJ=this.loaded,AI={},L=0,AF,AM,AL,AH,AG,AK,i,AN=A.cached(function(AV,AT){var AQ=AE[AV],AR,AU,AW,AP=AE[AT],AS;if(AJ[AT]||!AQ||!AP){return false;}AU=AQ.expanded;
+AW=AQ.after;if(AU&&A.Array.indexOf(AU,AT)>-1){return true;}if(AW&&A.Array.indexOf(AW,AT)>-1){return true;}AS=AE[AT]&&AE[AT].supersedes;if(AS){for(AR=0;AR<AS.length;AR=AR+1){if(AN(AV,AS[AR])){return true;}}}if(AQ.ext&&AQ.type==X&&!AP.ext&&AP.type==X){return true;}return false;});for(;;){AF=AO.length;AK=false;for(AH=L;AH<AF;AH=AH+1){AM=AO[AH];for(AG=AH+1;AG<AF;AG=AG+1){i=AM+AO[AG];if(!AI[i]&&AN(AM,AO[AG])){AL=AO.splice(AG,1);AO.splice(AH,0,AL[0]);AI[i]=true;AK=true;break;}}if(AK){break;}else{L=L+1;}}if(!AK){break;}}this.sorted=AO;},_insert:function(AE,AF,i){if(AE){this._config(AE);}this.calculate(AF);this.loadType=i;if(!i){var L=this;this._internalCallback=function(){var AG=L.onCSS;if(AG){AG.call(L.context,A);}L._internalCallback=null;L._insert(null,null,AB);};this._insert(null,null,X);return;}this._loading=true;this._combineComplete={};this.loadNext();},_continue:function(){if(!(Q.running)&&Q.size()>0){Q.running=true;Q.next()();}},insert:function(AE,i){var L=this,AF=A.merge(this,true);delete AF.require;delete AF.dirty;Q.add(function(){L._insert(AF,AE,i);});this._continue();},loadNext:function(AJ){if(!this._loading){return;}var AP,AH,AG,AF,L,AO=this,AK=this.loadType,AL,AE,AI,AM=function(AS){this._combineComplete[AK]=true;var AT=this._combining,AQ=AT.length,AR;for(AR=0;AR<AQ;AR=AR+1){this.inserted[AT[AR]]=true;}this.loadNext(AS.data);},AN=function(i){AO.loadNext(i.data);};if(this.combine&&(!this._combineComplete[AK])){this._combining=[];AP=this.sorted;AH=AP.length;L=this.comboBase;for(AG=0;AG<AH;AG=AG+1){AF=this.getModule(AP[AG]);if(AF&&(AF.type===AK)&&!AF.ext){L+=this.root+AF.path;if(AG<AH-1){L+="&";}this._combining.push(AP[AG]);}}if(this._combining.length){if(AK===X){AL=A.Get.css;AI=this.cssAttributes;}else{AL=A.Get.script;AI=this.jsAttributes;}AL(this._filter(L),{data:this._loading,onSuccess:AM,onFailure:this._onFailure,onTimeout:this._onTimeout,insertBefore:this.insertBefore,charset:this.charset,attributes:AI,timeout:this.timeout,autopurge:false,context:AO});return;}else{this._combineComplete[AK]=true;}}if(AJ){if(AJ!==this._loading){return;}this.inserted[AJ]=true;this.loaded[AJ]=true;if(this.onProgress){this.onProgress.call(this.context,{name:AJ,data:this.data});}}AP=this.sorted;AH=AP.length;for(AG=0;AG<AH;AG=AG+1){if(AP[AG] in this.inserted){continue;}if(AP[AG]===this._loading){return;}AF=this.getModule(AP[AG]);if(!AF){AE="Undefined module "+AP[AG]+" skipped";this.inserted[AP[AG]]=true;this.skipped[AP[AG]]=true;continue;}if(!AK||AK===AF.type){this._loading=AP[AG];if(AF.type===X){AL=A.Get.css;AI=this.cssAttributes;}else{AL=A.Get.script;AI=this.jsAttributes;}L=(AF.fullpath)?this._filter(AF.fullpath,AP[AG]):this._url(AF.path,AP[AG]);AL(L,{data:AP[AG],onSuccess:AN,insertBefore:this.insertBefore,charset:this.charset,attributes:AI,onFailure:this._onFailure,onTimeout:this._onTimeout,timeout:this.timeout,autopurge:false,context:AO});return;}}this._loading=null;AL=this._internalCallback;if(AL){this._internalCallback=null;AL.call(this);}else{this._onSuccess();}},_filter:function(AE,i){var AG=this.filter,L=i&&(i in this.filters),AF=L&&this.filters[i];if(AE){if(L){AG=(P.isString(AF))?this.FILTER_DEFS[AF.toUpperCase()]||null:AF;}if(AG){AE=AE.replace(new RegExp(AG.searchExp,"g"),AG.replaceStr);}}return AE;},_url:function(i,L){return this._filter((this.base||"")+i,L);}};})();},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('loader', function(Y) {
+
+(function() {
+/**
+ * Loader dynamically loads script and css files. It includes the dependency
+ * info for the version of the library in use, and will automatically pull in
+ * dependencies for the modules requested. It supports rollup files and will
+ * automatically use these when appropriate in order to minimize the number of
+ * http connections required to load all of the dependencies. It can load the
+ * files from the Yahoo! CDN, and it can utilize the combo service provided on
+ * this network to reduce the number of http connections required to download
+ * YUI files.
+ *
+ * @module loader
+ */
+
+/**
+ * Loader dynamically loads script and css files. It includes the dependency
+ * info for the version of the library in use, and will automatically pull in
+ * dependencies for the modules requested. It supports rollup files and will
+ * automatically use these when appropriate in order to minimize the number of
+ * http connections required to load all of the dependencies. It can load the
+ * files from the Yahoo! CDN, and it can utilize the combo service provided on
+ * this network to reduce the number of http connections required to download
+ * YUI files.
+ *
+ * While the loader can be instantiated by the end user, it normally is not.
+ * @see YUI.use for the normal use case. The use function automatically will
+ * pull in missing dependencies.
+ *
+ * @class Loader
+ * @constructor
+ * @param o an optional set of configuration options. Valid options:
+ * <ul>
+ * <li>base:
+ * The base dir</li>
+ * <li>secureBase:
+ * The secure base dir (not implemented)</li>
+ * <li>comboBase:
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?</li>
+ * <li>root:
+ * The root path to prepend to module names for the combo service. Ex: 2.5.2/build/</li>
+ * <li>filter:
+ *
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * </li>
+ * <li>filters: per-component filter specification. If specified for a given component, this overrides the filter config</li>
+ * <li>combine:
+ * Use the YUI combo service to reduce the number of http connections required to load your dependencies</li>
+ * <li>ignore:
+ * A list of modules that should never be dynamically loaded</li>
+ * <li>force:
+ * A list of modules that should always be loaded when required, even if already present on the page</li>
+ * <li>insertBefore:
+ * Node or id for a node that should be used as the insertion point for new nodes</li>
+ * <li>charset:
+ * charset for dynamic nodes (deprecated, use jsAttributes or cssAttributes)</li>
+ * <li>jsAttributes: object literal containing attributes to add to script nodes</li>
+ * <li>cssAttributes: object literal containing attributes to add to link nodes</li>
+ * <li>timeout:
+ * number of milliseconds before a timeout occurs when dynamically loading nodes. in not set, there is no timeout</li>
+ * <li>context:
+ * execution context for all callbacks</li>
+ * <li>onSuccess:
+ * callback for the 'success' event</li>
+ * <li>onFailure: callback for the 'failure' event</li>
+ * <li>onCSS: callback for the 'CSSComplete' event. When loading YUI components with CSS
+ * the CSS is loaded first, then the script. This provides a moment you can tie into to improve
+ * the presentation of the page while the script is loading.</li>
+ * <li>onTimeout:
+ * callback for the 'timeout' event</li>
+ * <li>onProgress:
+ * callback executed each time a script or css file is loaded</li>
+ * <li>modules:
+ * A list of module definitions. See Loader.addModule for the supported module metadata</li>
+ * </ul>
+ */
+
+/*
+ * Global loader queue
+ * @property _loaderQueue
+ * @type Queue
+ * @private
+ */
+YUI.Env._loaderQueue = YUI.Env._loaderQueue || new Y.Queue();
+
+var NOT_FOUND = {},
+ GLOBAL_ENV = YUI.Env,
+ GLOBAL_LOADED,
+ BASE = 'base',
+ CSS = 'css',
+ JS = 'js',
+ CSSRESET = 'cssreset',
+ CSSFONTS = 'cssfonts',
+ CSSGRIDS = 'cssgrids',
+ CSSBASE = 'cssbase',
+ CSS_AFTER = [CSSRESET, CSSFONTS, CSSGRIDS,
+ 'cssreset-context', 'cssfonts-context', 'cssgrids-context'],
+ YUI_CSS = ['reset', 'fonts', 'grids', BASE],
+ VERSION = Y.version,
+ ROOT = VERSION + '/build/',
+ CONTEXT = '-context',
+
+ ANIMBASE = 'anim-base',
+ ATTRIBUTE = 'attribute',
+ ATTRIBUTEBASE = ATTRIBUTE + '-base',
+ BASEBASE = 'base-base',
+ DDDRAG = 'dd-drag',
+ DOM = 'dom',
+ DATASCHEMABASE = 'dataschema-base',
+ DATASOURCELOCAL = 'datasource-local',
+ DOMBASE = 'dom-base',
+ DOMSTYLE = 'dom-style',
+ DOMSCREEN = 'dom-screen',
+ DUMP = 'dump',
+ GET = 'get',
+ EVENTBASE = 'event-base',
+ EVENTCUSTOM = 'event-custom',
+ EVENTCUSTOMBASE = 'event-custom-base',
+ IOBASE = 'io-base',
+ NODE = 'node',
+ NODEBASE = 'node-base',
+ NODESTYLE = 'node-style',
+ NODESCREEN = 'node-screen',
+ OOP = 'oop',
+ PLUGINHOST = 'pluginhost',
+ SELECTORCSS2 = 'selector-css2',
+ SUBSTITUTE = 'substitute',
+ WIDGET = 'widget',
+ WIDGETPOSITION = 'widget-position',
+ YUIBASE = 'yui-base',
+
+ PLUGIN = 'plugin',
+
+ META = {
+
+ version: VERSION,
+
+ root: ROOT,
+
+ base: 'http://yui.yahooapis.com/' + ROOT,
+
+ comboBase: 'http://yui.yahooapis.com/combo?',
+
+ skin: {
+ defaultSkin: 'sam',
+ base: 'assets/skins/',
+ path: 'skin.css',
+ after: CSS_AFTER
+ //rollup: 3
+ },
+
+ modules: {
+
+ dom: {
+ requires: [OOP],
+ submodules: {
+
+ 'dom-base': {
+ requires: [OOP]
+ },
+
+ 'dom-style': {
+ requires: [DOMBASE]
+ },
+
+ 'dom-screen': {
+ requires: [DOMBASE, DOMSTYLE]
+ },
+
+ 'selector-native': {
+ requires: [DOMBASE]
+ },
+
+ 'selector-css2': {
+ requires: ['selector-native']
+ },
+
+ 'selector': {
+ requires: [DOMBASE]
+ }
+
+ },
+
+ plugins: {
+ 'selector-css3': {
+ requires: [SELECTORCSS2]
+ }
+ }
+ },
+
+ node: {
+ requires: [DOM, EVENTBASE],
+ // expound: EVENT,
+
+ submodules: {
+ 'node-base': {
+ requires: [DOMBASE, SELECTORCSS2, EVENTBASE]
+ },
+
+ 'node-style': {
+ requires: [DOMSTYLE, NODEBASE]
+ },
+
+ 'node-screen': {
+ requires: [DOMSCREEN, NODEBASE]
+ },
+
+ 'node-pluginhost': {
+ requires: [NODEBASE, PLUGINHOST]
+ },
+
+
+ 'node-event-delegate': {
+ requires: [NODEBASE, 'event-delegate']
+ }
+ },
+
+ plugins: {
+ 'node-event-simulate': {
+ requires: [NODEBASE, 'event-simulate']
+ }
+ }
+ },
+
+ anim: {
+ submodules: {
+
+ 'anim-base': {
+ requires: [BASEBASE, NODESTYLE]
+ },
+
+ 'anim-color': {
+ requires: [ANIMBASE]
+ },
+
+ 'anim-easing': {
+ requires: [ANIMBASE]
+ },
+
+ 'anim-scroll': {
+ requires: [ANIMBASE]
+ },
+
+ 'anim-xy': {
+ requires: [ANIMBASE, NODESCREEN]
+ },
+
+ 'anim-curve': {
+ requires: ['anim-xy']
+ },
+
+ 'anim-node-plugin': {
+ requires: ['node-pluginhost', ANIMBASE]
+ }
+ }
+ },
+
+ attribute: {
+ submodules: {
+ 'attribute-base': {
+ requires: [EVENTCUSTOM]
+ },
+
+ 'attribute-complex': {
+ requires: [ATTRIBUTEBASE]
+ }
+ }
+ },
+
+ base: {
+ submodules: {
+ 'base-base': {
+ requires: [ATTRIBUTEBASE]
+ },
+
+ 'base-build': {
+ requires: [BASEBASE]
+ },
+
+ 'base-pluginhost': {
+ requires: [BASEBASE, PLUGINHOST]
+ }
+ }
+ },
+
+ cache: {
+ requires: [PLUGIN]
+ },
+
+ compat: {
+ requires: [NODE, DUMP, SUBSTITUTE]
+ },
+
+ classnamemanager: {
+ requires: [YUIBASE]
+ },
+
+ collection: {
+ requires: [OOP]
+ },
+
+ console: {
+ requires: ['yui-log', WIDGET, SUBSTITUTE],
+ skinnable: true,
+ plugins: {
+ 'console-filters': {
+ requires: [PLUGIN, 'console'],
+ skinnable: true
+ }
+ }
+ },
+
+ cookie: {
+ requires: [YUIBASE]
+ },
+
+ dataschema:{
+ submodules: {
+ 'dataschema-base': {
+ requires: [BASE]
+ },
+ 'dataschema-array': {
+ requires: [DATASCHEMABASE]
+ },
+ 'dataschema-json': {
+ requires: [DATASCHEMABASE, 'json']
+ },
+ 'dataschema-text': {
+ requires: [DATASCHEMABASE]
+ },
+ 'dataschema-xml': {
+ requires: [DATASCHEMABASE]
+ }
+ }
+ },
+
+ datasource:{
+ submodules: {
+ 'datasource-local': {
+ requires: [BASE]
+ },
+ 'datasource-arrayschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-array']
+ },
+ 'datasource-cache': {
+ requires: [DATASOURCELOCAL, 'cache']
+ },
+ 'datasource-function': {
+ requires: [DATASOURCELOCAL]
+ },
+ 'datasource-jsonschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-json']
+ },
+ 'datasource-polling': {
+ requires: [DATASOURCELOCAL]
+ },
+ 'datasource-get': {
+ requires: [DATASOURCELOCAL, GET]
+ },
+ 'datasource-textschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-text']
+ },
+ 'datasource-io': {
+ requires: [DATASOURCELOCAL, IOBASE]
+ },
+ 'datasource-xmlschema': {
+ requires: [DATASOURCELOCAL, PLUGIN, 'dataschema-xml']
+ }
+ }
+ },
+
+ datatype:{
+ submodules: {
+ 'datatype-date': {
+ requires: [YUIBASE]
+ },
+ 'datatype-number': {
+ requires: [YUIBASE]
+ },
+ 'datatype-xml': {
+ requires: [YUIBASE]
+ }
+ }
+ },
+
+ dd:{
+ submodules: {
+ 'dd-ddm-base': {
+ requires: [NODE, BASE]
+ },
+ 'dd-ddm':{
+ requires: ['dd-ddm-base', 'event-resize']
+ },
+ 'dd-ddm-drop':{
+ requires: ['dd-ddm']
+ },
+ 'dd-drag':{
+ requires: ['dd-ddm-base']
+ },
+ 'dd-drop':{
+ requires: ['dd-ddm-drop']
+ },
+ 'dd-proxy':{
+ requires: [DDDRAG]
+ },
+ 'dd-constrain':{
+ requires: [DDDRAG]
+ },
+ 'dd-scroll':{
+ requires: [DDDRAG]
+ },
+ 'dd-plugin':{
+ requires: [DDDRAG],
+ optional: ['dd-constrain', 'dd-proxy']
+ },
+ 'dd-drop-plugin':{
+ requires: ['dd-drop']
+ }
+ }
+ },
+
+ dump: {
+ requires: [YUIBASE]
+ },
+
+ event: {
+ expound: NODEBASE,
+ submodules: {
+ 'event-base': {
+ expound: NODEBASE,
+ requires: [EVENTCUSTOMBASE]
+ },
+ 'event-delegate': {
+ requires: [NODEBASE]
+ },
+ 'event-focus': {
+ requires: [NODEBASE]
+ },
+ 'event-key': {
+ requires: [NODEBASE]
+ },
+ 'event-mouseenter': {
+ requires: [NODEBASE]
+ },
+ 'event-mousewheel': {
+ requires: [NODEBASE]
+ },
+ 'event-resize': {
+ requires: [NODEBASE]
+ }
+ }
+ },
+
+ 'event-custom': {
+ submodules: {
+ 'event-custom-base': {
+ requires: [OOP, 'yui-later']
+ },
+ 'event-custom-complex': {
+ requires: [EVENTCUSTOMBASE]
+ }
+ }
+ },
+
+ 'event-simulate': {
+ requires: [EVENTBASE]
+ },
+
+ 'node-focusmanager': {
+ requires: [ATTRIBUTE, NODE, PLUGIN, 'node-event-simulate', 'event-key', 'event-focus']
+ },
+
+ history: {
+ requires: [NODE]
+ },
+
+ imageloader: {
+ requires: [BASEBASE, NODESTYLE, NODESCREEN]
+ },
+
+ io:{
+ submodules: {
+
+ 'io-base': {
+ requires: [EVENTCUSTOMBASE]
+ },
+
+ 'io-xdr': {
+ requires: [IOBASE, 'datatype-xml']
+ },
+
+ 'io-form': {
+ requires: [IOBASE, NODEBASE, NODESTYLE]
+ },
+
+ 'io-upload-iframe': {
+ requires: [IOBASE, NODEBASE]
+ },
+
+ 'io-queue': {
+ requires: [IOBASE, 'queue-promote']
+ }
+ }
+ },
+
+ json: {
+ submodules: {
+ 'json-parse': {
+ requires: [YUIBASE]
+ },
+
+ 'json-stringify': {
+ requires: [YUIBASE]
+ }
+ }
+ },
+
+ loader: {
+ requires: [GET]
+ },
+
+ 'node-menunav': {
+ requires: [NODE, 'classnamemanager', PLUGIN, 'node-focusmanager'],
+ skinnable: true
+ },
+
+ oop: {
+ requires: [YUIBASE]
+ },
+
+ overlay: {
+ requires: [WIDGET, WIDGETPOSITION, 'widget-position-ext', 'widget-stack', 'widget-stdmod'],
+ skinnable: true
+ },
+
+ plugin: {
+ requires: [BASEBASE]
+ },
+
+ pluginhost: {
+ requires: [YUIBASE]
+ },
+
+ profiler: {
+ requires: [YUIBASE]
+ },
+
+ 'queue-promote': {
+ requires: [YUIBASE]
+ },
+
+ // deprecated package, replaced with async-queue
+ 'queue-run': {
+ requires: [EVENTCUSTOM],
+ path: 'async-queue/async-queue-min.js'
+ },
+
+ 'async-queue': {
+ requires: [EVENTCUSTOM],
+ supersedes: ['queue-run']
+ },
+
+ slider: {
+ requires: [WIDGET, 'dd-constrain'],
+ skinnable: true
+ },
+
+ stylesheet: {
+ requires: [YUIBASE]
+ },
+
+ substitute: {
+ optional: [DUMP]
+ },
+
+ widget: {
+ requires: [ATTRIBUTE, 'event-focus', BASE, NODE, 'classnamemanager'],
+ plugins: {
+ 'widget-position': { },
+ 'widget-position-ext': {
+ requires: [WIDGETPOSITION]
+ },
+ 'widget-stack': {
+ skinnable: true
+ },
+ 'widget-stdmod': { }
+ },
+ skinnable: true
+ },
+
+ yui: {
+ submodules: {
+ 'yui-base': {},
+ get: {},
+ 'yui-log': {},
+ 'yui-later': {}
+ }
+ },
+
+ test: {
+ requires: [SUBSTITUTE, NODE, 'json', 'event-simulate']
+ }
+
+ }
+},
+
+_path = Y.cached(function(dir, file, type) {
+ return dir + '/' + file + '-min.' + (type || CSS);
+}),
+
+_queue = YUI.Env._loaderQueue,
+
+mods = META.modules, i, bname, mname, contextname,
+L = Y.Lang;
+
+// Create the metadata for both the regular and context-aware
+// versions of the YUI CSS foundation.
+for (i=0; i<YUI_CSS.length; i=i+1) {
+ bname = YUI_CSS[i];
+ mname = CSS + bname;
+
+ mods[mname] = {
+ type: CSS,
+ path: _path(mname, bname)
+ };
+
+ // define -context module
+ contextname = mname + CONTEXT;
+ bname = bname + CONTEXT;
+
+ mods[contextname] = {
+ type: CSS,
+ path: _path(mname, bname)
+ };
+
+ if (mname == CSSGRIDS) {
+ mods[mname].requires = [CSSFONTS];
+ mods[mname].optional = [CSSRESET];
+ mods[contextname].requires = [CSSFONTS + CONTEXT];
+ mods[contextname].optional = [CSSRESET + CONTEXT];
+ } else if (mname == CSSBASE) {
+ mods[mname].after = CSS_AFTER;
+ mods[contextname].after = CSS_AFTER;
+ }
+}
+
+Y.Env.meta = META;
+
+GLOBAL_LOADED = GLOBAL_ENV._loaded;
+
+Y.Loader = function(o) {
+
+ /**
+ * Internal callback to handle multiple internal insert() calls
+ * so that css is inserted prior to js
+ * @property _internalCallback
+ * @private
+ */
+ // this._internalCallback = null;
+
+ /**
+ * Callback that will be executed when the loader is finished
+ * with an insert
+ * @method onSuccess
+ * @type function
+ */
+ // this.onSuccess = null;
+
+ /**
+ * Callback that will be executed if there is a failure
+ * @method onFailure
+ * @type function
+ */
+ // this.onFailure = null;
+
+ /**
+ * Callback for the 'CSSComplete' event. When loading YUI components with CSS
+ * the CSS is loaded first, then the script. This provides a moment you can tie into to improve
+ * the presentation of the page while the script is loading.
+ * @method onCSS
+ * @type function
+ */
+ // this.onCSS = null;
+
+ /**
+ * Callback executed each time a script or css file is loaded
+ * @method onProgress
+ * @type function
+ */
+ // this.onProgress = null;
+
+ /**
+ * Callback that will be executed if a timeout occurs
+ * @method onTimeout
+ * @type function
+ */
+ // this.onTimeout = null;
+
+ /**
+ * The execution context for all callbacks
+ * @property context
+ * @default {YUI} the YUI instance
+ */
+ this.context = Y;
+
+ /**
+ * Data that is passed to all callbacks
+ * @property data
+ */
+ // this.data = null;
+
+ /**
+ * Node reference or id where new nodes should be inserted before
+ * @property insertBefore
+ * @type string|HTMLElement
+ */
+ // this.insertBefore = null;
+
+ /**
+ * The charset attribute for inserted nodes
+ * @property charset
+ * @type string
+ * @deprecated, use cssAttributes or jsAttributes
+ */
+ // this.charset = null;
+
+ /**
+ * An object literal containing attributes to add to link nodes
+ * @property cssAttributes
+ * @type object
+ */
+ // this.cssAttributes = null;
+
+ /**
+ * An object literal containing attributes to add to script nodes
+ * @property jsAttributes
+ * @type object
+ */
+ // this.jsAttributes = null;
+
+ /**
+ * The base directory.
+ * @property base
+ * @type string
+ * @default http://yui.yahooapis.com/[YUI VERSION]/build/
+ */
+ this.base = Y.Env.meta.base;
+
+ /**
+ * Base path for the combo service
+ * @property comboBase
+ * @type string
+ * @default http://yui.yahooapis.com/combo?
+ */
+ this.comboBase = Y.Env.meta.comboBase;
+
+ /**
+ * If configured, YUI JS resources will use the combo
+ * handler
+ * @property combine
+ * @type boolean
+ * @default true if a base dir isn't in the config
+ */
+ this.combine = o.base && (o.base.indexOf( this.comboBase.substr(0, 20)) > -1);
+
+ /**
+ * Ignore modules registered on the YUI global
+ * @property ignoreRegistered
+ * @default false
+ */
+ // this.ignoreRegistered = false;
+
+ /**
+ * Root path to prepend to module path for the combo
+ * service
+ * @property root
+ * @type string
+ * @default [YUI VERSION]/build/
+ */
+ this.root = Y.Env.meta.root;
+
+ /**
+ * Timeout value in milliseconds. If set, this value will be used by
+ * the get utility. the timeout event will fire if
+ * a timeout occurs.
+ * @property timeout
+ * @type int
+ */
+ this.timeout = 0;
+
+ /**
+ * A list of modules that should not be loaded, even if
+ * they turn up in the dependency tree
+ * @property ignore
+ * @type string[]
+ */
+ // this.ignore = null;
+
+ /**
+ * A list of modules that should always be loaded, even
+ * if they have already been inserted into the page.
+ * @property force
+ * @type string[]
+ */
+ // this.force = null;
+
+ this.forceMap = {};
+
+ /**
+ * Should we allow rollups
+ * @property allowRollup
+ * @type boolean
+ * @default true
+ */
+ // this.allowRollup = true;
+
+ /**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ * @property filter
+ * @type string|{searchExp: string, replaceStr: string}
+ */
+ // this.filter = null;
+
+ /**
+ * per-component filter specification. If specified for a given component, this
+ * overrides the filter config.
+ * @property filters
+ * @type object
+ */
+ this.filters = {};
+
+ /**
+ * The list of requested modules
+ * @property required
+ * @type {string: boolean}
+ */
+ this.required = {};
+
+ /**
+ * The library metadata
+ * @property moduleInfo
+ */
+ // this.moduleInfo = Y.merge(Y.Env.meta.moduleInfo);
+ this.moduleInfo = {};
+
+ /**
+ * Provides the information used to skin the skinnable components.
+ * The following skin definition would result in 'skin1' and 'skin2'
+ * being loaded for calendar (if calendar was requested), and
+ * 'sam' for all other skinnable components:
+ *
+ * <code>
+ * skin: {
+ *
+ * // The default skin, which is automatically applied if not
+ * // overriden by a component-specific skin definition.
+ * // Change this in to apply a different skin globally
+ * defaultSkin: 'sam',
+ *
+ * // This is combined with the loader base property to get
+ * // the default root directory for a skin. ex:
+ * // http://yui.yahooapis.com/2.3.0/build/assets/skins/sam/
+ * base: 'assets/skins/',
+ *
+ * // The name of the rollup css file for the skin
+ * path: 'skin.css',
+ *
+ * // The number of skinnable components requested that are
+ * // required before using the rollup file rather than the
+ * // individual component css files
+ * rollup: 3,
+ *
+ * // Any component-specific overrides can be specified here,
+ * // making it possible to load different skins for different
+ * // components. It is possible to load more than one skin
+ * // for a given component as well.
+ * overrides: {
+ * calendar: ['skin1', 'skin2']
+ * }
+ * }
+ * </code>
+ * @property skin
+ */
+ this.skin = Y.merge(Y.Env.meta.skin);
+
+ var defaults = Y.Env.meta.modules, i, onPage = YUI.Env.mods;
+
+ this._internal = true;
+ for (i in defaults) {
+ if (defaults.hasOwnProperty(i)) {
+ this.addModule(defaults[i], i);
+ }
+ }
+
+ for (i in onPage) {
+ if (onPage.hasOwnProperty(i) && !this.moduleInfo[i] && onPage[i].details) {
+ this.addModule(onPage[i].details, i);
+ }
+ }
+ this._internal = false;
+
+ /**
+ * List of rollup files found in the library metadata
+ * @property rollups
+ */
+ // this.rollups = null;
+
+ /**
+ * Whether or not to load optional dependencies for
+ * the requested modules
+ * @property loadOptional
+ * @type boolean
+ * @default false
+ */
+ // this.loadOptional = false;
+
+ /**
+ * All of the derived dependencies in sorted order, which
+ * will be populated when either calculate() or insert()
+ * is called
+ * @property sorted
+ * @type string[]
+ */
+ this.sorted = [];
+
+ /**
+ * Set when beginning to compute the dependency tree.
+ * Composed of what YUI reports to be loaded combined
+ * with what has been loaded by any instance on the page
+ * with the version number specified in the metadata.
+ * @propery loaded
+ * @type {string: boolean}
+ */
+ this.loaded = GLOBAL_LOADED[VERSION];
+
+ /**
+ * A list of modules to attach to the YUI instance when complete.
+ * If not supplied, the sorted list of dependencies are applied.
+ * @property attaching
+ */
+ // this.attaching = null;
+
+ /**
+ * Flag to indicate the dependency tree needs to be recomputed
+ * if insert is called again.
+ * @property dirty
+ * @type boolean
+ * @default true
+ */
+ this.dirty = true;
+
+ /**
+ * List of modules inserted by the utility
+ * @property inserted
+ * @type {string: boolean}
+ */
+ this.inserted = {};
+
+ /**
+ * List of skipped modules during insert() because the module
+ * was not defined
+ * @property skipped
+ */
+ this.skipped = {};
+
+
+ // Y.on('yui:load', this.loadNext, this);
+
+ this._config(o);
+
+};
+
+Y.Loader.prototype = {
+
+ FILTER_DEFS: {
+ RAW: {
+ 'searchExp': "-min\\.js",
+ 'replaceStr': ".js"
+ },
+ DEBUG: {
+ 'searchExp': "-min\\.js",
+ 'replaceStr': "-debug.js"
+ }
+ },
+
+ SKIN_PREFIX: "skin-",
+
+ _config: function(o) {
+
+ var i, j, val, f;
+
+ // apply config values
+ if (o) {
+ for (i in o) {
+ if (o.hasOwnProperty(i)) {
+ val = o[i];
+ if (i == 'require') {
+ this.require(val);
+ } else if (i == 'modules') {
+
+ // add a hash of module definitions
+ for (j in val) {
+ if (val.hasOwnProperty(j)) {
+ this.addModule(val[j], j);
+ }
+ }
+
+ } else {
+ this[i] = val;
+ }
+ }
+ }
+ }
+
+ // fix filter
+ f = this.filter;
+
+ if (L.isString(f)) {
+ f = f.toUpperCase();
+ this.filterName = f;
+ this.filter = this.FILTER_DEFS[f];
+ if (f == 'DEBUG') {
+ this.require('yui-log', 'dump');
+ }
+ }
+
+ },
+
+ /**
+ * Returns the skin module name for the specified skin name. If a
+ * module name is supplied, the returned skin module name is
+ * specific to the module passed in.
+ * @method formatSkin
+ * @param skin {string} the name of the skin
+ * @param mod {string} optional: the name of a module to skin
+ * @return {string} the full skin module name
+ */
+ formatSkin: function(skin, mod) {
+ var s = this.SKIN_PREFIX + skin;
+ if (mod) {
+ s = s + "-" + mod;
+ }
+
+ return s;
+ },
+
+ /*
+ * Reverses <code>formatSkin</code>, providing the skin name and
+ * module name if the string matches the pattern for skins.
+ * @method parseSkin
+ * @param mod {string} the module name to parse
+ * @return {skin: string, module: string} the parsed skin name
+ * and module name, or null if the supplied string does not match
+ * the skin pattern
+ *
+ * This isn't being used at the moment
+ *
+ */
+ // parseSkin: function(mod) {
+ //
+ // if (mod.indexOf(this.SKIN_PREFIX) === 0) {
+ // var a = mod.split("-");
+ // return {skin: a[1], module: a[2]};
+ // }
+ // return null;
+ // },
+
+ /**
+ * Adds the skin def to the module info
+ * @method _addSkin
+ * @param skin {string} the name of the skin
+ * @param mod {string} the name of the module
+ * @param parent {string} parent module if this is a skin of a
+ * submodule or plugin
+ * @return {string} the module name for the skin
+ * @private
+ */
+ _addSkin: function(skin, mod, parent) {
+
+ var name = this.formatSkin(skin),
+ info = this.moduleInfo,
+ sinf = this.skin,
+ ext = info[mod] && info[mod].ext,
+ mdef, pkg;
+
+ /*
+ // Add a module definition for the skin rollup css
+ if (!info[name]) {
+ this.addModule({
+ 'name': name,
+ 'type': 'css',
+ 'path': sinf.base + skin + '/' + sinf.path,
+ //'supersedes': '*',
+ 'after': sinf.after,
+ 'rollup': sinf.rollup,
+ 'ext': ext
+ });
+ }
+ */
+
+ // Add a module definition for the module-specific skin css
+ if (mod) {
+ name = this.formatSkin(skin, mod);
+ if (!info[name]) {
+ mdef = info[mod];
+ pkg = mdef.pkg || mod;
+ this.addModule({
+ 'name': name,
+ 'type': 'css',
+ 'after': sinf.after,
+ 'path': (parent || pkg) + '/' + sinf.base + skin + '/' + mod + '.css',
+ 'ext': ext
+ });
+ }
+ }
+
+ return name;
+ },
+
+ /** Add a new module to the component metadata.
+ * <dl>
+ * <dt>name:</dt> <dd>required, the component name</dd>
+ * <dt>type:</dt> <dd>required, the component type (js or css)</dd>
+ * <dt>path:</dt> <dd>required, the path to the script from "base"</dd>
+ * <dt>requires:</dt> <dd>array of modules required by this component</dd>
+ * <dt>optional:</dt> <dd>array of optional modules for this component</dd>
+ * <dt>supersedes:</dt> <dd>array of the modules this component replaces</dd>
+ * <dt>after:</dt> <dd>array of modules the components which, if present, should be sorted above this one</dd>
+ * <dt>rollup:</dt> <dd>the number of superseded modules required for automatic rollup</dd>
+ * <dt>fullpath:</dt> <dd>If fullpath is specified, this is used instead of the configured base + path</dd>
+ * <dt>skinnable:</dt> <dd>flag to determine if skin assets should automatically be pulled in</dd>
+ * <dt>submodules:</dt> <dd>a has of submodules</dd>
+ * </dl>
+ * @method addModule
+ * @param o An object containing the module data
+ * @param name the module name (optional), required if not in the module data
+ * @return {boolean} true if the module was added, false if
+ * the object passed in did not provide all required attributes
+ */
+ addModule: function(o, name) {
+
+ name = name || o.name;
+ o.name = name;
+
+ if (!o || !o.name) {
+ return false;
+ }
+
+ if (!o.type) {
+ o.type = JS;
+ }
+
+ if (!o.path && !o.fullpath) {
+ // o.path = name + "/" + name + "-min." + o.type;
+ o.path = _path(name, name, o.type);
+ }
+
+ o.ext = ('ext' in o) ? o.ext : (this._internal) ? false : true;
+ o.requires = o.requires || [];
+
+
+ this.moduleInfo[name] = o;
+
+ // Handle submodule logic
+ var subs = o.submodules, i, l, sup, s, smod, plugins, plug;
+ if (subs) {
+ sup = [];
+ l = 0;
+
+ for (i in subs) {
+ if (subs.hasOwnProperty(i)) {
+ s = subs[i];
+ s.path = _path(name, i, o.type);
+ this.addModule(s, i);
+ sup.push(i);
+
+ if (o.skinnable) {
+ smod = this._addSkin(this.skin.defaultSkin, i, name);
+ sup.push(smod.name);
+ }
+
+ l++;
+ }
+ }
+
+ o.supersedes = sup;
+ o.rollup = (l<4) ? l : Math.min(l-1, 4);
+ }
+
+ plugins = o.plugins;
+ if (plugins) {
+ for (i in plugins) {
+ if (plugins.hasOwnProperty(i)) {
+ plug = plugins[i];
+ plug.path = _path(name, i, o.type);
+ plug.requires = plug.requires || [];
+ // plug.requires.push(name);
+ this.addModule(plug, i);
+ if (o.skinnable) {
+ this._addSkin(this.skin.defaultSkin, i, name);
+ }
+ }
+ }
+ }
+
+ this.dirty = true;
+
+ return o;
+ },
+
+ /**
+ * Add a requirement for one or more module
+ * @method require
+ * @param what {string[] | string*} the modules to load
+ */
+ require: function(what) {
+ var a = (typeof what === "string") ? arguments : what;
+ this.dirty = true;
+ Y.mix(this.required, Y.Array.hash(a));
+ },
+
+ /**
+ * Returns an object containing properties for all modules required
+ * in order to load the requested module
+ * @method getRequires
+ * @param mod The module definition from moduleInfo
+ */
+ getRequires: function(mod) {
+
+ if (!mod) {
+ return [];
+ }
+
+ if (!this.dirty && mod.expanded) {
+ return mod.expanded;
+ }
+
+ var i, d=[], r=mod.requires, o=mod.optional,
+ info=this.moduleInfo, m, j, add;
+
+ for (i=0; i<r.length; i=i+1) {
+ d.push(r[i]);
+ m = this.getModule(r[i]);
+ add = this.getRequires(m);
+ for (j=0;j<add.length;j=j+1) {
+ d.push(add[j]);
+ }
+ }
+
+ // get the requirements from superseded modules, if any
+ r=mod.supersedes;
+ if (r) {
+ for (i=0; i<r.length; i=i+1) {
+ d.push(r[i]);
+ m = this.getModule(r[i]);
+ add = this.getRequires(m);
+ for (j=0;j<add.length;j=j+1) {
+ d.push(add[j]);
+ }
+ }
+ }
+
+ if (o && this.loadOptional) {
+ for (i=0; i<o.length; i=i+1) {
+ d.push(o[i]);
+ add = this.getRequires(info[o[i]]);
+ for (j=0;j<add.length;j=j+1) {
+ d.push(add[j]);
+ }
+ }
+ }
+
+ mod.expanded = Y.Object.keys(Y.Array.hash(d));
+ return mod.expanded;
+ },
+
+
+ /**
+ * Returns a hash of module names the supplied module satisfies.
+ * @method getProvides
+ * @param name {string} The name of the module
+ * @return what this module provides
+ */
+ getProvides: function(name) {
+ var m = this.getModule(name), o, s;
+
+ if (!m) {
+ return NOT_FOUND;
+ }
+
+ if (m && !m.provides) {
+ o = {};
+ s = m.supersedes;
+
+ if (s) {
+ Y.Array.each(s, function(v) {
+ Y.mix(o, this.getProvides(v));
+ }, this);
+ }
+
+ o[name] = true;
+ m.provides = o;
+ }
+
+ return m.provides;
+ },
+
+
+ /**
+ * Calculates the dependency tree, the result is stored in the sorted
+ * property
+ * @method calculate
+ * @param o optional options object
+ * @param type optional argument to prune modules
+ */
+ calculate: function(o, type) {
+ if (o || type || this.dirty) {
+ this._config(o);
+ this._setup();
+ this._explode();
+ if (this.allowRollup && !this.combine) {
+ this._rollup();
+ }
+ this._reduce();
+ this._sort();
+
+
+ this.dirty = false;
+ }
+ },
+
+ /**
+ * Investigates the current YUI configuration on the page. By default,
+ * modules already detected will not be loaded again unless a force
+ * option is encountered. Called by calculate()
+ * @method _setup
+ * @private
+ */
+ _setup: function() {
+
+ var info = this.moduleInfo, name, i, j, m, o, l, smod;
+
+ // Create skin modules
+ for (name in info) {
+ if (info.hasOwnProperty(name)) {
+ m = info[name];
+ if (m && m.skinnable) {
+ o = this.skin.overrides;
+ if (o && o[name]) {
+ for (i=0; i<o[name].length; i=i+1) {
+ smod = this._addSkin(o[name][i], name);
+ }
+ } else {
+ smod = this._addSkin(this.skin.defaultSkin, name);
+ }
+
+ m.requires.push(smod);
+ }
+ }
+ }
+
+ l = Y.merge(this.inserted); // shallow clone
+
+ // available modules
+ if (!this.ignoreRegistered) {
+ Y.mix(l, GLOBAL_ENV.mods);
+ }
+
+
+ // add the ignore list to the list of loaded packages
+ if (this.ignore) {
+ // OU.appendArray(l, this.ignore);
+ Y.mix(l, Y.Array.hash(this.ignore));
+ }
+
+ // expand the list to include superseded modules
+ for (j in l) {
+ if (l.hasOwnProperty(j)) {
+ Y.mix(l, this.getProvides(j));
+ }
+ }
+
+ // remove modules on the force list from the loaded list
+ if (this.force) {
+ for (i=0; i<this.force.length; i=i+1) {
+ if (this.force[i] in l) {
+ delete l[this.force[i]];
+ }
+ }
+ }
+
+
+ Y.mix(this.loaded, l);
+
+ // this.loaded = l;
+
+ },
+
+
+ /**
+ * Inspects the required modules list looking for additional
+ * dependencies. Expands the required list to include all
+ * required modules. Called by calculate()
+ * @method _explode
+ * @private
+ */
+ _explode: function() {
+
+ var r = this.required, m, reqs;
+
+ Y.Object.each(r, function(v, name) {
+
+ m = this.getModule(name);
+
+ var expound = m && m.expound;
+
+ if (m) {
+
+ if (expound) {
+ r[expound] = this.getModule(expound);
+ reqs = this.getRequires(r[expound]);
+ Y.mix(r, Y.Array.hash(reqs));
+ }
+
+ reqs = this.getRequires(m);
+
+ Y.mix(r, Y.Array.hash(reqs));
+ }
+
+ }, this);
+ },
+
+ getModule: function(name) {
+
+ var m = this.moduleInfo[name];
+
+ // create the default module
+ // if (!m) {
+ // m = this.addModule({ext: false}, name);
+ // }
+
+ return m;
+ },
+
+ /**
+ * Look for rollup packages to determine if all of the modules a
+ * rollup supersedes are required. If so, include the rollup to
+ * help reduce the total number of connections required. Called
+ * by calculate()
+ * @method _rollup
+ * @private
+ */
+ _rollup: function() {
+ var i, j, m, s, rollups={}, r=this.required, roll,
+ info = this.moduleInfo, rolled, c;
+
+ // find and cache rollup modules
+ if (this.dirty || !this.rollups) {
+ for (i in info) {
+ if (info.hasOwnProperty(i)) {
+ m = this.getModule(i);
+ // if (m && m.rollup && m.supersedes) {
+ if (m && m.rollup) {
+ rollups[i] = m;
+ }
+ }
+ }
+
+ this.rollups = rollups;
+ this.forceMap = (this.force) ? Y.Array.hash(this.force) : {};
+ }
+
+ // make as many passes as needed to pick up rollup rollups
+ for (;;) {
+ rolled = false;
+
+ // go through the rollup candidates
+ for (i in rollups) {
+
+ if (rollups.hasOwnProperty(i)) {
+
+ // there can be only one, unless forced
+ if (!r[i] && ((!this.loaded[i]) || this.forceMap[i])) {
+ m = this.getModule(i);
+ s = m.supersedes || [];
+ roll = false;
+
+ // @TODO remove continue
+ if (!m.rollup) {
+ continue;
+ }
+
+ c = 0;
+
+ // check the threshold
+ for (j=0;j<s.length;j=j+1) {
+
+
+ // if the superseded module is loaded, we can't load the rollup
+ // unless it has been forced
+ if (this.loaded[s[j]] && !this.forceMap[s[j]]) {
+ roll = false;
+ break;
+ // increment the counter if this module is required. if we are
+ // beyond the rollup threshold, we will use the rollup module
+ } else if (r[s[j]]) {
+ c++;
+ roll = (c >= m.rollup);
+ if (roll) {
+ break;
+ }
+ }
+ }
+
+ if (roll) {
+ // add the rollup
+ r[i] = true;
+ rolled = true;
+
+ // expand the rollup's dependencies
+ this.getRequires(m);
+ }
+ }
+ }
+ }
+
+ // if we made it here w/o rolling up something, we are done
+ if (!rolled) {
+ break;
+ }
+ }
+ },
+
+ /**
+ * Remove superceded modules and loaded modules. Called by
+ * calculate() after we have the mega list of all dependencies
+ * @method _reduce
+ * @private
+ */
+ _reduce: function() {
+ var i, j, s, m, r=this.required, type = this.loadType;
+ for (i in r) {
+ if (r.hasOwnProperty(i)) {
+ m = this.getModule(i);
+ // remove if already loaded
+ if ((this.loaded[i] && (!this.forceMap[i]) && !this.ignoreRegistered) || (type && m && m.type != type)) {
+ delete r[i];
+ // remove anything this module supersedes
+ } else {
+
+ s = m && m.supersedes;
+ if (s) {
+ for (j=0; j<s.length; j=j+1) {
+ if (s[j] in r) {
+ delete r[s[j]];
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ _attach: function() {
+ // this is the full list of items the YUI needs attached,
+ // which is needed if some dependencies are already on
+ // the page without their dependencies.
+ if (this.attaching) {
+ Y._attach(this.attaching);
+ } else {
+ Y._attach(this.sorted);
+ }
+
+ // this._pushEvents();
+
+ },
+
+ _finish: function() {
+ _queue.running = false;
+ this._continue();
+ },
+
+ _onSuccess: function() {
+
+
+ this._attach();
+
+ var skipped = this.skipped, i, f;
+
+ for (i in skipped) {
+ if (skipped.hasOwnProperty(i)) {
+ delete this.inserted[i];
+ }
+ }
+
+ this.skipped = {};
+
+ f = this.onSuccess;
+
+ if (f) {
+ f.call(this.context, {
+ msg: 'success',
+ data: this.data,
+ success: true
+ });
+ }
+
+ this._finish();
+
+ },
+
+ _onFailure: function(o) {
+
+
+ this._attach();
+
+ var f = this.onFailure;
+ if (f) {
+ f.call(this.context, {
+ msg: 'failure: ' + o.msg,
+ data: this.data,
+ success: false
+ });
+ }
+
+ this._finish();
+ },
+
+ _onTimeout: function() {
+
+
+ this._attach();
+
+ var f = this.onTimeout;
+ if (f) {
+ f.call(this.context, {
+ msg: 'timeout',
+ data: this.data,
+ success: false
+ });
+ }
+
+ this._finish();
+ },
+
+ /**
+ * Sorts the dependency tree. The last step of calculate()
+ * @method _sort
+ * @private
+ */
+ _sort: function() {
+
+ // create an indexed list
+ var s = Y.Object.keys(this.required),
+ info = this.moduleInfo,
+ loaded = this.loaded,
+ done = {},
+ p=0, l, a, b, j, k, moved, doneKey,
+
+ // returns true if b is not loaded, and is required
+ // directly or by means of modules it supersedes.
+ requires = Y.cached(function(mod1, mod2) {
+
+ var m = info[mod1], i, r, after, other = info[mod2], s;
+
+ if (loaded[mod2] || !m || !other) {
+ return false;
+ }
+
+ r = m.expanded;
+ after = m.after;
+
+ // check if this module requires the other directly
+ if (r && Y.Array.indexOf(r, mod2) > -1) {
+ return true;
+ }
+
+ // check if this module should be sorted after the other
+ if (after && Y.Array.indexOf(after, mod2) > -1) {
+ return true;
+ }
+
+ // check if this module requires one the other supersedes
+ s = info[mod2] && info[mod2].supersedes;
+ if (s) {
+ for (i=0; i<s.length; i=i+1) {
+ if (requires(mod1, s[i])) {
+ return true;
+ }
+ }
+ }
+
+ // external css files should be sorted below yui css
+ if (m.ext && m.type == CSS && !other.ext && other.type == CSS) {
+ return true;
+ }
+
+ return false;
+ });
+
+ // keep going until we make a pass without moving anything
+ for (;;) {
+
+ l = s.length;
+ moved = false;
+
+ // start the loop after items that are already sorted
+ for (j=p; j<l; j=j+1) {
+
+ // check the next module on the list to see if its
+ // dependencies have been met
+ a = s[j];
+
+ // check everything below current item and move if we
+ // find a requirement for the current item
+ for (k=j+1; k<l; k=k+1) {
+ doneKey = a + s[k];
+ if (!done[doneKey] && requires(a, s[k])) {
+
+ // extract the dependency so we can move it up
+ b = s.splice(k, 1);
+
+ // insert the dependency above the item that
+ // requires it
+ s.splice(j, 0, b[0]);
+
+ // only swap two dependencies once to short circut
+ // circular dependencies
+ done[doneKey] = true;
+
+ // keep working
+ moved = true;
+
+ break;
+ }
+ }
+
+ // jump out of loop if we moved something
+ if (moved) {
+ break;
+ // this item is sorted, move our pointer and keep going
+ } else {
+ p = p + 1;
+ }
+ }
+
+ // when we make it here and moved is false, we are
+ // finished sorting
+ if (!moved) {
+ break;
+ }
+
+ }
+
+ this.sorted = s;
+ },
+
+ _insert: function(source, o, type) {
+
+
+ // restore the state at the time of the request
+ if (source) {
+ this._config(source);
+ }
+
+ // build the dependency list
+ this.calculate(o); // don't include type so we can process CSS and script in
+ // one pass when the type is not specified.
+ this.loadType = type;
+
+ if (!type) {
+
+ var self = this;
+
+ this._internalCallback = function() {
+ var f = self.onCSS;
+ if (f) {
+ f.call(self.context, Y);
+ }
+ self._internalCallback = null;
+ self._insert(null, null, JS);
+ };
+
+ // _queue.running = false;
+ this._insert(null, null, CSS);
+
+ return;
+ }
+
+
+ // set a flag to indicate the load has started
+ this._loading = true;
+
+ // flag to indicate we are done with the combo service
+ // and any additional files will need to be loaded
+ // individually
+ this._combineComplete = {};
+
+
+ // start the load
+ this.loadNext();
+
+ },
+
+ _continue: function() {
+ if (!(_queue.running) && _queue.size() > 0) {
+ _queue.running = true;
+ _queue.next()();
+ }
+ },
+
+ /**
+ * inserts the requested modules and their dependencies.
+ * <code>type</code> can be "js" or "css". Both script and
+ * css are inserted if type is not provided.
+ * @method insert
+ * @param o optional options object
+ * @param type {string} the type of dependency to insert
+ */
+ insert: function(o, type) {
+
+
+ var self = this, copy = Y.merge(this, true);
+
+ delete copy.require;
+ delete copy.dirty;
+
+ _queue.add(function() {
+ self._insert(copy, o, type);
+ });
+
+ this._continue();
+
+ },
+
+ /**
+ * Executed every time a module is loaded, and if we are in a load
+ * cycle, we attempt to load the next script. Public so that it
+ * is possible to call this if using a method other than
+ * Y.register to determine when scripts are fully loaded
+ * @method loadNext
+ * @param mname {string} optional the name of the module that has
+ * been loaded (which is usually why it is time to load the next
+ * one)
+ */
+ loadNext: function(mname) {
+
+ // It is possible that this function is executed due to something
+ // else one the page loading a YUI module. Only react when we
+ // are actively loading something
+ if (!this._loading) {
+ return;
+ }
+
+ var s, len, i, m, url, self=this, type=this.loadType, fn, msg, attr,
+ callback=function(o) {
+ this._combineComplete[type] = true;
+
+ var c=this._combining, len=c.length, i;
+
+ for (i=0; i<len; i=i+1) {
+ this.inserted[c[i]] = true;
+ }
+
+ this.loadNext(o.data);
+ },
+ onsuccess=function(o) {
+ self.loadNext(o.data);
+ };
+
+ // @TODO this will need to handle the two phase insert when
+ // CSS support is added
+ if (this.combine && (!this._combineComplete[type])) {
+
+ this._combining = [];
+ s=this.sorted;
+ len=s.length;
+ url=this.comboBase;
+
+
+ for (i=0; i<len; i=i+1) {
+ m = this.getModule(s[i]);
+ // Do not try to combine non-yui JS
+ if (m && (m.type === type) && !m.ext) {
+ url += this.root + m.path;
+ if (i < len-1) {
+ url += '&';
+ }
+
+ this._combining.push(s[i]);
+ }
+ }
+
+ if (this._combining.length) {
+
+
+ // if (m.type === CSS) {
+ if (type === CSS) {
+ fn = Y.Get.css;
+ attr = this.cssAttributes;
+ } else {
+ fn = Y.Get.script;
+ attr = this.jsAttributes;
+ }
+
+ // @TODO get rid of the redundant Get code
+ fn(this._filter(url), {
+ data: this._loading,
+ onSuccess: callback,
+ onFailure: this._onFailure,
+ onTimeout: this._onTimeout,
+ insertBefore: this.insertBefore,
+ charset: this.charset,
+ attributes: attr,
+ timeout: this.timeout,
+ autopurge: false,
+ context: self
+ });
+
+ return;
+
+ } else {
+ this._combineComplete[type] = true;
+ }
+ }
+
+ if (mname) {
+
+ // if the module that was just loaded isn't what we were expecting,
+ // continue to wait
+ if (mname !== this._loading) {
+ return;
+ }
+
+
+ // The global handler that is called when each module is loaded
+ // will pass that module name to this function. Storing this
+ // data to avoid loading the same module multiple times
+ this.inserted[mname] = true;
+ this.loaded[mname] = true;
+
+ if (this.onProgress) {
+ this.onProgress.call(this.context, {
+ name: mname,
+ data: this.data
+ });
+ }
+
+
+ }
+
+ s=this.sorted;
+ len=s.length;
+
+ for (i=0; i<len; i=i+1) {
+
+ // this.inserted keeps track of what the loader has loaded.
+ // move on if this item is done.
+ if (s[i] in this.inserted) {
+ continue;
+ }
+
+ // Because rollups will cause multiple load notifications
+ // from Y, loadNext may be called multiple times for
+ // the same module when loading a rollup. We can safely
+ // skip the subsequent requests
+ if (s[i] === this._loading) {
+ return;
+ }
+
+ // log("inserting " + s[i]);
+ m = this.getModule(s[i]);
+
+ if (!m) {
+
+ msg = "Undefined module " + s[i] + " skipped";
+ this.inserted[s[i]] = true;
+ this.skipped[s[i]] = true;
+ continue;
+
+ }
+
+
+ // The load type is stored to offer the possibility to load
+ // the css separately from the script.
+ if (!type || type === m.type) {
+ this._loading = s[i];
+
+ if (m.type === CSS) {
+ fn = Y.Get.css;
+ attr = this.cssAttributes;
+ } else {
+ fn = Y.Get.script;
+ attr = this.jsAttributes;
+ }
+
+ url = (m.fullpath) ? this._filter(m.fullpath, s[i]) : this._url(m.path, s[i]);
+
+ fn(url, {
+ data: s[i],
+ onSuccess: onsuccess,
+ insertBefore: this.insertBefore,
+ charset: this.charset,
+ attributes: attr,
+ onFailure: this._onFailure,
+ onTimeout: this._onTimeout,
+ timeout: this.timeout,
+ autopurge: false,
+ context: self
+ });
+
+ return;
+ }
+ }
+
+ // we are finished
+ this._loading = null;
+
+ fn = this._internalCallback;
+
+ // internal callback for loading css first
+ if (fn) {
+ this._internalCallback = null;
+ fn.call(this);
+
+ // } else if (this.onSuccess) {
+ } else {
+ // call Y.use passing this instance. Y will use the sorted
+ // dependency list.
+ this._onSuccess();
+ }
+
+ },
+
+ /**
+ * Apply filter defined for this instance to a url/path
+ * method _filter
+ * @param u {string} the string to filter
+ * @param name {string} the name of the module, if we are processing
+ * a single module as opposed to a combined url
+ * @return {string} the filtered string
+ * @private
+ */
+ _filter: function(u, name) {
+
+ var f = this.filter,
+ hasFilter = name && (name in this.filters),
+ modFilter = hasFilter && this.filters[name];
+
+ if (u) {
+
+ if (hasFilter) {
+ f = (L.isString(modFilter)) ? this.FILTER_DEFS[modFilter.toUpperCase()] || null : modFilter;
+ }
+
+ if (f) {
+ u = u.replace(new RegExp(f.searchExp, 'g'), f.replaceStr);
+ }
+ }
+
+ return u;
+
+ },
+
+ /**
+ * Generates the full url for a module
+ * method _url
+ * @param path {string} the path fragment
+ * @return {string} the full url
+ * @private
+ */
+ _url: function(path, name) {
+ return this._filter((this.base || "") + path, name);
+ }
+
+};
+
+})();
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-focusmanager', function(Y) {
+
+/**
+* <p>The Focus Manager Node Plugin makes it easy to manage focus among
+* a Node's descendants. Primarily intended to help with widget development,
+* the Focus Manager Node Plugin can be used to improve the keyboard
+* accessibility of widgets.</p>
+*
+* <p>
+* When designing widgets that manage a set of descendant controls (i.e. buttons
+* in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to
+* limit the number of descendants in the browser's default tab flow. The fewer
+* number of descendants in the default tab flow, the easier it is for keyboard
+* users to navigate between widgets by pressing the tab key. When a widget has
+* focus it should provide a set of shortcut keys (typically the arrow keys)
+* to move focus among its descendants.
+* </p>
+*
+* <p>
+* To this end, the Focus Manager Node Plugin makes it easy to define a Node's
+* focusable descendants, define which descendant should be in the default tab
+* flow, and define the keys that move focus among each descendant.
+* Additionally, as the CSS
+* <a href="http://www.w3.org/TR/CSS21/selector.html#x38"><code>:focus</code></a>
+* pseudo class is not supported on all elements in all
+* <a href="http://developer.yahoo.com/yui/articles/gbs/">A-Grade browsers</a>,
+* the Focus Manager Node Plugin provides an easy, cross-browser means of
+* styling focus.
+* </p>
+*
+* @module node-focusmanager
+*/
+
+ // Frequently used strings
+
+var ACTIVE_DESCENDANT = "activeDescendant",
+ ID = "id",
+ DISABLED = "disabled",
+ TAB_INDEX = "tabIndex",
+ FOCUSED = "focused",
+ FOCUS_CLASS = "focusClass",
+ CIRCULAR = "circular",
+ UI = "UI",
+ KEY = "key",
+ ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
+ HOST = "host",
+
+ // Collection of keys that, when pressed, cause the browser viewport
+ // to scroll.
+ scrollKeys = {
+ 37: true,
+ 38: true,
+ 39: true,
+ 40: true
+ },
+
+ clickableElements = {
+ "a": true,
+ "button": true,
+ "input": true,
+ "object": true
+ },
+
+ // Library shortcuts
+
+ Lang = Y.Lang,
+ UA = Y.UA,
+
+ /**
+ * The NodeFocusManager class is a plugin for a Node instance. The class is used
+ * via the <a href="Node.html#method_plug"><code>plug</code></a> method of Node
+ * and should not be instantiated directly.
+ * @namespace plugin
+ * @class NodeFocusManager
+ */
+ NodeFocusManager = function () {
+
+ NodeFocusManager.superclass.constructor.apply(this, arguments);
+
+ };
+
+
+NodeFocusManager.ATTRS = {
+
+ /**
+ * Boolean indicating that one of the descendants is focused.
+ *
+ * @attribute focused
+ * @readOnly
+ * @default false
+ * @type boolean
+ */
+ focused: {
+
+ value: false,
+ readOnly: true
+
+ },
+
+
+ /**
+ * String representing the CSS selector used to define the descendant Nodes
+ * whose focus should be managed.
+ *
+ * @attribute descendants
+ * @type Y.NodeList
+ */
+ descendants: {
+
+ getter: function (value) {
+
+ return this.get(HOST).all(value);
+
+ }
+
+ },
+
+
+ /**
+ * <p>Node, or index of the Node, representing the descendant that is either
+ * focused or is focusable (<code>tabIndex</code> attribute is set to 0).
+ * The value cannot represent a disabled descendant Node. Use a value of -1
+ * to remove all descendant Nodes from the default tab flow.
+ * If no value is specified, the active descendant will be inferred using
+ * the following criteria:</p>
+ * <ol>
+ * <li>Examining the <code>tabIndex</code> attribute of each descendant and
+ * using the first descendant whose <code>tabIndex</code> attribute is set
+ * to 0</li>
+ * <li>If no default can be inferred then the value is set to either 0 or
+ * the index of the first enabled descendant.</li>
+ * </ol>
+ *
+ * @attribute activeDescendant
+ * @type Number
+ */
+ activeDescendant: {
+
+ setter: function (value) {
+
+ var isNumber = Lang.isNumber,
+ INVALID_VALUE = Y.Attribute.INVALID_VALUE,
+ descendantsMap = this._descendantsMap,
+ descendants = this._descendants,
+ nodeIndex,
+ returnValue,
+ oNode;
+
+
+ if (isNumber(value)) {
+ nodeIndex = value;
+ returnValue = nodeIndex;
+ }
+ else if ((value instanceof Y.Node) && descendantsMap) {
+
+ nodeIndex = descendantsMap[value.get(ID)];
+
+ if (isNumber(nodeIndex)) {
+ returnValue = nodeIndex;
+ }
+ else {
+
+ // The user passed a reference to a Node that wasn't one
+ // of the descendants.
+ returnValue = INVALID_VALUE;
+
+ }
+
+ }
+ else {
+ returnValue = INVALID_VALUE;
+ }
+
+
+ if (descendants) {
+
+ oNode = descendants.item(nodeIndex);
+
+ if (oNode && oNode.get("disabled")) {
+
+ // Setting the "activeDescendant" attribute to the index
+ // of a disabled descendant is invalid.
+ returnValue = INVALID_VALUE;
+
+ }
+
+ }
+
+ return returnValue;
+
+ }
+
+ },
+
+
+ /**
+ * Object literal representing the keys to be used to navigate between the
+ * next/previous descendant. The format for the attribute's value is
+ * <code>{ next: "down:40", previous: "down:38" }</code>. The value for the
+ * "next" and "previous" properties are used to attach
+ * <a href="event/#keylistener"><code>key</code></a> event listeners. See
+ * the <a href="event/#keylistener">Using the key Event</a> section of
+ * the Event documentation for more information on "key" event listeners.
+ *
+ * @attribute keys
+ * @type Object
+ */
+ keys: {
+
+ value: {
+
+ next: null,
+ previous: null
+
+ }
+
+
+ },
+
+
+ /**
+ * String representing the name of class applied to the focused active
+ * descendant Node. Can also be an object literal used to define both the
+ * class name, and the Node to which the class should be applied. If using
+ * an object literal, the format is:
+ * <code>{ className: "focus", fn: myFunction }</code>. The function
+ * referenced by the <code>fn</code> property in the object literal will be
+ * passed a reference to the currently focused active descendant Node.
+ *
+ * @attribute focusClass
+ * @type String|Object
+ */
+ focusClass: { },
+
+
+ /**
+ * Boolean indicating if focus should be set to the first/last descendant
+ * when the end or beginning of the descendants has been reached.
+ *
+ * @attribute circular
+ * @type Boolean
+ */
+ circular: {
+ value: true
+ }
+
+};
+
+Y.extend(NodeFocusManager, Y.Plugin.Base, {
+
+ // Protected properties
+
+ // Boolean indicating if the NodeFocusManager is active.
+ _stopped: true,
+
+ // NodeList representing the descendants selected via the
+ // "descendants" attribute.
+ _descendants: null,
+
+ // Object literal mapping the IDs of each descendant to its index in the
+ // "_descendants" NodeList.
+ _descendantsMap: null,
+
+ // Reference to the Node instance to which the focused class (defined
+ // by the "focusClass" attribute) is currently applied.
+ _focusedNode: null,
+
+ // Number representing the index of the last descendant Node.
+ _lastNodeIndex: 0,
+
+ // Array of handles for event handlers used for a NodeFocusManager instance.
+ _eventHandlers: null,
+
+
+
+ // Protected methods
+
+ /**
+ * @method _initDescendants
+ * @description Sets the <code>tabIndex</code> attribute of all of the
+ * descendants to -1, except the active descendant, whose
+ * <code>tabIndex</code> attribute is set to 0.
+ * @protected
+ */
+ _initDescendants: function () {
+
+ var descendants = this.get("descendants"),
+ descendantsMap = {},
+ nFirstEnabled = -1,
+ nDescendants,
+ nActiveDescendant = this.get(ACTIVE_DESCENDANT),
+ oNode,
+ sID,
+ i = 0;
+
+
+
+ if (Lang.isUndefined(nActiveDescendant)) {
+ nActiveDescendant = -1;
+ }
+
+
+ if (descendants) {
+
+ nDescendants = descendants.size();
+
+
+ if (nDescendants > 1) {
+
+ for (i = 0; i < nDescendants; i++) {
+
+ oNode = descendants.item(i);
+
+ if (nFirstEnabled === -1 && !oNode.get(DISABLED)) {
+ nFirstEnabled = i;
+ }
+
+
+ // If the user didn't specify a value for the
+ // "activeDescendant" attribute try to infer it from
+ // the markup.
+
+ // Need to pass "2" when using "getAttribute" for IE to get
+ // the attribute value as it is set in the markup.
+ // Need to use "parseInt" because IE always returns the
+ // value as a number, whereas all other browsers return
+ // the attribute as a string when accessed
+ // via "getAttribute".
+
+ if (nActiveDescendant < 0 &&
+ parseInt(oNode.getAttribute(TAB_INDEX, 2), 10) === 0) {
+
+ nActiveDescendant = i;
+
+ }
+
+ oNode.set(TAB_INDEX, -1);
+
+ sID = oNode.get(ID);
+
+ if (!sID) {
+ sID = Y.guid();
+ oNode.set(ID, sID);
+ }
+
+ descendantsMap[sID] = i;
+
+ }
+
+
+ // If the user didn't specify a value for the
+ // "activeDescendant" attribute and no default value could be
+ // determined from the markup, then default to 0.
+
+ if (nActiveDescendant < 0) {
+ nActiveDescendant = 0;
+ }
+
+
+ oNode = descendants.item(nActiveDescendant);
+
+ // Check to make sure the active descendant isn't disabled,
+ // and fall back to the first enabled descendant if it is.
+
+ if (!oNode || oNode.get(DISABLED)) {
+ oNode = descendants.item(nFirstEnabled);
+ nActiveDescendant = nFirstEnabled;
+ }
+
+ this._lastNodeIndex = nDescendants - 1;
+ this._descendants = descendants;
+ this._descendantsMap = descendantsMap;
+
+ this.set(ACTIVE_DESCENDANT, nActiveDescendant);
+
+ // Need to set the "tabIndex" attribute here, since the
+ // "activeDescendantChange" event handler used to manage
+ // the setting of the "tabIndex" attribute isn't wired up yet.
+
+ oNode.set(TAB_INDEX, 0);
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _isDescendant
+ * @description Determines if the specified Node instance is a descendant
+ * managed by the Focus Manager.
+ * @param node {Node} Node instance to be checked.
+ * @return {Boolean} Boolean indicating if the specified Node instance is a
+ * descendant managed by the Focus Manager.
+ * @protected
+ */
+ _isDescendant: function (node) {
+
+ return (node.get(ID) in this._descendantsMap);
+
+ },
+
+
+ /**
+ * @method _removeFocusClass
+ * @description Removes the class name representing focus (as specified by
+ * the "focusClass" attribute) from the Node instance to which it is
+ * currently applied.
+ * @protected
+ */
+ _removeFocusClass: function () {
+
+ var oFocusedNode = this._focusedNode,
+ focusClass = this.get(FOCUS_CLASS),
+ sClassName;
+
+ if (focusClass) {
+ sClassName = Lang.isString(focusClass) ?
+ focusClass : focusClass.className;
+ }
+
+ if (oFocusedNode && sClassName) {
+ oFocusedNode.removeClass(sClassName);
+ }
+
+ },
+
+
+ /**
+ * @method _detachKeyHandler
+ * @description Detaches the "key" event handlers used to support the "keys"
+ * attribute.
+ * @protected
+ */
+ _detachKeyHandler: function () {
+
+ var prevKeyHandler = this._prevKeyHandler,
+ nextKeyHandler = this._nextKeyHandler;
+
+ if (prevKeyHandler) {
+ prevKeyHandler.detach();
+ }
+
+ if (nextKeyHandler) {
+ nextKeyHandler.detach();
+ }
+
+ },
+
+
+ /**
+ * @method _preventScroll
+ * @description Prevents the viewport from scolling when the user presses
+ * the up, down, left, or right key.
+ * @protected
+ */
+ _preventScroll: function (event) {
+
+ if (scrollKeys[event.keyCode]) {
+ event.preventDefault();
+ }
+
+ },
+
+
+ /**
+ * @method _preventScroll
+ * @description Fires the click event if the enter key is pressed while
+ * focused on an HTML element that is not natively clickable.
+ * @protected
+ */
+ _fireClick: function (event) {
+
+ var oTarget = event.target,
+ sNodeName = oTarget.get("nodeName").toLowerCase();
+
+ if (event.keyCode === 13 && (!clickableElements[sNodeName] ||
+ (sNodeName === "a" && !oTarget.getAttribute("href")))) {
+
+ Y.log(("Firing click event for node:" + oTarget.get("id")), "info", "nodeFocusManager");
+
+ oTarget.simulate("click");
+
+ }
+
+ },
+
+
+ /**
+ * @method _attachKeyHandler
+ * @description Attaches the "key" event handlers used to support the "keys"
+ * attribute.
+ * @protected
+ */
+ _attachKeyHandler: function () {
+
+ this._detachKeyHandler();
+
+ var sNextKey = this.get("keys.next"),
+ sPrevKey = this.get("keys.previous"),
+ oNode = this.get(HOST),
+ aHandlers = this._eventHandlers;
+
+ if (sPrevKey) {
+ this._prevKeyHandler =
+ Y.on(KEY, Y.bind(this._focusPrevious, this), oNode, sPrevKey);
+ }
+
+ if (sNextKey) {
+ this._nextKeyHandler =
+ Y.on(KEY, Y.bind(this._focusNext, this), oNode, sNextKey);
+ }
+
+
+ // In Opera it is necessary to call the "preventDefault" method in
+ // response to the user pressing the arrow keys in order to prevent
+ // the viewport from scrolling when the user is moving focus among
+ // the focusable descendants.
+
+ if (UA.opera) {
+ aHandlers.push(oNode.on("keypress", this._preventScroll, this));
+ }
+
+
+ // For all browsers except Opera: HTML elements that are not natively
+ // focusable but made focusable via the tabIndex attribute don't
+ // fire a click event when the user presses the enter key. It is
+ // possible to work around this problem by simplying dispatching a
+ // click event in response to the user pressing the enter key.
+
+ if (!UA.opera) {
+ aHandlers.push(oNode.on("keypress", this._fireClick, this));
+ }
+
+ },
+
+
+ /**
+ * @method _detachEventHandlers
+ * @description Detaches all event handlers used by the Focus Manager.
+ * @protected
+ */
+ _detachEventHandlers: function () {
+
+ this._detachKeyHandler();
+
+ var aHandlers = this._eventHandlers;
+
+ if (aHandlers) {
+
+ Y.Array.each(aHandlers, function (handle) {
+ handle.detach();
+ });
+
+ this._eventHandlers = null;
+
+ }
+
+ },
+
+
+ /**
+ * @method _detachEventHandlers
+ * @description Attaches all event handlers used by the Focus Manager.
+ * @protected
+ */
+ _attachEventHandlers: function () {
+
+ var descendants = this._descendants,
+ aHandlers,
+ oDocument,
+ handle;
+
+ if (descendants && descendants.size() > 1) {
+
+ aHandlers = this._eventHandlers || [];
+ oDocument = this.get(HOST).get("ownerDocument");
+
+
+ if (aHandlers.length === 0) {
+
+ Y.log("Attaching base set of event handlers.", "info", "nodeFocusManager");
+
+ aHandlers.push(oDocument.on("focus", this._onDocFocus, this));
+
+ aHandlers.push(oDocument.on("mousedown",
+ this._onDocMouseDown, this));
+
+ aHandlers.push(
+ this.after("keysChange", this._attachKeyHandler));
+
+ aHandlers.push(
+ this.after("descendantsChange", this._initDescendants));
+
+ aHandlers.push(
+ this.after(ACTIVE_DESCENDANT_CHANGE,
+ this._afterActiveDescendantChange));
+
+
+ // For performance: defer attaching all key-related event
+ // handlers until the first time one of the specified
+ // descendants receives focus.
+
+ handle = this.after("focusedChange", Y.bind(function (event) {
+
+ if (event.newVal) {
+
+ Y.log("Attaching key event handlers.", "info", "nodeFocusManager");
+
+ this._attachKeyHandler();
+
+ // Detach this "focusedChange" handler so that the
+ // key-related handlers only get attached once.
+
+ handle.detach();
+
+ }
+
+ }, this));
+
+ aHandlers.push(handle);
+
+ }
+
+
+ this._eventHandlers = aHandlers;
+
+ }
+
+ },
+
+
+ // Protected event handlers
+
+ /**
+ * @method _onDocMouseDown
+ * @description "mousedown" event handler for the owner document of the
+ * Focus Manager's Node.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ */
+ _onDocMouseDown: function (event) {
+
+ var oHost = this.get(HOST),
+ oTarget = event.target,
+ bChildNode = oHost.contains(oTarget),
+ node,
+
+ getFocusable = function (node) {
+
+ var returnVal = false;
+
+ if (!node.compareTo(oHost)) {
+
+ returnVal = this._isDescendant(node) ? node :
+ getFocusable.call(this, node.get("parentNode"));
+
+ }
+
+ return returnVal;
+
+ };
+
+
+ if (bChildNode) {
+
+ // Check to make sure that the target isn't a child node of one
+ // of the focusable descendants.
+
+ node = getFocusable.call(this, oTarget);
+
+ if (node) {
+ oTarget = node;
+ }
+ else if (!node && this.get(FOCUSED)) {
+
+ // The target was a non-focusable descendant of the root
+ // node, so the "focused" attribute should be set to false.
+
+ this._set(FOCUSED, false);
+ this._onDocFocus(event);
+
+ }
+
+ }
+
+
+ if (bChildNode && this._isDescendant(oTarget)) {
+
+ // Fix general problem in Webkit: mousing down on a button or an
+ // anchor element doesn't focus it.
+
+ // For all browsers: makes sure that the descendant that
+ // was the target of the mousedown event is now considered the
+ // active descendant.
+
+ this.focus(oTarget);
+ }
+ else if (UA.webkit && this.get(FOCUSED) &&
+ (!bChildNode || (bChildNode && !this._isDescendant(oTarget)))) {
+
+ // Fix for Webkit:
+
+ // Document doesn't receive focus in Webkit when the user mouses
+ // down on it, so the "focused" attribute won't get set to the
+ // correct value.
+
+ // The goal is to force a blur if the user moused down on
+ // either: 1) A descendant node, but not one that managed by
+ // the FocusManager, or 2) an element outside of the
+ // FocusManager
+
+ this._set(FOCUSED, false);
+ this._onDocFocus(event);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onDocFocus
+ * @description "focus" event handler for the owner document of the
+ * Focus Manager's Node.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ */
+ _onDocFocus: function (event) {
+
+ var oTarget = this._focusTarget || event.target,
+ bFocused = this.get(FOCUSED),
+ focusClass = this.get(FOCUS_CLASS),
+ oFocusedNode = this._focusedNode,
+ bInCollection;
+
+ if (this._focusTarget) {
+ this._focusTarget = null;
+ }
+
+
+ if (this.get(HOST).contains(oTarget)) {
+
+ // The target is a descendant of the root Node.
+
+ bInCollection = this._isDescendant(oTarget);
+
+ if (!bFocused && bInCollection) {
+
+ // The user has focused a focusable descendant.
+
+ bFocused = true;
+
+ }
+ else if (bFocused && !bInCollection) {
+
+ // The user has focused a child of the root Node that is
+ // not one of the descendants managed by this Focus Manager
+ // so clear the currently focused descendant.
+
+ bFocused = false;
+
+ }
+
+ }
+ else {
+
+ // The target is some other node in the document.
+
+ bFocused = false;
+
+ }
+
+
+ if (focusClass) {
+
+ if (oFocusedNode && (!oFocusedNode.compareTo(oTarget) || !bFocused)) {
+ this._removeFocusClass();
+ }
+
+ if (bInCollection && bFocused) {
+
+ if (focusClass.fn) {
+ oTarget = focusClass.fn(oTarget);
+ oTarget.addClass(focusClass.className);
+ }
+ else {
+ oTarget.addClass(focusClass);
+ }
+
+ this._focusedNode = oTarget;
+
+ }
+
+ }
+
+
+ this._set(FOCUSED, bFocused);
+
+ },
+
+
+ /**
+ * @method _focusNext
+ * @description Keydown event handler that moves focus to the next
+ * enabled descendant.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ * @param activeDescendant {Number} Number representing the index of the
+ * next descendant to be focused
+ */
+ _focusNext: function (event, activeDescendant) {
+
+ var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
+ oNode;
+
+
+ if (this._isDescendant(event.target) &&
+ (nActiveDescendant <= this._lastNodeIndex)) {
+
+ nActiveDescendant = nActiveDescendant + 1;
+
+ if (nActiveDescendant === (this._lastNodeIndex + 1) &&
+ this.get(CIRCULAR)) {
+
+ nActiveDescendant = 0;
+
+ }
+
+ oNode = this._descendants.item(nActiveDescendant);
+
+ if (oNode.get(DISABLED)) {
+ this._focusNext(event, nActiveDescendant);
+ }
+ else {
+ this.focus(nActiveDescendant);
+ }
+
+ }
+
+ this._preventScroll(event);
+
+ },
+
+
+ /**
+ * @method _focusPrevious
+ * @description Keydown event handler that moves focus to the previous
+ * enabled descendant.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ * @param activeDescendant {Number} Number representing the index of the
+ * next descendant to be focused.
+ */
+ _focusPrevious: function (event, activeDescendant) {
+
+ var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
+ oNode;
+
+ if (this._isDescendant(event.target) && nActiveDescendant >= 0) {
+
+ nActiveDescendant = nActiveDescendant - 1;
+
+ if (nActiveDescendant === -1 && this.get(CIRCULAR)) {
+ nActiveDescendant = this._lastNodeIndex;
+ }
+
+ oNode = this._descendants.item(nActiveDescendant);
+
+ if (oNode.get(DISABLED)) {
+ this._focusPrevious(event, nActiveDescendant);
+ }
+ else {
+ this.focus(nActiveDescendant);
+ }
+
+ }
+
+ this._preventScroll(event);
+
+ },
+
+
+ /**
+ * @method _afterActiveDescendantChange
+ * @description afterChange event handler for the
+ * "activeDescendant" attribute.
+ * @protected
+ * @param event {Object} Object representing the change event.
+ */
+ _afterActiveDescendantChange: function (event) {
+
+ var oNode = this._descendants.item(event.prevVal);
+
+ if (oNode) {
+ oNode.set(TAB_INDEX, -1);
+ }
+
+ oNode = this._descendants.item(event.newVal);
+
+ if (oNode) {
+ oNode.set(TAB_INDEX, 0);
+ }
+
+ },
+
+
+
+ // Public methods
+
+ initializer: function (config) {
+
+ this.start();
+
+ },
+
+ destructor: function () {
+
+ this.stop();
+ this.get(HOST).focusManager = null;
+
+ },
+
+
+ /**
+ * @method focus
+ * @description Focuses the active descendant and sets the
+ * <code>focused</code> attribute to true.
+ * @param index {Number} Optional. Number representing the index of the
+ * descendant to be set as the active descendant.
+ * @param index {Node} Optional. Node instance representing the
+ * descendant to be set as the active descendant.
+ */
+ focus: function (index) {
+
+ if (Lang.isUndefined(index)) {
+ index = this.get(ACTIVE_DESCENDANT);
+ }
+
+ this.set(ACTIVE_DESCENDANT, index, { src: UI });
+
+ var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
+
+ if (oNode) {
+
+ oNode.focus();
+
+ // In Opera focusing a <BUTTON> element programmatically
+ // will result in the document-level focus event handler
+ // "_onDocFocus" being called, resulting in the handler
+ // incorrectly setting the "focused" Attribute to false. To fix
+ // this, set a flag ("_focusTarget") that the "_onDocFocus" method
+ // can look for to properly handle this edge case.
+
+ if (UA.opera && oNode.get("nodeName").toLowerCase() === "button") {
+ this._focusTarget = oNode;
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method blur
+ * @description Blurs the current active descendant and sets the
+ * <code>focused</code> attribute to false.
+ */
+ blur: function () {
+
+ var oNode;
+
+ if (this.get(FOCUSED)) {
+
+ oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
+
+ if (oNode) {
+
+ oNode.blur();
+
+ // For Opera and Webkit: Blurring an element in either browser
+ // doesn't result in another element (such as the document)
+ // being focused. Therefore, the "_onDocFocus" method
+ // responsible for managing the application and removal of the
+ // focus indicator class name is never called.
+
+ this._removeFocusClass();
+
+ }
+
+ this._set(FOCUSED, false, { src: UI });
+ }
+
+ },
+
+
+ /**
+ * @method start
+ * @description Enables the Focus Manager.
+ */
+ start: function () {
+
+ if (this._stopped) {
+
+ this._initDescendants();
+ this._attachEventHandlers();
+
+ this._stopped = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method stop
+ * @description Disables the Focus Manager by detaching all event handlers.
+ */
+ stop: function () {
+
+ if (!this._stopped) {
+
+ this._detachEventHandlers();
+
+ this._descendants = null;
+ this._focusedNode = null;
+ this._lastNodeIndex = 0;
+ this._stopped = true;
+
+ }
+
+ },
+
+
+ /**
+ * @method refresh
+ * @description Refreshes the Focus Manager's descendants by re-executing the
+ * CSS selector query specified by the <code>descendants</code> attribute.
+ */
+ refresh: function () {
+
+ this._initDescendants();
+
+ }
+
+});
+
+
+NodeFocusManager.NAME = "nodeFocusManager";
+NodeFocusManager.NS = "focusManager";
+
+Y.namespace("Plugin");
+Y.Plugin.NodeFocusManager = NodeFocusManager;
+
+
+}, '3.0.0' ,{requires:['attribute', 'node', 'plugin', 'node-event-simulate', 'event-key', 'event-focus']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-focusmanager",function(B){var J="activeDescendant",L="id",I="disabled",N="tabIndex",E="focused",A="focusClass",Q="circular",C="UI",F="key",G=J+"Change",O="host",P={37:true,38:true,39:true,40:true},M={"a":true,"button":true,"input":true,"object":true},H=B.Lang,K=B.UA,D=function(){D.superclass.constructor.apply(this,arguments);};D.ATTRS={focused:{value:false,readOnly:true},descendants:{getter:function(R){return this.get(O).all(R);}},activeDescendant:{setter:function(V){var T=H.isNumber,S=B.Attribute.INVALID_VALUE,R=this._descendantsMap,Y=this._descendants,X,U,W;if(T(V)){X=V;U=X;}else{if((V instanceof B.Node)&&R){X=R[V.get(L)];if(T(X)){U=X;}else{U=S;}}else{U=S;}}if(Y){W=Y.item(X);if(W&&W.get("disabled")){U=S;}}return U;}},keys:{value:{next:null,previous:null}},focusClass:{},circular:{value:true}};B.extend(D,B.Plugin.Base,{_stopped:true,_descendants:null,_descendantsMap:null,_focusedNode:null,_lastNodeIndex:0,_eventHandlers:null,_initDescendants:function(){var Y=this.get("descendants"),R={},W=-1,V,U=this.get(J),X,S,T=0;if(H.isUndefined(U)){U=-1;}if(Y){V=Y.size();if(V>1){for(T=0;T<V;T++){X=Y.item(T);if(W===-1&&!X.get(I)){W=T;}if(U<0&&parseInt(X.getAttribute(N,2),10)===0){U=T;}X.set(N,-1);S=X.get(L);if(!S){S=B.guid();X.set(L,S);}R[S]=T;}if(U<0){U=0;}X=Y.item(U);if(!X||X.get(I)){X=Y.item(W);U=W;}this._lastNodeIndex=V-1;this._descendants=Y;this._descendantsMap=R;this.set(J,U);X.set(N,0);}}},_isDescendant:function(R){return(R.get(L) in this._descendantsMap);},_removeFocusClass:function(){var S=this._focusedNode,T=this.get(A),R;if(T){R=H.isString(T)?T:T.className;}if(S&&R){S.removeClass(R);}},_detachKeyHandler:function(){var S=this._prevKeyHandler,R=this._nextKeyHandler;if(S){S.detach();}if(R){R.detach();}},_preventScroll:function(R){if(P[R.keyCode]){R.preventDefault();}},_fireClick:function(S){var R=S.target,T=R.get("nodeName").toLowerCase();if(S.keyCode===13&&(!M[T]||(T==="a"&&!R.getAttribute("href")))){R.simulate("click");}},_attachKeyHandler:function(){this._detachKeyHandler();var U=this.get("keys.next"),S=this.get("keys.previous"),T=this.get(O),R=this._eventHandlers;if(S){this._prevKeyHandler=B.on(F,B.bind(this._focusPrevious,this),T,S);}if(U){this._nextKeyHandler=B.on(F,B.bind(this._focusNext,this),T,U);}if(K.opera){R.push(T.on("keypress",this._preventScroll,this));}if(!K.opera){R.push(T.on("keypress",this._fireClick,this));}},_detachEventHandlers:function(){this._detachKeyHandler();var R=this._eventHandlers;if(R){B.Array.each(R,function(S){S.detach();});this._eventHandlers=null;}},_attachEventHandlers:function(){var U=this._descendants,R,S,T;if(U&&U.size()>1){R=this._eventHandlers||[];S=this.get(O).get("ownerDocument");if(R.length===0){R.push(S.on("focus",this._onDocFocus,this));R.push(S.on("mousedown",this._onDocMouseDown,this));R.push(this.after("keysChange",this._attachKeyHandler));R.push(this.after("descendantsChange",this._initDescendants));R.push(this.after(G,this._afterActiveDescendantChange));T=this.after("focusedChange",B.bind(function(V){if(V.newVal){this._attachKeyHandler();T.detach();}},this));R.push(T);}this._eventHandlers=R;}},_onDocMouseDown:function(U){var W=this.get(O),R=U.target,V=W.contains(R),T,S=function(Y){var X=false;if(!Y.compareTo(W)){X=this._isDescendant(Y)?Y:S.call(this,Y.get("parentNode"));}return X;};if(V){T=S.call(this,R);if(T){R=T;}else{if(!T&&this.get(E)){this._set(E,false);this._onDocFocus(U);}}}if(V&&this._isDescendant(R)){this.focus(R);}else{if(K.webkit&&this.get(E)&&(!V||(V&&!this._isDescendant(R)))){this._set(E,false);this._onDocFocus(U);}}},_onDocFocus:function(W){var U=this._focusTarget||W.target,S=this.get(E),V=this.get(A),T=this._focusedNode,R;if(this._focusTarget){this._focusTarget=null;}if(this.get(O).contains(U)){R=this._isDescendant(U);if(!S&&R){S=true;}else{if(S&&!R){S=false;}}}else{S=false;}if(V){if(T&&(!T.compareTo(U)||!S)){this._removeFocusClass();}if(R&&S){if(V.fn){U=V.fn(U);U.addClass(V.className);}else{U.addClass(V);}this._focusedNode=U;}}this._set(E,S);},_focusNext:function(S,T){var R=T||this.get(J),U;if(this._isDescendant(S.target)&&(R<=this._lastNodeIndex)){R=R+1;if(R===(this._lastNodeIndex+1)&&this.get(Q)){R=0;}U=this._descendants.item(R);if(U.get(I)){this._focusNext(S,R);}else{this.focus(R);}}this._preventScroll(S);},_focusPrevious:function(S,T){var R=T||this.get(J),U;if(this._isDescendant(S.target)&&R>=0){R=R-1;if(R===-1&&this.get(Q)){R=this._lastNodeIndex;}U=this._descendants.item(R);if(U.get(I)){this._focusPrevious(S,R);}else{this.focus(R);}}this._preventScroll(S);},_afterActiveDescendantChange:function(R){var S=this._descendants.item(R.prevVal);if(S){S.set(N,-1);}S=this._descendants.item(R.newVal);if(S){S.set(N,0);}},initializer:function(R){this.start();},destructor:function(){this.stop();this.get(O).focusManager=null;},focus:function(R){if(H.isUndefined(R)){R=this.get(J);}this.set(J,R,{src:C});var S=this._descendants.item(this.get(J));if(S){S.focus();if(K.opera&&S.get("nodeName").toLowerCase()==="button"){this._focusTarget=S;}}},blur:function(){var R;if(this.get(E)){R=this._descendants.item(this.get(J));if(R){R.blur();this._removeFocusClass();}this._set(E,false,{src:C});}},start:function(){if(this._stopped){this._initDescendants();this._attachEventHandlers();this._stopped=false;}},stop:function(){if(!this._stopped){this._detachEventHandlers();this._descendants=null;this._focusedNode=null;this._lastNodeIndex=0;this._stopped=true;}},refresh:function(){this._initDescendants();}});D.NAME="nodeFocusManager";D.NS="focusManager";B.namespace("Plugin");B.Plugin.NodeFocusManager=D;},"3.0.0",{requires:["attribute","node","plugin","node-event-simulate","event-key","event-focus"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-focusmanager', function(Y) {
+
+/**
+* <p>The Focus Manager Node Plugin makes it easy to manage focus among
+* a Node's descendants. Primarily intended to help with widget development,
+* the Focus Manager Node Plugin can be used to improve the keyboard
+* accessibility of widgets.</p>
+*
+* <p>
+* When designing widgets that manage a set of descendant controls (i.e. buttons
+* in a toolbar, tabs in a tablist, menuitems in a menu, etc.) it is important to
+* limit the number of descendants in the browser's default tab flow. The fewer
+* number of descendants in the default tab flow, the easier it is for keyboard
+* users to navigate between widgets by pressing the tab key. When a widget has
+* focus it should provide a set of shortcut keys (typically the arrow keys)
+* to move focus among its descendants.
+* </p>
+*
+* <p>
+* To this end, the Focus Manager Node Plugin makes it easy to define a Node's
+* focusable descendants, define which descendant should be in the default tab
+* flow, and define the keys that move focus among each descendant.
+* Additionally, as the CSS
+* <a href="http://www.w3.org/TR/CSS21/selector.html#x38"><code>:focus</code></a>
+* pseudo class is not supported on all elements in all
+* <a href="http://developer.yahoo.com/yui/articles/gbs/">A-Grade browsers</a>,
+* the Focus Manager Node Plugin provides an easy, cross-browser means of
+* styling focus.
+* </p>
+*
+* @module node-focusmanager
+*/
+
+ // Frequently used strings
+
+var ACTIVE_DESCENDANT = "activeDescendant",
+ ID = "id",
+ DISABLED = "disabled",
+ TAB_INDEX = "tabIndex",
+ FOCUSED = "focused",
+ FOCUS_CLASS = "focusClass",
+ CIRCULAR = "circular",
+ UI = "UI",
+ KEY = "key",
+ ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
+ HOST = "host",
+
+ // Collection of keys that, when pressed, cause the browser viewport
+ // to scroll.
+ scrollKeys = {
+ 37: true,
+ 38: true,
+ 39: true,
+ 40: true
+ },
+
+ clickableElements = {
+ "a": true,
+ "button": true,
+ "input": true,
+ "object": true
+ },
+
+ // Library shortcuts
+
+ Lang = Y.Lang,
+ UA = Y.UA,
+
+ /**
+ * The NodeFocusManager class is a plugin for a Node instance. The class is used
+ * via the <a href="Node.html#method_plug"><code>plug</code></a> method of Node
+ * and should not be instantiated directly.
+ * @namespace plugin
+ * @class NodeFocusManager
+ */
+ NodeFocusManager = function () {
+
+ NodeFocusManager.superclass.constructor.apply(this, arguments);
+
+ };
+
+
+NodeFocusManager.ATTRS = {
+
+ /**
+ * Boolean indicating that one of the descendants is focused.
+ *
+ * @attribute focused
+ * @readOnly
+ * @default false
+ * @type boolean
+ */
+ focused: {
+
+ value: false,
+ readOnly: true
+
+ },
+
+
+ /**
+ * String representing the CSS selector used to define the descendant Nodes
+ * whose focus should be managed.
+ *
+ * @attribute descendants
+ * @type Y.NodeList
+ */
+ descendants: {
+
+ getter: function (value) {
+
+ return this.get(HOST).all(value);
+
+ }
+
+ },
+
+
+ /**
+ * <p>Node, or index of the Node, representing the descendant that is either
+ * focused or is focusable (<code>tabIndex</code> attribute is set to 0).
+ * The value cannot represent a disabled descendant Node. Use a value of -1
+ * to remove all descendant Nodes from the default tab flow.
+ * If no value is specified, the active descendant will be inferred using
+ * the following criteria:</p>
+ * <ol>
+ * <li>Examining the <code>tabIndex</code> attribute of each descendant and
+ * using the first descendant whose <code>tabIndex</code> attribute is set
+ * to 0</li>
+ * <li>If no default can be inferred then the value is set to either 0 or
+ * the index of the first enabled descendant.</li>
+ * </ol>
+ *
+ * @attribute activeDescendant
+ * @type Number
+ */
+ activeDescendant: {
+
+ setter: function (value) {
+
+ var isNumber = Lang.isNumber,
+ INVALID_VALUE = Y.Attribute.INVALID_VALUE,
+ descendantsMap = this._descendantsMap,
+ descendants = this._descendants,
+ nodeIndex,
+ returnValue,
+ oNode;
+
+
+ if (isNumber(value)) {
+ nodeIndex = value;
+ returnValue = nodeIndex;
+ }
+ else if ((value instanceof Y.Node) && descendantsMap) {
+
+ nodeIndex = descendantsMap[value.get(ID)];
+
+ if (isNumber(nodeIndex)) {
+ returnValue = nodeIndex;
+ }
+ else {
+
+ // The user passed a reference to a Node that wasn't one
+ // of the descendants.
+ returnValue = INVALID_VALUE;
+
+ }
+
+ }
+ else {
+ returnValue = INVALID_VALUE;
+ }
+
+
+ if (descendants) {
+
+ oNode = descendants.item(nodeIndex);
+
+ if (oNode && oNode.get("disabled")) {
+
+ // Setting the "activeDescendant" attribute to the index
+ // of a disabled descendant is invalid.
+ returnValue = INVALID_VALUE;
+
+ }
+
+ }
+
+ return returnValue;
+
+ }
+
+ },
+
+
+ /**
+ * Object literal representing the keys to be used to navigate between the
+ * next/previous descendant. The format for the attribute's value is
+ * <code>{ next: "down:40", previous: "down:38" }</code>. The value for the
+ * "next" and "previous" properties are used to attach
+ * <a href="event/#keylistener"><code>key</code></a> event listeners. See
+ * the <a href="event/#keylistener">Using the key Event</a> section of
+ * the Event documentation for more information on "key" event listeners.
+ *
+ * @attribute keys
+ * @type Object
+ */
+ keys: {
+
+ value: {
+
+ next: null,
+ previous: null
+
+ }
+
+
+ },
+
+
+ /**
+ * String representing the name of class applied to the focused active
+ * descendant Node. Can also be an object literal used to define both the
+ * class name, and the Node to which the class should be applied. If using
+ * an object literal, the format is:
+ * <code>{ className: "focus", fn: myFunction }</code>. The function
+ * referenced by the <code>fn</code> property in the object literal will be
+ * passed a reference to the currently focused active descendant Node.
+ *
+ * @attribute focusClass
+ * @type String|Object
+ */
+ focusClass: { },
+
+
+ /**
+ * Boolean indicating if focus should be set to the first/last descendant
+ * when the end or beginning of the descendants has been reached.
+ *
+ * @attribute circular
+ * @type Boolean
+ */
+ circular: {
+ value: true
+ }
+
+};
+
+Y.extend(NodeFocusManager, Y.Plugin.Base, {
+
+ // Protected properties
+
+ // Boolean indicating if the NodeFocusManager is active.
+ _stopped: true,
+
+ // NodeList representing the descendants selected via the
+ // "descendants" attribute.
+ _descendants: null,
+
+ // Object literal mapping the IDs of each descendant to its index in the
+ // "_descendants" NodeList.
+ _descendantsMap: null,
+
+ // Reference to the Node instance to which the focused class (defined
+ // by the "focusClass" attribute) is currently applied.
+ _focusedNode: null,
+
+ // Number representing the index of the last descendant Node.
+ _lastNodeIndex: 0,
+
+ // Array of handles for event handlers used for a NodeFocusManager instance.
+ _eventHandlers: null,
+
+
+
+ // Protected methods
+
+ /**
+ * @method _initDescendants
+ * @description Sets the <code>tabIndex</code> attribute of all of the
+ * descendants to -1, except the active descendant, whose
+ * <code>tabIndex</code> attribute is set to 0.
+ * @protected
+ */
+ _initDescendants: function () {
+
+ var descendants = this.get("descendants"),
+ descendantsMap = {},
+ nFirstEnabled = -1,
+ nDescendants,
+ nActiveDescendant = this.get(ACTIVE_DESCENDANT),
+ oNode,
+ sID,
+ i = 0;
+
+
+
+ if (Lang.isUndefined(nActiveDescendant)) {
+ nActiveDescendant = -1;
+ }
+
+
+ if (descendants) {
+
+ nDescendants = descendants.size();
+
+
+ if (nDescendants > 1) {
+
+ for (i = 0; i < nDescendants; i++) {
+
+ oNode = descendants.item(i);
+
+ if (nFirstEnabled === -1 && !oNode.get(DISABLED)) {
+ nFirstEnabled = i;
+ }
+
+
+ // If the user didn't specify a value for the
+ // "activeDescendant" attribute try to infer it from
+ // the markup.
+
+ // Need to pass "2" when using "getAttribute" for IE to get
+ // the attribute value as it is set in the markup.
+ // Need to use "parseInt" because IE always returns the
+ // value as a number, whereas all other browsers return
+ // the attribute as a string when accessed
+ // via "getAttribute".
+
+ if (nActiveDescendant < 0 &&
+ parseInt(oNode.getAttribute(TAB_INDEX, 2), 10) === 0) {
+
+ nActiveDescendant = i;
+
+ }
+
+ oNode.set(TAB_INDEX, -1);
+
+ sID = oNode.get(ID);
+
+ if (!sID) {
+ sID = Y.guid();
+ oNode.set(ID, sID);
+ }
+
+ descendantsMap[sID] = i;
+
+ }
+
+
+ // If the user didn't specify a value for the
+ // "activeDescendant" attribute and no default value could be
+ // determined from the markup, then default to 0.
+
+ if (nActiveDescendant < 0) {
+ nActiveDescendant = 0;
+ }
+
+
+ oNode = descendants.item(nActiveDescendant);
+
+ // Check to make sure the active descendant isn't disabled,
+ // and fall back to the first enabled descendant if it is.
+
+ if (!oNode || oNode.get(DISABLED)) {
+ oNode = descendants.item(nFirstEnabled);
+ nActiveDescendant = nFirstEnabled;
+ }
+
+ this._lastNodeIndex = nDescendants - 1;
+ this._descendants = descendants;
+ this._descendantsMap = descendantsMap;
+
+ this.set(ACTIVE_DESCENDANT, nActiveDescendant);
+
+ // Need to set the "tabIndex" attribute here, since the
+ // "activeDescendantChange" event handler used to manage
+ // the setting of the "tabIndex" attribute isn't wired up yet.
+
+ oNode.set(TAB_INDEX, 0);
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _isDescendant
+ * @description Determines if the specified Node instance is a descendant
+ * managed by the Focus Manager.
+ * @param node {Node} Node instance to be checked.
+ * @return {Boolean} Boolean indicating if the specified Node instance is a
+ * descendant managed by the Focus Manager.
+ * @protected
+ */
+ _isDescendant: function (node) {
+
+ return (node.get(ID) in this._descendantsMap);
+
+ },
+
+
+ /**
+ * @method _removeFocusClass
+ * @description Removes the class name representing focus (as specified by
+ * the "focusClass" attribute) from the Node instance to which it is
+ * currently applied.
+ * @protected
+ */
+ _removeFocusClass: function () {
+
+ var oFocusedNode = this._focusedNode,
+ focusClass = this.get(FOCUS_CLASS),
+ sClassName;
+
+ if (focusClass) {
+ sClassName = Lang.isString(focusClass) ?
+ focusClass : focusClass.className;
+ }
+
+ if (oFocusedNode && sClassName) {
+ oFocusedNode.removeClass(sClassName);
+ }
+
+ },
+
+
+ /**
+ * @method _detachKeyHandler
+ * @description Detaches the "key" event handlers used to support the "keys"
+ * attribute.
+ * @protected
+ */
+ _detachKeyHandler: function () {
+
+ var prevKeyHandler = this._prevKeyHandler,
+ nextKeyHandler = this._nextKeyHandler;
+
+ if (prevKeyHandler) {
+ prevKeyHandler.detach();
+ }
+
+ if (nextKeyHandler) {
+ nextKeyHandler.detach();
+ }
+
+ },
+
+
+ /**
+ * @method _preventScroll
+ * @description Prevents the viewport from scolling when the user presses
+ * the up, down, left, or right key.
+ * @protected
+ */
+ _preventScroll: function (event) {
+
+ if (scrollKeys[event.keyCode]) {
+ event.preventDefault();
+ }
+
+ },
+
+
+ /**
+ * @method _preventScroll
+ * @description Fires the click event if the enter key is pressed while
+ * focused on an HTML element that is not natively clickable.
+ * @protected
+ */
+ _fireClick: function (event) {
+
+ var oTarget = event.target,
+ sNodeName = oTarget.get("nodeName").toLowerCase();
+
+ if (event.keyCode === 13 && (!clickableElements[sNodeName] ||
+ (sNodeName === "a" && !oTarget.getAttribute("href")))) {
+
+
+ oTarget.simulate("click");
+
+ }
+
+ },
+
+
+ /**
+ * @method _attachKeyHandler
+ * @description Attaches the "key" event handlers used to support the "keys"
+ * attribute.
+ * @protected
+ */
+ _attachKeyHandler: function () {
+
+ this._detachKeyHandler();
+
+ var sNextKey = this.get("keys.next"),
+ sPrevKey = this.get("keys.previous"),
+ oNode = this.get(HOST),
+ aHandlers = this._eventHandlers;
+
+ if (sPrevKey) {
+ this._prevKeyHandler =
+ Y.on(KEY, Y.bind(this._focusPrevious, this), oNode, sPrevKey);
+ }
+
+ if (sNextKey) {
+ this._nextKeyHandler =
+ Y.on(KEY, Y.bind(this._focusNext, this), oNode, sNextKey);
+ }
+
+
+ // In Opera it is necessary to call the "preventDefault" method in
+ // response to the user pressing the arrow keys in order to prevent
+ // the viewport from scrolling when the user is moving focus among
+ // the focusable descendants.
+
+ if (UA.opera) {
+ aHandlers.push(oNode.on("keypress", this._preventScroll, this));
+ }
+
+
+ // For all browsers except Opera: HTML elements that are not natively
+ // focusable but made focusable via the tabIndex attribute don't
+ // fire a click event when the user presses the enter key. It is
+ // possible to work around this problem by simplying dispatching a
+ // click event in response to the user pressing the enter key.
+
+ if (!UA.opera) {
+ aHandlers.push(oNode.on("keypress", this._fireClick, this));
+ }
+
+ },
+
+
+ /**
+ * @method _detachEventHandlers
+ * @description Detaches all event handlers used by the Focus Manager.
+ * @protected
+ */
+ _detachEventHandlers: function () {
+
+ this._detachKeyHandler();
+
+ var aHandlers = this._eventHandlers;
+
+ if (aHandlers) {
+
+ Y.Array.each(aHandlers, function (handle) {
+ handle.detach();
+ });
+
+ this._eventHandlers = null;
+
+ }
+
+ },
+
+
+ /**
+ * @method _detachEventHandlers
+ * @description Attaches all event handlers used by the Focus Manager.
+ * @protected
+ */
+ _attachEventHandlers: function () {
+
+ var descendants = this._descendants,
+ aHandlers,
+ oDocument,
+ handle;
+
+ if (descendants && descendants.size() > 1) {
+
+ aHandlers = this._eventHandlers || [];
+ oDocument = this.get(HOST).get("ownerDocument");
+
+
+ if (aHandlers.length === 0) {
+
+
+ aHandlers.push(oDocument.on("focus", this._onDocFocus, this));
+
+ aHandlers.push(oDocument.on("mousedown",
+ this._onDocMouseDown, this));
+
+ aHandlers.push(
+ this.after("keysChange", this._attachKeyHandler));
+
+ aHandlers.push(
+ this.after("descendantsChange", this._initDescendants));
+
+ aHandlers.push(
+ this.after(ACTIVE_DESCENDANT_CHANGE,
+ this._afterActiveDescendantChange));
+
+
+ // For performance: defer attaching all key-related event
+ // handlers until the first time one of the specified
+ // descendants receives focus.
+
+ handle = this.after("focusedChange", Y.bind(function (event) {
+
+ if (event.newVal) {
+
+
+ this._attachKeyHandler();
+
+ // Detach this "focusedChange" handler so that the
+ // key-related handlers only get attached once.
+
+ handle.detach();
+
+ }
+
+ }, this));
+
+ aHandlers.push(handle);
+
+ }
+
+
+ this._eventHandlers = aHandlers;
+
+ }
+
+ },
+
+
+ // Protected event handlers
+
+ /**
+ * @method _onDocMouseDown
+ * @description "mousedown" event handler for the owner document of the
+ * Focus Manager's Node.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ */
+ _onDocMouseDown: function (event) {
+
+ var oHost = this.get(HOST),
+ oTarget = event.target,
+ bChildNode = oHost.contains(oTarget),
+ node,
+
+ getFocusable = function (node) {
+
+ var returnVal = false;
+
+ if (!node.compareTo(oHost)) {
+
+ returnVal = this._isDescendant(node) ? node :
+ getFocusable.call(this, node.get("parentNode"));
+
+ }
+
+ return returnVal;
+
+ };
+
+
+ if (bChildNode) {
+
+ // Check to make sure that the target isn't a child node of one
+ // of the focusable descendants.
+
+ node = getFocusable.call(this, oTarget);
+
+ if (node) {
+ oTarget = node;
+ }
+ else if (!node && this.get(FOCUSED)) {
+
+ // The target was a non-focusable descendant of the root
+ // node, so the "focused" attribute should be set to false.
+
+ this._set(FOCUSED, false);
+ this._onDocFocus(event);
+
+ }
+
+ }
+
+
+ if (bChildNode && this._isDescendant(oTarget)) {
+
+ // Fix general problem in Webkit: mousing down on a button or an
+ // anchor element doesn't focus it.
+
+ // For all browsers: makes sure that the descendant that
+ // was the target of the mousedown event is now considered the
+ // active descendant.
+
+ this.focus(oTarget);
+ }
+ else if (UA.webkit && this.get(FOCUSED) &&
+ (!bChildNode || (bChildNode && !this._isDescendant(oTarget)))) {
+
+ // Fix for Webkit:
+
+ // Document doesn't receive focus in Webkit when the user mouses
+ // down on it, so the "focused" attribute won't get set to the
+ // correct value.
+
+ // The goal is to force a blur if the user moused down on
+ // either: 1) A descendant node, but not one that managed by
+ // the FocusManager, or 2) an element outside of the
+ // FocusManager
+
+ this._set(FOCUSED, false);
+ this._onDocFocus(event);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onDocFocus
+ * @description "focus" event handler for the owner document of the
+ * Focus Manager's Node.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ */
+ _onDocFocus: function (event) {
+
+ var oTarget = this._focusTarget || event.target,
+ bFocused = this.get(FOCUSED),
+ focusClass = this.get(FOCUS_CLASS),
+ oFocusedNode = this._focusedNode,
+ bInCollection;
+
+ if (this._focusTarget) {
+ this._focusTarget = null;
+ }
+
+
+ if (this.get(HOST).contains(oTarget)) {
+
+ // The target is a descendant of the root Node.
+
+ bInCollection = this._isDescendant(oTarget);
+
+ if (!bFocused && bInCollection) {
+
+ // The user has focused a focusable descendant.
+
+ bFocused = true;
+
+ }
+ else if (bFocused && !bInCollection) {
+
+ // The user has focused a child of the root Node that is
+ // not one of the descendants managed by this Focus Manager
+ // so clear the currently focused descendant.
+
+ bFocused = false;
+
+ }
+
+ }
+ else {
+
+ // The target is some other node in the document.
+
+ bFocused = false;
+
+ }
+
+
+ if (focusClass) {
+
+ if (oFocusedNode && (!oFocusedNode.compareTo(oTarget) || !bFocused)) {
+ this._removeFocusClass();
+ }
+
+ if (bInCollection && bFocused) {
+
+ if (focusClass.fn) {
+ oTarget = focusClass.fn(oTarget);
+ oTarget.addClass(focusClass.className);
+ }
+ else {
+ oTarget.addClass(focusClass);
+ }
+
+ this._focusedNode = oTarget;
+
+ }
+
+ }
+
+
+ this._set(FOCUSED, bFocused);
+
+ },
+
+
+ /**
+ * @method _focusNext
+ * @description Keydown event handler that moves focus to the next
+ * enabled descendant.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ * @param activeDescendant {Number} Number representing the index of the
+ * next descendant to be focused
+ */
+ _focusNext: function (event, activeDescendant) {
+
+ var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
+ oNode;
+
+
+ if (this._isDescendant(event.target) &&
+ (nActiveDescendant <= this._lastNodeIndex)) {
+
+ nActiveDescendant = nActiveDescendant + 1;
+
+ if (nActiveDescendant === (this._lastNodeIndex + 1) &&
+ this.get(CIRCULAR)) {
+
+ nActiveDescendant = 0;
+
+ }
+
+ oNode = this._descendants.item(nActiveDescendant);
+
+ if (oNode.get(DISABLED)) {
+ this._focusNext(event, nActiveDescendant);
+ }
+ else {
+ this.focus(nActiveDescendant);
+ }
+
+ }
+
+ this._preventScroll(event);
+
+ },
+
+
+ /**
+ * @method _focusPrevious
+ * @description Keydown event handler that moves focus to the previous
+ * enabled descendant.
+ * @protected
+ * @param event {Object} Object representing the DOM event.
+ * @param activeDescendant {Number} Number representing the index of the
+ * next descendant to be focused.
+ */
+ _focusPrevious: function (event, activeDescendant) {
+
+ var nActiveDescendant = activeDescendant || this.get(ACTIVE_DESCENDANT),
+ oNode;
+
+ if (this._isDescendant(event.target) && nActiveDescendant >= 0) {
+
+ nActiveDescendant = nActiveDescendant - 1;
+
+ if (nActiveDescendant === -1 && this.get(CIRCULAR)) {
+ nActiveDescendant = this._lastNodeIndex;
+ }
+
+ oNode = this._descendants.item(nActiveDescendant);
+
+ if (oNode.get(DISABLED)) {
+ this._focusPrevious(event, nActiveDescendant);
+ }
+ else {
+ this.focus(nActiveDescendant);
+ }
+
+ }
+
+ this._preventScroll(event);
+
+ },
+
+
+ /**
+ * @method _afterActiveDescendantChange
+ * @description afterChange event handler for the
+ * "activeDescendant" attribute.
+ * @protected
+ * @param event {Object} Object representing the change event.
+ */
+ _afterActiveDescendantChange: function (event) {
+
+ var oNode = this._descendants.item(event.prevVal);
+
+ if (oNode) {
+ oNode.set(TAB_INDEX, -1);
+ }
+
+ oNode = this._descendants.item(event.newVal);
+
+ if (oNode) {
+ oNode.set(TAB_INDEX, 0);
+ }
+
+ },
+
+
+
+ // Public methods
+
+ initializer: function (config) {
+
+ this.start();
+
+ },
+
+ destructor: function () {
+
+ this.stop();
+ this.get(HOST).focusManager = null;
+
+ },
+
+
+ /**
+ * @method focus
+ * @description Focuses the active descendant and sets the
+ * <code>focused</code> attribute to true.
+ * @param index {Number} Optional. Number representing the index of the
+ * descendant to be set as the active descendant.
+ * @param index {Node} Optional. Node instance representing the
+ * descendant to be set as the active descendant.
+ */
+ focus: function (index) {
+
+ if (Lang.isUndefined(index)) {
+ index = this.get(ACTIVE_DESCENDANT);
+ }
+
+ this.set(ACTIVE_DESCENDANT, index, { src: UI });
+
+ var oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
+
+ if (oNode) {
+
+ oNode.focus();
+
+ // In Opera focusing a <BUTTON> element programmatically
+ // will result in the document-level focus event handler
+ // "_onDocFocus" being called, resulting in the handler
+ // incorrectly setting the "focused" Attribute to false. To fix
+ // this, set a flag ("_focusTarget") that the "_onDocFocus" method
+ // can look for to properly handle this edge case.
+
+ if (UA.opera && oNode.get("nodeName").toLowerCase() === "button") {
+ this._focusTarget = oNode;
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method blur
+ * @description Blurs the current active descendant and sets the
+ * <code>focused</code> attribute to false.
+ */
+ blur: function () {
+
+ var oNode;
+
+ if (this.get(FOCUSED)) {
+
+ oNode = this._descendants.item(this.get(ACTIVE_DESCENDANT));
+
+ if (oNode) {
+
+ oNode.blur();
+
+ // For Opera and Webkit: Blurring an element in either browser
+ // doesn't result in another element (such as the document)
+ // being focused. Therefore, the "_onDocFocus" method
+ // responsible for managing the application and removal of the
+ // focus indicator class name is never called.
+
+ this._removeFocusClass();
+
+ }
+
+ this._set(FOCUSED, false, { src: UI });
+ }
+
+ },
+
+
+ /**
+ * @method start
+ * @description Enables the Focus Manager.
+ */
+ start: function () {
+
+ if (this._stopped) {
+
+ this._initDescendants();
+ this._attachEventHandlers();
+
+ this._stopped = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method stop
+ * @description Disables the Focus Manager by detaching all event handlers.
+ */
+ stop: function () {
+
+ if (!this._stopped) {
+
+ this._detachEventHandlers();
+
+ this._descendants = null;
+ this._focusedNode = null;
+ this._lastNodeIndex = 0;
+ this._stopped = true;
+
+ }
+
+ },
+
+
+ /**
+ * @method refresh
+ * @description Refreshes the Focus Manager's descendants by re-executing the
+ * CSS selector query specified by the <code>descendants</code> attribute.
+ */
+ refresh: function () {
+
+ this._initDescendants();
+
+ }
+
+});
+
+
+NodeFocusManager.NAME = "nodeFocusManager";
+NodeFocusManager.NS = "focusManager";
+
+Y.namespace("Plugin");
+Y.Plugin.NodeFocusManager = NodeFocusManager;
+
+
+}, '3.0.0' ,{requires:['attribute', 'node', 'plugin', 'node-event-simulate', 'event-key', 'event-focus']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-menu .yui-menu {
+
+ position: absolute;
+ z-index: 1;
+
+}
+
+
+.yui-menu .yui-shim {
+
+ /*
+ Styles for the <iframe> shim used to prevent <select> elements from poking through
+ submenus in IE < 7. Note: For peformance, creation of the <iframe> shim for each submenu
+ is deferred until it is initially made visible by the user.
+ */
+
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: -1;
+ opacity: 0;
+ filter: alpha(opacity=0); /* For IE since it doesn't implement the CSS3 "opacity" property. */
+ border: none;
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ width: 100%;
+
+}
+
+.yui-menu-hidden {
+
+ /*
+ Position hidden menus outside the viewport boundaries to prevent them from
+ triggering scrollbars on the viewport.
+ */
+
+ top: -10000px;
+ left: -10000px;
+
+ /*
+ Using "visibility:hidden" over "display" none because:
+
+ 1) As the "position" property for submenus is set to "absolute", they are out of
+ the document flow and take up no space. Therefore, from that perspective use of
+ "display:none" is redundant.
+
+ 2) According to MSDN use of "display:none" is more expensive:
+ "Display is the more expensive of the two CSS properties, so if you are
+ making elements appear and disappear often, visibility will be faster."
+ (See http://msdn.microsoft.com/en-us/library/bb264005(VS.85).aspx)
+ */
+
+ visibility: hidden;
+
+}
+
+.yui-menu li {
+
+ list-style-type: none;
+
+}
+
+.yui-menu ul,
+.yui-menu li {
+
+ margin: 0;
+ padding: 0;
+
+}
+
+.yui-menu-label,
+.yui-menuitem-content {
+
+ text-align: left;
+ white-space: nowrap;
+ display: block;
+
+}
+
+.yui-menu-horizontal li {
+
+ float: left;
+ width: auto;
+
+}
+
+.yui-menu-horizontal li li {
+
+ float: none;
+
+}
+
+.yui-menu-horizontal ul {
+
+ /*
+ Use of "zoom" sets the "hasLayout" property to "true" in IE (< 8). When "hasLayout" is
+ set to "true", an element can clear its floated descendents. For more:
+ http://msdn.microsoft.com/en-gb/library/ms533776(VS.85).aspx
+ */
+
+ *zoom: 1;
+
+}
+
+.yui-menu-horizontal ul ul {
+
+ /*
+ No need to clear <ul>s of submenus of horizontal menus since <li>s of submenus
+ aren't floated.
+ */
+
+ *zoom: normal;
+
+}
+
+.yui-menu-horizontal>.yui-menu-content>ul:after {
+
+ /* Self-clearing solution for Opera, Webkit, Gecko and IE > 7 */
+
+ content: "";
+ display: block;
+ clear: both;
+ line-height: 0;
+ font-size: 0;
+ visibility: hidden;
+
+}
+
+
+/*
+ The following two rules are for IE 7. Triggering "hasLayout" (via use of "zoom") prevents
+ first-tier submenus from hiding when the mouse is moving from an menu label in a root menu to
+ its corresponding submenu.
+*/
+
+.yui-menu-content {
+
+ *zoom: 1;
+
+}
+
+
+.yui-menu-hidden .yui-menu-content {
+
+ *zoom: normal;
+
+}
+
+
+/*
+ The following two rules are for IE 6 (Standards Mode and Quirks Mode) and IE 7 (Quirks Mode
+ only). Triggering "hasLayout" (via use of "zoom") fixes a bug in IE where mousing mousing off
+ the text node of menuitem or menu label will incorrectly trigger the mouseout event.
+*/
+
+.yui-menuitem-content,
+.yui-menu-label {
+
+ _zoom: 1;
+
+}
+
+.yui-menu-hiden .yui-menuitem-content,
+.yui-menu-hiden .yui-menu-label {
+
+ _zoom: normal;
+
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/* Vertical menus and submenus */
+
+.yui-skin-sam .yui-menu-content,
+.yui-skin-sam .yui-menu .yui-menu .yui-menu-content {
+
+ font-size: 93%; /* 12px */
+ line-height: 1.5; /* 18px */
+ *line-height: 1.45; /* For IE */
+ border: solid 1px #808080;
+ background: #fff;
+ padding: 3px 0;
+
+}
+
+.yui-skin-sam .yui-menu .yui-menu .yui-menu-content {
+
+ font-size: 100%;
+
+}
+
+
+/* Horizontal menus */
+
+.yui-skin-sam .yui-menu-horizontal .yui-menu-content {
+
+ line-height: 2; /* ~24px */
+ *line-height: 1.9; /* For IE */
+ background: url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0;
+ padding: 0;
+
+}
+
+
+.yui-skin-sam .yui-menu ul,
+.yui-skin-sam .yui-menu ul ul {
+
+ margin-top: 3px;
+ padding-top: 3px;
+ border-top: solid 1px #ccc;
+
+}
+
+.yui-skin-sam .yui-menu ul.first-of-type {
+
+ border: 0;
+ margin: 0;
+ padding: 0;
+
+}
+
+.yui-skin-sam .yui-menu-horizontal ul {
+
+ padding: 0;
+ margin: 0;
+ border: 0;
+
+}
+
+
+.yui-skin-sam .yui-menu li,
+.yui-skin-sam .yui-menu .yui-menu li {
+
+ /*
+ For and IE 6 (Strict Mode and Quirks Mode) and IE 7 (Quirks Mode only): Used to collapse
+ superfluous white space between <li> elements that is triggered by the "display" property
+ of the <a> elements being set to "block" by node-menunav-core.css file.
+ */
+
+ _border-bottom: solid 1px #fff;
+
+}
+
+.yui-skin-sam .yui-menu-horizontal li {
+
+ _border-bottom: 0;
+
+}
+
+.yui-skin-sam .yui-menubuttonnav li {
+
+ border-right: solid 1px #ccc;
+
+}
+
+.yui-skin-sam .yui-splitbuttonnav li {
+
+ border-right: solid 1px #808080;
+
+}
+
+.yui-skin-sam .yui-menubuttonnav li li,
+.yui-skin-sam .yui-splitbuttonnav li li {
+
+ border-right: 0;
+
+}
+
+
+/* Menuitems and menu labels */
+
+
+.yui-skin-sam .yui-menu-label,
+.yui-skin-sam .yui-menu .yui-menu .yui-menu-label,
+.yui-skin-sam .yui-menuitem-content,
+.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-content {
+
+ padding: 0 20px;
+ color: #000;
+ text-decoration: none;
+ cursor: default;
+
+ /*
+ Necessary specify values for border, position and margin to override values specified in
+ the selectors that follow.
+ */
+
+ float: none;
+ border: 0;
+ margin: 0;
+
+}
+
+.yui-skin-sam .yui-menu-horizontal .yui-menu-label,
+.yui-skin-sam .yui-menu-horizontal .yui-menuitem-content {
+
+ padding: 0 10px;
+ border-style: solid;
+ border-color: #808080;
+ border-width: 1px 0;
+ margin: -1px 0;
+
+ float: left; /* Ensures that menu labels clear floated descendents. Also gets negative
+ margins working in IE 7 (Strict Mode). */
+ width: auto;
+
+}
+
+.yui-skin-sam .yui-menu-label,
+.yui-skin-sam .yui-menu .yui-menu .yui-menu-label {
+
+ background: url(vertical-menu-submenu-indicator.png) right center no-repeat;
+
+}
+
+.yui-skin-sam .yui-menu-horizontal .yui-menu-label {
+
+ background: url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0;
+
+}
+
+.yui-skin-sam .yui-menubuttonnav .yui-menu-label,
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label {
+
+ background-image: none;
+
+}
+
+.yui-skin-sam .yui-menubuttonnav .yui-menu-label {
+
+ padding-right: 0;
+
+}
+
+.yui-skin-sam .yui-menubuttonnav .yui-menu-label em {
+
+ font-style: normal;
+ padding-right: 20px;
+ display: block;
+ background: url(horizontal-menu-submenu-indicator.png) right center no-repeat;
+
+}
+
+
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label {
+
+ padding: 0;
+
+}
+
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label a {
+
+ float: left;
+ width: auto;
+ color: #000;
+ text-decoration: none;
+ cursor: default;
+ padding: 0 5px 0 10px;
+
+}
+
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label .yui-menu-toggle {
+
+ padding: 0; /* Overide padding applied by the preceeding rule. */
+ border-left: solid 1px #ccc;
+ width: 15px;
+ overflow: hidden;
+ text-indent: -1000px;
+ background: url(horizontal-menu-submenu-indicator.png) 3px center no-repeat;
+
+}
+
+
+/* Selected menuitem */
+
+.yui-skin-sam .yui-menu-label-active,
+.yui-skin-sam .yui-menu-label-menuvisible,
+.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-active,
+.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-menuvisible {
+
+ background-color: #B3D4FF;
+
+}
+
+.yui-skin-sam .yui-menuitem-active .yui-menuitem-content,
+.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-active .yui-menuitem-content {
+
+ background-image: none;
+ background-color: #B3D4FF;
+
+ /*
+ Undo values set for "border-left-width" and "margin-left" when the root menu has a class of
+ "yui-menubuttonnav" or "yui-splitbuttonnav" applied.
+ */
+
+ border-left-width: 0;
+ margin-left: 0;
+
+}
+
+.yui-skin-sam .yui-menu-horizontal .yui-menu-label-active,
+.yui-skin-sam .yui-menu-horizontal .yui-menuitem-active .yui-menuitem-content,
+.yui-skin-sam .yui-menu-horizontal .yui-menu-label-menuvisible {
+
+ border-color: #7D98B8;
+ background: url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1700px;
+
+}
+
+.yui-skin-sam .yui-menubuttonnav .yui-menu-label-active,
+.yui-skin-sam .yui-menubuttonnav .yui-menuitem-active .yui-menuitem-content,
+.yui-skin-sam .yui-menubuttonnav .yui-menu-label-menuvisible,
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-active,
+.yui-skin-sam .yui-splitbuttonnav .yui-menuitem-active .yui-menuitem-content,
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible {
+
+ border-left-width: 1px;
+ margin-left: -1px;
+
+}
+
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible {
+
+ border-color: #808080;
+ background: transparent;
+
+}
+
+.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible .yui-menu-toggle {
+
+ border-color: #7D98B8;
+ background: url(horizontal-menu-submenu-toggle.png) left center no-repeat;
+
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-menu .yui-menu{position:absolute;z-index:1;}.yui-menu .yui-shim{position:absolute;top:0;left:0;z-index:-1;opacity:0;filter:alpha(opacity=0);border:none;margin:0;padding:0;height:100%;width:100%;}.yui-menu-hidden{top:-10000px;left:-10000px;visibility:hidden;}.yui-menu li{list-style-type:none;}.yui-menu ul,.yui-menu li{margin:0;padding:0;}.yui-menu-label,.yui-menuitem-content{text-align:left;white-space:nowrap;display:block;}.yui-menu-horizontal li{float:left;width:auto;}.yui-menu-horizontal li li{float:none;}.yui-menu-horizontal ul{*zoom:1;}.yui-menu-horizontal ul ul{*zoom:normal;}.yui-menu-horizontal>.yui-menu-content>ul:after{content:"";display:block;clear:both;line-height:0;font-size:0;visibility:hidden;}.yui-menu-content{*zoom:1;}.yui-menu-hidden .yui-menu-content{*zoom:normal;}.yui-menuitem-content,.yui-menu-label{_zoom:1;}.yui-menu-hiden .yui-menuitem-content,.yui-menu-hiden .yui-menu-label{_zoom:normal;}.yui-skin-sam .yui-menu-content,.yui-skin-sam .yui-menu .yui-menu .yui-menu-content{font-size:93%;line-height:1.5;*line-height:1.45;border:solid 1px #808080;background:#fff;padding:3px 0;}.yui-skin-sam .yui-menu .yui-menu .yui-menu-content{font-size:100%;}.yui-skin-sam .yui-menu-horizontal .yui-menu-content{line-height:2;*line-height:1.9;background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0;padding:0;}.yui-skin-sam .yui-menu ul,.yui-skin-sam .yui-menu ul ul{margin-top:3px;padding-top:3px;border-top:solid 1px #ccc;}.yui-skin-sam .yui-menu ul.first-of-type{border:0;margin:0;padding:0;}.yui-skin-sam .yui-menu-horizontal ul{padding:0;margin:0;border:0;}.yui-skin-sam .yui-menu li,.yui-skin-sam .yui-menu .yui-menu li{_border-bottom:solid 1px #fff;}.yui-skin-sam .yui-menu-horizontal li{_border-bottom:0;}.yui-skin-sam .yui-menubuttonnav li{border-right:solid 1px #ccc;}.yui-skin-sam .yui-splitbuttonnav li{border-right:solid 1px #808080;}.yui-skin-sam .yui-menubuttonnav li li,.yui-skin-sam .yui-splitbuttonnav li li{border-right:0;}.yui-skin-sam .yui-menu-label,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label,.yui-skin-sam .yui-menuitem-content,.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-content{padding:0 20px;color:#000;text-decoration:none;cursor:default;float:none;border:0;margin:0;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label,.yui-skin-sam .yui-menu-horizontal .yui-menuitem-content{padding:0 10px;border-style:solid;border-color:#808080;border-width:1px 0;margin:-1px 0;float:left;width:auto;}.yui-skin-sam .yui-menu-label,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label{background:url(vertical-menu-submenu-indicator.png) right center no-repeat;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label{background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 0;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label{background-image:none;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label{padding-right:0;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label em{font-style:normal;padding-right:20px;display:block;background:url(horizontal-menu-submenu-indicator.png) right center no-repeat;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label{padding:0;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label a{float:left;width:auto;color:#000;text-decoration:none;cursor:default;padding:0 5px 0 10px;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label .yui-menu-toggle{padding:0;border-left:solid 1px #ccc;width:15px;overflow:hidden;text-indent:-1000px;background:url(horizontal-menu-submenu-indicator.png) 3px center no-repeat;}.yui-skin-sam .yui-menu-label-active,.yui-skin-sam .yui-menu-label-menuvisible,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-active,.yui-skin-sam .yui-menu .yui-menu .yui-menu-label-menuvisible{background-color:#B3D4FF;}.yui-skin-sam .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menu .yui-menu .yui-menuitem-active .yui-menuitem-content{background-image:none;background-color:#B3D4FF;border-left-width:0;margin-left:0;}.yui-skin-sam .yui-menu-horizontal .yui-menu-label-active,.yui-skin-sam .yui-menu-horizontal .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menu-horizontal .yui-menu-label-menuvisible{border-color:#7D98B8;background:url(../../../../assets/skins/sam/sprite.png) repeat-x 0 -1700px;}.yui-skin-sam .yui-menubuttonnav .yui-menu-label-active,.yui-skin-sam .yui-menubuttonnav .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-menubuttonnav .yui-menu-label-menuvisible,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-active,.yui-skin-sam .yui-splitbuttonnav .yui-menuitem-active .yui-menuitem-content,.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible{border-left-width:1px;margin-left:-1px;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible{border-color:#808080;background:transparent;}.yui-skin-sam .yui-splitbuttonnav .yui-menu-label-menuvisible .yui-menu-toggle{border-color:#7D98B8;background:url(horizontal-menu-submenu-toggle.png) left center no-repeat;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-menunav', function(Y) {
+
+/**
+* <p>The MenuNav Node Plugin makes it easy to transform existing list-based
+* markup into traditional, drop down navigational menus that are both accessible
+* and easy to customize, and only require a small set of dependencies.</p>
+*
+*
+* <p>To use the MenuNav Node Plugin, simply pass a reference to the plugin to a
+* Node instance's <code>plug</code> method.</p>
+*
+* <p>
+* <code>
+* <script type="text/javascript"> <br>
+* <br>
+* // Call the "use" method, passing in "node-menunav". This will <br>
+* // load the script and CSS for the MenuNav Node Plugin and all of <br>
+* // the required dependencies. <br>
+* <br>
+* YUI().use("node-menunav", function(Y) { <br>
+* <br>
+* // Use the "contentready" event to initialize the menu when <br>
+* // the subtree of element representing the root menu <br>
+* // (<div id="menu-1">) is ready to be scripted. <br>
+* <br>
+* Y.on("contentready", function () { <br>
+* <br>
+* // The scope of the callback will be a Node instance <br>
+* // representing the root menu (<div id="menu-1">). <br>
+* // Therefore, since "this" represents a Node instance, it <br>
+* // is possible to just call "this.plug" passing in a <br>
+* // reference to the MenuNav Node Plugin. <br>
+* <br>
+* this.plug(Y.Plugin.NodeMenuNav); <br>
+* <br>
+* }, "#menu-1"); <br>
+* <br>
+* }); <br>
+* <br>
+* </script> <br>
+* </code>
+* </p>
+*
+* <p>The MenuNav Node Plugin has several configuration properties that can be
+* set via an object literal that is passed as a second argument to a Node
+* instance's <code>plug</code> method.
+* </p>
+*
+* <p>
+* <code>
+* <script type="text/javascript"> <br>
+* <br>
+* // Call the "use" method, passing in "node-menunav". This will <br>
+* // load the script and CSS for the MenuNav Node Plugin and all of <br>
+* // the required dependencies. <br>
+* <br>
+* YUI().use("node-menunav", function(Y) { <br>
+* <br>
+* // Use the "contentready" event to initialize the menu when <br>
+* // the subtree of element representing the root menu <br>
+* // (<div id="menu-1">) is ready to be scripted. <br>
+* <br>
+* Y.on("contentready", function () { <br>
+* <br>
+* // The scope of the callback will be a Node instance <br>
+* // representing the root menu (<div id="menu-1">). <br>
+* // Therefore, since "this" represents a Node instance, it <br>
+* // is possible to just call "this.plug" passing in a <br>
+* // reference to the MenuNav Node Plugin. <br>
+* <br>
+* this.plug(Y.Plugin.NodeMenuNav, { mouseOutHideDelay: 1000 });
+* <br><br>
+* }, "#menu-1"); <br>
+* <br>
+* }); <br>
+* <br>
+* </script> <br>
+* </code>
+* </p>
+*
+* @module node-menunav
+*/
+
+
+ // Util shortcuts
+
+var UA = Y.UA,
+ later = Y.later,
+ getClassName = Y.ClassNameManager.getClassName,
+
+
+
+ // Frequently used strings
+
+ MENU = "menu",
+ MENUITEM = "menuitem",
+ HIDDEN = "hidden",
+ PARENT_NODE = "parentNode",
+ CHILDREN = "children",
+ OFFSET_HEIGHT = "offsetHeight",
+ OFFSET_WIDTH = "offsetWidth",
+ PX = "px",
+ ID = "id",
+ PERIOD = ".",
+ HANDLED_MOUSEOUT = "handledMouseOut",
+ HANDLED_MOUSEOVER = "handledMouseOver",
+ ACTIVE = "active",
+ LABEL = "label",
+ LOWERCASE_A = "a",
+ MOUSEDOWN = "mousedown",
+ KEYDOWN = "keydown",
+ CLICK = "click",
+ EMPTY_STRING = "",
+ FIRST_OF_TYPE = "first-of-type",
+ ROLE = "role",
+ PRESENTATION = "presentation",
+ DESCENDANTS = "descendants",
+ UI = "UI",
+ ACTIVE_DESCENDANT = "activeDescendant",
+ USE_ARIA = "useARIA",
+ ARIA_HIDDEN = "aria-hidden",
+ CONTENT = "content",
+ HOST = "host",
+ ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
+
+ STANDARD_QUERY = ">.yui-menu-content>ul>li>a",
+ EXTENDED_QUERY = ">.yui-menu-content>ul>li>.yui-menu-label>a:first-child",
+
+
+ // Attribute keys
+
+ AUTO_SUBMENU_DISPLAY = "autoSubmenuDisplay",
+ MOUSEOUT_HIDE_DELAY = "mouseOutHideDelay",
+
+
+ // CSS class names
+
+ CSS_MENU = getClassName(MENU),
+ CSS_MENU_HIDDEN = getClassName(MENU, HIDDEN),
+ CSS_MENU_HORIZONTAL = getClassName(MENU, "horizontal"),
+ CSS_MENU_LABEL = getClassName(MENU, LABEL),
+ CSS_MENU_LABEL_ACTIVE = getClassName(MENU, LABEL, ACTIVE),
+ CSS_MENU_LABEL_MENUVISIBLE = getClassName(MENU, LABEL, (MENU + "visible")),
+ CSS_MENUITEM = getClassName(MENUITEM),
+ CSS_MENUITEM_ACTIVE = getClassName(MENUITEM, ACTIVE),
+
+
+ // CSS selectors
+
+ MENU_SELECTOR = PERIOD + CSS_MENU,
+ MENU_TOGGLE_SELECTOR = (PERIOD + getClassName(MENU, "toggle"));
+
+
+// Utility functions
+
+
+var getPreviousSibling = function (node) {
+
+ var oPrevious = node.previous(),
+ oChildren;
+
+ if (!oPrevious) {
+ oChildren = node.get(PARENT_NODE).get(CHILDREN);
+ oPrevious = oChildren.item(oChildren.size() - 1);
+ }
+
+ return oPrevious;
+
+};
+
+
+var getNextSibling = function (node) {
+
+ var oNext = node.next();
+
+ if (!oNext) {
+ oNext = node.get(PARENT_NODE).get(CHILDREN).item(0);
+ }
+
+ return oNext;
+
+};
+
+
+var isAnchor = function (node) {
+
+ var bReturnVal = false;
+
+ if (node) {
+ bReturnVal = node.get("nodeName").toLowerCase() === LOWERCASE_A;
+ }
+
+ return bReturnVal;
+
+};
+
+
+var isMenuItem = function (node) {
+
+ return node.hasClass(CSS_MENUITEM);
+
+};
+
+
+var isMenuLabel = function (node) {
+
+ return node.hasClass(CSS_MENU_LABEL);
+
+};
+
+
+var isHorizontalMenu = function (menu) {
+
+ return menu.hasClass(CSS_MENU_HORIZONTAL);
+
+};
+
+
+var hasVisibleSubmenu = function (menuLabel) {
+
+ return menuLabel.hasClass(CSS_MENU_LABEL_MENUVISIBLE);
+
+};
+
+
+var getItemAnchor = function (node) {
+
+ return isAnchor(node) ? node : node.one(LOWERCASE_A);
+
+};
+
+
+var getNodeWithClass = function (node, className, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+
+ if (node.hasClass(className)) {
+ oItem = node;
+ }
+
+ if (!oItem && searchAncestors) {
+ oItem = node.ancestor((PERIOD + className));
+ }
+
+ }
+
+ return oItem;
+
+};
+
+
+var getParentMenu = function (node) {
+
+ return node.ancestor(MENU_SELECTOR);
+
+};
+
+
+var getMenu = function (node, searchAncestors) {
+
+ return getNodeWithClass(node, CSS_MENU, searchAncestors);
+
+};
+
+
+var getMenuItem = function (node, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+ oItem = getNodeWithClass(node, CSS_MENUITEM, searchAncestors);
+ }
+
+ return oItem;
+
+};
+
+
+var getMenuLabel = function (node, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+
+ if (searchAncestors) {
+ oItem = getNodeWithClass(node, CSS_MENU_LABEL, searchAncestors);
+ }
+ else {
+ oItem = getNodeWithClass(node, CSS_MENU_LABEL) ||
+ node.one((PERIOD + CSS_MENU_LABEL));
+ }
+
+ }
+
+ return oItem;
+
+};
+
+
+var getItem = function (node, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+ oItem = getMenuItem(node, searchAncestors) ||
+ getMenuLabel(node, searchAncestors);
+ }
+
+ return oItem;
+
+};
+
+
+var getFirstItem = function (menu) {
+
+ return getItem(menu.one("li"));
+
+};
+
+
+var getActiveClass = function (node) {
+
+ return isMenuItem(node) ? CSS_MENUITEM_ACTIVE : CSS_MENU_LABEL_ACTIVE;
+
+};
+
+
+var handleMouseOverForNode = function (node, target) {
+
+ return node && !node[HANDLED_MOUSEOVER] &&
+ (node.compareTo(target) || node.contains(target));
+
+};
+
+
+var handleMouseOutForNode = function (node, relatedTarget) {
+
+ return node && !node[HANDLED_MOUSEOUT] &&
+ (!node.compareTo(relatedTarget) && !node.contains(relatedTarget));
+
+};
+
+/**
+* The NodeMenuNav class is a plugin for a Node instance. The class is used via
+* the <a href="Node.html#method_plug"><code>plug</code></a> method of Node and
+* should not be instantiated directly.
+* @namespace plugin
+* @class NodeMenuNav
+*/
+var NodeMenuNav = function () {
+
+ NodeMenuNav.superclass.constructor.apply(this, arguments);
+
+};
+
+NodeMenuNav.NAME = "nodeMenuNav";
+NodeMenuNav.NS = "menuNav";
+
+
+/**
+* @property NodeMenuNav.SHIM_TEMPLATE_TITLE
+* @description String representing the value for the <code>title</code>
+* attribute for the shim used to prevent <code><select></code> elements
+* from poking through menus in IE 6.
+* @default "Menu Stacking Shim"
+* @type String
+*/
+NodeMenuNav.SHIM_TEMPLATE_TITLE = "Menu Stacking Shim";
+
+
+/**
+* @property NodeMenuNav.SHIM_TEMPLATE
+* @description String representing the HTML used to create the
+* <code><iframe></code> shim used to prevent
+* <code><select></code> elements from poking through menus in IE 6.
+* @default "<iframe frameborder="0" tabindex="-1"
+* class="yui-shim" title="Menu Stacking Shim"
+* src="javascript:false;"></iframe>"
+* @type String
+*/
+
+// <iframe> shim notes:
+//
+// 1) Need to set the "frameBorder" property to 0 to suppress the default
+// <iframe> border in IE. (Setting the CSS "border" property alone doesn't
+// suppress it.)
+//
+// 2) The "src" attribute of the <iframe> is set to "javascript:false;" so
+// that it won't load a page inside it, preventing the secure/nonsecure
+// warning in IE when using HTTPS.
+//
+// 3) Since the role of the <iframe> shim is completely presentational, its
+// "tabindex" attribute is set to "-1" and its title attribute is set to
+// "Menu Stacking Shim". Both strategies help users of screen readers to
+// avoid mistakenly interacting with the <iframe> shim.
+
+NodeMenuNav.SHIM_TEMPLATE = '<iframe frameborder="0" tabindex="-1" class="' +
+ getClassName("shim") +
+ '" title="' + NodeMenuNav.SHIM_TEMPLATE_TITLE +
+ '" src="javascript:false;"></iframe>';
+
+
+NodeMenuNav.ATTRS = {
+
+ /**
+ * Boolean indicating if use of the WAI-ARIA Roles and States should be
+ * enabled for the menu.
+ *
+ * @attribute useARIA
+ * @readOnly
+ * @writeOnce
+ * @default true
+ * @type boolean
+ */
+ useARIA: {
+
+ value: true,
+ writeOnce: true,
+ lazyAdd: false,
+ setter: function (value) {
+
+ var oMenu = this.get(HOST),
+ oMenuLabel,
+ oMenuToggle,
+ oSubmenu,
+ sID;
+
+ if (value) {
+
+ oMenu.set(ROLE, MENU);
+
+ oMenu.all("ul,li,." + getClassName(MENU, CONTENT)).set(ROLE, PRESENTATION);
+
+ oMenu.all((PERIOD + getClassName(MENUITEM, CONTENT))).set(ROLE, MENUITEM);
+
+ oMenu.all((PERIOD + CSS_MENU_LABEL)).each(function (node) {
+
+ oMenuLabel = node;
+ oMenuToggle = node.one(MENU_TOGGLE_SELECTOR);
+
+ if (oMenuToggle) {
+ oMenuToggle.set(ROLE, PRESENTATION);
+ oMenuLabel = oMenuToggle.previous();
+ }
+
+ oMenuLabel.set(ROLE, MENUITEM);
+ oMenuLabel.set("aria-haspopup", true);
+
+ oSubmenu = node.next();
+
+ if (oSubmenu) {
+
+ oSubmenu.set(ROLE, MENU);
+
+ oMenuLabel = oSubmenu.previous();
+ oMenuToggle = oMenuLabel.one(MENU_TOGGLE_SELECTOR);
+
+ if (oMenuToggle) {
+ oMenuLabel = oMenuToggle;
+ }
+
+ sID = Y.stamp(oMenuLabel);
+
+ if (!oMenuLabel.get(ID)) {
+ oMenuLabel.set(ID, sID);
+ }
+
+ oSubmenu.set("aria-labelledby", sID);
+ oSubmenu.set(ARIA_HIDDEN, true);
+
+ }
+
+ });
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * Boolean indicating if submenus are automatically made visible when the
+ * user mouses over the menu's items.
+ *
+ * @attribute autoSubmenuDisplay
+ * @readOnly
+ * @writeOnce
+ * @default true
+ * @type boolean
+ */
+ autoSubmenuDisplay: {
+
+ value: true,
+ writeOnce: true
+
+ },
+
+
+ /**
+ * Number indicating the time (in milliseconds) that should expire before a
+ * submenu is made visible when the user mouses over the menu's label.
+ *
+ * @attribute submenuShowDelay
+ * @readOnly
+ * @writeOnce
+ * @default 250
+ * @type Number
+ */
+ submenuShowDelay: {
+
+ value: 250,
+ writeOnce: true
+
+ },
+
+
+ /**
+ * Number indicating the time (in milliseconds) that should expire before a
+ * submenu is hidden when the user mouses out of a menu label heading in the
+ * direction of a submenu.
+ *
+ * @attribute submenuHideDelay
+ * @readOnly
+ * @writeOnce
+ * @default 250
+ * @type Number
+ */
+ submenuHideDelay: {
+
+ value: 250,
+ writeOnce: true
+
+ },
+
+
+ /**
+ * Number indicating the time (in milliseconds) that should expire before a
+ * submenu is hidden when the user mouses out of it.
+ *
+ * @attribute mouseOutHideDelay
+ * @readOnly
+ * @writeOnce
+ * @default 750
+ * @type Number
+ */
+ mouseOutHideDelay: {
+
+ value: 750,
+ writeOnce: true
+
+ }
+
+};
+
+
+Y.extend(NodeMenuNav, Y.Plugin.Base, {
+
+ // Protected properties
+
+ /**
+ * @property _rootMenu
+ * @description Node instance representing the root menu in the menu.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _rootMenu: null,
+
+
+ /**
+ * @property _activeItem
+ * @description Node instance representing the menu's active descendent:
+ * the menuitem or menu label the user is currently interacting with.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _activeItem: null,
+
+
+ /**
+ * @property _activeMenu
+ * @description Node instance representing the menu that is the parent of
+ * the menu's active descendent.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _activeMenu: null,
+
+
+ /**
+ * @property _hasFocus
+ * @description Boolean indicating if the menu has focus.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _hasFocus: false,
+
+
+ // In gecko-based browsers a mouseover and mouseout event will fire even
+ // if a DOM element moves out from under the mouse without the user
+ // actually moving the mouse. This bug affects NodeMenuNav because the
+ // user can hit the Esc key to hide a menu, and if the mouse is over the
+ // menu when the user presses Esc, the _onMenuMouseOut handler will be
+ // called. To fix this bug the following flag (_blockMouseEvent) is used
+ // to block the code in the _onMenuMouseOut handler from executing.
+
+ /**
+ * @property _blockMouseEvent
+ * @description Boolean indicating whether or not to handle the
+ * "mouseover" event.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _blockMouseEvent: false,
+
+
+ /**
+ * @property _currentMouseX
+ * @description Number representing the current x coordinate of the mouse
+ * inside the menu.
+ * @default 0
+ * @protected
+ * @type Number
+ */
+ _currentMouseX: 0,
+
+
+ /**
+ * @property _movingToSubmenu
+ * @description Boolean indicating if the mouse is moving from a menu
+ * label to its corresponding submenu.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _movingToSubmenu: false,
+
+
+ /**
+ * @property _showSubmenuTimer
+ * @description Timer used to show a submenu.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _showSubmenuTimer: null,
+
+
+ /**
+ * @property _hideSubmenuTimer
+ * @description Timer used to hide a submenu.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _hideSubmenuTimer: null,
+
+
+ /**
+ * @property _hideAllSubmenusTimer
+ * @description Timer used to hide a all submenus.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _hideAllSubmenusTimer: null,
+
+
+ /**
+ * @property _firstItem
+ * @description Node instance representing the first item (menuitem or menu
+ * label) in the root menu of a menu.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _firstItem: null,
+
+
+ // Public methods
+
+
+ initializer: function (config) {
+
+ var menuNav = this,
+ oRootMenu = this.get(HOST),
+ aHandlers = [],
+ oDoc;
+
+
+ if (oRootMenu) {
+
+ menuNav._rootMenu = oRootMenu;
+
+ oRootMenu.all("ul:first-child").addClass(FIRST_OF_TYPE);
+
+ // Hide all visible submenus
+
+ oRootMenu.all(MENU_SELECTOR).addClass(CSS_MENU_HIDDEN);
+
+
+ // Wire up all event handlers
+
+ aHandlers.push(oRootMenu.on("mouseover", menuNav._onMouseOver, menuNav));
+ aHandlers.push(oRootMenu.on("mouseout", menuNav._onMouseOut, menuNav));
+ aHandlers.push(oRootMenu.on("mousemove", menuNav._onMouseMove, menuNav));
+ aHandlers.push(oRootMenu.on(MOUSEDOWN, menuNav._toggleSubmenuDisplay, menuNav));
+ aHandlers.push(Y.on("key", menuNav._toggleSubmenuDisplay, oRootMenu, "down:13", menuNav));
+ aHandlers.push(oRootMenu.on(CLICK, menuNav._toggleSubmenuDisplay, menuNav));
+ aHandlers.push(oRootMenu.on("keypress", menuNav._onKeyPress, menuNav));
+ aHandlers.push(oRootMenu.on(KEYDOWN, menuNav._onKeyDown, menuNav));
+
+ oDoc = oRootMenu.get("ownerDocument");
+
+ aHandlers.push(oDoc.on(MOUSEDOWN, menuNav._onDocMouseDown, menuNav));
+ aHandlers.push(oDoc.on("focus", menuNav._onDocFocus, menuNav));
+
+ this._eventHandlers = aHandlers;
+
+ menuNav._initFocusManager();
+
+ }
+
+
+ },
+
+ destructor: function () {
+
+ var aHandlers = this._eventHandlers;
+
+ if (aHandlers) {
+
+ Y.Array.each(aHandlers, function (handle) {
+ handle.detach();
+ });
+
+ this._eventHandlers = null;
+
+ }
+
+ this.get(HOST).unplug("focusManager");
+
+ },
+
+
+
+ // Protected methods
+
+ /**
+ * @method _isRoot
+ * @description Returns a boolean indicating if the specified menu is the
+ * root menu in the menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @return {Boolean} Boolean indicating if the specified menu is the root
+ * menu in the menu.
+ */
+ _isRoot: function (menu) {
+
+ return this._rootMenu.compareTo(menu);
+
+ },
+
+
+ /**
+ * @method _getTopmostSubmenu
+ * @description Returns the topmost submenu of a submenu hierarchy.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @return {Node} Node instance representing a menu.
+ */
+ _getTopmostSubmenu: function (menu) {
+
+ var menuNav = this,
+ oMenu = getParentMenu(menu),
+ returnVal;
+
+
+ if (!oMenu) {
+ returnVal = menu;
+ }
+ else if (menuNav._isRoot(oMenu)) {
+ returnVal = menu;
+ }
+ else {
+ returnVal = menuNav._getTopmostSubmenu(oMenu);
+ }
+
+ return returnVal;
+
+ },
+
+
+ /**
+ * @method _clearActiveItem
+ * @description Clears the menu's active descendent.
+ * @protected
+ */
+ _clearActiveItem: function () {
+
+ var menuNav = this,
+ oActiveItem = menuNav._activeItem;
+
+ if (oActiveItem) {
+ oActiveItem.removeClass(getActiveClass(oActiveItem));
+ }
+
+ menuNav._activeItem = null;
+
+ },
+
+
+ /**
+ * @method _setActiveItem
+ * @description Sets the specified menuitem or menu label as the menu's
+ * active descendent.
+ * @protected
+ * @param {Node} item Node instance representing a menuitem or menu label.
+ */
+ _setActiveItem: function (item) {
+
+ var menuNav = this;
+
+ if (item) {
+
+ menuNav._clearActiveItem();
+
+ item.addClass(getActiveClass(item));
+
+ menuNav._activeItem = item;
+
+ }
+
+ },
+
+
+ /**
+ * @method _focusItem
+ * @description Focuses the specified menuitem or menu label.
+ * @protected
+ * @param {Node} item Node instance representing a menuitem or menu label.
+ */
+ _focusItem: function (item) {
+
+ var menuNav = this,
+ oMenu,
+ oItem;
+
+ if (item && menuNav._hasFocus) {
+
+ oMenu = getParentMenu(item);
+ oItem = getItemAnchor(item);
+
+ if (oMenu && !oMenu.compareTo(menuNav._activeMenu)) {
+ menuNav._activeMenu = oMenu;
+ menuNav._initFocusManager();
+ }
+
+ menuNav._focusManager.focus(oItem);
+
+ }
+
+ },
+
+
+ /**
+ * @method _showMenu
+ * @description Shows the specified menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ */
+ _showMenu: function (menu) {
+
+ var oParentMenu = getParentMenu(menu),
+ oLI = menu.get(PARENT_NODE),
+ aXY = oLI.getXY();
+
+
+ if (this.get(USE_ARIA)) {
+ menu.set(ARIA_HIDDEN, false);
+ }
+
+
+ if (isHorizontalMenu(oParentMenu)) {
+ aXY[1] = aXY[1] + oLI.get(OFFSET_HEIGHT);
+ }
+ else {
+ aXY[0] = aXY[0] + oLI.get(OFFSET_WIDTH);
+ }
+
+ menu.setXY(aXY);
+
+ if (UA.ie < 8) {
+
+ if (UA.ie === 6 && !menu.hasIFrameShim) {
+
+ menu.appendChild(Y.Node.create(NodeMenuNav.SHIM_TEMPLATE));
+ menu.hasIFrameShim = true;
+
+ }
+
+ // Clear previous values for height and width
+
+ menu.setStyles({ height: EMPTY_STRING, width: EMPTY_STRING });
+
+ // Set the width and height of the menu's bounding box - this is
+ // necessary for IE 6 so that the CSS for the <iframe> shim can
+ // simply set the <iframe>'s width and height to 100% to ensure
+ // that dimensions of an <iframe> shim are always sync'd to the
+ // that of its parent menu. Specifying a width and height also
+ // helps when positioning decorator elements (for creating effects
+ // like rounded corners) inside a menu's bounding box in IE 7.
+
+ menu.setStyles({
+ height: (menu.get(OFFSET_HEIGHT) + PX),
+ width: (menu.get(OFFSET_WIDTH) + PX) });
+
+ }
+
+ menu.previous().addClass(CSS_MENU_LABEL_MENUVISIBLE);
+ menu.removeClass(CSS_MENU_HIDDEN);
+
+ },
+
+
+ /**
+ * @method _hideMenu
+ * @description Hides the specified menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @param {Boolean} activateAndFocusLabel Boolean indicating if the label
+ * for the specified
+ * menu should be focused and set as active.
+ */
+ _hideMenu: function (menu, activateAndFocusLabel) {
+
+ var menuNav = this,
+ oLabel = menu.previous(),
+ oActiveItem;
+
+ oLabel.removeClass(CSS_MENU_LABEL_MENUVISIBLE);
+
+
+ if (activateAndFocusLabel) {
+ menuNav._focusItem(oLabel);
+ menuNav._setActiveItem(oLabel);
+ }
+
+ oActiveItem = menu.one((PERIOD + CSS_MENUITEM_ACTIVE));
+
+ if (oActiveItem) {
+ oActiveItem.removeClass(CSS_MENUITEM_ACTIVE);
+ }
+
+ // Clear the values for top and left that were set by the call to
+ // "setXY" when the menu was shown so that the hidden position
+ // specified in the core CSS file will take affect.
+
+ menu.setStyles({ left: EMPTY_STRING, top: EMPTY_STRING });
+
+ menu.addClass(CSS_MENU_HIDDEN);
+
+ if (menuNav.get(USE_ARIA)) {
+ menu.set(ARIA_HIDDEN, true);
+ }
+
+ },
+
+
+ /**
+ * @method _hideAllSubmenus
+ * @description Hides all submenus of the specified menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ */
+ _hideAllSubmenus: function (menu) {
+
+ var menuNav = this;
+
+ menu.all(MENU_SELECTOR).each(Y.bind(function (submenuNode) {
+
+ menuNav._hideMenu(submenuNode);
+
+ }, menuNav));
+
+ },
+
+
+ /**
+ * @method _cancelShowSubmenuTimer
+ * @description Cancels the timer used to show a submenu.
+ * @protected
+ */
+ _cancelShowSubmenuTimer: function () {
+
+ var menuNav = this,
+ oShowSubmenuTimer = menuNav._showSubmenuTimer;
+
+ if (oShowSubmenuTimer) {
+ oShowSubmenuTimer.cancel();
+ menuNav._showSubmenuTimer = null;
+ }
+
+ },
+
+
+ /**
+ * @method _cancelHideSubmenuTimer
+ * @description Cancels the timer used to hide a submenu.
+ * @protected
+ */
+ _cancelHideSubmenuTimer: function () {
+
+ var menuNav = this,
+ oHideSubmenuTimer = menuNav._hideSubmenuTimer;
+
+
+ if (oHideSubmenuTimer) {
+ oHideSubmenuTimer.cancel();
+ menuNav._hideSubmenuTimer = null;
+ }
+
+ },
+
+
+ /**
+ * @method _initFocusManager
+ * @description Initializes and updates the Focus Manager so that is is
+ * always managing descendants of the active menu.
+ * @protected
+ */
+ _initFocusManager: function () {
+
+ var menuNav = this,
+ oRootMenu = menuNav._rootMenu,
+ oMenu = menuNav._activeMenu || oRootMenu,
+ sSelectorBase =
+ menuNav._isRoot(oMenu) ? EMPTY_STRING : ("#" + oMenu.get("id")),
+ oFocusManager = menuNav._focusManager,
+ sKeysVal,
+ sDescendantSelector,
+ sQuery;
+
+ if (isHorizontalMenu(oMenu)) {
+
+ sDescendantSelector = sSelectorBase + STANDARD_QUERY + "," +
+ sSelectorBase + EXTENDED_QUERY;
+
+ sKeysVal = { next: "down:39", previous: "down:37" };
+
+ }
+ else {
+
+ sDescendantSelector = sSelectorBase + STANDARD_QUERY;
+ sKeysVal = { next: "down:40", previous: "down:38" };
+
+ }
+
+
+ if (!oFocusManager) {
+
+ oRootMenu.plug(Y.Plugin.NodeFocusManager, {
+ descendants: sDescendantSelector,
+ keys: sKeysVal,
+ circular: true
+ });
+
+ oFocusManager = oRootMenu.focusManager;
+
+ sQuery = "#" + oRootMenu.get("id") + " .yui-menu a," +
+ MENU_TOGGLE_SELECTOR;
+
+ oRootMenu.all(sQuery).set("tabIndex", -1);
+
+ oFocusManager.on(ACTIVE_DESCENDANT_CHANGE,
+ this._onActiveDescendantChange, oFocusManager, this);
+
+ oFocusManager.after(ACTIVE_DESCENDANT_CHANGE,
+ this._afterActiveDescendantChange, oFocusManager, this);
+
+ menuNav._focusManager = oFocusManager;
+
+ }
+ else {
+
+ oFocusManager.set(ACTIVE_DESCENDANT, -1);
+ oFocusManager.set(DESCENDANTS, sDescendantSelector);
+ oFocusManager.set("keys", sKeysVal);
+
+ }
+
+ },
+
+
+ // Event handlers for discrete pieces of pieces of the menu
+
+
+ /**
+ * @method _onActiveDescendantChange
+ * @description "activeDescendantChange" event handler for menu's
+ * Focus Manager.
+ * @protected
+ * @param {Object} event Object representing the Attribute change event.
+ * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
+ */
+ _onActiveDescendantChange: function (event, menuNav) {
+
+ if (event.src === UI && menuNav._activeMenu &&
+ !menuNav._movingToSubmenu) {
+
+ menuNav._hideAllSubmenus(menuNav._activeMenu);
+
+ }
+
+ },
+
+
+ /**
+ * @method _afterActiveDescendantChange
+ * @description "activeDescendantChange" event handler for menu's
+ * Focus Manager.
+ * @protected
+ * @param {Object} event Object representing the Attribute change event.
+ * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
+ */
+ _afterActiveDescendantChange: function (event, menuNav) {
+
+ var oItem;
+
+ if (event.src === UI) {
+ oItem = getItem(this.get(DESCENDANTS).item(event.newVal), true);
+ menuNav._setActiveItem(oItem);
+ }
+
+ },
+
+
+ /**
+ * @method _onDocFocus
+ * @description "focus" event handler for the owner document of the MenuNav.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onDocFocus: function (event) {
+
+ var menuNav = this,
+ oActiveItem = menuNav._activeItem,
+ oTarget = event.target,
+ oMenu;
+
+
+ if (menuNav._rootMenu.contains(oTarget)) { // The menu has focus
+
+ if (menuNav._hasFocus) {
+
+ oMenu = getParentMenu(oTarget);
+
+ // If the element that was focused is a descendant of the
+ // root menu, but is in a submenu not currently being
+ // managed by the Focus Manager, update the Focus Manager so
+ // that it is now managing the submenu that is the parent of
+ // the element that was focused.
+
+ if (!menuNav._activeMenu.compareTo(oMenu)) {
+
+ menuNav._activeMenu = oMenu;
+ menuNav._initFocusManager();
+ menuNav._focusManager.set(ACTIVE_DESCENDANT, oTarget);
+ menuNav._setActiveItem(getItem(oTarget, true));
+
+ }
+
+ }
+ else { // Initial focus
+
+ // First time the menu has been focused, need to setup focused
+ // state and established active active descendant
+
+ menuNav._hasFocus = true;
+
+ oActiveItem = getItem(oTarget, true);
+
+ if (oActiveItem) {
+ menuNav._setActiveItem(oActiveItem);
+ }
+
+ }
+
+ }
+ else { // The menu has lost focus
+
+ menuNav._clearActiveItem();
+
+ menuNav._cancelShowSubmenuTimer();
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+
+ menuNav._activeMenu = menuNav._rootMenu;
+ menuNav._initFocusManager();
+
+ menuNav._focusManager.set(ACTIVE_DESCENDANT, 0);
+
+ menuNav._hasFocus = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuMouseOver
+ * @description "mouseover" event handler for a menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuMouseOver: function (menu, event) {
+
+ var menuNav = this,
+ oHideAllSubmenusTimer = menuNav._hideAllSubmenusTimer;
+
+ if (oHideAllSubmenusTimer) {
+ oHideAllSubmenusTimer.cancel();
+ menuNav._hideAllSubmenusTimer = null;
+ }
+
+ menuNav._cancelHideSubmenuTimer();
+
+ // Need to update the FocusManager in advance of focus a new
+ // Menu in order to avoid the FocusManager thinking that
+ // it has lost focus
+
+ if (menu && !menu.compareTo(menuNav._activeMenu)) {
+ menuNav._activeMenu = menu;
+
+ if (menuNav._hasFocus) {
+ menuNav._initFocusManager();
+ }
+
+ }
+
+ if (menuNav._movingToSubmenu && isHorizontalMenu(menu)) {
+ menuNav._movingToSubmenu = false;
+ }
+
+ },
+
+
+ /**
+ * @method _hideAndFocusLabel
+ * @description Hides all of the submenus of the root menu and focuses the
+ * label of the topmost submenu
+ * @protected
+ */
+ _hideAndFocusLabel: function () {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oSubmenu;
+
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+
+ if (oActiveMenu) {
+
+ // Focus the label element for the topmost submenu
+ oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
+ menuNav._focusItem(oSubmenu.previous());
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuMouseOut
+ * @description "mouseout" event handler for a menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuMouseOut: function (menu, event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oRelatedTarget = event.relatedTarget,
+ oActiveItem = menuNav._activeItem,
+ oParentMenu,
+ oMenu;
+
+
+ if (oActiveMenu && !oActiveMenu.contains(oRelatedTarget)) {
+
+ oParentMenu = getParentMenu(oActiveMenu);
+
+
+ if (oParentMenu && !oParentMenu.contains(oRelatedTarget)) {
+
+ if (menuNav.get(MOUSEOUT_HIDE_DELAY) > 0) {
+
+ menuNav._cancelShowSubmenuTimer();
+
+ menuNav._hideAllSubmenusTimer =
+
+ later(menuNav.get(MOUSEOUT_HIDE_DELAY),
+ menuNav, menuNav._hideAndFocusLabel);
+
+ }
+
+ }
+ else {
+
+ if (oActiveItem) {
+
+ oMenu = getParentMenu(oActiveItem);
+
+ if (!menuNav._isRoot(oMenu)) {
+ menuNav._focusItem(oMenu.previous());
+ }
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuLabelMouseOver
+ * @description "mouseover" event handler for a menu label.
+ * @protected
+ * @param {Node} menuLabel Node instance representing a menu label.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuLabelMouseOver: function (menuLabel, event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ bIsRoot = menuNav._isRoot(oActiveMenu),
+ bUseAutoSubmenuDisplay =
+ (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
+ oSubmenu;
+
+
+ menuNav._focusItem(menuLabel);
+ menuNav._setActiveItem(menuLabel);
+
+
+ if (bUseAutoSubmenuDisplay && !menuNav._movingToSubmenu) {
+
+ menuNav._cancelHideSubmenuTimer();
+ menuNav._cancelShowSubmenuTimer();
+
+
+ if (!hasVisibleSubmenu(menuLabel)) {
+
+ oSubmenu = menuLabel.next();
+
+
+ if (oSubmenu) {
+
+ menuNav._hideAllSubmenus(oActiveMenu);
+
+ menuNav._showSubmenuTimer =
+ later(menuNav.get("submenuShowDelay"), menuNav,
+ menuNav._showMenu, oSubmenu);
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuLabelMouseOut
+ * @description "mouseout" event handler for a menu label.
+ * @protected
+ * @param {Node} menuLabel Node instance representing a menu label.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuLabelMouseOut: function (menuLabel, event) {
+
+ var menuNav = this,
+ bIsRoot = menuNav._isRoot(menuNav._activeMenu),
+ bUseAutoSubmenuDisplay =
+ (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
+
+ oRelatedTarget = event.relatedTarget,
+ oSubmenu = menuLabel.next();
+
+ menuNav._clearActiveItem();
+
+ if (bUseAutoSubmenuDisplay) {
+
+ if (menuNav._movingToSubmenu &&
+ !menuNav._showSubmenuTimer && oSubmenu) {
+
+ // If the mouse is moving diagonally toward the submenu and
+ // another submenu isn't in the process of being displayed
+ // (via a timer), then hide the submenu via a timer to give
+ // the user some time to reach the submenu.
+
+ menuNav._hideSubmenuTimer =
+ later(menuNav.get("submenuHideDelay"), menuNav,
+ menuNav._hideMenu, oSubmenu);
+
+ }
+ else if (!menuNav._movingToSubmenu && oSubmenu &&
+ !oSubmenu.contains(oRelatedTarget) &&
+ !oRelatedTarget.compareTo(oSubmenu)) {
+
+ // If the mouse is not moving toward the submenu, cancel any
+ // submenus that might be in the process of being displayed
+ // (via a timer) and hide this submenu immediately.
+
+ menuNav._cancelShowSubmenuTimer();
+
+ menuNav._hideMenu(oSubmenu);
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuItemMouseOver
+ * @description "mouseover" event handler for a menuitem.
+ * @protected
+ * @param {Node} menuItem Node instance representing a menuitem.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuItemMouseOver: function (menuItem, event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ bIsRoot = menuNav._isRoot(oActiveMenu),
+ bUseAutoSubmenuDisplay =
+ (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot);
+
+
+ menuNav._focusItem(menuItem);
+ menuNav._setActiveItem(menuItem);
+
+
+ if (bUseAutoSubmenuDisplay && !menuNav._movingToSubmenu) {
+
+ menuNav._hideAllSubmenus(oActiveMenu);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuItemMouseOut
+ * @description "mouseout" event handler for a menuitem.
+ * @protected
+ * @param {Node} menuItem Node instance representing a menuitem.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuItemMouseOut: function (menuItem, event) {
+
+ this._clearActiveItem();
+
+ },
+
+
+ /**
+ * @method _onVerticalMenuKeyDown
+ * @description "keydown" event handler for vertical menus.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onVerticalMenuKeyDown: function (event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oRootMenu = menuNav._rootMenu,
+ oTarget = event.target,
+ bPreventDefault = false,
+ nKeyCode = event.keyCode,
+ oSubmenu,
+ oParentMenu,
+ oLI,
+ oItem;
+
+
+ switch (nKeyCode) {
+
+ case 37: // left arrow
+
+ oParentMenu = getParentMenu(oActiveMenu);
+
+ if (oParentMenu && isHorizontalMenu(oParentMenu)) {
+
+ menuNav._hideMenu(oActiveMenu);
+ oLI = getPreviousSibling(oActiveMenu.get(PARENT_NODE));
+ oItem = getItem(oLI);
+
+ if (oItem) {
+
+ if (isMenuLabel(oItem)) { // Menu label
+
+ oSubmenu = oItem.next();
+
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+ else {
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+ else { // MenuItem
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+
+ }
+ else if (!menuNav._isRoot(oActiveMenu)) {
+ menuNav._hideMenu(oActiveMenu, true);
+ }
+
+
+ bPreventDefault = true;
+
+ break;
+
+ case 39: // right arrow
+
+ if (isMenuLabel(oTarget)) {
+
+ oSubmenu = oTarget.next();
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+
+ }
+ else if (isHorizontalMenu(oRootMenu)) {
+
+ oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
+ oLI = getNextSibling(oSubmenu.get(PARENT_NODE));
+ oItem = getItem(oLI);
+
+ menuNav._hideAllSubmenus(oRootMenu);
+
+ if (oItem) {
+
+ if (isMenuLabel(oItem)) { // Menu label
+
+ oSubmenu = oItem.next();
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+ else {
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+ else { // MenuItem
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+
+ }
+
+ bPreventDefault = true;
+
+ break;
+
+ }
+
+
+ if (bPreventDefault) {
+
+ // Prevent the browser from scrolling the window
+
+ event.preventDefault();
+
+ }
+
+ },
+
+
+ /**
+ * @method _onHorizontalMenuKeyDown
+ * @description "keydown" event handler for horizontal menus.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onHorizontalMenuKeyDown: function (event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oTarget = event.target,
+ oFocusedItem = getItem(oTarget, true),
+ bPreventDefault = false,
+ nKeyCode = event.keyCode,
+ oSubmenu;
+
+
+ if (nKeyCode === 40) {
+
+ menuNav._hideAllSubmenus(oActiveMenu);
+
+ if (isMenuLabel(oFocusedItem)) {
+
+ oSubmenu = oFocusedItem.next();
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+
+ bPreventDefault = true;
+
+ }
+
+ }
+
+
+ if (bPreventDefault) {
+
+ // Prevent the browser from scrolling the window
+
+ event.preventDefault();
+
+ }
+
+ },
+
+
+ // Generic DOM Event handlers
+
+
+ /**
+ * @method _onMouseMove
+ * @description "mousemove" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMouseMove: function (event) {
+
+ var menuNav = this;
+
+ // Using a timer to set the value of the "_currentMouseX" property
+ // helps improve the reliability of the calculation used to set the
+ // value of the "_movingToSubmenu" property - especially in Opera.
+
+ later(10, menuNav, function () {
+
+ menuNav._currentMouseX = event.pageX;
+
+ });
+
+ },
+
+
+ /**
+ * @method _onMouseOver
+ * @description "mouseover" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMouseOver: function (event) {
+
+ var menuNav = this,
+ oTarget,
+ oMenu,
+ oMenuLabel,
+ oParentMenu,
+ oMenuItem;
+
+
+ if (menuNav._blockMouseEvent) {
+ menuNav._blockMouseEvent = false;
+ }
+ else {
+
+ oTarget = event.target;
+ oMenu = getMenu(oTarget, true);
+ oMenuLabel = getMenuLabel(oTarget, true);
+ oMenuItem = getMenuItem(oTarget, true);
+
+
+ if (handleMouseOverForNode(oMenu, oTarget)) {
+
+ menuNav._onMenuMouseOver(oMenu, event);
+
+ oMenu[HANDLED_MOUSEOVER] = true;
+ oMenu[HANDLED_MOUSEOUT] = false;
+
+ oParentMenu = getParentMenu(oMenu);
+
+ if (oParentMenu) {
+
+ oParentMenu[HANDLED_MOUSEOUT] = true;
+ oParentMenu[HANDLED_MOUSEOVER] = false;
+
+ }
+
+ }
+
+ if (handleMouseOverForNode(oMenuLabel, oTarget)) {
+
+ menuNav._onMenuLabelMouseOver(oMenuLabel, event);
+
+ oMenuLabel[HANDLED_MOUSEOVER] = true;
+ oMenuLabel[HANDLED_MOUSEOUT] = false;
+
+ }
+
+ if (handleMouseOverForNode(oMenuItem, oTarget)) {
+
+ menuNav._onMenuItemMouseOver(oMenuItem, event);
+
+ oMenuItem[HANDLED_MOUSEOVER] = true;
+ oMenuItem[HANDLED_MOUSEOUT] = false;
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMouseOut
+ * @description "mouseout" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMouseOut: function (event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ bMovingToSubmenu = false,
+ oTarget,
+ oRelatedTarget,
+ oMenu,
+ oMenuLabel,
+ oSubmenu,
+ oMenuItem;
+
+
+ menuNav._movingToSubmenu =
+ (oActiveMenu && !isHorizontalMenu(oActiveMenu) &&
+ ((event.pageX - 5) > menuNav._currentMouseX));
+
+ oTarget = event.target;
+ oRelatedTarget = event.relatedTarget;
+ oMenu = getMenu(oTarget, true);
+ oMenuLabel = getMenuLabel(oTarget, true);
+ oMenuItem = getMenuItem(oTarget, true);
+
+
+ if (handleMouseOutForNode(oMenuLabel, oRelatedTarget)) {
+
+ menuNav._onMenuLabelMouseOut(oMenuLabel, event);
+
+ oMenuLabel[HANDLED_MOUSEOUT] = true;
+ oMenuLabel[HANDLED_MOUSEOVER] = false;
+
+ }
+
+ if (handleMouseOutForNode(oMenuItem, oRelatedTarget)) {
+
+ menuNav._onMenuItemMouseOut(oMenuItem, event);
+
+ oMenuItem[HANDLED_MOUSEOUT] = true;
+ oMenuItem[HANDLED_MOUSEOVER] = false;
+
+ }
+
+
+ if (oMenuLabel) {
+
+ oSubmenu = oMenuLabel.next();
+
+ if (oSubmenu &&
+ (oRelatedTarget.compareTo(oSubmenu) ||
+ oSubmenu.contains(oRelatedTarget))) {
+
+ bMovingToSubmenu = true;
+
+ }
+
+ }
+
+
+ if (handleMouseOutForNode(oMenu, oRelatedTarget) || bMovingToSubmenu) {
+
+ menuNav._onMenuMouseOut(oMenu, event);
+
+ oMenu[HANDLED_MOUSEOUT] = true;
+ oMenu[HANDLED_MOUSEOVER] = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method _toggleSubmenuDisplay
+ * @description "mousedown," "keydown," and "click" event handler for the
+ * menu used to toggle the display of a submenu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _toggleSubmenuDisplay: function (event) {
+
+ var menuNav = this,
+ oTarget = event.target,
+ oMenuLabel = getMenuLabel(oTarget, true),
+ sType = event.type,
+ oAnchor,
+ oSubmenu,
+ sHref,
+ nHashPos,
+ nLen,
+ sId;
+
+
+ if (oMenuLabel) {
+
+ oAnchor = isAnchor(oTarget) ? oTarget : oTarget.ancestor(isAnchor);
+
+
+ if (oAnchor) {
+
+ // Need to pass "2" as a second argument to "getAttribute" for
+ // IE otherwise IE will return a fully qualified URL for the
+ // value of the "href" attribute.
+ // http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
+
+ sHref = oAnchor.getAttribute("href", 2);
+ nHashPos = sHref.indexOf("#");
+ nLen = sHref.length;
+
+ if (nHashPos === 0 && nLen > 1) {
+
+ sId = sHref.substr(1, nLen);
+ oSubmenu = oMenuLabel.next();
+
+ if (oSubmenu && (oSubmenu.get(ID) === sId)) {
+
+ if (sType === MOUSEDOWN || sType === KEYDOWN) {
+
+ if ((UA.opera || UA.gecko || UA.ie) && sType === KEYDOWN && !menuNav._preventClickHandle) {
+
+ // Prevent the browser from following the URL of
+ // the anchor element
+
+ menuNav._preventClickHandle = menuNav._rootMenu.on("click", function (event) {
+
+ event.preventDefault();
+
+ menuNav._preventClickHandle.detach();
+ menuNav._preventClickHandle = null;
+
+ });
+
+ }
+
+ if (sType == MOUSEDOWN) {
+
+ // Prevent the target from getting focused by
+ // default, since the element to be focused will
+ // be determined by weather or not the submenu
+ // is visible.
+ event.preventDefault();
+
+ // FocusManager will attempt to focus any
+ // descendant that is the target of the mousedown
+ // event. Since we want to explicitly control
+ // where focus is going, we need to call
+ // "stopImmediatePropagation" to stop the
+ // FocusManager from doing its thing.
+ event.stopImmediatePropagation();
+
+ // The "_focusItem" method relies on the
+ // "_hasFocus" property being set to true. The
+ // "_hasFocus" property is normally set via a
+ // "focus" event listener, but since we've
+ // blocked focus from happening, we need to set
+ // this property manually.
+ menuNav._hasFocus = true;
+
+ }
+
+
+ if (menuNav._isRoot(getParentMenu(oTarget))) { // Event target is a submenu label in the root menu
+
+ // Menu label toggle functionality
+
+ if (hasVisibleSubmenu(oMenuLabel)) {
+
+ menuNav._hideMenu(oSubmenu);
+ menuNav._focusItem(oMenuLabel);
+ menuNav._setActiveItem(oMenuLabel);
+
+ }
+ else {
+
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+ menuNav._showMenu(oSubmenu);
+
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+
+ }
+ else { // Event target is a submenu label within a submenu
+
+ if (menuNav._activeItem == oMenuLabel) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+ else {
+
+ if (!oMenuLabel._clickHandle) {
+
+ oMenuLabel._clickHandle = oMenuLabel.on("click", function () {
+
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+
+ menuNav._hasFocus = false;
+ menuNav._clearActiveItem();
+
+
+ oMenuLabel._clickHandle.detach();
+
+ oMenuLabel._clickHandle = null;
+
+ });
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ if (sType === CLICK) {
+
+ // Prevent the browser from following the URL of
+ // the anchor element
+
+ event.preventDefault();
+
+ }
+
+ }
+
+ }
+
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyPress
+ * @description "keypress" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onKeyPress: function (event) {
+
+ switch (event.keyCode) {
+
+ case 37: // left arrow
+ case 38: // up arrow
+ case 39: // right arrow
+ case 40: // down arrow
+
+ // Prevent the browser from scrolling the window
+
+ event.preventDefault();
+
+ break;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyDown
+ * @description "keydown" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onKeyDown: function (event) {
+
+ var menuNav = this,
+ oActiveItem = menuNav._activeItem,
+ oTarget = event.target,
+ oActiveMenu = getParentMenu(oTarget),
+ oSubmenu;
+
+ if (oActiveMenu) {
+
+ menuNav._activeMenu = oActiveMenu;
+
+ if (isHorizontalMenu(oActiveMenu)) {
+ menuNav._onHorizontalMenuKeyDown(event);
+ }
+ else {
+ menuNav._onVerticalMenuKeyDown(event);
+ }
+
+
+ if (event.keyCode === 27) {
+
+ if (!menuNav._isRoot(oActiveMenu)) {
+
+ if (UA.opera) {
+ later(0, menuNav, function () {
+ menuNav._hideMenu(oActiveMenu, true);
+ });
+ }
+ else {
+ menuNav._hideMenu(oActiveMenu, true);
+ }
+
+ event.stopPropagation();
+ menuNav._blockMouseEvent = UA.gecko ? true : false;
+
+ }
+ else if (oActiveItem) {
+
+ if (isMenuLabel(oActiveItem) &&
+ hasVisibleSubmenu(oActiveItem)) {
+
+ oSubmenu = oActiveItem.next();
+
+ if (oSubmenu) {
+ menuNav._hideMenu(oSubmenu);
+ }
+
+ }
+ else {
+
+ menuNav._focusManager.blur();
+
+ // This is necessary for Webkit since blurring the
+ // active menuitem won't result in the document
+ // gaining focus, meaning the that _onDocFocus
+ // listener won't clear the active menuitem.
+
+ menuNav._clearActiveItem();
+
+ menuNav._hasFocus = false;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ },
+
+ /**
+ * @method _onDocMouseDown
+ * @description "mousedown" event handler for the owner document of
+ * the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onDocMouseDown: function (event) {
+
+ var menuNav = this,
+ oRoot = menuNav._rootMenu,
+ oTarget = event.target;
+
+
+ if (!(oRoot.compareTo(oTarget) || oRoot.contains(oTarget))) {
+
+ menuNav._hideAllSubmenus(oRoot);
+
+ // Document doesn't receive focus in Webkit when the user mouses
+ // down on it, so the "_hasFocus" property won't get set to the
+ // correct value. The following line corrects the problem.
+
+ if (UA.webkit) {
+ menuNav._hasFocus = false;
+ menuNav._clearActiveItem();
+ }
+
+ }
+
+ }
+
+});
+
+
+Y.namespace('Plugin');
+
+Y.Plugin.NodeMenuNav = NodeMenuNav;
+
+
+}, '3.0.0' ,{requires:['node', 'classnamemanager', 'node-focusmanager']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-menunav",function(D){var m=D.UA,t=D.later,AL=D.ClassNameManager.getClassName,R="menu",G="menuitem",AH="hidden",S="parentNode",V="children",z="offsetHeight",AC="offsetWidth",AN="px",g="id",I=".",E="handledMouseOut",r="handledMouseOver",a="active",AJ="label",d="a",w="mousedown",AO="keydown",AB="click",Q="",U="first-of-type",AP="role",N="presentation",AD="descendants",j="UI",u="activeDescendant",J="useARIA",x="aria-hidden",y="content",c="host",h=u+"Change",AM=">.yui-menu-content>ul>li>a",O=">.yui-menu-content>ul>li>.yui-menu-label>a:first-child",v="autoSubmenuDisplay",T="mouseOutHideDelay",l=AL(R),AF=AL(R,AH),Z=AL(R,"horizontal"),AI=AL(R,AJ),k=AL(R,AJ,a),X=AL(R,AJ,(R+"visible")),K=AL(G),A=AL(G,a),i=I+l,AG=(I+AL(R,"toggle"));var L=function(Y){var AR=Y.previous(),AQ;if(!AR){AQ=Y.get(S).get(V);AR=AQ.item(AQ.size()-1);}return AR;};var b=function(Y){var AQ=Y.next();if(!AQ){AQ=Y.get(S).get(V).item(0);}return AQ;};var F=function(Y){var AQ=false;if(Y){AQ=Y.get("nodeName").toLowerCase()===d;}return AQ;};var P=function(Y){return Y.hasClass(K);};var s=function(Y){return Y.hasClass(AI);};var q=function(Y){return Y.hasClass(Z);};var n=function(Y){return Y.hasClass(X);};var p=function(Y){return F(Y)?Y:Y.one(d);};var AA=function(AR,AQ,Y){var AS;if(AR){if(AR.hasClass(AQ)){AS=AR;}if(!AS&&Y){AS=AR.ancestor((I+AQ));}}return AS;};var M=function(Y){return Y.ancestor(i);};var W=function(AQ,Y){return AA(AQ,l,Y);};var AE=function(AQ,Y){var AR;if(AQ){AR=AA(AQ,K,Y);}return AR;};var o=function(AQ,Y){var AR;if(AQ){if(Y){AR=AA(AQ,AI,Y);}else{AR=AA(AQ,AI)||AQ.one((I+AI));}}return AR;};var B=function(AQ,Y){var AR;if(AQ){AR=AE(AQ,Y)||o(AQ,Y);}return AR;};var C=function(Y){return B(Y.one("li"));};var f=function(Y){return P(Y)?A:k;};var e=function(Y,AQ){return Y&&!Y[r]&&(Y.compareTo(AQ)||Y.contains(AQ));};var H=function(AQ,Y){return AQ&&!AQ[E]&&(!AQ.compareTo(Y)&&!AQ.contains(Y));};var AK=function(){AK.superclass.constructor.apply(this,arguments);};AK.NAME="nodeMenuNav";AK.NS="menuNav";AK.SHIM_TEMPLATE_TITLE="Menu Stacking Shim";AK.SHIM_TEMPLATE='<iframe frameborder="0" tabindex="-1" class="'+AL("shim")+'" title="'+AK.SHIM_TEMPLATE_TITLE+'" src="javascript:false;"></iframe>';AK.ATTRS={useARIA:{value:true,writeOnce:true,lazyAdd:false,setter:function(AT){var AQ=this.get(c),AU,Y,AS,AR;if(AT){AQ.set(AP,R);AQ.all("ul,li,."+AL(R,y)).set(AP,N);AQ.all((I+AL(G,y))).set(AP,G);AQ.all((I+AI)).each(function(AV){AU=AV;Y=AV.one(AG);if(Y){Y.set(AP,N);AU=Y.previous();}AU.set(AP,G);AU.set("aria-haspopup",true);AS=AV.next();if(AS){AS.set(AP,R);AU=AS.previous();Y=AU.one(AG);if(Y){AU=Y;}AR=D.stamp(AU);if(!AU.get(g)){AU.set(g,AR);}AS.set("aria-labelledby",AR);AS.set(x,true);}});}}},autoSubmenuDisplay:{value:true,writeOnce:true},submenuShowDelay:{value:250,writeOnce:true},submenuHideDelay:{value:250,writeOnce:true},mouseOutHideDelay:{value:750,writeOnce:true}};D.extend(AK,D.Plugin.Base,{_rootMenu:null,_activeItem:null,_activeMenu:null,_hasFocus:false,_blockMouseEvent:false,_currentMouseX:0,_movingToSubmenu:false,_showSubmenuTimer:null,_hideSubmenuTimer:null,_hideAllSubmenusTimer:null,_firstItem:null,initializer:function(AR){var AS=this,AT=this.get(c),AQ=[],Y;if(AT){AS._rootMenu=AT;AT.all("ul:first-child").addClass(U);AT.all(i).addClass(AF);AQ.push(AT.on("mouseover",AS._onMouseOver,AS));AQ.push(AT.on("mouseout",AS._onMouseOut,AS));AQ.push(AT.on("mousemove",AS._onMouseMove,AS));AQ.push(AT.on(w,AS._toggleSubmenuDisplay,AS));AQ.push(D.on("key",AS._toggleSubmenuDisplay,AT,"down:13",AS));AQ.push(AT.on(AB,AS._toggleSubmenuDisplay,AS));AQ.push(AT.on("keypress",AS._onKeyPress,AS));AQ.push(AT.on(AO,AS._onKeyDown,AS));Y=AT.get("ownerDocument");AQ.push(Y.on(w,AS._onDocMouseDown,AS));AQ.push(Y.on("focus",AS._onDocFocus,AS));this._eventHandlers=AQ;AS._initFocusManager();}},destructor:function(){var Y=this._eventHandlers;if(Y){D.Array.each(Y,function(AQ){AQ.detach();});this._eventHandlers=null;}this.get(c).unplug("focusManager");},_isRoot:function(Y){return this._rootMenu.compareTo(Y);},_getTopmostSubmenu:function(AS){var AR=this,Y=M(AS),AQ;if(!Y){AQ=AS;}else{if(AR._isRoot(Y)){AQ=AS;}else{AQ=AR._getTopmostSubmenu(Y);}}return AQ;},_clearActiveItem:function(){var AQ=this,Y=AQ._activeItem;if(Y){Y.removeClass(f(Y));}AQ._activeItem=null;},_setActiveItem:function(AQ){var Y=this;if(AQ){Y._clearActiveItem();AQ.addClass(f(AQ));Y._activeItem=AQ;}},_focusItem:function(AR){var AQ=this,Y,AS;if(AR&&AQ._hasFocus){Y=M(AR);AS=p(AR);if(Y&&!Y.compareTo(AQ._activeMenu)){AQ._activeMenu=Y;AQ._initFocusManager();}AQ._focusManager.focus(AS);}},_showMenu:function(AS){var Y=M(AS),AR=AS.get(S),AQ=AR.getXY();if(this.get(J)){AS.set(x,false);}if(q(Y)){AQ[1]=AQ[1]+AR.get(z);}else{AQ[0]=AQ[0]+AR.get(AC);}AS.setXY(AQ);if(m.ie<8){if(m.ie===6&&!AS.hasIFrameShim){AS.appendChild(D.Node.create(AK.SHIM_TEMPLATE));AS.hasIFrameShim=true;}AS.setStyles({height:Q,width:Q});AS.setStyles({height:(AS.get(z)+AN),width:(AS.get(AC)+AN)});}AS.previous().addClass(X);AS.removeClass(AF);},_hideMenu:function(AS,AQ){var AR=this,AT=AS.previous(),Y;AT.removeClass(X);if(AQ){AR._focusItem(AT);AR._setActiveItem(AT);}Y=AS.one((I+A));if(Y){Y.removeClass(A);}AS.setStyles({left:Q,top:Q});AS.addClass(AF);if(AR.get(J)){AS.set(x,true);}},_hideAllSubmenus:function(AQ){var Y=this;AQ.all(i).each(D.bind(function(AR){Y._hideMenu(AR);},Y));},_cancelShowSubmenuTimer:function(){var AQ=this,Y=AQ._showSubmenuTimer;if(Y){Y.cancel();AQ._showSubmenuTimer=null;}},_cancelHideSubmenuTimer:function(){var Y=this,AQ=Y._hideSubmenuTimer;if(AQ){AQ.cancel();Y._hideSubmenuTimer=null;}},_initFocusManager:function(){var AS=this,AU=AS._rootMenu,AQ=AS._activeMenu||AU,AT=AS._isRoot(AQ)?Q:("#"+AQ.get("id")),Y=AS._focusManager,AR,AV,AW;if(q(AQ)){AV=AT+AM+","+AT+O;AR={next:"down:39",previous:"down:37"};}else{AV=AT+AM;AR={next:"down:40",previous:"down:38"};}if(!Y){AU.plug(D.Plugin.NodeFocusManager,{descendants:AV,keys:AR,circular:true});Y=AU.focusManager;AW="#"+AU.get("id")+" .yui-menu a,"+AG;AU.all(AW).set("tabIndex",-1);Y.on(h,this._onActiveDescendantChange,Y,this);
+Y.after(h,this._afterActiveDescendantChange,Y,this);AS._focusManager=Y;}else{Y.set(u,-1);Y.set(AD,AV);Y.set("keys",AR);}},_onActiveDescendantChange:function(AQ,Y){if(AQ.src===j&&Y._activeMenu&&!Y._movingToSubmenu){Y._hideAllSubmenus(Y._activeMenu);}},_afterActiveDescendantChange:function(AQ,Y){var AR;if(AQ.src===j){AR=B(this.get(AD).item(AQ.newVal),true);Y._setActiveItem(AR);}},_onDocFocus:function(AT){var AS=this,Y=AS._activeItem,AR=AT.target,AQ;if(AS._rootMenu.contains(AR)){if(AS._hasFocus){AQ=M(AR);if(!AS._activeMenu.compareTo(AQ)){AS._activeMenu=AQ;AS._initFocusManager();AS._focusManager.set(u,AR);AS._setActiveItem(B(AR,true));}}else{AS._hasFocus=true;Y=B(AR,true);if(Y){AS._setActiveItem(Y);}}}else{AS._clearActiveItem();AS._cancelShowSubmenuTimer();AS._hideAllSubmenus(AS._rootMenu);AS._activeMenu=AS._rootMenu;AS._initFocusManager();AS._focusManager.set(u,0);AS._hasFocus=false;}},_onMenuMouseOver:function(AS,AR){var AQ=this,Y=AQ._hideAllSubmenusTimer;if(Y){Y.cancel();AQ._hideAllSubmenusTimer=null;}AQ._cancelHideSubmenuTimer();if(AS&&!AS.compareTo(AQ._activeMenu)){AQ._activeMenu=AS;if(AQ._hasFocus){AQ._initFocusManager();}}if(AQ._movingToSubmenu&&q(AS)){AQ._movingToSubmenu=false;}},_hideAndFocusLabel:function(){var AR=this,AQ=AR._activeMenu,Y;AR._hideAllSubmenus(AR._rootMenu);if(AQ){Y=AR._getTopmostSubmenu(AQ);AR._focusItem(Y.previous());}},_onMenuMouseOut:function(AW,AU){var AT=this,AR=AT._activeMenu,AV=AU.relatedTarget,Y=AT._activeItem,AS,AQ;if(AR&&!AR.contains(AV)){AS=M(AR);if(AS&&!AS.contains(AV)){if(AT.get(T)>0){AT._cancelShowSubmenuTimer();AT._hideAllSubmenusTimer=t(AT.get(T),AT,AT._hideAndFocusLabel);}}else{if(Y){AQ=M(Y);if(!AT._isRoot(AQ)){AT._focusItem(AQ.previous());}}}}},_onMenuLabelMouseOver:function(AT,AV){var AU=this,AS=AU._activeMenu,Y=AU._isRoot(AS),AR=(AU.get(v)&&Y||!Y),AQ;AU._focusItem(AT);AU._setActiveItem(AT);if(AR&&!AU._movingToSubmenu){AU._cancelHideSubmenuTimer();AU._cancelShowSubmenuTimer();if(!n(AT)){AQ=AT.next();if(AQ){AU._hideAllSubmenus(AS);AU._showSubmenuTimer=t(AU.get("submenuShowDelay"),AU,AU._showMenu,AQ);}}}},_onMenuLabelMouseOut:function(AS,AU){var AT=this,Y=AT._isRoot(AT._activeMenu),AR=(AT.get(v)&&Y||!Y),AV=AU.relatedTarget,AQ=AS.next();AT._clearActiveItem();if(AR){if(AT._movingToSubmenu&&!AT._showSubmenuTimer&&AQ){AT._hideSubmenuTimer=t(AT.get("submenuHideDelay"),AT,AT._hideMenu,AQ);}else{if(!AT._movingToSubmenu&&AQ&&!AQ.contains(AV)&&!AV.compareTo(AQ)){AT._cancelShowSubmenuTimer();AT._hideMenu(AQ);}}}},_onMenuItemMouseOver:function(AS,AU){var AT=this,AR=AT._activeMenu,Y=AT._isRoot(AR),AQ=(AT.get(v)&&Y||!Y);AT._focusItem(AS);AT._setActiveItem(AS);if(AQ&&!AT._movingToSubmenu){AT._hideAllSubmenus(AR);}},_onMenuItemMouseOut:function(Y,AQ){this._clearActiveItem();},_onVerticalMenuKeyDown:function(Y){var AQ=this,AU=AQ._activeMenu,AZ=AQ._rootMenu,AR=Y.target,AT=false,AY=Y.keyCode,AW,AS,AV,AX;switch(AY){case 37:AS=M(AU);if(AS&&q(AS)){AQ._hideMenu(AU);AV=L(AU.get(S));AX=B(AV);if(AX){if(s(AX)){AW=AX.next();if(AW){AQ._showMenu(AW);AQ._focusItem(C(AW));AQ._setActiveItem(C(AW));}else{AQ._focusItem(AX);AQ._setActiveItem(AX);}}else{AQ._focusItem(AX);AQ._setActiveItem(AX);}}}else{if(!AQ._isRoot(AU)){AQ._hideMenu(AU,true);}}AT=true;break;case 39:if(s(AR)){AW=AR.next();if(AW){AQ._showMenu(AW);AQ._focusItem(C(AW));AQ._setActiveItem(C(AW));}}else{if(q(AZ)){AW=AQ._getTopmostSubmenu(AU);AV=b(AW.get(S));AX=B(AV);AQ._hideAllSubmenus(AZ);if(AX){if(s(AX)){AW=AX.next();if(AW){AQ._showMenu(AW);AQ._focusItem(C(AW));AQ._setActiveItem(C(AW));}else{AQ._focusItem(AX);AQ._setActiveItem(AX);}}else{AQ._focusItem(AX);AQ._setActiveItem(AX);}}}}AT=true;break;}if(AT){Y.preventDefault();}},_onHorizontalMenuKeyDown:function(AV){var AU=this,AS=AU._activeMenu,AQ=AV.target,Y=B(AQ,true),AT=false,AW=AV.keyCode,AR;if(AW===40){AU._hideAllSubmenus(AS);if(s(Y)){AR=Y.next();if(AR){AU._showMenu(AR);AU._focusItem(C(AR));AU._setActiveItem(C(AR));}AT=true;}}if(AT){AV.preventDefault();}},_onMouseMove:function(AQ){var Y=this;t(10,Y,function(){Y._currentMouseX=AQ.pageX;});},_onMouseOver:function(AT){var AS=this,AQ,Y,AV,AR,AU;if(AS._blockMouseEvent){AS._blockMouseEvent=false;}else{AQ=AT.target;Y=W(AQ,true);AV=o(AQ,true);AU=AE(AQ,true);if(e(Y,AQ)){AS._onMenuMouseOver(Y,AT);Y[r]=true;Y[E]=false;AR=M(Y);if(AR){AR[E]=true;AR[r]=false;}}if(e(AV,AQ)){AS._onMenuLabelMouseOver(AV,AT);AV[r]=true;AV[E]=false;}if(e(AU,AQ)){AS._onMenuItemMouseOver(AU,AT);AU[r]=true;AU[E]=false;}}},_onMouseOut:function(AQ){var AR=this,AT=AR._activeMenu,AY=false,AS,AU,AW,Y,AV,AX;AR._movingToSubmenu=(AT&&!q(AT)&&((AQ.pageX-5)>AR._currentMouseX));AS=AQ.target;AU=AQ.relatedTarget;AW=W(AS,true);Y=o(AS,true);AX=AE(AS,true);if(H(Y,AU)){AR._onMenuLabelMouseOut(Y,AQ);Y[E]=true;Y[r]=false;}if(H(AX,AU)){AR._onMenuItemMouseOut(AX,AQ);AX[E]=true;AX[r]=false;}if(Y){AV=Y.next();if(AV&&(AU.compareTo(AV)||AV.contains(AU))){AY=true;}}if(H(AW,AU)||AY){AR._onMenuMouseOut(AW,AQ);AW[E]=true;AW[r]=false;}},_toggleSubmenuDisplay:function(AR){var AS=this,AT=AR.target,AQ=o(AT,true),Y=AR.type,AX,AW,AV,AY,AZ,AU;if(AQ){AX=F(AT)?AT:AT.ancestor(F);if(AX){AV=AX.getAttribute("href",2);AY=AV.indexOf("#");AZ=AV.length;if(AY===0&&AZ>1){AU=AV.substr(1,AZ);AW=AQ.next();if(AW&&(AW.get(g)===AU)){if(Y===w||Y===AO){if((m.opera||m.gecko||m.ie)&&Y===AO&&!AS._preventClickHandle){AS._preventClickHandle=AS._rootMenu.on("click",function(Aa){Aa.preventDefault();AS._preventClickHandle.detach();AS._preventClickHandle=null;});}if(Y==w){AR.preventDefault();AR.stopImmediatePropagation();AS._hasFocus=true;}if(AS._isRoot(M(AT))){if(n(AQ)){AS._hideMenu(AW);AS._focusItem(AQ);AS._setActiveItem(AQ);}else{AS._hideAllSubmenus(AS._rootMenu);AS._showMenu(AW);AS._focusItem(C(AW));AS._setActiveItem(C(AW));}}else{if(AS._activeItem==AQ){AS._showMenu(AW);AS._focusItem(C(AW));AS._setActiveItem(C(AW));}else{if(!AQ._clickHandle){AQ._clickHandle=AQ.on("click",function(){AS._hideAllSubmenus(AS._rootMenu);AS._hasFocus=false;AS._clearActiveItem();AQ._clickHandle.detach();AQ._clickHandle=null;});}}}}if(Y===AB){AR.preventDefault();
+}}}}}},_onKeyPress:function(Y){switch(Y.keyCode){case 37:case 38:case 39:case 40:Y.preventDefault();break;}},_onKeyDown:function(AU){var AT=this,Y=AT._activeItem,AQ=AU.target,AS=M(AQ),AR;if(AS){AT._activeMenu=AS;if(q(AS)){AT._onHorizontalMenuKeyDown(AU);}else{AT._onVerticalMenuKeyDown(AU);}if(AU.keyCode===27){if(!AT._isRoot(AS)){if(m.opera){t(0,AT,function(){AT._hideMenu(AS,true);});}else{AT._hideMenu(AS,true);}AU.stopPropagation();AT._blockMouseEvent=m.gecko?true:false;}else{if(Y){if(s(Y)&&n(Y)){AR=Y.next();if(AR){AT._hideMenu(AR);}}else{AT._focusManager.blur();AT._clearActiveItem();AT._hasFocus=false;}}}}}},_onDocMouseDown:function(AS){var AR=this,AQ=AR._rootMenu,Y=AS.target;if(!(AQ.compareTo(Y)||AQ.contains(Y))){AR._hideAllSubmenus(AQ);if(m.webkit){AR._hasFocus=false;AR._clearActiveItem();}}}});D.namespace("Plugin");D.Plugin.NodeMenuNav=AK;},"3.0.0",{requires:["node","classnamemanager","node-focusmanager"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-menunav', function(Y) {
+
+/**
+* <p>The MenuNav Node Plugin makes it easy to transform existing list-based
+* markup into traditional, drop down navigational menus that are both accessible
+* and easy to customize, and only require a small set of dependencies.</p>
+*
+*
+* <p>To use the MenuNav Node Plugin, simply pass a reference to the plugin to a
+* Node instance's <code>plug</code> method.</p>
+*
+* <p>
+* <code>
+* <script type="text/javascript"> <br>
+* <br>
+* // Call the "use" method, passing in "node-menunav". This will <br>
+* // load the script and CSS for the MenuNav Node Plugin and all of <br>
+* // the required dependencies. <br>
+* <br>
+* YUI().use("node-menunav", function(Y) { <br>
+* <br>
+* // Use the "contentready" event to initialize the menu when <br>
+* // the subtree of element representing the root menu <br>
+* // (<div id="menu-1">) is ready to be scripted. <br>
+* <br>
+* Y.on("contentready", function () { <br>
+* <br>
+* // The scope of the callback will be a Node instance <br>
+* // representing the root menu (<div id="menu-1">). <br>
+* // Therefore, since "this" represents a Node instance, it <br>
+* // is possible to just call "this.plug" passing in a <br>
+* // reference to the MenuNav Node Plugin. <br>
+* <br>
+* this.plug(Y.Plugin.NodeMenuNav); <br>
+* <br>
+* }, "#menu-1"); <br>
+* <br>
+* }); <br>
+* <br>
+* </script> <br>
+* </code>
+* </p>
+*
+* <p>The MenuNav Node Plugin has several configuration properties that can be
+* set via an object literal that is passed as a second argument to a Node
+* instance's <code>plug</code> method.
+* </p>
+*
+* <p>
+* <code>
+* <script type="text/javascript"> <br>
+* <br>
+* // Call the "use" method, passing in "node-menunav". This will <br>
+* // load the script and CSS for the MenuNav Node Plugin and all of <br>
+* // the required dependencies. <br>
+* <br>
+* YUI().use("node-menunav", function(Y) { <br>
+* <br>
+* // Use the "contentready" event to initialize the menu when <br>
+* // the subtree of element representing the root menu <br>
+* // (<div id="menu-1">) is ready to be scripted. <br>
+* <br>
+* Y.on("contentready", function () { <br>
+* <br>
+* // The scope of the callback will be a Node instance <br>
+* // representing the root menu (<div id="menu-1">). <br>
+* // Therefore, since "this" represents a Node instance, it <br>
+* // is possible to just call "this.plug" passing in a <br>
+* // reference to the MenuNav Node Plugin. <br>
+* <br>
+* this.plug(Y.Plugin.NodeMenuNav, { mouseOutHideDelay: 1000 });
+* <br><br>
+* }, "#menu-1"); <br>
+* <br>
+* }); <br>
+* <br>
+* </script> <br>
+* </code>
+* </p>
+*
+* @module node-menunav
+*/
+
+
+ // Util shortcuts
+
+var UA = Y.UA,
+ later = Y.later,
+ getClassName = Y.ClassNameManager.getClassName,
+
+
+
+ // Frequently used strings
+
+ MENU = "menu",
+ MENUITEM = "menuitem",
+ HIDDEN = "hidden",
+ PARENT_NODE = "parentNode",
+ CHILDREN = "children",
+ OFFSET_HEIGHT = "offsetHeight",
+ OFFSET_WIDTH = "offsetWidth",
+ PX = "px",
+ ID = "id",
+ PERIOD = ".",
+ HANDLED_MOUSEOUT = "handledMouseOut",
+ HANDLED_MOUSEOVER = "handledMouseOver",
+ ACTIVE = "active",
+ LABEL = "label",
+ LOWERCASE_A = "a",
+ MOUSEDOWN = "mousedown",
+ KEYDOWN = "keydown",
+ CLICK = "click",
+ EMPTY_STRING = "",
+ FIRST_OF_TYPE = "first-of-type",
+ ROLE = "role",
+ PRESENTATION = "presentation",
+ DESCENDANTS = "descendants",
+ UI = "UI",
+ ACTIVE_DESCENDANT = "activeDescendant",
+ USE_ARIA = "useARIA",
+ ARIA_HIDDEN = "aria-hidden",
+ CONTENT = "content",
+ HOST = "host",
+ ACTIVE_DESCENDANT_CHANGE = ACTIVE_DESCENDANT + "Change",
+
+ STANDARD_QUERY = ">.yui-menu-content>ul>li>a",
+ EXTENDED_QUERY = ">.yui-menu-content>ul>li>.yui-menu-label>a:first-child",
+
+
+ // Attribute keys
+
+ AUTO_SUBMENU_DISPLAY = "autoSubmenuDisplay",
+ MOUSEOUT_HIDE_DELAY = "mouseOutHideDelay",
+
+
+ // CSS class names
+
+ CSS_MENU = getClassName(MENU),
+ CSS_MENU_HIDDEN = getClassName(MENU, HIDDEN),
+ CSS_MENU_HORIZONTAL = getClassName(MENU, "horizontal"),
+ CSS_MENU_LABEL = getClassName(MENU, LABEL),
+ CSS_MENU_LABEL_ACTIVE = getClassName(MENU, LABEL, ACTIVE),
+ CSS_MENU_LABEL_MENUVISIBLE = getClassName(MENU, LABEL, (MENU + "visible")),
+ CSS_MENUITEM = getClassName(MENUITEM),
+ CSS_MENUITEM_ACTIVE = getClassName(MENUITEM, ACTIVE),
+
+
+ // CSS selectors
+
+ MENU_SELECTOR = PERIOD + CSS_MENU,
+ MENU_TOGGLE_SELECTOR = (PERIOD + getClassName(MENU, "toggle"));
+
+
+// Utility functions
+
+
+var getPreviousSibling = function (node) {
+
+ var oPrevious = node.previous(),
+ oChildren;
+
+ if (!oPrevious) {
+ oChildren = node.get(PARENT_NODE).get(CHILDREN);
+ oPrevious = oChildren.item(oChildren.size() - 1);
+ }
+
+ return oPrevious;
+
+};
+
+
+var getNextSibling = function (node) {
+
+ var oNext = node.next();
+
+ if (!oNext) {
+ oNext = node.get(PARENT_NODE).get(CHILDREN).item(0);
+ }
+
+ return oNext;
+
+};
+
+
+var isAnchor = function (node) {
+
+ var bReturnVal = false;
+
+ if (node) {
+ bReturnVal = node.get("nodeName").toLowerCase() === LOWERCASE_A;
+ }
+
+ return bReturnVal;
+
+};
+
+
+var isMenuItem = function (node) {
+
+ return node.hasClass(CSS_MENUITEM);
+
+};
+
+
+var isMenuLabel = function (node) {
+
+ return node.hasClass(CSS_MENU_LABEL);
+
+};
+
+
+var isHorizontalMenu = function (menu) {
+
+ return menu.hasClass(CSS_MENU_HORIZONTAL);
+
+};
+
+
+var hasVisibleSubmenu = function (menuLabel) {
+
+ return menuLabel.hasClass(CSS_MENU_LABEL_MENUVISIBLE);
+
+};
+
+
+var getItemAnchor = function (node) {
+
+ return isAnchor(node) ? node : node.one(LOWERCASE_A);
+
+};
+
+
+var getNodeWithClass = function (node, className, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+
+ if (node.hasClass(className)) {
+ oItem = node;
+ }
+
+ if (!oItem && searchAncestors) {
+ oItem = node.ancestor((PERIOD + className));
+ }
+
+ }
+
+ return oItem;
+
+};
+
+
+var getParentMenu = function (node) {
+
+ return node.ancestor(MENU_SELECTOR);
+
+};
+
+
+var getMenu = function (node, searchAncestors) {
+
+ return getNodeWithClass(node, CSS_MENU, searchAncestors);
+
+};
+
+
+var getMenuItem = function (node, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+ oItem = getNodeWithClass(node, CSS_MENUITEM, searchAncestors);
+ }
+
+ return oItem;
+
+};
+
+
+var getMenuLabel = function (node, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+
+ if (searchAncestors) {
+ oItem = getNodeWithClass(node, CSS_MENU_LABEL, searchAncestors);
+ }
+ else {
+ oItem = getNodeWithClass(node, CSS_MENU_LABEL) ||
+ node.one((PERIOD + CSS_MENU_LABEL));
+ }
+
+ }
+
+ return oItem;
+
+};
+
+
+var getItem = function (node, searchAncestors) {
+
+ var oItem;
+
+ if (node) {
+ oItem = getMenuItem(node, searchAncestors) ||
+ getMenuLabel(node, searchAncestors);
+ }
+
+ return oItem;
+
+};
+
+
+var getFirstItem = function (menu) {
+
+ return getItem(menu.one("li"));
+
+};
+
+
+var getActiveClass = function (node) {
+
+ return isMenuItem(node) ? CSS_MENUITEM_ACTIVE : CSS_MENU_LABEL_ACTIVE;
+
+};
+
+
+var handleMouseOverForNode = function (node, target) {
+
+ return node && !node[HANDLED_MOUSEOVER] &&
+ (node.compareTo(target) || node.contains(target));
+
+};
+
+
+var handleMouseOutForNode = function (node, relatedTarget) {
+
+ return node && !node[HANDLED_MOUSEOUT] &&
+ (!node.compareTo(relatedTarget) && !node.contains(relatedTarget));
+
+};
+
+/**
+* The NodeMenuNav class is a plugin for a Node instance. The class is used via
+* the <a href="Node.html#method_plug"><code>plug</code></a> method of Node and
+* should not be instantiated directly.
+* @namespace plugin
+* @class NodeMenuNav
+*/
+var NodeMenuNav = function () {
+
+ NodeMenuNav.superclass.constructor.apply(this, arguments);
+
+};
+
+NodeMenuNav.NAME = "nodeMenuNav";
+NodeMenuNav.NS = "menuNav";
+
+
+/**
+* @property NodeMenuNav.SHIM_TEMPLATE_TITLE
+* @description String representing the value for the <code>title</code>
+* attribute for the shim used to prevent <code><select></code> elements
+* from poking through menus in IE 6.
+* @default "Menu Stacking Shim"
+* @type String
+*/
+NodeMenuNav.SHIM_TEMPLATE_TITLE = "Menu Stacking Shim";
+
+
+/**
+* @property NodeMenuNav.SHIM_TEMPLATE
+* @description String representing the HTML used to create the
+* <code><iframe></code> shim used to prevent
+* <code><select></code> elements from poking through menus in IE 6.
+* @default "<iframe frameborder="0" tabindex="-1"
+* class="yui-shim" title="Menu Stacking Shim"
+* src="javascript:false;"></iframe>"
+* @type String
+*/
+
+// <iframe> shim notes:
+//
+// 1) Need to set the "frameBorder" property to 0 to suppress the default
+// <iframe> border in IE. (Setting the CSS "border" property alone doesn't
+// suppress it.)
+//
+// 2) The "src" attribute of the <iframe> is set to "javascript:false;" so
+// that it won't load a page inside it, preventing the secure/nonsecure
+// warning in IE when using HTTPS.
+//
+// 3) Since the role of the <iframe> shim is completely presentational, its
+// "tabindex" attribute is set to "-1" and its title attribute is set to
+// "Menu Stacking Shim". Both strategies help users of screen readers to
+// avoid mistakenly interacting with the <iframe> shim.
+
+NodeMenuNav.SHIM_TEMPLATE = '<iframe frameborder="0" tabindex="-1" class="' +
+ getClassName("shim") +
+ '" title="' + NodeMenuNav.SHIM_TEMPLATE_TITLE +
+ '" src="javascript:false;"></iframe>';
+
+
+NodeMenuNav.ATTRS = {
+
+ /**
+ * Boolean indicating if use of the WAI-ARIA Roles and States should be
+ * enabled for the menu.
+ *
+ * @attribute useARIA
+ * @readOnly
+ * @writeOnce
+ * @default true
+ * @type boolean
+ */
+ useARIA: {
+
+ value: true,
+ writeOnce: true,
+ lazyAdd: false,
+ setter: function (value) {
+
+ var oMenu = this.get(HOST),
+ oMenuLabel,
+ oMenuToggle,
+ oSubmenu,
+ sID;
+
+ if (value) {
+
+ oMenu.set(ROLE, MENU);
+
+ oMenu.all("ul,li,." + getClassName(MENU, CONTENT)).set(ROLE, PRESENTATION);
+
+ oMenu.all((PERIOD + getClassName(MENUITEM, CONTENT))).set(ROLE, MENUITEM);
+
+ oMenu.all((PERIOD + CSS_MENU_LABEL)).each(function (node) {
+
+ oMenuLabel = node;
+ oMenuToggle = node.one(MENU_TOGGLE_SELECTOR);
+
+ if (oMenuToggle) {
+ oMenuToggle.set(ROLE, PRESENTATION);
+ oMenuLabel = oMenuToggle.previous();
+ }
+
+ oMenuLabel.set(ROLE, MENUITEM);
+ oMenuLabel.set("aria-haspopup", true);
+
+ oSubmenu = node.next();
+
+ if (oSubmenu) {
+
+ oSubmenu.set(ROLE, MENU);
+
+ oMenuLabel = oSubmenu.previous();
+ oMenuToggle = oMenuLabel.one(MENU_TOGGLE_SELECTOR);
+
+ if (oMenuToggle) {
+ oMenuLabel = oMenuToggle;
+ }
+
+ sID = Y.stamp(oMenuLabel);
+
+ if (!oMenuLabel.get(ID)) {
+ oMenuLabel.set(ID, sID);
+ }
+
+ oSubmenu.set("aria-labelledby", sID);
+ oSubmenu.set(ARIA_HIDDEN, true);
+
+ }
+
+ });
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * Boolean indicating if submenus are automatically made visible when the
+ * user mouses over the menu's items.
+ *
+ * @attribute autoSubmenuDisplay
+ * @readOnly
+ * @writeOnce
+ * @default true
+ * @type boolean
+ */
+ autoSubmenuDisplay: {
+
+ value: true,
+ writeOnce: true
+
+ },
+
+
+ /**
+ * Number indicating the time (in milliseconds) that should expire before a
+ * submenu is made visible when the user mouses over the menu's label.
+ *
+ * @attribute submenuShowDelay
+ * @readOnly
+ * @writeOnce
+ * @default 250
+ * @type Number
+ */
+ submenuShowDelay: {
+
+ value: 250,
+ writeOnce: true
+
+ },
+
+
+ /**
+ * Number indicating the time (in milliseconds) that should expire before a
+ * submenu is hidden when the user mouses out of a menu label heading in the
+ * direction of a submenu.
+ *
+ * @attribute submenuHideDelay
+ * @readOnly
+ * @writeOnce
+ * @default 250
+ * @type Number
+ */
+ submenuHideDelay: {
+
+ value: 250,
+ writeOnce: true
+
+ },
+
+
+ /**
+ * Number indicating the time (in milliseconds) that should expire before a
+ * submenu is hidden when the user mouses out of it.
+ *
+ * @attribute mouseOutHideDelay
+ * @readOnly
+ * @writeOnce
+ * @default 750
+ * @type Number
+ */
+ mouseOutHideDelay: {
+
+ value: 750,
+ writeOnce: true
+
+ }
+
+};
+
+
+Y.extend(NodeMenuNav, Y.Plugin.Base, {
+
+ // Protected properties
+
+ /**
+ * @property _rootMenu
+ * @description Node instance representing the root menu in the menu.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _rootMenu: null,
+
+
+ /**
+ * @property _activeItem
+ * @description Node instance representing the menu's active descendent:
+ * the menuitem or menu label the user is currently interacting with.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _activeItem: null,
+
+
+ /**
+ * @property _activeMenu
+ * @description Node instance representing the menu that is the parent of
+ * the menu's active descendent.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _activeMenu: null,
+
+
+ /**
+ * @property _hasFocus
+ * @description Boolean indicating if the menu has focus.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _hasFocus: false,
+
+
+ // In gecko-based browsers a mouseover and mouseout event will fire even
+ // if a DOM element moves out from under the mouse without the user
+ // actually moving the mouse. This bug affects NodeMenuNav because the
+ // user can hit the Esc key to hide a menu, and if the mouse is over the
+ // menu when the user presses Esc, the _onMenuMouseOut handler will be
+ // called. To fix this bug the following flag (_blockMouseEvent) is used
+ // to block the code in the _onMenuMouseOut handler from executing.
+
+ /**
+ * @property _blockMouseEvent
+ * @description Boolean indicating whether or not to handle the
+ * "mouseover" event.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _blockMouseEvent: false,
+
+
+ /**
+ * @property _currentMouseX
+ * @description Number representing the current x coordinate of the mouse
+ * inside the menu.
+ * @default 0
+ * @protected
+ * @type Number
+ */
+ _currentMouseX: 0,
+
+
+ /**
+ * @property _movingToSubmenu
+ * @description Boolean indicating if the mouse is moving from a menu
+ * label to its corresponding submenu.
+ * @default false
+ * @protected
+ * @type Boolean
+ */
+ _movingToSubmenu: false,
+
+
+ /**
+ * @property _showSubmenuTimer
+ * @description Timer used to show a submenu.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _showSubmenuTimer: null,
+
+
+ /**
+ * @property _hideSubmenuTimer
+ * @description Timer used to hide a submenu.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _hideSubmenuTimer: null,
+
+
+ /**
+ * @property _hideAllSubmenusTimer
+ * @description Timer used to hide a all submenus.
+ * @default null
+ * @protected
+ * @type Object
+ */
+ _hideAllSubmenusTimer: null,
+
+
+ /**
+ * @property _firstItem
+ * @description Node instance representing the first item (menuitem or menu
+ * label) in the root menu of a menu.
+ * @default null
+ * @protected
+ * @type Node
+ */
+ _firstItem: null,
+
+
+ // Public methods
+
+
+ initializer: function (config) {
+
+ var menuNav = this,
+ oRootMenu = this.get(HOST),
+ aHandlers = [],
+ oDoc;
+
+
+ if (oRootMenu) {
+
+ menuNav._rootMenu = oRootMenu;
+
+ oRootMenu.all("ul:first-child").addClass(FIRST_OF_TYPE);
+
+ // Hide all visible submenus
+
+ oRootMenu.all(MENU_SELECTOR).addClass(CSS_MENU_HIDDEN);
+
+
+ // Wire up all event handlers
+
+ aHandlers.push(oRootMenu.on("mouseover", menuNav._onMouseOver, menuNav));
+ aHandlers.push(oRootMenu.on("mouseout", menuNav._onMouseOut, menuNav));
+ aHandlers.push(oRootMenu.on("mousemove", menuNav._onMouseMove, menuNav));
+ aHandlers.push(oRootMenu.on(MOUSEDOWN, menuNav._toggleSubmenuDisplay, menuNav));
+ aHandlers.push(Y.on("key", menuNav._toggleSubmenuDisplay, oRootMenu, "down:13", menuNav));
+ aHandlers.push(oRootMenu.on(CLICK, menuNav._toggleSubmenuDisplay, menuNav));
+ aHandlers.push(oRootMenu.on("keypress", menuNav._onKeyPress, menuNav));
+ aHandlers.push(oRootMenu.on(KEYDOWN, menuNav._onKeyDown, menuNav));
+
+ oDoc = oRootMenu.get("ownerDocument");
+
+ aHandlers.push(oDoc.on(MOUSEDOWN, menuNav._onDocMouseDown, menuNav));
+ aHandlers.push(oDoc.on("focus", menuNav._onDocFocus, menuNav));
+
+ this._eventHandlers = aHandlers;
+
+ menuNav._initFocusManager();
+
+ }
+
+
+ },
+
+ destructor: function () {
+
+ var aHandlers = this._eventHandlers;
+
+ if (aHandlers) {
+
+ Y.Array.each(aHandlers, function (handle) {
+ handle.detach();
+ });
+
+ this._eventHandlers = null;
+
+ }
+
+ this.get(HOST).unplug("focusManager");
+
+ },
+
+
+
+ // Protected methods
+
+ /**
+ * @method _isRoot
+ * @description Returns a boolean indicating if the specified menu is the
+ * root menu in the menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @return {Boolean} Boolean indicating if the specified menu is the root
+ * menu in the menu.
+ */
+ _isRoot: function (menu) {
+
+ return this._rootMenu.compareTo(menu);
+
+ },
+
+
+ /**
+ * @method _getTopmostSubmenu
+ * @description Returns the topmost submenu of a submenu hierarchy.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @return {Node} Node instance representing a menu.
+ */
+ _getTopmostSubmenu: function (menu) {
+
+ var menuNav = this,
+ oMenu = getParentMenu(menu),
+ returnVal;
+
+
+ if (!oMenu) {
+ returnVal = menu;
+ }
+ else if (menuNav._isRoot(oMenu)) {
+ returnVal = menu;
+ }
+ else {
+ returnVal = menuNav._getTopmostSubmenu(oMenu);
+ }
+
+ return returnVal;
+
+ },
+
+
+ /**
+ * @method _clearActiveItem
+ * @description Clears the menu's active descendent.
+ * @protected
+ */
+ _clearActiveItem: function () {
+
+ var menuNav = this,
+ oActiveItem = menuNav._activeItem;
+
+ if (oActiveItem) {
+ oActiveItem.removeClass(getActiveClass(oActiveItem));
+ }
+
+ menuNav._activeItem = null;
+
+ },
+
+
+ /**
+ * @method _setActiveItem
+ * @description Sets the specified menuitem or menu label as the menu's
+ * active descendent.
+ * @protected
+ * @param {Node} item Node instance representing a menuitem or menu label.
+ */
+ _setActiveItem: function (item) {
+
+ var menuNav = this;
+
+ if (item) {
+
+ menuNav._clearActiveItem();
+
+ item.addClass(getActiveClass(item));
+
+ menuNav._activeItem = item;
+
+ }
+
+ },
+
+
+ /**
+ * @method _focusItem
+ * @description Focuses the specified menuitem or menu label.
+ * @protected
+ * @param {Node} item Node instance representing a menuitem or menu label.
+ */
+ _focusItem: function (item) {
+
+ var menuNav = this,
+ oMenu,
+ oItem;
+
+ if (item && menuNav._hasFocus) {
+
+ oMenu = getParentMenu(item);
+ oItem = getItemAnchor(item);
+
+ if (oMenu && !oMenu.compareTo(menuNav._activeMenu)) {
+ menuNav._activeMenu = oMenu;
+ menuNav._initFocusManager();
+ }
+
+ menuNav._focusManager.focus(oItem);
+
+ }
+
+ },
+
+
+ /**
+ * @method _showMenu
+ * @description Shows the specified menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ */
+ _showMenu: function (menu) {
+
+ var oParentMenu = getParentMenu(menu),
+ oLI = menu.get(PARENT_NODE),
+ aXY = oLI.getXY();
+
+
+ if (this.get(USE_ARIA)) {
+ menu.set(ARIA_HIDDEN, false);
+ }
+
+
+ if (isHorizontalMenu(oParentMenu)) {
+ aXY[1] = aXY[1] + oLI.get(OFFSET_HEIGHT);
+ }
+ else {
+ aXY[0] = aXY[0] + oLI.get(OFFSET_WIDTH);
+ }
+
+ menu.setXY(aXY);
+
+ if (UA.ie < 8) {
+
+ if (UA.ie === 6 && !menu.hasIFrameShim) {
+
+ menu.appendChild(Y.Node.create(NodeMenuNav.SHIM_TEMPLATE));
+ menu.hasIFrameShim = true;
+
+ }
+
+ // Clear previous values for height and width
+
+ menu.setStyles({ height: EMPTY_STRING, width: EMPTY_STRING });
+
+ // Set the width and height of the menu's bounding box - this is
+ // necessary for IE 6 so that the CSS for the <iframe> shim can
+ // simply set the <iframe>'s width and height to 100% to ensure
+ // that dimensions of an <iframe> shim are always sync'd to the
+ // that of its parent menu. Specifying a width and height also
+ // helps when positioning decorator elements (for creating effects
+ // like rounded corners) inside a menu's bounding box in IE 7.
+
+ menu.setStyles({
+ height: (menu.get(OFFSET_HEIGHT) + PX),
+ width: (menu.get(OFFSET_WIDTH) + PX) });
+
+ }
+
+ menu.previous().addClass(CSS_MENU_LABEL_MENUVISIBLE);
+ menu.removeClass(CSS_MENU_HIDDEN);
+
+ },
+
+
+ /**
+ * @method _hideMenu
+ * @description Hides the specified menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @param {Boolean} activateAndFocusLabel Boolean indicating if the label
+ * for the specified
+ * menu should be focused and set as active.
+ */
+ _hideMenu: function (menu, activateAndFocusLabel) {
+
+ var menuNav = this,
+ oLabel = menu.previous(),
+ oActiveItem;
+
+ oLabel.removeClass(CSS_MENU_LABEL_MENUVISIBLE);
+
+
+ if (activateAndFocusLabel) {
+ menuNav._focusItem(oLabel);
+ menuNav._setActiveItem(oLabel);
+ }
+
+ oActiveItem = menu.one((PERIOD + CSS_MENUITEM_ACTIVE));
+
+ if (oActiveItem) {
+ oActiveItem.removeClass(CSS_MENUITEM_ACTIVE);
+ }
+
+ // Clear the values for top and left that were set by the call to
+ // "setXY" when the menu was shown so that the hidden position
+ // specified in the core CSS file will take affect.
+
+ menu.setStyles({ left: EMPTY_STRING, top: EMPTY_STRING });
+
+ menu.addClass(CSS_MENU_HIDDEN);
+
+ if (menuNav.get(USE_ARIA)) {
+ menu.set(ARIA_HIDDEN, true);
+ }
+
+ },
+
+
+ /**
+ * @method _hideAllSubmenus
+ * @description Hides all submenus of the specified menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ */
+ _hideAllSubmenus: function (menu) {
+
+ var menuNav = this;
+
+ menu.all(MENU_SELECTOR).each(Y.bind(function (submenuNode) {
+
+ menuNav._hideMenu(submenuNode);
+
+ }, menuNav));
+
+ },
+
+
+ /**
+ * @method _cancelShowSubmenuTimer
+ * @description Cancels the timer used to show a submenu.
+ * @protected
+ */
+ _cancelShowSubmenuTimer: function () {
+
+ var menuNav = this,
+ oShowSubmenuTimer = menuNav._showSubmenuTimer;
+
+ if (oShowSubmenuTimer) {
+ oShowSubmenuTimer.cancel();
+ menuNav._showSubmenuTimer = null;
+ }
+
+ },
+
+
+ /**
+ * @method _cancelHideSubmenuTimer
+ * @description Cancels the timer used to hide a submenu.
+ * @protected
+ */
+ _cancelHideSubmenuTimer: function () {
+
+ var menuNav = this,
+ oHideSubmenuTimer = menuNav._hideSubmenuTimer;
+
+
+ if (oHideSubmenuTimer) {
+ oHideSubmenuTimer.cancel();
+ menuNav._hideSubmenuTimer = null;
+ }
+
+ },
+
+
+ /**
+ * @method _initFocusManager
+ * @description Initializes and updates the Focus Manager so that is is
+ * always managing descendants of the active menu.
+ * @protected
+ */
+ _initFocusManager: function () {
+
+ var menuNav = this,
+ oRootMenu = menuNav._rootMenu,
+ oMenu = menuNav._activeMenu || oRootMenu,
+ sSelectorBase =
+ menuNav._isRoot(oMenu) ? EMPTY_STRING : ("#" + oMenu.get("id")),
+ oFocusManager = menuNav._focusManager,
+ sKeysVal,
+ sDescendantSelector,
+ sQuery;
+
+ if (isHorizontalMenu(oMenu)) {
+
+ sDescendantSelector = sSelectorBase + STANDARD_QUERY + "," +
+ sSelectorBase + EXTENDED_QUERY;
+
+ sKeysVal = { next: "down:39", previous: "down:37" };
+
+ }
+ else {
+
+ sDescendantSelector = sSelectorBase + STANDARD_QUERY;
+ sKeysVal = { next: "down:40", previous: "down:38" };
+
+ }
+
+
+ if (!oFocusManager) {
+
+ oRootMenu.plug(Y.Plugin.NodeFocusManager, {
+ descendants: sDescendantSelector,
+ keys: sKeysVal,
+ circular: true
+ });
+
+ oFocusManager = oRootMenu.focusManager;
+
+ sQuery = "#" + oRootMenu.get("id") + " .yui-menu a," +
+ MENU_TOGGLE_SELECTOR;
+
+ oRootMenu.all(sQuery).set("tabIndex", -1);
+
+ oFocusManager.on(ACTIVE_DESCENDANT_CHANGE,
+ this._onActiveDescendantChange, oFocusManager, this);
+
+ oFocusManager.after(ACTIVE_DESCENDANT_CHANGE,
+ this._afterActiveDescendantChange, oFocusManager, this);
+
+ menuNav._focusManager = oFocusManager;
+
+ }
+ else {
+
+ oFocusManager.set(ACTIVE_DESCENDANT, -1);
+ oFocusManager.set(DESCENDANTS, sDescendantSelector);
+ oFocusManager.set("keys", sKeysVal);
+
+ }
+
+ },
+
+
+ // Event handlers for discrete pieces of pieces of the menu
+
+
+ /**
+ * @method _onActiveDescendantChange
+ * @description "activeDescendantChange" event handler for menu's
+ * Focus Manager.
+ * @protected
+ * @param {Object} event Object representing the Attribute change event.
+ * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
+ */
+ _onActiveDescendantChange: function (event, menuNav) {
+
+ if (event.src === UI && menuNav._activeMenu &&
+ !menuNav._movingToSubmenu) {
+
+ menuNav._hideAllSubmenus(menuNav._activeMenu);
+
+ }
+
+ },
+
+
+ /**
+ * @method _afterActiveDescendantChange
+ * @description "activeDescendantChange" event handler for menu's
+ * Focus Manager.
+ * @protected
+ * @param {Object} event Object representing the Attribute change event.
+ * @param {NodeMenuNav} menuNav Object representing the NodeMenuNav instance.
+ */
+ _afterActiveDescendantChange: function (event, menuNav) {
+
+ var oItem;
+
+ if (event.src === UI) {
+ oItem = getItem(this.get(DESCENDANTS).item(event.newVal), true);
+ menuNav._setActiveItem(oItem);
+ }
+
+ },
+
+
+ /**
+ * @method _onDocFocus
+ * @description "focus" event handler for the owner document of the MenuNav.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onDocFocus: function (event) {
+
+ var menuNav = this,
+ oActiveItem = menuNav._activeItem,
+ oTarget = event.target,
+ oMenu;
+
+
+ if (menuNav._rootMenu.contains(oTarget)) { // The menu has focus
+
+ if (menuNav._hasFocus) {
+
+ oMenu = getParentMenu(oTarget);
+
+ // If the element that was focused is a descendant of the
+ // root menu, but is in a submenu not currently being
+ // managed by the Focus Manager, update the Focus Manager so
+ // that it is now managing the submenu that is the parent of
+ // the element that was focused.
+
+ if (!menuNav._activeMenu.compareTo(oMenu)) {
+
+ menuNav._activeMenu = oMenu;
+ menuNav._initFocusManager();
+ menuNav._focusManager.set(ACTIVE_DESCENDANT, oTarget);
+ menuNav._setActiveItem(getItem(oTarget, true));
+
+ }
+
+ }
+ else { // Initial focus
+
+ // First time the menu has been focused, need to setup focused
+ // state and established active active descendant
+
+ menuNav._hasFocus = true;
+
+ oActiveItem = getItem(oTarget, true);
+
+ if (oActiveItem) {
+ menuNav._setActiveItem(oActiveItem);
+ }
+
+ }
+
+ }
+ else { // The menu has lost focus
+
+ menuNav._clearActiveItem();
+
+ menuNav._cancelShowSubmenuTimer();
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+
+ menuNav._activeMenu = menuNav._rootMenu;
+ menuNav._initFocusManager();
+
+ menuNav._focusManager.set(ACTIVE_DESCENDANT, 0);
+
+ menuNav._hasFocus = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuMouseOver
+ * @description "mouseover" event handler for a menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuMouseOver: function (menu, event) {
+
+ var menuNav = this,
+ oHideAllSubmenusTimer = menuNav._hideAllSubmenusTimer;
+
+ if (oHideAllSubmenusTimer) {
+ oHideAllSubmenusTimer.cancel();
+ menuNav._hideAllSubmenusTimer = null;
+ }
+
+ menuNav._cancelHideSubmenuTimer();
+
+ // Need to update the FocusManager in advance of focus a new
+ // Menu in order to avoid the FocusManager thinking that
+ // it has lost focus
+
+ if (menu && !menu.compareTo(menuNav._activeMenu)) {
+ menuNav._activeMenu = menu;
+
+ if (menuNav._hasFocus) {
+ menuNav._initFocusManager();
+ }
+
+ }
+
+ if (menuNav._movingToSubmenu && isHorizontalMenu(menu)) {
+ menuNav._movingToSubmenu = false;
+ }
+
+ },
+
+
+ /**
+ * @method _hideAndFocusLabel
+ * @description Hides all of the submenus of the root menu and focuses the
+ * label of the topmost submenu
+ * @protected
+ */
+ _hideAndFocusLabel: function () {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oSubmenu;
+
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+
+ if (oActiveMenu) {
+
+ // Focus the label element for the topmost submenu
+ oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
+ menuNav._focusItem(oSubmenu.previous());
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuMouseOut
+ * @description "mouseout" event handler for a menu.
+ * @protected
+ * @param {Node} menu Node instance representing a menu.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuMouseOut: function (menu, event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oRelatedTarget = event.relatedTarget,
+ oActiveItem = menuNav._activeItem,
+ oParentMenu,
+ oMenu;
+
+
+ if (oActiveMenu && !oActiveMenu.contains(oRelatedTarget)) {
+
+ oParentMenu = getParentMenu(oActiveMenu);
+
+
+ if (oParentMenu && !oParentMenu.contains(oRelatedTarget)) {
+
+ if (menuNav.get(MOUSEOUT_HIDE_DELAY) > 0) {
+
+ menuNav._cancelShowSubmenuTimer();
+
+ menuNav._hideAllSubmenusTimer =
+
+ later(menuNav.get(MOUSEOUT_HIDE_DELAY),
+ menuNav, menuNav._hideAndFocusLabel);
+
+ }
+
+ }
+ else {
+
+ if (oActiveItem) {
+
+ oMenu = getParentMenu(oActiveItem);
+
+ if (!menuNav._isRoot(oMenu)) {
+ menuNav._focusItem(oMenu.previous());
+ }
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuLabelMouseOver
+ * @description "mouseover" event handler for a menu label.
+ * @protected
+ * @param {Node} menuLabel Node instance representing a menu label.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuLabelMouseOver: function (menuLabel, event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ bIsRoot = menuNav._isRoot(oActiveMenu),
+ bUseAutoSubmenuDisplay =
+ (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
+ oSubmenu;
+
+
+ menuNav._focusItem(menuLabel);
+ menuNav._setActiveItem(menuLabel);
+
+
+ if (bUseAutoSubmenuDisplay && !menuNav._movingToSubmenu) {
+
+ menuNav._cancelHideSubmenuTimer();
+ menuNav._cancelShowSubmenuTimer();
+
+
+ if (!hasVisibleSubmenu(menuLabel)) {
+
+ oSubmenu = menuLabel.next();
+
+
+ if (oSubmenu) {
+
+ menuNav._hideAllSubmenus(oActiveMenu);
+
+ menuNav._showSubmenuTimer =
+ later(menuNav.get("submenuShowDelay"), menuNav,
+ menuNav._showMenu, oSubmenu);
+
+ }
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuLabelMouseOut
+ * @description "mouseout" event handler for a menu label.
+ * @protected
+ * @param {Node} menuLabel Node instance representing a menu label.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuLabelMouseOut: function (menuLabel, event) {
+
+ var menuNav = this,
+ bIsRoot = menuNav._isRoot(menuNav._activeMenu),
+ bUseAutoSubmenuDisplay =
+ (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot),
+
+ oRelatedTarget = event.relatedTarget,
+ oSubmenu = menuLabel.next();
+
+ menuNav._clearActiveItem();
+
+ if (bUseAutoSubmenuDisplay) {
+
+ if (menuNav._movingToSubmenu &&
+ !menuNav._showSubmenuTimer && oSubmenu) {
+
+ // If the mouse is moving diagonally toward the submenu and
+ // another submenu isn't in the process of being displayed
+ // (via a timer), then hide the submenu via a timer to give
+ // the user some time to reach the submenu.
+
+ menuNav._hideSubmenuTimer =
+ later(menuNav.get("submenuHideDelay"), menuNav,
+ menuNav._hideMenu, oSubmenu);
+
+ }
+ else if (!menuNav._movingToSubmenu && oSubmenu &&
+ !oSubmenu.contains(oRelatedTarget) &&
+ !oRelatedTarget.compareTo(oSubmenu)) {
+
+ // If the mouse is not moving toward the submenu, cancel any
+ // submenus that might be in the process of being displayed
+ // (via a timer) and hide this submenu immediately.
+
+ menuNav._cancelShowSubmenuTimer();
+
+ menuNav._hideMenu(oSubmenu);
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuItemMouseOver
+ * @description "mouseover" event handler for a menuitem.
+ * @protected
+ * @param {Node} menuItem Node instance representing a menuitem.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuItemMouseOver: function (menuItem, event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ bIsRoot = menuNav._isRoot(oActiveMenu),
+ bUseAutoSubmenuDisplay =
+ (menuNav.get(AUTO_SUBMENU_DISPLAY) && bIsRoot || !bIsRoot);
+
+
+ menuNav._focusItem(menuItem);
+ menuNav._setActiveItem(menuItem);
+
+
+ if (bUseAutoSubmenuDisplay && !menuNav._movingToSubmenu) {
+
+ menuNav._hideAllSubmenus(oActiveMenu);
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMenuItemMouseOut
+ * @description "mouseout" event handler for a menuitem.
+ * @protected
+ * @param {Node} menuItem Node instance representing a menuitem.
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMenuItemMouseOut: function (menuItem, event) {
+
+ this._clearActiveItem();
+
+ },
+
+
+ /**
+ * @method _onVerticalMenuKeyDown
+ * @description "keydown" event handler for vertical menus.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onVerticalMenuKeyDown: function (event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oRootMenu = menuNav._rootMenu,
+ oTarget = event.target,
+ bPreventDefault = false,
+ nKeyCode = event.keyCode,
+ oSubmenu,
+ oParentMenu,
+ oLI,
+ oItem;
+
+
+ switch (nKeyCode) {
+
+ case 37: // left arrow
+
+ oParentMenu = getParentMenu(oActiveMenu);
+
+ if (oParentMenu && isHorizontalMenu(oParentMenu)) {
+
+ menuNav._hideMenu(oActiveMenu);
+ oLI = getPreviousSibling(oActiveMenu.get(PARENT_NODE));
+ oItem = getItem(oLI);
+
+ if (oItem) {
+
+ if (isMenuLabel(oItem)) { // Menu label
+
+ oSubmenu = oItem.next();
+
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+ else {
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+ else { // MenuItem
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+
+ }
+ else if (!menuNav._isRoot(oActiveMenu)) {
+ menuNav._hideMenu(oActiveMenu, true);
+ }
+
+
+ bPreventDefault = true;
+
+ break;
+
+ case 39: // right arrow
+
+ if (isMenuLabel(oTarget)) {
+
+ oSubmenu = oTarget.next();
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+
+ }
+ else if (isHorizontalMenu(oRootMenu)) {
+
+ oSubmenu = menuNav._getTopmostSubmenu(oActiveMenu);
+ oLI = getNextSibling(oSubmenu.get(PARENT_NODE));
+ oItem = getItem(oLI);
+
+ menuNav._hideAllSubmenus(oRootMenu);
+
+ if (oItem) {
+
+ if (isMenuLabel(oItem)) { // Menu label
+
+ oSubmenu = oItem.next();
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+ else {
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+ else { // MenuItem
+
+ menuNav._focusItem(oItem);
+ menuNav._setActiveItem(oItem);
+
+ }
+
+ }
+
+ }
+
+ bPreventDefault = true;
+
+ break;
+
+ }
+
+
+ if (bPreventDefault) {
+
+ // Prevent the browser from scrolling the window
+
+ event.preventDefault();
+
+ }
+
+ },
+
+
+ /**
+ * @method _onHorizontalMenuKeyDown
+ * @description "keydown" event handler for horizontal menus.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onHorizontalMenuKeyDown: function (event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ oTarget = event.target,
+ oFocusedItem = getItem(oTarget, true),
+ bPreventDefault = false,
+ nKeyCode = event.keyCode,
+ oSubmenu;
+
+
+ if (nKeyCode === 40) {
+
+ menuNav._hideAllSubmenus(oActiveMenu);
+
+ if (isMenuLabel(oFocusedItem)) {
+
+ oSubmenu = oFocusedItem.next();
+
+ if (oSubmenu) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+
+ bPreventDefault = true;
+
+ }
+
+ }
+
+
+ if (bPreventDefault) {
+
+ // Prevent the browser from scrolling the window
+
+ event.preventDefault();
+
+ }
+
+ },
+
+
+ // Generic DOM Event handlers
+
+
+ /**
+ * @method _onMouseMove
+ * @description "mousemove" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMouseMove: function (event) {
+
+ var menuNav = this;
+
+ // Using a timer to set the value of the "_currentMouseX" property
+ // helps improve the reliability of the calculation used to set the
+ // value of the "_movingToSubmenu" property - especially in Opera.
+
+ later(10, menuNav, function () {
+
+ menuNav._currentMouseX = event.pageX;
+
+ });
+
+ },
+
+
+ /**
+ * @method _onMouseOver
+ * @description "mouseover" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMouseOver: function (event) {
+
+ var menuNav = this,
+ oTarget,
+ oMenu,
+ oMenuLabel,
+ oParentMenu,
+ oMenuItem;
+
+
+ if (menuNav._blockMouseEvent) {
+ menuNav._blockMouseEvent = false;
+ }
+ else {
+
+ oTarget = event.target;
+ oMenu = getMenu(oTarget, true);
+ oMenuLabel = getMenuLabel(oTarget, true);
+ oMenuItem = getMenuItem(oTarget, true);
+
+
+ if (handleMouseOverForNode(oMenu, oTarget)) {
+
+ menuNav._onMenuMouseOver(oMenu, event);
+
+ oMenu[HANDLED_MOUSEOVER] = true;
+ oMenu[HANDLED_MOUSEOUT] = false;
+
+ oParentMenu = getParentMenu(oMenu);
+
+ if (oParentMenu) {
+
+ oParentMenu[HANDLED_MOUSEOUT] = true;
+ oParentMenu[HANDLED_MOUSEOVER] = false;
+
+ }
+
+ }
+
+ if (handleMouseOverForNode(oMenuLabel, oTarget)) {
+
+ menuNav._onMenuLabelMouseOver(oMenuLabel, event);
+
+ oMenuLabel[HANDLED_MOUSEOVER] = true;
+ oMenuLabel[HANDLED_MOUSEOUT] = false;
+
+ }
+
+ if (handleMouseOverForNode(oMenuItem, oTarget)) {
+
+ menuNav._onMenuItemMouseOver(oMenuItem, event);
+
+ oMenuItem[HANDLED_MOUSEOVER] = true;
+ oMenuItem[HANDLED_MOUSEOUT] = false;
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onMouseOut
+ * @description "mouseout" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onMouseOut: function (event) {
+
+ var menuNav = this,
+ oActiveMenu = menuNav._activeMenu,
+ bMovingToSubmenu = false,
+ oTarget,
+ oRelatedTarget,
+ oMenu,
+ oMenuLabel,
+ oSubmenu,
+ oMenuItem;
+
+
+ menuNav._movingToSubmenu =
+ (oActiveMenu && !isHorizontalMenu(oActiveMenu) &&
+ ((event.pageX - 5) > menuNav._currentMouseX));
+
+ oTarget = event.target;
+ oRelatedTarget = event.relatedTarget;
+ oMenu = getMenu(oTarget, true);
+ oMenuLabel = getMenuLabel(oTarget, true);
+ oMenuItem = getMenuItem(oTarget, true);
+
+
+ if (handleMouseOutForNode(oMenuLabel, oRelatedTarget)) {
+
+ menuNav._onMenuLabelMouseOut(oMenuLabel, event);
+
+ oMenuLabel[HANDLED_MOUSEOUT] = true;
+ oMenuLabel[HANDLED_MOUSEOVER] = false;
+
+ }
+
+ if (handleMouseOutForNode(oMenuItem, oRelatedTarget)) {
+
+ menuNav._onMenuItemMouseOut(oMenuItem, event);
+
+ oMenuItem[HANDLED_MOUSEOUT] = true;
+ oMenuItem[HANDLED_MOUSEOVER] = false;
+
+ }
+
+
+ if (oMenuLabel) {
+
+ oSubmenu = oMenuLabel.next();
+
+ if (oSubmenu &&
+ (oRelatedTarget.compareTo(oSubmenu) ||
+ oSubmenu.contains(oRelatedTarget))) {
+
+ bMovingToSubmenu = true;
+
+ }
+
+ }
+
+
+ if (handleMouseOutForNode(oMenu, oRelatedTarget) || bMovingToSubmenu) {
+
+ menuNav._onMenuMouseOut(oMenu, event);
+
+ oMenu[HANDLED_MOUSEOUT] = true;
+ oMenu[HANDLED_MOUSEOVER] = false;
+
+ }
+
+ },
+
+
+ /**
+ * @method _toggleSubmenuDisplay
+ * @description "mousedown," "keydown," and "click" event handler for the
+ * menu used to toggle the display of a submenu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _toggleSubmenuDisplay: function (event) {
+
+ var menuNav = this,
+ oTarget = event.target,
+ oMenuLabel = getMenuLabel(oTarget, true),
+ sType = event.type,
+ oAnchor,
+ oSubmenu,
+ sHref,
+ nHashPos,
+ nLen,
+ sId;
+
+
+ if (oMenuLabel) {
+
+ oAnchor = isAnchor(oTarget) ? oTarget : oTarget.ancestor(isAnchor);
+
+
+ if (oAnchor) {
+
+ // Need to pass "2" as a second argument to "getAttribute" for
+ // IE otherwise IE will return a fully qualified URL for the
+ // value of the "href" attribute.
+ // http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
+
+ sHref = oAnchor.getAttribute("href", 2);
+ nHashPos = sHref.indexOf("#");
+ nLen = sHref.length;
+
+ if (nHashPos === 0 && nLen > 1) {
+
+ sId = sHref.substr(1, nLen);
+ oSubmenu = oMenuLabel.next();
+
+ if (oSubmenu && (oSubmenu.get(ID) === sId)) {
+
+ if (sType === MOUSEDOWN || sType === KEYDOWN) {
+
+ if ((UA.opera || UA.gecko || UA.ie) && sType === KEYDOWN && !menuNav._preventClickHandle) {
+
+ // Prevent the browser from following the URL of
+ // the anchor element
+
+ menuNav._preventClickHandle = menuNav._rootMenu.on("click", function (event) {
+
+ event.preventDefault();
+
+ menuNav._preventClickHandle.detach();
+ menuNav._preventClickHandle = null;
+
+ });
+
+ }
+
+ if (sType == MOUSEDOWN) {
+
+ // Prevent the target from getting focused by
+ // default, since the element to be focused will
+ // be determined by weather or not the submenu
+ // is visible.
+ event.preventDefault();
+
+ // FocusManager will attempt to focus any
+ // descendant that is the target of the mousedown
+ // event. Since we want to explicitly control
+ // where focus is going, we need to call
+ // "stopImmediatePropagation" to stop the
+ // FocusManager from doing its thing.
+ event.stopImmediatePropagation();
+
+ // The "_focusItem" method relies on the
+ // "_hasFocus" property being set to true. The
+ // "_hasFocus" property is normally set via a
+ // "focus" event listener, but since we've
+ // blocked focus from happening, we need to set
+ // this property manually.
+ menuNav._hasFocus = true;
+
+ }
+
+
+ if (menuNav._isRoot(getParentMenu(oTarget))) { // Event target is a submenu label in the root menu
+
+ // Menu label toggle functionality
+
+ if (hasVisibleSubmenu(oMenuLabel)) {
+
+ menuNav._hideMenu(oSubmenu);
+ menuNav._focusItem(oMenuLabel);
+ menuNav._setActiveItem(oMenuLabel);
+
+ }
+ else {
+
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+ menuNav._showMenu(oSubmenu);
+
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+
+ }
+ else { // Event target is a submenu label within a submenu
+
+ if (menuNav._activeItem == oMenuLabel) {
+
+ menuNav._showMenu(oSubmenu);
+ menuNav._focusItem(getFirstItem(oSubmenu));
+ menuNav._setActiveItem(getFirstItem(oSubmenu));
+
+ }
+ else {
+
+ if (!oMenuLabel._clickHandle) {
+
+ oMenuLabel._clickHandle = oMenuLabel.on("click", function () {
+
+ menuNav._hideAllSubmenus(menuNav._rootMenu);
+
+ menuNav._hasFocus = false;
+ menuNav._clearActiveItem();
+
+
+ oMenuLabel._clickHandle.detach();
+
+ oMenuLabel._clickHandle = null;
+
+ });
+
+ }
+
+ }
+
+ }
+
+ }
+
+
+ if (sType === CLICK) {
+
+ // Prevent the browser from following the URL of
+ // the anchor element
+
+ event.preventDefault();
+
+ }
+
+ }
+
+ }
+
+
+ }
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyPress
+ * @description "keypress" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onKeyPress: function (event) {
+
+ switch (event.keyCode) {
+
+ case 37: // left arrow
+ case 38: // up arrow
+ case 39: // right arrow
+ case 40: // down arrow
+
+ // Prevent the browser from scrolling the window
+
+ event.preventDefault();
+
+ break;
+
+ }
+
+ },
+
+
+ /**
+ * @method _onKeyDown
+ * @description "keydown" event handler for the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onKeyDown: function (event) {
+
+ var menuNav = this,
+ oActiveItem = menuNav._activeItem,
+ oTarget = event.target,
+ oActiveMenu = getParentMenu(oTarget),
+ oSubmenu;
+
+ if (oActiveMenu) {
+
+ menuNav._activeMenu = oActiveMenu;
+
+ if (isHorizontalMenu(oActiveMenu)) {
+ menuNav._onHorizontalMenuKeyDown(event);
+ }
+ else {
+ menuNav._onVerticalMenuKeyDown(event);
+ }
+
+
+ if (event.keyCode === 27) {
+
+ if (!menuNav._isRoot(oActiveMenu)) {
+
+ if (UA.opera) {
+ later(0, menuNav, function () {
+ menuNav._hideMenu(oActiveMenu, true);
+ });
+ }
+ else {
+ menuNav._hideMenu(oActiveMenu, true);
+ }
+
+ event.stopPropagation();
+ menuNav._blockMouseEvent = UA.gecko ? true : false;
+
+ }
+ else if (oActiveItem) {
+
+ if (isMenuLabel(oActiveItem) &&
+ hasVisibleSubmenu(oActiveItem)) {
+
+ oSubmenu = oActiveItem.next();
+
+ if (oSubmenu) {
+ menuNav._hideMenu(oSubmenu);
+ }
+
+ }
+ else {
+
+ menuNav._focusManager.blur();
+
+ // This is necessary for Webkit since blurring the
+ // active menuitem won't result in the document
+ // gaining focus, meaning the that _onDocFocus
+ // listener won't clear the active menuitem.
+
+ menuNav._clearActiveItem();
+
+ menuNav._hasFocus = false;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ },
+
+ /**
+ * @method _onDocMouseDown
+ * @description "mousedown" event handler for the owner document of
+ * the menu.
+ * @protected
+ * @param {Object} event Object representing the DOM event.
+ */
+ _onDocMouseDown: function (event) {
+
+ var menuNav = this,
+ oRoot = menuNav._rootMenu,
+ oTarget = event.target;
+
+
+ if (!(oRoot.compareTo(oTarget) || oRoot.contains(oTarget))) {
+
+ menuNav._hideAllSubmenus(oRoot);
+
+ // Document doesn't receive focus in Webkit when the user mouses
+ // down on it, so the "_hasFocus" property won't get set to the
+ // correct value. The following line corrects the problem.
+
+ if (UA.webkit) {
+ menuNav._hasFocus = false;
+ menuNav._clearActiveItem();
+ }
+
+ }
+
+ }
+
+});
+
+
+Y.namespace('Plugin');
+
+Y.Plugin.NodeMenuNav = NodeMenuNav;
+
+
+}, '3.0.0' ,{requires:['node', 'classnamemanager', 'node-focusmanager']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-aria', function(Y) {
+
+/**
+ * Aria support for Node
+ * @module node
+ * @submodule node-aria
+ */
+
+Y.Node.prototype.get = function(name) {
+ var val;
+ if (re_aria.test(name)) {
+ val = Y.Node.getDOMNode(this).getAttribute(name, 2);
+ } else {
+
+ }
+
+ setter: function(val) {
+ Y.Node.getDOMNode(this).setAttribute(name, val);
+ return val;
+ }
+ });
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-aria",function(A){A.Node.re_aria=/^(?:role$|aria-)/;A.Node.prototype._addAriaAttr=function(B){this.addAttr(B,{getter:function(){return A.Node.getDOMNode(this).getAttribute(B,2);},setter:function(C){A.Node.getDOMNode(this).setAttribute(B,C);return C;}});};},"3.0.0",{requires:["node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-aria', function(Y) {
+
+/**
+ * Aria support for Node
+ * @module node
+ * @submodule node-aria
+ */
+
+Y.Node.prototype.get = function(name) {
+ var val;
+ if (re_aria.test(name)) {
+ val = Y.Node.getDOMNode(this).getAttribute(name, 2);
+ } else {
+
+ }
+
+ setter: function(val) {
+ Y.Node.getDOMNode(this).setAttribute(name, val);
+ return val;
+ }
+ });
+};
+
+
+}, '3.0.0' ,{requires:['node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-base', function(Y) {
+
+/**
+ * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
+ * @module node
+ * @submodule node-base
+ */
+
+/**
+ * The Node class provides a wrapper for manipulating DOM Nodes.
+ * Node properties can be accessed via the set/get methods.
+ * Use Y.get() to retrieve Node instances.
+ *
+ * <strong>NOTE:</strong> Node properties are accessed using
+ * the <code>set</code> and <code>get</code> methods.
+ *
+ * @class Node
+ * @constructor
+ * @for Node
+ */
+
+// "globals"
+var DOT = '.',
+ NODE_NAME = 'nodeName',
+ NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TAG_NAME = 'tagName',
+ UID = '_yuid',
+
+ Node = function(node) {
+ var uid = node[UID];
+
+ if (uid && Node._instances[uid] && Node._instances[uid]._node !== node) {
+ node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
+ }
+
+ uid = Y.stamp(node);
+ if (!uid) { // stamp failed; likely IE non-HTMLElement
+ uid = Y.guid();
+ }
+
+ this[UID] = uid;
+
+ this._node = node;
+ Node._instances[uid] = this;
+
+ this._stateProxy = node; // when augmented with Attribute
+
+ if (this._initPlugins) { // when augmented with Plugin.Host
+ this._initPlugins();
+ }
+ },
+
+ // used with previous/next/ancestor tests
+ _wrapFn = function(fn) {
+ var ret = null;
+ if (fn) {
+ ret = (typeof fn === 'string') ?
+ function(n) {
+ return Y.Selector.test(n, fn);
+ } :
+ function(n) {
+ return fn(Node.get(n));
+ };
+ }
+
+ return ret;
+ };
+// end "globals"
+
+Node.NAME = 'Node';
+
+Node.re_aria = /^(?:role$|aria-)/;
+
+Node.DOM_EVENTS = {
+ abort: true,
+ beforeunload: true,
+ blur: true,
+ change: true,
+ click: true,
+ close: true,
+ command: true,
+ contextmenu: true,
+ drag: true,
+ dragstart: true,
+ dragenter: true,
+ dragover: true,
+ dragleave: true,
+ dragend: true,
+ drop: true,
+ dblclick: true,
+ error: true,
+ focus: true,
+ keydown: true,
+ keypress: true,
+ keyup: true,
+ load: true,
+ message: true,
+ mousedown: true,
+ mousemove: true,
+ mouseout: true,
+ mouseover: true,
+ mouseup: true,
+ mousemultiwheel: true,
+ mousewheel: true,
+ submit: true,
+ mouseenter: true,
+ mouseleave: true,
+ scroll: true,
+ reset: true,
+ resize: true,
+ select: true,
+ textInput: true,
+ unload: true
+};
+
+// Add custom event adaptors to this list. This will make it so
+// that delegate, key, available, contentready, etc all will
+// be available through Node.on
+Y.mix(Node.DOM_EVENTS, Y.Env.evt.plugins);
+
+Node._instances = {};
+
+/**
+ * Retrieves the DOM node bound to a Node instance
+ * @method Node.getDOMNode
+ * @static
+ *
+ * @param {Y.Node || HTMLNode} node The Node instance or an HTMLNode
+ * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
+ * as the node argument, it is simply returned.
+ */
+Node.getDOMNode = function(node) {
+ if (node) {
+ return (node.nodeType) ? node : node._node || null;
+ }
+ return null;
+};
+
+Node.scrubVal = function(val, node) {
+ if (node && val) { // only truthy values are risky
+ if (typeof val === 'object' || typeof val === 'function') { // safari nodeList === function
+ if (NODE_TYPE in val || Y.DOM.isWindow(val)) {// node || window
+ val = Node.get(val);
+ } else if ((val.item && !val._nodes) || // dom collection or Node instance
+ (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
+ val = Y.all(val);
+ }
+ }
+ } else if (val === undefined) {
+ val = node; // for chaining
+ }
+
+ return val;
+};
+
+Node.addMethod = function(name, fn, context) {
+ if (name && fn && typeof fn === 'function') {
+ Node.prototype[name] = function() {
+ context = context || this;
+ var args = Y.Array(arguments),
+ ret;
+
+ if (args[0] && args[0] instanceof Node) {
+ args[0] = args[0]._node;
+ }
+
+ if (args[1] && args[1] instanceof Node) {
+ args[1] = args[1]._node;
+ }
+ args.unshift(this._node);
+ ret = Node.scrubVal(fn.apply(context, args), this);
+ return ret;
+ };
+ } else {
+ Y.log('unable to add method: ' + name, 'warn', 'Node');
+ }
+};
+
+Node.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ Node.addMethod(altName, host[name], host);
+ } else {
+ Y.each(name, function(n) {
+ Node.importMethod(host, n);
+ });
+ }
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.one = function(node) {
+ var instance = null,
+ cachedNode,
+ uid;
+
+ if (node) {
+ if (typeof node === 'string') {
+ if (node.indexOf('doc') === 0) { // doc OR document
+ node = Y.config.doc;
+ } else if (node.indexOf('win') === 0) { // win OR window
+ node = Y.config.win;
+ } else {
+ node = Y.Selector.query(node, null, true);
+ }
+ if (!node) {
+ return null;
+ }
+ } else if (node instanceof Node) {
+ return node; // NOTE: return
+ }
+
+ uid = node._yuid;
+ instance = Node._instances[uid]; // reuse exising instances
+ cachedNode = instance ? instance._node : null;
+ if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
+ instance = new Node(node);
+ }
+ }
+ return instance;
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.get
+ * @deprecated Use Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.get = function() {
+ Y.log('Y.get is deprecated, use Y.one', 'warn', 'deprecated');
+ return Node.one.apply(Node, arguments);
+};
+
+/**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @static
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+Node.create = function() {
+ return Node.get(Y.DOM.create.apply(Y.DOM, arguments));
+};
+
+Node.ATTRS = {
+ /**
+ * Allows for getting and setting the text of an element.
+ * Formatting is preserved and special characters are treated literally.
+ * @config text
+ * @type String
+ */
+ text: {
+ getter: function() {
+ return Y.DOM.getText(this._node);
+ },
+
+ setter: function(content) {
+ Y.DOM.setText(this._node, content);
+ return content;
+ }
+ },
+
+ 'options': {
+ getter: function() {
+ return this._node.getElementsByTagName('option');
+ }
+ },
+
+ // IE: elements collection is also FORM node which trips up scrubVal.
+ // preconverting to NodeList
+ // TODO: break out for IE only
+ 'elements': {
+ getter: function() {
+ return Y.all(this._node.elements);
+ }
+ },
+
+ /**
+ * Returns a NodeList instance of all HTMLElement children.
+ * @readOnly
+ * @config children
+ * @type NodeList
+ */
+ 'children': {
+ getter: function() {
+ var node = this._node,
+ children = node.children,
+ childNodes, i, len;
+
+ if (!children) {
+ childNodes = node.childNodes;
+ children = [];
+
+ for (i = 0, len = childNodes.length; i < len; ++i) {
+ if (childNodes[i][TAG_NAME]) {
+ children[children.length] = childNodes[i];
+ }
+ }
+ }
+ return Y.all(children);
+ }
+ },
+
+ value: {
+ getter: function() {
+ return Y.DOM.getValue(this._node);
+ },
+
+ setter: function(val) {
+ Y.DOM.setValue(this._node, val);
+ return val;
+ }
+ },
+
+ data: {
+ getter: function() {
+ return this._data;
+ },
+
+ setter: function(val) {
+ this._data = val;
+ return val;
+ }
+ }
+};
+
+// call with instance context
+Node.DEFAULT_SETTER = function(name, val) {
+ var node = this._stateProxy,
+ strPath;
+
+ if (name.indexOf(DOT) > -1) {
+ strPath = name;
+ name = name.split(DOT);
+ // only allow when defined on node
+ Y.Object.setValue(node, name, val);
+ } else if (node[name] !== undefined) { // pass thru DOM properties
+ node[name] = val;
+ }
+
+ return val;
+};
+
+// call with instance context
+Node.DEFAULT_GETTER = function(name) {
+ var node = this._stateProxy,
+ val;
+
+ if (name.indexOf && name.indexOf(DOT) > -1) {
+ val = Y.Object.getValue(node, name.split(DOT));
+ } else if (node[name] !== undefined) { // pass thru from DOM
+ val = node[name];
+ }
+
+ return val;
+};
+
+Y.augment(Node, Y.Event.Target);
+
+Y.mix(Node.prototype, {
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to a node',
+ node = this._node;
+
+ if (node) {
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ // TODO: add yuid?
+ str += ' ' + this[UID];
+ }
+ return str || errorMsg;
+ },
+
+ /**
+ * Returns an attribute value on the Node instance
+ * @method get
+ * @param {String} attr The attribute to be set
+ * @return {any} The current value of the attribute
+ */
+ get: function(attr) {
+ var val;
+
+ if (this._getAttr) { // use Attribute imple
+ val = this._getAttr(attr);
+ } else {
+ val = this._get(attr);
+ }
+
+ if (val) {
+ val = Y.Node.scrubVal(val, this);
+ }
+ return val;
+ },
+
+ _get: function(attr) {
+ var attrConfig = Node.ATTRS[attr],
+ val;
+
+ if (attrConfig && attrConfig.getter) {
+ val = attrConfig.getter.call(this);
+ } else if (Node.re_aria.test(attr)) {
+ val = this._node.getAttribute(attr, 2);
+ } else {
+ val = Node.DEFAULT_GETTER.apply(this, arguments);
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets an attribute on the Node instance.
+ * @method set
+ * @param {String} attr The attribute to be set.
+ * @param {any} val The value to set the attribute to.
+ * @chainable
+ */
+ set: function(attr, val) {
+ var attrConfig = Node.ATTRS[attr];
+
+ if (this._setAttr) { // use Attribute imple
+ this._setAttr.apply(this, arguments);
+ } else { // use setters inline
+ if (attrConfig && attrConfig.setter) {
+ attrConfig.setter.call(this, val);
+ } else if (Node.re_aria.test(attr)) { // special case Aria
+ this._node.setAttribute(attr, val);
+ } else {
+ Node.DEFAULT_SETTER.apply(this, arguments);
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Sets multiple attributes.
+ * @method setAttrs
+ * @param {Object} attrMap an object of name/value pairs to set
+ * @chainable
+ */
+ setAttrs: function(attrMap) {
+ if (this._setAttrs) { // use Attribute imple
+ this._setAttrs(attrMap);
+ } else { // use setters inline
+ Y.Object.each(attrMap, function(v, n) {
+ this.set(n, v);
+ }, this);
+ }
+
+ return this;
+ },
+
+ /**
+ * Returns an object containing the values for the requested attributes.
+ * @method getAttrs
+ * @param {Array} attrs an array of attributes to get values
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs: function(attrs) {
+ var ret = {};
+ if (this._getAttrs) { // use Attribute imple
+ this._getAttrs(attrs);
+ } else { // use setters inline
+ Y.Array.each(attrs, function(v, n) {
+ ret[v] = this.get(v);
+ }, this);
+ }
+
+ return ret;
+ },
+
+ /**
+ * Creates a new Node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+ create: Node.create,
+
+ /**
+ * Compares nodes to determine if they match.
+ * Node instances can be compared to each other and/or HTMLElements.
+ * @method compareTo
+ * @param {HTMLElement | Node} refNode The reference node to compare to the node.
+ * @return {Boolean} True if the nodes match, false if they do not.
+ */
+ compareTo: function(refNode) {
+ var node = this._node;
+ if (refNode instanceof Y.Node) {
+ refNode = refNode._node;
+ }
+ return node === refNode;
+ },
+
+ /**
+ * Determines whether the node is appended to the document.
+ * @method inDoc
+ * @param {Node|HTMLElement} doc optional An optional document to check against.
+ * Defaults to current document.
+ * @return {Boolean} Whether or not this node is appended to the document.
+ */
+ inDoc: function(doc) {
+ var node = this._node;
+ doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
+ if (doc.documentElement) {
+ return Y.DOM.contains(doc.documentElement, node);
+ }
+ },
+
+ getById: function(id) {
+ var node = this._node,
+ ret = Y.DOM.byId(id, node[OWNER_DOCUMENT]);
+ if (ret && Y.DOM.contains(node, ret)) {
+ ret = Y.one(ret);
+ } else {
+ ret = null;
+ }
+ return ret;
+ },
+
+ /**
+ * Returns the nearest ancestor that passes the test applied by supplied boolean method.
+ * @method ancestor
+ * @param {String | Function} fn A selector string or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} The matching Node instance or null if not found
+ */
+ ancestor: function(fn) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'parentNode', _wrapFn(fn)));
+ },
+
+ /**
+ * Returns the previous matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method previous
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ previous: function(fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Returns the next matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method next
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ next: function(node, fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method one
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ one: function(selector) {
+ return Y.one(Y.Selector.query(selector, this._node, true));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method query
+ * @deprecated Use one()
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ query: function(selector) {
+ Y.log('query() is deprecated, use one()', 'warn', 'deprecated');
+ return this.one(selector);
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method all
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ all: function(selector) {
+ var nodelist = Y.all(Y.Selector.query(selector, this._node));
+ nodelist._query = selector;
+ return nodelist;
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method queryAll
+ * @deprecated Use all()
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ queryAll: function(selector) {
+ Y.log('queryAll() is deprecated, use all()', 'warn', 'deprecated');
+ return this.all(selector);
+ },
+
+ // TODO: allow fn test
+ /**
+ * Test if the supplied node matches the supplied selector.
+ * @method test
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {boolean} Whether or not the node matches the selector.
+ */
+ test: function(selector) {
+ return Y.Selector.test(this._node, selector);
+ },
+
+ /**
+ * Removes the node from its parent.
+ * Shortcut for myNode.get('parentNode').removeChild(myNode);
+ * @method remove
+ * @chainable
+ *
+ */
+ remove: function(destroy) {
+ var node = this._node;
+ node.parentNode.removeChild(node);
+ if (destroy) {
+ this.destroy(true);
+ }
+ return this;
+ },
+
+ /**
+ * Replace the node with the other node. This is a DOM update only
+ * and does not change the node bound to the Node instance.
+ * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
+ * @method replace
+ * @chainable
+ *
+ */
+ replace: function(newNode) {
+ var node = this._node;
+ node.parentNode.replaceChild(newNode, node);
+ return this;
+ },
+
+ purge: function(recurse, type) {
+ Y.Event.purgeElement(this._node, recurse, type);
+ },
+
+ destroy: function(purge) {
+ delete Node._instances[this[UID]];
+ if (purge) {
+ this.purge(true);
+ }
+
+ if (this.unplug) {
+ this.unplug();
+ }
+
+ this._node._yuid = null;
+ this._node = null;
+ this._stateProxy = null;
+ },
+
+ /**
+ * Invokes a method on the Node instance
+ * @method invoke
+ * @param {String} method The name of the method to invoke
+ * @param {Any} a, b, c, etc. Arguments to invoke the method with.
+ * @return Whatever the underly method returns.
+ * DOM Nodes and Collections return values
+ * are converted to Node/NodeList instances.
+ *
+ */
+ invoke: function(method, a, b, c, d, e) {
+ var node = this._node,
+ ret;
+
+ if (a && a instanceof Y.Node) {
+ a = a._node;
+ }
+
+ if (b && b instanceof Y.Node) {
+ b = b._node;
+ }
+
+ ret = node[method](a, b, c, d, e);
+ return Y.Node.scrubVal(ret, this);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @deprecated Use NodeList
+ * @param {Function} fn The function to apply
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the NodeList instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ context = context || this;
+ Y.log('each is deprecated on Node', 'warn', 'deprecated');
+ return fn.call(context, this);
+ },
+
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ * @deprecated Use NodeList
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ Y.log('item is deprecated on Node', 'warn', 'deprecated');
+ return this;
+ },
+
+ /**
+ * Returns the current number of items in the Node.
+ * @method size
+ * @deprecated Use NodeList
+ * @return {Int} The number of items in the Node.
+ */
+ size: function() {
+ Y.log('size is deprecated on Node', 'warn', 'deprecated');
+ return this._node ? 1 : 0;
+ },
+
+ /**
+ * Inserts the content before the reference node.
+ * @method insert
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @param {Int | Y.Node | HTMLElement | String} where The position to insert at.
+ * @chainable
+ */
+ insert: function(content, where) {
+ var node = this._node;
+
+ if (content) {
+ if (typeof where === 'number') { // allow index
+ where = this._node.childNodes[where];
+ }
+
+ if (typeof content !== 'string') { // allow Node or NodeList/Array instances
+ if (content._node) { // Node
+ content = content._node;
+ } else if (content._nodes || (!content.nodeType && content.length)) { // NodeList or Array
+ Y.each(content._nodes, function(n) {
+ Y.DOM.addHTML(node, n, where);
+ });
+
+ return this; // NOTE: early return
+ }
+ }
+ Y.DOM.addHTML(node, content, where);
+ }
+ return this;
+ },
+
+ /**
+ * Inserts the content as the firstChild of the node.
+ * @method prepend
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ prepend: function(content) {
+ return this.insert(content, 0);
+ },
+
+ /**
+ * Inserts the content as the lastChild of the node.
+ * @method append
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ append: function(content) {
+ return this.insert(content, null);
+ },
+
+ /**
+ * Replaces the node's current content with the content.
+ * @method setContent
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ setContent: function(content) {
+ Y.DOM.addHTML(this._node, content, 'replace');
+ return this;
+ },
+
+ // TODO: need this?
+ hasMethod: function(method) {
+ var node = this._node;
+ return (node && (typeof node === 'function'));
+ }
+}, true);
+
+Y.Node = Node;
+Y.get = Y.Node.get;
+Y.one = Y.Node.one;
+/**
+ * The NodeList module provides support for managing collections of Nodes.
+ * @module node
+ * @submodule nodelist
+ */
+
+/**
+ * The NodeList class provides a wrapper for manipulating DOM NodeLists.
+ * NodeList properties can be accessed via the set/get methods.
+ * Use Y.all() to retrieve NodeList instances.
+ *
+ * @class NodeList
+ * @constructor
+ */
+
+var NodeList = function(nodes) {
+ if (typeof nodes === 'string') {
+ this._query = nodes;
+ nodes = Y.Selector.query(nodes);
+ } else {
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ NodeList._instances[Y.stamp(this)] = this;
+ this._nodes = nodes;
+};
+// end "globals"
+
+NodeList.NAME = 'NodeList';
+
+/**
+ * Retrieves the DOM nodes bound to a NodeList instance
+ * @method NodeList.getDOMNodes
+ * @static
+ *
+ * @param {Y.NodeList} node The NodeList instance
+ * @return {Array} The array of DOM nodes bound to the NodeList
+ */
+NodeList.getDOMNodes = function(nodeList) {
+ return nodeList._nodes;
+};
+
+NodeList._instances = [];
+
+NodeList.each = function(instance, fn, context) {
+ var nodes = instance._nodes;
+ if (nodes && nodes.length) {
+ Y.Array.each(nodes, fn, context || instance);
+ } else {
+ Y.log('no nodes bound to ' + this, 'warn', 'NodeList');
+ }
+};
+
+NodeList.addMethod = function(name, fn, context) {
+ if (name && fn) {
+ NodeList.prototype[name] = function() {
+ var ret = [],
+ args = arguments;
+
+ Y.Array.each(this._nodes, function(node) {
+ var UID = '_yuid',
+ instance = Y.Node._instances[node[UID]],
+ ctx,
+ result;
+
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+ ctx = context || instance;
+ result = fn.apply(ctx, args);
+ if (result !== undefined && result !== instance) {
+ ret[ret.length] = result;
+ }
+ });
+
+ // TODO: remove tmp pointer
+ return ret.length ? ret : this;
+ };
+ } else {
+ Y.log('unable to add method: ' + name, 'warn', 'Node');
+ }
+};
+
+NodeList.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ NodeList.addMethod(name, host[name]);
+ } else {
+ Y.each(name, function(n) {
+ NodeList.importMethod(host, n);
+ });
+ }
+};
+
+NodeList._getTempNode = function(node) {
+ var tmp = NodeList._tempNode;
+ if (!tmp) {
+ tmp = Y.Node.create('<div></div>');
+ NodeList._tempNode = tmp;
+ }
+
+ tmp._node = node;
+ tmp._stateProxy = node;
+ return tmp;
+};
+
+Y.mix(NodeList.prototype, {
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ return Y.one((this._nodes || [])[index]);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the current Node instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ var instance = this;
+ Y.Array.each(this._nodes, function(node, index) {
+ node = Y.one(node);
+ return fn.call(context || node, node, index, instance);
+ });
+ return instance;
+ },
+
+ batch: function(fn, context) {
+ var nodelist = this;
+
+ Y.Array.each(this._nodes, function(node, index) {
+ var instance = Y.Node._instances[node[UID]];
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+
+ return fn.call(context || instance, instance, index, nodelist);
+ });
+ return nodelist;
+ },
+
+ /**
+ * Executes the function once for each node until a true value is returned.
+ * @method some
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to execute the function from.
+ * Default context is the current Node instance
+ * @return {Boolean} Whether or not the function returned true for any node.
+ */
+ some: function(fn, context) {
+ var instance = this;
+ return Y.Array.some(this._nodes, function(node, index) {
+ node = Y.one(node);
+ context = context || node;
+ return fn.call(context, node, index, instance);
+ });
+ },
+
+ /**
+ * Creates a documenFragment from the nodes bound to the NodeList instance
+ * @method toDocFrag
+ * @return Node a Node instance bound to the documentFragment
+ */
+ toFrag: function() {
+ return Y.one(Y.DOM._nl2frag(this._nodes));
+ },
+
+ /**
+ * Returns the index of the node in the NodeList instance
+ * or -1 if the node isn't found.
+ * @method indexOf
+ * @param {Y.Node || DOMNode} node the node to search for
+ * @return {Int} the index of the node value or -1 if not found
+ */
+ indexOf: function(node) {
+ return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
+ },
+
+ /**
+ * Filters the NodeList instance down to only nodes matching the given selector.
+ * @method filter
+ * @param {String} selector The selector to filter against
+ * @return {NodeList} NodeList containing the updated collection
+ * @see Selector
+ */
+ filter: function(selector) {
+ return Y.all(Y.Selector.filter(this._nodes, selector));
+ },
+
+
+ /**
+ * Creates a new NodeList containing all nodes at every n indices, where
+ * remainder n % index equals r.
+ * (zero-based index).
+ * @method modulus
+ * @param {Int} n The offset to use (return every nth node)
+ * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ modulus: function(n, r) {
+ r = r || 0;
+ var nodes = [];
+ NodeList.each(this, function(node, i) {
+ if (i % n === r) {
+ nodes.push(node);
+ }
+ });
+
+ return Y.all(nodes);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at odd indices
+ * (zero-based index).
+ * @method odd
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ odd: function() {
+ return this.modulus(2, 1);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at even indices
+ * (zero-based index), including zero.
+ * @method even
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ even: function() {
+ return this.modulus(2);
+ },
+
+ destructor: function() {
+ delete NodeList._instances[this[UID]];
+ },
+
+ /**
+ * Reruns the initial query, when created using a selector query
+ * @method refresh
+ * @chainable
+ */
+ refresh: function() {
+ var doc,
+ nodes = this._nodes;
+ if (this._query) {
+ if (nodes && nodes[0] && nodes[0].ownerDocument) {
+ doc = nodes[0].ownerDocument;
+ }
+
+ this._nodes = Y.Selector.query(this._query, doc || Y.config.doc);
+ }
+
+ return this;
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * @method on
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ on: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.on.apply(Y, args);
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * The handler is called only after all on() handlers are called
+ * and the event is not prevented.
+ * @method after
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ after: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.after.apply(Y, args);
+ },
+
+ /**
+ * Returns the current number of items in the NodeList.
+ * @method size
+ * @return {Int} The number of items in the NodeList.
+ */
+ size: function() {
+ return this._nodes.length;
+ },
+
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to any nodes',
+ nodes = this._nodes,
+ node;
+
+ if (nodes && nodes[0]) {
+ node = nodes[0];
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ if (nodes.length > 1) {
+ str += '...[' + nodes.length + ' items]';
+ }
+ }
+ return str || errorMsg;
+ }
+
+}, true);
+
+NodeList.importMethod(Y.Node.prototype, [
+ /**
+ * Called on each Node instance
+ * @for NodeList
+ * @method append
+ * @see Node.append
+ */
+ 'append',
+
+ /**
+ * Called on each Node instance
+ * @method detach
+ * @see Node.detach
+ */
+ 'detach',
+
+ /** Called on each Node instance
+ * @method detachAll
+ * @see Node.detachAll
+ */
+ 'detachAll',
+
+ /** Called on each Node instance
+ * @method insert
+ * @see NodeInsert
+ */
+ 'insert',
+
+ /** Called on each Node instance
+ * @method prepend
+ * @see Node.prepend
+ */
+ 'prepend',
+
+ /** Called on each Node instance
+ * @method remove
+ * @see Node.remove
+ */
+ 'remove',
+
+ /** Called on each Node instance
+ * @method set
+ * @see Node.set
+ */
+ 'set',
+
+ /** Called on each Node instance
+ * @method setContent
+ * @see Node.setContent
+ */
+ 'setContent'
+]);
+
+// one-off implementation to convert array of Nodes to NodeList
+// e.g. Y.all('input').get('parentNode');
+
+/** Called on each Node instance
+ * @method get
+ * @see Node
+ */
+NodeList.prototype.get = function(attr) {
+ var ret = [],
+ nodes = this._nodes,
+ isNodeList = false,
+ getTemp = NodeList._getTempNode,
+ instance,
+ val;
+
+ if (nodes[0]) {
+ instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
+ val = instance._get(attr);
+ if (val && val.nodeType) {
+ isNodeList = true;
+ }
+ }
+
+ Y.Array.each(nodes, function(node) {
+ instance = Y.Node._instances[node._yuid];
+
+ if (!instance) {
+ instance = getTemp(node);
+ }
+
+ val = instance._get(attr);
+ if (!isNodeList) { // convert array of Nodes to NodeList
+ val = Y.Node.scrubVal(val, instance);
+ }
+
+ ret.push(val);
+ });
+
+ return (isNodeList) ? Y.all(ret) : ret;
+};
+
+Y.NodeList = NodeList;
+
+Y.all = function(nodes) {
+ return new NodeList(nodes);
+};
+
+Y.Node.all = Y.all;
+Y.Array.each([
+ /**
+ * Passes through to DOM method.
+ * @method replaceChild
+ * @for Node
+ * @param {HTMLElement | Node} node Node to be inserted
+ * @param {HTMLElement | Node} refNode Node to be replaced
+ * @return {Node} The replaced node
+ */
+ 'replaceChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method appendChild
+ * @param {HTMLElement | Node} node Node to be appended
+ * @return {Node} The appended node
+ */
+ 'appendChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method insertBefore
+ * @param {HTMLElement | Node} newNode Node to be appended
+ * @param {HTMLElement | Node} refNode Node to be inserted before
+ * @return {Node} The inserted node
+ */
+ 'insertBefore',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeChild
+ * @param {HTMLElement | Node} node Node to be removed
+ * @return {Node} The removed node
+ */
+ 'removeChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasChildNodes
+ * @return {Boolean} Whether or not the node has any childNodes
+ */
+ 'hasChildNodes',
+
+ /**
+ * Passes through to DOM method.
+ * @method cloneNode
+ * @param {Boolean} deep Whether or not to perform a deep clone, which includes
+ * subtree and attributes
+ * @return {Node} The clone
+ */
+ 'cloneNode',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasAttribute
+ * @param {String} attribute The attribute to test for
+ * @return {Boolean} Whether or not the attribute is present
+ */
+ 'hasAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeAttribute
+ * @param {String} attribute The attribute to be removed
+ * @chainable
+ */
+ 'removeAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method scrollIntoView
+ * @chainable
+ */
+ 'scrollIntoView',
+
+ /**
+ * Passes through to DOM method.
+ * @method getElementsByTagName
+ * @param {String} tagName The tagName to collect
+ * @return {NodeList} A NodeList representing the HTMLCollection
+ */
+ 'getElementsByTagName',
+
+ /**
+ * Passes through to DOM method.
+ * @method focus
+ * @chainable
+ */
+ 'focus',
+
+ /**
+ * Passes through to DOM method.
+ * @method blur
+ * @chainable
+ */
+ 'blur',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method submit
+ * @chainable
+ */
+ 'submit',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method reset
+ * @chainable
+ */
+ 'reset',
+
+ /**
+ * Passes through to DOM method.
+ * @method select
+ * @chainable
+ */
+ 'select'
+], function(method) {
+ Y.Node.prototype[method] = function(arg1, arg2, arg3) {
+ var ret = this.invoke(method, arg1, arg2, arg3);
+ return ret;
+ };
+});
+
+Node.importMethod(Y.DOM, [
+ /**
+ * Determines whether the ndoe is an ancestor of another HTML element in the DOM hierarchy.
+ * @method contains
+ * @param {Node | HTMLElement} needle The possible node or descendent
+ * @return {Boolean} Whether or not this node is the needle its ancestor
+ */
+ 'contains',
+ /**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @for Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+ 'setAttribute',
+ /**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @for Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+ 'getAttribute'
+]);
+
+/**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @see Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+
+/**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @see Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+Y.NodeList.importMethod(Y.Node.prototype, ['getAttribute', 'setAttribute']);
+(function(Y) {
+ var methods = [
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @for Node
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+ 'hasClass',
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+ 'addClass',
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+ 'removeClass',
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+ 'replaceClass',
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ 'toggleClass'
+ ];
+
+ Y.Node.importMethod(Y.DOM, methods);
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @see Node.hasClass
+ * @for NodeList
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @see Node.addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @see Node.removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @see Node.replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @see Node.toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+if (!document.documentElement.hasAttribute) { // IE < 8
+ Y.Node.prototype.hasAttribute = function(attr) {
+ return Y.DOM.getAttribute(this._node, attr) !== '';
+ };
+}
+
+// IE throws error when setting input.type = 'hidden',
+// input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden'
+Y.Node.ATTRS.type = {
+ setter: function(val) {
+ if (val === 'hidden') {
+ try {
+ this._node.type = 'hidden';
+ } catch(e) {
+ this.setStyle('display', 'none');
+ this._inputType = 'hidden';
+ }
+ } else {
+ try { // IE errors when changing the type from "hidden'
+ this._node.type = val;
+ } catch (e) {
+ Y.log('error setting type: ' + val, 'info', 'node');
+ }
+ }
+ return val;
+ },
+
+ getter: function() {
+ return this._inputType || this._node.type;
+ },
+
+ _bypassProxy: true // don't update DOM when using with Attribute
+};
+
+
+}, '3.0.0' ,{requires:['dom-base', 'selector-css2', 'event-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-base",function(C){var G=".",E="nodeName",I="nodeType",B="ownerDocument",H="tagName",D="_yuid",F=function(L){var K=L[D];if(K&&F._instances[K]&&F._instances[K]._node!==L){L[D]=null;}K=C.stamp(L);if(!K){K=C.guid();}this[D]=K;this._node=L;F._instances[K]=this;this._stateProxy=L;if(this._initPlugins){this._initPlugins();}},J=function(L){var K=null;if(L){K=(typeof L==="string")?function(M){return C.Selector.test(M,L);}:function(M){return L(F.get(M));};}return K;};F.NAME="Node";F.re_aria=/^(?:role$|aria-)/;F.DOM_EVENTS={abort:true,beforeunload:true,blur:true,change:true,click:true,close:true,command:true,contextmenu:true,drag:true,dragstart:true,dragenter:true,dragover:true,dragleave:true,dragend:true,drop:true,dblclick:true,error:true,focus:true,keydown:true,keypress:true,keyup:true,load:true,message:true,mousedown:true,mousemove:true,mouseout:true,mouseover:true,mouseup:true,mousemultiwheel:true,mousewheel:true,submit:true,mouseenter:true,mouseleave:true,scroll:true,reset:true,resize:true,select:true,textInput:true,unload:true};C.mix(F.DOM_EVENTS,C.Env.evt.plugins);F._instances={};F.getDOMNode=function(K){if(K){return(K.nodeType)?K:K._node||null;}return null;};F.scrubVal=function(L,K){if(K&&L){if(typeof L==="object"||typeof L==="function"){if(I in L||C.DOM.isWindow(L)){L=F.get(L);}else{if((L.item&&!L._nodes)||(L[0]&&L[0][I])){L=C.all(L);}}}}else{if(L===undefined){L=K;}}return L;};F.addMethod=function(K,M,L){if(K&&M&&typeof M==="function"){F.prototype[K]=function(){L=L||this;var O=C.Array(arguments),N;if(O[0]&&O[0] instanceof F){O[0]=O[0]._node;}if(O[1]&&O[1] instanceof F){O[1]=O[1]._node;}O.unshift(this._node);N=F.scrubVal(M.apply(L,O),this);return N;};}else{}};F.importMethod=function(M,K,L){if(typeof K==="string"){L=L||K;F.addMethod(L,M[K],M);}else{C.each(K,function(N){F.importMethod(M,N);});}};F.one=function(N){var K=null,M,L;if(N){if(typeof N==="string"){if(N.indexOf("doc")===0){N=C.config.doc;}else{if(N.indexOf("win")===0){N=C.config.win;}else{N=C.Selector.query(N,null,true);}}if(!N){return null;}}else{if(N instanceof F){return N;}}L=N._yuid;K=F._instances[L];M=K?K._node:null;if(!K||(M&&N!==M)){K=new F(N);}}return K;};F.get=function(){return F.one.apply(F,arguments);};F.create=function(){return F.get(C.DOM.create.apply(C.DOM,arguments));};F.ATTRS={text:{getter:function(){return C.DOM.getText(this._node);},setter:function(K){C.DOM.setText(this._node,K);return K;}},"options":{getter:function(){return this._node.getElementsByTagName("option");}},"elements":{getter:function(){return C.all(this._node.elements);}},"children":{getter:function(){var N=this._node,M=N.children,O,L,K;if(!M){O=N.childNodes;M=[];for(L=0,K=O.length;L<K;++L){if(O[L][H]){M[M.length]=O[L];}}}return C.all(M);}},value:{getter:function(){return C.DOM.getValue(this._node);},setter:function(K){C.DOM.setValue(this._node,K);return K;}},data:{getter:function(){return this._data;},setter:function(K){this._data=K;return K;}}};F.DEFAULT_SETTER=function(K,M){var L=this._stateProxy,N;if(K.indexOf(G)>-1){N=K;K=K.split(G);C.Object.setValue(L,K,M);}else{if(L[K]!==undefined){L[K]=M;}}return M;};F.DEFAULT_GETTER=function(K){var L=this._stateProxy,M;if(K.indexOf&&K.indexOf(G)>-1){M=C.Object.getValue(L,K.split(G));}else{if(L[K]!==undefined){M=L[K];}}return M;};C.augment(F,C.Event.Target);C.mix(F.prototype,{toString:function(){var M="",L=this[D]+": not bound to a node",K=this._node;if(K){M+=K[E];if(K.id){M+="#"+K.id;}if(K.className){M+="."+K.className.replace(" ",".");}M+=" "+this[D];}return M||L;},get:function(K){var L;if(this._getAttr){L=this._getAttr(K);}else{L=this._get(K);}if(L){L=C.Node.scrubVal(L,this);}return L;},_get:function(K){var L=F.ATTRS[K],M;if(L&&L.getter){M=L.getter.call(this);}else{if(F.re_aria.test(K)){M=this._node.getAttribute(K,2);}else{M=F.DEFAULT_GETTER.apply(this,arguments);}}return M;},set:function(K,M){var L=F.ATTRS[K];if(this._setAttr){this._setAttr.apply(this,arguments);}else{if(L&&L.setter){L.setter.call(this,M);}else{if(F.re_aria.test(K)){this._node.setAttribute(K,M);}else{F.DEFAULT_SETTER.apply(this,arguments);}}}return this;},setAttrs:function(K){if(this._setAttrs){this._setAttrs(K);}else{C.Object.each(K,function(L,M){this.set(M,L);},this);}return this;},getAttrs:function(L){var K={};if(this._getAttrs){this._getAttrs(L);}else{C.Array.each(L,function(M,N){K[M]=this.get(M);},this);}return K;},create:F.create,compareTo:function(K){var L=this._node;if(K instanceof C.Node){K=K._node;}return L===K;},inDoc:function(L){var K=this._node;L=(L)?L._node||L:K[B];if(L.documentElement){return C.DOM.contains(L.documentElement,K);}},getById:function(M){var L=this._node,K=C.DOM.byId(M,L[B]);if(K&&C.DOM.contains(L,K)){K=C.one(K);}else{K=null;}return K;},ancestor:function(K){return F.get(C.DOM.elementByAxis(this._node,"parentNode",J(K)));},previous:function(L,K){return F.get(C.DOM.elementByAxis(this._node,"previousSibling",J(L),K));},next:function(M,L,K){return F.get(C.DOM.elementByAxis(this._node,"nextSibling",J(L),K));},one:function(K){return C.one(C.Selector.query(K,this._node,true));},query:function(K){return this.one(K);},all:function(K){var L=C.all(C.Selector.query(K,this._node));L._query=K;return L;},queryAll:function(K){return this.all(K);},test:function(K){return C.Selector.test(this._node,K);},remove:function(K){var L=this._node;L.parentNode.removeChild(L);if(K){this.destroy(true);}return this;},replace:function(K){var L=this._node;L.parentNode.replaceChild(K,L);return this;},purge:function(L,K){C.Event.purgeElement(this._node,L,K);},destroy:function(K){delete F._instances[this[D]];if(K){this.purge(true);}if(this.unplug){this.unplug();}this._node._yuid=null;this._node=null;this._stateProxy=null;},invoke:function(R,L,K,Q,P,O){var N=this._node,M;if(L&&L instanceof C.Node){L=L._node;}if(K&&K instanceof C.Node){K=K._node;}M=N[R](L,K,Q,P,O);return C.Node.scrubVal(M,this);},each:function(L,K){K=K||this;return L.call(K,this);},item:function(K){return this;},size:function(){return this._node?1:0;},insert:function(M,K){var L=this._node;
+if(M){if(typeof K==="number"){K=this._node.childNodes[K];}if(typeof M!=="string"){if(M._node){M=M._node;}else{if(M._nodes||(!M.nodeType&&M.length)){C.each(M._nodes,function(N){C.DOM.addHTML(L,N,K);});return this;}}}C.DOM.addHTML(L,M,K);}return this;},prepend:function(K){return this.insert(K,0);},append:function(K){return this.insert(K,null);},setContent:function(K){C.DOM.addHTML(this._node,K,"replace");return this;},hasMethod:function(L){var K=this._node;return(K&&(typeof K==="function"));}},true);C.Node=F;C.get=C.Node.get;C.one=C.Node.one;var A=function(K){if(typeof K==="string"){this._query=K;K=C.Selector.query(K);}else{K=C.Array(K,0,true);}A._instances[C.stamp(this)]=this;this._nodes=K;};A.NAME="NodeList";A.getDOMNodes=function(K){return K._nodes;};A._instances=[];A.each=function(K,N,M){var L=K._nodes;if(L&&L.length){C.Array.each(L,N,M||K);}else{}};A.addMethod=function(K,M,L){if(K&&M){A.prototype[K]=function(){var O=[],N=arguments;C.Array.each(this._nodes,function(T){var S="_yuid",Q=C.Node._instances[T[S]],R,P;if(!Q){Q=A._getTempNode(T);}R=L||Q;P=M.apply(R,N);if(P!==undefined&&P!==Q){O[O.length]=P;}});return O.length?O:this;};}else{}};A.importMethod=function(M,K,L){if(typeof K==="string"){L=L||K;A.addMethod(K,M[K]);}else{C.each(K,function(N){A.importMethod(M,N);});}};A._getTempNode=function(L){var K=A._tempNode;if(!K){K=C.Node.create("<div></div>");A._tempNode=K;}K._node=L;K._stateProxy=L;return K;};C.mix(A.prototype,{item:function(K){return C.one((this._nodes||[])[K]);},each:function(M,L){var K=this;C.Array.each(this._nodes,function(O,N){O=C.one(O);return M.call(L||O,O,N,K);});return K;},batch:function(L,K){var M=this;C.Array.each(this._nodes,function(P,O){var N=C.Node._instances[P[D]];if(!N){N=A._getTempNode(P);}return L.call(K||N,N,O,M);});return M;},some:function(M,L){var K=this;return C.Array.some(this._nodes,function(O,N){O=C.one(O);L=L||O;return M.call(L,O,N,K);});},toFrag:function(){return C.one(C.DOM._nl2frag(this._nodes));},indexOf:function(K){return C.Array.indexOf(this._nodes,C.Node.getDOMNode(K));},filter:function(K){return C.all(C.Selector.filter(this._nodes,K));},modulus:function(M,L){L=L||0;var K=[];A.each(this,function(O,N){if(N%M===L){K.push(O);}});return C.all(K);},odd:function(){return this.modulus(2,1);},even:function(){return this.modulus(2);},destructor:function(){delete A._instances[this[D]];},refresh:function(){var L,K=this._nodes;if(this._query){if(K&&K[0]&&K[0].ownerDocument){L=K[0].ownerDocument;}this._nodes=C.Selector.query(this._query,L||C.config.doc);}return this;},on:function(N,M,L){var K=C.Array(arguments,0,true);K.splice(2,0,this._nodes);K[3]=L||this;return C.on.apply(C,K);},after:function(N,M,L){var K=C.Array(arguments,0,true);K.splice(2,0,this._nodes);K[3]=L||this;return C.after.apply(C,K);},size:function(){return this._nodes.length;},toString:function(){var N="",M=this[D]+": not bound to any nodes",K=this._nodes,L;if(K&&K[0]){L=K[0];N+=L[E];if(L.id){N+="#"+L.id;}if(L.className){N+="."+L.className.replace(" ",".");}if(K.length>1){N+="...["+K.length+" items]";}}return N||M;}},true);A.importMethod(C.Node.prototype,["append","detach","detachAll","insert","prepend","remove","set","setContent"]);A.prototype.get=function(L){var O=[],N=this._nodes,M=false,P=A._getTempNode,K,Q;if(N[0]){K=C.Node._instances[N[0]._yuid]||P(N[0]);Q=K._get(L);if(Q&&Q.nodeType){M=true;}}C.Array.each(N,function(R){K=C.Node._instances[R._yuid];if(!K){K=P(R);}Q=K._get(L);if(!M){Q=C.Node.scrubVal(Q,K);}O.push(Q);});return(M)?C.all(O):O;};C.NodeList=A;C.all=function(K){return new A(K);};C.Node.all=C.all;C.Array.each(["replaceChild","appendChild","insertBefore","removeChild","hasChildNodes","cloneNode","hasAttribute","removeAttribute","scrollIntoView","getElementsByTagName","focus","blur","submit","reset","select"],function(K){C.Node.prototype[K]=function(O,M,L){var N=this.invoke(K,O,M,L);return N;};});F.importMethod(C.DOM,["contains","setAttribute","getAttribute"]);C.NodeList.importMethod(C.Node.prototype,["getAttribute","setAttribute"]);(function(L){var K=["hasClass","addClass","removeClass","replaceClass","toggleClass"];L.Node.importMethod(L.DOM,K);L.NodeList.importMethod(L.Node.prototype,K);})(C);if(!document.documentElement.hasAttribute){C.Node.prototype.hasAttribute=function(K){return C.DOM.getAttribute(this._node,K)!=="";};}C.Node.ATTRS.type={setter:function(L){if(L==="hidden"){try{this._node.type="hidden";}catch(K){this.setStyle("display","none");this._inputType="hidden";}}else{try{this._node.type=L;}catch(K){}}return L;},getter:function(){return this._inputType||this._node.type;},_bypassProxy:true};},"3.0.0",{requires:["dom-base","selector-css2","event-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-base', function(Y) {
+
+/**
+ * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
+ * @module node
+ * @submodule node-base
+ */
+
+/**
+ * The Node class provides a wrapper for manipulating DOM Nodes.
+ * Node properties can be accessed via the set/get methods.
+ * Use Y.get() to retrieve Node instances.
+ *
+ * <strong>NOTE:</strong> Node properties are accessed using
+ * the <code>set</code> and <code>get</code> methods.
+ *
+ * @class Node
+ * @constructor
+ * @for Node
+ */
+
+// "globals"
+var DOT = '.',
+ NODE_NAME = 'nodeName',
+ NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TAG_NAME = 'tagName',
+ UID = '_yuid',
+
+ Node = function(node) {
+ var uid = node[UID];
+
+ if (uid && Node._instances[uid] && Node._instances[uid]._node !== node) {
+ node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
+ }
+
+ uid = Y.stamp(node);
+ if (!uid) { // stamp failed; likely IE non-HTMLElement
+ uid = Y.guid();
+ }
+
+ this[UID] = uid;
+
+ this._node = node;
+ Node._instances[uid] = this;
+
+ this._stateProxy = node; // when augmented with Attribute
+
+ if (this._initPlugins) { // when augmented with Plugin.Host
+ this._initPlugins();
+ }
+ },
+
+ // used with previous/next/ancestor tests
+ _wrapFn = function(fn) {
+ var ret = null;
+ if (fn) {
+ ret = (typeof fn === 'string') ?
+ function(n) {
+ return Y.Selector.test(n, fn);
+ } :
+ function(n) {
+ return fn(Node.get(n));
+ };
+ }
+
+ return ret;
+ };
+// end "globals"
+
+Node.NAME = 'Node';
+
+Node.re_aria = /^(?:role$|aria-)/;
+
+Node.DOM_EVENTS = {
+ abort: true,
+ beforeunload: true,
+ blur: true,
+ change: true,
+ click: true,
+ close: true,
+ command: true,
+ contextmenu: true,
+ drag: true,
+ dragstart: true,
+ dragenter: true,
+ dragover: true,
+ dragleave: true,
+ dragend: true,
+ drop: true,
+ dblclick: true,
+ error: true,
+ focus: true,
+ keydown: true,
+ keypress: true,
+ keyup: true,
+ load: true,
+ message: true,
+ mousedown: true,
+ mousemove: true,
+ mouseout: true,
+ mouseover: true,
+ mouseup: true,
+ mousemultiwheel: true,
+ mousewheel: true,
+ submit: true,
+ mouseenter: true,
+ mouseleave: true,
+ scroll: true,
+ reset: true,
+ resize: true,
+ select: true,
+ textInput: true,
+ unload: true
+};
+
+// Add custom event adaptors to this list. This will make it so
+// that delegate, key, available, contentready, etc all will
+// be available through Node.on
+Y.mix(Node.DOM_EVENTS, Y.Env.evt.plugins);
+
+Node._instances = {};
+
+/**
+ * Retrieves the DOM node bound to a Node instance
+ * @method Node.getDOMNode
+ * @static
+ *
+ * @param {Y.Node || HTMLNode} node The Node instance or an HTMLNode
+ * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
+ * as the node argument, it is simply returned.
+ */
+Node.getDOMNode = function(node) {
+ if (node) {
+ return (node.nodeType) ? node : node._node || null;
+ }
+ return null;
+};
+
+Node.scrubVal = function(val, node) {
+ if (node && val) { // only truthy values are risky
+ if (typeof val === 'object' || typeof val === 'function') { // safari nodeList === function
+ if (NODE_TYPE in val || Y.DOM.isWindow(val)) {// node || window
+ val = Node.get(val);
+ } else if ((val.item && !val._nodes) || // dom collection or Node instance
+ (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
+ val = Y.all(val);
+ }
+ }
+ } else if (val === undefined) {
+ val = node; // for chaining
+ }
+
+ return val;
+};
+
+Node.addMethod = function(name, fn, context) {
+ if (name && fn && typeof fn === 'function') {
+ Node.prototype[name] = function() {
+ context = context || this;
+ var args = Y.Array(arguments),
+ ret;
+
+ if (args[0] && args[0] instanceof Node) {
+ args[0] = args[0]._node;
+ }
+
+ if (args[1] && args[1] instanceof Node) {
+ args[1] = args[1]._node;
+ }
+ args.unshift(this._node);
+ ret = Node.scrubVal(fn.apply(context, args), this);
+ return ret;
+ };
+ } else {
+ }
+};
+
+Node.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ Node.addMethod(altName, host[name], host);
+ } else {
+ Y.each(name, function(n) {
+ Node.importMethod(host, n);
+ });
+ }
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.one = function(node) {
+ var instance = null,
+ cachedNode,
+ uid;
+
+ if (node) {
+ if (typeof node === 'string') {
+ if (node.indexOf('doc') === 0) { // doc OR document
+ node = Y.config.doc;
+ } else if (node.indexOf('win') === 0) { // win OR window
+ node = Y.config.win;
+ } else {
+ node = Y.Selector.query(node, null, true);
+ }
+ if (!node) {
+ return null;
+ }
+ } else if (node instanceof Node) {
+ return node; // NOTE: return
+ }
+
+ uid = node._yuid;
+ instance = Node._instances[uid]; // reuse exising instances
+ cachedNode = instance ? instance._node : null;
+ if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
+ instance = new Node(node);
+ }
+ }
+ return instance;
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.get
+ * @deprecated Use Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.get = function() {
+ return Node.one.apply(Node, arguments);
+};
+
+/**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @static
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+Node.create = function() {
+ return Node.get(Y.DOM.create.apply(Y.DOM, arguments));
+};
+
+Node.ATTRS = {
+ /**
+ * Allows for getting and setting the text of an element.
+ * Formatting is preserved and special characters are treated literally.
+ * @config text
+ * @type String
+ */
+ text: {
+ getter: function() {
+ return Y.DOM.getText(this._node);
+ },
+
+ setter: function(content) {
+ Y.DOM.setText(this._node, content);
+ return content;
+ }
+ },
+
+ 'options': {
+ getter: function() {
+ return this._node.getElementsByTagName('option');
+ }
+ },
+
+ // IE: elements collection is also FORM node which trips up scrubVal.
+ // preconverting to NodeList
+ // TODO: break out for IE only
+ 'elements': {
+ getter: function() {
+ return Y.all(this._node.elements);
+ }
+ },
+
+ /**
+ * Returns a NodeList instance of all HTMLElement children.
+ * @readOnly
+ * @config children
+ * @type NodeList
+ */
+ 'children': {
+ getter: function() {
+ var node = this._node,
+ children = node.children,
+ childNodes, i, len;
+
+ if (!children) {
+ childNodes = node.childNodes;
+ children = [];
+
+ for (i = 0, len = childNodes.length; i < len; ++i) {
+ if (childNodes[i][TAG_NAME]) {
+ children[children.length] = childNodes[i];
+ }
+ }
+ }
+ return Y.all(children);
+ }
+ },
+
+ value: {
+ getter: function() {
+ return Y.DOM.getValue(this._node);
+ },
+
+ setter: function(val) {
+ Y.DOM.setValue(this._node, val);
+ return val;
+ }
+ },
+
+ data: {
+ getter: function() {
+ return this._data;
+ },
+
+ setter: function(val) {
+ this._data = val;
+ return val;
+ }
+ }
+};
+
+// call with instance context
+Node.DEFAULT_SETTER = function(name, val) {
+ var node = this._stateProxy,
+ strPath;
+
+ if (name.indexOf(DOT) > -1) {
+ strPath = name;
+ name = name.split(DOT);
+ // only allow when defined on node
+ Y.Object.setValue(node, name, val);
+ } else if (node[name] !== undefined) { // pass thru DOM properties
+ node[name] = val;
+ }
+
+ return val;
+};
+
+// call with instance context
+Node.DEFAULT_GETTER = function(name) {
+ var node = this._stateProxy,
+ val;
+
+ if (name.indexOf && name.indexOf(DOT) > -1) {
+ val = Y.Object.getValue(node, name.split(DOT));
+ } else if (node[name] !== undefined) { // pass thru from DOM
+ val = node[name];
+ }
+
+ return val;
+};
+
+Y.augment(Node, Y.Event.Target);
+
+Y.mix(Node.prototype, {
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to a node',
+ node = this._node;
+
+ if (node) {
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ // TODO: add yuid?
+ str += ' ' + this[UID];
+ }
+ return str || errorMsg;
+ },
+
+ /**
+ * Returns an attribute value on the Node instance
+ * @method get
+ * @param {String} attr The attribute to be set
+ * @return {any} The current value of the attribute
+ */
+ get: function(attr) {
+ var val;
+
+ if (this._getAttr) { // use Attribute imple
+ val = this._getAttr(attr);
+ } else {
+ val = this._get(attr);
+ }
+
+ if (val) {
+ val = Y.Node.scrubVal(val, this);
+ }
+ return val;
+ },
+
+ _get: function(attr) {
+ var attrConfig = Node.ATTRS[attr],
+ val;
+
+ if (attrConfig && attrConfig.getter) {
+ val = attrConfig.getter.call(this);
+ } else if (Node.re_aria.test(attr)) {
+ val = this._node.getAttribute(attr, 2);
+ } else {
+ val = Node.DEFAULT_GETTER.apply(this, arguments);
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets an attribute on the Node instance.
+ * @method set
+ * @param {String} attr The attribute to be set.
+ * @param {any} val The value to set the attribute to.
+ * @chainable
+ */
+ set: function(attr, val) {
+ var attrConfig = Node.ATTRS[attr];
+
+ if (this._setAttr) { // use Attribute imple
+ this._setAttr.apply(this, arguments);
+ } else { // use setters inline
+ if (attrConfig && attrConfig.setter) {
+ attrConfig.setter.call(this, val);
+ } else if (Node.re_aria.test(attr)) { // special case Aria
+ this._node.setAttribute(attr, val);
+ } else {
+ Node.DEFAULT_SETTER.apply(this, arguments);
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Sets multiple attributes.
+ * @method setAttrs
+ * @param {Object} attrMap an object of name/value pairs to set
+ * @chainable
+ */
+ setAttrs: function(attrMap) {
+ if (this._setAttrs) { // use Attribute imple
+ this._setAttrs(attrMap);
+ } else { // use setters inline
+ Y.Object.each(attrMap, function(v, n) {
+ this.set(n, v);
+ }, this);
+ }
+
+ return this;
+ },
+
+ /**
+ * Returns an object containing the values for the requested attributes.
+ * @method getAttrs
+ * @param {Array} attrs an array of attributes to get values
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs: function(attrs) {
+ var ret = {};
+ if (this._getAttrs) { // use Attribute imple
+ this._getAttrs(attrs);
+ } else { // use setters inline
+ Y.Array.each(attrs, function(v, n) {
+ ret[v] = this.get(v);
+ }, this);
+ }
+
+ return ret;
+ },
+
+ /**
+ * Creates a new Node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+ create: Node.create,
+
+ /**
+ * Compares nodes to determine if they match.
+ * Node instances can be compared to each other and/or HTMLElements.
+ * @method compareTo
+ * @param {HTMLElement | Node} refNode The reference node to compare to the node.
+ * @return {Boolean} True if the nodes match, false if they do not.
+ */
+ compareTo: function(refNode) {
+ var node = this._node;
+ if (refNode instanceof Y.Node) {
+ refNode = refNode._node;
+ }
+ return node === refNode;
+ },
+
+ /**
+ * Determines whether the node is appended to the document.
+ * @method inDoc
+ * @param {Node|HTMLElement} doc optional An optional document to check against.
+ * Defaults to current document.
+ * @return {Boolean} Whether or not this node is appended to the document.
+ */
+ inDoc: function(doc) {
+ var node = this._node;
+ doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
+ if (doc.documentElement) {
+ return Y.DOM.contains(doc.documentElement, node);
+ }
+ },
+
+ getById: function(id) {
+ var node = this._node,
+ ret = Y.DOM.byId(id, node[OWNER_DOCUMENT]);
+ if (ret && Y.DOM.contains(node, ret)) {
+ ret = Y.one(ret);
+ } else {
+ ret = null;
+ }
+ return ret;
+ },
+
+ /**
+ * Returns the nearest ancestor that passes the test applied by supplied boolean method.
+ * @method ancestor
+ * @param {String | Function} fn A selector string or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} The matching Node instance or null if not found
+ */
+ ancestor: function(fn) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'parentNode', _wrapFn(fn)));
+ },
+
+ /**
+ * Returns the previous matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method previous
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ previous: function(fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Returns the next matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method next
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ next: function(node, fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method one
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ one: function(selector) {
+ return Y.one(Y.Selector.query(selector, this._node, true));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method query
+ * @deprecated Use one()
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ query: function(selector) {
+ return this.one(selector);
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method all
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ all: function(selector) {
+ var nodelist = Y.all(Y.Selector.query(selector, this._node));
+ nodelist._query = selector;
+ return nodelist;
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method queryAll
+ * @deprecated Use all()
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ queryAll: function(selector) {
+ return this.all(selector);
+ },
+
+ // TODO: allow fn test
+ /**
+ * Test if the supplied node matches the supplied selector.
+ * @method test
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {boolean} Whether or not the node matches the selector.
+ */
+ test: function(selector) {
+ return Y.Selector.test(this._node, selector);
+ },
+
+ /**
+ * Removes the node from its parent.
+ * Shortcut for myNode.get('parentNode').removeChild(myNode);
+ * @method remove
+ * @chainable
+ *
+ */
+ remove: function(destroy) {
+ var node = this._node;
+ node.parentNode.removeChild(node);
+ if (destroy) {
+ this.destroy(true);
+ }
+ return this;
+ },
+
+ /**
+ * Replace the node with the other node. This is a DOM update only
+ * and does not change the node bound to the Node instance.
+ * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
+ * @method replace
+ * @chainable
+ *
+ */
+ replace: function(newNode) {
+ var node = this._node;
+ node.parentNode.replaceChild(newNode, node);
+ return this;
+ },
+
+ purge: function(recurse, type) {
+ Y.Event.purgeElement(this._node, recurse, type);
+ },
+
+ destroy: function(purge) {
+ delete Node._instances[this[UID]];
+ if (purge) {
+ this.purge(true);
+ }
+
+ if (this.unplug) {
+ this.unplug();
+ }
+
+ this._node._yuid = null;
+ this._node = null;
+ this._stateProxy = null;
+ },
+
+ /**
+ * Invokes a method on the Node instance
+ * @method invoke
+ * @param {String} method The name of the method to invoke
+ * @param {Any} a, b, c, etc. Arguments to invoke the method with.
+ * @return Whatever the underly method returns.
+ * DOM Nodes and Collections return values
+ * are converted to Node/NodeList instances.
+ *
+ */
+ invoke: function(method, a, b, c, d, e) {
+ var node = this._node,
+ ret;
+
+ if (a && a instanceof Y.Node) {
+ a = a._node;
+ }
+
+ if (b && b instanceof Y.Node) {
+ b = b._node;
+ }
+
+ ret = node[method](a, b, c, d, e);
+ return Y.Node.scrubVal(ret, this);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @deprecated Use NodeList
+ * @param {Function} fn The function to apply
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the NodeList instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ context = context || this;
+ return fn.call(context, this);
+ },
+
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ * @deprecated Use NodeList
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ return this;
+ },
+
+ /**
+ * Returns the current number of items in the Node.
+ * @method size
+ * @deprecated Use NodeList
+ * @return {Int} The number of items in the Node.
+ */
+ size: function() {
+ return this._node ? 1 : 0;
+ },
+
+ /**
+ * Inserts the content before the reference node.
+ * @method insert
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @param {Int | Y.Node | HTMLElement | String} where The position to insert at.
+ * @chainable
+ */
+ insert: function(content, where) {
+ var node = this._node;
+
+ if (content) {
+ if (typeof where === 'number') { // allow index
+ where = this._node.childNodes[where];
+ }
+
+ if (typeof content !== 'string') { // allow Node or NodeList/Array instances
+ if (content._node) { // Node
+ content = content._node;
+ } else if (content._nodes || (!content.nodeType && content.length)) { // NodeList or Array
+ Y.each(content._nodes, function(n) {
+ Y.DOM.addHTML(node, n, where);
+ });
+
+ return this; // NOTE: early return
+ }
+ }
+ Y.DOM.addHTML(node, content, where);
+ }
+ return this;
+ },
+
+ /**
+ * Inserts the content as the firstChild of the node.
+ * @method prepend
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ prepend: function(content) {
+ return this.insert(content, 0);
+ },
+
+ /**
+ * Inserts the content as the lastChild of the node.
+ * @method append
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ append: function(content) {
+ return this.insert(content, null);
+ },
+
+ /**
+ * Replaces the node's current content with the content.
+ * @method setContent
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ setContent: function(content) {
+ Y.DOM.addHTML(this._node, content, 'replace');
+ return this;
+ },
+
+ // TODO: need this?
+ hasMethod: function(method) {
+ var node = this._node;
+ return (node && (typeof node === 'function'));
+ }
+}, true);
+
+Y.Node = Node;
+Y.get = Y.Node.get;
+Y.one = Y.Node.one;
+/**
+ * The NodeList module provides support for managing collections of Nodes.
+ * @module node
+ * @submodule nodelist
+ */
+
+/**
+ * The NodeList class provides a wrapper for manipulating DOM NodeLists.
+ * NodeList properties can be accessed via the set/get methods.
+ * Use Y.all() to retrieve NodeList instances.
+ *
+ * @class NodeList
+ * @constructor
+ */
+
+var NodeList = function(nodes) {
+ if (typeof nodes === 'string') {
+ this._query = nodes;
+ nodes = Y.Selector.query(nodes);
+ } else {
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ NodeList._instances[Y.stamp(this)] = this;
+ this._nodes = nodes;
+};
+// end "globals"
+
+NodeList.NAME = 'NodeList';
+
+/**
+ * Retrieves the DOM nodes bound to a NodeList instance
+ * @method NodeList.getDOMNodes
+ * @static
+ *
+ * @param {Y.NodeList} node The NodeList instance
+ * @return {Array} The array of DOM nodes bound to the NodeList
+ */
+NodeList.getDOMNodes = function(nodeList) {
+ return nodeList._nodes;
+};
+
+NodeList._instances = [];
+
+NodeList.each = function(instance, fn, context) {
+ var nodes = instance._nodes;
+ if (nodes && nodes.length) {
+ Y.Array.each(nodes, fn, context || instance);
+ } else {
+ }
+};
+
+NodeList.addMethod = function(name, fn, context) {
+ if (name && fn) {
+ NodeList.prototype[name] = function() {
+ var ret = [],
+ args = arguments;
+
+ Y.Array.each(this._nodes, function(node) {
+ var UID = '_yuid',
+ instance = Y.Node._instances[node[UID]],
+ ctx,
+ result;
+
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+ ctx = context || instance;
+ result = fn.apply(ctx, args);
+ if (result !== undefined && result !== instance) {
+ ret[ret.length] = result;
+ }
+ });
+
+ // TODO: remove tmp pointer
+ return ret.length ? ret : this;
+ };
+ } else {
+ }
+};
+
+NodeList.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ NodeList.addMethod(name, host[name]);
+ } else {
+ Y.each(name, function(n) {
+ NodeList.importMethod(host, n);
+ });
+ }
+};
+
+NodeList._getTempNode = function(node) {
+ var tmp = NodeList._tempNode;
+ if (!tmp) {
+ tmp = Y.Node.create('<div></div>');
+ NodeList._tempNode = tmp;
+ }
+
+ tmp._node = node;
+ tmp._stateProxy = node;
+ return tmp;
+};
+
+Y.mix(NodeList.prototype, {
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ return Y.one((this._nodes || [])[index]);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the current Node instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ var instance = this;
+ Y.Array.each(this._nodes, function(node, index) {
+ node = Y.one(node);
+ return fn.call(context || node, node, index, instance);
+ });
+ return instance;
+ },
+
+ batch: function(fn, context) {
+ var nodelist = this;
+
+ Y.Array.each(this._nodes, function(node, index) {
+ var instance = Y.Node._instances[node[UID]];
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+
+ return fn.call(context || instance, instance, index, nodelist);
+ });
+ return nodelist;
+ },
+
+ /**
+ * Executes the function once for each node until a true value is returned.
+ * @method some
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to execute the function from.
+ * Default context is the current Node instance
+ * @return {Boolean} Whether or not the function returned true for any node.
+ */
+ some: function(fn, context) {
+ var instance = this;
+ return Y.Array.some(this._nodes, function(node, index) {
+ node = Y.one(node);
+ context = context || node;
+ return fn.call(context, node, index, instance);
+ });
+ },
+
+ /**
+ * Creates a documenFragment from the nodes bound to the NodeList instance
+ * @method toDocFrag
+ * @return Node a Node instance bound to the documentFragment
+ */
+ toFrag: function() {
+ return Y.one(Y.DOM._nl2frag(this._nodes));
+ },
+
+ /**
+ * Returns the index of the node in the NodeList instance
+ * or -1 if the node isn't found.
+ * @method indexOf
+ * @param {Y.Node || DOMNode} node the node to search for
+ * @return {Int} the index of the node value or -1 if not found
+ */
+ indexOf: function(node) {
+ return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
+ },
+
+ /**
+ * Filters the NodeList instance down to only nodes matching the given selector.
+ * @method filter
+ * @param {String} selector The selector to filter against
+ * @return {NodeList} NodeList containing the updated collection
+ * @see Selector
+ */
+ filter: function(selector) {
+ return Y.all(Y.Selector.filter(this._nodes, selector));
+ },
+
+
+ /**
+ * Creates a new NodeList containing all nodes at every n indices, where
+ * remainder n % index equals r.
+ * (zero-based index).
+ * @method modulus
+ * @param {Int} n The offset to use (return every nth node)
+ * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ modulus: function(n, r) {
+ r = r || 0;
+ var nodes = [];
+ NodeList.each(this, function(node, i) {
+ if (i % n === r) {
+ nodes.push(node);
+ }
+ });
+
+ return Y.all(nodes);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at odd indices
+ * (zero-based index).
+ * @method odd
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ odd: function() {
+ return this.modulus(2, 1);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at even indices
+ * (zero-based index), including zero.
+ * @method even
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ even: function() {
+ return this.modulus(2);
+ },
+
+ destructor: function() {
+ delete NodeList._instances[this[UID]];
+ },
+
+ /**
+ * Reruns the initial query, when created using a selector query
+ * @method refresh
+ * @chainable
+ */
+ refresh: function() {
+ var doc,
+ nodes = this._nodes;
+ if (this._query) {
+ if (nodes && nodes[0] && nodes[0].ownerDocument) {
+ doc = nodes[0].ownerDocument;
+ }
+
+ this._nodes = Y.Selector.query(this._query, doc || Y.config.doc);
+ }
+
+ return this;
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * @method on
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ on: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.on.apply(Y, args);
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * The handler is called only after all on() handlers are called
+ * and the event is not prevented.
+ * @method after
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ after: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.after.apply(Y, args);
+ },
+
+ /**
+ * Returns the current number of items in the NodeList.
+ * @method size
+ * @return {Int} The number of items in the NodeList.
+ */
+ size: function() {
+ return this._nodes.length;
+ },
+
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to any nodes',
+ nodes = this._nodes,
+ node;
+
+ if (nodes && nodes[0]) {
+ node = nodes[0];
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ if (nodes.length > 1) {
+ str += '...[' + nodes.length + ' items]';
+ }
+ }
+ return str || errorMsg;
+ }
+
+}, true);
+
+NodeList.importMethod(Y.Node.prototype, [
+ /**
+ * Called on each Node instance
+ * @for NodeList
+ * @method append
+ * @see Node.append
+ */
+ 'append',
+
+ /**
+ * Called on each Node instance
+ * @method detach
+ * @see Node.detach
+ */
+ 'detach',
+
+ /** Called on each Node instance
+ * @method detachAll
+ * @see Node.detachAll
+ */
+ 'detachAll',
+
+ /** Called on each Node instance
+ * @method insert
+ * @see NodeInsert
+ */
+ 'insert',
+
+ /** Called on each Node instance
+ * @method prepend
+ * @see Node.prepend
+ */
+ 'prepend',
+
+ /** Called on each Node instance
+ * @method remove
+ * @see Node.remove
+ */
+ 'remove',
+
+ /** Called on each Node instance
+ * @method set
+ * @see Node.set
+ */
+ 'set',
+
+ /** Called on each Node instance
+ * @method setContent
+ * @see Node.setContent
+ */
+ 'setContent'
+]);
+
+// one-off implementation to convert array of Nodes to NodeList
+// e.g. Y.all('input').get('parentNode');
+
+/** Called on each Node instance
+ * @method get
+ * @see Node
+ */
+NodeList.prototype.get = function(attr) {
+ var ret = [],
+ nodes = this._nodes,
+ isNodeList = false,
+ getTemp = NodeList._getTempNode,
+ instance,
+ val;
+
+ if (nodes[0]) {
+ instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
+ val = instance._get(attr);
+ if (val && val.nodeType) {
+ isNodeList = true;
+ }
+ }
+
+ Y.Array.each(nodes, function(node) {
+ instance = Y.Node._instances[node._yuid];
+
+ if (!instance) {
+ instance = getTemp(node);
+ }
+
+ val = instance._get(attr);
+ if (!isNodeList) { // convert array of Nodes to NodeList
+ val = Y.Node.scrubVal(val, instance);
+ }
+
+ ret.push(val);
+ });
+
+ return (isNodeList) ? Y.all(ret) : ret;
+};
+
+Y.NodeList = NodeList;
+
+Y.all = function(nodes) {
+ return new NodeList(nodes);
+};
+
+Y.Node.all = Y.all;
+Y.Array.each([
+ /**
+ * Passes through to DOM method.
+ * @method replaceChild
+ * @for Node
+ * @param {HTMLElement | Node} node Node to be inserted
+ * @param {HTMLElement | Node} refNode Node to be replaced
+ * @return {Node} The replaced node
+ */
+ 'replaceChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method appendChild
+ * @param {HTMLElement | Node} node Node to be appended
+ * @return {Node} The appended node
+ */
+ 'appendChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method insertBefore
+ * @param {HTMLElement | Node} newNode Node to be appended
+ * @param {HTMLElement | Node} refNode Node to be inserted before
+ * @return {Node} The inserted node
+ */
+ 'insertBefore',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeChild
+ * @param {HTMLElement | Node} node Node to be removed
+ * @return {Node} The removed node
+ */
+ 'removeChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasChildNodes
+ * @return {Boolean} Whether or not the node has any childNodes
+ */
+ 'hasChildNodes',
+
+ /**
+ * Passes through to DOM method.
+ * @method cloneNode
+ * @param {Boolean} deep Whether or not to perform a deep clone, which includes
+ * subtree and attributes
+ * @return {Node} The clone
+ */
+ 'cloneNode',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasAttribute
+ * @param {String} attribute The attribute to test for
+ * @return {Boolean} Whether or not the attribute is present
+ */
+ 'hasAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeAttribute
+ * @param {String} attribute The attribute to be removed
+ * @chainable
+ */
+ 'removeAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method scrollIntoView
+ * @chainable
+ */
+ 'scrollIntoView',
+
+ /**
+ * Passes through to DOM method.
+ * @method getElementsByTagName
+ * @param {String} tagName The tagName to collect
+ * @return {NodeList} A NodeList representing the HTMLCollection
+ */
+ 'getElementsByTagName',
+
+ /**
+ * Passes through to DOM method.
+ * @method focus
+ * @chainable
+ */
+ 'focus',
+
+ /**
+ * Passes through to DOM method.
+ * @method blur
+ * @chainable
+ */
+ 'blur',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method submit
+ * @chainable
+ */
+ 'submit',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method reset
+ * @chainable
+ */
+ 'reset',
+
+ /**
+ * Passes through to DOM method.
+ * @method select
+ * @chainable
+ */
+ 'select'
+], function(method) {
+ Y.Node.prototype[method] = function(arg1, arg2, arg3) {
+ var ret = this.invoke(method, arg1, arg2, arg3);
+ return ret;
+ };
+});
+
+Node.importMethod(Y.DOM, [
+ /**
+ * Determines whether the ndoe is an ancestor of another HTML element in the DOM hierarchy.
+ * @method contains
+ * @param {Node | HTMLElement} needle The possible node or descendent
+ * @return {Boolean} Whether or not this node is the needle its ancestor
+ */
+ 'contains',
+ /**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @for Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+ 'setAttribute',
+ /**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @for Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+ 'getAttribute'
+]);
+
+/**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @see Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+
+/**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @see Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+Y.NodeList.importMethod(Y.Node.prototype, ['getAttribute', 'setAttribute']);
+(function(Y) {
+ var methods = [
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @for Node
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+ 'hasClass',
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+ 'addClass',
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+ 'removeClass',
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+ 'replaceClass',
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ 'toggleClass'
+ ];
+
+ Y.Node.importMethod(Y.DOM, methods);
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @see Node.hasClass
+ * @for NodeList
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @see Node.addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @see Node.removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @see Node.replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @see Node.toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+if (!document.documentElement.hasAttribute) { // IE < 8
+ Y.Node.prototype.hasAttribute = function(attr) {
+ return Y.DOM.getAttribute(this._node, attr) !== '';
+ };
+}
+
+// IE throws error when setting input.type = 'hidden',
+// input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden'
+Y.Node.ATTRS.type = {
+ setter: function(val) {
+ if (val === 'hidden') {
+ try {
+ this._node.type = 'hidden';
+ } catch(e) {
+ this.setStyle('display', 'none');
+ this._inputType = 'hidden';
+ }
+ } else {
+ try { // IE errors when changing the type from "hidden'
+ this._node.type = val;
+ } catch (e) {
+ }
+ }
+ return val;
+ },
+
+ getter: function() {
+ return this._inputType || this._node.type;
+ },
+
+ _bypassProxy: true // don't update DOM when using with Attribute
+};
+
+
+}, '3.0.0' ,{requires:['dom-base', 'selector-css2', 'event-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-base', function(Y) {
+
+/**
+ * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
+ * @module node
+ * @submodule node-base
+ */
+
+/**
+ * The Node class provides a wrapper for manipulating DOM Nodes.
+ * Node properties can be accessed via the set/get methods.
+ * Use Y.get() to retrieve Node instances.
+ *
+ * <strong>NOTE:</strong> Node properties are accessed using
+ * the <code>set</code> and <code>get</code> methods.
+ *
+ * @class Node
+ * @constructor
+ * @for Node
+ */
+
+// "globals"
+var DOT = '.',
+ NODE_NAME = 'nodeName',
+ NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TAG_NAME = 'tagName',
+ UID = '_yuid',
+
+ Node = function(node) {
+ var uid = node[UID];
+
+ if (uid && Node._instances[uid] && Node._instances[uid]._node !== node) {
+ node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
+ }
+
+ uid = Y.stamp(node);
+ if (!uid) { // stamp failed; likely IE non-HTMLElement
+ uid = Y.guid();
+ }
+
+ this[UID] = uid;
+
+ this._node = node;
+ Node._instances[uid] = this;
+
+ this._stateProxy = node; // when augmented with Attribute
+
+ if (this._initPlugins) { // when augmented with Plugin.Host
+ this._initPlugins();
+ }
+ },
+
+ // used with previous/next/ancestor tests
+ _wrapFn = function(fn) {
+ var ret = null;
+ if (fn) {
+ ret = (typeof fn === 'string') ?
+ function(n) {
+ return Y.Selector.test(n, fn);
+ } :
+ function(n) {
+ return fn(Node.get(n));
+ };
+ }
+
+ return ret;
+ };
+// end "globals"
+
+Node.NAME = 'Node';
+
+Node.re_aria = /^(?:role$|aria-)/;
+
+Node.DOM_EVENTS = {
+ abort: true,
+ beforeunload: true,
+ blur: true,
+ change: true,
+ click: true,
+ close: true,
+ command: true,
+ contextmenu: true,
+ drag: true,
+ dragstart: true,
+ dragenter: true,
+ dragover: true,
+ dragleave: true,
+ dragend: true,
+ drop: true,
+ dblclick: true,
+ error: true,
+ focus: true,
+ keydown: true,
+ keypress: true,
+ keyup: true,
+ load: true,
+ message: true,
+ mousedown: true,
+ mousemove: true,
+ mouseout: true,
+ mouseover: true,
+ mouseup: true,
+ mousemultiwheel: true,
+ mousewheel: true,
+ submit: true,
+ mouseenter: true,
+ mouseleave: true,
+ scroll: true,
+ reset: true,
+ resize: true,
+ select: true,
+ textInput: true,
+ unload: true
+};
+
+// Add custom event adaptors to this list. This will make it so
+// that delegate, key, available, contentready, etc all will
+// be available through Node.on
+Y.mix(Node.DOM_EVENTS, Y.Env.evt.plugins);
+
+Node._instances = {};
+
+/**
+ * Retrieves the DOM node bound to a Node instance
+ * @method Node.getDOMNode
+ * @static
+ *
+ * @param {Y.Node || HTMLNode} node The Node instance or an HTMLNode
+ * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
+ * as the node argument, it is simply returned.
+ */
+Node.getDOMNode = function(node) {
+ if (node) {
+ return (node.nodeType) ? node : node._node || null;
+ }
+ return null;
+};
+
+Node.scrubVal = function(val, node) {
+ if (node && val) { // only truthy values are risky
+ if (typeof val === 'object' || typeof val === 'function') { // safari nodeList === function
+ if (NODE_TYPE in val || Y.DOM.isWindow(val)) {// node || window
+ val = Node.get(val);
+ } else if ((val.item && !val._nodes) || // dom collection or Node instance
+ (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
+ val = Y.all(val);
+ }
+ }
+ } else if (val === undefined) {
+ val = node; // for chaining
+ }
+
+ return val;
+};
+
+Node.addMethod = function(name, fn, context) {
+ if (name && fn && typeof fn === 'function') {
+ Node.prototype[name] = function() {
+ context = context || this;
+ var args = Y.Array(arguments),
+ ret;
+
+ if (args[0] && args[0] instanceof Node) {
+ args[0] = args[0]._node;
+ }
+
+ if (args[1] && args[1] instanceof Node) {
+ args[1] = args[1]._node;
+ }
+ args.unshift(this._node);
+ ret = Node.scrubVal(fn.apply(context, args), this);
+ return ret;
+ };
+ } else {
+ Y.log('unable to add method: ' + name, 'warn', 'Node');
+ }
+};
+
+Node.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ Node.addMethod(altName, host[name], host);
+ } else {
+ Y.each(name, function(n) {
+ Node.importMethod(host, n);
+ });
+ }
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.one = function(node) {
+ var instance = null,
+ cachedNode,
+ uid;
+
+ if (node) {
+ if (typeof node === 'string') {
+ if (node.indexOf('doc') === 0) { // doc OR document
+ node = Y.config.doc;
+ } else if (node.indexOf('win') === 0) { // win OR window
+ node = Y.config.win;
+ } else {
+ node = Y.Selector.query(node, null, true);
+ }
+ if (!node) {
+ return null;
+ }
+ } else if (node instanceof Node) {
+ return node; // NOTE: return
+ }
+
+ uid = node._yuid;
+ instance = Node._instances[uid]; // reuse exising instances
+ cachedNode = instance ? instance._node : null;
+ if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
+ instance = new Node(node);
+ }
+ }
+ return instance;
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.get
+ * @deprecated Use Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.get = function() {
+ Y.log('Y.get is deprecated, use Y.one', 'warn', 'deprecated');
+ return Node.one.apply(Node, arguments);
+};
+
+/**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @static
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+Node.create = function() {
+ return Node.get(Y.DOM.create.apply(Y.DOM, arguments));
+};
+
+Node.ATTRS = {
+ /**
+ * Allows for getting and setting the text of an element.
+ * Formatting is preserved and special characters are treated literally.
+ * @config text
+ * @type String
+ */
+ text: {
+ getter: function() {
+ return Y.DOM.getText(this._node);
+ },
+
+ setter: function(content) {
+ Y.DOM.setText(this._node, content);
+ return content;
+ }
+ },
+
+ 'options': {
+ getter: function() {
+ return this._node.getElementsByTagName('option');
+ }
+ },
+
+ // IE: elements collection is also FORM node which trips up scrubVal.
+ // preconverting to NodeList
+ // TODO: break out for IE only
+ 'elements': {
+ getter: function() {
+ return Y.all(this._node.elements);
+ }
+ },
+
+ /**
+ * Returns a NodeList instance of all HTMLElement children.
+ * @readOnly
+ * @config children
+ * @type NodeList
+ */
+ 'children': {
+ getter: function() {
+ var node = this._node,
+ children = node.children,
+ childNodes, i, len;
+
+ if (!children) {
+ childNodes = node.childNodes;
+ children = [];
+
+ for (i = 0, len = childNodes.length; i < len; ++i) {
+ if (childNodes[i][TAG_NAME]) {
+ children[children.length] = childNodes[i];
+ }
+ }
+ }
+ return Y.all(children);
+ }
+ },
+
+ value: {
+ getter: function() {
+ return Y.DOM.getValue(this._node);
+ },
+
+ setter: function(val) {
+ Y.DOM.setValue(this._node, val);
+ return val;
+ }
+ },
+
+ data: {
+ getter: function() {
+ return this._data;
+ },
+
+ setter: function(val) {
+ this._data = val;
+ return val;
+ }
+ }
+};
+
+// call with instance context
+Node.DEFAULT_SETTER = function(name, val) {
+ var node = this._stateProxy,
+ strPath;
+
+ if (name.indexOf(DOT) > -1) {
+ strPath = name;
+ name = name.split(DOT);
+ // only allow when defined on node
+ Y.Object.setValue(node, name, val);
+ } else if (node[name] !== undefined) { // pass thru DOM properties
+ node[name] = val;
+ }
+
+ return val;
+};
+
+// call with instance context
+Node.DEFAULT_GETTER = function(name) {
+ var node = this._stateProxy,
+ val;
+
+ if (name.indexOf && name.indexOf(DOT) > -1) {
+ val = Y.Object.getValue(node, name.split(DOT));
+ } else if (node[name] !== undefined) { // pass thru from DOM
+ val = node[name];
+ }
+
+ return val;
+};
+
+Y.augment(Node, Y.Event.Target);
+
+Y.mix(Node.prototype, {
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to a node',
+ node = this._node;
+
+ if (node) {
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ // TODO: add yuid?
+ str += ' ' + this[UID];
+ }
+ return str || errorMsg;
+ },
+
+ /**
+ * Returns an attribute value on the Node instance
+ * @method get
+ * @param {String} attr The attribute to be set
+ * @return {any} The current value of the attribute
+ */
+ get: function(attr) {
+ var val;
+
+ if (this._getAttr) { // use Attribute imple
+ val = this._getAttr(attr);
+ } else {
+ val = this._get(attr);
+ }
+
+ if (val) {
+ val = Y.Node.scrubVal(val, this);
+ }
+ return val;
+ },
+
+ _get: function(attr) {
+ var attrConfig = Node.ATTRS[attr],
+ val;
+
+ if (attrConfig && attrConfig.getter) {
+ val = attrConfig.getter.call(this);
+ } else if (Node.re_aria.test(attr)) {
+ val = this._node.getAttribute(attr, 2);
+ } else {
+ val = Node.DEFAULT_GETTER.apply(this, arguments);
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets an attribute on the Node instance.
+ * @method set
+ * @param {String} attr The attribute to be set.
+ * @param {any} val The value to set the attribute to.
+ * @chainable
+ */
+ set: function(attr, val) {
+ var attrConfig = Node.ATTRS[attr];
+
+ if (this._setAttr) { // use Attribute imple
+ this._setAttr.apply(this, arguments);
+ } else { // use setters inline
+ if (attrConfig && attrConfig.setter) {
+ attrConfig.setter.call(this, val);
+ } else if (Node.re_aria.test(attr)) { // special case Aria
+ this._node.setAttribute(attr, val);
+ } else {
+ Node.DEFAULT_SETTER.apply(this, arguments);
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Sets multiple attributes.
+ * @method setAttrs
+ * @param {Object} attrMap an object of name/value pairs to set
+ * @chainable
+ */
+ setAttrs: function(attrMap) {
+ if (this._setAttrs) { // use Attribute imple
+ this._setAttrs(attrMap);
+ } else { // use setters inline
+ Y.Object.each(attrMap, function(v, n) {
+ this.set(n, v);
+ }, this);
+ }
+
+ return this;
+ },
+
+ /**
+ * Returns an object containing the values for the requested attributes.
+ * @method getAttrs
+ * @param {Array} attrs an array of attributes to get values
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs: function(attrs) {
+ var ret = {};
+ if (this._getAttrs) { // use Attribute imple
+ this._getAttrs(attrs);
+ } else { // use setters inline
+ Y.Array.each(attrs, function(v, n) {
+ ret[v] = this.get(v);
+ }, this);
+ }
+
+ return ret;
+ },
+
+ /**
+ * Creates a new Node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+ create: Node.create,
+
+ /**
+ * Compares nodes to determine if they match.
+ * Node instances can be compared to each other and/or HTMLElements.
+ * @method compareTo
+ * @param {HTMLElement | Node} refNode The reference node to compare to the node.
+ * @return {Boolean} True if the nodes match, false if they do not.
+ */
+ compareTo: function(refNode) {
+ var node = this._node;
+ if (refNode instanceof Y.Node) {
+ refNode = refNode._node;
+ }
+ return node === refNode;
+ },
+
+ /**
+ * Determines whether the node is appended to the document.
+ * @method inDoc
+ * @param {Node|HTMLElement} doc optional An optional document to check against.
+ * Defaults to current document.
+ * @return {Boolean} Whether or not this node is appended to the document.
+ */
+ inDoc: function(doc) {
+ var node = this._node;
+ doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
+ if (doc.documentElement) {
+ return Y.DOM.contains(doc.documentElement, node);
+ }
+ },
+
+ getById: function(id) {
+ var node = this._node,
+ ret = Y.DOM.byId(id, node[OWNER_DOCUMENT]);
+ if (ret && Y.DOM.contains(node, ret)) {
+ ret = Y.one(ret);
+ } else {
+ ret = null;
+ }
+ return ret;
+ },
+
+ /**
+ * Returns the nearest ancestor that passes the test applied by supplied boolean method.
+ * @method ancestor
+ * @param {String | Function} fn A selector string or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} The matching Node instance or null if not found
+ */
+ ancestor: function(fn) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'parentNode', _wrapFn(fn)));
+ },
+
+ /**
+ * Returns the previous matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method previous
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ previous: function(fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Returns the next matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method next
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ next: function(node, fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method one
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ one: function(selector) {
+ return Y.one(Y.Selector.query(selector, this._node, true));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method query
+ * @deprecated Use one()
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ query: function(selector) {
+ Y.log('query() is deprecated, use one()', 'warn', 'deprecated');
+ return this.one(selector);
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method all
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ all: function(selector) {
+ var nodelist = Y.all(Y.Selector.query(selector, this._node));
+ nodelist._query = selector;
+ return nodelist;
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method queryAll
+ * @deprecated Use all()
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ queryAll: function(selector) {
+ Y.log('queryAll() is deprecated, use all()', 'warn', 'deprecated');
+ return this.all(selector);
+ },
+
+ // TODO: allow fn test
+ /**
+ * Test if the supplied node matches the supplied selector.
+ * @method test
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {boolean} Whether or not the node matches the selector.
+ */
+ test: function(selector) {
+ return Y.Selector.test(this._node, selector);
+ },
+
+ /**
+ * Removes the node from its parent.
+ * Shortcut for myNode.get('parentNode').removeChild(myNode);
+ * @method remove
+ * @chainable
+ *
+ */
+ remove: function(destroy) {
+ var node = this._node;
+ node.parentNode.removeChild(node);
+ if (destroy) {
+ this.destroy(true);
+ }
+ return this;
+ },
+
+ /**
+ * Replace the node with the other node. This is a DOM update only
+ * and does not change the node bound to the Node instance.
+ * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
+ * @method replace
+ * @chainable
+ *
+ */
+ replace: function(newNode) {
+ var node = this._node;
+ node.parentNode.replaceChild(newNode, node);
+ return this;
+ },
+
+ purge: function(recurse, type) {
+ Y.Event.purgeElement(this._node, recurse, type);
+ },
+
+ destroy: function(purge) {
+ delete Node._instances[this[UID]];
+ if (purge) {
+ this.purge(true);
+ }
+
+ if (this.unplug) {
+ this.unplug();
+ }
+
+ this._node._yuid = null;
+ this._node = null;
+ this._stateProxy = null;
+ },
+
+ /**
+ * Invokes a method on the Node instance
+ * @method invoke
+ * @param {String} method The name of the method to invoke
+ * @param {Any} a, b, c, etc. Arguments to invoke the method with.
+ * @return Whatever the underly method returns.
+ * DOM Nodes and Collections return values
+ * are converted to Node/NodeList instances.
+ *
+ */
+ invoke: function(method, a, b, c, d, e) {
+ var node = this._node,
+ ret;
+
+ if (a && a instanceof Y.Node) {
+ a = a._node;
+ }
+
+ if (b && b instanceof Y.Node) {
+ b = b._node;
+ }
+
+ ret = node[method](a, b, c, d, e);
+ return Y.Node.scrubVal(ret, this);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @deprecated Use NodeList
+ * @param {Function} fn The function to apply
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the NodeList instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ context = context || this;
+ Y.log('each is deprecated on Node', 'warn', 'deprecated');
+ return fn.call(context, this);
+ },
+
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ * @deprecated Use NodeList
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ Y.log('item is deprecated on Node', 'warn', 'deprecated');
+ return this;
+ },
+
+ /**
+ * Returns the current number of items in the Node.
+ * @method size
+ * @deprecated Use NodeList
+ * @return {Int} The number of items in the Node.
+ */
+ size: function() {
+ Y.log('size is deprecated on Node', 'warn', 'deprecated');
+ return this._node ? 1 : 0;
+ },
+
+ /**
+ * Inserts the content before the reference node.
+ * @method insert
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @param {Int | Y.Node | HTMLElement | String} where The position to insert at.
+ * @chainable
+ */
+ insert: function(content, where) {
+ var node = this._node;
+
+ if (content) {
+ if (typeof where === 'number') { // allow index
+ where = this._node.childNodes[where];
+ }
+
+ if (typeof content !== 'string') { // allow Node or NodeList/Array instances
+ if (content._node) { // Node
+ content = content._node;
+ } else if (content._nodes || (!content.nodeType && content.length)) { // NodeList or Array
+ Y.each(content._nodes, function(n) {
+ Y.DOM.addHTML(node, n, where);
+ });
+
+ return this; // NOTE: early return
+ }
+ }
+ Y.DOM.addHTML(node, content, where);
+ }
+ return this;
+ },
+
+ /**
+ * Inserts the content as the firstChild of the node.
+ * @method prepend
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ prepend: function(content) {
+ return this.insert(content, 0);
+ },
+
+ /**
+ * Inserts the content as the lastChild of the node.
+ * @method append
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ append: function(content) {
+ return this.insert(content, null);
+ },
+
+ /**
+ * Replaces the node's current content with the content.
+ * @method setContent
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ setContent: function(content) {
+ Y.DOM.addHTML(this._node, content, 'replace');
+ return this;
+ },
+
+ // TODO: need this?
+ hasMethod: function(method) {
+ var node = this._node;
+ return (node && (typeof node === 'function'));
+ }
+}, true);
+
+Y.Node = Node;
+Y.get = Y.Node.get;
+Y.one = Y.Node.one;
+/**
+ * The NodeList module provides support for managing collections of Nodes.
+ * @module node
+ * @submodule nodelist
+ */
+
+/**
+ * The NodeList class provides a wrapper for manipulating DOM NodeLists.
+ * NodeList properties can be accessed via the set/get methods.
+ * Use Y.all() to retrieve NodeList instances.
+ *
+ * @class NodeList
+ * @constructor
+ */
+
+var NodeList = function(nodes) {
+ if (typeof nodes === 'string') {
+ this._query = nodes;
+ nodes = Y.Selector.query(nodes);
+ } else {
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ NodeList._instances[Y.stamp(this)] = this;
+ this._nodes = nodes;
+};
+// end "globals"
+
+NodeList.NAME = 'NodeList';
+
+/**
+ * Retrieves the DOM nodes bound to a NodeList instance
+ * @method NodeList.getDOMNodes
+ * @static
+ *
+ * @param {Y.NodeList} node The NodeList instance
+ * @return {Array} The array of DOM nodes bound to the NodeList
+ */
+NodeList.getDOMNodes = function(nodeList) {
+ return nodeList._nodes;
+};
+
+NodeList._instances = [];
+
+NodeList.each = function(instance, fn, context) {
+ var nodes = instance._nodes;
+ if (nodes && nodes.length) {
+ Y.Array.each(nodes, fn, context || instance);
+ } else {
+ Y.log('no nodes bound to ' + this, 'warn', 'NodeList');
+ }
+};
+
+NodeList.addMethod = function(name, fn, context) {
+ if (name && fn) {
+ NodeList.prototype[name] = function() {
+ var ret = [],
+ args = arguments;
+
+ Y.Array.each(this._nodes, function(node) {
+ var UID = '_yuid',
+ instance = Y.Node._instances[node[UID]],
+ ctx,
+ result;
+
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+ ctx = context || instance;
+ result = fn.apply(ctx, args);
+ if (result !== undefined && result !== instance) {
+ ret[ret.length] = result;
+ }
+ });
+
+ // TODO: remove tmp pointer
+ return ret.length ? ret : this;
+ };
+ } else {
+ Y.log('unable to add method: ' + name, 'warn', 'Node');
+ }
+};
+
+NodeList.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ NodeList.addMethod(name, host[name]);
+ } else {
+ Y.each(name, function(n) {
+ NodeList.importMethod(host, n);
+ });
+ }
+};
+
+NodeList._getTempNode = function(node) {
+ var tmp = NodeList._tempNode;
+ if (!tmp) {
+ tmp = Y.Node.create('<div></div>');
+ NodeList._tempNode = tmp;
+ }
+
+ tmp._node = node;
+ tmp._stateProxy = node;
+ return tmp;
+};
+
+Y.mix(NodeList.prototype, {
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ return Y.one((this._nodes || [])[index]);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the current Node instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ var instance = this;
+ Y.Array.each(this._nodes, function(node, index) {
+ node = Y.one(node);
+ return fn.call(context || node, node, index, instance);
+ });
+ return instance;
+ },
+
+ batch: function(fn, context) {
+ var nodelist = this;
+
+ Y.Array.each(this._nodes, function(node, index) {
+ var instance = Y.Node._instances[node[UID]];
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+
+ return fn.call(context || instance, instance, index, nodelist);
+ });
+ return nodelist;
+ },
+
+ /**
+ * Executes the function once for each node until a true value is returned.
+ * @method some
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to execute the function from.
+ * Default context is the current Node instance
+ * @return {Boolean} Whether or not the function returned true for any node.
+ */
+ some: function(fn, context) {
+ var instance = this;
+ return Y.Array.some(this._nodes, function(node, index) {
+ node = Y.one(node);
+ context = context || node;
+ return fn.call(context, node, index, instance);
+ });
+ },
+
+ /**
+ * Creates a documenFragment from the nodes bound to the NodeList instance
+ * @method toDocFrag
+ * @return Node a Node instance bound to the documentFragment
+ */
+ toFrag: function() {
+ return Y.one(Y.DOM._nl2frag(this._nodes));
+ },
+
+ /**
+ * Returns the index of the node in the NodeList instance
+ * or -1 if the node isn't found.
+ * @method indexOf
+ * @param {Y.Node || DOMNode} node the node to search for
+ * @return {Int} the index of the node value or -1 if not found
+ */
+ indexOf: function(node) {
+ return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
+ },
+
+ /**
+ * Filters the NodeList instance down to only nodes matching the given selector.
+ * @method filter
+ * @param {String} selector The selector to filter against
+ * @return {NodeList} NodeList containing the updated collection
+ * @see Selector
+ */
+ filter: function(selector) {
+ return Y.all(Y.Selector.filter(this._nodes, selector));
+ },
+
+
+ /**
+ * Creates a new NodeList containing all nodes at every n indices, where
+ * remainder n % index equals r.
+ * (zero-based index).
+ * @method modulus
+ * @param {Int} n The offset to use (return every nth node)
+ * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ modulus: function(n, r) {
+ r = r || 0;
+ var nodes = [];
+ NodeList.each(this, function(node, i) {
+ if (i % n === r) {
+ nodes.push(node);
+ }
+ });
+
+ return Y.all(nodes);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at odd indices
+ * (zero-based index).
+ * @method odd
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ odd: function() {
+ return this.modulus(2, 1);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at even indices
+ * (zero-based index), including zero.
+ * @method even
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ even: function() {
+ return this.modulus(2);
+ },
+
+ destructor: function() {
+ delete NodeList._instances[this[UID]];
+ },
+
+ /**
+ * Reruns the initial query, when created using a selector query
+ * @method refresh
+ * @chainable
+ */
+ refresh: function() {
+ var doc,
+ nodes = this._nodes;
+ if (this._query) {
+ if (nodes && nodes[0] && nodes[0].ownerDocument) {
+ doc = nodes[0].ownerDocument;
+ }
+
+ this._nodes = Y.Selector.query(this._query, doc || Y.config.doc);
+ }
+
+ return this;
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * @method on
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ on: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.on.apply(Y, args);
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * The handler is called only after all on() handlers are called
+ * and the event is not prevented.
+ * @method after
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ after: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.after.apply(Y, args);
+ },
+
+ /**
+ * Returns the current number of items in the NodeList.
+ * @method size
+ * @return {Int} The number of items in the NodeList.
+ */
+ size: function() {
+ return this._nodes.length;
+ },
+
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to any nodes',
+ nodes = this._nodes,
+ node;
+
+ if (nodes && nodes[0]) {
+ node = nodes[0];
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ if (nodes.length > 1) {
+ str += '...[' + nodes.length + ' items]';
+ }
+ }
+ return str || errorMsg;
+ }
+
+}, true);
+
+NodeList.importMethod(Y.Node.prototype, [
+ /**
+ * Called on each Node instance
+ * @for NodeList
+ * @method append
+ * @see Node.append
+ */
+ 'append',
+
+ /**
+ * Called on each Node instance
+ * @method detach
+ * @see Node.detach
+ */
+ 'detach',
+
+ /** Called on each Node instance
+ * @method detachAll
+ * @see Node.detachAll
+ */
+ 'detachAll',
+
+ /** Called on each Node instance
+ * @method insert
+ * @see NodeInsert
+ */
+ 'insert',
+
+ /** Called on each Node instance
+ * @method prepend
+ * @see Node.prepend
+ */
+ 'prepend',
+
+ /** Called on each Node instance
+ * @method remove
+ * @see Node.remove
+ */
+ 'remove',
+
+ /** Called on each Node instance
+ * @method set
+ * @see Node.set
+ */
+ 'set',
+
+ /** Called on each Node instance
+ * @method setContent
+ * @see Node.setContent
+ */
+ 'setContent'
+]);
+
+// one-off implementation to convert array of Nodes to NodeList
+// e.g. Y.all('input').get('parentNode');
+
+/** Called on each Node instance
+ * @method get
+ * @see Node
+ */
+NodeList.prototype.get = function(attr) {
+ var ret = [],
+ nodes = this._nodes,
+ isNodeList = false,
+ getTemp = NodeList._getTempNode,
+ instance,
+ val;
+
+ if (nodes[0]) {
+ instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
+ val = instance._get(attr);
+ if (val && val.nodeType) {
+ isNodeList = true;
+ }
+ }
+
+ Y.Array.each(nodes, function(node) {
+ instance = Y.Node._instances[node._yuid];
+
+ if (!instance) {
+ instance = getTemp(node);
+ }
+
+ val = instance._get(attr);
+ if (!isNodeList) { // convert array of Nodes to NodeList
+ val = Y.Node.scrubVal(val, instance);
+ }
+
+ ret.push(val);
+ });
+
+ return (isNodeList) ? Y.all(ret) : ret;
+};
+
+Y.NodeList = NodeList;
+
+Y.all = function(nodes) {
+ return new NodeList(nodes);
+};
+
+Y.Node.all = Y.all;
+Y.Array.each([
+ /**
+ * Passes through to DOM method.
+ * @method replaceChild
+ * @for Node
+ * @param {HTMLElement | Node} node Node to be inserted
+ * @param {HTMLElement | Node} refNode Node to be replaced
+ * @return {Node} The replaced node
+ */
+ 'replaceChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method appendChild
+ * @param {HTMLElement | Node} node Node to be appended
+ * @return {Node} The appended node
+ */
+ 'appendChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method insertBefore
+ * @param {HTMLElement | Node} newNode Node to be appended
+ * @param {HTMLElement | Node} refNode Node to be inserted before
+ * @return {Node} The inserted node
+ */
+ 'insertBefore',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeChild
+ * @param {HTMLElement | Node} node Node to be removed
+ * @return {Node} The removed node
+ */
+ 'removeChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasChildNodes
+ * @return {Boolean} Whether or not the node has any childNodes
+ */
+ 'hasChildNodes',
+
+ /**
+ * Passes through to DOM method.
+ * @method cloneNode
+ * @param {Boolean} deep Whether or not to perform a deep clone, which includes
+ * subtree and attributes
+ * @return {Node} The clone
+ */
+ 'cloneNode',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasAttribute
+ * @param {String} attribute The attribute to test for
+ * @return {Boolean} Whether or not the attribute is present
+ */
+ 'hasAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeAttribute
+ * @param {String} attribute The attribute to be removed
+ * @chainable
+ */
+ 'removeAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method scrollIntoView
+ * @chainable
+ */
+ 'scrollIntoView',
+
+ /**
+ * Passes through to DOM method.
+ * @method getElementsByTagName
+ * @param {String} tagName The tagName to collect
+ * @return {NodeList} A NodeList representing the HTMLCollection
+ */
+ 'getElementsByTagName',
+
+ /**
+ * Passes through to DOM method.
+ * @method focus
+ * @chainable
+ */
+ 'focus',
+
+ /**
+ * Passes through to DOM method.
+ * @method blur
+ * @chainable
+ */
+ 'blur',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method submit
+ * @chainable
+ */
+ 'submit',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method reset
+ * @chainable
+ */
+ 'reset',
+
+ /**
+ * Passes through to DOM method.
+ * @method select
+ * @chainable
+ */
+ 'select'
+], function(method) {
+ Y.Node.prototype[method] = function(arg1, arg2, arg3) {
+ var ret = this.invoke(method, arg1, arg2, arg3);
+ return ret;
+ };
+});
+
+Node.importMethod(Y.DOM, [
+ /**
+ * Determines whether the ndoe is an ancestor of another HTML element in the DOM hierarchy.
+ * @method contains
+ * @param {Node | HTMLElement} needle The possible node or descendent
+ * @return {Boolean} Whether or not this node is the needle its ancestor
+ */
+ 'contains',
+ /**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @for Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+ 'setAttribute',
+ /**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @for Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+ 'getAttribute'
+]);
+
+/**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @see Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+
+/**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @see Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+Y.NodeList.importMethod(Y.Node.prototype, ['getAttribute', 'setAttribute']);
+(function(Y) {
+ var methods = [
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @for Node
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+ 'hasClass',
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+ 'addClass',
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+ 'removeClass',
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+ 'replaceClass',
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ 'toggleClass'
+ ];
+
+ Y.Node.importMethod(Y.DOM, methods);
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @see Node.hasClass
+ * @for NodeList
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @see Node.addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @see Node.removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @see Node.replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @see Node.toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+if (!document.documentElement.hasAttribute) { // IE < 8
+ Y.Node.prototype.hasAttribute = function(attr) {
+ return Y.DOM.getAttribute(this._node, attr) !== '';
+ };
+}
+
+// IE throws error when setting input.type = 'hidden',
+// input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden'
+Y.Node.ATTRS.type = {
+ setter: function(val) {
+ if (val === 'hidden') {
+ try {
+ this._node.type = 'hidden';
+ } catch(e) {
+ this.setStyle('display', 'none');
+ this._inputType = 'hidden';
+ }
+ } else {
+ try { // IE errors when changing the type from "hidden'
+ this._node.type = val;
+ } catch (e) {
+ Y.log('error setting type: ' + val, 'info', 'node');
+ }
+ }
+ return val;
+ },
+
+ getter: function() {
+ return this._inputType || this._node.type;
+ },
+
+ _bypassProxy: true // don't update DOM when using with Attribute
+};
+
+
+}, '3.0.0' ,{requires:['dom-base', 'selector-css2', 'event-base']});
+YUI.add('node-style', function(Y) {
+
+(function(Y) {
+/**
+ * Extended Node interface for managing node styles.
+ * @module node
+ * @submodule node-style
+ */
+
+var methods = [
+ /**
+ * Returns the style's current value.
+ * @method getStyle
+ * @for Node
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The current value of the style property for the element.
+ */
+ 'getStyle',
+
+ /**
+ * Returns the computed value for the given style property.
+ * @method getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The computed value of the style property for the element.
+ */
+ 'getComputedStyle',
+
+ /**
+ * Sets a style property of the node.
+ * @method setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+ 'setStyle',
+
+ /**
+ * Sets multiple style properties on the node.
+ * @method setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+ 'setStyles'
+];
+Y.Node.importMethod(Y.DOM, methods);
+/**
+ * Returns an array of values for each node.
+ * @method getStyle
+ * @for NodeList
+ * @see Node.getStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The current values of the style property for the element.
+ */
+
+/**
+ * Returns an array of the computed value for each node.
+ * @method getComputedStyle
+ * @see Node.getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The computed values for each node.
+ */
+
+/**
+ * Sets a style property on each node.
+ * @method setStyle
+ * @see Node.setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+
+/**
+ * Sets multiple style properties on each node.
+ * @method setStyles
+ * @see Node.setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-style', 'node-base']});
+YUI.add('node-screen', function(Y) {
+
+/**
+ * Extended Node interface for managing regions and screen positioning.
+ * Adds support for positioning elements and normalizes window size and scroll detection.
+ * @module node
+ * @submodule node-screen
+ */
+
+// these are all "safe" returns, no wrapping required
+Y.each([
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @config winWidth
+ * @for Node
+ * @type {Int}
+ */
+ 'winWidth',
+
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @config winHeight
+ * @type {Int}
+ */
+ 'winHeight',
+
+ /**
+ * Document width
+ * @config winHeight
+ * @type {Int}
+ */
+ 'docWidth',
+
+ /**
+ * Document height
+ * @config docHeight
+ * @type {Int}
+ */
+ 'docHeight',
+
+ /**
+ * Amount page has been scroll vertically
+ * @config docScrollX
+ * @type {Int}
+ */
+ 'docScrollX',
+
+ /**
+ * Amount page has been scroll horizontally
+ * @config docScrollY
+ * @type {Int}
+ */
+ 'docScrollY'
+ ],
+ function(name) {
+ Y.Node.ATTRS[name] = {
+ getter: function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(Y.Node.getDOMNode(this));
+
+ return Y.DOM[name].apply(this, args);
+ }
+ };
+ }
+);
+
+Y.Node.ATTRS.scrollLeft = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollLeft' in node) {
+ node.scrollLeft = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
+ }
+ } else {
+ Y.log('unable to set scrollLeft for ' + node, 'error', 'Node');
+ }
+ }
+};
+
+Y.Node.ATTRS.scrollTop = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollTop' in node) {
+ node.scrollTop = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
+ }
+ } else {
+ Y.log('unable to set scrollTop for ' + node, 'error', 'Node');
+ }
+ }
+};
+
+Y.Node.importMethod(Y.DOM, [
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getXY
+ * @for Node
+ * @return {Array} The XY position of the node
+*/
+ 'getXY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setXY
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setXY',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getX
+ * @return {Int} The X position of the node
+*/
+ 'getX',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setX
+ * @param {Int} x X value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setX',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getY
+ * @return {Int} The Y position of the node
+*/
+ 'getY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setY
+ * @param {Int} y Y value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setY'
+]);
+
+/**
+ * Returns a region object for the node
+ * @config region
+ * @for Node
+ * @type Node
+ */
+Y.Node.ATTRS.region = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ if (node && !node.tagName) {
+ if (node.nodeType === 9) { // document
+ node = node.documentElement;
+ } else if (node.alert) { // window
+ node = node.document.documentElement;
+ }
+ }
+ return Y.DOM.region(node);
+ }
+};
+
+/**
+ * Returns a region object for the node's viewport
+ * @config viewportRegion
+ * @type Node
+ */
+Y.Node.ATTRS.viewportRegion = {
+ getter: function() {
+ return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
+ }
+};
+
+Y.Node.importMethod(Y.DOM, 'inViewportRegion');
+
+// these need special treatment to extract 2nd node arg
+/**
+ * Compares the intersection of the node with another node or region
+ * @method intersect
+ * @for Node
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.intersect = function(node2, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.intersect(node1, node2, altRegion);
+};
+
+/**
+ * Determines whether or not the node is within the giving region.
+ * @method inRegion
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Boolean} all Whether or not all of the node must be in the region.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.inRegion = function(node2, all, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.inRegion(node1, node2, all, altRegion);
+};
+
+
+}, '3.0.0' ,{requires:['dom-screen']});
+YUI.add('node-pluginhost', function(Y) {
+
+/**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of Node by default).
+ *
+ * @method Node.plug
+ * @static
+ *
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+Y.Node.plug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.plug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+/**
+ * Unregisters any class level plugins which have been registered by the Node
+ *
+ * @method Node.unplug
+ * @static
+ *
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+Y.Node.unplug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.unplug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
+
+// allow batching of plug/unplug via NodeList
+// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
+Y.NodeList.prototype.plug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.plug.apply(Y.one(node), args);
+ });
+};
+
+Y.NodeList.prototype.unplug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.unplug.apply(Y.one(node), args);
+ });
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'pluginhost']});
+YUI.add('node-event-delegate', function(Y) {
+
+/**
+ * Functionality to make the node a delegated event container
+ * @module node
+ * @submodule node-event-delegate
+ */
+
+/**
+ * Functionality to make the node a delegated event container
+ * @method delegate
+ * @param type {String} the event type to delegate
+ * @param fn {Function} the function to execute
+ * @param selector {String} a selector that must match the target of the event.
+ * @return {Event.Handle} the detach handle
+ * @for Node
+ */
+Y.Node.prototype.delegate = function(type, fn, selector) {
+
+ var args = Array.prototype.slice.call(arguments, 3),
+ a = [type, fn, Y.Node.getDOMNode(this), selector];
+ a = a.concat(args);
+
+ return Y.delegate.apply(Y, a);
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'event-delegate', 'pluginhost']});
+
+
+YUI.add('node', function(Y){}, '3.0.0' ,{skinnable:false, use:['node-base', 'node-style', 'node-screen', 'node-pluginhost', 'node-event-delegate'], requires:['dom', 'event-base', 'event-delegate', 'pluginhost']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-event-delegate', function(Y) {
+
+/**
+ * Functionality to make the node a delegated event container
+ * @module node
+ * @submodule node-event-delegate
+ */
+
+/**
+ * Functionality to make the node a delegated event container
+ * @method delegate
+ * @param type {String} the event type to delegate
+ * @param fn {Function} the function to execute
+ * @param selector {String} a selector that must match the target of the event.
+ * @return {Event.Handle} the detach handle
+ * @for Node
+ */
+Y.Node.prototype.delegate = function(type, fn, selector) {
+
+ var args = Array.prototype.slice.call(arguments, 3),
+ a = [type, fn, Y.Node.getDOMNode(this), selector];
+ a = a.concat(args);
+
+ return Y.delegate.apply(Y, a);
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'event-delegate', 'pluginhost']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-event-delegate",function(A){A.Node.prototype.delegate=function(F,E,B){var D=Array.prototype.slice.call(arguments,3),C=[F,E,A.Node.getDOMNode(this),B];C=C.concat(D);return A.delegate.apply(A,C);};},"3.0.0",{requires:["node-base","event-delegate","pluginhost"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-event-delegate', function(Y) {
+
+/**
+ * Functionality to make the node a delegated event container
+ * @module node
+ * @submodule node-event-delegate
+ */
+
+/**
+ * Functionality to make the node a delegated event container
+ * @method delegate
+ * @param type {String} the event type to delegate
+ * @param fn {Function} the function to execute
+ * @param selector {String} a selector that must match the target of the event.
+ * @return {Event.Handle} the detach handle
+ * @for Node
+ */
+Y.Node.prototype.delegate = function(type, fn, selector) {
+
+ var args = Array.prototype.slice.call(arguments, 3),
+ a = [type, fn, Y.Node.getDOMNode(this), selector];
+ a = a.concat(args);
+
+ return Y.delegate.apply(Y, a);
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'event-delegate', 'pluginhost']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-event-simulate', function(Y) {
+
+/*
+ * Functionality to simulate events.
+ * @module node
+ * @for Node
+ * @submodule node-event-simulate
+ */
+
+ /**
+ * Simulates an event on the node.
+ * @param {String} type The type of event to simulate (i.e., "click").
+ * @param {Object} options (Optional) Extra options to copy onto the event object.
+ * @return {void}
+ * @method simulate
+ * @static
+ */
+ Y.Node.prototype.simulate = function(type, options) {
+ Y.Event.simulate(Y.Node.getDOMNode(this), type, options);
+ };
+
+
+
+}, '3.0.0' ,{requires:['node-base', 'event-simulate']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-event-simulate",function(A){A.Node.prototype.simulate=function(C,B){A.Event.simulate(A.Node.getDOMNode(this),C,B);};},"3.0.0",{requires:["node-base","event-simulate"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-event-simulate', function(Y) {
+
+/*
+ * Functionality to simulate events.
+ * @module node
+ * @for Node
+ * @submodule node-event-simulate
+ */
+
+ /**
+ * Simulates an event on the node.
+ * @param {String} type The type of event to simulate (i.e., "click").
+ * @param {Object} options (Optional) Extra options to copy onto the event object.
+ * @return {void}
+ * @method simulate
+ * @static
+ */
+ Y.Node.prototype.simulate = function(type, options) {
+ Y.Event.simulate(Y.Node.getDOMNode(this), type, options);
+ };
+
+
+
+}, '3.0.0' ,{requires:['node-base', 'event-simulate']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-base",function(C){var G=".",E="nodeName",I="nodeType",B="ownerDocument",H="tagName",D="_yuid",F=function(L){var K=L[D];if(K&&F._instances[K]&&F._instances[K]._node!==L){L[D]=null;}K=C.stamp(L);if(!K){K=C.guid();}this[D]=K;this._node=L;F._instances[K]=this;this._stateProxy=L;if(this._initPlugins){this._initPlugins();}},J=function(L){var K=null;if(L){K=(typeof L==="string")?function(M){return C.Selector.test(M,L);}:function(M){return L(F.get(M));};}return K;};F.NAME="Node";F.re_aria=/^(?:role$|aria-)/;F.DOM_EVENTS={abort:true,beforeunload:true,blur:true,change:true,click:true,close:true,command:true,contextmenu:true,drag:true,dragstart:true,dragenter:true,dragover:true,dragleave:true,dragend:true,drop:true,dblclick:true,error:true,focus:true,keydown:true,keypress:true,keyup:true,load:true,message:true,mousedown:true,mousemove:true,mouseout:true,mouseover:true,mouseup:true,mousemultiwheel:true,mousewheel:true,submit:true,mouseenter:true,mouseleave:true,scroll:true,reset:true,resize:true,select:true,textInput:true,unload:true};C.mix(F.DOM_EVENTS,C.Env.evt.plugins);F._instances={};F.getDOMNode=function(K){if(K){return(K.nodeType)?K:K._node||null;}return null;};F.scrubVal=function(L,K){if(K&&L){if(typeof L==="object"||typeof L==="function"){if(I in L||C.DOM.isWindow(L)){L=F.get(L);}else{if((L.item&&!L._nodes)||(L[0]&&L[0][I])){L=C.all(L);}}}}else{if(L===undefined){L=K;}}return L;};F.addMethod=function(K,M,L){if(K&&M&&typeof M==="function"){F.prototype[K]=function(){L=L||this;var O=C.Array(arguments),N;if(O[0]&&O[0] instanceof F){O[0]=O[0]._node;}if(O[1]&&O[1] instanceof F){O[1]=O[1]._node;}O.unshift(this._node);N=F.scrubVal(M.apply(L,O),this);return N;};}else{}};F.importMethod=function(M,K,L){if(typeof K==="string"){L=L||K;F.addMethod(L,M[K],M);}else{C.each(K,function(N){F.importMethod(M,N);});}};F.one=function(N){var K=null,M,L;if(N){if(typeof N==="string"){if(N.indexOf("doc")===0){N=C.config.doc;}else{if(N.indexOf("win")===0){N=C.config.win;}else{N=C.Selector.query(N,null,true);}}if(!N){return null;}}else{if(N instanceof F){return N;}}L=N._yuid;K=F._instances[L];M=K?K._node:null;if(!K||(M&&N!==M)){K=new F(N);}}return K;};F.get=function(){return F.one.apply(F,arguments);};F.create=function(){return F.get(C.DOM.create.apply(C.DOM,arguments));};F.ATTRS={text:{getter:function(){return C.DOM.getText(this._node);},setter:function(K){C.DOM.setText(this._node,K);return K;}},"options":{getter:function(){return this._node.getElementsByTagName("option");}},"elements":{getter:function(){return C.all(this._node.elements);}},"children":{getter:function(){var N=this._node,M=N.children,O,L,K;if(!M){O=N.childNodes;M=[];for(L=0,K=O.length;L<K;++L){if(O[L][H]){M[M.length]=O[L];}}}return C.all(M);}},value:{getter:function(){return C.DOM.getValue(this._node);},setter:function(K){C.DOM.setValue(this._node,K);return K;}},data:{getter:function(){return this._data;},setter:function(K){this._data=K;return K;}}};F.DEFAULT_SETTER=function(K,M){var L=this._stateProxy,N;if(K.indexOf(G)>-1){N=K;K=K.split(G);C.Object.setValue(L,K,M);}else{if(L[K]!==undefined){L[K]=M;}}return M;};F.DEFAULT_GETTER=function(K){var L=this._stateProxy,M;if(K.indexOf&&K.indexOf(G)>-1){M=C.Object.getValue(L,K.split(G));}else{if(L[K]!==undefined){M=L[K];}}return M;};C.augment(F,C.Event.Target);C.mix(F.prototype,{toString:function(){var M="",L=this[D]+": not bound to a node",K=this._node;if(K){M+=K[E];if(K.id){M+="#"+K.id;}if(K.className){M+="."+K.className.replace(" ",".");}M+=" "+this[D];}return M||L;},get:function(K){var L;if(this._getAttr){L=this._getAttr(K);}else{L=this._get(K);}if(L){L=C.Node.scrubVal(L,this);}return L;},_get:function(K){var L=F.ATTRS[K],M;if(L&&L.getter){M=L.getter.call(this);}else{if(F.re_aria.test(K)){M=this._node.getAttribute(K,2);}else{M=F.DEFAULT_GETTER.apply(this,arguments);}}return M;},set:function(K,M){var L=F.ATTRS[K];if(this._setAttr){this._setAttr.apply(this,arguments);}else{if(L&&L.setter){L.setter.call(this,M);}else{if(F.re_aria.test(K)){this._node.setAttribute(K,M);}else{F.DEFAULT_SETTER.apply(this,arguments);}}}return this;},setAttrs:function(K){if(this._setAttrs){this._setAttrs(K);}else{C.Object.each(K,function(L,M){this.set(M,L);},this);}return this;},getAttrs:function(L){var K={};if(this._getAttrs){this._getAttrs(L);}else{C.Array.each(L,function(M,N){K[M]=this.get(M);},this);}return K;},create:F.create,compareTo:function(K){var L=this._node;if(K instanceof C.Node){K=K._node;}return L===K;},inDoc:function(L){var K=this._node;L=(L)?L._node||L:K[B];if(L.documentElement){return C.DOM.contains(L.documentElement,K);}},getById:function(M){var L=this._node,K=C.DOM.byId(M,L[B]);if(K&&C.DOM.contains(L,K)){K=C.one(K);}else{K=null;}return K;},ancestor:function(K){return F.get(C.DOM.elementByAxis(this._node,"parentNode",J(K)));},previous:function(L,K){return F.get(C.DOM.elementByAxis(this._node,"previousSibling",J(L),K));},next:function(M,L,K){return F.get(C.DOM.elementByAxis(this._node,"nextSibling",J(L),K));},one:function(K){return C.one(C.Selector.query(K,this._node,true));},query:function(K){return this.one(K);},all:function(K){var L=C.all(C.Selector.query(K,this._node));L._query=K;return L;},queryAll:function(K){return this.all(K);},test:function(K){return C.Selector.test(this._node,K);},remove:function(K){var L=this._node;L.parentNode.removeChild(L);if(K){this.destroy(true);}return this;},replace:function(K){var L=this._node;L.parentNode.replaceChild(K,L);return this;},purge:function(L,K){C.Event.purgeElement(this._node,L,K);},destroy:function(K){delete F._instances[this[D]];if(K){this.purge(true);}if(this.unplug){this.unplug();}this._node._yuid=null;this._node=null;this._stateProxy=null;},invoke:function(R,L,K,Q,P,O){var N=this._node,M;if(L&&L instanceof C.Node){L=L._node;}if(K&&K instanceof C.Node){K=K._node;}M=N[R](L,K,Q,P,O);return C.Node.scrubVal(M,this);},each:function(L,K){K=K||this;return L.call(K,this);},item:function(K){return this;},size:function(){return this._node?1:0;},insert:function(M,K){var L=this._node;
+if(M){if(typeof K==="number"){K=this._node.childNodes[K];}if(typeof M!=="string"){if(M._node){M=M._node;}else{if(M._nodes||(!M.nodeType&&M.length)){C.each(M._nodes,function(N){C.DOM.addHTML(L,N,K);});return this;}}}C.DOM.addHTML(L,M,K);}return this;},prepend:function(K){return this.insert(K,0);},append:function(K){return this.insert(K,null);},setContent:function(K){C.DOM.addHTML(this._node,K,"replace");return this;},hasMethod:function(L){var K=this._node;return(K&&(typeof K==="function"));}},true);C.Node=F;C.get=C.Node.get;C.one=C.Node.one;var A=function(K){if(typeof K==="string"){this._query=K;K=C.Selector.query(K);}else{K=C.Array(K,0,true);}A._instances[C.stamp(this)]=this;this._nodes=K;};A.NAME="NodeList";A.getDOMNodes=function(K){return K._nodes;};A._instances=[];A.each=function(K,N,M){var L=K._nodes;if(L&&L.length){C.Array.each(L,N,M||K);}else{}};A.addMethod=function(K,M,L){if(K&&M){A.prototype[K]=function(){var O=[],N=arguments;C.Array.each(this._nodes,function(T){var S="_yuid",Q=C.Node._instances[T[S]],R,P;if(!Q){Q=A._getTempNode(T);}R=L||Q;P=M.apply(R,N);if(P!==undefined&&P!==Q){O[O.length]=P;}});return O.length?O:this;};}else{}};A.importMethod=function(M,K,L){if(typeof K==="string"){L=L||K;A.addMethod(K,M[K]);}else{C.each(K,function(N){A.importMethod(M,N);});}};A._getTempNode=function(L){var K=A._tempNode;if(!K){K=C.Node.create("<div></div>");A._tempNode=K;}K._node=L;K._stateProxy=L;return K;};C.mix(A.prototype,{item:function(K){return C.one((this._nodes||[])[K]);},each:function(M,L){var K=this;C.Array.each(this._nodes,function(O,N){O=C.one(O);return M.call(L||O,O,N,K);});return K;},batch:function(L,K){var M=this;C.Array.each(this._nodes,function(P,O){var N=C.Node._instances[P[D]];if(!N){N=A._getTempNode(P);}return L.call(K||N,N,O,M);});return M;},some:function(M,L){var K=this;return C.Array.some(this._nodes,function(O,N){O=C.one(O);L=L||O;return M.call(L,O,N,K);});},toFrag:function(){return C.one(C.DOM._nl2frag(this._nodes));},indexOf:function(K){return C.Array.indexOf(this._nodes,C.Node.getDOMNode(K));},filter:function(K){return C.all(C.Selector.filter(this._nodes,K));},modulus:function(M,L){L=L||0;var K=[];A.each(this,function(O,N){if(N%M===L){K.push(O);}});return C.all(K);},odd:function(){return this.modulus(2,1);},even:function(){return this.modulus(2);},destructor:function(){delete A._instances[this[D]];},refresh:function(){var L,K=this._nodes;if(this._query){if(K&&K[0]&&K[0].ownerDocument){L=K[0].ownerDocument;}this._nodes=C.Selector.query(this._query,L||C.config.doc);}return this;},on:function(N,M,L){var K=C.Array(arguments,0,true);K.splice(2,0,this._nodes);K[3]=L||this;return C.on.apply(C,K);},after:function(N,M,L){var K=C.Array(arguments,0,true);K.splice(2,0,this._nodes);K[3]=L||this;return C.after.apply(C,K);},size:function(){return this._nodes.length;},toString:function(){var N="",M=this[D]+": not bound to any nodes",K=this._nodes,L;if(K&&K[0]){L=K[0];N+=L[E];if(L.id){N+="#"+L.id;}if(L.className){N+="."+L.className.replace(" ",".");}if(K.length>1){N+="...["+K.length+" items]";}}return N||M;}},true);A.importMethod(C.Node.prototype,["append","detach","detachAll","insert","prepend","remove","set","setContent"]);A.prototype.get=function(L){var O=[],N=this._nodes,M=false,P=A._getTempNode,K,Q;if(N[0]){K=C.Node._instances[N[0]._yuid]||P(N[0]);Q=K._get(L);if(Q&&Q.nodeType){M=true;}}C.Array.each(N,function(R){K=C.Node._instances[R._yuid];if(!K){K=P(R);}Q=K._get(L);if(!M){Q=C.Node.scrubVal(Q,K);}O.push(Q);});return(M)?C.all(O):O;};C.NodeList=A;C.all=function(K){return new A(K);};C.Node.all=C.all;C.Array.each(["replaceChild","appendChild","insertBefore","removeChild","hasChildNodes","cloneNode","hasAttribute","removeAttribute","scrollIntoView","getElementsByTagName","focus","blur","submit","reset","select"],function(K){C.Node.prototype[K]=function(O,M,L){var N=this.invoke(K,O,M,L);return N;};});F.importMethod(C.DOM,["contains","setAttribute","getAttribute"]);C.NodeList.importMethod(C.Node.prototype,["getAttribute","setAttribute"]);(function(L){var K=["hasClass","addClass","removeClass","replaceClass","toggleClass"];L.Node.importMethod(L.DOM,K);L.NodeList.importMethod(L.Node.prototype,K);})(C);if(!document.documentElement.hasAttribute){C.Node.prototype.hasAttribute=function(K){return C.DOM.getAttribute(this._node,K)!=="";};}C.Node.ATTRS.type={setter:function(L){if(L==="hidden"){try{this._node.type="hidden";}catch(K){this.setStyle("display","none");this._inputType="hidden";}}else{try{this._node.type=L;}catch(K){}}return L;},getter:function(){return this._inputType||this._node.type;},_bypassProxy:true};},"3.0.0",{requires:["dom-base","selector-css2","event-base"]});YUI.add("node-style",function(A){(function(C){var B=["getStyle","getComputedStyle","setStyle","setStyles"];C.Node.importMethod(C.DOM,B);C.NodeList.importMethod(C.Node.prototype,B);})(A);},"3.0.0",{requires:["dom-style","node-base"]});YUI.add("node-screen",function(A){A.each(["winWidth","winHeight","docWidth","docHeight","docScrollX","docScrollY"],function(B){A.Node.ATTRS[B]={getter:function(){var C=Array.prototype.slice.call(arguments);C.unshift(A.Node.getDOMNode(this));return A.DOM[B].apply(this,C);}};});A.Node.ATTRS.scrollLeft={getter:function(){var B=A.Node.getDOMNode(this);return("scrollLeft" in B)?B.scrollLeft:A.DOM.docScrollX(B);},setter:function(C){var B=A.Node.getDOMNode(this);if(B){if("scrollLeft" in B){B.scrollLeft=C;}else{if(B.document||B.nodeType===9){A.DOM._getWin(B).scrollTo(C,A.DOM.docScrollY(B));}}}else{}}};A.Node.ATTRS.scrollTop={getter:function(){var B=A.Node.getDOMNode(this);return("scrollTop" in B)?B.scrollTop:A.DOM.docScrollY(B);},setter:function(C){var B=A.Node.getDOMNode(this);if(B){if("scrollTop" in B){B.scrollTop=C;}else{if(B.document||B.nodeType===9){A.DOM._getWin(B).scrollTo(A.DOM.docScrollX(B),C);}}}else{}}};A.Node.importMethod(A.DOM,["getXY","setXY","getX","setX","getY","setY"]);A.Node.ATTRS.region={getter:function(){var B=A.Node.getDOMNode(this);if(B&&!B.tagName){if(B.nodeType===9){B=B.documentElement;
+}else{if(B.alert){B=B.document.documentElement;}}}return A.DOM.region(B);}};A.Node.ATTRS.viewportRegion={getter:function(){return A.DOM.viewportRegion(A.Node.getDOMNode(this));}};A.Node.importMethod(A.DOM,"inViewportRegion");A.Node.prototype.intersect=function(B,D){var C=A.Node.getDOMNode(this);if(B instanceof A.Node){B=A.Node.getDOMNode(B);}return A.DOM.intersect(C,B,D);};A.Node.prototype.inRegion=function(B,D,E){var C=A.Node.getDOMNode(this);if(B instanceof A.Node){B=A.Node.getDOMNode(B);}return A.DOM.inRegion(C,B,D,E);};},"3.0.0",{requires:["dom-screen"]});YUI.add("node-pluginhost",function(A){A.Node.plug=function(){var B=A.Array(arguments);B.unshift(A.Node);A.Plugin.Host.plug.apply(A.Base,B);return A.Node;};A.Node.unplug=function(){var B=A.Array(arguments);B.unshift(A.Node);A.Plugin.Host.unplug.apply(A.Base,B);return A.Node;};A.mix(A.Node,A.Plugin.Host,false,null,1);A.NodeList.prototype.plug=function(){var B=arguments;A.NodeList.each(this,function(C){A.Node.prototype.plug.apply(A.one(C),B);});};A.NodeList.prototype.unplug=function(){var B=arguments;A.NodeList.each(this,function(C){A.Node.prototype.unplug.apply(A.one(C),B);});};},"3.0.0",{requires:["node-base","pluginhost"]});YUI.add("node-event-delegate",function(A){A.Node.prototype.delegate=function(F,E,B){var D=Array.prototype.slice.call(arguments,3),C=[F,E,A.Node.getDOMNode(this),B];C=C.concat(D);return A.delegate.apply(A,C);};},"3.0.0",{requires:["node-base","event-delegate","pluginhost"]});YUI.add("node",function(A){},"3.0.0",{skinnable:false,use:["node-base","node-style","node-screen","node-pluginhost","node-event-delegate"],requires:["dom","event-base","event-delegate","pluginhost"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-pluginhost', function(Y) {
+
+/**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of Node by default).
+ *
+ * @method Node.plug
+ * @static
+ *
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+Y.Node.plug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.plug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+/**
+ * Unregisters any class level plugins which have been registered by the Node
+ *
+ * @method Node.unplug
+ * @static
+ *
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+Y.Node.unplug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.unplug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
+
+// allow batching of plug/unplug via NodeList
+// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
+Y.NodeList.prototype.plug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.plug.apply(Y.one(node), args);
+ });
+};
+
+Y.NodeList.prototype.unplug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.unplug.apply(Y.one(node), args);
+ });
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'pluginhost']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-pluginhost",function(A){A.Node.plug=function(){var B=A.Array(arguments);B.unshift(A.Node);A.Plugin.Host.plug.apply(A.Base,B);return A.Node;};A.Node.unplug=function(){var B=A.Array(arguments);B.unshift(A.Node);A.Plugin.Host.unplug.apply(A.Base,B);return A.Node;};A.mix(A.Node,A.Plugin.Host,false,null,1);A.NodeList.prototype.plug=function(){var B=arguments;A.NodeList.each(this,function(C){A.Node.prototype.plug.apply(A.one(C),B);});};A.NodeList.prototype.unplug=function(){var B=arguments;A.NodeList.each(this,function(C){A.Node.prototype.unplug.apply(A.one(C),B);});};},"3.0.0",{requires:["node-base","pluginhost"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-pluginhost', function(Y) {
+
+/**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of Node by default).
+ *
+ * @method Node.plug
+ * @static
+ *
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+Y.Node.plug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.plug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+/**
+ * Unregisters any class level plugins which have been registered by the Node
+ *
+ * @method Node.unplug
+ * @static
+ *
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+Y.Node.unplug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.unplug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
+
+// allow batching of plug/unplug via NodeList
+// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
+Y.NodeList.prototype.plug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.plug.apply(Y.one(node), args);
+ });
+};
+
+Y.NodeList.prototype.unplug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.unplug.apply(Y.one(node), args);
+ });
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'pluginhost']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-screen', function(Y) {
+
+/**
+ * Extended Node interface for managing regions and screen positioning.
+ * Adds support for positioning elements and normalizes window size and scroll detection.
+ * @module node
+ * @submodule node-screen
+ */
+
+// these are all "safe" returns, no wrapping required
+Y.each([
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @config winWidth
+ * @for Node
+ * @type {Int}
+ */
+ 'winWidth',
+
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @config winHeight
+ * @type {Int}
+ */
+ 'winHeight',
+
+ /**
+ * Document width
+ * @config winHeight
+ * @type {Int}
+ */
+ 'docWidth',
+
+ /**
+ * Document height
+ * @config docHeight
+ * @type {Int}
+ */
+ 'docHeight',
+
+ /**
+ * Amount page has been scroll vertically
+ * @config docScrollX
+ * @type {Int}
+ */
+ 'docScrollX',
+
+ /**
+ * Amount page has been scroll horizontally
+ * @config docScrollY
+ * @type {Int}
+ */
+ 'docScrollY'
+ ],
+ function(name) {
+ Y.Node.ATTRS[name] = {
+ getter: function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(Y.Node.getDOMNode(this));
+
+ return Y.DOM[name].apply(this, args);
+ }
+ };
+ }
+);
+
+Y.Node.ATTRS.scrollLeft = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollLeft' in node) {
+ node.scrollLeft = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
+ }
+ } else {
+ Y.log('unable to set scrollLeft for ' + node, 'error', 'Node');
+ }
+ }
+};
+
+Y.Node.ATTRS.scrollTop = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollTop' in node) {
+ node.scrollTop = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
+ }
+ } else {
+ Y.log('unable to set scrollTop for ' + node, 'error', 'Node');
+ }
+ }
+};
+
+Y.Node.importMethod(Y.DOM, [
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getXY
+ * @for Node
+ * @return {Array} The XY position of the node
+*/
+ 'getXY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setXY
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setXY',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getX
+ * @return {Int} The X position of the node
+*/
+ 'getX',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setX
+ * @param {Int} x X value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setX',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getY
+ * @return {Int} The Y position of the node
+*/
+ 'getY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setY
+ * @param {Int} y Y value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setY'
+]);
+
+/**
+ * Returns a region object for the node
+ * @config region
+ * @for Node
+ * @type Node
+ */
+Y.Node.ATTRS.region = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ if (node && !node.tagName) {
+ if (node.nodeType === 9) { // document
+ node = node.documentElement;
+ } else if (node.alert) { // window
+ node = node.document.documentElement;
+ }
+ }
+ return Y.DOM.region(node);
+ }
+};
+
+/**
+ * Returns a region object for the node's viewport
+ * @config viewportRegion
+ * @type Node
+ */
+Y.Node.ATTRS.viewportRegion = {
+ getter: function() {
+ return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
+ }
+};
+
+Y.Node.importMethod(Y.DOM, 'inViewportRegion');
+
+// these need special treatment to extract 2nd node arg
+/**
+ * Compares the intersection of the node with another node or region
+ * @method intersect
+ * @for Node
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.intersect = function(node2, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.intersect(node1, node2, altRegion);
+};
+
+/**
+ * Determines whether or not the node is within the giving region.
+ * @method inRegion
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Boolean} all Whether or not all of the node must be in the region.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.inRegion = function(node2, all, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.inRegion(node1, node2, all, altRegion);
+};
+
+
+}, '3.0.0' ,{requires:['dom-screen']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-screen",function(A){A.each(["winWidth","winHeight","docWidth","docHeight","docScrollX","docScrollY"],function(B){A.Node.ATTRS[B]={getter:function(){var C=Array.prototype.slice.call(arguments);C.unshift(A.Node.getDOMNode(this));return A.DOM[B].apply(this,C);}};});A.Node.ATTRS.scrollLeft={getter:function(){var B=A.Node.getDOMNode(this);return("scrollLeft" in B)?B.scrollLeft:A.DOM.docScrollX(B);},setter:function(C){var B=A.Node.getDOMNode(this);if(B){if("scrollLeft" in B){B.scrollLeft=C;}else{if(B.document||B.nodeType===9){A.DOM._getWin(B).scrollTo(C,A.DOM.docScrollY(B));}}}else{}}};A.Node.ATTRS.scrollTop={getter:function(){var B=A.Node.getDOMNode(this);return("scrollTop" in B)?B.scrollTop:A.DOM.docScrollY(B);},setter:function(C){var B=A.Node.getDOMNode(this);if(B){if("scrollTop" in B){B.scrollTop=C;}else{if(B.document||B.nodeType===9){A.DOM._getWin(B).scrollTo(A.DOM.docScrollX(B),C);}}}else{}}};A.Node.importMethod(A.DOM,["getXY","setXY","getX","setX","getY","setY"]);A.Node.ATTRS.region={getter:function(){var B=A.Node.getDOMNode(this);if(B&&!B.tagName){if(B.nodeType===9){B=B.documentElement;}else{if(B.alert){B=B.document.documentElement;}}}return A.DOM.region(B);}};A.Node.ATTRS.viewportRegion={getter:function(){return A.DOM.viewportRegion(A.Node.getDOMNode(this));}};A.Node.importMethod(A.DOM,"inViewportRegion");A.Node.prototype.intersect=function(B,D){var C=A.Node.getDOMNode(this);if(B instanceof A.Node){B=A.Node.getDOMNode(B);}return A.DOM.intersect(C,B,D);};A.Node.prototype.inRegion=function(B,D,E){var C=A.Node.getDOMNode(this);if(B instanceof A.Node){B=A.Node.getDOMNode(B);}return A.DOM.inRegion(C,B,D,E);};},"3.0.0",{requires:["dom-screen"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-screen', function(Y) {
+
+/**
+ * Extended Node interface for managing regions and screen positioning.
+ * Adds support for positioning elements and normalizes window size and scroll detection.
+ * @module node
+ * @submodule node-screen
+ */
+
+// these are all "safe" returns, no wrapping required
+Y.each([
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @config winWidth
+ * @for Node
+ * @type {Int}
+ */
+ 'winWidth',
+
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @config winHeight
+ * @type {Int}
+ */
+ 'winHeight',
+
+ /**
+ * Document width
+ * @config winHeight
+ * @type {Int}
+ */
+ 'docWidth',
+
+ /**
+ * Document height
+ * @config docHeight
+ * @type {Int}
+ */
+ 'docHeight',
+
+ /**
+ * Amount page has been scroll vertically
+ * @config docScrollX
+ * @type {Int}
+ */
+ 'docScrollX',
+
+ /**
+ * Amount page has been scroll horizontally
+ * @config docScrollY
+ * @type {Int}
+ */
+ 'docScrollY'
+ ],
+ function(name) {
+ Y.Node.ATTRS[name] = {
+ getter: function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(Y.Node.getDOMNode(this));
+
+ return Y.DOM[name].apply(this, args);
+ }
+ };
+ }
+);
+
+Y.Node.ATTRS.scrollLeft = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollLeft' in node) {
+ node.scrollLeft = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
+ }
+ } else {
+ }
+ }
+};
+
+Y.Node.ATTRS.scrollTop = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollTop' in node) {
+ node.scrollTop = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
+ }
+ } else {
+ }
+ }
+};
+
+Y.Node.importMethod(Y.DOM, [
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getXY
+ * @for Node
+ * @return {Array} The XY position of the node
+*/
+ 'getXY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setXY
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setXY',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getX
+ * @return {Int} The X position of the node
+*/
+ 'getX',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setX
+ * @param {Int} x X value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setX',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getY
+ * @return {Int} The Y position of the node
+*/
+ 'getY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setY
+ * @param {Int} y Y value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setY'
+]);
+
+/**
+ * Returns a region object for the node
+ * @config region
+ * @for Node
+ * @type Node
+ */
+Y.Node.ATTRS.region = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ if (node && !node.tagName) {
+ if (node.nodeType === 9) { // document
+ node = node.documentElement;
+ } else if (node.alert) { // window
+ node = node.document.documentElement;
+ }
+ }
+ return Y.DOM.region(node);
+ }
+};
+
+/**
+ * Returns a region object for the node's viewport
+ * @config viewportRegion
+ * @type Node
+ */
+Y.Node.ATTRS.viewportRegion = {
+ getter: function() {
+ return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
+ }
+};
+
+Y.Node.importMethod(Y.DOM, 'inViewportRegion');
+
+// these need special treatment to extract 2nd node arg
+/**
+ * Compares the intersection of the node with another node or region
+ * @method intersect
+ * @for Node
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.intersect = function(node2, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.intersect(node1, node2, altRegion);
+};
+
+/**
+ * Determines whether or not the node is within the giving region.
+ * @method inRegion
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Boolean} all Whether or not all of the node must be in the region.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.inRegion = function(node2, all, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.inRegion(node1, node2, all, altRegion);
+};
+
+
+}, '3.0.0' ,{requires:['dom-screen']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-style', function(Y) {
+
+(function(Y) {
+/**
+ * Extended Node interface for managing node styles.
+ * @module node
+ * @submodule node-style
+ */
+
+var methods = [
+ /**
+ * Returns the style's current value.
+ * @method getStyle
+ * @for Node
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The current value of the style property for the element.
+ */
+ 'getStyle',
+
+ /**
+ * Returns the computed value for the given style property.
+ * @method getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The computed value of the style property for the element.
+ */
+ 'getComputedStyle',
+
+ /**
+ * Sets a style property of the node.
+ * @method setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+ 'setStyle',
+
+ /**
+ * Sets multiple style properties on the node.
+ * @method setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+ 'setStyles'
+];
+Y.Node.importMethod(Y.DOM, methods);
+/**
+ * Returns an array of values for each node.
+ * @method getStyle
+ * @for NodeList
+ * @see Node.getStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The current values of the style property for the element.
+ */
+
+/**
+ * Returns an array of the computed value for each node.
+ * @method getComputedStyle
+ * @see Node.getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The computed values for each node.
+ */
+
+/**
+ * Sets a style property on each node.
+ * @method setStyle
+ * @see Node.setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+
+/**
+ * Sets multiple style properties on each node.
+ * @method setStyles
+ * @see Node.setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-style', 'node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("node-style",function(A){(function(C){var B=["getStyle","getComputedStyle","setStyle","setStyles"];C.Node.importMethod(C.DOM,B);C.NodeList.importMethod(C.Node.prototype,B);})(A);},"3.0.0",{requires:["dom-style","node-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-style', function(Y) {
+
+(function(Y) {
+/**
+ * Extended Node interface for managing node styles.
+ * @module node
+ * @submodule node-style
+ */
+
+var methods = [
+ /**
+ * Returns the style's current value.
+ * @method getStyle
+ * @for Node
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The current value of the style property for the element.
+ */
+ 'getStyle',
+
+ /**
+ * Returns the computed value for the given style property.
+ * @method getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The computed value of the style property for the element.
+ */
+ 'getComputedStyle',
+
+ /**
+ * Sets a style property of the node.
+ * @method setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+ 'setStyle',
+
+ /**
+ * Sets multiple style properties on the node.
+ * @method setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+ 'setStyles'
+];
+Y.Node.importMethod(Y.DOM, methods);
+/**
+ * Returns an array of values for each node.
+ * @method getStyle
+ * @for NodeList
+ * @see Node.getStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The current values of the style property for the element.
+ */
+
+/**
+ * Returns an array of the computed value for each node.
+ * @method getComputedStyle
+ * @see Node.getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The computed values for each node.
+ */
+
+/**
+ * Sets a style property on each node.
+ * @method setStyle
+ * @see Node.setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+
+/**
+ * Sets multiple style properties on each node.
+ * @method setStyles
+ * @see Node.setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-style', 'node-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('node-base', function(Y) {
+
+/**
+ * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
+ * @module node
+ * @submodule node-base
+ */
+
+/**
+ * The Node class provides a wrapper for manipulating DOM Nodes.
+ * Node properties can be accessed via the set/get methods.
+ * Use Y.get() to retrieve Node instances.
+ *
+ * <strong>NOTE:</strong> Node properties are accessed using
+ * the <code>set</code> and <code>get</code> methods.
+ *
+ * @class Node
+ * @constructor
+ * @for Node
+ */
+
+// "globals"
+var DOT = '.',
+ NODE_NAME = 'nodeName',
+ NODE_TYPE = 'nodeType',
+ OWNER_DOCUMENT = 'ownerDocument',
+ TAG_NAME = 'tagName',
+ UID = '_yuid',
+
+ Node = function(node) {
+ var uid = node[UID];
+
+ if (uid && Node._instances[uid] && Node._instances[uid]._node !== node) {
+ node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
+ }
+
+ uid = Y.stamp(node);
+ if (!uid) { // stamp failed; likely IE non-HTMLElement
+ uid = Y.guid();
+ }
+
+ this[UID] = uid;
+
+ this._node = node;
+ Node._instances[uid] = this;
+
+ this._stateProxy = node; // when augmented with Attribute
+
+ if (this._initPlugins) { // when augmented with Plugin.Host
+ this._initPlugins();
+ }
+ },
+
+ // used with previous/next/ancestor tests
+ _wrapFn = function(fn) {
+ var ret = null;
+ if (fn) {
+ ret = (typeof fn === 'string') ?
+ function(n) {
+ return Y.Selector.test(n, fn);
+ } :
+ function(n) {
+ return fn(Node.get(n));
+ };
+ }
+
+ return ret;
+ };
+// end "globals"
+
+Node.NAME = 'Node';
+
+Node.re_aria = /^(?:role$|aria-)/;
+
+Node.DOM_EVENTS = {
+ abort: true,
+ beforeunload: true,
+ blur: true,
+ change: true,
+ click: true,
+ close: true,
+ command: true,
+ contextmenu: true,
+ drag: true,
+ dragstart: true,
+ dragenter: true,
+ dragover: true,
+ dragleave: true,
+ dragend: true,
+ drop: true,
+ dblclick: true,
+ error: true,
+ focus: true,
+ keydown: true,
+ keypress: true,
+ keyup: true,
+ load: true,
+ message: true,
+ mousedown: true,
+ mousemove: true,
+ mouseout: true,
+ mouseover: true,
+ mouseup: true,
+ mousemultiwheel: true,
+ mousewheel: true,
+ submit: true,
+ mouseenter: true,
+ mouseleave: true,
+ scroll: true,
+ reset: true,
+ resize: true,
+ select: true,
+ textInput: true,
+ unload: true
+};
+
+// Add custom event adaptors to this list. This will make it so
+// that delegate, key, available, contentready, etc all will
+// be available through Node.on
+Y.mix(Node.DOM_EVENTS, Y.Env.evt.plugins);
+
+Node._instances = {};
+
+/**
+ * Retrieves the DOM node bound to a Node instance
+ * @method Node.getDOMNode
+ * @static
+ *
+ * @param {Y.Node || HTMLNode} node The Node instance or an HTMLNode
+ * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
+ * as the node argument, it is simply returned.
+ */
+Node.getDOMNode = function(node) {
+ if (node) {
+ return (node.nodeType) ? node : node._node || null;
+ }
+ return null;
+};
+
+Node.scrubVal = function(val, node) {
+ if (node && val) { // only truthy values are risky
+ if (typeof val === 'object' || typeof val === 'function') { // safari nodeList === function
+ if (NODE_TYPE in val || Y.DOM.isWindow(val)) {// node || window
+ val = Node.get(val);
+ } else if ((val.item && !val._nodes) || // dom collection or Node instance
+ (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
+ val = Y.all(val);
+ }
+ }
+ } else if (val === undefined) {
+ val = node; // for chaining
+ }
+
+ return val;
+};
+
+Node.addMethod = function(name, fn, context) {
+ if (name && fn && typeof fn === 'function') {
+ Node.prototype[name] = function() {
+ context = context || this;
+ var args = Y.Array(arguments),
+ ret;
+
+ if (args[0] && args[0] instanceof Node) {
+ args[0] = args[0]._node;
+ }
+
+ if (args[1] && args[1] instanceof Node) {
+ args[1] = args[1]._node;
+ }
+ args.unshift(this._node);
+ ret = Node.scrubVal(fn.apply(context, args), this);
+ return ret;
+ };
+ } else {
+ }
+};
+
+Node.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ Node.addMethod(altName, host[name], host);
+ } else {
+ Y.each(name, function(n) {
+ Node.importMethod(host, n);
+ });
+ }
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.one = function(node) {
+ var instance = null,
+ cachedNode,
+ uid;
+
+ if (node) {
+ if (typeof node === 'string') {
+ if (node.indexOf('doc') === 0) { // doc OR document
+ node = Y.config.doc;
+ } else if (node.indexOf('win') === 0) { // win OR window
+ node = Y.config.win;
+ } else {
+ node = Y.Selector.query(node, null, true);
+ }
+ if (!node) {
+ return null;
+ }
+ } else if (node instanceof Node) {
+ return node; // NOTE: return
+ }
+
+ uid = node._yuid;
+ instance = Node._instances[uid]; // reuse exising instances
+ cachedNode = instance ? instance._node : null;
+ if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
+ instance = new Node(node);
+ }
+ }
+ return instance;
+};
+
+/**
+ * Returns a single Node instance bound to the node or the
+ * first element matching the given selector.
+ * @method Y.get
+ * @deprecated Use Y.one
+ * @static
+ * @param {String | HTMLElement} node a node or Selector
+ * @param {Y.Node || HTMLElement} doc an optional document to scan. Defaults to Y.config.doc.
+ */
+Node.get = function() {
+ return Node.one.apply(Node, arguments);
+};
+
+/**
+ * Creates a new dom node using the provided markup string.
+ * @method create
+ * @static
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+Node.create = function() {
+ return Node.get(Y.DOM.create.apply(Y.DOM, arguments));
+};
+
+Node.ATTRS = {
+ /**
+ * Allows for getting and setting the text of an element.
+ * Formatting is preserved and special characters are treated literally.
+ * @config text
+ * @type String
+ */
+ text: {
+ getter: function() {
+ return Y.DOM.getText(this._node);
+ },
+
+ setter: function(content) {
+ Y.DOM.setText(this._node, content);
+ return content;
+ }
+ },
+
+ 'options': {
+ getter: function() {
+ return this._node.getElementsByTagName('option');
+ }
+ },
+
+ // IE: elements collection is also FORM node which trips up scrubVal.
+ // preconverting to NodeList
+ // TODO: break out for IE only
+ 'elements': {
+ getter: function() {
+ return Y.all(this._node.elements);
+ }
+ },
+
+ /**
+ * Returns a NodeList instance of all HTMLElement children.
+ * @readOnly
+ * @config children
+ * @type NodeList
+ */
+ 'children': {
+ getter: function() {
+ var node = this._node,
+ children = node.children,
+ childNodes, i, len;
+
+ if (!children) {
+ childNodes = node.childNodes;
+ children = [];
+
+ for (i = 0, len = childNodes.length; i < len; ++i) {
+ if (childNodes[i][TAG_NAME]) {
+ children[children.length] = childNodes[i];
+ }
+ }
+ }
+ return Y.all(children);
+ }
+ },
+
+ value: {
+ getter: function() {
+ return Y.DOM.getValue(this._node);
+ },
+
+ setter: function(val) {
+ Y.DOM.setValue(this._node, val);
+ return val;
+ }
+ },
+
+ data: {
+ getter: function() {
+ return this._data;
+ },
+
+ setter: function(val) {
+ this._data = val;
+ return val;
+ }
+ }
+};
+
+// call with instance context
+Node.DEFAULT_SETTER = function(name, val) {
+ var node = this._stateProxy,
+ strPath;
+
+ if (name.indexOf(DOT) > -1) {
+ strPath = name;
+ name = name.split(DOT);
+ // only allow when defined on node
+ Y.Object.setValue(node, name, val);
+ } else if (node[name] !== undefined) { // pass thru DOM properties
+ node[name] = val;
+ }
+
+ return val;
+};
+
+// call with instance context
+Node.DEFAULT_GETTER = function(name) {
+ var node = this._stateProxy,
+ val;
+
+ if (name.indexOf && name.indexOf(DOT) > -1) {
+ val = Y.Object.getValue(node, name.split(DOT));
+ } else if (node[name] !== undefined) { // pass thru from DOM
+ val = node[name];
+ }
+
+ return val;
+};
+
+Y.augment(Node, Y.Event.Target);
+
+Y.mix(Node.prototype, {
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to a node',
+ node = this._node;
+
+ if (node) {
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ // TODO: add yuid?
+ str += ' ' + this[UID];
+ }
+ return str || errorMsg;
+ },
+
+ /**
+ * Returns an attribute value on the Node instance
+ * @method get
+ * @param {String} attr The attribute to be set
+ * @return {any} The current value of the attribute
+ */
+ get: function(attr) {
+ var val;
+
+ if (this._getAttr) { // use Attribute imple
+ val = this._getAttr(attr);
+ } else {
+ val = this._get(attr);
+ }
+
+ if (val) {
+ val = Y.Node.scrubVal(val, this);
+ }
+ return val;
+ },
+
+ _get: function(attr) {
+ var attrConfig = Node.ATTRS[attr],
+ val;
+
+ if (attrConfig && attrConfig.getter) {
+ val = attrConfig.getter.call(this);
+ } else if (Node.re_aria.test(attr)) {
+ val = this._node.getAttribute(attr, 2);
+ } else {
+ val = Node.DEFAULT_GETTER.apply(this, arguments);
+ }
+
+ return val;
+ },
+
+ /**
+ * Sets an attribute on the Node instance.
+ * @method set
+ * @param {String} attr The attribute to be set.
+ * @param {any} val The value to set the attribute to.
+ * @chainable
+ */
+ set: function(attr, val) {
+ var attrConfig = Node.ATTRS[attr];
+
+ if (this._setAttr) { // use Attribute imple
+ this._setAttr.apply(this, arguments);
+ } else { // use setters inline
+ if (attrConfig && attrConfig.setter) {
+ attrConfig.setter.call(this, val);
+ } else if (Node.re_aria.test(attr)) { // special case Aria
+ this._node.setAttribute(attr, val);
+ } else {
+ Node.DEFAULT_SETTER.apply(this, arguments);
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Sets multiple attributes.
+ * @method setAttrs
+ * @param {Object} attrMap an object of name/value pairs to set
+ * @chainable
+ */
+ setAttrs: function(attrMap) {
+ if (this._setAttrs) { // use Attribute imple
+ this._setAttrs(attrMap);
+ } else { // use setters inline
+ Y.Object.each(attrMap, function(v, n) {
+ this.set(n, v);
+ }, this);
+ }
+
+ return this;
+ },
+
+ /**
+ * Returns an object containing the values for the requested attributes.
+ * @method getAttrs
+ * @param {Array} attrs an array of attributes to get values
+ * @return {Object} An object with attribute name/value pairs.
+ */
+ getAttrs: function(attrs) {
+ var ret = {};
+ if (this._getAttrs) { // use Attribute imple
+ this._getAttrs(attrs);
+ } else { // use setters inline
+ Y.Array.each(attrs, function(v, n) {
+ ret[v] = this.get(v);
+ }, this);
+ }
+
+ return ret;
+ },
+
+ /**
+ * Creates a new Node using the provided markup string.
+ * @method create
+ * @param {String} html The markup used to create the element
+ * @param {HTMLDocument} doc An optional document context
+ * @return {Node} A Node instance bound to a DOM node or fragment
+ */
+ create: Node.create,
+
+ /**
+ * Compares nodes to determine if they match.
+ * Node instances can be compared to each other and/or HTMLElements.
+ * @method compareTo
+ * @param {HTMLElement | Node} refNode The reference node to compare to the node.
+ * @return {Boolean} True if the nodes match, false if they do not.
+ */
+ compareTo: function(refNode) {
+ var node = this._node;
+ if (refNode instanceof Y.Node) {
+ refNode = refNode._node;
+ }
+ return node === refNode;
+ },
+
+ /**
+ * Determines whether the node is appended to the document.
+ * @method inDoc
+ * @param {Node|HTMLElement} doc optional An optional document to check against.
+ * Defaults to current document.
+ * @return {Boolean} Whether or not this node is appended to the document.
+ */
+ inDoc: function(doc) {
+ var node = this._node;
+ doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
+ if (doc.documentElement) {
+ return Y.DOM.contains(doc.documentElement, node);
+ }
+ },
+
+ getById: function(id) {
+ var node = this._node,
+ ret = Y.DOM.byId(id, node[OWNER_DOCUMENT]);
+ if (ret && Y.DOM.contains(node, ret)) {
+ ret = Y.one(ret);
+ } else {
+ ret = null;
+ }
+ return ret;
+ },
+
+ /**
+ * Returns the nearest ancestor that passes the test applied by supplied boolean method.
+ * @method ancestor
+ * @param {String | Function} fn A selector string or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} The matching Node instance or null if not found
+ */
+ ancestor: function(fn) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'parentNode', _wrapFn(fn)));
+ },
+
+ /**
+ * Returns the previous matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method previous
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ previous: function(fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Returns the next matching sibling.
+ * Returns the nearest element node sibling if no method provided.
+ * @method next
+ * @param {String | Function} fn A selector or boolean method for testing elements.
+ * If a function is used, it receives the current node being tested as the only argument.
+ * @return {Node} Node instance or null if not found
+ */
+ next: function(node, fn, all) {
+ return Node.get(Y.DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method one
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ one: function(selector) {
+ return Y.one(Y.Selector.query(selector, this._node, true));
+ },
+
+ /**
+ * Retrieves a Node instance of nodes based on the given CSS selector.
+ * @method query
+ * @deprecated Use one()
+ * @param {string} selector The CSS selector to test against.
+ * @return {Node} A Node instance for the matching HTMLElement.
+ */
+ query: function(selector) {
+ return this.one(selector);
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method all
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ all: function(selector) {
+ var nodelist = Y.all(Y.Selector.query(selector, this._node));
+ nodelist._query = selector;
+ return nodelist;
+ },
+
+ /**
+ * Retrieves a nodeList based on the given CSS selector.
+ * @method queryAll
+ * @deprecated Use all()
+ * @param {string} selector The CSS selector to test against.
+ * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
+ */
+ queryAll: function(selector) {
+ return this.all(selector);
+ },
+
+ // TODO: allow fn test
+ /**
+ * Test if the supplied node matches the supplied selector.
+ * @method test
+ *
+ * @param {string} selector The CSS selector to test against.
+ * @return {boolean} Whether or not the node matches the selector.
+ */
+ test: function(selector) {
+ return Y.Selector.test(this._node, selector);
+ },
+
+ /**
+ * Removes the node from its parent.
+ * Shortcut for myNode.get('parentNode').removeChild(myNode);
+ * @method remove
+ * @chainable
+ *
+ */
+ remove: function(destroy) {
+ var node = this._node;
+ node.parentNode.removeChild(node);
+ if (destroy) {
+ this.destroy(true);
+ }
+ return this;
+ },
+
+ /**
+ * Replace the node with the other node. This is a DOM update only
+ * and does not change the node bound to the Node instance.
+ * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
+ * @method replace
+ * @chainable
+ *
+ */
+ replace: function(newNode) {
+ var node = this._node;
+ node.parentNode.replaceChild(newNode, node);
+ return this;
+ },
+
+ purge: function(recurse, type) {
+ Y.Event.purgeElement(this._node, recurse, type);
+ },
+
+ destroy: function(purge) {
+ delete Node._instances[this[UID]];
+ if (purge) {
+ this.purge(true);
+ }
+
+ if (this.unplug) {
+ this.unplug();
+ }
+
+ this._node._yuid = null;
+ this._node = null;
+ this._stateProxy = null;
+ },
+
+ /**
+ * Invokes a method on the Node instance
+ * @method invoke
+ * @param {String} method The name of the method to invoke
+ * @param {Any} a, b, c, etc. Arguments to invoke the method with.
+ * @return Whatever the underly method returns.
+ * DOM Nodes and Collections return values
+ * are converted to Node/NodeList instances.
+ *
+ */
+ invoke: function(method, a, b, c, d, e) {
+ var node = this._node,
+ ret;
+
+ if (a && a instanceof Y.Node) {
+ a = a._node;
+ }
+
+ if (b && b instanceof Y.Node) {
+ b = b._node;
+ }
+
+ ret = node[method](a, b, c, d, e);
+ return Y.Node.scrubVal(ret, this);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @deprecated Use NodeList
+ * @param {Function} fn The function to apply
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the NodeList instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ context = context || this;
+ return fn.call(context, this);
+ },
+
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ * @deprecated Use NodeList
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ return this;
+ },
+
+ /**
+ * Returns the current number of items in the Node.
+ * @method size
+ * @deprecated Use NodeList
+ * @return {Int} The number of items in the Node.
+ */
+ size: function() {
+ return this._node ? 1 : 0;
+ },
+
+ /**
+ * Inserts the content before the reference node.
+ * @method insert
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @param {Int | Y.Node | HTMLElement | String} where The position to insert at.
+ * @chainable
+ */
+ insert: function(content, where) {
+ var node = this._node;
+
+ if (content) {
+ if (typeof where === 'number') { // allow index
+ where = this._node.childNodes[where];
+ }
+
+ if (typeof content !== 'string') { // allow Node or NodeList/Array instances
+ if (content._node) { // Node
+ content = content._node;
+ } else if (content._nodes || (!content.nodeType && content.length)) { // NodeList or Array
+ Y.each(content._nodes, function(n) {
+ Y.DOM.addHTML(node, n, where);
+ });
+
+ return this; // NOTE: early return
+ }
+ }
+ Y.DOM.addHTML(node, content, where);
+ }
+ return this;
+ },
+
+ /**
+ * Inserts the content as the firstChild of the node.
+ * @method prepend
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ prepend: function(content) {
+ return this.insert(content, 0);
+ },
+
+ /**
+ * Inserts the content as the lastChild of the node.
+ * @method append
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ append: function(content) {
+ return this.insert(content, null);
+ },
+
+ /**
+ * Replaces the node's current content with the content.
+ * @method setContent
+ * @param {String | Y.Node | HTMLElement} content The content to insert
+ * @chainable
+ */
+ setContent: function(content) {
+ Y.DOM.addHTML(this._node, content, 'replace');
+ return this;
+ },
+
+ // TODO: need this?
+ hasMethod: function(method) {
+ var node = this._node;
+ return (node && (typeof node === 'function'));
+ }
+}, true);
+
+Y.Node = Node;
+Y.get = Y.Node.get;
+Y.one = Y.Node.one;
+/**
+ * The NodeList module provides support for managing collections of Nodes.
+ * @module node
+ * @submodule nodelist
+ */
+
+/**
+ * The NodeList class provides a wrapper for manipulating DOM NodeLists.
+ * NodeList properties can be accessed via the set/get methods.
+ * Use Y.all() to retrieve NodeList instances.
+ *
+ * @class NodeList
+ * @constructor
+ */
+
+var NodeList = function(nodes) {
+ if (typeof nodes === 'string') {
+ this._query = nodes;
+ nodes = Y.Selector.query(nodes);
+ } else {
+ nodes = Y.Array(nodes, 0, true);
+ }
+
+ NodeList._instances[Y.stamp(this)] = this;
+ this._nodes = nodes;
+};
+// end "globals"
+
+NodeList.NAME = 'NodeList';
+
+/**
+ * Retrieves the DOM nodes bound to a NodeList instance
+ * @method NodeList.getDOMNodes
+ * @static
+ *
+ * @param {Y.NodeList} node The NodeList instance
+ * @return {Array} The array of DOM nodes bound to the NodeList
+ */
+NodeList.getDOMNodes = function(nodeList) {
+ return nodeList._nodes;
+};
+
+NodeList._instances = [];
+
+NodeList.each = function(instance, fn, context) {
+ var nodes = instance._nodes;
+ if (nodes && nodes.length) {
+ Y.Array.each(nodes, fn, context || instance);
+ } else {
+ }
+};
+
+NodeList.addMethod = function(name, fn, context) {
+ if (name && fn) {
+ NodeList.prototype[name] = function() {
+ var ret = [],
+ args = arguments;
+
+ Y.Array.each(this._nodes, function(node) {
+ var UID = '_yuid',
+ instance = Y.Node._instances[node[UID]],
+ ctx,
+ result;
+
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+ ctx = context || instance;
+ result = fn.apply(ctx, args);
+ if (result !== undefined && result !== instance) {
+ ret[ret.length] = result;
+ }
+ });
+
+ // TODO: remove tmp pointer
+ return ret.length ? ret : this;
+ };
+ } else {
+ }
+};
+
+NodeList.importMethod = function(host, name, altName) {
+ if (typeof name === 'string') {
+ altName = altName || name;
+ NodeList.addMethod(name, host[name]);
+ } else {
+ Y.each(name, function(n) {
+ NodeList.importMethod(host, n);
+ });
+ }
+};
+
+NodeList._getTempNode = function(node) {
+ var tmp = NodeList._tempNode;
+ if (!tmp) {
+ tmp = Y.Node.create('<div></div>');
+ NodeList._tempNode = tmp;
+ }
+
+ tmp._node = node;
+ tmp._stateProxy = node;
+ return tmp;
+};
+
+Y.mix(NodeList.prototype, {
+ /**
+ * Retrieves the Node instance at the given index.
+ * @method item
+ *
+ * @param {Number} index The index of the target Node.
+ * @return {Node} The Node instance at the given index.
+ */
+ item: function(index) {
+ return Y.one((this._nodes || [])[index]);
+ },
+
+ /**
+ * Applies the given function to each Node in the NodeList.
+ * @method each
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to apply the function with
+ * Default context is the current Node instance
+ * @chainable
+ */
+ each: function(fn, context) {
+ var instance = this;
+ Y.Array.each(this._nodes, function(node, index) {
+ node = Y.one(node);
+ return fn.call(context || node, node, index, instance);
+ });
+ return instance;
+ },
+
+ batch: function(fn, context) {
+ var nodelist = this;
+
+ Y.Array.each(this._nodes, function(node, index) {
+ var instance = Y.Node._instances[node[UID]];
+ if (!instance) {
+ instance = NodeList._getTempNode(node);
+ }
+
+ return fn.call(context || instance, instance, index, nodelist);
+ });
+ return nodelist;
+ },
+
+ /**
+ * Executes the function once for each node until a true value is returned.
+ * @method some
+ * @param {Function} fn The function to apply. It receives 3 arguments:
+ * the current node instance, the node's index, and the NodeList instance
+ * @param {Object} context optional An optional context to execute the function from.
+ * Default context is the current Node instance
+ * @return {Boolean} Whether or not the function returned true for any node.
+ */
+ some: function(fn, context) {
+ var instance = this;
+ return Y.Array.some(this._nodes, function(node, index) {
+ node = Y.one(node);
+ context = context || node;
+ return fn.call(context, node, index, instance);
+ });
+ },
+
+ /**
+ * Creates a documenFragment from the nodes bound to the NodeList instance
+ * @method toDocFrag
+ * @return Node a Node instance bound to the documentFragment
+ */
+ toFrag: function() {
+ return Y.one(Y.DOM._nl2frag(this._nodes));
+ },
+
+ /**
+ * Returns the index of the node in the NodeList instance
+ * or -1 if the node isn't found.
+ * @method indexOf
+ * @param {Y.Node || DOMNode} node the node to search for
+ * @return {Int} the index of the node value or -1 if not found
+ */
+ indexOf: function(node) {
+ return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
+ },
+
+ /**
+ * Filters the NodeList instance down to only nodes matching the given selector.
+ * @method filter
+ * @param {String} selector The selector to filter against
+ * @return {NodeList} NodeList containing the updated collection
+ * @see Selector
+ */
+ filter: function(selector) {
+ return Y.all(Y.Selector.filter(this._nodes, selector));
+ },
+
+
+ /**
+ * Creates a new NodeList containing all nodes at every n indices, where
+ * remainder n % index equals r.
+ * (zero-based index).
+ * @method modulus
+ * @param {Int} n The offset to use (return every nth node)
+ * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ modulus: function(n, r) {
+ r = r || 0;
+ var nodes = [];
+ NodeList.each(this, function(node, i) {
+ if (i % n === r) {
+ nodes.push(node);
+ }
+ });
+
+ return Y.all(nodes);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at odd indices
+ * (zero-based index).
+ * @method odd
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ odd: function() {
+ return this.modulus(2, 1);
+ },
+
+ /**
+ * Creates a new NodeList containing all nodes at even indices
+ * (zero-based index), including zero.
+ * @method even
+ * @return {NodeList} NodeList containing the updated collection
+ */
+ even: function() {
+ return this.modulus(2);
+ },
+
+ destructor: function() {
+ delete NodeList._instances[this[UID]];
+ },
+
+ /**
+ * Reruns the initial query, when created using a selector query
+ * @method refresh
+ * @chainable
+ */
+ refresh: function() {
+ var doc,
+ nodes = this._nodes;
+ if (this._query) {
+ if (nodes && nodes[0] && nodes[0].ownerDocument) {
+ doc = nodes[0].ownerDocument;
+ }
+
+ this._nodes = Y.Selector.query(this._query, doc || Y.config.doc);
+ }
+
+ return this;
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * @method on
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ on: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.on.apply(Y, args);
+ },
+
+ /**
+ * Applies an event listener to each Node bound to the NodeList.
+ * The handler is called only after all on() handlers are called
+ * and the event is not prevented.
+ * @method after
+ * @param {String} type The event being listened for
+ * @param {Function} fn The handler to call when the event fires
+ * @param {Object} context The context to call the handler with.
+ * Default is the NodeList instance.
+ * @return {Object} Returns an event handle that can later be use to detach().
+ * @see Event.on
+ */
+ after: function(type, fn, context) {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(2, 0, this._nodes);
+ args[3] = context || this;
+ return Y.after.apply(Y, args);
+ },
+
+ /**
+ * Returns the current number of items in the NodeList.
+ * @method size
+ * @return {Int} The number of items in the NodeList.
+ */
+ size: function() {
+ return this._nodes.length;
+ },
+
+ toString: function() {
+ var str = '',
+ errorMsg = this[UID] + ': not bound to any nodes',
+ nodes = this._nodes,
+ node;
+
+ if (nodes && nodes[0]) {
+ node = nodes[0];
+ str += node[NODE_NAME];
+ if (node.id) {
+ str += '#' + node.id;
+ }
+
+ if (node.className) {
+ str += '.' + node.className.replace(' ', '.');
+ }
+
+ if (nodes.length > 1) {
+ str += '...[' + nodes.length + ' items]';
+ }
+ }
+ return str || errorMsg;
+ }
+
+}, true);
+
+NodeList.importMethod(Y.Node.prototype, [
+ /**
+ * Called on each Node instance
+ * @for NodeList
+ * @method append
+ * @see Node.append
+ */
+ 'append',
+
+ /**
+ * Called on each Node instance
+ * @method detach
+ * @see Node.detach
+ */
+ 'detach',
+
+ /** Called on each Node instance
+ * @method detachAll
+ * @see Node.detachAll
+ */
+ 'detachAll',
+
+ /** Called on each Node instance
+ * @method insert
+ * @see NodeInsert
+ */
+ 'insert',
+
+ /** Called on each Node instance
+ * @method prepend
+ * @see Node.prepend
+ */
+ 'prepend',
+
+ /** Called on each Node instance
+ * @method remove
+ * @see Node.remove
+ */
+ 'remove',
+
+ /** Called on each Node instance
+ * @method set
+ * @see Node.set
+ */
+ 'set',
+
+ /** Called on each Node instance
+ * @method setContent
+ * @see Node.setContent
+ */
+ 'setContent'
+]);
+
+// one-off implementation to convert array of Nodes to NodeList
+// e.g. Y.all('input').get('parentNode');
+
+/** Called on each Node instance
+ * @method get
+ * @see Node
+ */
+NodeList.prototype.get = function(attr) {
+ var ret = [],
+ nodes = this._nodes,
+ isNodeList = false,
+ getTemp = NodeList._getTempNode,
+ instance,
+ val;
+
+ if (nodes[0]) {
+ instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
+ val = instance._get(attr);
+ if (val && val.nodeType) {
+ isNodeList = true;
+ }
+ }
+
+ Y.Array.each(nodes, function(node) {
+ instance = Y.Node._instances[node._yuid];
+
+ if (!instance) {
+ instance = getTemp(node);
+ }
+
+ val = instance._get(attr);
+ if (!isNodeList) { // convert array of Nodes to NodeList
+ val = Y.Node.scrubVal(val, instance);
+ }
+
+ ret.push(val);
+ });
+
+ return (isNodeList) ? Y.all(ret) : ret;
+};
+
+Y.NodeList = NodeList;
+
+Y.all = function(nodes) {
+ return new NodeList(nodes);
+};
+
+Y.Node.all = Y.all;
+Y.Array.each([
+ /**
+ * Passes through to DOM method.
+ * @method replaceChild
+ * @for Node
+ * @param {HTMLElement | Node} node Node to be inserted
+ * @param {HTMLElement | Node} refNode Node to be replaced
+ * @return {Node} The replaced node
+ */
+ 'replaceChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method appendChild
+ * @param {HTMLElement | Node} node Node to be appended
+ * @return {Node} The appended node
+ */
+ 'appendChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method insertBefore
+ * @param {HTMLElement | Node} newNode Node to be appended
+ * @param {HTMLElement | Node} refNode Node to be inserted before
+ * @return {Node} The inserted node
+ */
+ 'insertBefore',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeChild
+ * @param {HTMLElement | Node} node Node to be removed
+ * @return {Node} The removed node
+ */
+ 'removeChild',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasChildNodes
+ * @return {Boolean} Whether or not the node has any childNodes
+ */
+ 'hasChildNodes',
+
+ /**
+ * Passes through to DOM method.
+ * @method cloneNode
+ * @param {Boolean} deep Whether or not to perform a deep clone, which includes
+ * subtree and attributes
+ * @return {Node} The clone
+ */
+ 'cloneNode',
+
+ /**
+ * Passes through to DOM method.
+ * @method hasAttribute
+ * @param {String} attribute The attribute to test for
+ * @return {Boolean} Whether or not the attribute is present
+ */
+ 'hasAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method removeAttribute
+ * @param {String} attribute The attribute to be removed
+ * @chainable
+ */
+ 'removeAttribute',
+
+ /**
+ * Passes through to DOM method.
+ * @method scrollIntoView
+ * @chainable
+ */
+ 'scrollIntoView',
+
+ /**
+ * Passes through to DOM method.
+ * @method getElementsByTagName
+ * @param {String} tagName The tagName to collect
+ * @return {NodeList} A NodeList representing the HTMLCollection
+ */
+ 'getElementsByTagName',
+
+ /**
+ * Passes through to DOM method.
+ * @method focus
+ * @chainable
+ */
+ 'focus',
+
+ /**
+ * Passes through to DOM method.
+ * @method blur
+ * @chainable
+ */
+ 'blur',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method submit
+ * @chainable
+ */
+ 'submit',
+
+ /**
+ * Passes through to DOM method.
+ * Only valid on FORM elements
+ * @method reset
+ * @chainable
+ */
+ 'reset',
+
+ /**
+ * Passes through to DOM method.
+ * @method select
+ * @chainable
+ */
+ 'select'
+], function(method) {
+ Y.Node.prototype[method] = function(arg1, arg2, arg3) {
+ var ret = this.invoke(method, arg1, arg2, arg3);
+ return ret;
+ };
+});
+
+Node.importMethod(Y.DOM, [
+ /**
+ * Determines whether the ndoe is an ancestor of another HTML element in the DOM hierarchy.
+ * @method contains
+ * @param {Node | HTMLElement} needle The possible node or descendent
+ * @return {Boolean} Whether or not this node is the needle its ancestor
+ */
+ 'contains',
+ /**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @for Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+ 'setAttribute',
+ /**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @for Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+ 'getAttribute'
+]);
+
+/**
+ * Allows setting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method setAttribute
+ * @see Node
+ * @for NodeList
+ * @chainable
+ * @param {string} name The attribute name
+ * @param {string} value The value to set
+ */
+
+/**
+ * Allows getting attributes on DOM nodes, normalizing in some cases.
+ * This passes through to the DOM node, allowing for custom attributes.
+ * @method getAttribute
+ * @see Node
+ * @for NodeList
+ * @param {string} name The attribute name
+ * @return {string} The attribute value
+ */
+Y.NodeList.importMethod(Y.Node.prototype, ['getAttribute', 'setAttribute']);
+(function(Y) {
+ var methods = [
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @for Node
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+ 'hasClass',
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+ 'addClass',
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+ 'removeClass',
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+ 'replaceClass',
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ 'toggleClass'
+ ];
+
+ Y.Node.importMethod(Y.DOM, methods);
+ /**
+ * Determines whether each node has the given className.
+ * @method hasClass
+ * @see Node.hasClass
+ * @for NodeList
+ * @param {String} className the class name to search for
+ * @return {Array} An array of booleans for each node bound to the NodeList.
+ */
+
+ /**
+ * Adds a class name to each node.
+ * @method addClass
+ * @see Node.addClass
+ * @param {String} className the class name to add to the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Removes a class name from each node.
+ * @method removeClass
+ * @see Node.removeClass
+ * @param {String} className the class name to remove from the node's class attribute
+ * @chainable
+ */
+
+ /**
+ * Replace a class with another class for each node.
+ * If no oldClassName is present, the newClassName is simply added.
+ * @method replaceClass
+ * @see Node.replaceClass
+ * @param {String} oldClassName the class name to be replaced
+ * @param {String} newClassName the class name that will be replacing the old class name
+ * @chainable
+ */
+
+ /**
+ * If the className exists on the node it is removed, if it doesn't exist it is added.
+ * @method toggleClass
+ * @see Node.toggleClass
+ * @param {String} className the class name to be toggled
+ * @chainable
+ */
+ Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+if (!document.documentElement.hasAttribute) { // IE < 8
+ Y.Node.prototype.hasAttribute = function(attr) {
+ return Y.DOM.getAttribute(this._node, attr) !== '';
+ };
+}
+
+// IE throws error when setting input.type = 'hidden',
+// input.setAttribute('type', 'hidden') and input.attributes.type.value = 'hidden'
+Y.Node.ATTRS.type = {
+ setter: function(val) {
+ if (val === 'hidden') {
+ try {
+ this._node.type = 'hidden';
+ } catch(e) {
+ this.setStyle('display', 'none');
+ this._inputType = 'hidden';
+ }
+ } else {
+ try { // IE errors when changing the type from "hidden'
+ this._node.type = val;
+ } catch (e) {
+ }
+ }
+ return val;
+ },
+
+ getter: function() {
+ return this._inputType || this._node.type;
+ },
+
+ _bypassProxy: true // don't update DOM when using with Attribute
+};
+
+
+}, '3.0.0' ,{requires:['dom-base', 'selector-css2', 'event-base']});
+YUI.add('node-style', function(Y) {
+
+(function(Y) {
+/**
+ * Extended Node interface for managing node styles.
+ * @module node
+ * @submodule node-style
+ */
+
+var methods = [
+ /**
+ * Returns the style's current value.
+ * @method getStyle
+ * @for Node
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The current value of the style property for the element.
+ */
+ 'getStyle',
+
+ /**
+ * Returns the computed value for the given style property.
+ * @method getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {String} The computed value of the style property for the element.
+ */
+ 'getComputedStyle',
+
+ /**
+ * Sets a style property of the node.
+ * @method setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+ 'setStyle',
+
+ /**
+ * Sets multiple style properties on the node.
+ * @method setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+ 'setStyles'
+];
+Y.Node.importMethod(Y.DOM, methods);
+/**
+ * Returns an array of values for each node.
+ * @method getStyle
+ * @for NodeList
+ * @see Node.getStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The current values of the style property for the element.
+ */
+
+/**
+ * Returns an array of the computed value for each node.
+ * @method getComputedStyle
+ * @see Node.getComputedStyle
+ * @param {String} attr The style attribute to retrieve.
+ * @return {Array} The computed values for each node.
+ */
+
+/**
+ * Sets a style property on each node.
+ * @method setStyle
+ * @see Node.setStyle
+ * @param {String} attr The style attribute to set.
+ * @param {String|Number} val The value.
+ * @chainable
+ */
+
+/**
+ * Sets multiple style properties on each node.
+ * @method setStyles
+ * @see Node.setStyles
+ * @param {Object} hash An object literal of property:value pairs.
+ * @chainable
+ */
+Y.NodeList.importMethod(Y.Node.prototype, methods);
+})(Y);
+
+
+}, '3.0.0' ,{requires:['dom-style', 'node-base']});
+YUI.add('node-screen', function(Y) {
+
+/**
+ * Extended Node interface for managing regions and screen positioning.
+ * Adds support for positioning elements and normalizes window size and scroll detection.
+ * @module node
+ * @submodule node-screen
+ */
+
+// these are all "safe" returns, no wrapping required
+Y.each([
+ /**
+ * Returns the inner width of the viewport (exludes scrollbar).
+ * @config winWidth
+ * @for Node
+ * @type {Int}
+ */
+ 'winWidth',
+
+ /**
+ * Returns the inner height of the viewport (exludes scrollbar).
+ * @config winHeight
+ * @type {Int}
+ */
+ 'winHeight',
+
+ /**
+ * Document width
+ * @config winHeight
+ * @type {Int}
+ */
+ 'docWidth',
+
+ /**
+ * Document height
+ * @config docHeight
+ * @type {Int}
+ */
+ 'docHeight',
+
+ /**
+ * Amount page has been scroll vertically
+ * @config docScrollX
+ * @type {Int}
+ */
+ 'docScrollX',
+
+ /**
+ * Amount page has been scroll horizontally
+ * @config docScrollY
+ * @type {Int}
+ */
+ 'docScrollY'
+ ],
+ function(name) {
+ Y.Node.ATTRS[name] = {
+ getter: function() {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift(Y.Node.getDOMNode(this));
+
+ return Y.DOM[name].apply(this, args);
+ }
+ };
+ }
+);
+
+Y.Node.ATTRS.scrollLeft = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollLeft' in node) ? node.scrollLeft : Y.DOM.docScrollX(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollLeft' in node) {
+ node.scrollLeft = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(val, Y.DOM.docScrollY(node)); // scroll window if win or doc
+ }
+ } else {
+ }
+ }
+};
+
+Y.Node.ATTRS.scrollTop = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ return ('scrollTop' in node) ? node.scrollTop : Y.DOM.docScrollY(node);
+ },
+
+ setter: function(val) {
+ var node = Y.Node.getDOMNode(this);
+ if (node) {
+ if ('scrollTop' in node) {
+ node.scrollTop = val;
+ } else if (node.document || node.nodeType === 9) {
+ Y.DOM._getWin(node).scrollTo(Y.DOM.docScrollX(node), val); // scroll window if win or doc
+ }
+ } else {
+ }
+ }
+};
+
+Y.Node.importMethod(Y.DOM, [
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getXY
+ * @for Node
+ * @return {Array} The XY position of the node
+*/
+ 'getXY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setXY
+ * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setXY',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getX
+ * @return {Int} The X position of the node
+*/
+ 'getX',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setX
+ * @param {Int} x X value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setX',
+
+/**
+ * Gets the current position of the node in page coordinates.
+ * @method getY
+ * @return {Int} The Y position of the node
+*/
+ 'getY',
+
+/**
+ * Set the position of the node in page coordinates, regardless of how the node is positioned.
+ * @method setY
+ * @param {Int} y Y value for new position (coordinates are page-based)
+ * @chainable
+ */
+ 'setY'
+]);
+
+/**
+ * Returns a region object for the node
+ * @config region
+ * @for Node
+ * @type Node
+ */
+Y.Node.ATTRS.region = {
+ getter: function() {
+ var node = Y.Node.getDOMNode(this);
+ if (node && !node.tagName) {
+ if (node.nodeType === 9) { // document
+ node = node.documentElement;
+ } else if (node.alert) { // window
+ node = node.document.documentElement;
+ }
+ }
+ return Y.DOM.region(node);
+ }
+};
+
+/**
+ * Returns a region object for the node's viewport
+ * @config viewportRegion
+ * @type Node
+ */
+Y.Node.ATTRS.viewportRegion = {
+ getter: function() {
+ return Y.DOM.viewportRegion(Y.Node.getDOMNode(this));
+ }
+};
+
+Y.Node.importMethod(Y.DOM, 'inViewportRegion');
+
+// these need special treatment to extract 2nd node arg
+/**
+ * Compares the intersection of the node with another node or region
+ * @method intersect
+ * @for Node
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.intersect = function(node2, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.intersect(node1, node2, altRegion);
+};
+
+/**
+ * Determines whether or not the node is within the giving region.
+ * @method inRegion
+ * @param {Node|Object} node2 The node or region to compare with.
+ * @param {Boolean} all Whether or not all of the node must be in the region.
+ * @param {Object} altRegion An alternate region to use (rather than this node's).
+ * @return {Object} An object representing the intersection of the regions.
+ */
+Y.Node.prototype.inRegion = function(node2, all, altRegion) {
+ var node1 = Y.Node.getDOMNode(this);
+ if (node2 instanceof Y.Node) { // might be a region object
+ node2 = Y.Node.getDOMNode(node2);
+ }
+ return Y.DOM.inRegion(node1, node2, all, altRegion);
+};
+
+
+}, '3.0.0' ,{requires:['dom-screen']});
+YUI.add('node-pluginhost', function(Y) {
+
+/**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of Node by default).
+ *
+ * @method Node.plug
+ * @static
+ *
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+Y.Node.plug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.plug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+/**
+ * Unregisters any class level plugins which have been registered by the Node
+ *
+ * @method Node.unplug
+ * @static
+ *
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+Y.Node.unplug = function() {
+ var args = Y.Array(arguments);
+ args.unshift(Y.Node);
+ Y.Plugin.Host.unplug.apply(Y.Base, args);
+ return Y.Node;
+};
+
+Y.mix(Y.Node, Y.Plugin.Host, false, null, 1);
+
+// allow batching of plug/unplug via NodeList
+// doesn't use NodeList.importMethod because we need real Nodes (not tmpNode)
+Y.NodeList.prototype.plug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.plug.apply(Y.one(node), args);
+ });
+};
+
+Y.NodeList.prototype.unplug = function() {
+ var args = arguments;
+ Y.NodeList.each(this, function(node) {
+ Y.Node.prototype.unplug.apply(Y.one(node), args);
+ });
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'pluginhost']});
+YUI.add('node-event-delegate', function(Y) {
+
+/**
+ * Functionality to make the node a delegated event container
+ * @module node
+ * @submodule node-event-delegate
+ */
+
+/**
+ * Functionality to make the node a delegated event container
+ * @method delegate
+ * @param type {String} the event type to delegate
+ * @param fn {Function} the function to execute
+ * @param selector {String} a selector that must match the target of the event.
+ * @return {Event.Handle} the detach handle
+ * @for Node
+ */
+Y.Node.prototype.delegate = function(type, fn, selector) {
+
+ var args = Array.prototype.slice.call(arguments, 3),
+ a = [type, fn, Y.Node.getDOMNode(this), selector];
+ a = a.concat(args);
+
+ return Y.delegate.apply(Y, a);
+};
+
+
+}, '3.0.0' ,{requires:['node-base', 'event-delegate', 'pluginhost']});
+
+
+YUI.add('node', function(Y){}, '3.0.0' ,{skinnable:false, use:['node-base', 'node-style', 'node-screen', 'node-pluginhost', 'node-event-delegate'], requires:['dom', 'event-base', 'event-delegate', 'pluginhost']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('oop', function(Y) {
+
+/**
+ * Supplies object inheritance and manipulation utilities. This adds
+ * additional functionaity to what is provided in yui-base, and the
+ * methods are applied directly to the YUI instance. This module
+ * is required for most YUI components.
+ * @module oop
+ */
+
+ var L = Y.Lang,
+ A = Y.Array,
+ OP = Object.prototype,
+ CLONE_MARKER = "_~yuim~_";
+
+ // dispatch = function(o, f, c, proto, action) {
+ // if (o[action] && o.item) {
+ // return o[action].call(o, f, c);
+ // } else {
+ // switch (A.test(o)) {
+ // case 1:
+ // return A[action](o, f, c);
+ // case 2:
+ // return A[action](Y.Array(o, 0, true), f, c);
+ // default:
+ // return Y.Object[action](o, f, c, proto);
+ // }
+ // }
+ // };
+
+ /**
+ * The following methods are added to the YUI instance
+ * @class YUI~oop
+ */
+
+ /**
+ * Applies prototype properties from the supplier to the receiver.
+ * The receiver can be a constructor or an instance.
+ * @method augment
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param args {Array | Any} arg or arguments to apply to the supplier
+ * constructor when initializing.
+ * @return {object} the augmented object
+ *
+ * @todo constructor optional?
+ * @todo understanding what an instance is augmented with
+ * @TODO best practices for overriding sequestered methods.
+ */
+ Y.augment = function(r, s, ov, wl, args) {
+ var sProto = s.prototype,
+ newProto = null,
+ construct = s,
+ a = (args) ? Y.Array(args) : [],
+ rProto = r.prototype,
+ target = rProto || r,
+ applyConstructor = false,
+ sequestered, replacements, i;
+
+ // working on a class, so apply constructor infrastructure
+ if (rProto && construct) {
+ sequestered = {};
+ replacements = {};
+ newProto = {};
+
+ // sequester all of the functions in the supplier and replace with
+ // one that will restore all of them.
+ Y.each(sProto, function(v, k) {
+ replacements[k] = function() {
+
+// Y.log('sequestered function "' + k + '" executed. Initializing EventTarget');
+// overwrite the prototype with all of the sequestered functions,
+// but only if it hasn't been overridden
+ for (i in sequestered) {
+ if (sequestered.hasOwnProperty(i) && (this[i] === replacements[i])) {
+ // Y.log('... restoring ' + k);
+ this[i] = sequestered[i];
+ }
+ }
+
+ // apply the constructor
+ construct.apply(this, a);
+
+ // apply the original sequestered function
+ return sequestered[k].apply(this, arguments);
+ };
+
+ if ((!wl || (k in wl)) && (ov || !(k in this))) {
+ // Y.log('augment: ' + k);
+ if (L.isFunction(v)) {
+ // sequester the function
+ sequestered[k] = v;
+
+// replace the sequestered function with a function that will
+// restore all sequestered functions and exectue the constructor.
+ this[k] = replacements[k];
+ } else {
+ // Y.log('augment() applying non-function: ' + k);
+ this[k] = v;
+ }
+
+ }
+
+ }, newProto, true);
+
+ // augmenting an instance, so apply the constructor immediately
+ } else {
+ applyConstructor = true;
+ }
+
+ Y.mix(target, newProto || sProto, ov, wl);
+
+ if (applyConstructor) {
+ s.apply(target, a);
+ }
+
+ return r;
+ };
+
+ /**
+ * Applies object properties from the supplier to the receiver. If
+ * the target has the property, and the property is an object, the target
+ * object will be augmented with the supplier's value. If the property
+ * is an array, the suppliers value will be appended to the target.
+ * @method aggregate
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @return {object} the extended object
+ */
+ Y.aggregate = function(r, s, ov, wl) {
+ return Y.mix(r, s, ov, wl, 0, true);
+ };
+
+ /**
+ * Utility to set up the prototype, constructor and superclass properties to
+ * support an inheritance strategy that can chain constructors and methods.
+ * Static members will not be inherited.
+ *
+ * @method extend
+ * @param {Function} r the object to modify
+ * @param {Function} s the object to inherit
+ * @param {Object} px prototype properties to add/override
+ * @param {Object} sx static properties to add/override
+ * @return {YUI} the YUI instance
+ */
+ Y.extend = function(r, s, px, sx) {
+ if (!s||!r) {
+ // @TODO error symbols
+ Y.error("extend failed, verify dependencies");
+ }
+
+ var sp = s.prototype, rp=Y.Object(sp);
+ r.prototype=rp;
+
+ rp.constructor=r;
+ r.superclass=sp;
+
+ // assign constructor property
+ if (s != Object && sp.constructor == OP.constructor) {
+ sp.constructor=s;
+ }
+
+ // add prototype overrides
+ if (px) {
+ Y.mix(rp, px, true);
+ }
+
+ // add object overrides
+ if (sx) {
+ Y.mix(r, sx, true);
+ }
+
+ return r;
+ };
+
+ /**
+ * Executes the supplied function for each item in
+ * a collection. Supports arrays, objects, and
+ * Y.NodeLists
+ * @method each
+ * @param o the object to iterate
+ * @param f the function to execute. This function
+ * receives the value, key, and object as parameters
+ * @param proto if true, prototype properties are
+ * iterated on objects
+ * @return {YUI} the YUI instance
+ */
+ Y.each = function(o, f, c, proto) {
+
+ if (o.each && o.item) {
+ return o.each.call(o, f, c);
+ } else {
+ switch (A.test(o)) {
+ case 1:
+ return A.each(o, f, c);
+ case 2:
+ return A.each(Y.Array(o, 0, true), f, c);
+ default:
+ return Y.Object.each(o, f, c, proto);
+ }
+ }
+
+ // return Y.Object.each(o, f, c);
+ };
+
+ // Y.each = function(o, f, c, proto) {
+ // return dispatch(o, f, c, proto, 'each');
+ // };
+
+ /*
+ * Executes the supplied function for each item in
+ * a collection. The operation stops if the function
+ * returns true. Supports arrays, objects, and
+ * Y.NodeLists.
+ * @method some
+ * @param o the object to iterate
+ * @param f the function to execute. This function
+ * receives the value, key, and object as parameters
+ * @param proto if true, prototype properties are
+ * iterated on objects
+ * @return {boolean} true if the function ever returns true, false otherwise
+ */
+ // Y.some = function(o, f, c, proto) {
+ // return dispatch(o, f, c, proto, 'some');
+ // };
+
+ /**
+ * Deep obj/array copy. Functions are cloned with Y.bind.
+ * Array-like objects are treated as arrays.
+ * Primitives are returned untouched. Optionally, a
+ * function can be provided to handle other data types,
+ * filter keys, validate values, etc.
+ *
+ * @method clone
+ * @param o what to clone
+ * @param safe {boolean} if true, objects will not have prototype
+ * items from the source. If false, they will. In this case, the
+ * original is initially protected, but the clone is not completely immune
+ * from changes to the source object prototype. Also, cloned prototype
+ * items that are deleted from the clone will result in the value
+ * of the source prototype being exposed. If operating on a non-safe
+ * clone, items should be nulled out rather than deleted.
+ * @TODO review
+ * @param f optional function to apply to each item in a collection;
+ * it will be executed prior to applying the value to
+ * the new object. Return false to prevent the copy.
+ * @param c optional execution context for f
+ * @param owner Owner object passed when clone is iterating an
+ * object. Used to set up context for cloned functions.
+ * @return {Array|Object} the cloned object
+ */
+ Y.clone = function(o, safe, f, c, owner, cloned) {
+
+ if (!L.isObject(o)) {
+ return o;
+ }
+
+ var o2, marked = cloned || {}, stamp;
+
+ switch (L.type(o)) {
+ case 'date':
+ return new Date(o);
+ case 'regexp':
+ return new RegExp(o.source);
+ case 'function':
+ o2 = Y.bind(o, owner);
+ break;
+ case 'array':
+ o2 = [];
+ break;
+ default:
+
+ // #2528250 only one clone of a given object should be created.
+ if (o[CLONE_MARKER]) {
+ return marked[o[CLONE_MARKER]];
+ }
+
+ stamp = Y.guid();
+
+ o2 = (safe) ? {} : Y.Object(o);
+
+ o[CLONE_MARKER] = stamp;
+ marked[stamp] = o;
+ }
+
+ // #2528250 don't try to clone element properties
+ if (!o.addEventListener && !o.attachEvent) {
+ Y.each(o, function(v, k) {
+ if (!f || (f.call(c || this, v, k, this, o) !== false)) {
+ if (k !== CLONE_MARKER) {
+ this[k] = Y.clone(v, safe, f, c, owner || o, marked);
+ }
+ }
+ }, o2);
+ }
+
+ if (!cloned) {
+ Y.each(marked, function(v, k) {
+ delete v[CLONE_MARKER];
+ });
+ marked = null;
+ }
+
+ return o2;
+ };
+
+
+ /**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the beginning of the arguments collection the
+ * supplied to the function.
+ *
+ * @method bind
+ * @param f {Function|String} the function to bind, or a function name
+ * to execute on the context object
+ * @param c the execution context
+ * @param args* 0..n arguments to include before the arguments the
+ * function is executed with.
+ * @return {function} the wrapped function
+ */
+ Y.bind = function(f, c) {
+ var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
+ return function () {
+ var fn = L.isString(f) ? c[f] : f,
+ args = (xargs) ? xargs.concat(Y.Array(arguments, 0, true)) : arguments;
+ return fn.apply(c || fn, args);
+ };
+ };
+
+ /**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the end of the arguments the function
+ * is executed with.
+ *
+ * @method rbind
+ * @param f {Function|String} the function to bind, or a function name
+ * to execute on the context object
+ * @param c the execution context
+ * @param args* 0..n arguments to append to the end of arguments collection
+ * supplied to the function
+ * @return {function} the wrapped function
+ */
+ Y.rbind = function(f, c) {
+ var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
+ return function () {
+ var fn = L.isString(f) ? c[f] : f,
+ args = (xargs) ? Y.Array(arguments, 0, true).concat(xargs) : arguments;
+ return fn.apply(c || fn, args);
+ };
+ };
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("oop",function(F){var E=F.Lang,D=F.Array,C=Object.prototype,B="_~yuim~_";F.augment=function(A,T,I,R,N){var L=T.prototype,P=null,S=T,O=(N)?F.Array(N):[],H=A.prototype,M=H||A,Q=false,G,J,K;if(H&&S){G={};J={};P={};F.each(L,function(V,U){J[U]=function(){for(K in G){if(G.hasOwnProperty(K)&&(this[K]===J[K])){this[K]=G[K];}}S.apply(this,O);return G[U].apply(this,arguments);};if((!R||(U in R))&&(I||!(U in this))){if(E.isFunction(V)){G[U]=V;this[U]=J[U];}else{this[U]=V;}}},P,true);}else{Q=true;}F.mix(M,P||L,I,R);if(Q){T.apply(M,O);}return A;};F.aggregate=function(H,G,A,I){return F.mix(H,G,A,I,0,true);};F.extend=function(I,H,A,K){if(!H||!I){F.error("extend failed, verify dependencies");}var J=H.prototype,G=F.Object(J);I.prototype=G;G.constructor=I;I.superclass=J;if(H!=Object&&J.constructor==C.constructor){J.constructor=H;}if(A){F.mix(G,A,true);}if(K){F.mix(I,K,true);}return I;};F.each=function(H,G,I,A){if(H.each&&H.item){return H.each.call(H,G,I);}else{switch(D.test(H)){case 1:return D.each(H,G,I);case 2:return D.each(F.Array(H,0,true),G,I);default:return F.Object.each(H,G,I,A);}}};F.clone=function(I,J,M,N,H,L){if(!E.isObject(I)){return I;}var K,G=L||{},A;switch(E.type(I)){case"date":return new Date(I);case"regexp":return new RegExp(I.source);case"function":K=F.bind(I,H);break;case"array":K=[];break;default:if(I[B]){return G[I[B]];}A=F.guid();K=(J)?{}:F.Object(I);I[B]=A;G[A]=I;}if(!I.addEventListener&&!I.attachEvent){F.each(I,function(P,O){if(!M||(M.call(N||this,P,O,this,I)!==false)){if(O!==B){this[O]=F.clone(P,J,M,N,H||I,G);}}},K);}if(!L){F.each(G,function(P,O){delete P[B];});G=null;}return K;};F.bind=function(A,H){var G=arguments.length>2?F.Array(arguments,2,true):null;return function(){var J=E.isString(A)?H[A]:A,I=(G)?G.concat(F.Array(arguments,0,true)):arguments;return J.apply(H||J,I);};};F.rbind=function(A,H){var G=arguments.length>2?F.Array(arguments,2,true):null;return function(){var J=E.isString(A)?H[A]:A,I=(G)?F.Array(arguments,0,true).concat(G):arguments;return J.apply(H||J,I);};};},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('oop', function(Y) {
+
+/**
+ * Supplies object inheritance and manipulation utilities. This adds
+ * additional functionaity to what is provided in yui-base, and the
+ * methods are applied directly to the YUI instance. This module
+ * is required for most YUI components.
+ * @module oop
+ */
+
+ var L = Y.Lang,
+ A = Y.Array,
+ OP = Object.prototype,
+ CLONE_MARKER = "_~yuim~_";
+
+ // dispatch = function(o, f, c, proto, action) {
+ // if (o[action] && o.item) {
+ // return o[action].call(o, f, c);
+ // } else {
+ // switch (A.test(o)) {
+ // case 1:
+ // return A[action](o, f, c);
+ // case 2:
+ // return A[action](Y.Array(o, 0, true), f, c);
+ // default:
+ // return Y.Object[action](o, f, c, proto);
+ // }
+ // }
+ // };
+
+ /**
+ * The following methods are added to the YUI instance
+ * @class YUI~oop
+ */
+
+ /**
+ * Applies prototype properties from the supplier to the receiver.
+ * The receiver can be a constructor or an instance.
+ * @method augment
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param args {Array | Any} arg or arguments to apply to the supplier
+ * constructor when initializing.
+ * @return {object} the augmented object
+ *
+ * @todo constructor optional?
+ * @todo understanding what an instance is augmented with
+ * @TODO best practices for overriding sequestered methods.
+ */
+ Y.augment = function(r, s, ov, wl, args) {
+ var sProto = s.prototype,
+ newProto = null,
+ construct = s,
+ a = (args) ? Y.Array(args) : [],
+ rProto = r.prototype,
+ target = rProto || r,
+ applyConstructor = false,
+ sequestered, replacements, i;
+
+ // working on a class, so apply constructor infrastructure
+ if (rProto && construct) {
+ sequestered = {};
+ replacements = {};
+ newProto = {};
+
+ // sequester all of the functions in the supplier and replace with
+ // one that will restore all of them.
+ Y.each(sProto, function(v, k) {
+ replacements[k] = function() {
+
+// overwrite the prototype with all of the sequestered functions,
+// but only if it hasn't been overridden
+ for (i in sequestered) {
+ if (sequestered.hasOwnProperty(i) && (this[i] === replacements[i])) {
+ this[i] = sequestered[i];
+ }
+ }
+
+ // apply the constructor
+ construct.apply(this, a);
+
+ // apply the original sequestered function
+ return sequestered[k].apply(this, arguments);
+ };
+
+ if ((!wl || (k in wl)) && (ov || !(k in this))) {
+ if (L.isFunction(v)) {
+ // sequester the function
+ sequestered[k] = v;
+
+// replace the sequestered function with a function that will
+// restore all sequestered functions and exectue the constructor.
+ this[k] = replacements[k];
+ } else {
+ this[k] = v;
+ }
+
+ }
+
+ }, newProto, true);
+
+ // augmenting an instance, so apply the constructor immediately
+ } else {
+ applyConstructor = true;
+ }
+
+ Y.mix(target, newProto || sProto, ov, wl);
+
+ if (applyConstructor) {
+ s.apply(target, a);
+ }
+
+ return r;
+ };
+
+ /**
+ * Applies object properties from the supplier to the receiver. If
+ * the target has the property, and the property is an object, the target
+ * object will be augmented with the supplier's value. If the property
+ * is an array, the suppliers value will be appended to the target.
+ * @method aggregate
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @return {object} the extended object
+ */
+ Y.aggregate = function(r, s, ov, wl) {
+ return Y.mix(r, s, ov, wl, 0, true);
+ };
+
+ /**
+ * Utility to set up the prototype, constructor and superclass properties to
+ * support an inheritance strategy that can chain constructors and methods.
+ * Static members will not be inherited.
+ *
+ * @method extend
+ * @param {Function} r the object to modify
+ * @param {Function} s the object to inherit
+ * @param {Object} px prototype properties to add/override
+ * @param {Object} sx static properties to add/override
+ * @return {YUI} the YUI instance
+ */
+ Y.extend = function(r, s, px, sx) {
+ if (!s||!r) {
+ // @TODO error symbols
+ Y.error("extend failed, verify dependencies");
+ }
+
+ var sp = s.prototype, rp=Y.Object(sp);
+ r.prototype=rp;
+
+ rp.constructor=r;
+ r.superclass=sp;
+
+ // assign constructor property
+ if (s != Object && sp.constructor == OP.constructor) {
+ sp.constructor=s;
+ }
+
+ // add prototype overrides
+ if (px) {
+ Y.mix(rp, px, true);
+ }
+
+ // add object overrides
+ if (sx) {
+ Y.mix(r, sx, true);
+ }
+
+ return r;
+ };
+
+ /**
+ * Executes the supplied function for each item in
+ * a collection. Supports arrays, objects, and
+ * Y.NodeLists
+ * @method each
+ * @param o the object to iterate
+ * @param f the function to execute. This function
+ * receives the value, key, and object as parameters
+ * @param proto if true, prototype properties are
+ * iterated on objects
+ * @return {YUI} the YUI instance
+ */
+ Y.each = function(o, f, c, proto) {
+
+ if (o.each && o.item) {
+ return o.each.call(o, f, c);
+ } else {
+ switch (A.test(o)) {
+ case 1:
+ return A.each(o, f, c);
+ case 2:
+ return A.each(Y.Array(o, 0, true), f, c);
+ default:
+ return Y.Object.each(o, f, c, proto);
+ }
+ }
+
+ // return Y.Object.each(o, f, c);
+ };
+
+ // Y.each = function(o, f, c, proto) {
+ // return dispatch(o, f, c, proto, 'each');
+ // };
+
+ /*
+ * Executes the supplied function for each item in
+ * a collection. The operation stops if the function
+ * returns true. Supports arrays, objects, and
+ * Y.NodeLists.
+ * @method some
+ * @param o the object to iterate
+ * @param f the function to execute. This function
+ * receives the value, key, and object as parameters
+ * @param proto if true, prototype properties are
+ * iterated on objects
+ * @return {boolean} true if the function ever returns true, false otherwise
+ */
+ // Y.some = function(o, f, c, proto) {
+ // return dispatch(o, f, c, proto, 'some');
+ // };
+
+ /**
+ * Deep obj/array copy. Functions are cloned with Y.bind.
+ * Array-like objects are treated as arrays.
+ * Primitives are returned untouched. Optionally, a
+ * function can be provided to handle other data types,
+ * filter keys, validate values, etc.
+ *
+ * @method clone
+ * @param o what to clone
+ * @param safe {boolean} if true, objects will not have prototype
+ * items from the source. If false, they will. In this case, the
+ * original is initially protected, but the clone is not completely immune
+ * from changes to the source object prototype. Also, cloned prototype
+ * items that are deleted from the clone will result in the value
+ * of the source prototype being exposed. If operating on a non-safe
+ * clone, items should be nulled out rather than deleted.
+ * @TODO review
+ * @param f optional function to apply to each item in a collection;
+ * it will be executed prior to applying the value to
+ * the new object. Return false to prevent the copy.
+ * @param c optional execution context for f
+ * @param owner Owner object passed when clone is iterating an
+ * object. Used to set up context for cloned functions.
+ * @return {Array|Object} the cloned object
+ */
+ Y.clone = function(o, safe, f, c, owner, cloned) {
+
+ if (!L.isObject(o)) {
+ return o;
+ }
+
+ var o2, marked = cloned || {}, stamp;
+
+ switch (L.type(o)) {
+ case 'date':
+ return new Date(o);
+ case 'regexp':
+ return new RegExp(o.source);
+ case 'function':
+ o2 = Y.bind(o, owner);
+ break;
+ case 'array':
+ o2 = [];
+ break;
+ default:
+
+ // #2528250 only one clone of a given object should be created.
+ if (o[CLONE_MARKER]) {
+ return marked[o[CLONE_MARKER]];
+ }
+
+ stamp = Y.guid();
+
+ o2 = (safe) ? {} : Y.Object(o);
+
+ o[CLONE_MARKER] = stamp;
+ marked[stamp] = o;
+ }
+
+ // #2528250 don't try to clone element properties
+ if (!o.addEventListener && !o.attachEvent) {
+ Y.each(o, function(v, k) {
+ if (!f || (f.call(c || this, v, k, this, o) !== false)) {
+ if (k !== CLONE_MARKER) {
+ this[k] = Y.clone(v, safe, f, c, owner || o, marked);
+ }
+ }
+ }, o2);
+ }
+
+ if (!cloned) {
+ Y.each(marked, function(v, k) {
+ delete v[CLONE_MARKER];
+ });
+ marked = null;
+ }
+
+ return o2;
+ };
+
+
+ /**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the beginning of the arguments collection the
+ * supplied to the function.
+ *
+ * @method bind
+ * @param f {Function|String} the function to bind, or a function name
+ * to execute on the context object
+ * @param c the execution context
+ * @param args* 0..n arguments to include before the arguments the
+ * function is executed with.
+ * @return {function} the wrapped function
+ */
+ Y.bind = function(f, c) {
+ var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
+ return function () {
+ var fn = L.isString(f) ? c[f] : f,
+ args = (xargs) ? xargs.concat(Y.Array(arguments, 0, true)) : arguments;
+ return fn.apply(c || fn, args);
+ };
+ };
+
+ /**
+ * Returns a function that will execute the supplied function in the
+ * supplied object's context, optionally adding any additional
+ * supplied parameters to the end of the arguments the function
+ * is executed with.
+ *
+ * @method rbind
+ * @param f {Function|String} the function to bind, or a function name
+ * to execute on the context object
+ * @param c the execution context
+ * @param args* 0..n arguments to append to the end of arguments collection
+ * supplied to the function
+ * @return {function} the wrapped function
+ */
+ Y.rbind = function(f, c) {
+ var xargs = arguments.length > 2 ? Y.Array(arguments, 2, true) : null;
+ return function () {
+ var fn = L.isString(f) ? c[f] : f,
+ args = (xargs) ? Y.Array(arguments, 0, true).concat(xargs) : arguments;
+ return fn.apply(c || fn, args);
+ };
+ };
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-overlay {
+ position:absolute;
+}
+
+.yui-overlay-hidden {
+ visibility:hidden
+}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-overlay{position:absolute;}.yui-overlay-hidden{visibility:hidden;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('overlay', function(Y) {
+
+/**
+ * Provides a basic Overlay widget, with Standard Module content support. The Overlay widget
+ * provides Page XY positioning support, alignment and centering support along with basic
+ * stackable support (z-index and shimming).
+ *
+ * @module overlay
+ */
+
+/**
+ * A basic Overlay Widget, which can be positioned based on Page XY co-ordinates and is stackable (z-index support).
+ * It also provides alignment and centering support and uses a standard module format for it's content, with header,
+ * body and footer section support.
+ *
+ * @class Overlay
+ * @constructor
+ * @extends Widget
+ * @uses WidgetPosition
+ * @uses WidgetStack
+ * @uses WidgetPositionExt
+ * @uses WidgetStdMod
+ * @param {Object} object The user configuration for the instance.
+ */
+Y.Overlay = Y.Base.build("overlay", Y.Widget, [Y.WidgetPosition, Y.WidgetStack, Y.WidgetPositionExt, Y.WidgetStdMod]);
+
+
+
+}, '3.0.0' ,{requires:['widget', 'widget-position', 'widget-stack', 'widget-position-ext', 'widget-stdmod']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("overlay",function(A){A.Overlay=A.Base.build("overlay",A.Widget,[A.WidgetPosition,A.WidgetStack,A.WidgetPositionExt,A.WidgetStdMod]);},"3.0.0",{requires:["widget","widget-position","widget-stack","widget-position-ext","widget-stdmod"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('overlay', function(Y) {
+
+/**
+ * Provides a basic Overlay widget, with Standard Module content support. The Overlay widget
+ * provides Page XY positioning support, alignment and centering support along with basic
+ * stackable support (z-index and shimming).
+ *
+ * @module overlay
+ */
+
+/**
+ * A basic Overlay Widget, which can be positioned based on Page XY co-ordinates and is stackable (z-index support).
+ * It also provides alignment and centering support and uses a standard module format for it's content, with header,
+ * body and footer section support.
+ *
+ * @class Overlay
+ * @constructor
+ * @extends Widget
+ * @uses WidgetPosition
+ * @uses WidgetStack
+ * @uses WidgetPositionExt
+ * @uses WidgetStdMod
+ * @param {Object} object The user configuration for the instance.
+ */
+Y.Overlay = Y.Base.build("overlay", Y.Widget, [Y.WidgetPosition, Y.WidgetStack, Y.WidgetPositionExt, Y.WidgetStdMod]);
+
+
+
+}, '3.0.0' ,{requires:['widget', 'widget-position', 'widget-stack', 'widget-position-ext', 'widget-stdmod']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('plugin', function(Y) {
+
+ /**
+ * Provides the base Plugin class, which plugin developers should extend, when creating custom plugins
+ *
+ * @module plugin
+ */
+
+ /**
+ * The base class for all Plugin instances.
+ *
+ * @class Plugin.Base
+ * @extends Base
+ * @param {Object} config Configuration object with property name/value pairs.
+ */
+ function Plugin(config) {
+ Plugin.superclass.constructor.apply(this, arguments);
+ }
+
+ /**
+ * Object defining the set of attributes supported by the Plugin.Base class
+ *
+ * @property Plugin.Base.ATTRS
+ * @type Object
+ * @static
+ */
+ Plugin.ATTRS = {
+
+ /**
+ * The plugin's host object.
+ *
+ * @attribute host
+ * @writeonce
+ * @type Plugin.Host
+ */
+ host : {
+ writeOnce: true
+ }
+ };
+
+ /**
+ * The string identifying the Plugin.Base class. Plugins extending
+ * Plugin.Base should set their own NAME value.
+ *
+ * @property Plugin.Base.NAME
+ * @type String
+ * @static
+ */
+ Plugin.NAME = 'plugin';
+
+ /**
+ * The name of the property the the plugin will be attached to
+ * when plugged into a Plugin Host. Plugins extending Plugin.Base,
+ * should set their own NS value.
+ *
+ * @property Plugin.NS
+ * @type String
+ * @static
+ */
+ Plugin.NS = 'plugin';
+
+ Y.extend(Plugin, Y.Base, {
+
+ /**
+ * The list of event handles for event listeners or AOP injected methods
+ * applied by the plugin to the host object.
+ *
+ * @property _handles
+ * @private
+ * @type Array
+ * @value null
+ */
+ _handles: null,
+
+ /**
+ * Initializer lifecycle implementation.
+ *
+ * @method initializer
+ * @param {Object} config Configuration object with property name/value pairs.
+ */
+ initializer : function(config) {
+ this._handles = [];
+ if (!this.get("host")) { Y.log('No host defined for plugin ' + this, 'warn', 'Plugin');}
+ Y.log('Initializing: ' + this.constructor.NAME, 'info', 'Plugin');
+ },
+
+ /**
+ * Destructor lifecycle implementation.
+ *
+ * Removes any event listeners or injected methods applied by the Plugin
+ *
+ * @method destructor
+ */
+ destructor: function() {
+ // remove all handles
+ if (this._handles) {
+ for (var i = 0, l = this._handles.length; i < l; i++) {
+ this._handles[i].detach();
+ }
+ }
+ },
+
+ /**
+ * Listens for the "on" moment of events fired by the host,
+ * or injects code "before" a given method on the host.
+ *
+ * @method doBefore
+ *
+ * @param sFn {String} The event to listen for, or method to inject logic before.
+ * @param fn {Function} The handler function. For events, the "on" moment listener. For methods, the function to execute before the given method is executed.
+ * @param context {Object} An optional context to call the handler with. The default context is the plugin instance.
+ * @return handle {EventHandle} The detach handle for the handler.
+ */
+ doBefore: function(sFn, fn, context) {
+ var host = this.get("host"),
+ handle;
+
+ context = context || this;
+
+ if (sFn in host) { // method
+ handle = Y.Do.before(fn, host, sFn, context);
+ } else if (host.on) { // event
+ handle = host.on(sFn, fn, context);
+ }
+
+ this._handles.push(handle);
+ return handle;
+ },
+
+ /**
+ * Listens for the "after" moment of events fired by the host,
+ * or injects code "after" a given method on the host.
+ *
+ * @method doAfter
+ *
+ * @param sFn {String} The event to listen for, or method to inject logic after.
+ * @param fn {Function} The handler function. For events, the "after" moment listener. For methods, the function to execute after the given method is executed.
+ * @param context {Object} An optional context to call the handler with. The default context is the plugin instance.
+ * @return handle {EventHandle} The detach handle for the handler.
+ */
+ doAfter: function(sFn, fn, context) {
+ var host = this.get("host"),
+ handle;
+
+ context = context || this;
+
+ if (sFn in host) { // method
+ handle = Y.Do.after(fn, host, sFn, context);
+ } else if (host.after) { // event
+ handle = host.after(sFn, fn, context);
+ }
+
+ this._handles.push(handle);
+ return handle;
+ },
+
+ toString: function() {
+ return this.constructor.NAME + '[' + this.constructor.NS + ']';
+ }
+ });
+
+ Y.namespace("Plugin").Base = Plugin;
+
+
+}, '3.0.0' ,{requires:['base-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("plugin",function(B){function A(C){A.superclass.constructor.apply(this,arguments);}A.ATTRS={host:{writeOnce:true}};A.NAME="plugin";A.NS="plugin";B.extend(A,B.Base,{_handles:null,initializer:function(C){this._handles=[];},destructor:function(){if(this._handles){for(var D=0,C=this._handles.length;D<C;D++){this._handles[D].detach();}}},doBefore:function(G,D,C){var E=this.get("host"),F;C=C||this;if(G in E){F=B.Do.before(D,E,G,C);}else{if(E.on){F=E.on(G,D,C);}}this._handles.push(F);return F;},doAfter:function(G,D,C){var E=this.get("host"),F;C=C||this;if(G in E){F=B.Do.after(D,E,G,C);}else{if(E.after){F=E.after(G,D,C);}}this._handles.push(F);return F;},toString:function(){return this.constructor.NAME+"["+this.constructor.NS+"]";}});B.namespace("Plugin").Base=A;},"3.0.0",{requires:["base-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('plugin', function(Y) {
+
+ /**
+ * Provides the base Plugin class, which plugin developers should extend, when creating custom plugins
+ *
+ * @module plugin
+ */
+
+ /**
+ * The base class for all Plugin instances.
+ *
+ * @class Plugin.Base
+ * @extends Base
+ * @param {Object} config Configuration object with property name/value pairs.
+ */
+ function Plugin(config) {
+ Plugin.superclass.constructor.apply(this, arguments);
+ }
+
+ /**
+ * Object defining the set of attributes supported by the Plugin.Base class
+ *
+ * @property Plugin.Base.ATTRS
+ * @type Object
+ * @static
+ */
+ Plugin.ATTRS = {
+
+ /**
+ * The plugin's host object.
+ *
+ * @attribute host
+ * @writeonce
+ * @type Plugin.Host
+ */
+ host : {
+ writeOnce: true
+ }
+ };
+
+ /**
+ * The string identifying the Plugin.Base class. Plugins extending
+ * Plugin.Base should set their own NAME value.
+ *
+ * @property Plugin.Base.NAME
+ * @type String
+ * @static
+ */
+ Plugin.NAME = 'plugin';
+
+ /**
+ * The name of the property the the plugin will be attached to
+ * when plugged into a Plugin Host. Plugins extending Plugin.Base,
+ * should set their own NS value.
+ *
+ * @property Plugin.NS
+ * @type String
+ * @static
+ */
+ Plugin.NS = 'plugin';
+
+ Y.extend(Plugin, Y.Base, {
+
+ /**
+ * The list of event handles for event listeners or AOP injected methods
+ * applied by the plugin to the host object.
+ *
+ * @property _handles
+ * @private
+ * @type Array
+ * @value null
+ */
+ _handles: null,
+
+ /**
+ * Initializer lifecycle implementation.
+ *
+ * @method initializer
+ * @param {Object} config Configuration object with property name/value pairs.
+ */
+ initializer : function(config) {
+ this._handles = [];
+ },
+
+ /**
+ * Destructor lifecycle implementation.
+ *
+ * Removes any event listeners or injected methods applied by the Plugin
+ *
+ * @method destructor
+ */
+ destructor: function() {
+ // remove all handles
+ if (this._handles) {
+ for (var i = 0, l = this._handles.length; i < l; i++) {
+ this._handles[i].detach();
+ }
+ }
+ },
+
+ /**
+ * Listens for the "on" moment of events fired by the host,
+ * or injects code "before" a given method on the host.
+ *
+ * @method doBefore
+ *
+ * @param sFn {String} The event to listen for, or method to inject logic before.
+ * @param fn {Function} The handler function. For events, the "on" moment listener. For methods, the function to execute before the given method is executed.
+ * @param context {Object} An optional context to call the handler with. The default context is the plugin instance.
+ * @return handle {EventHandle} The detach handle for the handler.
+ */
+ doBefore: function(sFn, fn, context) {
+ var host = this.get("host"),
+ handle;
+
+ context = context || this;
+
+ if (sFn in host) { // method
+ handle = Y.Do.before(fn, host, sFn, context);
+ } else if (host.on) { // event
+ handle = host.on(sFn, fn, context);
+ }
+
+ this._handles.push(handle);
+ return handle;
+ },
+
+ /**
+ * Listens for the "after" moment of events fired by the host,
+ * or injects code "after" a given method on the host.
+ *
+ * @method doAfter
+ *
+ * @param sFn {String} The event to listen for, or method to inject logic after.
+ * @param fn {Function} The handler function. For events, the "after" moment listener. For methods, the function to execute after the given method is executed.
+ * @param context {Object} An optional context to call the handler with. The default context is the plugin instance.
+ * @return handle {EventHandle} The detach handle for the handler.
+ */
+ doAfter: function(sFn, fn, context) {
+ var host = this.get("host"),
+ handle;
+
+ context = context || this;
+
+ if (sFn in host) { // method
+ handle = Y.Do.after(fn, host, sFn, context);
+ } else if (host.after) { // event
+ handle = host.after(sFn, fn, context);
+ }
+
+ this._handles.push(handle);
+ return handle;
+ },
+
+ toString: function() {
+ return this.constructor.NAME + '[' + this.constructor.NS + ']';
+ }
+ });
+
+ Y.namespace("Plugin").Base = Plugin;
+
+
+}, '3.0.0' ,{requires:['base-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('pluginhost', function(Y) {
+
+ /**
+ * Provides the augmentable PluginHost interface, which can be added to any class.
+ * @module pluginhost
+ */
+
+ /**
+ * <p>
+ * An augmentable class, which provides the augmented class with the ability to host plugins.
+ * It adds <a href="#method_plug">plug</a> and <a href="#method_unplug">unplug</a> methods to the augmented class, which can
+ * be used to add or remove plugins from instances of the class.
+ * </p>
+ *
+ * <p>Plugins can also be added through the constructor configuration object passed to the host class' constructor using
+ * the "plugins" property. Supported values for the "plugins" property are those defined by the <a href="#method_plug">plug</a> method.
+ *
+ * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host):
+ * <xmp>
+ * var o = new Overlay({plugins: [ AnimPlugin, {fn:IOPlugin, cfg:{section:"header"}}]});
+ * </xmp>
+ * </p>
+ * <p>
+ * Plug.Host's protected <a href="#method_initPlugins">_initPlugins</a> and <a href="#method_destroyPlugins">_destroyPlugins</a>
+ * methods should be invoked by the host class at the appropriate point in the host's lifecyle.
+ * </p>
+ *
+ * @class Plugin.Host
+ */
+
+ var L = Y.Lang;
+
+ function PluginHost() {
+ this._plugins = {};
+ }
+
+ PluginHost.prototype = {
+
+ /**
+ * Adds a plugin to the host object. This will instantiate the
+ * plugin and attach it to the configured namespace on the host object.
+ *
+ * @method plug
+ * @chainable
+ * @param p {Function | Object |Array} Accepts the plugin class, or an
+ * object with a "fn" property specifying the plugin class and
+ * a "cfg" property specifying the configuration for the Plugin.
+ * <p>
+ * Additionally an Array can also be passed in, with the above function or
+ * object values, allowing the user to add multiple plugins in a single call.
+ * </p>
+ * @param config (Optional) If the first argument is the plugin class, the second argument
+ * can be the configuration for the plugin.
+ * @return {Base} A reference to the host object
+ */
+
+ plug: function(p, config) {
+ if (p) {
+ if (L.isFunction(p)) {
+ this._plug(p, config);
+ } else if (L.isArray(p)) {
+ for (var i = 0, ln = p.length; i < ln; i++) {
+ this.plug(p[i]);
+ }
+ } else {
+ this._plug(p.fn, p.cfg);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Removes a plugin from the host object. This will destroy the
+ * plugin instance and delete the namepsace from the host object.
+ *
+ * @method unplug
+ * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided,
+ * all registered plugins are unplugged.
+ * @return {Base} A reference to the host object
+ * @chainable
+ */
+ unplug: function(plugin) {
+ if (plugin) {
+ this._unplug(plugin);
+ } else {
+ var ns;
+ for (ns in this._plugins) {
+ if (this._plugins.hasOwnProperty(ns)) {
+ this._unplug(ns);
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Determines if a plugin has plugged into this host.
+ *
+ * @method hasPlugin
+ * @param {String} ns The plugin's namespace
+ * @return {boolean} returns true, if the plugin has been plugged into this host, false otherwise.
+ */
+ hasPlugin : function(ns) {
+ return (this._plugins[ns] && this[ns]);
+ },
+
+ /**
+ * Initializes static plugins registered on the host (using the
+ * Base.plug static method) and any plugins passed to the
+ * instance through the "plugins" configuration property.
+ *
+ * @method _initPlugins
+ * @param {Config} config The configuration object with property name/value pairs.
+ * @private
+ */
+ _initPlugins: function(config) {
+ this._plugins = this._plugins || {};
+
+ // Class Configuration
+ var classes = (this._getClasses) ? this._getClasses() : [this.constructor],
+ plug = [],
+ unplug = {},
+ constructor, i, classPlug, classUnplug, pluginClassName;
+
+ //TODO: Room for optimization. Can we apply statically/unplug in same pass?
+ for (i = classes.length - 1; i >= 0; i--) {
+ constructor = classes[i];
+
+ classUnplug = constructor._UNPLUG;
+ if (classUnplug) {
+ // subclasses over-write
+ Y.mix(unplug, classUnplug, true);
+ }
+
+ classPlug = constructor._PLUG;
+ if (classPlug) {
+ // subclasses over-write
+ Y.mix(plug, classPlug, true);
+ }
+ }
+
+ for (pluginClassName in plug) {
+ if (plug.hasOwnProperty(pluginClassName)) {
+ if (!unplug[pluginClassName]) {
+ this.plug(plug[pluginClassName]);
+ }
+ }
+ }
+
+ // User Configuration
+ if (config && config.plugins) {
+ this.plug(config.plugins);
+ }
+ },
+
+ /**
+ * Unplugs and destroys all plugins on the host
+ * @method _destroyPlugins
+ * @private
+ */
+ _destroyPlugins: function() {
+ this._unplug();
+ },
+
+ /**
+ * Private method used to instantiate and attach plugins to the host
+ *
+ * @method _plug
+ * @param {Function} PluginClass The plugin class to instantiate
+ * @param {Object} config The configuration object for the plugin
+ * @private
+ */
+ _plug: function(PluginClass, config) {
+ if (PluginClass && PluginClass.NS) {
+ var ns = PluginClass.NS;
+
+ config = config || {};
+ config.host = this;
+
+ if (this.hasPlugin(ns)) {
+ // Update config
+ this[ns].setAttrs(config);
+ } else {
+ // Create new instance
+ this[ns] = new PluginClass(config);
+ this._plugins[ns] = PluginClass;
+ }
+ }
+ },
+
+ /**
+ * Unplugs and destroys a plugin already instantiated with the host.
+ *
+ * @method _unplug
+ * @private
+ * @param {String | Function} plugin The namespace for the plugin, or a plugin class with the static NS property defined.
+ */
+ _unplug : function(plugin) {
+ var ns = plugin,
+ plugins = this._plugins;
+
+ if (L.isFunction(plugin)) {
+ ns = plugin.NS;
+ if (ns && (!plugins[ns] || plugins[ns] !== plugin)) {
+ ns = null;
+ }
+ }
+
+ if (ns) {
+ if (this[ns]) {
+ this[ns].destroy();
+ delete this[ns];
+ }
+ if (plugins[ns]) {
+ delete plugins[ns];
+ }
+ }
+ }
+ };
+
+ /**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of the class by default).
+ *
+ * @method Plugin.Host.plug
+ * @static
+ *
+ * @param {Function} hostClass The host class on which to register the plugins
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+ PluginHost.plug = function(hostClass, plugin, config) {
+ // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ]
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._PLUG = hostClass._PLUG || {};
+
+ if (!L.isArray(plugin)) {
+ if (config) {
+ plugin = {fn:plugin, cfg:config};
+ }
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l;i++) {
+ p = plugin[i];
+ name = p.NAME || p.fn.NAME;
+ hostClass._PLUG[name] = p;
+ }
+ }
+ };
+
+ /**
+ * Unregisters any class level plugins which have been registered by the host class, or any
+ * other class in the hierarchy.
+ *
+ * @method Plugin.Host.unplug
+ * @static
+ *
+ * @param {Function} hostClass The host class from which to unregister the plugins
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+ PluginHost.unplug = function(hostClass, plugin) {
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._UNPLUG = hostClass._UNPLUG || {};
+
+ if (!L.isArray(plugin)) {
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l; i++) {
+ p = plugin[i];
+ name = p.NAME;
+ if (!hostClass._PLUG[name]) {
+ hostClass._UNPLUG[name] = p;
+ } else {
+ delete hostClass._PLUG[name];
+ }
+ }
+ }
+ };
+
+ Y.namespace("Plugin").Host = PluginHost;
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("pluginhost",function(C){var A=C.Lang;function B(){this._plugins={};}B.prototype={plug:function(G,D){if(G){if(A.isFunction(G)){this._plug(G,D);}else{if(A.isArray(G)){for(var E=0,F=G.length;E<F;E++){this.plug(G[E]);}}else{this._plug(G.fn,G.cfg);}}}return this;},unplug:function(E){if(E){this._unplug(E);}else{var D;for(D in this._plugins){if(this._plugins.hasOwnProperty(D)){this._unplug(D);}}}return this;},hasPlugin:function(D){return(this._plugins[D]&&this[D]);},_initPlugins:function(E){this._plugins=this._plugins||{};var G=(this._getClasses)?this._getClasses():[this.constructor],D=[],H={},F,I,K,L,J;for(I=G.length-1;I>=0;I--){F=G[I];L=F._UNPLUG;if(L){C.mix(H,L,true);}K=F._PLUG;if(K){C.mix(D,K,true);}}for(J in D){if(D.hasOwnProperty(J)){if(!H[J]){this.plug(D[J]);}}}if(E&&E.plugins){this.plug(E.plugins);}},_destroyPlugins:function(){this._unplug();},_plug:function(F,D){if(F&&F.NS){var E=F.NS;D=D||{};D.host=this;if(this.hasPlugin(E)){this[E].setAttrs(D);}else{this[E]=new F(D);this._plugins[E]=F;}}},_unplug:function(F){var E=F,D=this._plugins;if(A.isFunction(F)){E=F.NS;if(E&&(!D[E]||D[E]!==F)){E=null;}}if(E){if(this[E]){this[E].destroy();delete this[E];}if(D[E]){delete D[E];}}}};B.plug=function(E,I,G){var J,H,D,F;if(E!==C.Base){E._PLUG=E._PLUG||{};if(!A.isArray(I)){if(G){I={fn:I,cfg:G};}I=[I];}for(H=0,D=I.length;H<D;H++){J=I[H];F=J.NAME||J.fn.NAME;E._PLUG[F]=J;}}};B.unplug=function(E,H){var I,G,D,F;if(E!==C.Base){E._UNPLUG=E._UNPLUG||{};if(!A.isArray(H)){H=[H];}for(G=0,D=H.length;G<D;G++){I=H[G];F=I.NAME;if(!E._PLUG[F]){E._UNPLUG[F]=I;}else{delete E._PLUG[F];}}}};C.namespace("Plugin").Host=B;},"3.0.0",{requires:["yui-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('pluginhost', function(Y) {
+
+ /**
+ * Provides the augmentable PluginHost interface, which can be added to any class.
+ * @module pluginhost
+ */
+
+ /**
+ * <p>
+ * An augmentable class, which provides the augmented class with the ability to host plugins.
+ * It adds <a href="#method_plug">plug</a> and <a href="#method_unplug">unplug</a> methods to the augmented class, which can
+ * be used to add or remove plugins from instances of the class.
+ * </p>
+ *
+ * <p>Plugins can also be added through the constructor configuration object passed to the host class' constructor using
+ * the "plugins" property. Supported values for the "plugins" property are those defined by the <a href="#method_plug">plug</a> method.
+ *
+ * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host):
+ * <xmp>
+ * var o = new Overlay({plugins: [ AnimPlugin, {fn:IOPlugin, cfg:{section:"header"}}]});
+ * </xmp>
+ * </p>
+ * <p>
+ * Plug.Host's protected <a href="#method_initPlugins">_initPlugins</a> and <a href="#method_destroyPlugins">_destroyPlugins</a>
+ * methods should be invoked by the host class at the appropriate point in the host's lifecyle.
+ * </p>
+ *
+ * @class Plugin.Host
+ */
+
+ var L = Y.Lang;
+
+ function PluginHost() {
+ this._plugins = {};
+ }
+
+ PluginHost.prototype = {
+
+ /**
+ * Adds a plugin to the host object. This will instantiate the
+ * plugin and attach it to the configured namespace on the host object.
+ *
+ * @method plug
+ * @chainable
+ * @param p {Function | Object |Array} Accepts the plugin class, or an
+ * object with a "fn" property specifying the plugin class and
+ * a "cfg" property specifying the configuration for the Plugin.
+ * <p>
+ * Additionally an Array can also be passed in, with the above function or
+ * object values, allowing the user to add multiple plugins in a single call.
+ * </p>
+ * @param config (Optional) If the first argument is the plugin class, the second argument
+ * can be the configuration for the plugin.
+ * @return {Base} A reference to the host object
+ */
+
+ plug: function(p, config) {
+ if (p) {
+ if (L.isFunction(p)) {
+ this._plug(p, config);
+ } else if (L.isArray(p)) {
+ for (var i = 0, ln = p.length; i < ln; i++) {
+ this.plug(p[i]);
+ }
+ } else {
+ this._plug(p.fn, p.cfg);
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Removes a plugin from the host object. This will destroy the
+ * plugin instance and delete the namepsace from the host object.
+ *
+ * @method unplug
+ * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided,
+ * all registered plugins are unplugged.
+ * @return {Base} A reference to the host object
+ * @chainable
+ */
+ unplug: function(plugin) {
+ if (plugin) {
+ this._unplug(plugin);
+ } else {
+ var ns;
+ for (ns in this._plugins) {
+ if (this._plugins.hasOwnProperty(ns)) {
+ this._unplug(ns);
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Determines if a plugin has plugged into this host.
+ *
+ * @method hasPlugin
+ * @param {String} ns The plugin's namespace
+ * @return {boolean} returns true, if the plugin has been plugged into this host, false otherwise.
+ */
+ hasPlugin : function(ns) {
+ return (this._plugins[ns] && this[ns]);
+ },
+
+ /**
+ * Initializes static plugins registered on the host (using the
+ * Base.plug static method) and any plugins passed to the
+ * instance through the "plugins" configuration property.
+ *
+ * @method _initPlugins
+ * @param {Config} config The configuration object with property name/value pairs.
+ * @private
+ */
+ _initPlugins: function(config) {
+ this._plugins = this._plugins || {};
+
+ // Class Configuration
+ var classes = (this._getClasses) ? this._getClasses() : [this.constructor],
+ plug = [],
+ unplug = {},
+ constructor, i, classPlug, classUnplug, pluginClassName;
+
+ //TODO: Room for optimization. Can we apply statically/unplug in same pass?
+ for (i = classes.length - 1; i >= 0; i--) {
+ constructor = classes[i];
+
+ classUnplug = constructor._UNPLUG;
+ if (classUnplug) {
+ // subclasses over-write
+ Y.mix(unplug, classUnplug, true);
+ }
+
+ classPlug = constructor._PLUG;
+ if (classPlug) {
+ // subclasses over-write
+ Y.mix(plug, classPlug, true);
+ }
+ }
+
+ for (pluginClassName in plug) {
+ if (plug.hasOwnProperty(pluginClassName)) {
+ if (!unplug[pluginClassName]) {
+ this.plug(plug[pluginClassName]);
+ }
+ }
+ }
+
+ // User Configuration
+ if (config && config.plugins) {
+ this.plug(config.plugins);
+ }
+ },
+
+ /**
+ * Unplugs and destroys all plugins on the host
+ * @method _destroyPlugins
+ * @private
+ */
+ _destroyPlugins: function() {
+ this._unplug();
+ },
+
+ /**
+ * Private method used to instantiate and attach plugins to the host
+ *
+ * @method _plug
+ * @param {Function} PluginClass The plugin class to instantiate
+ * @param {Object} config The configuration object for the plugin
+ * @private
+ */
+ _plug: function(PluginClass, config) {
+ if (PluginClass && PluginClass.NS) {
+ var ns = PluginClass.NS;
+
+ config = config || {};
+ config.host = this;
+
+ if (this.hasPlugin(ns)) {
+ // Update config
+ this[ns].setAttrs(config);
+ } else {
+ // Create new instance
+ this[ns] = new PluginClass(config);
+ this._plugins[ns] = PluginClass;
+ }
+ }
+ },
+
+ /**
+ * Unplugs and destroys a plugin already instantiated with the host.
+ *
+ * @method _unplug
+ * @private
+ * @param {String | Function} plugin The namespace for the plugin, or a plugin class with the static NS property defined.
+ */
+ _unplug : function(plugin) {
+ var ns = plugin,
+ plugins = this._plugins;
+
+ if (L.isFunction(plugin)) {
+ ns = plugin.NS;
+ if (ns && (!plugins[ns] || plugins[ns] !== plugin)) {
+ ns = null;
+ }
+ }
+
+ if (ns) {
+ if (this[ns]) {
+ this[ns].destroy();
+ delete this[ns];
+ }
+ if (plugins[ns]) {
+ delete plugins[ns];
+ }
+ }
+ }
+ };
+
+ /**
+ * Registers plugins to be instantiated at the class level (plugins
+ * which should be plugged into every instance of the class by default).
+ *
+ * @method Plugin.Host.plug
+ * @static
+ *
+ * @param {Function} hostClass The host class on which to register the plugins
+ * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined)
+ * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin
+ */
+ PluginHost.plug = function(hostClass, plugin, config) {
+ // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ]
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._PLUG = hostClass._PLUG || {};
+
+ if (!L.isArray(plugin)) {
+ if (config) {
+ plugin = {fn:plugin, cfg:config};
+ }
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l;i++) {
+ p = plugin[i];
+ name = p.NAME || p.fn.NAME;
+ hostClass._PLUG[name] = p;
+ }
+ }
+ };
+
+ /**
+ * Unregisters any class level plugins which have been registered by the host class, or any
+ * other class in the hierarchy.
+ *
+ * @method Plugin.Host.unplug
+ * @static
+ *
+ * @param {Function} hostClass The host class from which to unregister the plugins
+ * @param {Function | Array} plugin The plugin class, or an array of plugin classes
+ */
+ PluginHost.unplug = function(hostClass, plugin) {
+ var p, i, l, name;
+
+ if (hostClass !== Y.Base) {
+ hostClass._UNPLUG = hostClass._UNPLUG || {};
+
+ if (!L.isArray(plugin)) {
+ plugin = [plugin];
+ }
+
+ for (i = 0, l = plugin.length; i < l; i++) {
+ p = plugin[i];
+ name = p.NAME;
+ if (!hostClass._PLUG[name]) {
+ hostClass._UNPLUG[name] = p;
+ } else {
+ delete hostClass._PLUG[name];
+ }
+ }
+ }
+ };
+
+ Y.namespace("Plugin").Host = PluginHost;
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('profiler', function(Y) {
+
+ /**
+ * The YUI JavaScript profiler.
+ * @module profiler
+ * @requires yui
+ */
+
+ //-------------------------------------------------------------------------
+ // Private Variables and Functions
+ //-------------------------------------------------------------------------
+
+ var container = {}, //Container object on which to put the original unprofiled methods.
+ report = {}, //Profiling information for functions
+ stopwatches = {}, //Additional stopwatch information
+
+ WATCH_STARTED = 0,
+ WATCH_STOPPED = 1,
+ WATCH_PAUSED = 2,
+
+ //shortcuts
+ L = Y.Lang;
+
+ /* (intentionally not documented)
+ * Creates a report object with the given name.
+ * @param {String} name The name to store for the report object.
+ * @return {Void}
+ * @method createReport
+ * @private
+ */
+ function createReport(name){
+ report[name] = {
+ calls: 0,
+ max: 0,
+ min: 0,
+ avg: 0,
+ points: []
+ };
+ return report[name];
+ }
+
+ /* (intentionally not documented)
+ * Called when a method ends execution. Marks the start and end time of the
+ * method so it can calculate how long the function took to execute. Also
+ * updates min/max/avg calculations for the function.
+ * @param {String} name The name of the function to mark as stopped.
+ * @param {int} duration The number of milliseconds it took the function to
+ * execute.
+ * @return {Void}
+ * @method saveDataPoint
+ * @private
+ * @static
+ */
+ function saveDataPoint(name, duration){
+
+ //get the function data
+ var functionData /*:Object*/ = report[name];
+
+ //just in case clear() was called
+ if (!functionData){
+ functionData = createReport(name);
+ }
+
+ //increment the calls
+ functionData.calls++;
+ functionData.points.push(duration);
+
+ //if it's already been called at least once, do more complex calculations
+ if (functionData.calls > 1) {
+ functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;
+ functionData.min = Math.min(functionData.min, duration);
+ functionData.max = Math.max(functionData.max, duration);
+ } else {
+ functionData.avg = duration;
+ functionData.min = duration;
+ functionData.max = duration;
+ }
+
+ }
+
+ //-------------------------------------------------------------------------
+ // Public Interface
+ //-------------------------------------------------------------------------
+
+ /**
+ * Profiles functions in JavaScript.
+ * @class Profiler
+ * @static
+ */
+ Y.Profiler = {
+
+ //-------------------------------------------------------------------------
+ // Utility Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Removes all report data from the profiler.
+ * @param {String} name (Optional) The name of the report to clear. If
+ * omitted, then all report data is cleared.
+ * @return {Void}
+ * @method clear
+ * @static
+ */
+ clear: function(name){
+ if (L.isString(name)){
+ delete report[name];
+ delete stopwatches[name];
+ } else {
+ report = {};
+ stopwatches = {};
+ }
+ },
+
+ /**
+ * Returns the uninstrumented version of a function/object.
+ * @param {String} name The name of the function/object to retrieve.
+ * @return {Function|Object} The uninstrumented version of a function/object.
+ * @method getOriginal
+ * @static
+ */
+ getOriginal: function(name){
+ return container[name];
+ },
+
+ /**
+ * Instruments a method to have profiling calls.
+ * @param {String} name The name of the report for the function.
+ * @param {Function} method The function to instrument.
+ * @return {Function} An instrumented version of the function.
+ * @method instrument
+ * @static
+ */
+ instrument: function(name, method){
+
+ //create instrumented version of function
+ var newMethod = function () {
+
+ var start = new Date(),
+ retval = method.apply(this, arguments),
+ stop = new Date();
+
+ saveDataPoint(name, stop-start);
+
+ return retval;
+
+ };
+
+ //copy the function properties over
+ Y.mix(newMethod, method);
+
+ //assign prototype and flag as being profiled
+ newMethod.__yuiProfiled = true;
+ newMethod.prototype = method.prototype;
+
+ //store original method
+ container[name] = method;
+ container[name].__yuiFuncName = name;
+
+ //create the report
+ createReport(name);
+
+ //return the new method
+ return newMethod;
+ },
+
+ //-------------------------------------------------------------------------
+ // Stopwatch Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Pauses profiling information for a given name.
+ * @param {String} name The name of the data point.
+ * @return {Void}
+ * @method pause
+ * @static
+ */
+ pause: function(name){
+ var now = new Date(),
+ stopwatch = stopwatches[name];
+
+ if (stopwatch && stopwatch.state == WATCH_STARTED){
+ stopwatch.total += (now - stopwatch.start);
+ stopwatch.start = 0;
+ stopwatch.state = WATCH_PAUSED;
+ }
+
+ },
+
+ /**
+ * Start profiling information for a given name. The name cannot be the name
+ * of a registered function or object. This is used to start timing for a
+ * particular block of code rather than instrumenting the entire function.
+ * @param {String} name The name of the data point.
+ * @return {Void}
+ * @method start
+ * @static
+ */
+ start: function(name){
+ if(container[name]){
+ throw new Error("Cannot use '" + name + "' for profiling through start(), name is already in use.");
+ } else {
+
+ //create report if necessary
+ if (!report[name]){
+ createReport(name);
+ }
+
+ //create stopwatch object if necessary
+ if (!stopwatches[name]){
+ stopwatches[name] = {
+ state: WATCH_STOPPED,
+ start: 0,
+ total: 0
+ };
+ }
+
+ if (stopwatches[name].state == WATCH_STOPPED){
+ stopwatches[name].state = WATCH_STARTED;
+ stopwatches[name].start = new Date();
+ }
+
+ }
+ },
+
+ /**
+ * Stops profiling information for a given name.
+ * @param {String} name The name of the data point.
+ * @return {Void}
+ * @method stop
+ * @static
+ */
+ stop: function(name){
+ var now = new Date(),
+ stopwatch = stopwatches[name];
+
+ if (stopwatch){
+ if (stopwatch.state == WATCH_STARTED){
+ saveDataPoint(name, stopwatch.total + (now - stopwatch.start));
+ } else if (stopwatch.state == WATCH_PAUSED){
+ saveDataPoint(name, stopwatch.total);
+ }
+
+ //reset stopwatch information
+ stopwatch.start = 0;
+ stopwatch.total = 0;
+ stopwatch.state = WATCH_STOPPED;
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Reporting Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Returns the average amount of time (in milliseconds) that the function
+ * with the given name takes to execute.
+ * @param {String} name The name of the function whose data should be returned.
+ * If an object type method, it should be 'constructor.prototype.methodName';
+ * a normal object method would just be 'object.methodName'.
+ * @return {float} The average time it takes the function to execute.
+ * @method getAverage
+ * @static
+ */
+ getAverage : function (name /*:String*/) /*:float*/ {
+ return report[name].avg;
+ },
+
+ /**
+ * Returns the number of times that the given function has been called.
+ * @param {String} name The name of the function whose data should be returned.
+ * @return {int} The number of times the function was called.
+ * @method getCallCount
+ * @static
+ */
+ getCallCount : function (name /*:String*/) /*:int*/ {
+ return report[name].calls;
+ },
+
+ /**
+ * Returns the maximum amount of time (in milliseconds) that the function
+ * with the given name takes to execute.
+ * @param {String} name The name of the function whose data should be returned.
+ * If an object type method, it should be 'constructor.prototype.methodName';
+ * a normal object method would just be 'object.methodName'.
+ * @return {float} The maximum time it takes the function to execute.
+ * @method getMax
+ * @static
+ */
+ getMax : function (name /*:String*/) /*:int*/ {
+ return report[name].max;
+ },
+
+ /**
+ * Returns the minimum amount of time (in milliseconds) that the function
+ * with the given name takes to execute.
+ * @param {String} name The name of the function whose data should be returned.
+ * If an object type method, it should be 'constructor.prototype.methodName';
+ * a normal object method would just be 'object.methodName'.
+ * @return {float} The minimum time it takes the function to execute.
+ * @method getMin
+ * @static
+ */
+ getMin : function (name /*:String*/) /*:int*/ {
+ return report[name].min;
+ },
+
+ /**
+ * Returns an object containing profiling data for a single function.
+ * The object has an entry for min, max, avg, calls, and points).
+ * @return {Object} An object containing profile data for a given function.
+ * @method getFunctionReport
+ * @static
+ * @deprecated Use getReport() instead.
+ */
+ getFunctionReport : function (name /*:String*/) /*:Object*/ {
+ return report[name];
+ },
+
+ /**
+ * Returns an object containing profiling data for a single function.
+ * The object has an entry for min, max, avg, calls, and points).
+ * @return {Object} An object containing profile data for a given function.
+ * @method getReport
+ * @static
+ */
+ getReport : function (name /*:String*/) /*:Object*/ {
+ return report[name];
+ },
+
+ /**
+ * Returns an object containing profiling data for all of the functions
+ * that were profiled. The object has an entry for each function and
+ * returns all information (min, max, average, calls, etc.) for each
+ * function.
+ * @return {Object} An object containing all profile data.
+ * @static
+ */
+ getFullReport : function (filter /*:Function*/) /*:Object*/ {
+ filter = filter || function(){return true;};
+
+ if (L.isFunction(filter)) {
+ var fullReport = {};
+
+ for (var name in report){
+ if (filter(report[name])){
+ fullReport[name] = report[name];
+ }
+ }
+
+ return fullReport;
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Profiling Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Sets up a constructor for profiling, including all properties and methods on the prototype.
+ * @param {string} name The fully-qualified name of the function including namespace information.
+ * @param {Object} owner (Optional) The object that owns the function (namespace or containing object).
+ * @return {Void}
+ * @method registerConstructor
+ * @static
+ */
+ registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ {
+ this.registerFunction(name, owner, true);
+ },
+
+ /**
+ * Sets up a function for profiling. It essentially overwrites the function with one
+ * that has instrumentation data. This method also creates an entry for the function
+ * in the profile report. The original function is stored on the container object.
+ * @param {String} name The full name of the function including namespacing. This
+ * is the name of the function that is stored in the report.
+ * @param {Object} owner (Optional) The object that owns the function. If the function
+ * isn't global then this argument is required. This could be the namespace that
+ * the function belongs to or the object on which it's
+ * a method.
+ * @param {Boolean} registerPrototype (Optional) Indicates that the prototype should
+ * also be instrumented. Setting to true has the same effect as calling
+ * registerConstructor().
+ * @return {Void}
+ * @method registerFunction
+ * @static
+ */
+ registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{
+
+ //figure out the function name without namespacing
+ var funcName = (name.indexOf(".") > -1 ?
+ name.substring(name.lastIndexOf(".")+1) : name),
+ method,
+ prototype;
+
+ //if owner isn't an object, try to find it from the name
+ if (!L.isObject(owner)){
+ owner = eval(name.substring(0, name.lastIndexOf(".")));
+ }
+
+ //get the method and prototype
+ method = owner[funcName];
+ prototype = method.prototype;
+
+ //see if the method has already been registered
+ if (L.isFunction(method) && !method.__yuiProfiled){
+
+ //replace the function with the profiling one
+ owner[funcName] = this.instrument(name, method);
+
+ /*
+ * Store original function information. We store the actual
+ * function as well as the owner and the name used to identify
+ * the function so it can be restored later.
+ */
+ container[name].__yuiOwner = owner;
+ container[name].__yuiFuncName = funcName; //overwrite with less-specific name
+
+ //register prototype if necessary
+ if (registerPrototype) {
+ this.registerObject(name + ".prototype", prototype);
+ }
+
+ }
+
+ },
+
+
+ /**
+ * Sets up an object for profiling. It takes the object and looks for functions.
+ * When a function is found, registerMethod() is called on it. If set to recrusive
+ * mode, it will also setup objects found inside of this object for profiling,
+ * using the same methodology.
+ * @param {String} name The name of the object to profile (shows up in report).
+ * @param {Object} owner (Optional) The object represented by the name.
+ * @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled.
+ * @return {Void}
+ * @method registerObject
+ * @static
+ */
+ registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{
+
+ //get the object
+ object = (L.isObject(object) ? object : eval(name));
+
+ //save the object
+ container[name] = object;
+
+ for (var prop in object) {
+ if (typeof object[prop] == "function"){
+ if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive
+ this.registerFunction(name + "." + prop, object);
+ }
+ } else if (typeof object[prop] == "object" && recurse){
+ this.registerObject(name + "." + prop, object[prop], recurse);
+ }
+ }
+
+ },
+
+ /**
+ * Removes a constructor function from profiling. Reverses the registerConstructor() method.
+ * @param {String} name The full name of the function including namespacing. This
+ * is the name of the function that is stored in the report.
+ * @return {Void}
+ * @method unregisterFunction
+ * @static
+ */
+ unregisterConstructor : function(name /*:String*/) /*:Void*/{
+
+ //see if the method has been registered
+ if (L.isFunction(container[name])){
+ this.unregisterFunction(name, true);
+ }
+ },
+
+ /**
+ * Removes function from profiling. Reverses the registerFunction() method.
+ * @param {String} name The full name of the function including namespacing. This
+ * is the name of the function that is stored in the report.
+ * @return {Void}
+ * @method unregisterFunction
+ * @static
+ */
+ unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{
+
+ //see if the method has been registered
+ if (L.isFunction(container[name])){
+
+ //check to see if you should unregister the prototype
+ if (unregisterPrototype){
+ this.unregisterObject(name + ".prototype", container[name].prototype);
+ }
+
+ //get original data
+ var owner /*:Object*/ = container[name].__yuiOwner,
+ funcName /*:String*/ = container[name].__yuiFuncName;
+
+ //delete extra information
+ delete container[name].__yuiOwner;
+ delete container[name].__yuiFuncName;
+
+ //replace instrumented function
+ owner[funcName] = container[name];
+
+ //delete supporting information
+ delete container[name];
+ }
+
+
+ },
+
+ /**
+ * Unregisters an object for profiling. It takes the object and looks for functions.
+ * When a function is found, unregisterMethod() is called on it. If set to recrusive
+ * mode, it will also unregister objects found inside of this object,
+ * using the same methodology.
+ * @param {String} name The name of the object to unregister.
+ * @param {Boolean} recurse (Optional) Determines if subobject methods should also be
+ * unregistered.
+ * @return {Void}
+ * @method unregisterObject
+ * @static
+ */
+ unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{
+
+ //get the object
+ if (L.isObject(container[name])){
+ var object = container[name];
+
+ for (var prop in object) {
+ if (typeof object[prop] == "function"){
+ this.unregisterFunction(name + "." + prop);
+ } else if (typeof object[prop] == "object" && recurse){
+ this.unregisterObject(name + "." + prop, recurse);
+ }
+ }
+
+ delete container[name];
+ }
+
+ }
+
+
+ };
+
+
+
+}, '3.0.0' ,{requires:['oop']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("profiler",function(Y){var container={},report={},stopwatches={},WATCH_STARTED=0,WATCH_STOPPED=1,WATCH_PAUSED=2,L=Y.Lang;function createReport(name){report[name]={calls:0,max:0,min:0,avg:0,points:[]};return report[name];}function saveDataPoint(name,duration){var functionData=report[name];if(!functionData){functionData=createReport(name);}functionData.calls++;functionData.points.push(duration);if(functionData.calls>1){functionData.avg=((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;functionData.min=Math.min(functionData.min,duration);functionData.max=Math.max(functionData.max,duration);}else{functionData.avg=duration;functionData.min=duration;functionData.max=duration;}}Y.Profiler={clear:function(name){if(L.isString(name)){delete report[name];delete stopwatches[name];}else{report={};stopwatches={};}},getOriginal:function(name){return container[name];},instrument:function(name,method){var newMethod=function(){var start=new Date(),retval=method.apply(this,arguments),stop=new Date();saveDataPoint(name,stop-start);return retval;};Y.mix(newMethod,method);newMethod.__yuiProfiled=true;newMethod.prototype=method.prototype;container[name]=method;container[name].__yuiFuncName=name;createReport(name);return newMethod;},pause:function(name){var now=new Date(),stopwatch=stopwatches[name];if(stopwatch&&stopwatch.state==WATCH_STARTED){stopwatch.total+=(now-stopwatch.start);stopwatch.start=0;stopwatch.state=WATCH_PAUSED;}},start:function(name){if(container[name]){throw new Error("Cannot use '"+name+"' for profiling through start(), name is already in use.");}else{if(!report[name]){createReport(name);}if(!stopwatches[name]){stopwatches[name]={state:WATCH_STOPPED,start:0,total:0};}if(stopwatches[name].state==WATCH_STOPPED){stopwatches[name].state=WATCH_STARTED;stopwatches[name].start=new Date();}}},stop:function(name){var now=new Date(),stopwatch=stopwatches[name];if(stopwatch){if(stopwatch.state==WATCH_STARTED){saveDataPoint(name,stopwatch.total+(now-stopwatch.start));}else{if(stopwatch.state==WATCH_PAUSED){saveDataPoint(name,stopwatch.total);}}stopwatch.start=0;stopwatch.total=0;stopwatch.state=WATCH_STOPPED;}},getAverage:function(name){return report[name].avg;},getCallCount:function(name){return report[name].calls;},getMax:function(name){return report[name].max;},getMin:function(name){return report[name].min;},getFunctionReport:function(name){return report[name];},getReport:function(name){return report[name];},getFullReport:function(filter){filter=filter||function(){return true;};if(L.isFunction(filter)){var fullReport={};for(var name in report){if(filter(report[name])){fullReport[name]=report[name];}}return fullReport;}},registerConstructor:function(name,owner){this.registerFunction(name,owner,true);},registerFunction:function(name,owner,registerPrototype){var funcName=(name.indexOf(".")>-1?name.substring(name.lastIndexOf(".")+1):name),method,prototype;if(!L.isObject(owner)){owner=eval(name.substring(0,name.lastIndexOf(".")));}method=owner[funcName];prototype=method.prototype;if(L.isFunction(method)&&!method.__yuiProfiled){owner[funcName]=this.instrument(name,method);container[name].__yuiOwner=owner;container[name].__yuiFuncName=funcName;if(registerPrototype){this.registerObject(name+".prototype",prototype);}}},registerObject:function(name,object,recurse){object=(L.isObject(object)?object:eval(name));container[name]=object;for(var prop in object){if(typeof object[prop]=="function"){if(prop!="constructor"&&prop!="superclass"){this.registerFunction(name+"."+prop,object);}}else{if(typeof object[prop]=="object"&&recurse){this.registerObject(name+"."+prop,object[prop],recurse);}}}},unregisterConstructor:function(name){if(L.isFunction(container[name])){this.unregisterFunction(name,true);}},unregisterFunction:function(name,unregisterPrototype){if(L.isFunction(container[name])){if(unregisterPrototype){this.unregisterObject(name+".prototype",container[name].prototype);}var owner=container[name].__yuiOwner,funcName=container[name].__yuiFuncName;delete container[name].__yuiOwner;delete container[name].__yuiFuncName;owner[funcName]=container[name];delete container[name];}},unregisterObject:function(name,recurse){if(L.isObject(container[name])){var object=container[name];for(var prop in object){if(typeof object[prop]=="function"){this.unregisterFunction(name+"."+prop);}else{if(typeof object[prop]=="object"&&recurse){this.unregisterObject(name+"."+prop,recurse);}}}delete container[name];}}};},"3.0.0",{requires:["oop"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('profiler', function(Y) {
+
+ /**
+ * The YUI JavaScript profiler.
+ * @module profiler
+ * @requires yui
+ */
+
+ //-------------------------------------------------------------------------
+ // Private Variables and Functions
+ //-------------------------------------------------------------------------
+
+ var container = {}, //Container object on which to put the original unprofiled methods.
+ report = {}, //Profiling information for functions
+ stopwatches = {}, //Additional stopwatch information
+
+ WATCH_STARTED = 0,
+ WATCH_STOPPED = 1,
+ WATCH_PAUSED = 2,
+
+ //shortcuts
+ L = Y.Lang;
+
+ /* (intentionally not documented)
+ * Creates a report object with the given name.
+ * @param {String} name The name to store for the report object.
+ * @return {Void}
+ * @method createReport
+ * @private
+ */
+ function createReport(name){
+ report[name] = {
+ calls: 0,
+ max: 0,
+ min: 0,
+ avg: 0,
+ points: []
+ };
+ return report[name];
+ }
+
+ /* (intentionally not documented)
+ * Called when a method ends execution. Marks the start and end time of the
+ * method so it can calculate how long the function took to execute. Also
+ * updates min/max/avg calculations for the function.
+ * @param {String} name The name of the function to mark as stopped.
+ * @param {int} duration The number of milliseconds it took the function to
+ * execute.
+ * @return {Void}
+ * @method saveDataPoint
+ * @private
+ * @static
+ */
+ function saveDataPoint(name, duration){
+
+ //get the function data
+ var functionData /*:Object*/ = report[name];
+
+ //just in case clear() was called
+ if (!functionData){
+ functionData = createReport(name);
+ }
+
+ //increment the calls
+ functionData.calls++;
+ functionData.points.push(duration);
+
+ //if it's already been called at least once, do more complex calculations
+ if (functionData.calls > 1) {
+ functionData.avg = ((functionData.avg*(functionData.calls-1))+duration)/functionData.calls;
+ functionData.min = Math.min(functionData.min, duration);
+ functionData.max = Math.max(functionData.max, duration);
+ } else {
+ functionData.avg = duration;
+ functionData.min = duration;
+ functionData.max = duration;
+ }
+
+ }
+
+ //-------------------------------------------------------------------------
+ // Public Interface
+ //-------------------------------------------------------------------------
+
+ /**
+ * Profiles functions in JavaScript.
+ * @class Profiler
+ * @static
+ */
+ Y.Profiler = {
+
+ //-------------------------------------------------------------------------
+ // Utility Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Removes all report data from the profiler.
+ * @param {String} name (Optional) The name of the report to clear. If
+ * omitted, then all report data is cleared.
+ * @return {Void}
+ * @method clear
+ * @static
+ */
+ clear: function(name){
+ if (L.isString(name)){
+ delete report[name];
+ delete stopwatches[name];
+ } else {
+ report = {};
+ stopwatches = {};
+ }
+ },
+
+ /**
+ * Returns the uninstrumented version of a function/object.
+ * @param {String} name The name of the function/object to retrieve.
+ * @return {Function|Object} The uninstrumented version of a function/object.
+ * @method getOriginal
+ * @static
+ */
+ getOriginal: function(name){
+ return container[name];
+ },
+
+ /**
+ * Instruments a method to have profiling calls.
+ * @param {String} name The name of the report for the function.
+ * @param {Function} method The function to instrument.
+ * @return {Function} An instrumented version of the function.
+ * @method instrument
+ * @static
+ */
+ instrument: function(name, method){
+
+ //create instrumented version of function
+ var newMethod = function () {
+
+ var start = new Date(),
+ retval = method.apply(this, arguments),
+ stop = new Date();
+
+ saveDataPoint(name, stop-start);
+
+ return retval;
+
+ };
+
+ //copy the function properties over
+ Y.mix(newMethod, method);
+
+ //assign prototype and flag as being profiled
+ newMethod.__yuiProfiled = true;
+ newMethod.prototype = method.prototype;
+
+ //store original method
+ container[name] = method;
+ container[name].__yuiFuncName = name;
+
+ //create the report
+ createReport(name);
+
+ //return the new method
+ return newMethod;
+ },
+
+ //-------------------------------------------------------------------------
+ // Stopwatch Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Pauses profiling information for a given name.
+ * @param {String} name The name of the data point.
+ * @return {Void}
+ * @method pause
+ * @static
+ */
+ pause: function(name){
+ var now = new Date(),
+ stopwatch = stopwatches[name];
+
+ if (stopwatch && stopwatch.state == WATCH_STARTED){
+ stopwatch.total += (now - stopwatch.start);
+ stopwatch.start = 0;
+ stopwatch.state = WATCH_PAUSED;
+ }
+
+ },
+
+ /**
+ * Start profiling information for a given name. The name cannot be the name
+ * of a registered function or object. This is used to start timing for a
+ * particular block of code rather than instrumenting the entire function.
+ * @param {String} name The name of the data point.
+ * @return {Void}
+ * @method start
+ * @static
+ */
+ start: function(name){
+ if(container[name]){
+ throw new Error("Cannot use '" + name + "' for profiling through start(), name is already in use.");
+ } else {
+
+ //create report if necessary
+ if (!report[name]){
+ createReport(name);
+ }
+
+ //create stopwatch object if necessary
+ if (!stopwatches[name]){
+ stopwatches[name] = {
+ state: WATCH_STOPPED,
+ start: 0,
+ total: 0
+ };
+ }
+
+ if (stopwatches[name].state == WATCH_STOPPED){
+ stopwatches[name].state = WATCH_STARTED;
+ stopwatches[name].start = new Date();
+ }
+
+ }
+ },
+
+ /**
+ * Stops profiling information for a given name.
+ * @param {String} name The name of the data point.
+ * @return {Void}
+ * @method stop
+ * @static
+ */
+ stop: function(name){
+ var now = new Date(),
+ stopwatch = stopwatches[name];
+
+ if (stopwatch){
+ if (stopwatch.state == WATCH_STARTED){
+ saveDataPoint(name, stopwatch.total + (now - stopwatch.start));
+ } else if (stopwatch.state == WATCH_PAUSED){
+ saveDataPoint(name, stopwatch.total);
+ }
+
+ //reset stopwatch information
+ stopwatch.start = 0;
+ stopwatch.total = 0;
+ stopwatch.state = WATCH_STOPPED;
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Reporting Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Returns the average amount of time (in milliseconds) that the function
+ * with the given name takes to execute.
+ * @param {String} name The name of the function whose data should be returned.
+ * If an object type method, it should be 'constructor.prototype.methodName';
+ * a normal object method would just be 'object.methodName'.
+ * @return {float} The average time it takes the function to execute.
+ * @method getAverage
+ * @static
+ */
+ getAverage : function (name /*:String*/) /*:float*/ {
+ return report[name].avg;
+ },
+
+ /**
+ * Returns the number of times that the given function has been called.
+ * @param {String} name The name of the function whose data should be returned.
+ * @return {int} The number of times the function was called.
+ * @method getCallCount
+ * @static
+ */
+ getCallCount : function (name /*:String*/) /*:int*/ {
+ return report[name].calls;
+ },
+
+ /**
+ * Returns the maximum amount of time (in milliseconds) that the function
+ * with the given name takes to execute.
+ * @param {String} name The name of the function whose data should be returned.
+ * If an object type method, it should be 'constructor.prototype.methodName';
+ * a normal object method would just be 'object.methodName'.
+ * @return {float} The maximum time it takes the function to execute.
+ * @method getMax
+ * @static
+ */
+ getMax : function (name /*:String*/) /*:int*/ {
+ return report[name].max;
+ },
+
+ /**
+ * Returns the minimum amount of time (in milliseconds) that the function
+ * with the given name takes to execute.
+ * @param {String} name The name of the function whose data should be returned.
+ * If an object type method, it should be 'constructor.prototype.methodName';
+ * a normal object method would just be 'object.methodName'.
+ * @return {float} The minimum time it takes the function to execute.
+ * @method getMin
+ * @static
+ */
+ getMin : function (name /*:String*/) /*:int*/ {
+ return report[name].min;
+ },
+
+ /**
+ * Returns an object containing profiling data for a single function.
+ * The object has an entry for min, max, avg, calls, and points).
+ * @return {Object} An object containing profile data for a given function.
+ * @method getFunctionReport
+ * @static
+ * @deprecated Use getReport() instead.
+ */
+ getFunctionReport : function (name /*:String*/) /*:Object*/ {
+ return report[name];
+ },
+
+ /**
+ * Returns an object containing profiling data for a single function.
+ * The object has an entry for min, max, avg, calls, and points).
+ * @return {Object} An object containing profile data for a given function.
+ * @method getReport
+ * @static
+ */
+ getReport : function (name /*:String*/) /*:Object*/ {
+ return report[name];
+ },
+
+ /**
+ * Returns an object containing profiling data for all of the functions
+ * that were profiled. The object has an entry for each function and
+ * returns all information (min, max, average, calls, etc.) for each
+ * function.
+ * @return {Object} An object containing all profile data.
+ * @static
+ */
+ getFullReport : function (filter /*:Function*/) /*:Object*/ {
+ filter = filter || function(){return true;};
+
+ if (L.isFunction(filter)) {
+ var fullReport = {};
+
+ for (var name in report){
+ if (filter(report[name])){
+ fullReport[name] = report[name];
+ }
+ }
+
+ return fullReport;
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Profiling Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Sets up a constructor for profiling, including all properties and methods on the prototype.
+ * @param {string} name The fully-qualified name of the function including namespace information.
+ * @param {Object} owner (Optional) The object that owns the function (namespace or containing object).
+ * @return {Void}
+ * @method registerConstructor
+ * @static
+ */
+ registerConstructor : function (name /*:String*/, owner /*:Object*/) /*:Void*/ {
+ this.registerFunction(name, owner, true);
+ },
+
+ /**
+ * Sets up a function for profiling. It essentially overwrites the function with one
+ * that has instrumentation data. This method also creates an entry for the function
+ * in the profile report. The original function is stored on the container object.
+ * @param {String} name The full name of the function including namespacing. This
+ * is the name of the function that is stored in the report.
+ * @param {Object} owner (Optional) The object that owns the function. If the function
+ * isn't global then this argument is required. This could be the namespace that
+ * the function belongs to or the object on which it's
+ * a method.
+ * @param {Boolean} registerPrototype (Optional) Indicates that the prototype should
+ * also be instrumented. Setting to true has the same effect as calling
+ * registerConstructor().
+ * @return {Void}
+ * @method registerFunction
+ * @static
+ */
+ registerFunction : function(name /*:String*/, owner /*:Object*/, registerPrototype /*:Boolean*/) /*:Void*/{
+
+ //figure out the function name without namespacing
+ var funcName = (name.indexOf(".") > -1 ?
+ name.substring(name.lastIndexOf(".")+1) : name),
+ method,
+ prototype;
+
+ //if owner isn't an object, try to find it from the name
+ if (!L.isObject(owner)){
+ owner = eval(name.substring(0, name.lastIndexOf(".")));
+ }
+
+ //get the method and prototype
+ method = owner[funcName];
+ prototype = method.prototype;
+
+ //see if the method has already been registered
+ if (L.isFunction(method) && !method.__yuiProfiled){
+
+ //replace the function with the profiling one
+ owner[funcName] = this.instrument(name, method);
+
+ /*
+ * Store original function information. We store the actual
+ * function as well as the owner and the name used to identify
+ * the function so it can be restored later.
+ */
+ container[name].__yuiOwner = owner;
+ container[name].__yuiFuncName = funcName; //overwrite with less-specific name
+
+ //register prototype if necessary
+ if (registerPrototype) {
+ this.registerObject(name + ".prototype", prototype);
+ }
+
+ }
+
+ },
+
+
+ /**
+ * Sets up an object for profiling. It takes the object and looks for functions.
+ * When a function is found, registerMethod() is called on it. If set to recrusive
+ * mode, it will also setup objects found inside of this object for profiling,
+ * using the same methodology.
+ * @param {String} name The name of the object to profile (shows up in report).
+ * @param {Object} owner (Optional) The object represented by the name.
+ * @param {Boolean} recurse (Optional) Determines if subobject methods are also profiled.
+ * @return {Void}
+ * @method registerObject
+ * @static
+ */
+ registerObject : function (name /*:String*/, object /*:Object*/, recurse /*:Boolean*/) /*:Void*/{
+
+ //get the object
+ object = (L.isObject(object) ? object : eval(name));
+
+ //save the object
+ container[name] = object;
+
+ for (var prop in object) {
+ if (typeof object[prop] == "function"){
+ if (prop != "constructor" && prop != "superclass"){ //don't do constructor or superclass, it's recursive
+ this.registerFunction(name + "." + prop, object);
+ }
+ } else if (typeof object[prop] == "object" && recurse){
+ this.registerObject(name + "." + prop, object[prop], recurse);
+ }
+ }
+
+ },
+
+ /**
+ * Removes a constructor function from profiling. Reverses the registerConstructor() method.
+ * @param {String} name The full name of the function including namespacing. This
+ * is the name of the function that is stored in the report.
+ * @return {Void}
+ * @method unregisterFunction
+ * @static
+ */
+ unregisterConstructor : function(name /*:String*/) /*:Void*/{
+
+ //see if the method has been registered
+ if (L.isFunction(container[name])){
+ this.unregisterFunction(name, true);
+ }
+ },
+
+ /**
+ * Removes function from profiling. Reverses the registerFunction() method.
+ * @param {String} name The full name of the function including namespacing. This
+ * is the name of the function that is stored in the report.
+ * @return {Void}
+ * @method unregisterFunction
+ * @static
+ */
+ unregisterFunction : function(name /*:String*/, unregisterPrototype /*:Boolean*/) /*:Void*/{
+
+ //see if the method has been registered
+ if (L.isFunction(container[name])){
+
+ //check to see if you should unregister the prototype
+ if (unregisterPrototype){
+ this.unregisterObject(name + ".prototype", container[name].prototype);
+ }
+
+ //get original data
+ var owner /*:Object*/ = container[name].__yuiOwner,
+ funcName /*:String*/ = container[name].__yuiFuncName;
+
+ //delete extra information
+ delete container[name].__yuiOwner;
+ delete container[name].__yuiFuncName;
+
+ //replace instrumented function
+ owner[funcName] = container[name];
+
+ //delete supporting information
+ delete container[name];
+ }
+
+
+ },
+
+ /**
+ * Unregisters an object for profiling. It takes the object and looks for functions.
+ * When a function is found, unregisterMethod() is called on it. If set to recrusive
+ * mode, it will also unregister objects found inside of this object,
+ * using the same methodology.
+ * @param {String} name The name of the object to unregister.
+ * @param {Boolean} recurse (Optional) Determines if subobject methods should also be
+ * unregistered.
+ * @return {Void}
+ * @method unregisterObject
+ * @static
+ */
+ unregisterObject : function (name /*:String*/, recurse /*:Boolean*/) /*:Void*/{
+
+ //get the object
+ if (L.isObject(container[name])){
+ var object = container[name];
+
+ for (var prop in object) {
+ if (typeof object[prop] == "function"){
+ this.unregisterFunction(name + "." + prop);
+ } else if (typeof object[prop] == "object" && recurse){
+ this.unregisterObject(name + "." + prop, recurse);
+ }
+ }
+
+ delete container[name];
+ }
+
+ }
+
+
+ };
+
+
+
+}, '3.0.0' ,{requires:['oop']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('queue-promote', function(Y) {
+
+/**
+ * Adds methods promote, remove, and indexOf to Queue instances.
+ *
+ * @module queue-promote
+ * @for Queue
+ */
+
+Y.mix(Y.Queue.prototype, {
+ /**
+ * Returns the current index in the queue of the specified item
+ *
+ * @method indexOf
+ * @param needle {MIXED} the item to search for
+ * @return {Number} the index of the item or -1 if not found
+ */
+ indexOf : function (callback) {
+ return Y.Array.indexOf(this._q, callback);
+ },
+
+ /**
+ * Moves the referenced item to the head of the queue
+ *
+ * @method promote
+ * @param item {MIXED} an item in the queue
+ */
+ promote : function (callback) {
+ var index = this.indexOf(callback);
+
+ if (index > -1) {
+ this._q.unshift(this._q.splice(index,1));
+ }
+ },
+
+ /**
+ * Removes the referenced item from the queue
+ *
+ * @method remove
+ * @param item {MIXED} an item in the queue
+ */
+ remove : function (callback) {
+ var index = this.indexOf(callback);
+
+ if (index > -1) {
+ this._q.splice(index,1);
+ }
+ }
+
+});
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("queue-promote",function(A){A.mix(A.Queue.prototype,{indexOf:function(B){return A.Array.indexOf(this._q,B);},promote:function(C){var B=this.indexOf(C);if(B>-1){this._q.unshift(this._q.splice(B,1));}},remove:function(C){var B=this.indexOf(C);if(B>-1){this._q.splice(B,1);}}});},"3.0.0",{requires:["yui-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('queue-promote', function(Y) {
+
+/**
+ * Adds methods promote, remove, and indexOf to Queue instances.
+ *
+ * @module queue-promote
+ * @for Queue
+ */
+
+Y.mix(Y.Queue.prototype, {
+ /**
+ * Returns the current index in the queue of the specified item
+ *
+ * @method indexOf
+ * @param needle {MIXED} the item to search for
+ * @return {Number} the index of the item or -1 if not found
+ */
+ indexOf : function (callback) {
+ return Y.Array.indexOf(this._q, callback);
+ },
+
+ /**
+ * Moves the referenced item to the head of the queue
+ *
+ * @method promote
+ * @param item {MIXED} an item in the queue
+ */
+ promote : function (callback) {
+ var index = this.indexOf(callback);
+
+ if (index > -1) {
+ this._q.unshift(this._q.splice(index,1));
+ }
+ },
+
+ /**
+ * Removes the referenced item from the queue
+ *
+ * @method remove
+ * @param item {MIXED} an item in the queue
+ */
+ remove : function (callback) {
+ var index = this.indexOf(callback);
+
+ if (index > -1) {
+ this._q.splice(index,1);
+ }
+ }
+
+});
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-skin-sam .yui-slider-rail-x {
+ background: url("rail-classic-x.png") repeat-x 0 7px;
+ min-height: 19px;
+ *height: 19px;
+}
+
+.yui-skin-sam .yui-slider-rail-y {
+ background: url("rail-classic-y.png") repeat-y 7px 0;
+ min-width: 19px;
+ *width: 19px;
+}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-slider{display:-moz-inline-stack;display:inline-block;*display:inline;zoom:1;vertical-align:middle;}.yui-slider-content{position:relative;}.yui-slider-rail{position:relative;}.yui-slider-thumb{position:absolute;}.yui-slider-thumb-image{display:block;}.yui-slider-image-error .yui-slider-thumb{height:10px;width:10px;background:#000;color:#000;overflow:hidden;}.yui-slider-image-error .yui-slider-thumb-image{display:none;}.yui-skin-sam .yui-slider-rail-x{background:url("rail-classic-x.png") repeat-x 0 7px;min-height:19px;*height:19px;}.yui-skin-sam .yui-slider-rail-y{background:url("rail-classic-y.png") repeat-y 7px 0;min-width:19px;*width:19px;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-slider {
+ /* xbrowser inline-block styles */
+ display: -moz-inline-stack; /* FF2 */
+ display: inline-block;
+ *display: inline; /* IE 7- (with zoom) */
+ zoom: 1;
+ vertical-align: middle;
+}
+
+.yui-slider-content {
+ position: relative;
+}
+
+.yui-slider-rail {
+ position: relative;
+}
+
+.yui-slider-thumb {
+ position: absolute;
+}
+.yui-slider-thumb-image {
+ /* to collapse vertical space around short thumbs in tall line-height */
+ display: block;
+}
+
+.yui-slider-image-error .yui-slider-thumb {
+ height: 10px;
+ width: 10px;
+ background: #000;
+ color: #000;
+ overflow: hidden;
+}
+.yui-slider-image-error .yui-slider-thumb-image {
+ display: none;
+}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('slider', function(Y) {
+
+/**
+ * Create a sliding value range input visualized as a draggable thumb on a
+ * background element.
+ *
+ * @module slider
+ */
+
+var SLIDER = 'slider',
+ RAIL = 'rail',
+ THUMB = 'thumb',
+ VALUE = 'value',
+ MIN = 'min',
+ MAX = 'max',
+ MIN_GUTTER = 'minGutter',
+ MAX_GUTTER = 'maxGutter',
+ THUMB_IMAGE = 'thumbImage',
+ RAIL_SIZE = 'railSize',
+ CONTENT_BOX = 'contentBox',
+
+ SLIDE_START = 'slideStart',
+ SLIDE_END = 'slideEnd',
+
+ THUMB_DRAG = 'thumbDrag',
+ SYNC = 'sync',
+ POSITION_THUMB = 'positionThumb',
+ RENDERED = 'rendered',
+ DISABLED = 'disabled',
+ DISABLED_CHANGE = 'disabledChange',
+
+ DOT = '.',
+ PX = 'px',
+ WIDTH = 'width',
+ HEIGHT = 'height',
+ COMPLETE = 'complete',
+
+ L = Y.Lang,
+ isBoolean= L.isBoolean,
+ isString = L.isString,
+ isNumber = L.isNumber,
+
+ getCN = Y.ClassNameManager.getClassName,
+
+ IMAGE = 'image',
+ C_RAIL = getCN(SLIDER,RAIL),
+ C_THUMB = getCN(SLIDER,THUMB),
+ C_THUMB_IMAGE = getCN(SLIDER,THUMB,IMAGE),
+ C_IMAGE_ERROR = getCN(SLIDER,IMAGE,'error'),
+
+ M = Math,
+ max = M.max,
+ round = M.round,
+ floor = M.floor;
+
+/**
+ * Create a slider to represent an integer value between a given minimum and
+ * maximum. Sliders may be aligned vertically or horizontally, based on the
+ * <code>axis</code> configuration.
+ *
+ * @class Slider
+ * @extends Widget
+ * @param config {Object} Configuration object
+ * @constructor
+ */
+function Slider() {
+ Slider.superclass.constructor.apply(this,arguments);
+}
+
+Y.mix(Slider, {
+
+ /**
+ * The identity of the widget.
+ *
+ * @property Slider.NAME
+ * @type String
+ * @static
+ */
+ NAME : SLIDER,
+
+ /**
+ * Object property names used for respective X and Y axis Sliders (e.g.
+ * "left" vs. "top" for placing the thumb according to
+ * its representative value).
+ *
+ * @property Slider._AXIS_KEYS
+ * @type Object
+ * @protected
+ * @static
+ */
+ _AXIS_KEYS : {
+ x : {
+ dim : WIDTH,
+ offAxisDim : HEIGHT,
+ eventPageAxis : 'pageX',
+ ddStick : 'stickX',
+ xyIndex : 0
+ },
+ y : {
+ dim : HEIGHT,
+ offAxisDim : WIDTH,
+ eventPageAxis : 'pageY',
+ ddStick : 'stickY',
+ xyIndex : 1
+ }
+ },
+
+ /**
+ * Static Object hash used to capture existing markup for progressive
+ * enhancement. Keys correspond to config attribute names and values
+ * are selectors used to inspect the contentBox for an existing node
+ * structure.
+ *
+ * @property Slider.HTML_PARSER
+ * @type Object
+ * @protected
+ * @static
+ */
+ HTML_PARSER : {
+ rail : DOT + C_RAIL,
+ thumb : DOT + C_THUMB,
+ thumbImage : DOT + C_THUMB_IMAGE
+ },
+
+ /**
+ * Static property used to define the default attribute configuration of
+ * the Widget.
+ *
+ * @property Slider.ATTRS
+ * @type Object
+ * @protected
+ * @static
+ */
+ ATTRS : {
+
+ /**
+ * Axis upon which the Slider's thumb moves. "x" for
+ * horizontal, "y" for vertical.
+ *
+ * @attribute axis
+ * @type String
+ * @default "x"
+ * @writeOnce
+ */
+ axis : {
+ value : 'x',
+ writeOnce : true,
+ validator : function (v) {
+ return this._validateNewAxis(v);
+ },
+ setter : function (v) {
+ return this._setAxisFn(v);
+ }
+ },
+
+ /**
+ * Value associated with the left or top most position of the thumb on
+ * the rail.
+ *
+ * @attribute min
+ * @type Number
+ * @default 0
+ */
+ min : {
+ value : 0,
+ validator : function (v) {
+ return this._validateNewMin(v);
+ }
+ },
+
+ /**
+ * Value associated with the right or bottom most position of the thumb
+ * on the rail.
+ *
+ * @attribute max
+ * @type Number
+ * @default 100
+ */
+ max : {
+ value : 100,
+ validator : function (v) {
+ return this._validateNewMax(v);
+ }
+ },
+
+ /**
+ * The current value of the Slider. This value is interpretted into a
+ * position for the thumb along the Slider's rail.
+ *
+ * @attribute value
+ * @type Number
+ * @default 0
+ */
+ value : {
+ value : 0,
+ validator : function (v) {
+ return this._validateNewValue(v);
+ }
+ },
+
+ /**
+ * The Node representing the Slider's rail, usually visualized as a
+ * bar of some sort using a background image, along which the thumb
+ * moves. This Node contains the thumb Node.
+ *
+ * @attribute rail
+ * @type Node
+ * @default null
+ */
+ rail : {
+ value : null,
+ validator : function (v) {
+ return this._validateNewRail(v);
+ },
+ setter : function (v) {
+ return this._setRailFn(v);
+ }
+ },
+
+ /**
+ * <p>The Node representing the Slider's thumb, usually visualized as a
+ * pointer using a contained image Node (see thumbImage). The current
+ * value of the Slider is calculated from the centerpoint of this
+ * Node in relation to the rail Node. If provided, the thumbImage
+ * Node is contained within this Node.</p>
+ *
+ * <p>If no thumbImage is provided and the Node passed as the thumb is
+ * an <code>img</code> element, the assigned Node will be allocated to
+ * the thumbImage and the thumb container defaulted.</p>
+ *
+ * @attribute thumb
+ * @type Node
+ * @default null
+ */
+ thumb : {
+ value : null,
+ validator : function (v) {
+ return this._validateNewThumb(v);
+ },
+ setter : function (v) {
+ return this._setThumbFn(v);
+ }
+ },
+
+ /**
+ * <p>The Node representing the image element to use for the Slider's
+ * thumb.</p>
+ *
+ * <p>Alternately, an image URL can be passed and an <code>img</code>
+ * Node will be generated accordingly.</p>
+ *
+ * <p>If no thumbImage is provided and the Node passed as the thumb is
+ * an <code>img</code> element, the assigned Node will be allocated to
+ * the thumbImage and the thumb container defaulted.</p>
+ *
+ * <p>If thumbImage is provided but its URL resolves to a 404, a default
+ * style will be applied to maintain basic functionality.</p>
+ *
+ * @attribute thumbImage
+ * @type Node|String
+ * @default null
+ */
+ thumbImage : {
+ value : null,
+ validator : function (v) {
+ return this._validateNewThumbImage(v);
+ },
+ setter : function (v) {
+ return this._setThumbImageFn(v);
+ }
+ },
+
+ /**
+ * <p>The width or height of the rail element representing the physical
+ * space along which the thumb can move. CSS size values (e.g. '30em')
+ * accepted but converted to pixels during render.</p>
+ *
+ * <p>Alternately, but not recommended, this attribute can be left
+ * unassigned in favor of specifying height or width.</p>
+ *
+ * @attribute railSize
+ * @type String
+ * @default '0'
+ */
+ railSize : {
+ value : '0',
+ validator : function (v) {
+ return this._validateNewRailSize(v);
+ }
+ },
+
+ /**
+ * Boolean indicating whether clicking and dragging on the rail will
+ * trigger thumb movement.
+ *
+ * @attribute railEnabled
+ * @type Boolean
+ * @default true
+ */
+ railEnabled : {
+ value : true,
+ validator : isBoolean
+ },
+
+ /**
+ * Like CSS padding, the distance in pixels from the inner top or left
+ * edge of the rail node within which the thumb can travel. Negative
+ * values allow the edge of the thumb to escape the rail node
+ * boundaries.
+ *
+ * @attribute minGutter
+ * @type Number
+ * @default 0
+ */
+ minGutter : {
+ value : 0,
+ validator : isNumber
+ },
+
+ /**
+ * Like CSS padding, the distance in pixels from the inner bottom or
+ * right edge of the rail node within which the thumb can travel.
+ * Negative values allow the edge of the thumb to escape the rail node
+ * boundaries.
+ *
+ * @attribute maxGutter
+ * @type Number
+ * @default 0
+ */
+ maxGutter : {
+ value : 0,
+ validator : isNumber
+ }
+ }
+});
+
+Y.extend(Slider, Y.Widget, {
+
+ /**
+ * Collection of object property names from the appropriate hash set in
+ * Slider._AXIS_KEYS.
+ *
+ * @property _key
+ * @type Object
+ * @protected
+ */
+ _key : null,
+
+ /**
+ * Factor used to translate positional coordinates (e.g. left or top) to
+ * the Slider's value.
+ *
+ * @property _factor
+ * @type Number
+ * @protected
+ */
+ _factor : 1,
+
+ /**
+ * Pixel dimension of the rail Node's width for X axis Sliders or height
+ * for Y axis Sliders. Used with _factor to calculate positional
+ * coordinates for the thumb.
+ *
+ * @property _railSize
+ * @type Number
+ * @protected
+ */
+ _railSize : null,
+
+ /**
+ * Pixel dimension of the thumb Node's width for X axis Sliders or height
+ * for Y axis Sliders. Used with _factor to calculate positional
+ * coordinates for the thumb.
+ *
+ * @property _thumbSize
+ * @type Number
+ * @protected
+ */
+ _thumbSize : null,
+
+ /**
+ * Pixel offset of the point in the thumb element from its top/left edge
+ * to where the value calculation should take place. By default, this is
+ * calculated to half the width of the thumb, causing the value to be
+ * marked from the center of the thumb.
+ *
+ * @property _thumbOffset
+ * @type Number
+ * @protected
+ */
+ _thumbOffset : 0,
+
+ /**
+ * Object returned from temporary subscription to disabledChange event to
+ * defer setting the disabled state while Slider is loading the thumb
+ * image.
+ *
+ * @property _stall
+ * @type Object
+ * @protected
+ */
+ _stall : false,
+
+ /**
+ * Deferred value for the disabled attribute when stalled (see _stall
+ * property).
+ *
+ * @property _disabled
+ * @type Boolean
+ * @protected
+ */
+ _disabled : false,
+
+ /**
+ * Construction logic executed durint Slider instantiation. Subscribes to
+ * after events for min, max, and railSize. Publishes custom events
+ * including slideStart and slideEnd.
+ *
+ * @method initializer
+ * @protected
+ */
+ initializer : function () {
+ this._key = Slider._AXIS_KEYS[this.get('axis')];
+
+ this.after('minChange', this._afterMinChange);
+ this.after('maxChange', this._afterMaxChange);
+
+ this.after('railSizeChange', this._afterRailSizeChange);
+
+ /**
+ * Signals the beginning of a thumb drag operation. Payload includes
+ * the DD.Drag instance's drag:start event under key ddEvent.
+ *
+ * @event slideStart
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>ddEvent</dt>
+ * <dd><code>drag:start</code> event from the managed DD.Drag instance</dd>
+ * </dl>
+ */
+ this.publish(SLIDE_START);
+
+ /**
+ * Signals the end of a thumb drag operation. Payload includes
+ * the DD.Drag instance's drag:end event under key ddEvent.
+ *
+ * @event slideEnd
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>ddEvent</dt>
+ * <dd><code>drag:end</code> event from the managed DD.Drag instance</dd>
+ * </dl>
+ */
+ this.publish(SLIDE_END);
+
+ /**
+ * Communicates a request to synchronize the Slider UI with the
+ * attribute state. Links the sync request with the default sync
+ * logic in _defSyncFn.
+ *
+ * @event sync
+ * @param event {Event.Facade} Event Facade object
+ * @preventable _defSyncFn
+ */
+ this.publish(SYNC, { defaultFn: this._defSyncFn });
+
+ /**
+ * Signals a request to reposition the thumb in response to API methods.
+ * Triggers the thumb placement logic in _defPositionThumbFn.
+ *
+ * @event positionThumb
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>changeEv</dt>
+ * <dd><code>valueChange</code> event fired in response to the change in the value attribute</dd>
+ * </dl>
+ * @preventable _defPositionThumbFn
+ */
+ this.publish(POSITION_THUMB, { defaultFn: this._defPositionThumbFn });
+ },
+
+ /**
+ * Create the DOM structure for the Slider.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI : function () {
+ this._initRail();
+ this._initThumb();
+ },
+
+ /**
+ * Creates the rail element if not provided and not discovered via
+ * HTML_PARSER.
+ *
+ * @method _initRail
+ * @protected
+ */
+ _initRail : function () {
+ var cb = this.get(CONTENT_BOX),
+ rail = this.get(RAIL);
+
+ // Create rail if necessary. Make sure it's in the contentBox
+ if (!rail) {
+ rail = cb.appendChild(
+ Y.Node.create('<div class="'+C_RAIL+'"></div>'));
+
+ this.set(RAIL,rail);
+ } else if (!cb.contains(rail)) {
+ cb.appendChild(rail);
+ }
+
+ rail.addClass(C_RAIL);
+ rail.addClass(this.getClassName(RAIL,this.get('axis')));
+ },
+
+ /**
+ * <p>Creates the thumb element (not image) if not provided and not
+ * discovered via HTML_PARSER. If the thumb is an <code>img</code> element
+ * but no thumbImage configured or discovered, reassigns the thumb element
+ * to the thumbImage and defaults the thumb element as a div.</p>
+ *
+ * <p>Makes sure the thumb is a child of the rail element and calls
+ * _initThumbImage if thumbImage is provided.</p>
+ *
+ * @method _initThumb
+ * @protected
+ */
+ _initThumb : function () {
+ var rail = this.get(RAIL),
+ thumb = this.get(THUMB);
+
+ // Passed an img element as the thumb
+ if (thumb && !this.get(THUMB_IMAGE) &&
+ thumb.get('nodeName').toLowerCase() === 'img') {
+ this.set(THUMB_IMAGE, thumb);
+ this.set(THUMB,null);
+ thumb = null;
+ }
+
+ if (!thumb) {
+ thumb = Y.Node.create(
+ '<div class="'+C_THUMB+'"></div>');
+
+ this.set(THUMB,thumb);
+ }
+
+ thumb.addClass(C_THUMB);
+
+ if (!rail.contains(thumb)) {
+ rail.appendChild(thumb);
+ }
+
+ if (this.get(THUMB_IMAGE)) {
+ this._initThumbImage();
+ }
+ },
+
+ /**
+ * Ensures the thumbImage is a child of the thumb element.
+ *
+ * @method _initThumbImage
+ * @protected
+ */
+ _initThumbImage : function () {
+ var thumb = this.get(THUMB),
+ img = this.get(THUMB_IMAGE);
+
+ if (img) {
+ img.replaceClass(C_THUMB,C_THUMB_IMAGE);
+
+ if (!thumb.contains(img)) {
+ thumb.appendChild(img);
+ }
+ }
+ },
+
+ /**
+ * Creates the Y.DD instance used to handle the thumb movement and binds
+ * Slider interaction to the configured value model.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI : function () {
+ /**
+ * Bridges user interaction with the thumb to the value attribute.
+ *
+ * @event thumbDrag
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>ddEvent</dt>
+ * <dd><code>drag:drag</code> event from the managed DD.Drag instance</dd>
+ * </dl>
+ * @preventable _defThumbDragFn
+ */
+ this.publish(THUMB_DRAG, {defaultFn: this._defThumbDragFn});
+
+ this._bindThumbDD();
+
+ this.after('valueChange', this._afterValueChange);
+ this.after('thumbImageChange', this._afterThumbImageChange);
+ this.after(DISABLED_CHANGE, this._afterDisabledChange);
+ },
+
+ /**
+ * Creates the Y.DD instance used to handle the thumb interaction.
+ *
+ * @method _bindThumbDD
+ * @protected
+ */
+ _bindThumbDD : function () {
+ var ddConf = {
+ node : this.get(THUMB),
+ bubble : false
+ },
+ conConf = {
+ constrain2node : this.get(RAIL)
+ };
+
+ conConf[this._key.ddStick] = true;
+
+ this._dd = new Y.DD.Drag(ddConf).plug(Y.Plugin.DDConstrained, conConf);
+ this._dd.on('drag:start', Y.bind(this._onDDStartDrag, this));
+ this._dd.on('drag:drag', Y.bind(this._onDDDrag, this));
+ this._dd.on('drag:end', Y.bind(this._onDDEndDrag, this));
+
+ this._initRailDD();
+ },
+
+ /**
+ * Subscribes to the rail Node's mousedown event to actuate the thumb when
+ * backgroundEnabled is true.
+ *
+ * @method _initRailDD
+ * @protected
+ */
+ _initRailDD : function () {
+ this.get(RAIL).on('mousedown',Y.bind(this._handleRailMouseDown,this));
+ },
+
+ /**
+ * If the Slider is not disabled and railEnabled is true, moves the thumb
+ * to the mousedown position and hands control over to DD.
+ *
+ * @method _handleRailMouseDown
+ * @param e {Event} Mousedown event facade
+ * @protected
+ */
+ _handleRailMouseDown : function (e) {
+ if (this.get('railEnabled') && !this.get(DISABLED)) {
+ var dd = this._dd,
+ xyIndex = this._key.xyIndex,
+ xy;
+
+ if (dd.get('primaryButtonOnly') && e.button > 1) {
+ Y.log('Mousedown was not produced by the primary button',
+ 'warn', 'dd-drag');
+ return false;
+ }
+
+ dd._dragThreshMet = true;
+
+ dd._fixIEMouseDown();
+ e.halt();
+
+ Y.DD.DDM.activeDrag = dd;
+
+ // Adjust registered starting position by half the thumb's x/y
+ xy = dd.get('dragNode').getXY();
+ xy[xyIndex] += this._thumbOffset;
+
+ dd._setStartPosition(xy);
+ dd.set('activeHandle',dd.get('dragNode'));
+
+ dd.start();
+ dd._alignNode([e.pageX,e.pageY]);
+ }
+ },
+
+ /**
+ * Synchronizes the DOM state with the attribute settings (most notably
+ * railSize and value). If thumbImage is provided and is still loading,
+ * sync is delayed until it is complete, since the image's dimensions are
+ * taken into consideration for calculations.
+ *
+ * @method syncUI
+ */
+ syncUI : function () {
+ this.get(CONTENT_BOX).removeClass(C_IMAGE_ERROR);
+
+ var img = this.get(THUMB_IMAGE);
+
+ if (this._isImageLoading(img)) {
+ Y.log('Thumb image loading. Scheduling sync.','info','slider');
+ // Schedule the sync for when the image loads/errors
+ this._scheduleSync();
+ } else {
+ Y.log('No thumb image, or image already loaded. Syncing immediately.','info','slider');
+ this._ready(img,!this._isImageLoaded(img));
+ }
+ },
+
+ /**
+ * Binds to the load and error event on the thumbImage to sync the DOM
+ * state with the attribute settings when the image resource is resolved.
+ * The Slider is disabled while it waits.
+ *
+ * @method _scheduleSync
+ * @protected
+ */
+ _scheduleSync : function () {
+ var img, handler;
+
+ if (!this._stall) {
+ // disable the control until the image is loaded
+ this._disabled = this.get(DISABLED);
+ this.set(DISABLED,true);
+ this._stall = this.on(DISABLED_CHANGE,this._stallDisabledChange);
+
+ img = this.get(THUMB_IMAGE);
+ handler = Y.bind(this._imageLoaded,this,img);
+ img.on('load', handler);
+ img.on('error',handler);
+ }
+ },
+
+ /**
+ * Method subscribed to the disabledChange event when thumbImage is being
+ * loaded. Prevents manually enabling the Slider until the thumbImage
+ * resource is resolved. Intended value is stored during load and set upon
+ * completion.
+ *
+ * @method _stallDisabledChange
+ * @param e {Event} Change event for the disabled attribute
+ * @protected
+ */
+ _stallDisabledChange : function (e) {
+ this._disabled = e.newVal;
+ e.preventDefault();
+ },
+
+ /**
+ * Event handler assigned to the thumbImage's load and error event if it
+ * was not loaded prior to instantiation. Restores the disabled value.
+ *
+ * @method _imageLoaded
+ * @param img {Node} The thumbImage Node
+ * @param e {Event} load or error event fired by the thumbImage
+ * @protected
+ */
+ _imageLoaded : function (img,e) {
+ var error = (e.type.toLowerCase().indexOf('error') > -1);
+
+ // Need to execute inside a setTimeout because IE doesn't report
+ // img.complete === true until after the img.onload handler
+ // @TODO: readyState reports correctly in onload. Lose this wrapper
+ // and use that in _isImageLoaded.
+ Y.later(0, this, function () {
+ if (this._stall) {
+ this._stall.detach();
+ }
+
+ Y.log('Thumb image '+e.type+'ed. Syncing','info','slider');
+
+ this._stall = false;
+
+ this._ready(img,error);
+
+ this.set(DISABLED,this._disabled);
+ });
+ },
+
+ /**
+ * Applies a class to the content box if the thumbImage failed to resolve,
+ * the fires the internal sync event triggering a sync between UI and
+ * state.
+ *
+ * @method _ready
+ * @param img {Node} the thumbImage Node
+ * @param error {Boolean} Indicates an error while loading the thumbImage
+ * @protected
+ */
+ _ready : function (img,error) {
+ var method = error ? 'addClass' : 'removeClass';
+
+ // If the thumb image url results in 404, assign a class to provide
+ // default thumb dimensions/UI
+ this.get(CONTENT_BOX)[method](C_IMAGE_ERROR);
+
+ this.fire(SYNC);
+ },
+
+ /**
+ * The default synchronization behavior, updating the Slider's DOM state to
+ * match the current attribute values.
+ *
+ * @method _defSyncFn
+ * @param e {Event} Internal sync event
+ * @protected
+ */
+ _defSyncFn : function (e) {
+ this._uiSetThumbSize();
+
+ this._setThumbOffset();
+
+ this._uiSetRailSize();
+
+ this._setRailOffsetXY();
+
+ this._setDDGutter();
+
+ this._resetDDCacheRegion();
+
+ this._setFactor();
+
+ var val = this.get(VALUE);
+
+ this.fire(POSITION_THUMB, {
+ value : val,
+ offset : this._convertValueToOffset(val)
+ });
+
+ // Forces a reflow of the bounding box to address IE8 inline-block
+ // container not expanding correctly. bug 2527905
+ this.get('boundingBox').toggleClass('');
+ },
+
+ /**
+ * Captures the thumb's pixel height or width (depending on the Slider's
+ * axis) for use in positioning calculations.
+ *
+ * @method _uiSetThumbSize
+ * @protected
+ */
+ _uiSetThumbSize : function () {
+ var thumb = this.get(THUMB),
+ dim = this._key.dim,
+ img = this.get(THUMB_IMAGE),
+ size;
+
+ // offsetWidth fails in hidden containers
+ size = parseInt(thumb.getComputedStyle(dim),10);
+
+ Y.log('thumb '+dim+': '+size+'px','info','slider');
+
+ if (img && this._isImageLoaded(img)) {
+ Y.log('using thumbImage '+dim+' ('+img.get(dim)+') for _thumbSize','info','slider');
+
+ size = img.get(dim);
+ }
+
+ this._thumbSize = size;
+ },
+
+ /**
+ * Establishes the point in the thumb that should align to the rail
+ * position representing the calculated value.
+ *
+ * @method _setThumbOffset
+ * @protected
+ */
+ _setThumbOffset : function () {
+ this._thumbOffset = floor(this._thumbSize / 2);
+ Y.log('_thumbOffset calculated to '+this._thumbOffset+'px','info','slider');
+ },
+
+ /**
+ * Stores the rail Node's pixel height or width, depending on the Slider's
+ * axis, for use in calculating thumb position from the value.
+ *
+ * @method _uiSetRailSize
+ * @protected
+ */
+ _uiSetRailSize : function () {
+ var rail = this.get(RAIL),
+ thumb = this.get(THUMB),
+ img = this.get(THUMB_IMAGE),
+ dim = this._key.dim,
+ size = this.get(RAIL_SIZE),
+ setxy = false;
+
+ if (parseInt(size,10)) {
+ Y.log('railSize provided: '+size,'info','slider');
+
+ // Convert to pixels
+ rail.setStyle(dim,size);
+ size = parseInt(rail.getComputedStyle(dim),10);
+
+ Y.log('pixel '+dim+' of railSize: '+size+'px', 'info', 'slider');
+ } else {
+ Y.log('defaulting railSize from max of computed style and configured '+dim+' attribute value', 'info', 'slider');
+ // Default from height or width (axis respective), or dims assigned
+ // via css to the rail or thumb, whichever is largest.
+ // Dear implementers, please use railSize, not height/width to
+ // set the rail dims
+ size = this.get(dim);
+ if (parseInt(size,10)) {
+ setxy = true;
+ rail.setStyle(dim,size);
+ size = parseInt(rail.getComputedStyle(dim),10);
+ }
+ size = max(
+ size|0,
+ parseInt(thumb.getComputedStyle(dim),10),
+ parseInt(rail.getComputedStyle(dim),10));
+
+ Y.log('pixel '+dim+' of rail: '+size+'px', 'info', 'slider');
+
+ if (img && this._isImageLoaded(img)) {
+ Y.log('using max of thumbImage '+dim+' ('+img.get(dim)+' and '+size+' for railSize', 'info', 'slider');
+
+ size = max(img.get(dim),size);
+ }
+ }
+
+ rail.setStyle(dim, size + PX);
+
+ this._railSize = size;
+
+ // handle the (not recommended) fallback case of setting rail size via
+ // widget height/width params. This is the only case that sets the
+ // off-axis rail dim in the code.
+ if (setxy) {
+ dim = this._key.offAxisDim;
+ size = this.get(dim);
+ if (size) {
+ rail.set(dim,size);
+ }
+ }
+ },
+
+ /**
+ * Store the current XY position of the rail Node on the page. For use in
+ * calculating thumb position from value.
+ *
+ * @method _setRailOffsetXY
+ * @protected
+ */
+ _setRailOffsetXY : function () {
+ this._offsetXY = this.get(RAIL).getXY()[this._key.xyIndex] +
+ this.get(MIN_GUTTER);
+ },
+
+ /**
+ * Passes the gutter attribute value to the DDConstrain gutter attribute.
+ *
+ * @method _setDDGutter
+ * @protected
+ */
+ _setDDGutter : function () {
+ var gutter = this._key.xyIndex ?
+ this.get(MIN_GUTTER) + " 0 " + this.get(MAX_GUTTER) :
+ "0 " + this.get(MAX_GUTTER) + " 0 " + this.get(MIN_GUTTER);
+
+ Y.log('setting DDConstrain gutter "'+gutter+'"','info','slider');
+
+ this._dd.con.set('gutter', gutter);
+ },
+
+ /**
+ * Resets the cached region inside the DD constrain instance to support
+ * repositioning the Slider after instantiation.
+ *
+ * @method _resetDDCacheRegion
+ * @protected
+ */
+ _resetDDCacheRegion : function () {
+ // Workaround for ticket #2527964
+ this._dd.con._cacheRegion();
+ },
+
+ /**
+ * Calculates the multiplier used to translate the value into a thumb
+ * position.
+ *
+ * @method _setFactor
+ * @protected
+ */
+ _setFactor : function () {
+ var range = this._railSize - this._thumbSize -
+ this.get(MIN_GUTTER) - this.get(MAX_GUTTER);
+
+ this._factor = this._railSize ?
+ (this.get(MAX) - this.get(MIN)) / range :
+ 1;
+
+ Y.log('_factor set to '+this._factor,'info','slider');
+ },
+
+ /**
+ * Convenience method for accessing the current value of the Slider.
+ * Equivalent to <code>slider.get("value")</code>.
+ *
+ * @method getValue
+ * @return {Number} the value
+ */
+ getValue : function () {
+ return this.get(VALUE);
+ },
+
+ /**
+ * Convenience method for updating the current value of the Slider.
+ * Equivalent to <code>slider.set("value",val)</code>.
+ *
+ * @method setValue
+ * @param val {Number} the new value
+ */
+ setValue : function (val) {
+ this.set(VALUE,val);
+ },
+
+ /**
+ * Validator applied to new values for the axis attribute. Only
+ * "x" and "y" are permitted.
+ *
+ * @method _validateNewAxis
+ * @param v {String} proposed value for the axis attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewAxis : function (v) {
+ return isString(v) && 'xXyY'.indexOf(v.charAt(0)) > -1;
+ },
+
+ /**
+ * Validator applied to the min attribute.
+ *
+ * @method _validateNewMin
+ * @param v {MIXED} proposed value for the min attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewMin : function (v) {
+ return isNumber(v);
+ },
+
+ /**
+ * Validator applied to the max attribute.
+ *
+ * @method _validateNewMax
+ * @param v {MIXED} proposed value for the max attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewMax : function (v) {
+ return isNumber(v);
+ },
+
+ /**
+ * Validator applied to the value attribute.
+ *
+ * @method _validateNewValue
+ * @param v {MIXED} proposed value for the value attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewValue : function (v) {
+ var min = this.get(MIN),
+ max = this.get(MAX);
+
+ return isNumber(v) &&
+ (min < max ? (v >= min && v <= max) : (v >= max && v <= min));
+ },
+
+ /**
+ * Validator applied to the rail attribute. Rejects all values after the
+ * Slider has been rendered.
+ *
+ * @method _validateNewRail
+ * @param v {MIXED} proposed value for the rail attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewRail : function (v) {
+ return !this.get(RENDERED) || v;
+ },
+
+ /**
+ * Validator applied to the thumb attribute. Rejects all values after the
+ * Slider has been rendered.
+ *
+ * @method _validateNewThumb
+ * @param v {MIXED} proposed value for the thumb attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewThumb : function (v) {
+ return !this.get(RENDERED) || v;
+ },
+
+ /**
+ * Validator applied to the thumbImage attribute. Rejects all values after
+ * the Slider has been rendered.
+ *
+ * @method _validateNewThumbImage
+ * @param v {MIXED} proposed value for the thumbImage attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewThumbImage : function (v) {
+ return !this.get(RENDERED) || v;
+ },
+
+ /**
+ * Validator applied to the railSize attribute. Only strings of css size
+ * values (e.g. '200px') are allowed.
+ *
+ * @method _validateNewRailSize
+ * @param v {String} proposed value for the railSize attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewRailSize : function (v) {
+ return isString(v) &&
+ (v === '0' || /^\d+(?:p[xtc]|%|e[mx]|in|[mc]m)$/.test(v));
+ },
+
+ /**
+ * Setter applied to the input when updating the axis attribute.
+ *
+ * @method _setAxisFn
+ * @param v {String} proposed value for the axis attribute
+ * @return {String} lowercased first character of the input string
+ * @protected
+ */
+ _setAxisFn : function (v) {
+ return v.charAt(0).toLowerCase();
+ },
+
+ /**
+ * Setter applied to the input when updating the rail attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setRailFn
+ * @param v {Node|String|HTMLElement} The rail element Node or selector
+ * @return {Node} The Node if found. Otherwise null.
+ * @protected
+ */
+ _setRailFn : function (v) {
+ return Y.get(v) || null;
+ },
+
+ /**
+ * Setter applied to the input when updating the thumb attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setThumbFn
+ * @param v {Node|String|HTMLElement} The thumb element Node or selector
+ * @return {Node} The Node if found. Otherwise null.
+ * @protected
+ */
+ _setThumbFn : function (v) {
+ return Y.get(v) || null;
+ },
+
+ /**
+ * Setter applied to the input when updating the thumbImage attribute.
+ * Input can be a Node, raw HTMLElement, selector string to locate it, or
+ * the URL for an image resource.
+ *
+ * String input will be treated as a selector. If no element is found using
+ * the selector, an <code>img</code> Node will be created with the string
+ * used as the <code>src</code> attribute.
+ *
+ * @method _setThumbImageFn
+ * @param v {Node|String|HTMLElement} The thumbImage element Node, selector,
+ * or image URL
+ * @return {Node} The Node if found or created. Otherwise null.
+ * @protected
+ */
+ _setThumbImageFn : function (v) {
+ return v ? Y.get(v) ||
+ Y.Node.create('<img src="'+v+'" alt="Slider thumb">') :
+ null;
+ },
+
+
+ /**
+ * Caches the current page position of the rail element and fires the
+ * slideStart event in response to the DD's drag:start.
+ *
+ * @method _onDDStartDrag
+ * @param e {Event} the DD instance's drag:start custom event
+ * @protected
+ */
+ _onDDStartDrag : function (e) {
+ Y.log('slide start','info','slider');
+ this._setRailOffsetXY();
+ this.fire(SLIDE_START,{ ddEvent: e });
+ },
+
+ /**
+ * Fires the thumbDrag event to queue Slider value update.
+ *
+ * @method _onDDDrag
+ * @param e {Event} the DD instance's drag:drag custom event
+ * @protected
+ */
+ _onDDDrag : function (e) {
+ Y.log('thumb drag','info','slider');
+ this.fire(THUMB_DRAG, { ddEvent: e });
+ },
+
+ /**
+ * The default value update behavior in response to Slider thumb
+ * interaction. Calculates the value using stored offsets, the _factor
+ * multiplier and the min value.
+ *
+ * @method _defThumbDragFn
+ * @param e {Event} the internal thumbDrag event
+ * @protected
+ */
+ _defThumbDragFn : function (e) {
+ var before = this.get(VALUE),
+ val = e.ddEvent[this._key.eventPageAxis] - this._offsetXY;
+
+ Y.log('setting value from thumb drag: before('+before+') raw('+val+') factored('+this._convertOffsetToValue(val)+')', 'info','slider');
+
+ val = this._convertOffsetToValue(val);
+
+ if (before !== val) {
+ this.set(VALUE, val, { ddEvent: e.ddEvent });
+ }
+ },
+
+ /**
+ * Fires the slideEnd event.
+ *
+ * @method _onDDEndDrag
+ * @param e {Event} the DD instance's drag:end custom event
+ * @protected
+ */
+ _onDDEndDrag : function (e) {
+ Y.log('slide end','info','slider');
+ this.fire(SLIDE_END,{ ddEvent: e });
+ },
+
+
+
+
+ /**
+ * Calls _uiPositionThumb with the value of the custom event's
+ * "offset" property.
+ *
+ * @method _defPositionThumbFn
+ * @param e {Event} the positionThumb custom event
+ * @protected
+ */
+ _defPositionThumbFn : function (e) {
+ Y.log('setting thumb offset ('+e.offset+') from value attribute update ('+e.value+')', 'info', 'slider');
+
+ this._uiPositionThumb(e.offset);
+ },
+
+ /**
+ * Places the thumb at a particular X or Y location based on the configured
+ * axis.
+ *
+ * @method _uiPositionThumb
+ * @param xy {Number} the desired left or top pixel position of the thumb
+ * in relation to the rail Node.
+ * @protected
+ */
+ _uiPositionThumb : function (xy) {
+ var dd = this._dd,
+ thumb = dd.get('dragNode'),
+ hidden = thumb.ancestor(this._isDisplayNone);
+
+ if (!hidden) {
+ dd._setStartPosition(dd.get('dragNode').getXY());
+
+ // stickX/stickY config on DD instance will negate off-axis move
+ dd._alignNode([xy,xy],true);
+ }
+ },
+
+ /**
+ * Helper function to search up the ancestor axis looking for a node with
+ * style display: none. This is passed as a function to node.ancestor(..)
+ * to test if a given node is in the displayed DOM and can get accurate
+ * positioning information.
+ *
+ * @method _isDisplayNone
+ * @param el {Node} ancestor node as the function walks up the parent axis
+ * @return {Boolean} true if the node is styled with display: none
+ * @protected
+ */
+ _isDisplayNone : function (node) {
+ return node.getComputedStyle('display') === 'none';
+ },
+
+ /**
+ * Fires the internal positionThumb event in response to a change in the
+ * value attribute.
+ *
+ * @method _afterValueChange
+ * @param e {Event} valueChange custom event
+ * @protected
+ */
+ _afterValueChange : function (e) {
+ if (!e.ddEvent) {
+ var xy = this._convertValueToOffset(e.newVal);
+
+ Y.log('firing positionThumb to position thumb', 'info', 'slider');
+
+ this.fire(POSITION_THUMB,{ value: e.newVal, offset: xy });
+ }
+ },
+
+ /**
+ * Converts a value to a pixel offset for the thumb position on the rail.
+ *
+ * @method _convertValueToOffset
+ * @param v {Number} value between the Slider's min and max
+ * @protected
+ */
+ _convertValueToOffset : function (v) {
+ return round((v - this.get(MIN)) / this._factor) + this._offsetXY;
+ },
+
+ /**
+ * Converts a pixel offset of the thumb on the rail to a value.
+ *
+ * @method _convertOffsetToValue
+ * @param v {Number} pixel offset of the thumb on the rail
+ * @protected
+ */
+ _convertOffsetToValue : function (v) {
+ return round(this.get(MIN) + (v * this._factor));
+ },
+
+ /**
+ * Replaces the thumb Node in response to a change in the thumb attribute.
+ * This only has effect after the Slider is rendered.
+ *
+ * @method _afterThumbChange
+ * @param e {Event} thumbChange custom event
+ * @protected
+ */
+ _afterThumbChange : function (e) {
+ var thumb;
+
+ if (this.get(RENDERED)) {
+ if (e.prevValue) {
+ e.prevValue.get('parentNode').removeChild(e.prevValue);
+ }
+
+ this._initThumb();
+
+ thumb = this.get(THUMB);
+ this._dd.set('node',thumb);
+ this._dd.set('dragNode',thumb);
+
+ this.syncUI();
+ }
+ },
+
+ /**
+ * Sets or replaces the thumb's contained <code>img</code> Node with the
+ * new Node in response to a change in the thumbImage attribute. This only
+ * has effect after the Slider is rendered.
+ *
+ * @method _afterThumbImageChange
+ * @param e {Event} thumbImageChange custom event
+ * @protected
+ */
+ _afterThumbImageChange : function (e) {
+ if (this.get(RENDERED)) {
+ if (e.prevValue) {
+ e.prevValue.get('parentNode').removeChild(e.prevValue);
+ }
+
+ this._initThumbImage();
+
+ this.syncUI();
+ }
+ },
+
+ /**
+ * Updates the Slider UI in response to change in the min attribute.
+ *
+ * @method _afterMinChange
+ * @param e {Event} minChange custom event
+ * @protected
+ */
+ _afterMinChange : function (e) {
+ this._refresh(e);
+ },
+
+ /**
+ * Updates the Slider UI in response to change in the max attribute.
+ *
+ * @method _afterMaxChange
+ * @param e {Event} maxChange custom event
+ * @protected
+ */
+ _afterMaxChange : function (e) {
+ this._refresh(e);
+ },
+
+ /**
+ * Updates the Slider UI in response to change in the railSize attribute.
+ *
+ * @method _afterRailSizeChange
+ * @param e {Event} railSizeChange custom event
+ * @protected
+ */
+ _afterRailSizeChange : function (e) {
+ this._refresh(e);
+ },
+
+ /**
+ * Locks or unlocks the DD instance in response to a change in the disabled
+ * attribute.
+ *
+ * @method _afterDisabledChange
+ * @param e {Event} disabledChange custom event
+ * @protected
+ */
+ _afterDisabledChange : function (e) {
+ if (this._dd) {
+ this._dd.set('lock',e.newVal);
+ }
+ },
+
+ /**
+ * Common handler to call syncUI in response to change events that occurred
+ * after the Slider is rendered.
+ *
+ * @method _refresh
+ * @param e {Event} An attribute change event
+ * @protected
+ */
+ _refresh : function (e) {
+ if (e.newVal !== e.prevVal && this.get(RENDERED)) {
+ this.syncUI();
+ }
+ },
+
+ /**
+ * Used to determine if there is a current or pending request for the
+ * thumbImage resource.
+ *
+ * @method _isImageLoading
+ * @param img {Node} <code>img</code> Node
+ * @return Boolean
+ * @protected
+ */
+ _isImageLoading : function (img) {
+ return img && !img.get(COMPLETE);
+ },
+
+ /**
+ * Used to determine if the image resource loaded successfully or there was
+ * an error.
+ *
+ * NOTES:
+ * <ul>
+ * <li>img load error fired xbrowser for image resources not yet resolved</li>
+ * <li>img.complete reports false in IE for images not yet loaded as well as images that failed to load</li>
+ * <li>img.complete true && img.naturalWidth == 0 in FF and Safari indicate image failed to load</li>
+ * <li>img.complete && img.width == 0 in Opera indicates image failed to load</li>
+ * </ul>
+ *
+ * @method _isImageLoaded
+ * @param img {Node} <code>img</code> Node
+ * @return Boolean
+ * @protected
+ */
+ _isImageLoaded : function (img) {
+ if (img) {
+ var w = img.get('naturalWidth');
+ return img.get(COMPLETE) && (!isNumber(w) ? img.get(WIDTH) : w);
+ }
+
+ return true;
+ }
+
+});
+
+Y.Slider = Slider;
+
+
+}, '3.0.0' ,{requires:['widget','dd-constrain']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("slider",function(A){var i="slider",Z="rail",m="thumb",l="value",U="min",o="max",J="minGutter",b="maxGutter",R="thumbImage",n="railSize",c="contentBox",Q="slideStart",a="slideEnd",X="thumbDrag",e="sync",H="positionThumb",j="rendered",d="disabled",O="disabledChange",W=".",q="px",D="width",f="height",S="complete",K=A.Lang,g=K.isBoolean,P=K.isString,p=K.isNumber,G=A.ClassNameManager.getClassName,N="image",B=G(i,Z),C=G(i,m),T=G(i,m,N),E=G(i,N,"error"),I=Math,V=I.max,k=I.round,F=I.floor;function h(){h.superclass.constructor.apply(this,arguments);}A.mix(h,{NAME:i,_AXIS_KEYS:{x:{dim:D,offAxisDim:f,eventPageAxis:"pageX",ddStick:"stickX",xyIndex:0},y:{dim:f,offAxisDim:D,eventPageAxis:"pageY",ddStick:"stickY",xyIndex:1}},HTML_PARSER:{rail:W+B,thumb:W+C,thumbImage:W+T},ATTRS:{axis:{value:"x",writeOnce:true,validator:function(L){return this._validateNewAxis(L);},setter:function(L){return this._setAxisFn(L);}},min:{value:0,validator:function(L){return this._validateNewMin(L);}},max:{value:100,validator:function(L){return this._validateNewMax(L);}},value:{value:0,validator:function(L){return this._validateNewValue(L);}},rail:{value:null,validator:function(L){return this._validateNewRail(L);},setter:function(L){return this._setRailFn(L);}},thumb:{value:null,validator:function(L){return this._validateNewThumb(L);},setter:function(L){return this._setThumbFn(L);}},thumbImage:{value:null,validator:function(L){return this._validateNewThumbImage(L);},setter:function(L){return this._setThumbImageFn(L);}},railSize:{value:"0",validator:function(L){return this._validateNewRailSize(L);}},railEnabled:{value:true,validator:g},minGutter:{value:0,validator:p},maxGutter:{value:0,validator:p}}});A.extend(h,A.Widget,{_key:null,_factor:1,_railSize:null,_thumbSize:null,_thumbOffset:0,_stall:false,_disabled:false,initializer:function(){this._key=h._AXIS_KEYS[this.get("axis")];this.after("minChange",this._afterMinChange);this.after("maxChange",this._afterMaxChange);this.after("railSizeChange",this._afterRailSizeChange);this.publish(Q);this.publish(a);this.publish(e,{defaultFn:this._defSyncFn});this.publish(H,{defaultFn:this._defPositionThumbFn});},renderUI:function(){this._initRail();this._initThumb();},_initRail:function(){var L=this.get(c),M=this.get(Z);if(!M){M=L.appendChild(A.Node.create('<div class="'+B+'"></div>'));this.set(Z,M);}else{if(!L.contains(M)){L.appendChild(M);}}M.addClass(B);M.addClass(this.getClassName(Z,this.get("axis")));},_initThumb:function(){var M=this.get(Z),L=this.get(m);if(L&&!this.get(R)&&L.get("nodeName").toLowerCase()==="img"){this.set(R,L);this.set(m,null);L=null;}if(!L){L=A.Node.create('<div class="'+C+'"></div>');this.set(m,L);}L.addClass(C);if(!M.contains(L)){M.appendChild(L);}if(this.get(R)){this._initThumbImage();}},_initThumbImage:function(){var M=this.get(m),L=this.get(R);if(L){L.replaceClass(C,T);if(!M.contains(L)){M.appendChild(L);}}},bindUI:function(){this.publish(X,{defaultFn:this._defThumbDragFn});this._bindThumbDD();this.after("valueChange",this._afterValueChange);this.after("thumbImageChange",this._afterThumbImageChange);this.after(O,this._afterDisabledChange);},_bindThumbDD:function(){var M={node:this.get(m),bubble:false},L={constrain2node:this.get(Z)};L[this._key.ddStick]=true;this._dd=new A.DD.Drag(M).plug(A.Plugin.DDConstrained,L);this._dd.on("drag:start",A.bind(this._onDDStartDrag,this));this._dd.on("drag:drag",A.bind(this._onDDDrag,this));this._dd.on("drag:end",A.bind(this._onDDEndDrag,this));this._initRailDD();},_initRailDD:function(){this.get(Z).on("mousedown",A.bind(this._handleRailMouseDown,this));},_handleRailMouseDown:function(Y){if(this.get("railEnabled")&&!this.get(d)){var L=this._dd,r=this._key.xyIndex,M;if(L.get("primaryButtonOnly")&&Y.button>1){return false;}L._dragThreshMet=true;L._fixIEMouseDown();Y.halt();A.DD.DDM.activeDrag=L;M=L.get("dragNode").getXY();M[r]+=this._thumbOffset;L._setStartPosition(M);L.set("activeHandle",L.get("dragNode"));L.start();L._alignNode([Y.pageX,Y.pageY]);}},syncUI:function(){this.get(c).removeClass(E);var L=this.get(R);if(this._isImageLoading(L)){this._scheduleSync();}else{this._ready(L,!this._isImageLoaded(L));}},_scheduleSync:function(){var L,M;if(!this._stall){this._disabled=this.get(d);this.set(d,true);this._stall=this.on(O,this._stallDisabledChange);L=this.get(R);M=A.bind(this._imageLoaded,this,L);L.on("load",M);L.on("error",M);}},_stallDisabledChange:function(L){this._disabled=L.newVal;L.preventDefault();},_imageLoaded:function(L,Y){var M=(Y.type.toLowerCase().indexOf("error")>-1);A.later(0,this,function(){if(this._stall){this._stall.detach();}this._stall=false;this._ready(L,M);this.set(d,this._disabled);});},_ready:function(L,M){var Y=M?"addClass":"removeClass";this.get(c)[Y](E);this.fire(e);},_defSyncFn:function(L){this._uiSetThumbSize();this._setThumbOffset();this._uiSetRailSize();this._setRailOffsetXY();this._setDDGutter();this._resetDDCacheRegion();this._setFactor();var M=this.get(l);this.fire(H,{value:M,offset:this._convertValueToOffset(M)});this.get("boundingBox").toggleClass("");},_uiSetThumbSize:function(){var M=this.get(m),r=this._key.dim,L=this.get(R),Y;Y=parseInt(M.getComputedStyle(r),10);if(L&&this._isImageLoaded(L)){Y=L.get(r);}this._thumbSize=Y;},_setThumbOffset:function(){this._thumbOffset=F(this._thumbSize/2);},_uiSetRailSize:function(){var t=this.get(Z),M=this.get(m),L=this.get(R),s=this._key.dim,Y=this.get(n),r=false;if(parseInt(Y,10)){t.setStyle(s,Y);Y=parseInt(t.getComputedStyle(s),10);}else{Y=this.get(s);if(parseInt(Y,10)){r=true;t.setStyle(s,Y);Y=parseInt(t.getComputedStyle(s),10);}Y=V(Y|0,parseInt(M.getComputedStyle(s),10),parseInt(t.getComputedStyle(s),10));if(L&&this._isImageLoaded(L)){Y=V(L.get(s),Y);}}t.setStyle(s,Y+q);this._railSize=Y;if(r){s=this._key.offAxisDim;Y=this.get(s);if(Y){t.set(s,Y);}}},_setRailOffsetXY:function(){this._offsetXY=this.get(Z).getXY()[this._key.xyIndex]+this.get(J);},_setDDGutter:function(){var L=this._key.xyIndex?this.get(J)+" 0 "+this.get(b):"0 "+this.get(b)+" 0 "+this.get(J);
+this._dd.con.set("gutter",L);},_resetDDCacheRegion:function(){this._dd.con._cacheRegion();},_setFactor:function(){var L=this._railSize-this._thumbSize-this.get(J)-this.get(b);this._factor=this._railSize?(this.get(o)-this.get(U))/L:1;},getValue:function(){return this.get(l);},setValue:function(L){this.set(l,L);},_validateNewAxis:function(L){return P(L)&&"xXyY".indexOf(L.charAt(0))>-1;},_validateNewMin:function(L){return p(L);},_validateNewMax:function(L){return p(L);},_validateNewValue:function(M){var Y=this.get(U),L=this.get(o);return p(M)&&(Y<L?(M>=Y&&M<=L):(M>=L&&M<=Y));},_validateNewRail:function(L){return !this.get(j)||L;},_validateNewThumb:function(L){return !this.get(j)||L;},_validateNewThumbImage:function(L){return !this.get(j)||L;},_validateNewRailSize:function(L){return P(L)&&(L==="0"||/^\d+(?:p[xtc]|%|e[mx]|in|[mc]m)$/.test(L));},_setAxisFn:function(L){return L.charAt(0).toLowerCase();},_setRailFn:function(L){return A.get(L)||null;},_setThumbFn:function(L){return A.get(L)||null;},_setThumbImageFn:function(L){return L?A.get(L)||A.Node.create('<img src="'+L+'" alt="Slider thumb">'):null;},_onDDStartDrag:function(L){this._setRailOffsetXY();this.fire(Q,{ddEvent:L});},_onDDDrag:function(L){this.fire(X,{ddEvent:L});},_defThumbDragFn:function(M){var L=this.get(l),Y=M.ddEvent[this._key.eventPageAxis]-this._offsetXY;Y=this._convertOffsetToValue(Y);if(L!==Y){this.set(l,Y,{ddEvent:M.ddEvent});}},_onDDEndDrag:function(L){this.fire(a,{ddEvent:L});},_defPositionThumbFn:function(L){this._uiPositionThumb(L.offset);},_uiPositionThumb:function(r){var L=this._dd,M=L.get("dragNode"),Y=M.ancestor(this._isDisplayNone);if(!Y){L._setStartPosition(L.get("dragNode").getXY());L._alignNode([r,r],true);}},_isDisplayNone:function(L){return L.getComputedStyle("display")==="none";},_afterValueChange:function(M){if(!M.ddEvent){var L=this._convertValueToOffset(M.newVal);this.fire(H,{value:M.newVal,offset:L});}},_convertValueToOffset:function(L){return k((L-this.get(U))/this._factor)+this._offsetXY;},_convertOffsetToValue:function(L){return k(this.get(U)+(L*this._factor));},_afterThumbChange:function(M){var L;if(this.get(j)){if(M.prevValue){M.prevValue.get("parentNode").removeChild(M.prevValue);}this._initThumb();L=this.get(m);this._dd.set("node",L);this._dd.set("dragNode",L);this.syncUI();}},_afterThumbImageChange:function(L){if(this.get(j)){if(L.prevValue){L.prevValue.get("parentNode").removeChild(L.prevValue);}this._initThumbImage();this.syncUI();}},_afterMinChange:function(L){this._refresh(L);},_afterMaxChange:function(L){this._refresh(L);},_afterRailSizeChange:function(L){this._refresh(L);},_afterDisabledChange:function(L){if(this._dd){this._dd.set("lock",L.newVal);}},_refresh:function(L){if(L.newVal!==L.prevVal&&this.get(j)){this.syncUI();}},_isImageLoading:function(L){return L&&!L.get(S);},_isImageLoaded:function(M){if(M){var L=M.get("naturalWidth");return M.get(S)&&(!p(L)?M.get(D):L);}return true;}});A.Slider=h;},"3.0.0",{requires:["widget","dd-constrain"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('slider', function(Y) {
+
+/**
+ * Create a sliding value range input visualized as a draggable thumb on a
+ * background element.
+ *
+ * @module slider
+ */
+
+var SLIDER = 'slider',
+ RAIL = 'rail',
+ THUMB = 'thumb',
+ VALUE = 'value',
+ MIN = 'min',
+ MAX = 'max',
+ MIN_GUTTER = 'minGutter',
+ MAX_GUTTER = 'maxGutter',
+ THUMB_IMAGE = 'thumbImage',
+ RAIL_SIZE = 'railSize',
+ CONTENT_BOX = 'contentBox',
+
+ SLIDE_START = 'slideStart',
+ SLIDE_END = 'slideEnd',
+
+ THUMB_DRAG = 'thumbDrag',
+ SYNC = 'sync',
+ POSITION_THUMB = 'positionThumb',
+ RENDERED = 'rendered',
+ DISABLED = 'disabled',
+ DISABLED_CHANGE = 'disabledChange',
+
+ DOT = '.',
+ PX = 'px',
+ WIDTH = 'width',
+ HEIGHT = 'height',
+ COMPLETE = 'complete',
+
+ L = Y.Lang,
+ isBoolean= L.isBoolean,
+ isString = L.isString,
+ isNumber = L.isNumber,
+
+ getCN = Y.ClassNameManager.getClassName,
+
+ IMAGE = 'image',
+ C_RAIL = getCN(SLIDER,RAIL),
+ C_THUMB = getCN(SLIDER,THUMB),
+ C_THUMB_IMAGE = getCN(SLIDER,THUMB,IMAGE),
+ C_IMAGE_ERROR = getCN(SLIDER,IMAGE,'error'),
+
+ M = Math,
+ max = M.max,
+ round = M.round,
+ floor = M.floor;
+
+/**
+ * Create a slider to represent an integer value between a given minimum and
+ * maximum. Sliders may be aligned vertically or horizontally, based on the
+ * <code>axis</code> configuration.
+ *
+ * @class Slider
+ * @extends Widget
+ * @param config {Object} Configuration object
+ * @constructor
+ */
+function Slider() {
+ Slider.superclass.constructor.apply(this,arguments);
+}
+
+Y.mix(Slider, {
+
+ /**
+ * The identity of the widget.
+ *
+ * @property Slider.NAME
+ * @type String
+ * @static
+ */
+ NAME : SLIDER,
+
+ /**
+ * Object property names used for respective X and Y axis Sliders (e.g.
+ * "left" vs. "top" for placing the thumb according to
+ * its representative value).
+ *
+ * @property Slider._AXIS_KEYS
+ * @type Object
+ * @protected
+ * @static
+ */
+ _AXIS_KEYS : {
+ x : {
+ dim : WIDTH,
+ offAxisDim : HEIGHT,
+ eventPageAxis : 'pageX',
+ ddStick : 'stickX',
+ xyIndex : 0
+ },
+ y : {
+ dim : HEIGHT,
+ offAxisDim : WIDTH,
+ eventPageAxis : 'pageY',
+ ddStick : 'stickY',
+ xyIndex : 1
+ }
+ },
+
+ /**
+ * Static Object hash used to capture existing markup for progressive
+ * enhancement. Keys correspond to config attribute names and values
+ * are selectors used to inspect the contentBox for an existing node
+ * structure.
+ *
+ * @property Slider.HTML_PARSER
+ * @type Object
+ * @protected
+ * @static
+ */
+ HTML_PARSER : {
+ rail : DOT + C_RAIL,
+ thumb : DOT + C_THUMB,
+ thumbImage : DOT + C_THUMB_IMAGE
+ },
+
+ /**
+ * Static property used to define the default attribute configuration of
+ * the Widget.
+ *
+ * @property Slider.ATTRS
+ * @type Object
+ * @protected
+ * @static
+ */
+ ATTRS : {
+
+ /**
+ * Axis upon which the Slider's thumb moves. "x" for
+ * horizontal, "y" for vertical.
+ *
+ * @attribute axis
+ * @type String
+ * @default "x"
+ * @writeOnce
+ */
+ axis : {
+ value : 'x',
+ writeOnce : true,
+ validator : function (v) {
+ return this._validateNewAxis(v);
+ },
+ setter : function (v) {
+ return this._setAxisFn(v);
+ }
+ },
+
+ /**
+ * Value associated with the left or top most position of the thumb on
+ * the rail.
+ *
+ * @attribute min
+ * @type Number
+ * @default 0
+ */
+ min : {
+ value : 0,
+ validator : function (v) {
+ return this._validateNewMin(v);
+ }
+ },
+
+ /**
+ * Value associated with the right or bottom most position of the thumb
+ * on the rail.
+ *
+ * @attribute max
+ * @type Number
+ * @default 100
+ */
+ max : {
+ value : 100,
+ validator : function (v) {
+ return this._validateNewMax(v);
+ }
+ },
+
+ /**
+ * The current value of the Slider. This value is interpretted into a
+ * position for the thumb along the Slider's rail.
+ *
+ * @attribute value
+ * @type Number
+ * @default 0
+ */
+ value : {
+ value : 0,
+ validator : function (v) {
+ return this._validateNewValue(v);
+ }
+ },
+
+ /**
+ * The Node representing the Slider's rail, usually visualized as a
+ * bar of some sort using a background image, along which the thumb
+ * moves. This Node contains the thumb Node.
+ *
+ * @attribute rail
+ * @type Node
+ * @default null
+ */
+ rail : {
+ value : null,
+ validator : function (v) {
+ return this._validateNewRail(v);
+ },
+ setter : function (v) {
+ return this._setRailFn(v);
+ }
+ },
+
+ /**
+ * <p>The Node representing the Slider's thumb, usually visualized as a
+ * pointer using a contained image Node (see thumbImage). The current
+ * value of the Slider is calculated from the centerpoint of this
+ * Node in relation to the rail Node. If provided, the thumbImage
+ * Node is contained within this Node.</p>
+ *
+ * <p>If no thumbImage is provided and the Node passed as the thumb is
+ * an <code>img</code> element, the assigned Node will be allocated to
+ * the thumbImage and the thumb container defaulted.</p>
+ *
+ * @attribute thumb
+ * @type Node
+ * @default null
+ */
+ thumb : {
+ value : null,
+ validator : function (v) {
+ return this._validateNewThumb(v);
+ },
+ setter : function (v) {
+ return this._setThumbFn(v);
+ }
+ },
+
+ /**
+ * <p>The Node representing the image element to use for the Slider's
+ * thumb.</p>
+ *
+ * <p>Alternately, an image URL can be passed and an <code>img</code>
+ * Node will be generated accordingly.</p>
+ *
+ * <p>If no thumbImage is provided and the Node passed as the thumb is
+ * an <code>img</code> element, the assigned Node will be allocated to
+ * the thumbImage and the thumb container defaulted.</p>
+ *
+ * <p>If thumbImage is provided but its URL resolves to a 404, a default
+ * style will be applied to maintain basic functionality.</p>
+ *
+ * @attribute thumbImage
+ * @type Node|String
+ * @default null
+ */
+ thumbImage : {
+ value : null,
+ validator : function (v) {
+ return this._validateNewThumbImage(v);
+ },
+ setter : function (v) {
+ return this._setThumbImageFn(v);
+ }
+ },
+
+ /**
+ * <p>The width or height of the rail element representing the physical
+ * space along which the thumb can move. CSS size values (e.g. '30em')
+ * accepted but converted to pixels during render.</p>
+ *
+ * <p>Alternately, but not recommended, this attribute can be left
+ * unassigned in favor of specifying height or width.</p>
+ *
+ * @attribute railSize
+ * @type String
+ * @default '0'
+ */
+ railSize : {
+ value : '0',
+ validator : function (v) {
+ return this._validateNewRailSize(v);
+ }
+ },
+
+ /**
+ * Boolean indicating whether clicking and dragging on the rail will
+ * trigger thumb movement.
+ *
+ * @attribute railEnabled
+ * @type Boolean
+ * @default true
+ */
+ railEnabled : {
+ value : true,
+ validator : isBoolean
+ },
+
+ /**
+ * Like CSS padding, the distance in pixels from the inner top or left
+ * edge of the rail node within which the thumb can travel. Negative
+ * values allow the edge of the thumb to escape the rail node
+ * boundaries.
+ *
+ * @attribute minGutter
+ * @type Number
+ * @default 0
+ */
+ minGutter : {
+ value : 0,
+ validator : isNumber
+ },
+
+ /**
+ * Like CSS padding, the distance in pixels from the inner bottom or
+ * right edge of the rail node within which the thumb can travel.
+ * Negative values allow the edge of the thumb to escape the rail node
+ * boundaries.
+ *
+ * @attribute maxGutter
+ * @type Number
+ * @default 0
+ */
+ maxGutter : {
+ value : 0,
+ validator : isNumber
+ }
+ }
+});
+
+Y.extend(Slider, Y.Widget, {
+
+ /**
+ * Collection of object property names from the appropriate hash set in
+ * Slider._AXIS_KEYS.
+ *
+ * @property _key
+ * @type Object
+ * @protected
+ */
+ _key : null,
+
+ /**
+ * Factor used to translate positional coordinates (e.g. left or top) to
+ * the Slider's value.
+ *
+ * @property _factor
+ * @type Number
+ * @protected
+ */
+ _factor : 1,
+
+ /**
+ * Pixel dimension of the rail Node's width for X axis Sliders or height
+ * for Y axis Sliders. Used with _factor to calculate positional
+ * coordinates for the thumb.
+ *
+ * @property _railSize
+ * @type Number
+ * @protected
+ */
+ _railSize : null,
+
+ /**
+ * Pixel dimension of the thumb Node's width for X axis Sliders or height
+ * for Y axis Sliders. Used with _factor to calculate positional
+ * coordinates for the thumb.
+ *
+ * @property _thumbSize
+ * @type Number
+ * @protected
+ */
+ _thumbSize : null,
+
+ /**
+ * Pixel offset of the point in the thumb element from its top/left edge
+ * to where the value calculation should take place. By default, this is
+ * calculated to half the width of the thumb, causing the value to be
+ * marked from the center of the thumb.
+ *
+ * @property _thumbOffset
+ * @type Number
+ * @protected
+ */
+ _thumbOffset : 0,
+
+ /**
+ * Object returned from temporary subscription to disabledChange event to
+ * defer setting the disabled state while Slider is loading the thumb
+ * image.
+ *
+ * @property _stall
+ * @type Object
+ * @protected
+ */
+ _stall : false,
+
+ /**
+ * Deferred value for the disabled attribute when stalled (see _stall
+ * property).
+ *
+ * @property _disabled
+ * @type Boolean
+ * @protected
+ */
+ _disabled : false,
+
+ /**
+ * Construction logic executed durint Slider instantiation. Subscribes to
+ * after events for min, max, and railSize. Publishes custom events
+ * including slideStart and slideEnd.
+ *
+ * @method initializer
+ * @protected
+ */
+ initializer : function () {
+ this._key = Slider._AXIS_KEYS[this.get('axis')];
+
+ this.after('minChange', this._afterMinChange);
+ this.after('maxChange', this._afterMaxChange);
+
+ this.after('railSizeChange', this._afterRailSizeChange);
+
+ /**
+ * Signals the beginning of a thumb drag operation. Payload includes
+ * the DD.Drag instance's drag:start event under key ddEvent.
+ *
+ * @event slideStart
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>ddEvent</dt>
+ * <dd><code>drag:start</code> event from the managed DD.Drag instance</dd>
+ * </dl>
+ */
+ this.publish(SLIDE_START);
+
+ /**
+ * Signals the end of a thumb drag operation. Payload includes
+ * the DD.Drag instance's drag:end event under key ddEvent.
+ *
+ * @event slideEnd
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>ddEvent</dt>
+ * <dd><code>drag:end</code> event from the managed DD.Drag instance</dd>
+ * </dl>
+ */
+ this.publish(SLIDE_END);
+
+ /**
+ * Communicates a request to synchronize the Slider UI with the
+ * attribute state. Links the sync request with the default sync
+ * logic in _defSyncFn.
+ *
+ * @event sync
+ * @param event {Event.Facade} Event Facade object
+ * @preventable _defSyncFn
+ */
+ this.publish(SYNC, { defaultFn: this._defSyncFn });
+
+ /**
+ * Signals a request to reposition the thumb in response to API methods.
+ * Triggers the thumb placement logic in _defPositionThumbFn.
+ *
+ * @event positionThumb
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>changeEv</dt>
+ * <dd><code>valueChange</code> event fired in response to the change in the value attribute</dd>
+ * </dl>
+ * @preventable _defPositionThumbFn
+ */
+ this.publish(POSITION_THUMB, { defaultFn: this._defPositionThumbFn });
+ },
+
+ /**
+ * Create the DOM structure for the Slider.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI : function () {
+ this._initRail();
+ this._initThumb();
+ },
+
+ /**
+ * Creates the rail element if not provided and not discovered via
+ * HTML_PARSER.
+ *
+ * @method _initRail
+ * @protected
+ */
+ _initRail : function () {
+ var cb = this.get(CONTENT_BOX),
+ rail = this.get(RAIL);
+
+ // Create rail if necessary. Make sure it's in the contentBox
+ if (!rail) {
+ rail = cb.appendChild(
+ Y.Node.create('<div class="'+C_RAIL+'"></div>'));
+
+ this.set(RAIL,rail);
+ } else if (!cb.contains(rail)) {
+ cb.appendChild(rail);
+ }
+
+ rail.addClass(C_RAIL);
+ rail.addClass(this.getClassName(RAIL,this.get('axis')));
+ },
+
+ /**
+ * <p>Creates the thumb element (not image) if not provided and not
+ * discovered via HTML_PARSER. If the thumb is an <code>img</code> element
+ * but no thumbImage configured or discovered, reassigns the thumb element
+ * to the thumbImage and defaults the thumb element as a div.</p>
+ *
+ * <p>Makes sure the thumb is a child of the rail element and calls
+ * _initThumbImage if thumbImage is provided.</p>
+ *
+ * @method _initThumb
+ * @protected
+ */
+ _initThumb : function () {
+ var rail = this.get(RAIL),
+ thumb = this.get(THUMB);
+
+ // Passed an img element as the thumb
+ if (thumb && !this.get(THUMB_IMAGE) &&
+ thumb.get('nodeName').toLowerCase() === 'img') {
+ this.set(THUMB_IMAGE, thumb);
+ this.set(THUMB,null);
+ thumb = null;
+ }
+
+ if (!thumb) {
+ thumb = Y.Node.create(
+ '<div class="'+C_THUMB+'"></div>');
+
+ this.set(THUMB,thumb);
+ }
+
+ thumb.addClass(C_THUMB);
+
+ if (!rail.contains(thumb)) {
+ rail.appendChild(thumb);
+ }
+
+ if (this.get(THUMB_IMAGE)) {
+ this._initThumbImage();
+ }
+ },
+
+ /**
+ * Ensures the thumbImage is a child of the thumb element.
+ *
+ * @method _initThumbImage
+ * @protected
+ */
+ _initThumbImage : function () {
+ var thumb = this.get(THUMB),
+ img = this.get(THUMB_IMAGE);
+
+ if (img) {
+ img.replaceClass(C_THUMB,C_THUMB_IMAGE);
+
+ if (!thumb.contains(img)) {
+ thumb.appendChild(img);
+ }
+ }
+ },
+
+ /**
+ * Creates the Y.DD instance used to handle the thumb movement and binds
+ * Slider interaction to the configured value model.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI : function () {
+ /**
+ * Bridges user interaction with the thumb to the value attribute.
+ *
+ * @event thumbDrag
+ * @param event {Event.Facade} An Event Facade object with the following attribute specific properties added:
+ * <dl>
+ * <dt>ddEvent</dt>
+ * <dd><code>drag:drag</code> event from the managed DD.Drag instance</dd>
+ * </dl>
+ * @preventable _defThumbDragFn
+ */
+ this.publish(THUMB_DRAG, {defaultFn: this._defThumbDragFn});
+
+ this._bindThumbDD();
+
+ this.after('valueChange', this._afterValueChange);
+ this.after('thumbImageChange', this._afterThumbImageChange);
+ this.after(DISABLED_CHANGE, this._afterDisabledChange);
+ },
+
+ /**
+ * Creates the Y.DD instance used to handle the thumb interaction.
+ *
+ * @method _bindThumbDD
+ * @protected
+ */
+ _bindThumbDD : function () {
+ var ddConf = {
+ node : this.get(THUMB),
+ bubble : false
+ },
+ conConf = {
+ constrain2node : this.get(RAIL)
+ };
+
+ conConf[this._key.ddStick] = true;
+
+ this._dd = new Y.DD.Drag(ddConf).plug(Y.Plugin.DDConstrained, conConf);
+ this._dd.on('drag:start', Y.bind(this._onDDStartDrag, this));
+ this._dd.on('drag:drag', Y.bind(this._onDDDrag, this));
+ this._dd.on('drag:end', Y.bind(this._onDDEndDrag, this));
+
+ this._initRailDD();
+ },
+
+ /**
+ * Subscribes to the rail Node's mousedown event to actuate the thumb when
+ * backgroundEnabled is true.
+ *
+ * @method _initRailDD
+ * @protected
+ */
+ _initRailDD : function () {
+ this.get(RAIL).on('mousedown',Y.bind(this._handleRailMouseDown,this));
+ },
+
+ /**
+ * If the Slider is not disabled and railEnabled is true, moves the thumb
+ * to the mousedown position and hands control over to DD.
+ *
+ * @method _handleRailMouseDown
+ * @param e {Event} Mousedown event facade
+ * @protected
+ */
+ _handleRailMouseDown : function (e) {
+ if (this.get('railEnabled') && !this.get(DISABLED)) {
+ var dd = this._dd,
+ xyIndex = this._key.xyIndex,
+ xy;
+
+ if (dd.get('primaryButtonOnly') && e.button > 1) {
+ return false;
+ }
+
+ dd._dragThreshMet = true;
+
+ dd._fixIEMouseDown();
+ e.halt();
+
+ Y.DD.DDM.activeDrag = dd;
+
+ // Adjust registered starting position by half the thumb's x/y
+ xy = dd.get('dragNode').getXY();
+ xy[xyIndex] += this._thumbOffset;
+
+ dd._setStartPosition(xy);
+ dd.set('activeHandle',dd.get('dragNode'));
+
+ dd.start();
+ dd._alignNode([e.pageX,e.pageY]);
+ }
+ },
+
+ /**
+ * Synchronizes the DOM state with the attribute settings (most notably
+ * railSize and value). If thumbImage is provided and is still loading,
+ * sync is delayed until it is complete, since the image's dimensions are
+ * taken into consideration for calculations.
+ *
+ * @method syncUI
+ */
+ syncUI : function () {
+ this.get(CONTENT_BOX).removeClass(C_IMAGE_ERROR);
+
+ var img = this.get(THUMB_IMAGE);
+
+ if (this._isImageLoading(img)) {
+ // Schedule the sync for when the image loads/errors
+ this._scheduleSync();
+ } else {
+ this._ready(img,!this._isImageLoaded(img));
+ }
+ },
+
+ /**
+ * Binds to the load and error event on the thumbImage to sync the DOM
+ * state with the attribute settings when the image resource is resolved.
+ * The Slider is disabled while it waits.
+ *
+ * @method _scheduleSync
+ * @protected
+ */
+ _scheduleSync : function () {
+ var img, handler;
+
+ if (!this._stall) {
+ // disable the control until the image is loaded
+ this._disabled = this.get(DISABLED);
+ this.set(DISABLED,true);
+ this._stall = this.on(DISABLED_CHANGE,this._stallDisabledChange);
+
+ img = this.get(THUMB_IMAGE);
+ handler = Y.bind(this._imageLoaded,this,img);
+ img.on('load', handler);
+ img.on('error',handler);
+ }
+ },
+
+ /**
+ * Method subscribed to the disabledChange event when thumbImage is being
+ * loaded. Prevents manually enabling the Slider until the thumbImage
+ * resource is resolved. Intended value is stored during load and set upon
+ * completion.
+ *
+ * @method _stallDisabledChange
+ * @param e {Event} Change event for the disabled attribute
+ * @protected
+ */
+ _stallDisabledChange : function (e) {
+ this._disabled = e.newVal;
+ e.preventDefault();
+ },
+
+ /**
+ * Event handler assigned to the thumbImage's load and error event if it
+ * was not loaded prior to instantiation. Restores the disabled value.
+ *
+ * @method _imageLoaded
+ * @param img {Node} The thumbImage Node
+ * @param e {Event} load or error event fired by the thumbImage
+ * @protected
+ */
+ _imageLoaded : function (img,e) {
+ var error = (e.type.toLowerCase().indexOf('error') > -1);
+
+ // Need to execute inside a setTimeout because IE doesn't report
+ // img.complete === true until after the img.onload handler
+ // @TODO: readyState reports correctly in onload. Lose this wrapper
+ // and use that in _isImageLoaded.
+ Y.later(0, this, function () {
+ if (this._stall) {
+ this._stall.detach();
+ }
+
+
+ this._stall = false;
+
+ this._ready(img,error);
+
+ this.set(DISABLED,this._disabled);
+ });
+ },
+
+ /**
+ * Applies a class to the content box if the thumbImage failed to resolve,
+ * the fires the internal sync event triggering a sync between UI and
+ * state.
+ *
+ * @method _ready
+ * @param img {Node} the thumbImage Node
+ * @param error {Boolean} Indicates an error while loading the thumbImage
+ * @protected
+ */
+ _ready : function (img,error) {
+ var method = error ? 'addClass' : 'removeClass';
+
+ // If the thumb image url results in 404, assign a class to provide
+ // default thumb dimensions/UI
+ this.get(CONTENT_BOX)[method](C_IMAGE_ERROR);
+
+ this.fire(SYNC);
+ },
+
+ /**
+ * The default synchronization behavior, updating the Slider's DOM state to
+ * match the current attribute values.
+ *
+ * @method _defSyncFn
+ * @param e {Event} Internal sync event
+ * @protected
+ */
+ _defSyncFn : function (e) {
+ this._uiSetThumbSize();
+
+ this._setThumbOffset();
+
+ this._uiSetRailSize();
+
+ this._setRailOffsetXY();
+
+ this._setDDGutter();
+
+ this._resetDDCacheRegion();
+
+ this._setFactor();
+
+ var val = this.get(VALUE);
+
+ this.fire(POSITION_THUMB, {
+ value : val,
+ offset : this._convertValueToOffset(val)
+ });
+
+ // Forces a reflow of the bounding box to address IE8 inline-block
+ // container not expanding correctly. bug 2527905
+ this.get('boundingBox').toggleClass('');
+ },
+
+ /**
+ * Captures the thumb's pixel height or width (depending on the Slider's
+ * axis) for use in positioning calculations.
+ *
+ * @method _uiSetThumbSize
+ * @protected
+ */
+ _uiSetThumbSize : function () {
+ var thumb = this.get(THUMB),
+ dim = this._key.dim,
+ img = this.get(THUMB_IMAGE),
+ size;
+
+ // offsetWidth fails in hidden containers
+ size = parseInt(thumb.getComputedStyle(dim),10);
+
+
+ if (img && this._isImageLoaded(img)) {
+
+ size = img.get(dim);
+ }
+
+ this._thumbSize = size;
+ },
+
+ /**
+ * Establishes the point in the thumb that should align to the rail
+ * position representing the calculated value.
+ *
+ * @method _setThumbOffset
+ * @protected
+ */
+ _setThumbOffset : function () {
+ this._thumbOffset = floor(this._thumbSize / 2);
+ },
+
+ /**
+ * Stores the rail Node's pixel height or width, depending on the Slider's
+ * axis, for use in calculating thumb position from the value.
+ *
+ * @method _uiSetRailSize
+ * @protected
+ */
+ _uiSetRailSize : function () {
+ var rail = this.get(RAIL),
+ thumb = this.get(THUMB),
+ img = this.get(THUMB_IMAGE),
+ dim = this._key.dim,
+ size = this.get(RAIL_SIZE),
+ setxy = false;
+
+ if (parseInt(size,10)) {
+
+ // Convert to pixels
+ rail.setStyle(dim,size);
+ size = parseInt(rail.getComputedStyle(dim),10);
+
+ } else {
+ // Default from height or width (axis respective), or dims assigned
+ // via css to the rail or thumb, whichever is largest.
+ // Dear implementers, please use railSize, not height/width to
+ // set the rail dims
+ size = this.get(dim);
+ if (parseInt(size,10)) {
+ setxy = true;
+ rail.setStyle(dim,size);
+ size = parseInt(rail.getComputedStyle(dim),10);
+ }
+ size = max(
+ size|0,
+ parseInt(thumb.getComputedStyle(dim),10),
+ parseInt(rail.getComputedStyle(dim),10));
+
+
+ if (img && this._isImageLoaded(img)) {
+
+ size = max(img.get(dim),size);
+ }
+ }
+
+ rail.setStyle(dim, size + PX);
+
+ this._railSize = size;
+
+ // handle the (not recommended) fallback case of setting rail size via
+ // widget height/width params. This is the only case that sets the
+ // off-axis rail dim in the code.
+ if (setxy) {
+ dim = this._key.offAxisDim;
+ size = this.get(dim);
+ if (size) {
+ rail.set(dim,size);
+ }
+ }
+ },
+
+ /**
+ * Store the current XY position of the rail Node on the page. For use in
+ * calculating thumb position from value.
+ *
+ * @method _setRailOffsetXY
+ * @protected
+ */
+ _setRailOffsetXY : function () {
+ this._offsetXY = this.get(RAIL).getXY()[this._key.xyIndex] +
+ this.get(MIN_GUTTER);
+ },
+
+ /**
+ * Passes the gutter attribute value to the DDConstrain gutter attribute.
+ *
+ * @method _setDDGutter
+ * @protected
+ */
+ _setDDGutter : function () {
+ var gutter = this._key.xyIndex ?
+ this.get(MIN_GUTTER) + " 0 " + this.get(MAX_GUTTER) :
+ "0 " + this.get(MAX_GUTTER) + " 0 " + this.get(MIN_GUTTER);
+
+
+ this._dd.con.set('gutter', gutter);
+ },
+
+ /**
+ * Resets the cached region inside the DD constrain instance to support
+ * repositioning the Slider after instantiation.
+ *
+ * @method _resetDDCacheRegion
+ * @protected
+ */
+ _resetDDCacheRegion : function () {
+ // Workaround for ticket #2527964
+ this._dd.con._cacheRegion();
+ },
+
+ /**
+ * Calculates the multiplier used to translate the value into a thumb
+ * position.
+ *
+ * @method _setFactor
+ * @protected
+ */
+ _setFactor : function () {
+ var range = this._railSize - this._thumbSize -
+ this.get(MIN_GUTTER) - this.get(MAX_GUTTER);
+
+ this._factor = this._railSize ?
+ (this.get(MAX) - this.get(MIN)) / range :
+ 1;
+
+ },
+
+ /**
+ * Convenience method for accessing the current value of the Slider.
+ * Equivalent to <code>slider.get("value")</code>.
+ *
+ * @method getValue
+ * @return {Number} the value
+ */
+ getValue : function () {
+ return this.get(VALUE);
+ },
+
+ /**
+ * Convenience method for updating the current value of the Slider.
+ * Equivalent to <code>slider.set("value",val)</code>.
+ *
+ * @method setValue
+ * @param val {Number} the new value
+ */
+ setValue : function (val) {
+ this.set(VALUE,val);
+ },
+
+ /**
+ * Validator applied to new values for the axis attribute. Only
+ * "x" and "y" are permitted.
+ *
+ * @method _validateNewAxis
+ * @param v {String} proposed value for the axis attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewAxis : function (v) {
+ return isString(v) && 'xXyY'.indexOf(v.charAt(0)) > -1;
+ },
+
+ /**
+ * Validator applied to the min attribute.
+ *
+ * @method _validateNewMin
+ * @param v {MIXED} proposed value for the min attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewMin : function (v) {
+ return isNumber(v);
+ },
+
+ /**
+ * Validator applied to the max attribute.
+ *
+ * @method _validateNewMax
+ * @param v {MIXED} proposed value for the max attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewMax : function (v) {
+ return isNumber(v);
+ },
+
+ /**
+ * Validator applied to the value attribute.
+ *
+ * @method _validateNewValue
+ * @param v {MIXED} proposed value for the value attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewValue : function (v) {
+ var min = this.get(MIN),
+ max = this.get(MAX);
+
+ return isNumber(v) &&
+ (min < max ? (v >= min && v <= max) : (v >= max && v <= min));
+ },
+
+ /**
+ * Validator applied to the rail attribute. Rejects all values after the
+ * Slider has been rendered.
+ *
+ * @method _validateNewRail
+ * @param v {MIXED} proposed value for the rail attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewRail : function (v) {
+ return !this.get(RENDERED) || v;
+ },
+
+ /**
+ * Validator applied to the thumb attribute. Rejects all values after the
+ * Slider has been rendered.
+ *
+ * @method _validateNewThumb
+ * @param v {MIXED} proposed value for the thumb attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewThumb : function (v) {
+ return !this.get(RENDERED) || v;
+ },
+
+ /**
+ * Validator applied to the thumbImage attribute. Rejects all values after
+ * the Slider has been rendered.
+ *
+ * @method _validateNewThumbImage
+ * @param v {MIXED} proposed value for the thumbImage attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewThumbImage : function (v) {
+ return !this.get(RENDERED) || v;
+ },
+
+ /**
+ * Validator applied to the railSize attribute. Only strings of css size
+ * values (e.g. '200px') are allowed.
+ *
+ * @method _validateNewRailSize
+ * @param v {String} proposed value for the railSize attribute
+ * @return Boolean
+ * @protected
+ */
+ _validateNewRailSize : function (v) {
+ return isString(v) &&
+ (v === '0' || /^\d+(?:p[xtc]|%|e[mx]|in|[mc]m)$/.test(v));
+ },
+
+ /**
+ * Setter applied to the input when updating the axis attribute.
+ *
+ * @method _setAxisFn
+ * @param v {String} proposed value for the axis attribute
+ * @return {String} lowercased first character of the input string
+ * @protected
+ */
+ _setAxisFn : function (v) {
+ return v.charAt(0).toLowerCase();
+ },
+
+ /**
+ * Setter applied to the input when updating the rail attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setRailFn
+ * @param v {Node|String|HTMLElement} The rail element Node or selector
+ * @return {Node} The Node if found. Otherwise null.
+ * @protected
+ */
+ _setRailFn : function (v) {
+ return Y.get(v) || null;
+ },
+
+ /**
+ * Setter applied to the input when updating the thumb attribute. Input can
+ * be a Node, raw HTMLElement, or a selector string to locate it.
+ *
+ * @method _setThumbFn
+ * @param v {Node|String|HTMLElement} The thumb element Node or selector
+ * @return {Node} The Node if found. Otherwise null.
+ * @protected
+ */
+ _setThumbFn : function (v) {
+ return Y.get(v) || null;
+ },
+
+ /**
+ * Setter applied to the input when updating the thumbImage attribute.
+ * Input can be a Node, raw HTMLElement, selector string to locate it, or
+ * the URL for an image resource.
+ *
+ * String input will be treated as a selector. If no element is found using
+ * the selector, an <code>img</code> Node will be created with the string
+ * used as the <code>src</code> attribute.
+ *
+ * @method _setThumbImageFn
+ * @param v {Node|String|HTMLElement} The thumbImage element Node, selector,
+ * or image URL
+ * @return {Node} The Node if found or created. Otherwise null.
+ * @protected
+ */
+ _setThumbImageFn : function (v) {
+ return v ? Y.get(v) ||
+ Y.Node.create('<img src="'+v+'" alt="Slider thumb">') :
+ null;
+ },
+
+
+ /**
+ * Caches the current page position of the rail element and fires the
+ * slideStart event in response to the DD's drag:start.
+ *
+ * @method _onDDStartDrag
+ * @param e {Event} the DD instance's drag:start custom event
+ * @protected
+ */
+ _onDDStartDrag : function (e) {
+ this._setRailOffsetXY();
+ this.fire(SLIDE_START,{ ddEvent: e });
+ },
+
+ /**
+ * Fires the thumbDrag event to queue Slider value update.
+ *
+ * @method _onDDDrag
+ * @param e {Event} the DD instance's drag:drag custom event
+ * @protected
+ */
+ _onDDDrag : function (e) {
+ this.fire(THUMB_DRAG, { ddEvent: e });
+ },
+
+ /**
+ * The default value update behavior in response to Slider thumb
+ * interaction. Calculates the value using stored offsets, the _factor
+ * multiplier and the min value.
+ *
+ * @method _defThumbDragFn
+ * @param e {Event} the internal thumbDrag event
+ * @protected
+ */
+ _defThumbDragFn : function (e) {
+ var before = this.get(VALUE),
+ val = e.ddEvent[this._key.eventPageAxis] - this._offsetXY;
+
+
+ val = this._convertOffsetToValue(val);
+
+ if (before !== val) {
+ this.set(VALUE, val, { ddEvent: e.ddEvent });
+ }
+ },
+
+ /**
+ * Fires the slideEnd event.
+ *
+ * @method _onDDEndDrag
+ * @param e {Event} the DD instance's drag:end custom event
+ * @protected
+ */
+ _onDDEndDrag : function (e) {
+ this.fire(SLIDE_END,{ ddEvent: e });
+ },
+
+
+
+
+ /**
+ * Calls _uiPositionThumb with the value of the custom event's
+ * "offset" property.
+ *
+ * @method _defPositionThumbFn
+ * @param e {Event} the positionThumb custom event
+ * @protected
+ */
+ _defPositionThumbFn : function (e) {
+
+ this._uiPositionThumb(e.offset);
+ },
+
+ /**
+ * Places the thumb at a particular X or Y location based on the configured
+ * axis.
+ *
+ * @method _uiPositionThumb
+ * @param xy {Number} the desired left or top pixel position of the thumb
+ * in relation to the rail Node.
+ * @protected
+ */
+ _uiPositionThumb : function (xy) {
+ var dd = this._dd,
+ thumb = dd.get('dragNode'),
+ hidden = thumb.ancestor(this._isDisplayNone);
+
+ if (!hidden) {
+ dd._setStartPosition(dd.get('dragNode').getXY());
+
+ // stickX/stickY config on DD instance will negate off-axis move
+ dd._alignNode([xy,xy],true);
+ }
+ },
+
+ /**
+ * Helper function to search up the ancestor axis looking for a node with
+ * style display: none. This is passed as a function to node.ancestor(..)
+ * to test if a given node is in the displayed DOM and can get accurate
+ * positioning information.
+ *
+ * @method _isDisplayNone
+ * @param el {Node} ancestor node as the function walks up the parent axis
+ * @return {Boolean} true if the node is styled with display: none
+ * @protected
+ */
+ _isDisplayNone : function (node) {
+ return node.getComputedStyle('display') === 'none';
+ },
+
+ /**
+ * Fires the internal positionThumb event in response to a change in the
+ * value attribute.
+ *
+ * @method _afterValueChange
+ * @param e {Event} valueChange custom event
+ * @protected
+ */
+ _afterValueChange : function (e) {
+ if (!e.ddEvent) {
+ var xy = this._convertValueToOffset(e.newVal);
+
+
+ this.fire(POSITION_THUMB,{ value: e.newVal, offset: xy });
+ }
+ },
+
+ /**
+ * Converts a value to a pixel offset for the thumb position on the rail.
+ *
+ * @method _convertValueToOffset
+ * @param v {Number} value between the Slider's min and max
+ * @protected
+ */
+ _convertValueToOffset : function (v) {
+ return round((v - this.get(MIN)) / this._factor) + this._offsetXY;
+ },
+
+ /**
+ * Converts a pixel offset of the thumb on the rail to a value.
+ *
+ * @method _convertOffsetToValue
+ * @param v {Number} pixel offset of the thumb on the rail
+ * @protected
+ */
+ _convertOffsetToValue : function (v) {
+ return round(this.get(MIN) + (v * this._factor));
+ },
+
+ /**
+ * Replaces the thumb Node in response to a change in the thumb attribute.
+ * This only has effect after the Slider is rendered.
+ *
+ * @method _afterThumbChange
+ * @param e {Event} thumbChange custom event
+ * @protected
+ */
+ _afterThumbChange : function (e) {
+ var thumb;
+
+ if (this.get(RENDERED)) {
+ if (e.prevValue) {
+ e.prevValue.get('parentNode').removeChild(e.prevValue);
+ }
+
+ this._initThumb();
+
+ thumb = this.get(THUMB);
+ this._dd.set('node',thumb);
+ this._dd.set('dragNode',thumb);
+
+ this.syncUI();
+ }
+ },
+
+ /**
+ * Sets or replaces the thumb's contained <code>img</code> Node with the
+ * new Node in response to a change in the thumbImage attribute. This only
+ * has effect after the Slider is rendered.
+ *
+ * @method _afterThumbImageChange
+ * @param e {Event} thumbImageChange custom event
+ * @protected
+ */
+ _afterThumbImageChange : function (e) {
+ if (this.get(RENDERED)) {
+ if (e.prevValue) {
+ e.prevValue.get('parentNode').removeChild(e.prevValue);
+ }
+
+ this._initThumbImage();
+
+ this.syncUI();
+ }
+ },
+
+ /**
+ * Updates the Slider UI in response to change in the min attribute.
+ *
+ * @method _afterMinChange
+ * @param e {Event} minChange custom event
+ * @protected
+ */
+ _afterMinChange : function (e) {
+ this._refresh(e);
+ },
+
+ /**
+ * Updates the Slider UI in response to change in the max attribute.
+ *
+ * @method _afterMaxChange
+ * @param e {Event} maxChange custom event
+ * @protected
+ */
+ _afterMaxChange : function (e) {
+ this._refresh(e);
+ },
+
+ /**
+ * Updates the Slider UI in response to change in the railSize attribute.
+ *
+ * @method _afterRailSizeChange
+ * @param e {Event} railSizeChange custom event
+ * @protected
+ */
+ _afterRailSizeChange : function (e) {
+ this._refresh(e);
+ },
+
+ /**
+ * Locks or unlocks the DD instance in response to a change in the disabled
+ * attribute.
+ *
+ * @method _afterDisabledChange
+ * @param e {Event} disabledChange custom event
+ * @protected
+ */
+ _afterDisabledChange : function (e) {
+ if (this._dd) {
+ this._dd.set('lock',e.newVal);
+ }
+ },
+
+ /**
+ * Common handler to call syncUI in response to change events that occurred
+ * after the Slider is rendered.
+ *
+ * @method _refresh
+ * @param e {Event} An attribute change event
+ * @protected
+ */
+ _refresh : function (e) {
+ if (e.newVal !== e.prevVal && this.get(RENDERED)) {
+ this.syncUI();
+ }
+ },
+
+ /**
+ * Used to determine if there is a current or pending request for the
+ * thumbImage resource.
+ *
+ * @method _isImageLoading
+ * @param img {Node} <code>img</code> Node
+ * @return Boolean
+ * @protected
+ */
+ _isImageLoading : function (img) {
+ return img && !img.get(COMPLETE);
+ },
+
+ /**
+ * Used to determine if the image resource loaded successfully or there was
+ * an error.
+ *
+ * NOTES:
+ * <ul>
+ * <li>img load error fired xbrowser for image resources not yet resolved</li>
+ * <li>img.complete reports false in IE for images not yet loaded as well as images that failed to load</li>
+ * <li>img.complete true && img.naturalWidth == 0 in FF and Safari indicate image failed to load</li>
+ * <li>img.complete && img.width == 0 in Opera indicates image failed to load</li>
+ * </ul>
+ *
+ * @method _isImageLoaded
+ * @param img {Node} <code>img</code> Node
+ * @return Boolean
+ * @protected
+ */
+ _isImageLoaded : function (img) {
+ if (img) {
+ var w = img.get('naturalWidth');
+ return img.get(COMPLETE) && (!isNumber(w) ? img.get(WIDTH) : w);
+ }
+
+ return true;
+ }
+
+});
+
+Y.Slider = Slider;
+
+
+}, '3.0.0' ,{requires:['widget','dd-constrain']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('stylesheet', function(Y) {
+
+/**
+ * The StyleSheet component is a module for creating and modifying CSS
+ * stylesheets.
+ *
+ * @module stylesheet
+ */
+var d = Y.config.doc,
+ p = d.createElement('p'), // Have to hold the node (see notes)
+ workerStyle = p.style, // worker style collection
+ isString = Y.Lang.isString,
+ selectors = {},
+ sheets = {},
+ floatAttr = ('cssFloat' in workerStyle) ? 'cssFloat' : 'styleFloat',
+ _toCssText,
+ _unsetOpacity,
+ _unsetProperty,
+ OPACITY = 'opacity',
+ FLOAT = 'float',
+ EMPTY = '';
+
+// Normalizes the removal of an assigned style for opacity. IE uses the filter
+// property.
+_unsetOpacity = (OPACITY in workerStyle) ?
+ function (style) { style.opacity = EMPTY; } :
+ function (style) { style.filter = EMPTY; };
+
+// Normalizes the removal of an assigned style for a given property. Expands
+// shortcut properties if necessary and handles the various names for the float
+// property.
+workerStyle.border = "1px solid red";
+workerStyle.border = EMPTY; // IE doesn't unset child properties
+_unsetProperty = workerStyle.borderLeft ?
+ function (style,prop) {
+ var p;
+ if (prop !== floatAttr && prop.toLowerCase().indexOf(FLOAT) != -1) {
+ prop = floatAttr;
+ }
+ if (isString(style[prop])) {
+ switch (prop) {
+ case OPACITY:
+ case 'filter' : _unsetOpacity(style); break;
+ case 'font' :
+ style.font = style.fontStyle = style.fontVariant =
+ style.fontWeight = style.fontSize = style.lineHeight =
+ style.fontFamily = EMPTY;
+ break;
+ default :
+ for (p in style) {
+ if (p.indexOf(prop) === 0) {
+ style[p] = EMPTY;
+ }
+ }
+ }
+ }
+ } :
+ function (style,prop) {
+ if (prop !== floatAttr && prop.toLowerCase().indexOf(FLOAT) != -1) {
+ prop = floatAttr;
+ }
+ if (isString(style[prop])) {
+ if (prop === OPACITY) {
+ _unsetOpacity(style);
+ } else {
+ style[prop] = EMPTY;
+ }
+ }
+ };
+
+/**
+ * Create an instance of StyleSheet to encapsulate a css stylesheet.
+ * The constructor can be called using function or constructor syntax.
+ * <pre><code>var sheet = Y.StyleSheet(..);</pre></code>
+ * or
+ * <pre><code>var sheet = new Y.StyleSheet(..);</pre></code>
+ *
+ * The first parameter passed can be any of the following things:
+ * <ul>
+ * <li>The desired string name to register a new empty sheet</li>
+ * <li>The string name of an existing StyleSheet instance</li>
+ * <li>The unique guid generated for an existing StyleSheet instance</li>
+ * <li>The id of an existing <code><link></code> or <code><style></code> node</li>
+ * <li>The node reference for an existing <code><link></code> or <code><style></code> node</li>
+ * <li>The Y.Node instance wrapping an existing <code><link></code> or <code><style></code> node</li>
+ * <li>A chunk of css text to create a new stylesheet from</li>
+ * </ul>
+ *
+ * <p>If a string is passed, StyleSheet will first look in its static name
+ * registry for an existing sheet, then in the DOM for an element with that id.
+ * If neither are found and the string contains the { character, it will be
+ * used as a the initial cssText for a new StyleSheet. Otherwise, a new empty
+ * StyleSheet is created, assigned the string value as a name, and registered
+ * statically by that name.</p>
+ *
+ * <p>The optional second parameter is a string name to register the sheet as.
+ * This param is largely useful when providing a node id/ref or chunk of css
+ * text to create a populated instance.</p>
+ *
+ * @class StyleSheet
+ * @constructor
+ * @param seed {String|HTMLElement|Node} a style or link node, its id, or a
+ * name or guid of a StyleSheet, or a string of css text
+ * @param name {String} (optional) name to register instance for future static
+ * access
+ */
+function StyleSheet(seed, name) {
+ var head,
+ node,
+ sheet,
+ cssRules = {},
+ _rules,
+ _insertRule,
+ _deleteRule,
+ i,r,sel;
+
+ // Factory or constructor
+ if (!(this instanceof StyleSheet)) {
+ return new StyleSheet(seed,name);
+ }
+
+ // Extract the DOM node from Node instances
+ if (seed) {
+ if (Y.Node && seed instanceof Y.Node) {
+ node = Y.Node.getDOMNode(seed);
+ } else if (seed.nodeName) {
+ node = seed;
+ // capture the DOM node if the string is an id
+ } else if (isString(seed)) {
+ if (seed && sheets[seed]) {
+ return sheets[seed];
+ }
+ node = d.getElementById(seed.replace(/^#/,EMPTY));
+ }
+
+ // Check for the StyleSheet in the static registry
+ if (node && sheets[Y.stamp(node)]) {
+ return sheets[Y.stamp(node)];
+ }
+ }
+
+
+ // Create a style node if necessary
+ if (!node || !/^(?:style|link)$/i.test(node.nodeName)) {
+ node = d.createElement('style');
+ node.type = 'text/css';
+ }
+
+ if (isString(seed)) {
+ // Create entire sheet from seed cssText
+ if (seed.indexOf('{') != -1) {
+ // Not a load-time fork because low run-time impact and IE fails
+ // test for s.styleSheet at page load time (oddly)
+ if (node.styleSheet) {
+ node.styleSheet.cssText = seed;
+ } else {
+ node.appendChild(d.createTextNode(seed));
+ }
+ } else if (!name) {
+ name = seed;
+ }
+ }
+
+ // Make sure the node is attached to the appropriate head element
+ if (!node.parentNode || node.parentNode.nodeName.toLowerCase() !== 'head') {
+ head = (node.ownerDocument || d).getElementsByTagName('head')[0];
+ // styleSheet isn't available on the style node in FF2 until appended
+ // to the head element. style nodes appended to body do not affect
+ // change in Safari.
+ head.appendChild(node);
+ }
+
+ // Begin setting up private aliases to the important moving parts
+ // 1. The stylesheet object
+ // IE stores StyleSheet under the "styleSheet" property
+ // Safari doesn't populate sheet for xdomain link elements
+ sheet = node.sheet || node.styleSheet;
+
+ // 2. The style rules collection
+ // IE stores the rules collection under the "rules" property
+ _rules = sheet && ('cssRules' in sheet) ? 'cssRules' : 'rules';
+
+ // 3. The method to remove a rule from the stylesheet
+ // IE supports removeRule
+ _deleteRule = ('deleteRule' in sheet) ?
+ function (i) { sheet.deleteRule(i); } :
+ function (i) { sheet.removeRule(i); };
+
+ // 4. The method to add a new rule to the stylesheet
+ // IE supports addRule with different signature
+ _insertRule = ('insertRule' in sheet) ?
+ function (sel,css,i) { sheet.insertRule(sel+' {'+css+'}',i); } :
+ function (sel,css,i) { sheet.addRule(sel,css,i); };
+
+ // 5. Initialize the cssRules map from the node
+ // xdomain link nodes forbid access to the cssRules collection, so this
+ // will throw an error.
+ // TODO: research alternate stylesheet, @media
+ for (i = sheet[_rules].length - 1; i >= 0; --i) {
+ r = sheet[_rules][i];
+ sel = r.selectorText;
+
+ if (cssRules[sel]) {
+ cssRules[sel].style.cssText += ';' + r.style.cssText;
+ _deleteRule(i);
+ } else {
+ cssRules[sel] = r;
+ }
+ }
+
+ // Cache the instance by the generated Id
+ StyleSheet.register(Y.stamp(node),this);
+
+ // Register the instance by name if provided or defaulted from seed
+ if (name) {
+ StyleSheet.register(name,this);
+ }
+
+ // Public API
+ Y.mix(this,{
+ /**
+ * Get the unique stamp for this StyleSheet instance
+ *
+ * @method getId
+ * @return {Number} the static id
+ */
+ getId : function () { return Y.stamp(node); },
+
+ /**
+ * Enable all the rules in the sheet
+ *
+ * @method enable
+ * @return {StyleSheet}
+ * @chainable
+ */
+ enable : function () { sheet.disabled = false; return this; },
+
+ /**
+ * Disable all the rules in the sheet. Rules may be changed while the
+ * StyleSheet is disabled.
+ *
+ * @method disable
+ * @return {StyleSheet}
+ * @chainable
+ */
+ disable : function () { sheet.disabled = true; return this; },
+
+ /**
+ * Returns false if the StyleSheet is disabled. Otherwise true.
+ *
+ * @method isEnabled
+ * @return {Boolean}
+ */
+ isEnabled : function () { return !sheet.disabled; },
+
+ /**
+ * <p>Set style properties for a provided selector string.
+ * If the selector includes commas, it will be split into individual
+ * selectors and applied accordingly. If the selector string does not
+ * have a corresponding rule in the sheet, it will be added.</p>
+ *
+ * <p>The object properties in the second parameter must be the JavaScript
+ * names of style properties. E.g. fontSize rather than font-size.</p>
+ *
+ * <p>The float style property will be set by any of "float",
+ * "styleFloat", or "cssFloat".</p>
+ *
+ * @method set
+ * @param sel {String} the selector string to apply the changes to
+ * @param css {Object} Object literal of style properties and new values
+ * @return {StyleSheet}
+ * @chainable
+ */
+ set : function (sel,css) {
+ var rule = cssRules[sel],
+ multi = sel.split(/\s*,\s*/),i,
+ idx;
+
+ // IE's addRule doesn't support multiple comma delimited selectors
+ if (multi.length > 1) {
+ for (i = multi.length - 1; i >= 0; --i) {
+ this.set(multi[i], css);
+ }
+ return this;
+ }
+
+ // Some selector values can cause IE to hang
+ if (!StyleSheet.isValidSelector(sel)) {
+ Y.log("Invalid selector '"+sel+"' passed to set (ignoring).",'error','StyleSheet');
+ return this;
+ }
+
+ // Opera throws an error if there's a syntax error in assigned
+ // cssText. Avoid this using a worker style collection, then
+ // assigning the resulting cssText.
+ if (rule) {
+ rule.style.cssText = StyleSheet.toCssText(css,rule.style.cssText);
+ } else {
+ idx = sheet[_rules].length;
+ css = StyleSheet.toCssText(css);
+
+ // IE throws an error when attempting to addRule(sel,'',n)
+ // which would crop up if no, or only invalid values are used
+ if (css) {
+ _insertRule(sel, css, idx);
+
+ // Safari replaces the rules collection, but maintains the
+ // rule instances in the new collection when rules are
+ // added/removed
+ cssRules[sel] = sheet[_rules][idx];
+ }
+ }
+ return this;
+ },
+
+ /**
+ * <p>Unset style properties for a provided selector string, removing
+ * their effect from the style cascade.</p>
+ *
+ * <p>If the selector includes commas, it will be split into individual
+ * selectors and applied accordingly. If there are no properties
+ * remaining in the rule after unsetting, the rule is removed.</p>
+ *
+ * <p>The style property or properties in the second parameter must be the
+ * JavaScript style property names. E.g. fontSize rather than font-size.</p>
+ *
+ * <p>The float style property will be unset by any of "float",
+ * "styleFloat", or "cssFloat".</p>
+ *
+ * @method unset
+ * @param sel {String} the selector string to apply the changes to
+ * @param css {String|Array} style property name or Array of names
+ * @return {StyleSheet}
+ * @chainable
+ */
+ unset : function (sel,css) {
+ var rule = cssRules[sel],
+ multi = sel.split(/\s*,\s*/),
+ remove = !css,
+ rules, i;
+
+ // IE's addRule doesn't support multiple comma delimited selectors
+ // so rules are mapped internally by atomic selectors
+ if (multi.length > 1) {
+ for (i = multi.length - 1; i >= 0; --i) {
+ this.unset(multi[i], css);
+ }
+ return this;
+ }
+
+ if (rule) {
+ if (!remove) {
+ css = Y.Array(css);
+
+ workerStyle.cssText = rule.style.cssText;
+ for (i = css.length - 1; i >= 0; --i) {
+ _unsetProperty(workerStyle,css[i]);
+ }
+
+ if (workerStyle.cssText) {
+ rule.style.cssText = workerStyle.cssText;
+ } else {
+ remove = true;
+ }
+ }
+
+ if (remove) { // remove the rule altogether
+ rules = sheet[_rules];
+ for (i = rules.length - 1; i >= 0; --i) {
+ if (rules[i] === rule) {
+ delete cssRules[sel];
+ _deleteRule(i);
+ break;
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Get the current cssText for a rule or the entire sheet. If the
+ * selector param is supplied, only the cssText for that rule will be
+ * returned, if found. If the selector string targets multiple
+ * selectors separated by commas, the cssText of the first rule only
+ * will be returned. If no selector string, the stylesheet's full
+ * cssText will be returned.
+ *
+ * @method getCssText
+ * @param sel {String} Selector string
+ * @return {String}
+ */
+ getCssText : function (sel) {
+ var rule,css;
+
+ if (isString(sel)) {
+ // IE's addRule doesn't support multiple comma delimited
+ // selectors so rules are mapped internally by atomic selectors
+ rule = cssRules[sel.split(/\s*,\s*/)[0]];
+
+ return rule ? rule.style.cssText : null;
+ } else {
+ css = [];
+ for (sel in cssRules) {
+ if (cssRules.hasOwnProperty(sel)) {
+ rule = cssRules[sel];
+ css.push(rule.selectorText+" {"+rule.style.cssText+"}");
+ }
+ }
+ return css.join("\n");
+ }
+ }
+ });
+
+}
+
+_toCssText = function (css,base) {
+ var f = css.styleFloat || css.cssFloat || css[FLOAT],
+ trim = Y.Lang.trim,
+ prop;
+
+ workerStyle.cssText = base || EMPTY;
+
+ if (f && !css[floatAttr]) {
+ css = Y.merge(css);
+ delete css.styleFloat; delete css.cssFloat; delete css[FLOAT];
+ css[floatAttr] = f;
+ }
+
+ for (prop in css) {
+ if (css.hasOwnProperty(prop)) {
+ try {
+ // IE throws Invalid Value errors and doesn't like whitespace
+ // in values ala ' red' or 'red '
+ workerStyle[prop] = trim(css[prop]);
+ }
+ catch (e) {
+ Y.log('Error assigning property "'+prop+'" to "'+css[prop]+
+ "\" (ignored):\n"+e.message,'warn','StyleSheet');
+ }
+ }
+ }
+ return workerStyle.cssText;
+};
+
+Y.mix(StyleSheet, {
+ /**
+ * <p>Converts an object literal of style properties and values into a string
+ * of css text. This can then be assigned to el.style.cssText.</p>
+ *
+ * <p>The optional second parameter is a cssText string representing the
+ * starting state of the style prior to alterations. This is most often
+ * extracted from the eventual target's current el.style.cssText.</p>
+ *
+ * @method StyleSheet.toCssText
+ * @param css {Object} object literal of style properties and values
+ * @param cssText {String} (optional) starting cssText value
+ * @return {String} the resulting cssText string
+ * @static
+ */
+ toCssText : ((OPACITY in workerStyle) ? _toCssText :
+ // Wrap IE's toCssText to catch opacity. The copy/merge is to preserve
+ // the input object's integrity, but if float and opacity are set, the
+ // input will be copied twice in IE. Is there a way to avoid this
+ // without increasing the byte count?
+ function (css, cssText) {
+ if (OPACITY in css) {
+ css = Y.merge(css,{
+ filter: 'alpha(opacity='+(css.opacity*100)+')'
+ });
+ delete css.opacity;
+ }
+ return _toCssText(css,cssText);
+ }),
+
+ /**
+ * Registers a StyleSheet instance in the static registry by the given name
+ *
+ * @method StyleSheet.register
+ * @param name {String} the name to assign the StyleSheet in the registry
+ * @param sheet {StyleSheet} The StyleSheet instance
+ * @return {Boolean} false if no name or sheet is not a StyleSheet
+ * instance. true otherwise.
+ * @static
+ */
+ register : function (name,sheet) {
+ return !!(name && sheet instanceof StyleSheet &&
+ !sheets[name] && (sheets[name] = sheet));
+ },
+
+ /**
+ * <p>Determines if a selector string is safe to use. Used internally
+ * in set to prevent IE from locking up when attempting to add a rule for a
+ * "bad selector".</p>
+ *
+ * <p>Bad selectors are considered to be any string containing unescaped
+ * `~!@$%^&()+=|{}[];'"?< or space. Also forbidden are . or # followed by
+ * anything other than an alphanumeric. Additionally -abc or .-abc or
+ * #_abc or '# ' all fail. There are likely more failure cases, so
+ * please file a bug if you encounter one.</p>
+ *
+ * @method StyleSheet.isValidSelector
+ * @param sel {String} the selector string
+ * @return {Boolean}
+ * @static
+ */
+ isValidSelector : function (sel) {
+ var valid = false;
+
+ if (sel && isString(sel)) {
+
+ if (!selectors.hasOwnProperty(sel)) {
+ // TEST: there should be nothing but white-space left after
+ // these destructive regexs
+ selectors[sel] = !/\S/.test(
+ // combinators
+ sel.replace(/\s+|\s*[+~>]\s*/g,' ').
+ // attribute selectors (contents not validated)
+ replace(/([^ ])\[.*?\]/g,'$1').
+ // pseudo-class|element selectors (contents of parens
+ // such as :nth-of-type(2) or :not(...) not validated)
+ replace(/([^ ])::?[a-z][a-z\-]+[a-z](?:\(.*?\))?/ig,'$1').
+ // element tags
+ replace(/(?:^| )[a-z0-6]+/ig,' ').
+ // escaped characters
+ replace(/\\./g,EMPTY).
+ // class and id identifiers
+ replace(/[.#]\w[\w\-]*/g,EMPTY));
+ }
+
+ valid = selectors[sel];
+ }
+
+ return valid;
+ }
+},true);
+
+Y.StyleSheet = StyleSheet;
+
+/*
+
+NOTES
+ * Style node must be added to the head element. Safari does not honor styles
+ applied to StyleSheet objects on style nodes in the body.
+ * StyleSheet object is created on the style node when the style node is added
+ to the head element in Firefox 2 (and maybe 3?)
+ * The cssRules collection is replaced after insertRule/deleteRule calls in
+ Safari 3.1. Existing Rules are used in the new collection, so the collection
+ cannot be cached, but the rules can be.
+ * Opera requires that the index be passed with insertRule.
+ * Same-domain restrictions prevent modifying StyleSheet objects attached to
+ link elements with remote href (or "about:blank" or "javascript:false")
+ * Same-domain restrictions prevent reading StyleSheet cssRules/rules
+ collection of link elements with remote href (or "about:blank" or
+ "javascript:false")
+ * Same-domain restrictions result in Safari not populating node.sheet property
+ for link elements with remote href (et.al)
+ * IE names StyleSheet related properties and methods differently (see code)
+ * IE converts tag names to upper case in the Rule's selectorText
+ * IE converts empty string assignment to complex properties to value settings
+ for all child properties. E.g. style.background = '' sets non-'' values on
+ style.backgroundPosition, style.backgroundColor, etc. All else clear
+ style.background and all child properties.
+ * IE assignment style.filter = '' will result in style.cssText == 'FILTER:'
+ * All browsers support Rule.style.cssText as a read/write property, leaving
+ only opacity needing to be accounted for.
+ * Benchmarks of style.property = value vs style.cssText += 'property: value'
+ indicate cssText is slightly slower for single property assignment. For
+ multiple property assignment, cssText speed stays relatively the same where
+ style.property speed decreases linearly by the number of properties set.
+ Exception being Opera 9.27, where style.property is always faster than
+ style.cssText.
+ * Opera 9.5b throws a syntax error when assigning cssText with a syntax error.
+ * Opera 9.5 doesn't honor rule.style.cssText = ''. Previous style persists.
+ You have to remove the rule altogether.
+ * Stylesheet properties set with !important will trump inline style set on an
+ element or in el.style.property.
+ * Creating a worker style collection like document.createElement('p').style;
+ will fail after a time in FF (~5secs of inactivity). Property assignments
+ will not alter the property or cssText. It may be the generated node is
+ garbage collected and the style collection becomes inert (speculation).
+ * IE locks up when attempting to add a rule with a selector including at least
+ characters {[]}~`!@%^&*()+=|? (unescaped) and leading _ or -
+ such as addRule('-foo','{ color: red }') or addRule('._abc','{...}')
+ * IE's addRule doesn't support comma separated selectors such as
+ addRule('.foo, .bar','{..}')
+ * IE throws an error on valid values with leading/trailing white space.
+ * When creating an entire sheet at once, only FF2/3 & Opera allow creating a
+ style node, setting its innerHTML and appending to head.
+ * When creating an entire sheet at once, Safari requires the style node to be
+ created with content in innerHTML of another element.
+ * When creating an entire sheet at once, IE requires the style node content to
+ be set via node.styleSheet.cssText
+ * When creating an entire sheet at once in IE, styleSheet.cssText can't be
+ written until node.type = 'text/css'; is performed.
+ * When creating an entire sheet at once in IE, load-time fork on
+ var styleNode = d.createElement('style'); _method = styleNode.styleSheet ?..
+ fails (falsey). During run-time, the test for .styleSheet works fine
+ * Setting complex properties in cssText will SOMETIMES allow child properties
+ to be unset
+ set unset FF2 FF3 S3.1 IE6 IE7 Op9.27 Op9.5
+ ---------- ----------------- --- --- ---- --- --- ------ -----
+ border -top NO NO YES YES YES YES YES
+ -top-color NO NO YES YES YES
+ -color NO NO NO NO NO
+ background -color NO NO YES YES YES
+ -position NO NO YES YES YES
+ -position-x NO NO NO NO NO
+ font line-height YES YES NO NO NO NO YES
+ -style YES YES NO YES YES
+ -size YES YES NO YES YES
+ -size-adjust ??? ??? n/a n/a n/a ??? ???
+ padding -top NO NO YES YES YES
+ margin -top NO NO YES YES YES
+ list-style -type YES YES YES YES YES
+ -position YES YES YES YES YES
+ overflow -x NO NO YES n/a YES
+
+ ??? - unsetting font-size-adjust has the same effect as unsetting font-size
+ * FireFox and WebKit populate rule.cssText as "SELECTOR { CSSTEXT }", but
+ Opera and IE do not.
+ * IE6 and IE7 silently ignore the { and } if passed into addRule('.foo','{
+ color:#000}',0). IE8 does not and creates an empty rule.
+ * IE6-8 addRule('.foo','',n) throws an error. Must supply *some* cssText
+*/
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("stylesheet",function(B){var J=B.config.doc,C=J.createElement("p"),F=C.style,D=B.Lang.isString,M={},I={},K=("cssFloat" in F)?"cssFloat":"styleFloat",G,A,L,N="opacity",O="float",E="";A=(N in F)?function(P){P.opacity=E;}:function(P){P.filter=E;};F.border="1px solid red";F.border=E;L=F.borderLeft?function(P,R){var Q;if(R!==K&&R.toLowerCase().indexOf(O)!=-1){R=K;}if(D(P[R])){switch(R){case N:case"filter":A(P);break;case"font":P.font=P.fontStyle=P.fontVariant=P.fontWeight=P.fontSize=P.lineHeight=P.fontFamily=E;break;default:for(Q in P){if(Q.indexOf(R)===0){P[Q]=E;}}}}}:function(P,Q){if(Q!==K&&Q.toLowerCase().indexOf(O)!=-1){Q=K;}if(D(P[Q])){if(Q===N){A(P);}else{P[Q]=E;}}};function H(W,R){var Z,U,Y,X={},Q,a,T,V,P,S;if(!(this instanceof H)){return new H(W,R);}if(W){if(B.Node&&W instanceof B.Node){U=B.Node.getDOMNode(W);}else{if(W.nodeName){U=W;}else{if(D(W)){if(W&&I[W]){return I[W];}U=J.getElementById(W.replace(/^#/,E));}}}if(U&&I[B.stamp(U)]){return I[B.stamp(U)];}}if(!U||!/^(?:style|link)$/i.test(U.nodeName)){U=J.createElement("style");U.type="text/css";}if(D(W)){if(W.indexOf("{")!=-1){if(U.styleSheet){U.styleSheet.cssText=W;}else{U.appendChild(J.createTextNode(W));}}else{if(!R){R=W;}}}if(!U.parentNode||U.parentNode.nodeName.toLowerCase()!=="head"){Z=(U.ownerDocument||J).getElementsByTagName("head")[0];Z.appendChild(U);}Y=U.sheet||U.styleSheet;Q=Y&&("cssRules" in Y)?"cssRules":"rules";T=("deleteRule" in Y)?function(b){Y.deleteRule(b);}:function(b){Y.removeRule(b);};a=("insertRule" in Y)?function(d,c,b){Y.insertRule(d+" {"+c+"}",b);}:function(d,c,b){Y.addRule(d,c,b);};for(V=Y[Q].length-1;V>=0;--V){P=Y[Q][V];S=P.selectorText;if(X[S]){X[S].style.cssText+=";"+P.style.cssText;T(V);}else{X[S]=P;}}H.register(B.stamp(U),this);if(R){H.register(R,this);}B.mix(this,{getId:function(){return B.stamp(U);},enable:function(){Y.disabled=false;return this;},disable:function(){Y.disabled=true;return this;},isEnabled:function(){return !Y.disabled;},set:function(e,d){var g=X[e],f=e.split(/\s*,\s*/),c,b;if(f.length>1){for(c=f.length-1;c>=0;--c){this.set(f[c],d);}return this;}if(!H.isValidSelector(e)){return this;}if(g){g.style.cssText=H.toCssText(d,g.style.cssText);}else{b=Y[Q].length;d=H.toCssText(d);if(d){a(e,d,b);X[e]=Y[Q][b];}}return this;},unset:function(e,d){var g=X[e],f=e.split(/\s*,\s*/),b=!d,h,c;if(f.length>1){for(c=f.length-1;c>=0;--c){this.unset(f[c],d);}return this;}if(g){if(!b){d=B.Array(d);F.cssText=g.style.cssText;for(c=d.length-1;c>=0;--c){L(F,d[c]);}if(F.cssText){g.style.cssText=F.cssText;}else{b=true;}}if(b){h=Y[Q];for(c=h.length-1;c>=0;--c){if(h[c]===g){delete X[e];T(c);break;}}}}return this;},getCssText:function(c){var d,b;if(D(c)){d=X[c.split(/\s*,\s*/)[0]];return d?d.style.cssText:null;}else{b=[];for(c in X){if(X.hasOwnProperty(c)){d=X[c];b.push(d.selectorText+" {"+d.style.cssText+"}");}}return b.join("\n");}}});}G=function(Q,S){var R=Q.styleFloat||Q.cssFloat||Q[O],P=B.Lang.trim,U;F.cssText=S||E;if(R&&!Q[K]){Q=B.merge(Q);delete Q.styleFloat;delete Q.cssFloat;delete Q[O];Q[K]=R;}for(U in Q){if(Q.hasOwnProperty(U)){try{F[U]=P(Q[U]);}catch(T){}}}return F.cssText;};B.mix(H,{toCssText:((N in F)?G:function(P,Q){if(N in P){P=B.merge(P,{filter:"alpha(opacity="+(P.opacity*100)+")"});delete P.opacity;}return G(P,Q);}),register:function(P,Q){return !!(P&&Q instanceof H&&!I[P]&&(I[P]=Q));},isValidSelector:function(Q){var P=false;if(Q&&D(Q)){if(!M.hasOwnProperty(Q)){M[Q]=!/\S/.test(Q.replace(/\s+|\s*[+~>]\s*/g," ").replace(/([^ ])\[.*?\]/g,"$1").replace(/([^ ])::?[a-z][a-z\-]+[a-z](?:\(.*?\))?/ig,"$1").replace(/(?:^| )[a-z0-6]+/ig," ").replace(/\\./g,E).replace(/[.#]\w[\w\-]*/g,E));}P=M[Q];}return P;}},true);B.StyleSheet=H;},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('stylesheet', function(Y) {
+
+/**
+ * The StyleSheet component is a module for creating and modifying CSS
+ * stylesheets.
+ *
+ * @module stylesheet
+ */
+var d = Y.config.doc,
+ p = d.createElement('p'), // Have to hold the node (see notes)
+ workerStyle = p.style, // worker style collection
+ isString = Y.Lang.isString,
+ selectors = {},
+ sheets = {},
+ floatAttr = ('cssFloat' in workerStyle) ? 'cssFloat' : 'styleFloat',
+ _toCssText,
+ _unsetOpacity,
+ _unsetProperty,
+ OPACITY = 'opacity',
+ FLOAT = 'float',
+ EMPTY = '';
+
+// Normalizes the removal of an assigned style for opacity. IE uses the filter
+// property.
+_unsetOpacity = (OPACITY in workerStyle) ?
+ function (style) { style.opacity = EMPTY; } :
+ function (style) { style.filter = EMPTY; };
+
+// Normalizes the removal of an assigned style for a given property. Expands
+// shortcut properties if necessary and handles the various names for the float
+// property.
+workerStyle.border = "1px solid red";
+workerStyle.border = EMPTY; // IE doesn't unset child properties
+_unsetProperty = workerStyle.borderLeft ?
+ function (style,prop) {
+ var p;
+ if (prop !== floatAttr && prop.toLowerCase().indexOf(FLOAT) != -1) {
+ prop = floatAttr;
+ }
+ if (isString(style[prop])) {
+ switch (prop) {
+ case OPACITY:
+ case 'filter' : _unsetOpacity(style); break;
+ case 'font' :
+ style.font = style.fontStyle = style.fontVariant =
+ style.fontWeight = style.fontSize = style.lineHeight =
+ style.fontFamily = EMPTY;
+ break;
+ default :
+ for (p in style) {
+ if (p.indexOf(prop) === 0) {
+ style[p] = EMPTY;
+ }
+ }
+ }
+ }
+ } :
+ function (style,prop) {
+ if (prop !== floatAttr && prop.toLowerCase().indexOf(FLOAT) != -1) {
+ prop = floatAttr;
+ }
+ if (isString(style[prop])) {
+ if (prop === OPACITY) {
+ _unsetOpacity(style);
+ } else {
+ style[prop] = EMPTY;
+ }
+ }
+ };
+
+/**
+ * Create an instance of StyleSheet to encapsulate a css stylesheet.
+ * The constructor can be called using function or constructor syntax.
+ * <pre><code>var sheet = Y.StyleSheet(..);</pre></code>
+ * or
+ * <pre><code>var sheet = new Y.StyleSheet(..);</pre></code>
+ *
+ * The first parameter passed can be any of the following things:
+ * <ul>
+ * <li>The desired string name to register a new empty sheet</li>
+ * <li>The string name of an existing StyleSheet instance</li>
+ * <li>The unique guid generated for an existing StyleSheet instance</li>
+ * <li>The id of an existing <code><link></code> or <code><style></code> node</li>
+ * <li>The node reference for an existing <code><link></code> or <code><style></code> node</li>
+ * <li>The Y.Node instance wrapping an existing <code><link></code> or <code><style></code> node</li>
+ * <li>A chunk of css text to create a new stylesheet from</li>
+ * </ul>
+ *
+ * <p>If a string is passed, StyleSheet will first look in its static name
+ * registry for an existing sheet, then in the DOM for an element with that id.
+ * If neither are found and the string contains the { character, it will be
+ * used as a the initial cssText for a new StyleSheet. Otherwise, a new empty
+ * StyleSheet is created, assigned the string value as a name, and registered
+ * statically by that name.</p>
+ *
+ * <p>The optional second parameter is a string name to register the sheet as.
+ * This param is largely useful when providing a node id/ref or chunk of css
+ * text to create a populated instance.</p>
+ *
+ * @class StyleSheet
+ * @constructor
+ * @param seed {String|HTMLElement|Node} a style or link node, its id, or a
+ * name or guid of a StyleSheet, or a string of css text
+ * @param name {String} (optional) name to register instance for future static
+ * access
+ */
+function StyleSheet(seed, name) {
+ var head,
+ node,
+ sheet,
+ cssRules = {},
+ _rules,
+ _insertRule,
+ _deleteRule,
+ i,r,sel;
+
+ // Factory or constructor
+ if (!(this instanceof StyleSheet)) {
+ return new StyleSheet(seed,name);
+ }
+
+ // Extract the DOM node from Node instances
+ if (seed) {
+ if (Y.Node && seed instanceof Y.Node) {
+ node = Y.Node.getDOMNode(seed);
+ } else if (seed.nodeName) {
+ node = seed;
+ // capture the DOM node if the string is an id
+ } else if (isString(seed)) {
+ if (seed && sheets[seed]) {
+ return sheets[seed];
+ }
+ node = d.getElementById(seed.replace(/^#/,EMPTY));
+ }
+
+ // Check for the StyleSheet in the static registry
+ if (node && sheets[Y.stamp(node)]) {
+ return sheets[Y.stamp(node)];
+ }
+ }
+
+
+ // Create a style node if necessary
+ if (!node || !/^(?:style|link)$/i.test(node.nodeName)) {
+ node = d.createElement('style');
+ node.type = 'text/css';
+ }
+
+ if (isString(seed)) {
+ // Create entire sheet from seed cssText
+ if (seed.indexOf('{') != -1) {
+ // Not a load-time fork because low run-time impact and IE fails
+ // test for s.styleSheet at page load time (oddly)
+ if (node.styleSheet) {
+ node.styleSheet.cssText = seed;
+ } else {
+ node.appendChild(d.createTextNode(seed));
+ }
+ } else if (!name) {
+ name = seed;
+ }
+ }
+
+ // Make sure the node is attached to the appropriate head element
+ if (!node.parentNode || node.parentNode.nodeName.toLowerCase() !== 'head') {
+ head = (node.ownerDocument || d).getElementsByTagName('head')[0];
+ // styleSheet isn't available on the style node in FF2 until appended
+ // to the head element. style nodes appended to body do not affect
+ // change in Safari.
+ head.appendChild(node);
+ }
+
+ // Begin setting up private aliases to the important moving parts
+ // 1. The stylesheet object
+ // IE stores StyleSheet under the "styleSheet" property
+ // Safari doesn't populate sheet for xdomain link elements
+ sheet = node.sheet || node.styleSheet;
+
+ // 2. The style rules collection
+ // IE stores the rules collection under the "rules" property
+ _rules = sheet && ('cssRules' in sheet) ? 'cssRules' : 'rules';
+
+ // 3. The method to remove a rule from the stylesheet
+ // IE supports removeRule
+ _deleteRule = ('deleteRule' in sheet) ?
+ function (i) { sheet.deleteRule(i); } :
+ function (i) { sheet.removeRule(i); };
+
+ // 4. The method to add a new rule to the stylesheet
+ // IE supports addRule with different signature
+ _insertRule = ('insertRule' in sheet) ?
+ function (sel,css,i) { sheet.insertRule(sel+' {'+css+'}',i); } :
+ function (sel,css,i) { sheet.addRule(sel,css,i); };
+
+ // 5. Initialize the cssRules map from the node
+ // xdomain link nodes forbid access to the cssRules collection, so this
+ // will throw an error.
+ // TODO: research alternate stylesheet, @media
+ for (i = sheet[_rules].length - 1; i >= 0; --i) {
+ r = sheet[_rules][i];
+ sel = r.selectorText;
+
+ if (cssRules[sel]) {
+ cssRules[sel].style.cssText += ';' + r.style.cssText;
+ _deleteRule(i);
+ } else {
+ cssRules[sel] = r;
+ }
+ }
+
+ // Cache the instance by the generated Id
+ StyleSheet.register(Y.stamp(node),this);
+
+ // Register the instance by name if provided or defaulted from seed
+ if (name) {
+ StyleSheet.register(name,this);
+ }
+
+ // Public API
+ Y.mix(this,{
+ /**
+ * Get the unique stamp for this StyleSheet instance
+ *
+ * @method getId
+ * @return {Number} the static id
+ */
+ getId : function () { return Y.stamp(node); },
+
+ /**
+ * Enable all the rules in the sheet
+ *
+ * @method enable
+ * @return {StyleSheet}
+ * @chainable
+ */
+ enable : function () { sheet.disabled = false; return this; },
+
+ /**
+ * Disable all the rules in the sheet. Rules may be changed while the
+ * StyleSheet is disabled.
+ *
+ * @method disable
+ * @return {StyleSheet}
+ * @chainable
+ */
+ disable : function () { sheet.disabled = true; return this; },
+
+ /**
+ * Returns false if the StyleSheet is disabled. Otherwise true.
+ *
+ * @method isEnabled
+ * @return {Boolean}
+ */
+ isEnabled : function () { return !sheet.disabled; },
+
+ /**
+ * <p>Set style properties for a provided selector string.
+ * If the selector includes commas, it will be split into individual
+ * selectors and applied accordingly. If the selector string does not
+ * have a corresponding rule in the sheet, it will be added.</p>
+ *
+ * <p>The object properties in the second parameter must be the JavaScript
+ * names of style properties. E.g. fontSize rather than font-size.</p>
+ *
+ * <p>The float style property will be set by any of "float",
+ * "styleFloat", or "cssFloat".</p>
+ *
+ * @method set
+ * @param sel {String} the selector string to apply the changes to
+ * @param css {Object} Object literal of style properties and new values
+ * @return {StyleSheet}
+ * @chainable
+ */
+ set : function (sel,css) {
+ var rule = cssRules[sel],
+ multi = sel.split(/\s*,\s*/),i,
+ idx;
+
+ // IE's addRule doesn't support multiple comma delimited selectors
+ if (multi.length > 1) {
+ for (i = multi.length - 1; i >= 0; --i) {
+ this.set(multi[i], css);
+ }
+ return this;
+ }
+
+ // Some selector values can cause IE to hang
+ if (!StyleSheet.isValidSelector(sel)) {
+ return this;
+ }
+
+ // Opera throws an error if there's a syntax error in assigned
+ // cssText. Avoid this using a worker style collection, then
+ // assigning the resulting cssText.
+ if (rule) {
+ rule.style.cssText = StyleSheet.toCssText(css,rule.style.cssText);
+ } else {
+ idx = sheet[_rules].length;
+ css = StyleSheet.toCssText(css);
+
+ // IE throws an error when attempting to addRule(sel,'',n)
+ // which would crop up if no, or only invalid values are used
+ if (css) {
+ _insertRule(sel, css, idx);
+
+ // Safari replaces the rules collection, but maintains the
+ // rule instances in the new collection when rules are
+ // added/removed
+ cssRules[sel] = sheet[_rules][idx];
+ }
+ }
+ return this;
+ },
+
+ /**
+ * <p>Unset style properties for a provided selector string, removing
+ * their effect from the style cascade.</p>
+ *
+ * <p>If the selector includes commas, it will be split into individual
+ * selectors and applied accordingly. If there are no properties
+ * remaining in the rule after unsetting, the rule is removed.</p>
+ *
+ * <p>The style property or properties in the second parameter must be the
+ * JavaScript style property names. E.g. fontSize rather than font-size.</p>
+ *
+ * <p>The float style property will be unset by any of "float",
+ * "styleFloat", or "cssFloat".</p>
+ *
+ * @method unset
+ * @param sel {String} the selector string to apply the changes to
+ * @param css {String|Array} style property name or Array of names
+ * @return {StyleSheet}
+ * @chainable
+ */
+ unset : function (sel,css) {
+ var rule = cssRules[sel],
+ multi = sel.split(/\s*,\s*/),
+ remove = !css,
+ rules, i;
+
+ // IE's addRule doesn't support multiple comma delimited selectors
+ // so rules are mapped internally by atomic selectors
+ if (multi.length > 1) {
+ for (i = multi.length - 1; i >= 0; --i) {
+ this.unset(multi[i], css);
+ }
+ return this;
+ }
+
+ if (rule) {
+ if (!remove) {
+ css = Y.Array(css);
+
+ workerStyle.cssText = rule.style.cssText;
+ for (i = css.length - 1; i >= 0; --i) {
+ _unsetProperty(workerStyle,css[i]);
+ }
+
+ if (workerStyle.cssText) {
+ rule.style.cssText = workerStyle.cssText;
+ } else {
+ remove = true;
+ }
+ }
+
+ if (remove) { // remove the rule altogether
+ rules = sheet[_rules];
+ for (i = rules.length - 1; i >= 0; --i) {
+ if (rules[i] === rule) {
+ delete cssRules[sel];
+ _deleteRule(i);
+ break;
+ }
+ }
+ }
+ }
+ return this;
+ },
+
+ /**
+ * Get the current cssText for a rule or the entire sheet. If the
+ * selector param is supplied, only the cssText for that rule will be
+ * returned, if found. If the selector string targets multiple
+ * selectors separated by commas, the cssText of the first rule only
+ * will be returned. If no selector string, the stylesheet's full
+ * cssText will be returned.
+ *
+ * @method getCssText
+ * @param sel {String} Selector string
+ * @return {String}
+ */
+ getCssText : function (sel) {
+ var rule,css;
+
+ if (isString(sel)) {
+ // IE's addRule doesn't support multiple comma delimited
+ // selectors so rules are mapped internally by atomic selectors
+ rule = cssRules[sel.split(/\s*,\s*/)[0]];
+
+ return rule ? rule.style.cssText : null;
+ } else {
+ css = [];
+ for (sel in cssRules) {
+ if (cssRules.hasOwnProperty(sel)) {
+ rule = cssRules[sel];
+ css.push(rule.selectorText+" {"+rule.style.cssText+"}");
+ }
+ }
+ return css.join("\n");
+ }
+ }
+ });
+
+}
+
+_toCssText = function (css,base) {
+ var f = css.styleFloat || css.cssFloat || css[FLOAT],
+ trim = Y.Lang.trim,
+ prop;
+
+ workerStyle.cssText = base || EMPTY;
+
+ if (f && !css[floatAttr]) {
+ css = Y.merge(css);
+ delete css.styleFloat; delete css.cssFloat; delete css[FLOAT];
+ css[floatAttr] = f;
+ }
+
+ for (prop in css) {
+ if (css.hasOwnProperty(prop)) {
+ try {
+ // IE throws Invalid Value errors and doesn't like whitespace
+ // in values ala ' red' or 'red '
+ workerStyle[prop] = trim(css[prop]);
+ }
+ catch (e) {
+ }
+ }
+ }
+ return workerStyle.cssText;
+};
+
+Y.mix(StyleSheet, {
+ /**
+ * <p>Converts an object literal of style properties and values into a string
+ * of css text. This can then be assigned to el.style.cssText.</p>
+ *
+ * <p>The optional second parameter is a cssText string representing the
+ * starting state of the style prior to alterations. This is most often
+ * extracted from the eventual target's current el.style.cssText.</p>
+ *
+ * @method StyleSheet.toCssText
+ * @param css {Object} object literal of style properties and values
+ * @param cssText {String} (optional) starting cssText value
+ * @return {String} the resulting cssText string
+ * @static
+ */
+ toCssText : ((OPACITY in workerStyle) ? _toCssText :
+ // Wrap IE's toCssText to catch opacity. The copy/merge is to preserve
+ // the input object's integrity, but if float and opacity are set, the
+ // input will be copied twice in IE. Is there a way to avoid this
+ // without increasing the byte count?
+ function (css, cssText) {
+ if (OPACITY in css) {
+ css = Y.merge(css,{
+ filter: 'alpha(opacity='+(css.opacity*100)+')'
+ });
+ delete css.opacity;
+ }
+ return _toCssText(css,cssText);
+ }),
+
+ /**
+ * Registers a StyleSheet instance in the static registry by the given name
+ *
+ * @method StyleSheet.register
+ * @param name {String} the name to assign the StyleSheet in the registry
+ * @param sheet {StyleSheet} The StyleSheet instance
+ * @return {Boolean} false if no name or sheet is not a StyleSheet
+ * instance. true otherwise.
+ * @static
+ */
+ register : function (name,sheet) {
+ return !!(name && sheet instanceof StyleSheet &&
+ !sheets[name] && (sheets[name] = sheet));
+ },
+
+ /**
+ * <p>Determines if a selector string is safe to use. Used internally
+ * in set to prevent IE from locking up when attempting to add a rule for a
+ * "bad selector".</p>
+ *
+ * <p>Bad selectors are considered to be any string containing unescaped
+ * `~!@$%^&()+=|{}[];'"?< or space. Also forbidden are . or # followed by
+ * anything other than an alphanumeric. Additionally -abc or .-abc or
+ * #_abc or '# ' all fail. There are likely more failure cases, so
+ * please file a bug if you encounter one.</p>
+ *
+ * @method StyleSheet.isValidSelector
+ * @param sel {String} the selector string
+ * @return {Boolean}
+ * @static
+ */
+ isValidSelector : function (sel) {
+ var valid = false;
+
+ if (sel && isString(sel)) {
+
+ if (!selectors.hasOwnProperty(sel)) {
+ // TEST: there should be nothing but white-space left after
+ // these destructive regexs
+ selectors[sel] = !/\S/.test(
+ // combinators
+ sel.replace(/\s+|\s*[+~>]\s*/g,' ').
+ // attribute selectors (contents not validated)
+ replace(/([^ ])\[.*?\]/g,'$1').
+ // pseudo-class|element selectors (contents of parens
+ // such as :nth-of-type(2) or :not(...) not validated)
+ replace(/([^ ])::?[a-z][a-z\-]+[a-z](?:\(.*?\))?/ig,'$1').
+ // element tags
+ replace(/(?:^| )[a-z0-6]+/ig,' ').
+ // escaped characters
+ replace(/\\./g,EMPTY).
+ // class and id identifiers
+ replace(/[.#]\w[\w\-]*/g,EMPTY));
+ }
+
+ valid = selectors[sel];
+ }
+
+ return valid;
+ }
+},true);
+
+Y.StyleSheet = StyleSheet;
+
+/*
+
+NOTES
+ * Style node must be added to the head element. Safari does not honor styles
+ applied to StyleSheet objects on style nodes in the body.
+ * StyleSheet object is created on the style node when the style node is added
+ to the head element in Firefox 2 (and maybe 3?)
+ * The cssRules collection is replaced after insertRule/deleteRule calls in
+ Safari 3.1. Existing Rules are used in the new collection, so the collection
+ cannot be cached, but the rules can be.
+ * Opera requires that the index be passed with insertRule.
+ * Same-domain restrictions prevent modifying StyleSheet objects attached to
+ link elements with remote href (or "about:blank" or "javascript:false")
+ * Same-domain restrictions prevent reading StyleSheet cssRules/rules
+ collection of link elements with remote href (or "about:blank" or
+ "javascript:false")
+ * Same-domain restrictions result in Safari not populating node.sheet property
+ for link elements with remote href (et.al)
+ * IE names StyleSheet related properties and methods differently (see code)
+ * IE converts tag names to upper case in the Rule's selectorText
+ * IE converts empty string assignment to complex properties to value settings
+ for all child properties. E.g. style.background = '' sets non-'' values on
+ style.backgroundPosition, style.backgroundColor, etc. All else clear
+ style.background and all child properties.
+ * IE assignment style.filter = '' will result in style.cssText == 'FILTER:'
+ * All browsers support Rule.style.cssText as a read/write property, leaving
+ only opacity needing to be accounted for.
+ * Benchmarks of style.property = value vs style.cssText += 'property: value'
+ indicate cssText is slightly slower for single property assignment. For
+ multiple property assignment, cssText speed stays relatively the same where
+ style.property speed decreases linearly by the number of properties set.
+ Exception being Opera 9.27, where style.property is always faster than
+ style.cssText.
+ * Opera 9.5b throws a syntax error when assigning cssText with a syntax error.
+ * Opera 9.5 doesn't honor rule.style.cssText = ''. Previous style persists.
+ You have to remove the rule altogether.
+ * Stylesheet properties set with !important will trump inline style set on an
+ element or in el.style.property.
+ * Creating a worker style collection like document.createElement('p').style;
+ will fail after a time in FF (~5secs of inactivity). Property assignments
+ will not alter the property or cssText. It may be the generated node is
+ garbage collected and the style collection becomes inert (speculation).
+ * IE locks up when attempting to add a rule with a selector including at least
+ characters {[]}~`!@%^&*()+=|? (unescaped) and leading _ or -
+ such as addRule('-foo','{ color: red }') or addRule('._abc','{...}')
+ * IE's addRule doesn't support comma separated selectors such as
+ addRule('.foo, .bar','{..}')
+ * IE throws an error on valid values with leading/trailing white space.
+ * When creating an entire sheet at once, only FF2/3 & Opera allow creating a
+ style node, setting its innerHTML and appending to head.
+ * When creating an entire sheet at once, Safari requires the style node to be
+ created with content in innerHTML of another element.
+ * When creating an entire sheet at once, IE requires the style node content to
+ be set via node.styleSheet.cssText
+ * When creating an entire sheet at once in IE, styleSheet.cssText can't be
+ written until node.type = 'text/css'; is performed.
+ * When creating an entire sheet at once in IE, load-time fork on
+ var styleNode = d.createElement('style'); _method = styleNode.styleSheet ?..
+ fails (falsey). During run-time, the test for .styleSheet works fine
+ * Setting complex properties in cssText will SOMETIMES allow child properties
+ to be unset
+ set unset FF2 FF3 S3.1 IE6 IE7 Op9.27 Op9.5
+ ---------- ----------------- --- --- ---- --- --- ------ -----
+ border -top NO NO YES YES YES YES YES
+ -top-color NO NO YES YES YES
+ -color NO NO NO NO NO
+ background -color NO NO YES YES YES
+ -position NO NO YES YES YES
+ -position-x NO NO NO NO NO
+ font line-height YES YES NO NO NO NO YES
+ -style YES YES NO YES YES
+ -size YES YES NO YES YES
+ -size-adjust ??? ??? n/a n/a n/a ??? ???
+ padding -top NO NO YES YES YES
+ margin -top NO NO YES YES YES
+ list-style -type YES YES YES YES YES
+ -position YES YES YES YES YES
+ overflow -x NO NO YES n/a YES
+
+ ??? - unsetting font-size-adjust has the same effect as unsetting font-size
+ * FireFox and WebKit populate rule.cssText as "SELECTOR { CSSTEXT }", but
+ Opera and IE do not.
+ * IE6 and IE7 silently ignore the { and } if passed into addRule('.foo','{
+ color:#000}',0). IE8 does not and creates an empty rule.
+ * IE6-8 addRule('.foo','',n) throws an error. Must supply *some* cssText
+*/
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('substitute', function(Y) {
+
+/**
+ * String variable substitution and string formatting.
+ * If included, the substitute method is added to the YUI instance.
+ *
+ * @module substitute
+ */
+
+ var L = Y.Lang, DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}',
+
+ /**
+ * The following methods are added to the YUI instance
+ * @class YUI~substitute
+ */
+
+ // @todo template configurability is not implemented yet
+ // @param ldelim {string} optional left delimiter for the replacement token (default: left brace)
+ // @param rdelim {string} optional right delimiter for the replacement token (default: right brace)
+
+ /**
+ * Does variable substitution on a string. It scans through the string
+ * looking for expressions enclosed in { } braces. If an expression
+ * is found, it is used a key on the object. If there is a space in
+ * the key, the first word is used for the key and the rest is provided
+ * to an optional function to be used to programatically determine the
+ * value (the extra information might be used for this decision). If
+ * the value for the key in the object, or what is returned from the
+ * function has a string value, number value, or object value, it is
+ * substituted for the bracket expression and it repeats. If this
+ * value is an object, it uses the Object's toString() if this has
+ * been overridden, otherwise it does a shallow dump of the key/value
+ * pairs if Y.dump is available (if dump isn't available, toString()
+ * is used).
+ *
+ * This method is included in the 'substitute' module. It is not included
+ * in the YUI module.
+ *
+ * @method substitute
+ * @param s {string} The string that will be modified.
+ * @param o An object containing the replacement values
+ * @param f {function} An optional function that can be used to
+ * process each match. It receives the key,
+ * value, and any extra metadata included with
+ * the key inside of the braces.
+ * @return {string} the substituted string
+ */
+
+ substitute = function (s, o, f, ldelim, rdelim) {
+ var i, j, k, key, v, meta, saved=[], token, dump;
+ ldelim = ldelim || LBRACE;
+ rdelim = rdelim || RBRACE;
+
+ for (;;) {
+ i = s.lastIndexOf(ldelim);
+ if (i < 0) {
+ break;
+ }
+ j = s.indexOf(rdelim, i);
+ if (i + 1 >= j) {
+ break;
+ }
+
+ //Extract key and meta info
+ token = s.substring(i + 1, j);
+ key = token;
+ meta = null;
+ k = key.indexOf(SPACE);
+ if (k > -1) {
+ meta = key.substring(k + 1);
+ key = key.substring(0, k);
+ }
+
+ // lookup the value
+ v = o[key];
+
+ // if a substitution function was provided, execute it
+ if (f) {
+ v = f(key, v, meta);
+ }
+
+ if (L.isObject(v)) {
+ if (!Y.dump) {
+ v = v.toString();
+ } else {
+ if (L.isArray(v)) {
+ v = Y.dump(v, parseInt(meta, 10));
+ } else {
+ meta = meta || "";
+
+ // look for the keyword 'dump', if found force obj dump
+ dump = meta.indexOf(DUMP);
+ if (dump > -1) {
+ meta = meta.substring(4);
+ }
+
+ // use the toString if it is not the Object toString
+ // and the 'dump' meta info was not found
+ if (v.toString===Object.prototype.toString||dump>-1) {
+ v = Y.dump(v, parseInt(meta, 10));
+ } else {
+ v = v.toString();
+ }
+ }
+ }
+ } else if (!L.isString(v) && !L.isNumber(v)) {
+ // This {block} has no replace string. Save it for later.
+ v = "~-" + saved.length + "-~";
+ saved[saved.length] = token;
+
+ // break;
+ }
+
+ s = s.substring(0, i) + v + s.substring(j + 1);
+
+ }
+
+ // restore saved {block}s
+ for (i=saved.length-1; i>=0; i=i-1) {
+ s = s.replace(new RegExp("~-" + i + "-~"), ldelim + saved[i] + rdelim, "g");
+ }
+
+ return s;
+
+ };
+
+ Y.substitute = substitute;
+ L.substitute = substitute;
+
+
+
+}, '3.0.0' ,{optional:['dump']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("substitute",function(G){var B=G.Lang,D="dump",F=" ",C="{",E="}",A=function(U,I,P,K,H){var N,M,L,S,R,T,Q=[],J,O;K=K||C;H=H||E;for(;;){N=U.lastIndexOf(K);if(N<0){break;}M=U.indexOf(H,N);if(N+1>=M){break;}J=U.substring(N+1,M);S=J;T=null;L=S.indexOf(F);if(L>-1){T=S.substring(L+1);S=S.substring(0,L);}R=I[S];if(P){R=P(S,R,T);}if(B.isObject(R)){if(!G.dump){R=R.toString();}else{if(B.isArray(R)){R=G.dump(R,parseInt(T,10));}else{T=T||"";O=T.indexOf(D);if(O>-1){T=T.substring(4);}if(R.toString===Object.prototype.toString||O>-1){R=G.dump(R,parseInt(T,10));}else{R=R.toString();}}}}else{if(!B.isString(R)&&!B.isNumber(R)){R="~-"+Q.length+"-~";Q[Q.length]=J;}}U=U.substring(0,N)+R+U.substring(M+1);}for(N=Q.length-1;N>=0;N=N-1){U=U.replace(new RegExp("~-"+N+"-~"),K+Q[N]+H,"g");}return U;};G.substitute=A;B.substitute=A;},"3.0.0",{optional:["dump"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('substitute', function(Y) {
+
+/**
+ * String variable substitution and string formatting.
+ * If included, the substitute method is added to the YUI instance.
+ *
+ * @module substitute
+ */
+
+ var L = Y.Lang, DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}',
+
+ /**
+ * The following methods are added to the YUI instance
+ * @class YUI~substitute
+ */
+
+ // @todo template configurability is not implemented yet
+ // @param ldelim {string} optional left delimiter for the replacement token (default: left brace)
+ // @param rdelim {string} optional right delimiter for the replacement token (default: right brace)
+
+ /**
+ * Does variable substitution on a string. It scans through the string
+ * looking for expressions enclosed in { } braces. If an expression
+ * is found, it is used a key on the object. If there is a space in
+ * the key, the first word is used for the key and the rest is provided
+ * to an optional function to be used to programatically determine the
+ * value (the extra information might be used for this decision). If
+ * the value for the key in the object, or what is returned from the
+ * function has a string value, number value, or object value, it is
+ * substituted for the bracket expression and it repeats. If this
+ * value is an object, it uses the Object's toString() if this has
+ * been overridden, otherwise it does a shallow dump of the key/value
+ * pairs if Y.dump is available (if dump isn't available, toString()
+ * is used).
+ *
+ * This method is included in the 'substitute' module. It is not included
+ * in the YUI module.
+ *
+ * @method substitute
+ * @param s {string} The string that will be modified.
+ * @param o An object containing the replacement values
+ * @param f {function} An optional function that can be used to
+ * process each match. It receives the key,
+ * value, and any extra metadata included with
+ * the key inside of the braces.
+ * @return {string} the substituted string
+ */
+
+ substitute = function (s, o, f, ldelim, rdelim) {
+ var i, j, k, key, v, meta, saved=[], token, dump;
+ ldelim = ldelim || LBRACE;
+ rdelim = rdelim || RBRACE;
+
+ for (;;) {
+ i = s.lastIndexOf(ldelim);
+ if (i < 0) {
+ break;
+ }
+ j = s.indexOf(rdelim, i);
+ if (i + 1 >= j) {
+ break;
+ }
+
+ //Extract key and meta info
+ token = s.substring(i + 1, j);
+ key = token;
+ meta = null;
+ k = key.indexOf(SPACE);
+ if (k > -1) {
+ meta = key.substring(k + 1);
+ key = key.substring(0, k);
+ }
+
+ // lookup the value
+ v = o[key];
+
+ // if a substitution function was provided, execute it
+ if (f) {
+ v = f(key, v, meta);
+ }
+
+ if (L.isObject(v)) {
+ if (!Y.dump) {
+ v = v.toString();
+ } else {
+ if (L.isArray(v)) {
+ v = Y.dump(v, parseInt(meta, 10));
+ } else {
+ meta = meta || "";
+
+ // look for the keyword 'dump', if found force obj dump
+ dump = meta.indexOf(DUMP);
+ if (dump > -1) {
+ meta = meta.substring(4);
+ }
+
+ // use the toString if it is not the Object toString
+ // and the 'dump' meta info was not found
+ if (v.toString===Object.prototype.toString||dump>-1) {
+ v = Y.dump(v, parseInt(meta, 10));
+ } else {
+ v = v.toString();
+ }
+ }
+ }
+ } else if (!L.isString(v) && !L.isNumber(v)) {
+ // This {block} has no replace string. Save it for later.
+ v = "~-" + saved.length + "-~";
+ saved[saved.length] = token;
+
+ // break;
+ }
+
+ s = s.substring(0, i) + v + s.substring(j + 1);
+
+ }
+
+ // restore saved {block}s
+ for (i=saved.length-1; i>=0; i=i-1) {
+ s = s.replace(new RegExp("~-" + i + "-~"), ldelim + saved[i] + rdelim, "g");
+ }
+
+ return s;
+
+ };
+
+ Y.substitute = substitute;
+ L.substitute = substitute;
+
+
+
+}, '3.0.0' ,{optional:['dump']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-log {padding-top:3em;}
+/*
+.yui-log-container {position:relative;width:60em}
+.yui-log .yui-log-bd {height:auto; overflow:visible;}
+*/
+.yui-log-container {width:40em}
+.yui-log .yui-log-bd {height:60em}
+.yui-log .yui-log-btns {display:none;}
+.yui-log .yui-log-ft .yui-log-sourcefilters {visibility:hidden;}
+.yui-log .yui-log-hd {display:none;}
+.yui-log .yui-log-ft {position:absolute;top:0em;}
+
+.pass {
+ background-color: green;
+ font-weight: bold;
+ color: white;
+}
+.fail {
+ background-color: red;
+ font-weight: bold;
+ color: white;
+}
+.ignore {
+ background-color: #666;
+ font-weight: bold;
+ color: white;
+}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('test', function(Y) {
+
+ /**
+ * YUI JavaScript Testing Framework
+ *
+ * @module test
+ */
+
+
+ Y.namespace("Test");
+
+ /**
+ * Test case containing various tests to run.
+ * @param template An object containing any number of test methods, other methods,
+ * an optional name, and anything else the test case needs.
+ * @class Case
+ * @namespace Test
+ * @constructor
+ */
+ Y.Test.Case = function (template) {
+
+ /**
+ * Special rules for the test case. Possible subobjects
+ * are fail, for tests that should fail, and error, for
+ * tests that should throw an error.
+ */
+ this._should = {};
+
+ //copy over all properties from the template to this object
+ for (var prop in template) {
+ this[prop] = template[prop];
+ }
+
+ //check for a valid name
+ if (!Y.Lang.isString(this.name)){
+ /**
+ * Name for the test case.
+ */
+ this.name = "testCase" + Y.guid();
+ }
+
+ };
+
+ Y.Test.Case.prototype = {
+
+ /**
+ * Resumes a paused test and runs the given function.
+ * @param {Function} segment (Optional) The function to run.
+ * If omitted, the test automatically passes.
+ * @return {Void}
+ * @method resume
+ */
+ resume : function (segment) {
+ Y.Test.Runner.resume(segment);
+ },
+
+ /**
+ * Causes the test case to wait a specified amount of time and then
+ * continue executing the given code.
+ * @param {Function} segment (Optional) The function to run after the delay.
+ * If omitted, the TestRunner will wait until resume() is called.
+ * @param {int} delay (Optional) The number of milliseconds to wait before running
+ * the function. If omitted, defaults to zero.
+ * @return {Void}
+ * @method wait
+ */
+ wait : function (segment, delay){
+ var args = arguments;
+ if (Y.Lang.isFunction(args[0])){
+ throw new Y.Test.Wait(args[0], args[1]);
+ } else {
+ throw new Y.Test.Wait(function(){
+ Y.Assert.fail("Timeout: wait() called but resume() never called.");
+ }, (Y.Lang.isNumber(args[0]) ? args[0] : 10000));
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Stub Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Function to run before each test is executed.
+ * @return {Void}
+ * @method setUp
+ */
+ setUp : function () {
+ },
+
+ /**
+ * Function to run after each test is executed.
+ * @return {Void}
+ * @method tearDown
+ */
+ tearDown: function () {
+ }
+ };
+
+ /**
+ * Represents a stoppage in test execution to wait for an amount of time before
+ * continuing.
+ * @param {Function} segment A function to run when the wait is over.
+ * @param {int} delay The number of milliseconds to wait before running the code.
+ * @class Wait
+ * @namespace Test
+ * @constructor
+ *
+ */
+ Y.Test.Wait = function (segment, delay) {
+
+ /**
+ * The segment of code to run when the wait is over.
+ * @type Function
+ * @property segment
+ */
+ this.segment = (Y.Lang.isFunction(segment) ? segment : null);
+
+ /**
+ * The delay before running the segment of code.
+ * @type int
+ * @property delay
+ */
+ this.delay = (Y.Lang.isNumber(delay) ? delay : 0);
+ };
+
+
+
+ Y.namespace("Test");
+
+ /**
+ * A test suite that can contain a collection of TestCase and TestSuite objects.
+ * @param {String||Object} data The name of the test suite or an object containing
+ * a name property as well as setUp and tearDown methods.
+ * @namespace Test
+ * @class Suite
+ * @constructor
+ */
+ Y.Test.Suite = function (data /*:String||Object*/) {
+
+ /**
+ * The name of the test suite.
+ * @type String
+ * @property name
+ */
+ this.name = "";
+
+ /**
+ * Array of test suites and
+ * @private
+ */
+ this.items = [];
+
+ //initialize the properties
+ if (Y.Lang.isString(data)){
+ this.name = data;
+ } else if (Y.Lang.isObject(data)){
+ Y.mix(this, data, true);
+ }
+
+ //double-check name
+ if (this.name === ""){
+ this.name = "testSuite" + Y.guid();
+ }
+
+ };
+
+ Y.Test.Suite.prototype = {
+
+ /**
+ * Adds a test suite or test case to the test suite.
+ * @param {Y.Test.Suite||Y.Test.Case} testObject The test suite or test case to add.
+ * @return {Void}
+ * @method add
+ */
+ add : function (testObject /*:Y.Test.Suite*/) {
+ if (testObject instanceof Y.Test.Suite || testObject instanceof Y.Test.Case) {
+ this.items.push(testObject);
+ }
+ return this;
+ },
+
+ //-------------------------------------------------------------------------
+ // Stub Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Function to run before each test is executed.
+ * @return {Void}
+ * @method setUp
+ */
+ setUp : function () {
+ },
+
+ /**
+ * Function to run after each test is executed.
+ * @return {Void}
+ * @method tearDown
+ */
+ tearDown: function () {
+ }
+
+ };
+
+
+ /*
+ * Runs test suites and test cases, providing events to allowing for the
+ * interpretation of test results.
+ * @namespace Test
+ * @class Runner
+ * @static
+ */
+ Y.Test.Runner = (function(){
+
+ /**
+ * A node in the test tree structure. May represent a TestSuite, TestCase, or
+ * test function.
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
+ * @class TestNode
+ * @constructor
+ * @private
+ */
+ function TestNode(testObject){
+
+ /**
+ * The TestSuite, TestCase, or test function represented by this node.
+ * @type Variant
+ * @property testObject
+ */
+ this.testObject = testObject;
+
+ /**
+ * Pointer to this node's first child.
+ * @type TestNode
+ * @property firstChild
+ */
+ this.firstChild = null;
+
+ /**
+ * Pointer to this node's last child.
+ * @type TestNode
+ * @property lastChild
+ */
+ this.lastChild = null;
+
+ /**
+ * Pointer to this node's parent.
+ * @type TestNode
+ * @property parent
+ */
+ this.parent = null;
+
+ /**
+ * Pointer to this node's next sibling.
+ * @type TestNode
+ * @property next
+ */
+ this.next = null;
+
+ /**
+ * Test results for this test object.
+ * @type object
+ * @property results
+ */
+ this.results = {
+ passed : 0,
+ failed : 0,
+ total : 0,
+ ignored : 0
+ };
+
+ //initialize results
+ if (testObject instanceof Y.Test.Suite){
+ this.results.type = "testsuite";
+ this.results.name = testObject.name;
+ } else if (testObject instanceof Y.Test.Case){
+ this.results.type = "testcase";
+ this.results.name = testObject.name;
+ }
+
+ }
+
+ TestNode.prototype = {
+
+ /**
+ * Appends a new test object (TestSuite, TestCase, or test function name) as a child
+ * of this node.
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
+ * @return {Void}
+ */
+ appendChild : function (testObject){
+ var node = new TestNode(testObject);
+ if (this.firstChild === null){
+ this.firstChild = this.lastChild = node;
+ } else {
+ this.lastChild.next = node;
+ this.lastChild = node;
+ }
+ node.parent = this;
+ return node;
+ }
+ };
+
+ /**
+ * Runs test suites and test cases, providing events to allowing for the
+ * interpretation of test results.
+ * @namespace Test
+ * @class Runner
+ * @static
+ */
+ function TestRunner(){
+
+ //inherit from EventProvider
+ TestRunner.superclass.constructor.apply(this,arguments);
+
+ /**
+ * Suite on which to attach all TestSuites and TestCases to be run.
+ * @type Y.Test.Suite
+ * @property masterSuite
+ * @static
+ * @private
+ */
+ this.masterSuite /*:Y.Test.Suite*/ = new Y.Test.Suite("YUI Test Results");
+
+ /**
+ * Pointer to the current node in the test tree.
+ * @type TestNode
+ * @private
+ * @property _cur
+ * @static
+ */
+ this._cur = null;
+
+ /**
+ * Pointer to the root node in the test tree.
+ * @type TestNode
+ * @private
+ * @property _root
+ * @static
+ */
+ this._root = null;
+
+ /**
+ * Indicates if the TestRunner will log events or not.
+ * @type Boolean
+ * @property _log
+ * @private
+ * @static
+ */
+ this._log = true;
+
+ /**
+ * Indicates if the TestRunner is waiting as a result of
+ * wait() being called.
+ * @type Boolean
+ * @property _waiting
+ * @private
+ * @static
+ */
+ this._waiting = false;
+
+ //create events
+ var events = [
+ this.TEST_CASE_BEGIN_EVENT,
+ this.TEST_CASE_COMPLETE_EVENT,
+ this.TEST_SUITE_BEGIN_EVENT,
+ this.TEST_SUITE_COMPLETE_EVENT,
+ this.TEST_PASS_EVENT,
+ this.TEST_FAIL_EVENT,
+ this.TEST_IGNORE_EVENT,
+ this.COMPLETE_EVENT,
+ this.BEGIN_EVENT
+ ];
+ for (var i=0; i < events.length; i++){
+ this.subscribe(events[i], this._logEvent, this, true);
+ }
+
+ }
+
+ Y.extend(TestRunner, Y.Event.Target, {
+
+ //-------------------------------------------------------------------------
+ // Constants
+ //-------------------------------------------------------------------------
+
+ /**
+ * Fires when a test case is opened but before the first
+ * test is executed.
+ * @event testcasebegin
+ * @static
+ */
+ TEST_CASE_BEGIN_EVENT : "testcasebegin",
+
+ /**
+ * Fires when all tests in a test case have been executed.
+ * @event testcasecomplete
+ * @static
+ */
+ TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
+
+ /**
+ * Fires when a test suite is opened but before the first
+ * test is executed.
+ * @event testsuitebegin
+ * @static
+ */
+ TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
+
+ /**
+ * Fires when all test cases in a test suite have been
+ * completed.
+ * @event testsuitecomplete
+ * @static
+ */
+ TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
+
+ /**
+ * Fires when a test has passed.
+ * @event pass
+ * @static
+ */
+ TEST_PASS_EVENT : "pass",
+
+ /**
+ * Fires when a test has failed.
+ * @event fail
+ * @static
+ */
+ TEST_FAIL_EVENT : "fail",
+
+ /**
+ * Fires when a test has been ignored.
+ * @event ignore
+ * @static
+ */
+ TEST_IGNORE_EVENT : "ignore",
+
+ /**
+ * Fires when all test suites and test cases have been completed.
+ * @event complete
+ * @static
+ */
+ COMPLETE_EVENT : "complete",
+
+ /**
+ * Fires when the run() method is called.
+ * @event begin
+ * @static
+ */
+ BEGIN_EVENT : "begin",
+
+ //-------------------------------------------------------------------------
+ // Logging-Related Methods
+ //-------------------------------------------------------------------------
+
+
+ /**
+ * Disable logging via Y.log(). Test output will not be visible unless
+ * TestRunner events are subscribed to.
+ * @return {Void}
+ * @method disableLogging
+ * @static
+ */
+ disableLogging: function(){
+ this._log = false;
+ },
+
+ /**
+ * Enable logging via Y.log(). Test output is published and can be read via
+ * logreader.
+ * @return {Void}
+ * @method enableLogging
+ * @static
+ */
+ enableLogging: function(){
+ this._log = true;
+ },
+
+ /**
+ * Logs TestRunner events using Y.log().
+ * @param {Object} event The event object for the event.
+ * @return {Void}
+ * @method _logEvent
+ * @private
+ * @static
+ */
+ _logEvent: function(event){
+
+ //data variables
+ var message = "";
+ var messageType = "";
+
+ switch(event.type){
+ case this.BEGIN_EVENT:
+ message = "Testing began at " + (new Date()).toString() + ".";
+ messageType = "info";
+ break;
+
+ case this.COMPLETE_EVENT:
+ message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" +
+ event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
+ messageType = "info";
+ break;
+
+ case this.TEST_FAIL_EVENT:
+ message = event.testName + ": failed.\n" + event.error.getMessage();
+ messageType = "fail";
+ break;
+
+ case this.TEST_IGNORE_EVENT:
+ message = event.testName + ": ignored.";
+ messageType = "ignore";
+ break;
+
+ case this.TEST_PASS_EVENT:
+ message = event.testName + ": passed.";
+ messageType = "pass";
+ break;
+
+ case this.TEST_SUITE_BEGIN_EVENT:
+ message = "Test suite \"" + event.testSuite.name + "\" started.";
+ messageType = "info";
+ break;
+
+ case this.TEST_SUITE_COMPLETE_EVENT:
+ message = "Test suite \"" + event.testSuite.name + "\" completed.\nPassed:" +
+ event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
+ messageType = "info";
+ break;
+
+ case this.TEST_CASE_BEGIN_EVENT:
+ message = "Test case \"" + event.testCase.name + "\" started.";
+ messageType = "info";
+ break;
+
+ case this.TEST_CASE_COMPLETE_EVENT:
+ message = "Test case \"" + event.testCase.name + "\" completed.\nPassed:" +
+ event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
+ messageType = "info";
+ break;
+ default:
+ message = "Unexpected event " + event.type;
+ message = "info";
+ }
+
+ //only log if required
+ if (this._log){
+ Y.log(message, messageType, "TestRunner");
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Test Tree-Related Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Adds a test case to the test tree as a child of the specified node.
+ * @param {TestNode} parentNode The node to add the test case to as a child.
+ * @param {Y.Test.Case} testCase The test case to add.
+ * @return {Void}
+ * @static
+ * @private
+ * @method _addTestCaseToTestTree
+ */
+ _addTestCaseToTestTree : function (parentNode, testCase /*:Y.Test.Case*/){
+
+ //add the test suite
+ var node = parentNode.appendChild(testCase),
+ prop,
+ testName;
+
+ //iterate over the items in the test case
+ for (prop in testCase){
+ if ((prop.indexOf("test") === 0 || (prop.toLowerCase().indexOf("should") > -1 && prop.indexOf(" ") > -1 ))&& Y.Lang.isFunction(testCase[prop])){
+ node.appendChild(prop);
+ }
+ }
+
+ },
+
+ /**
+ * Adds a test suite to the test tree as a child of the specified node.
+ * @param {TestNode} parentNode The node to add the test suite to as a child.
+ * @param {Y.Test.Suite} testSuite The test suite to add.
+ * @return {Void}
+ * @static
+ * @private
+ * @method _addTestSuiteToTestTree
+ */
+ _addTestSuiteToTestTree : function (parentNode, testSuite /*:Y.Test.Suite*/) {
+
+ //add the test suite
+ var node = parentNode.appendChild(testSuite);
+
+ //iterate over the items in the master suite
+ for (var i=0; i < testSuite.items.length; i++){
+ if (testSuite.items[i] instanceof Y.Test.Suite) {
+ this._addTestSuiteToTestTree(node, testSuite.items[i]);
+ } else if (testSuite.items[i] instanceof Y.Test.Case) {
+ this._addTestCaseToTestTree(node, testSuite.items[i]);
+ }
+ }
+ },
+
+ /**
+ * Builds the test tree based on items in the master suite. The tree is a hierarchical
+ * representation of the test suites, test cases, and test functions. The resulting tree
+ * is stored in _root and the pointer _cur is set to the root initially.
+ * @return {Void}
+ * @static
+ * @private
+ * @method _buildTestTree
+ */
+ _buildTestTree : function () {
+
+ this._root = new TestNode(this.masterSuite);
+ this._cur = this._root;
+
+ //iterate over the items in the master suite
+ for (var i=0; i < this.masterSuite.items.length; i++){
+ if (this.masterSuite.items[i] instanceof Y.Test.Suite) {
+ this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
+ } else if (this.masterSuite.items[i] instanceof Y.Test.Case) {
+ this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
+ }
+ }
+
+ },
+
+ //-------------------------------------------------------------------------
+ // Private Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Handles the completion of a test object's tests. Tallies test results
+ * from one level up to the next.
+ * @param {TestNode} node The TestNode representing the test object.
+ * @return {Void}
+ * @method _handleTestObjectComplete
+ * @private
+ */
+ _handleTestObjectComplete : function (node) {
+ if (Y.Lang.isObject(node.testObject)){
+ node.parent.results.passed += node.results.passed;
+ node.parent.results.failed += node.results.failed;
+ node.parent.results.total += node.results.total;
+ node.parent.results.ignored += node.results.ignored;
+ node.parent.results[node.testObject.name] = node.results;
+
+ if (node.testObject instanceof Y.Test.Suite){
+ node.testObject.tearDown();
+ this.fire(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
+ } else if (node.testObject instanceof Y.Test.Case){
+ this.fire(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
+ }
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Navigation Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Retrieves the next node in the test tree.
+ * @return {TestNode} The next node in the test tree or null if the end is reached.
+ * @private
+ * @static
+ * @method _next
+ */
+ _next : function () {
+
+ if (this._cur.firstChild) {
+ this._cur = this._cur.firstChild;
+ } else if (this._cur.next) {
+ this._cur = this._cur.next;
+ } else {
+ while (this._cur && !this._cur.next && this._cur !== this._root){
+ this._handleTestObjectComplete(this._cur);
+ this._cur = this._cur.parent;
+ }
+
+ if (this._cur == this._root){
+ this._cur.results.type = "report";
+ this._cur.results.timestamp = (new Date()).toLocaleString();
+ this._cur.results.duration = (new Date()) - this._cur.results.duration;
+ this.fire(this.COMPLETE_EVENT, { results: this._cur.results});
+ this._cur = null;
+ } else {
+ this._handleTestObjectComplete(this._cur);
+ this._cur = this._cur.next;
+ }
+ }
+
+ return this._cur;
+ },
+
+ /**
+ * Runs a test case or test suite, returning the results.
+ * @param {Y.Test.Case|Y.Test.Suite} testObject The test case or test suite to run.
+ * @return {Object} Results of the execution with properties passed, failed, and total.
+ * @private
+ * @method _run
+ * @static
+ */
+ _run : function () {
+
+ //flag to indicate if the TestRunner should wait before continuing
+ var shouldWait = false;
+
+ //get the next test node
+ var node = this._next();
+
+ if (node !== null) {
+ var testObject = node.testObject;
+
+ //figure out what to do
+ if (Y.Lang.isObject(testObject)){
+ if (testObject instanceof Y.Test.Suite){
+ this.fire(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
+ testObject.setUp();
+ } else if (testObject instanceof Y.Test.Case){
+ this.fire(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
+ }
+
+ //some environments don't support setTimeout
+ if (typeof setTimeout != "undefined"){
+ setTimeout(function(){
+ Y.Test.Runner._run();
+ }, 0);
+ } else {
+ this._run();
+ }
+ } else {
+ this._runTest(node);
+ }
+
+ }
+ },
+
+ _resumeTest : function (segment) {
+
+ //get relevant information
+ var node = this._cur;
+
+ //we know there's no more waiting now
+ this._waiting = false;
+
+ //if there's no node, it probably means a wait() was called after resume()
+ if (!node){
+ //TODO: Handle in some way?
+ //console.log("wait() called after resume()");
+ //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
+ return;
+ }
+
+ var testName = node.testObject;
+ var testCase /*:Y.Test.Case*/ = node.parent.testObject;
+
+ //cancel other waits if available
+ if (testCase.__yui_wait){
+ clearTimeout(testCase.__yui_wait);
+ delete testCase.__yui_wait;
+ }
+
+ //get the "should" test cases
+ var shouldFail = (testCase._should.fail || {})[testName];
+ var shouldError = (testCase._should.error || {})[testName];
+
+ //variable to hold whether or not the test failed
+ var failed = false;
+ var error = null;
+
+ //try the test
+ try {
+
+ //run the test
+ segment.apply(testCase);
+
+ //if it should fail, and it got here, then it's a fail because it didn't
+ if (shouldFail){
+ error = new Y.Assert.ShouldFail();
+ failed = true;
+ } else if (shouldError){
+ error = new Y.Assert.ShouldError();
+ failed = true;
+ }
+
+ } catch (thrown){
+
+ //cancel any pending waits, the test already failed
+ if (testCase.__yui_wait){
+ clearTimeout(testCase.__yui_wait);
+ delete testCase.__yui_wait;
+ }
+
+ //figure out what type of error it was
+ if (thrown instanceof Y.Assert.Error) {
+ if (!shouldFail){
+ error = thrown;
+ failed = true;
+ }
+ } else if (thrown instanceof Y.Test.Wait){
+
+ if (Y.Lang.isFunction(thrown.segment)){
+ if (Y.Lang.isNumber(thrown.delay)){
+
+ //some environments don't support setTimeout
+ if (typeof setTimeout != "undefined"){
+ testCase.__yui_wait = setTimeout(function(){
+ Y.Test.Runner._resumeTest(thrown.segment);
+ }, thrown.delay);
+ this._waiting = true;
+ } else {
+ throw new Error("Asynchronous tests not supported in this environment.");
+ }
+ }
+ }
+
+ return;
+
+ } else {
+ //first check to see if it should error
+ if (!shouldError) {
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ } else {
+ //check to see what type of data we have
+ if (Y.Lang.isString(shouldError)){
+
+ //if it's a string, check the error message
+ if (thrown.message != shouldError){
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ }
+ } else if (Y.Lang.isFunction(shouldError)){
+
+ //if it's a function, see if the error is an instance of it
+ if (!(thrown instanceof shouldError)){
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ }
+
+ } else if (Y.Lang.isObject(shouldError)){
+
+ //if it's an object, check the instance and message
+ if (!(thrown instanceof shouldError.constructor) ||
+ thrown.message != shouldError.message){
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ }
+
+ }
+
+ }
+ }
+
+ }
+
+ //fire appropriate event
+ if (failed) {
+ this.fire(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
+ } else {
+ this.fire(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
+ }
+
+ //run the tear down
+ testCase.tearDown();
+
+ //update results
+ node.parent.results[testName] = {
+ result: failed ? "fail" : "pass",
+ message: error ? error.getMessage() : "Test passed",
+ type: "test",
+ name: testName
+ };
+
+ if (failed){
+ node.parent.results.failed++;
+ } else {
+ node.parent.results.passed++;
+ }
+ node.parent.results.total++;
+
+ //set timeout not supported in all environments
+ if (typeof setTimeout != "undefined"){
+ setTimeout(function(){
+ Y.Test.Runner._run();
+ }, 0);
+ } else {
+ this._run();
+ }
+
+ },
+
+ /**
+ * Handles an error as if it occurred within the currently executing
+ * test. This is for mock methods that may be called asynchronously
+ * and therefore out of the scope of the TestRunner. Previously, this
+ * error would bubble up to the browser. Now, this method is used
+ * to tell TestRunner about the error. This should never be called
+ * by anyplace other than the Mock object.
+ * @param {Error} error The error object.
+ * @return {Void}
+ * @method _handleError
+ * @private
+ * @static
+ */
+ _handleError: function(error){
+
+ if (this._waiting){
+ this._resumeTest(function(){
+ throw error;
+ });
+ } else {
+ throw error;
+ }
+
+ },
+
+ /**
+ * Runs a single test based on the data provided in the node.
+ * @param {TestNode} node The TestNode representing the test to run.
+ * @return {Void}
+ * @static
+ * @private
+ * @name _runTest
+ */
+ _runTest : function (node) {
+
+ //get relevant information
+ var testName = node.testObject;
+ var testCase /*:Y.Test.Case*/ = node.parent.testObject;
+ var test = testCase[testName];
+
+ //get the "should" test cases
+ var shouldIgnore = (testCase._should.ignore || {})[testName];
+
+ //figure out if the test should be ignored or not
+ if (shouldIgnore){
+
+ //update results
+ node.parent.results[testName] = {
+ result: "ignore",
+ message: "Test ignored",
+ type: "test",
+ name: testName
+ };
+
+ node.parent.results.ignored++;
+ node.parent.results.total++;
+
+ this.fire(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
+
+ //some environments don't support setTimeout
+ if (typeof setTimeout != "undefined"){
+ setTimeout(function(){
+ Y.Test.Runner._run();
+ }, 0);
+ } else {
+ this._run();
+ }
+
+ } else {
+
+ //run the setup
+ testCase.setUp();
+
+ //now call the body of the test
+ this._resumeTest(test);
+ }
+
+ },
+
+ //-------------------------------------------------------------------------
+ // Protected Methods
+ //-------------------------------------------------------------------------
+
+ /*
+ * Fires events for the TestRunner. This overrides the default fire()
+ * method from EventProvider to add the type property to the data that is
+ * passed through on each event call.
+ * @param {String} type The type of event to fire.
+ * @param {Object} data (Optional) Data for the event.
+ * @method fire
+ * @static
+ * @protected
+ */
+ fire : function (type, data) {
+ data = data || {};
+ data.type = type;
+ TestRunner.superclass.fire.call(this, type, data);
+ },
+
+ //-------------------------------------------------------------------------
+ // Public Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Adds a test suite or test case to the list of test objects to run.
+ * @param testObject Either a TestCase or a TestSuite that should be run.
+ * @return {Void}
+ * @method add
+ * @static
+ */
+ add : function (testObject) {
+ this.masterSuite.add(testObject);
+ return this;
+ },
+
+ /**
+ * Removes all test objects from the runner.
+ * @return {Void}
+ * @method clear
+ * @static
+ */
+ clear : function () {
+ this.masterSuite.items = [];
+ },
+
+ /**
+ * Indicates if the TestRunner is waiting for a test to resume
+ * @return {Boolean} True if the TestRunner is waiting, false if not.
+ * @method isWaiting
+ * @static
+ */
+ isWaiting: function() {
+ return this._waiting;
+ },
+
+ /**
+ * Resumes the TestRunner after wait() was called.
+ * @param {Function} segment The function to run as the rest
+ * of the haulted test.
+ * @return {Void}
+ * @method resume
+ * @static
+ */
+ resume : function (segment) {
+ this._resumeTest(segment || function(){});
+ },
+
+ /**
+ * Runs the test suite.
+ * @return {Void}
+ * @method run
+ * @static
+ */
+ run : function (testObject) {
+
+ //pointer to runner to avoid scope issues
+ var runner = Y.Test.Runner;
+
+ //build the test tree
+ runner._buildTestTree();
+
+ //set when the test started
+ runner._root.results.duration = (new Date()).valueOf();
+
+ //fire the begin event
+ runner.fire(runner.BEGIN_EVENT);
+
+ //begin the testing
+ runner._run();
+ }
+ });
+
+ return new TestRunner();
+
+ })();
+
+
+ /**
+ * The Assert object provides functions to test JavaScript values against
+ * known and expected results. Whenever a comparison (assertion) fails,
+ * an error is thrown.
+ *
+ * @class Assert
+ * @static
+ */
+ Y.Assert = {
+
+ /**
+ * The number of assertions performed.
+ * @property _asserts
+ * @type int
+ * @private
+ */
+ _asserts: 0,
+
+ //-------------------------------------------------------------------------
+ // Helper Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Formats a message so that it can contain the original assertion message
+ * in addition to the custom message.
+ * @param {String} customMessage The message passed in by the developer.
+ * @param {String} defaultMessage The message created by the error by default.
+ * @return {String} The final error message, containing either or both.
+ * @protected
+ * @static
+ * @method _formatMessage
+ */
+ _formatMessage : function (customMessage, defaultMessage) {
+ var message = customMessage;
+ if (Y.Lang.isString(customMessage) && customMessage.length > 0){
+ return Y.Lang.substitute(customMessage, { message: defaultMessage });
+ } else {
+ return defaultMessage;
+ }
+ },
+
+ /**
+ * Returns the number of assertions that have been performed.
+ * @method _getCount
+ * @protected
+ * @static
+ */
+ _getCount: function(){
+ return this._asserts;
+ },
+
+ /**
+ * Increments the number of assertions that have been performed.
+ * @method _increment
+ * @protected
+ * @static
+ */
+ _increment: function(){
+ this._asserts++;
+ },
+
+ /**
+ * Resets the number of assertions that have been performed to 0.
+ * @method _reset
+ * @protected
+ * @static
+ */
+ _reset: function(){
+ this._asserts = 0;
+ },
+
+ //-------------------------------------------------------------------------
+ // Generic Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Forces an assertion error to occur.
+ * @param {String} message (Optional) The message to display with the failure.
+ * @method fail
+ * @static
+ */
+ fail : function (message) {
+ throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Test force-failed."));
+ },
+
+ //-------------------------------------------------------------------------
+ // Equality Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is equal to another. This uses the double equals sign
+ * so type cohersion may occur.
+ * @param {Object} expected The expected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areEqual
+ * @static
+ */
+ areEqual : function (expected, actual, message) {
+ Y.Assert._increment();
+ if (expected != actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be equal."), expected, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is not equal to another. This uses the double equals sign
+ * so type cohersion may occur.
+ * @param {Object} unexpected The unexpected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areNotEqual
+ * @static
+ */
+ areNotEqual : function (unexpected, actual,
+ message) {
+ Y.Assert._increment();
+ if (unexpected == actual) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be equal."), unexpected);
+ }
+ },
+
+ /**
+ * Asserts that a value is not the same as another. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} unexpected The unexpected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areNotSame
+ * @static
+ */
+ areNotSame : function (unexpected, actual, message) {
+ Y.Assert._increment();
+ if (unexpected === actual) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be the same."), unexpected);
+ }
+ },
+
+ /**
+ * Asserts that a value is the same as another. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} expected The expected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areSame
+ * @static
+ */
+ areSame : function (expected, actual, message) {
+ Y.Assert._increment();
+ if (expected !== actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be the same."), expected, actual);
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Boolean Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is false. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isFalse
+ * @static
+ */
+ isFalse : function (actual, message) {
+ Y.Assert._increment();
+ if (false !== actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be false."), false, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is true. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isTrue
+ * @static
+ */
+ isTrue : function (actual, message) {
+ Y.Assert._increment();
+ if (true !== actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be true."), true, actual);
+ }
+
+ },
+
+ //-------------------------------------------------------------------------
+ // Special Value Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is not a number.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNaN
+ * @static
+ */
+ isNaN : function (actual, message){
+ Y.Assert._increment();
+ if (!isNaN(actual)){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is not the special NaN value.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotNaN
+ * @static
+ */
+ isNotNaN : function (actual, message){
+ Y.Assert._increment();
+ if (isNaN(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be NaN."), NaN);
+ }
+ },
+
+ /**
+ * Asserts that a value is not null. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotNull
+ * @static
+ */
+ isNotNull : function (actual, message) {
+ Y.Assert._increment();
+ if (Y.Lang.isNull(actual)) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be null."), null);
+ }
+ },
+
+ /**
+ * Asserts that a value is not undefined. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotUndefined
+ * @static
+ */
+ isNotUndefined : function (actual, message) {
+ Y.Assert._increment();
+ if (Y.Lang.isUndefined(actual)) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should not be undefined."), undefined);
+ }
+ },
+
+ /**
+ * Asserts that a value is null. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNull
+ * @static
+ */
+ isNull : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isNull(actual)) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be null."), null, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is undefined. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isUndefined
+ * @static
+ */
+ isUndefined : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isUndefined(actual)) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Instance Assertion Methods
+ //--------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is an array.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isArray
+ * @static
+ */
+ isArray : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isArray(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an array."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a Boolean.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isBoolean
+ * @static
+ */
+ isBoolean : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isBoolean(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a Boolean."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a function.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isFunction
+ * @static
+ */
+ isFunction : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isFunction(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a function."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is an instance of a particular object. This may return
+ * incorrect results when comparing objects from one frame to constructors in
+ * another frame. For best results, don't use in a cross-frame manner.
+ * @param {Function} expected The function that the object should be an instance of.
+ * @param {Object} actual The object to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isInstanceOf
+ * @static
+ */
+ isInstanceOf : function (expected, actual, message) {
+ Y.Assert._increment();
+ if (!(actual instanceof expected)){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a number.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNumber
+ * @static
+ */
+ isNumber : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isNumber(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a number."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is an object.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isObject
+ * @static
+ */
+ isObject : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isObject(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an object."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a string.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isString
+ * @static
+ */
+ isString : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isString(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a string."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is of a particular type.
+ * @param {String} expectedType The expected type of the variable.
+ * @param {Object} actualValue The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isTypeOf
+ * @static
+ */
+ isTypeOf : function (expectedType, actualValue, message){
+ Y.Assert._increment();
+ if (typeof actualValue != expectedType){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expected, typeof actualValue);
+ }
+ }
+ };
+
+ /**
+ * Asserts that a given condition is true. If not, then a Y.Assert.Error object is thrown
+ * and the test fails.
+ * @method Y.assert
+ * @param {Boolean} condition The condition to test.
+ * @param {String} message The message to display if the assertion fails.
+ * @static
+ */
+ Y.assert = function(condition, message){
+ Y.Assert._increment();
+ if (!condition){
+ throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Assertion failed."));
+ }
+ };
+
+ /**
+ * Forces an assertion error to occur. Shortcut for Y.Assert.fail().
+ * @method Y.fail
+ * @param {String} message (Optional) The message to display with the failure.
+ * @static
+ */
+ Y.fail = Y.Assert.fail;
+
+ //-----------------------------------------------------------------------------
+ // Assertion errors
+ //-----------------------------------------------------------------------------
+
+ /**
+ * Error is thrown whenever an assertion fails. It provides methods
+ * to more easily get at error information and also provides a base class
+ * from which more specific assertion errors can be derived.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @namespace Assert
+ * @class Error
+ * @constructor
+ */
+ Y.Assert.Error = function (message){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message);
+
+ /*
+ * Error message. Must be duplicated to ensure browser receives it.
+ * @type String
+ * @property message
+ */
+ this.message = message;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "Assert Error";
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.Error, Error, {
+
+ /**
+ * Returns a fully formatted error for an assertion failure. This should
+ * be overridden by all subclasses to provide specific information.
+ * @method getMessage
+ * @return {String} A string describing the error.
+ */
+ getMessage : function () {
+ return this.message;
+ },
+
+ /**
+ * Returns a string representation of the error.
+ * @method toString
+ * @return {String} A string representation of the error.
+ */
+ toString : function () {
+ return this.name + ": " + this.getMessage();
+ },
+
+ /**
+ * Returns a primitive value version of the error. Same as toString().
+ * @method valueOf
+ * @return {String} A primitive value version of the error.
+ */
+ valueOf : function () {
+ return this.toString();
+ }
+
+ });
+
+ /**
+ * ComparisonFailure is subclass of Error that is thrown whenever
+ * a comparison between two values fails. It provides mechanisms to retrieve
+ * both the expected and actual value.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @param {Object} expected The expected value.
+ * @param {Object} actual The actual value that caused the assertion to fail.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class ComparisonFailure
+ * @constructor
+ */
+ Y.Assert.ComparisonFailure = function (message, expected, actual){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message);
+
+ /**
+ * The expected value.
+ * @type Object
+ * @property expected
+ */
+ this.expected = expected;
+
+ /**
+ * The actual value.
+ * @type Object
+ * @property actual
+ */
+ this.actual = actual;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "ComparisonFailure";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.ComparisonFailure, Y.Assert.Error, {
+
+ /**
+ * Returns a fully formatted error for an assertion failure. This message
+ * provides information about the expected and actual values.
+ * @method toString
+ * @return {String} A string describing the error.
+ */
+ getMessage : function () {
+ return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
+ "\nActual: " + this.actual + " (" + (typeof this.actual) + ")";
+ }
+
+ });
+
+ /**
+ * UnexpectedValue is subclass of Error that is thrown whenever
+ * a value was unexpected in its scope. This typically means that a test
+ * was performed to determine that a value was *not* equal to a certain
+ * value.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @param {Object} unexpected The unexpected value.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class UnexpectedValue
+ * @constructor
+ */
+ Y.Assert.UnexpectedValue = function (message, unexpected){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message);
+
+ /**
+ * The unexpected value.
+ * @type Object
+ * @property unexpected
+ */
+ this.unexpected = unexpected;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "UnexpectedValue";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.UnexpectedValue, Y.Assert.Error, {
+
+ /**
+ * Returns a fully formatted error for an assertion failure. The message
+ * contains information about the unexpected value that was encountered.
+ * @method getMessage
+ * @return {String} A string describing the error.
+ */
+ getMessage : function () {
+ return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
+ }
+
+ });
+
+ /**
+ * ShouldFail is subclass of Error that is thrown whenever
+ * a test was expected to fail but did not.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class ShouldFail
+ * @constructor
+ */
+ Y.Assert.ShouldFail = function (message){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message || "This test should fail but didn't.");
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "ShouldFail";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.ShouldFail, Y.Assert.Error);
+
+ /**
+ * ShouldError is subclass of Error that is thrown whenever
+ * a test is expected to throw an error but doesn't.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class ShouldError
+ * @constructor
+ */
+ Y.Assert.ShouldError = function (message){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message || "This test should have thrown an error but didn't.");
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "ShouldError";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.ShouldError, Y.Assert.Error);
+
+ /**
+ * UnexpectedError is subclass of Error that is thrown whenever
+ * an error occurs within the course of a test and the test was not expected
+ * to throw an error.
+ *
+ * @param {Error} cause The unexpected error that caused this error to be
+ * thrown.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class UnexpectedError
+ * @constructor
+ */
+ Y.Assert.UnexpectedError = function (cause){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, "Unexpected error: " + cause.message);
+
+ /**
+ * The unexpected error that occurred.
+ * @type Error
+ * @property cause
+ */
+ this.cause = cause;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "UnexpectedError";
+
+ /**
+ * Stack information for the error (if provided).
+ * @type String
+ * @property stack
+ */
+ this.stack = cause.stack;
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.UnexpectedError, Y.Assert.Error);
+
+
+
+ /**
+ * The ArrayAssert object provides functions to test JavaScript array objects
+ * for a variety of cases.
+ *
+ * @class ArrayAssert
+ * @static
+ */
+
+ Y.ArrayAssert = {
+
+ /**
+ * Asserts that a value is present in an array. This uses the triple equals
+ * sign so no type cohersion may occur.
+ * @param {Object} needle The value that is expected in the array.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method contains
+ * @static
+ */
+ contains : function (needle, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ if (Y.Array.indexOf(haystack, needle) == -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that a set of values are present in an array. This uses the triple equals
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
+ * be found.
+ * @param {Object[]} needles An array of values that are expected in the array.
+ * @param {Array} haystack An array of values to check.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method containsItems
+ * @static
+ */
+ containsItems : function (needles, haystack,
+ message) {
+ Y.Assert._increment();
+
+ //begin checking values
+ for (var i=0; i < needles.length; i++){
+ if (Y.Array.indexOf(haystack, needles[i]) == -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value " + needles[i] + " (" + (typeof needles[i]) + ") not found in array [" + haystack + "]."));
+ }
+ }
+ },
+
+ /**
+ * Asserts that a value matching some condition is present in an array. This uses
+ * a function to determine a match.
+ * @param {Function} matcher A function that returns true if the items matches or false if not.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method containsMatch
+ * @static
+ */
+ containsMatch : function (matcher, haystack,
+ message) {
+
+ Y.Assert._increment();
+ //check for valid matcher
+ if (typeof matcher != "function"){
+ throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
+ }
+
+ if (!Y.Array.some(haystack, matcher)){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that a value is not present in an array. This uses the triple equals
+ * Asserts that a value is not present in an array. This uses the triple equals
+ * sign so no type cohersion may occur.
+ * @param {Object} needle The value that is expected in the array.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method doesNotContain
+ * @static
+ */
+ doesNotContain : function (needle, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ if (Y.Array.indexOf(haystack, needle) > -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that a set of values are not present in an array. This uses the triple equals
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
+ * not be found.
+ * @param {Object[]} needles An array of values that are not expected in the array.
+ * @param {Array} haystack An array of values to check.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method doesNotContainItems
+ * @static
+ */
+ doesNotContainItems : function (needles, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ for (var i=0; i < needles.length; i++){
+ if (Y.Array.indexOf(haystack, needles[i]) > -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
+ }
+ }
+
+ },
+
+ /**
+ * Asserts that no values matching a condition are present in an array. This uses
+ * a function to determine a match.
+ * @param {Function} matcher A function that returns true if the items matches or false if not.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method doesNotContainMatch
+ * @static
+ */
+ doesNotContainMatch : function (matcher, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ //check for valid matcher
+ if (typeof matcher != "function"){
+ throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
+ }
+
+ if (Y.Array.some(haystack, matcher)){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that the given value is contained in an array at the specified index.
+ * This uses the triple equals sign so no type cohersion will occur.
+ * @param {Object} needle The value to look for.
+ * @param {Array} haystack The array to search in.
+ * @param {int} index The index at which the value should exist.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method indexOf
+ * @static
+ */
+ indexOf : function (needle, haystack, index, message) {
+
+ Y.Assert._increment();
+
+ //try to find the value in the array
+ for (var i=0; i < haystack.length; i++){
+ if (haystack[i] === needle){
+ if (index != i){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
+ }
+ return;
+ }
+ }
+
+ //if it makes it here, it wasn't found at all
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
+ },
+
+ /**
+ * Asserts that the values in an array are equal, and in the same position,
+ * as values in another array. This uses the double equals sign
+ * so type cohersion may occur. Note that the array objects themselves
+ * need not be the same for this test to pass.
+ * @param {Array} expected An array of the expected values.
+ * @param {Array} actual Any array of the actual values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method itemsAreEqual
+ * @static
+ */
+ itemsAreEqual : function (expected, actual,
+ message) {
+
+ Y.Assert._increment();
+
+ //first check array length
+ if (expected.length != actual.length){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
+ }
+
+ //begin checking values
+ for (var i=0; i < expected.length; i++){
+ if (expected[i] != actual[i]){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not equal."), expected[i], actual[i]);
+ }
+ }
+ },
+
+ /**
+ * Asserts that the values in an array are equivalent, and in the same position,
+ * as values in another array. This uses a function to determine if the values
+ * are equivalent. Note that the array objects themselves
+ * need not be the same for this test to pass.
+ * @param {Array} expected An array of the expected values.
+ * @param {Array} actual Any array of the actual values.
+ * @param {Function} comparator A function that returns true if the values are equivalent
+ * or false if not.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @return {Void}
+ * @method itemsAreEquivalent
+ * @static
+ */
+ itemsAreEquivalent : function (expected, actual,
+ comparator, message) {
+
+ Y.Assert._increment();
+
+ //make sure the comparator is valid
+ if (typeof comparator != "function"){
+ throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
+ }
+
+ //first check array length
+ if (expected.length != actual.length){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
+ }
+
+ //begin checking values
+ for (var i=0; i < expected.length; i++){
+ if (!comparator(expected[i], actual[i])){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
+ }
+ }
+ },
+
+ /**
+ * Asserts that an array is empty.
+ * @param {Array} actual The array to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isEmpty
+ * @static
+ */
+ isEmpty : function (actual, message) {
+ Y.Assert._increment();
+ if (actual.length > 0){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should be empty."));
+ }
+ },
+
+ /**
+ * Asserts that an array is not empty.
+ * @param {Array} actual The array to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotEmpty
+ * @static
+ */
+ isNotEmpty : function (actual, message) {
+ Y.Assert._increment();
+ if (actual.length === 0){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should not be empty."));
+ }
+ },
+
+ /**
+ * Asserts that the values in an array are the same, and in the same position,
+ * as values in another array. This uses the triple equals sign
+ * so no type cohersion will occur. Note that the array objects themselves
+ * need not be the same for this test to pass.
+ * @param {Array} expected An array of the expected values.
+ * @param {Array} actual Any array of the actual values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method itemsAreSame
+ * @static
+ */
+ itemsAreSame : function (expected, actual,
+ message) {
+
+ Y.Assert._increment();
+
+ //first check array length
+ if (expected.length != actual.length){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
+ }
+
+ //begin checking values
+ for (var i=0; i < expected.length; i++){
+ if (expected[i] !== actual[i]){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not the same."), expected[i], actual[i]);
+ }
+ }
+ },
+
+ /**
+ * Asserts that the given value is contained in an array at the specified index,
+ * starting from the back of the array.
+ * This uses the triple equals sign so no type cohersion will occur.
+ * @param {Object} needle The value to look for.
+ * @param {Array} haystack The array to search in.
+ * @param {int} index The index at which the value should exist.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method lastIndexOf
+ * @static
+ */
+ lastIndexOf : function (needle, haystack, index, message) {
+
+ //try to find the value in the array
+ for (var i=haystack.length; i >= 0; i--){
+ if (haystack[i] === needle){
+ if (index != i){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
+ }
+ return;
+ }
+ }
+
+ //if it makes it here, it wasn't found at all
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array."));
+ }
+
+ };
+
+
+ /**
+ * The ObjectAssert object provides functions to test JavaScript objects
+ * for a variety of cases.
+ *
+ * @class ObjectAssert
+ * @static
+ */
+ Y.ObjectAssert = {
+
+ areEqual: function(expected, actual, message) {
+ Y.Assert._increment();
+ Y.Object.each(expected, function(value, name){
+ if (expected[name] != actual[name]){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be equal for property " + name), expected[name], actual[name]);
+ }
+ });
+ },
+
+ /**
+ * Asserts that an object has a property with the given name.
+ * @param {String} propertyName The name of the property to test.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method hasKey
+ * @static
+ */
+ hasKey: function (propertyName, object, message) {
+ Y.Assert._increment();
+ if (!Y.Object.hasKey(object, propertyName)){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
+ }
+ },
+
+ /**
+ * Asserts that an object has all properties of a reference object.
+ * @param {Array} properties An array of property names that should be on the object.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method hasKeys
+ * @static
+ */
+ hasKeys: function (properties, object, message) {
+ Y.Assert._increment();
+ for (var i=0; i < properties.length; i++){
+ if (!Y.Object.hasKey(object, properties[i])){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object."));
+ }
+ }
+ },
+
+ /**
+ * Asserts that a property with the given name exists on an object instance (not on its prototype).
+ * @param {String} propertyName The name of the property to test.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method ownsKey
+ * @static
+ */
+ ownsKey: function (propertyName, object, message) {
+ Y.Assert._increment();
+ if (!object.hasOwnProperty(propertyName)){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
+ }
+ },
+
+ /**
+ * Asserts that all properties exist on an object instance (not on its prototype).
+ * @param {Array} properties An array of property names that should be on the object.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method ownsKeys
+ * @static
+ */
+ ownsKeys: function (properties, object, message) {
+ Y.Assert._increment();
+ for (var i=0; i < properties.length; i++){
+ if (!object.hasOwnProperty(properties[i])){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
+ }
+ }
+ },
+
+ /**
+ * Asserts that an object owns no properties.
+ * @param {Object} object The object to check.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method ownsNoKeys
+ * @static
+ */
+ ownsNoKeys : function (object, message) {
+ Y.Assert._increment();
+
+ var keys = Y.Object.keys(object);
+
+ if (keys.length > 0){
+ Y.fail(Y.Assert._formatMessage(message, "Object owns " + keys.length + " properties but should own none."));
+ }
+
+ }
+ };
+
+
+
+ /**
+ * The DateAssert object provides functions to test JavaScript Date objects
+ * for a variety of cases.
+ *
+ * @class DateAssert
+ * @static
+ */
+
+ Y.DateAssert = {
+
+ /**
+ * Asserts that a date's month, day, and year are equal to another date's.
+ * @param {Date} expected The expected date.
+ * @param {Date} actual The actual date to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method datesAreEqual
+ * @static
+ */
+ datesAreEqual : function (expected, actual, message){
+ Y.Assert._increment();
+ if (expected instanceof Date && actual instanceof Date){
+ var msg = "";
+
+ //check years first
+ if (expected.getFullYear() != actual.getFullYear()){
+ msg = "Years should be equal.";
+ }
+
+ //now check months
+ if (expected.getMonth() != actual.getMonth()){
+ msg = "Months should be equal.";
+ }
+
+ //last, check the day of the month
+ if (expected.getDate() != actual.getDate()){
+ msg = "Days of month should be equal.";
+ }
+
+ if (msg.length){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, msg), expected, actual);
+ }
+ } else {
+ throw new TypeError("Y.Assert.datesAreEqual(): Expected and actual values must be Date objects.");
+ }
+ },
+
+ /**
+ * Asserts that a date's hour, minutes, and seconds are equal to another date's.
+ * @param {Date} expected The expected date.
+ * @param {Date} actual The actual date to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method timesAreEqual
+ * @static
+ */
+ timesAreEqual : function (expected, actual, message){
+ Y.Assert._increment();
+ if (expected instanceof Date && actual instanceof Date){
+ var msg = "";
+
+ //check hours first
+ if (expected.getHours() != actual.getHours()){
+ msg = "Hours should be equal.";
+ }
+
+ //now check minutes
+ if (expected.getMinutes() != actual.getMinutes()){
+ msg = "Minutes should be equal.";
+ }
+
+ //last, check the seconds
+ if (expected.getSeconds() != actual.getSeconds()){
+ msg = "Seconds should be equal.";
+ }
+
+ if (msg.length){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, msg), expected, actual);
+ }
+ } else {
+ throw new TypeError("DateY.AsserttimesAreEqual(): Expected and actual values must be Date objects.");
+ }
+ }
+
+ };
+
+
+ Y.namespace("Test.Format");
+
+ /* (intentionally not documented)
+ * Basic XML escaping method. Replaces quotes, less-than, greater-than,
+ * apostrophe, and ampersand characters with their corresponding entities.
+ * @param {String} text The text to encode.
+ * @return {String} The XML-escaped text.
+ */
+ function xmlEscape(text){
+
+ return text.replace(/[<>"'&]/g, function(value){
+ switch(value){
+ case "<": return "<";
+ case ">": return ">";
+ case "\"": return """;
+ case "'": return "'";
+ case "&": return "&";
+ }
+ });
+
+ }
+
+ /**
+ * Returns test results formatted as a JSON string. Requires JSON utility.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} A JSON-formatted string of results.
+ * @namespace Test.Format
+ * @method JSON
+ * @static
+ */
+ Y.Test.Format.JSON = function(results) {
+ return Y.JSON.stringify(results);
+ };
+
+ /**
+ * Returns test results formatted as an XML string.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} An XML-formatted string of results.
+ * @namespace Test.Format
+ * @method XML
+ * @static
+ */
+ Y.Test.Format.XML = function(results) {
+
+ var l = Y.Lang;
+ var xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
+
+ if (results.type == "test"){
+ xml += " result=\"" + xmlEscape(results.result) + "\" message=\"" + xmlEscape(results.message) + "\">";
+ } else {
+ xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += arguments.callee(value);
+ }
+ });
+ }
+
+ xml += "</" + results.type + ">";
+
+ return xml;
+
+ };
+
+ /**
+ * Returns test results formatted as an XML string.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} An XML-formatted string of results.
+ * @namespace Test.Format
+ * @method XML
+ * @static
+ */
+ Y.Test.Format.XML = function(results) {
+
+ function serializeToXML(results){
+ var l = Y.Lang,
+ xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
+
+ if (l.isNumber(results.duration)){
+ xml += " duration=\"" + results.duration + "\"";
+ }
+
+ if (results.type == "test"){
+ xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
+ } else {
+ xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToXML(value);
+ }
+ });
+ }
+
+ xml += "</" + results.type + ">";
+
+ return xml;
+ }
+
+ return "<?xml version=\"1.0\" charset=\"UTF-8\"?>" + serializeToXML(results);
+
+ };
+
+
+ /**
+ * Returns test results formatted in JUnit XML format.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} An XML-formatted string of results.
+ * @namespace Test.Format
+ * @method JUnitXML
+ * @static
+ */
+ Y.Test.Format.JUnitXML = function(results) {
+
+
+ function serializeToJUnitXML(results){
+ var l = Y.Lang,
+ xml = "",
+ prop;
+
+ switch (results.type){
+ //equivalent to testcase in JUnit
+ case "test":
+ if (results.result != "ignore"){
+ xml = "<testcase name=\"" + xmlEscape(results.name) + "\">";
+ if (results.result == "fail"){
+ xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
+ }
+ xml+= "</testcase>";
+ }
+ break;
+
+ //equivalent to testsuite in JUnit
+ case "testcase":
+
+ xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\">";
+
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToJUnitXML(value);
+ }
+ });
+
+ xml += "</testsuite>";
+ break;
+
+ case "testsuite":
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToJUnitXML(value);
+ }
+ });
+
+ //skip output - no JUnit equivalent
+ break;
+
+ case "report":
+
+ xml = "<testsuites>";
+
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToJUnitXML(value);
+ }
+ });
+
+ xml += "</testsuites>";
+
+ //no default
+ }
+
+ return xml;
+
+ }
+
+ return "<?xml version=\"1.0\" charset=\"UTF-8\"?>" + serializeToJUnitXML(results);
+ };
+
+
+
+ Y.namespace("Test");
+
+ /**
+ * An object capable of sending test results to a server.
+ * @param {String} url The URL to submit the results to.
+ * @param {Function} format (Optiona) A function that outputs the results in a specific format.
+ * Default is Y.Test.Format.XML.
+ * @constructor
+ * @namespace Test
+ * @class Reporter
+ */
+ Y.Test.Reporter = function(url, format) {
+
+ /**
+ * The URL to submit the data to.
+ * @type String
+ * @property url
+ */
+ this.url = url;
+
+ /**
+ * The formatting function to call when submitting the data.
+ * @type Function
+ * @property format
+ */
+ this.format = format || Y.Test.Format.XML;
+
+ /**
+ * Extra fields to submit with the request.
+ * @type Object
+ * @property _fields
+ * @private
+ */
+ this._fields = new Object();
+
+ /**
+ * The form element used to submit the results.
+ * @type HTMLFormElement
+ * @property _form
+ * @private
+ */
+ this._form = null;
+
+ /**
+ * Iframe used as a target for form submission.
+ * @type HTMLIFrameElement
+ * @property _iframe
+ * @private
+ */
+ this._iframe = null;
+ };
+
+ Y.Test.Reporter.prototype = {
+
+ //restore missing constructor
+ constructor: Y.Test.Reporter,
+
+ /**
+ * Adds a field to the form that submits the results.
+ * @param {String} name The name of the field.
+ * @param {Variant} value The value of the field.
+ * @return {Void}
+ * @method addField
+ */
+ addField : function (name, value){
+ this._fields[name] = value;
+ },
+
+ /**
+ * Removes all previous defined fields.
+ * @return {Void}
+ * @method addField
+ */
+ clearFields : function(){
+ this._fields = new Object();
+ },
+
+ /**
+ * Cleans up the memory associated with the TestReporter, removing DOM elements
+ * that were created.
+ * @return {Void}
+ * @method destroy
+ */
+ destroy : function() {
+ if (this._form){
+ this._form.parentNode.removeChild(this._form);
+ this._form = null;
+ }
+ if (this._iframe){
+ this._iframe.parentNode.removeChild(this._iframe);
+ this._iframe = null;
+ }
+ this._fields = null;
+ },
+
+ /**
+ * Sends the report to the server.
+ * @param {Object} results The results object created by TestRunner.
+ * @return {Void}
+ * @method report
+ */
+ report : function(results){
+
+ //if the form hasn't been created yet, create it
+ if (!this._form){
+ this._form = document.createElement("form");
+ this._form.method = "post";
+ this._form.style.visibility = "hidden";
+ this._form.style.position = "absolute";
+ this._form.style.top = 0;
+ document.body.appendChild(this._form);
+
+ //IE won't let you assign a name using the DOM, must do it the hacky way
+ if (Y.UA.ie){
+ this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
+ } else {
+ this._iframe = document.createElement("iframe");
+ this._iframe.name = "yuiTestTarget";
+ }
+
+ this._iframe.src = "javascript:false";
+ this._iframe.style.visibility = "hidden";
+ this._iframe.style.position = "absolute";
+ this._iframe.style.top = 0;
+ document.body.appendChild(this._iframe);
+
+ this._form.target = "yuiTestTarget";
+ }
+
+ //set the form's action
+ this._form.action = this.url;
+
+ //remove any existing fields
+ while(this._form.hasChildNodes()){
+ this._form.removeChild(this._form.lastChild);
+ }
+
+ //create default fields
+ this._fields.results = this.format(results);
+ this._fields.useragent = navigator.userAgent;
+ this._fields.timestamp = (new Date()).toLocaleString();
+
+ //add fields to the form
+ Y.Object.each(this._fields, function(value, prop){
+ if (typeof value != "function"){
+ var input = document.createElement("input");
+ input.type = "hidden";
+ input.name = prop;
+ input.value = value;
+ this._form.appendChild(input);
+ }
+ }, this);
+
+ //remove default fields
+ delete this._fields.results;
+ delete this._fields.useragent;
+ delete this._fields.timestamp;
+
+ if (arguments[1] !== false){
+ this._form.submit();
+ }
+
+ }
+
+ };
+
+ /**
+ * Creates a new mock object.
+ * @class Mock
+ * @constructor
+ * @param {Object} template (Optional) An object whose methods
+ * should be stubbed out on the mock object.
+ */
+ Y.Mock = function(template){
+
+ //use blank object is nothing is passed in
+ template = template || {};
+
+ var mock = null;
+
+ //try to create mock that keeps prototype chain intact
+ try {
+ mock = Y.Object(template);
+ } catch (ex) {
+ mock = {};
+ Y.log("Couldn't create mock with prototype.", "warn", "Mock");
+ }
+
+ //create new versions of the methods so that they don't actually do anything
+ Y.Object.each(template, function(name){
+ if (Y.Lang.isFunction(template[name])){
+ mock[name] = function(){
+ Y.Assert.fail("Method " + name + "() was called but was not expected to be.");
+ };
+ }
+ });
+
+ //return it
+ return mock;
+ };
+
+ /**
+ * Assigns an expectation to a mock object. This is used to create
+ * methods and properties on the mock object that are monitored for
+ * calls and changes, respectively.
+ * @param {Object} mock The object to add the expectation to.
+ * @param {Object} expectation An object defining the expectation. For
+ * a method, the keys "method" and "args" are required with
+ * an optional "returns" key available. For properties, the keys
+ * "property" and "value" are required.
+ * @return {void}
+ * @method expect
+ * @static
+ */
+ Y.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
+
+ //make sure there's a place to store the expectations
+ if (!mock.__expectations) {
+ mock.__expectations = {};
+ }
+
+ //method expectation
+ if (expectation.method){
+ var name = expectation.method,
+ args = expectation.args || expectation.arguments || [],
+ result = expectation.returns,
+ callCount = Y.Lang.isNumber(expectation.callCount) ? expectation.callCount : 1,
+ error = expectation.error,
+ run = expectation.run || function(){};
+
+ //save expectations
+ mock.__expectations[name] = expectation;
+ expectation.callCount = callCount;
+ expectation.actualCallCount = 0;
+
+ //process arguments
+ Y.Array.each(args, function(arg, i, array){
+ if (!(array[i] instanceof Y.Mock.Value)){
+ array[i] = Y.Mock.Value(Y.Assert.areSame, [arg], "Argument " + i + " of " + name + "() is incorrect.");
+ }
+ });
+
+ //if the method is expected to be called
+ if (callCount > 0){
+ mock[name] = function(){
+ try {
+ expectation.actualCallCount++;
+ Y.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
+ for (var i=0, len=args.length; i < len; i++){
+ //if (args[i]){
+ args[i].verify(arguments[i]);
+ //} else {
+ // Y.Assert.fail("Argument " + i + " (" + arguments[i] + ") was not expected to be used.");
+ //}
+
+ }
+
+ run.apply(this, arguments);
+
+ if (error){
+ throw error;
+ }
+ } catch (ex){
+ //route through TestRunner for proper handling
+ Y.Test.Runner._handleError(ex);
+ }
+
+ return result;
+ };
+ } else {
+
+ //method should fail if called when not expected
+ mock[name] = function(){
+ try {
+ Y.Assert.fail("Method " + name + "() should not have been called.");
+ } catch (ex){
+ //route through TestRunner for proper handling
+ Y.Test.Runner._handleError(ex);
+ }
+ };
+ }
+ } else if (expectation.property){
+ //save expectations
+ mock.__expectations[name] = expectation;
+ }
+ };
+
+ /**
+ * Verifies that all expectations of a mock object have been met and
+ * throws an assertion error if not.
+ * @param {Object} mock The object to verify..
+ * @return {void}
+ * @method verify
+ * @static
+ */
+ Y.Mock.verify = function(mock /*:Object*/){
+ try {
+ Y.Object.each(mock.__expectations, function(expectation){
+ if (expectation.method) {
+ Y.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
+ } else if (expectation.property){
+ Y.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value.");
+ }
+ });
+ } catch (ex){
+ //route through TestRunner for proper handling
+ Y.Test.Runner._handleError(ex);
+ }
+ };
+
+ Y.Mock.Value = function(method, originalArgs, message){
+ if (this instanceof Y.Mock.Value){
+ this.verify = function(value){
+ var args = [].concat(originalArgs || []);
+ args.push(value);
+ args.push(message);
+ method.apply(null, args);
+ };
+ } else {
+ return new Y.Mock.Value(method, originalArgs, message);
+ }
+ };
+
+ Y.Mock.Value.Any = Y.Mock.Value(function(){});
+ Y.Mock.Value.Boolean = Y.Mock.Value(Y.Assert.isBoolean);
+ Y.Mock.Value.Number = Y.Mock.Value(Y.Assert.isNumber);
+ Y.Mock.Value.String = Y.Mock.Value(Y.Assert.isString);
+ Y.Mock.Value.Object = Y.Mock.Value(Y.Assert.isObject);
+ Y.Mock.Value.Function = Y.Mock.Value(Y.Assert.isFunction);
+
+
+
+}, '3.0.0' ,{requires:['substitute','event-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("test",function(B){B.namespace("Test");B.Test.Case=function(C){this._should={};for(var D in C){this[D]=C[D];}if(!B.Lang.isString(this.name)){this.name="testCase"+B.guid();}};B.Test.Case.prototype={resume:function(C){B.Test.Runner.resume(C);},wait:function(E,D){var C=arguments;if(B.Lang.isFunction(C[0])){throw new B.Test.Wait(C[0],C[1]);}else{throw new B.Test.Wait(function(){B.Assert.fail("Timeout: wait() called but resume() never called.");},(B.Lang.isNumber(C[0])?C[0]:10000));}},setUp:function(){},tearDown:function(){}};B.Test.Wait=function(D,C){this.segment=(B.Lang.isFunction(D)?D:null);this.delay=(B.Lang.isNumber(C)?C:0);};B.namespace("Test");B.Test.Suite=function(C){this.name="";this.items=[];if(B.Lang.isString(C)){this.name=C;}else{if(B.Lang.isObject(C)){B.mix(this,C,true);}}if(this.name===""){this.name="testSuite"+B.guid();}};B.Test.Suite.prototype={add:function(C){if(C instanceof B.Test.Suite||C instanceof B.Test.Case){this.items.push(C);}return this;},setUp:function(){},tearDown:function(){}};B.Test.Runner=(function(){function D(E){this.testObject=E;this.firstChild=null;this.lastChild=null;this.parent=null;this.next=null;this.results={passed:0,failed:0,total:0,ignored:0};if(E instanceof B.Test.Suite){this.results.type="testsuite";this.results.name=E.name;}else{if(E instanceof B.Test.Case){this.results.type="testcase";this.results.name=E.name;}}}D.prototype={appendChild:function(E){var F=new D(E);if(this.firstChild===null){this.firstChild=this.lastChild=F;}else{this.lastChild.next=F;this.lastChild=F;}F.parent=this;return F;}};function C(){C.superclass.constructor.apply(this,arguments);this.masterSuite=new B.Test.Suite("YUI Test Results");this._cur=null;this._root=null;this._log=true;this._waiting=false;var F=[this.TEST_CASE_BEGIN_EVENT,this.TEST_CASE_COMPLETE_EVENT,this.TEST_SUITE_BEGIN_EVENT,this.TEST_SUITE_COMPLETE_EVENT,this.TEST_PASS_EVENT,this.TEST_FAIL_EVENT,this.TEST_IGNORE_EVENT,this.COMPLETE_EVENT,this.BEGIN_EVENT];for(var E=0;E<F.length;E++){this.subscribe(F[E],this._logEvent,this,true);}}B.extend(C,B.Event.Target,{TEST_CASE_BEGIN_EVENT:"testcasebegin",TEST_CASE_COMPLETE_EVENT:"testcasecomplete",TEST_SUITE_BEGIN_EVENT:"testsuitebegin",TEST_SUITE_COMPLETE_EVENT:"testsuitecomplete",TEST_PASS_EVENT:"pass",TEST_FAIL_EVENT:"fail",TEST_IGNORE_EVENT:"ignore",COMPLETE_EVENT:"complete",BEGIN_EVENT:"begin",disableLogging:function(){this._log=false;},enableLogging:function(){this._log=true;},_logEvent:function(G){var F="";var E="";switch(G.type){case this.BEGIN_EVENT:F="Testing began at "+(new Date()).toString()+".";E="info";break;case this.COMPLETE_EVENT:F="Testing completed at "+(new Date()).toString()+".\nPassed:"+G.results.passed+" Failed:"+G.results.failed+" Total:"+G.results.total;E="info";break;case this.TEST_FAIL_EVENT:F=G.testName+": failed.\n"+G.error.getMessage();E="fail";break;case this.TEST_IGNORE_EVENT:F=G.testName+": ignored.";E="ignore";break;case this.TEST_PASS_EVENT:F=G.testName+": passed.";E="pass";break;case this.TEST_SUITE_BEGIN_EVENT:F='Test suite "'+G.testSuite.name+'" started.';E="info";break;case this.TEST_SUITE_COMPLETE_EVENT:F='Test suite "'+G.testSuite.name+'" completed.\nPassed:'+G.results.passed+" Failed:"+G.results.failed+" Total:"+G.results.total;E="info";break;case this.TEST_CASE_BEGIN_EVENT:F='Test case "'+G.testCase.name+'" started.';E="info";break;case this.TEST_CASE_COMPLETE_EVENT:F='Test case "'+G.testCase.name+'" completed.\nPassed:'+G.results.passed+" Failed:"+G.results.failed+" Total:"+G.results.total;E="info";break;default:F="Unexpected event "+G.type;F="info";}if(this._log){B.log(F,E,"TestRunner");}},_addTestCaseToTestTree:function(F,G){var H=F.appendChild(G),I,E;for(I in G){if((I.indexOf("test")===0||(I.toLowerCase().indexOf("should")>-1&&I.indexOf(" ")>-1))&&B.Lang.isFunction(G[I])){H.appendChild(I);}}},_addTestSuiteToTestTree:function(E,H){var G=E.appendChild(H);for(var F=0;F<H.items.length;F++){if(H.items[F] instanceof B.Test.Suite){this._addTestSuiteToTestTree(G,H.items[F]);}else{if(H.items[F] instanceof B.Test.Case){this._addTestCaseToTestTree(G,H.items[F]);}}}},_buildTestTree:function(){this._root=new D(this.masterSuite);this._cur=this._root;for(var E=0;E<this.masterSuite.items.length;E++){if(this.masterSuite.items[E] instanceof B.Test.Suite){this._addTestSuiteToTestTree(this._root,this.masterSuite.items[E]);}else{if(this.masterSuite.items[E] instanceof B.Test.Case){this._addTestCaseToTestTree(this._root,this.masterSuite.items[E]);}}}},_handleTestObjectComplete:function(E){if(B.Lang.isObject(E.testObject)){E.parent.results.passed+=E.results.passed;E.parent.results.failed+=E.results.failed;E.parent.results.total+=E.results.total;E.parent.results.ignored+=E.results.ignored;E.parent.results[E.testObject.name]=E.results;if(E.testObject instanceof B.Test.Suite){E.testObject.tearDown();this.fire(this.TEST_SUITE_COMPLETE_EVENT,{testSuite:E.testObject,results:E.results});}else{if(E.testObject instanceof B.Test.Case){this.fire(this.TEST_CASE_COMPLETE_EVENT,{testCase:E.testObject,results:E.results});}}}},_next:function(){if(this._cur.firstChild){this._cur=this._cur.firstChild;}else{if(this._cur.next){this._cur=this._cur.next;}else{while(this._cur&&!this._cur.next&&this._cur!==this._root){this._handleTestObjectComplete(this._cur);this._cur=this._cur.parent;}if(this._cur==this._root){this._cur.results.type="report";this._cur.results.timestamp=(new Date()).toLocaleString();this._cur.results.duration=(new Date())-this._cur.results.duration;this.fire(this.COMPLETE_EVENT,{results:this._cur.results});this._cur=null;}else{this._handleTestObjectComplete(this._cur);this._cur=this._cur.next;}}}return this._cur;},_run:function(){var G=false;var F=this._next();if(F!==null){var E=F.testObject;if(B.Lang.isObject(E)){if(E instanceof B.Test.Suite){this.fire(this.TEST_SUITE_BEGIN_EVENT,{testSuite:E});E.setUp();}else{if(E instanceof B.Test.Case){this.fire(this.TEST_CASE_BEGIN_EVENT,{testCase:E});}}if(typeof setTimeout!="undefined"){setTimeout(function(){B.Test.Runner._run();
+},0);}else{this._run();}}else{this._runTest(F);}}},_resumeTest:function(I){var E=this._cur;this._waiting=false;if(!E){return;}var J=E.testObject;var G=E.parent.testObject;if(G.__yui_wait){clearTimeout(G.__yui_wait);delete G.__yui_wait;}var M=(G._should.fail||{})[J];var F=(G._should.error||{})[J];var H=false;var K=null;try{I.apply(G);if(M){K=new B.Assert.ShouldFail();H=true;}else{if(F){K=new B.Assert.ShouldError();H=true;}}}catch(L){if(G.__yui_wait){clearTimeout(G.__yui_wait);delete G.__yui_wait;}if(L instanceof B.Assert.Error){if(!M){K=L;H=true;}}else{if(L instanceof B.Test.Wait){if(B.Lang.isFunction(L.segment)){if(B.Lang.isNumber(L.delay)){if(typeof setTimeout!="undefined"){G.__yui_wait=setTimeout(function(){B.Test.Runner._resumeTest(L.segment);},L.delay);this._waiting=true;}else{throw new Error("Asynchronous tests not supported in this environment.");}}}return;}else{if(!F){K=new B.Assert.UnexpectedError(L);H=true;}else{if(B.Lang.isString(F)){if(L.message!=F){K=new B.Assert.UnexpectedError(L);H=true;}}else{if(B.Lang.isFunction(F)){if(!(L instanceof F)){K=new B.Assert.UnexpectedError(L);H=true;}}else{if(B.Lang.isObject(F)){if(!(L instanceof F.constructor)||L.message!=F.message){K=new B.Assert.UnexpectedError(L);H=true;}}}}}}}}if(H){this.fire(this.TEST_FAIL_EVENT,{testCase:G,testName:J,error:K});}else{this.fire(this.TEST_PASS_EVENT,{testCase:G,testName:J});}G.tearDown();E.parent.results[J]={result:H?"fail":"pass",message:K?K.getMessage():"Test passed",type:"test",name:J};if(H){E.parent.results.failed++;}else{E.parent.results.passed++;}E.parent.results.total++;if(typeof setTimeout!="undefined"){setTimeout(function(){B.Test.Runner._run();},0);}else{this._run();}},_handleError:function(E){if(this._waiting){this._resumeTest(function(){throw E;});}else{throw E;}},_runTest:function(H){var E=H.testObject;var F=H.parent.testObject;var I=F[E];var G=(F._should.ignore||{})[E];if(G){H.parent.results[E]={result:"ignore",message:"Test ignored",type:"test",name:E};H.parent.results.ignored++;H.parent.results.total++;this.fire(this.TEST_IGNORE_EVENT,{testCase:F,testName:E});if(typeof setTimeout!="undefined"){setTimeout(function(){B.Test.Runner._run();},0);}else{this._run();}}else{F.setUp();this._resumeTest(I);}},fire:function(E,F){F=F||{};F.type=E;C.superclass.fire.call(this,E,F);},add:function(E){this.masterSuite.add(E);return this;},clear:function(){this.masterSuite.items=[];},isWaiting:function(){return this._waiting;},resume:function(E){this._resumeTest(E||function(){});},run:function(E){var F=B.Test.Runner;F._buildTestTree();F._root.results.duration=(new Date()).valueOf();F.fire(F.BEGIN_EVENT);F._run();}});return new C();})();B.Assert={_asserts:0,_formatMessage:function(D,C){var E=D;if(B.Lang.isString(D)&&D.length>0){return B.Lang.substitute(D,{message:C});}else{return C;}},_getCount:function(){return this._asserts;},_increment:function(){this._asserts++;},_reset:function(){this._asserts=0;},fail:function(C){throw new B.Assert.Error(B.Assert._formatMessage(C,"Test force-failed."));},areEqual:function(D,E,C){B.Assert._increment();if(D!=E){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Values should be equal."),D,E);}},areNotEqual:function(C,E,D){B.Assert._increment();if(C==E){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(D,"Values should not be equal."),C);}},areNotSame:function(C,E,D){B.Assert._increment();if(C===E){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(D,"Values should not be the same."),C);}},areSame:function(D,E,C){B.Assert._increment();if(D!==E){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Values should be the same."),D,E);}},isFalse:function(D,C){B.Assert._increment();if(false!==D){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Value should be false."),false,D);}},isTrue:function(D,C){B.Assert._increment();if(true!==D){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Value should be true."),true,D);}},isNaN:function(D,C){B.Assert._increment();if(!isNaN(D)){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Value should be NaN."),NaN,D);}},isNotNaN:function(D,C){B.Assert._increment();if(isNaN(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Values should not be NaN."),NaN);}},isNotNull:function(D,C){B.Assert._increment();if(B.Lang.isNull(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Values should not be null."),null);}},isNotUndefined:function(D,C){B.Assert._increment();if(B.Lang.isUndefined(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Value should not be undefined."),undefined);}},isNull:function(D,C){B.Assert._increment();if(!B.Lang.isNull(D)){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Value should be null."),null,D);}},isUndefined:function(D,C){B.Assert._increment();if(!B.Lang.isUndefined(D)){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Value should be undefined."),undefined,D);}},isArray:function(D,C){B.Assert._increment();if(!B.Lang.isArray(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Value should be an array."),D);}},isBoolean:function(D,C){B.Assert._increment();if(!B.Lang.isBoolean(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Value should be a Boolean."),D);}},isFunction:function(D,C){B.Assert._increment();if(!B.Lang.isFunction(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Value should be a function."),D);}},isInstanceOf:function(D,E,C){B.Assert._increment();if(!(E instanceof D)){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Value isn't an instance of expected type."),D,E);}},isNumber:function(D,C){B.Assert._increment();if(!B.Lang.isNumber(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Value should be a number."),D);}},isObject:function(D,C){B.Assert._increment();if(!B.Lang.isObject(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Value should be an object."),D);}},isString:function(D,C){B.Assert._increment();
+if(!B.Lang.isString(D)){throw new B.Assert.UnexpectedValue(B.Assert._formatMessage(C,"Value should be a string."),D);}},isTypeOf:function(C,E,D){B.Assert._increment();if(typeof E!=C){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(D,"Value should be of type "+C+"."),expected,typeof E);}}};B.assert=function(D,C){B.Assert._increment();if(!D){throw new B.Assert.Error(B.Assert._formatMessage(C,"Assertion failed."));}};B.fail=B.Assert.fail;B.Assert.Error=function(C){arguments.callee.superclass.constructor.call(this,C);this.message=C;this.name="Assert Error";};B.extend(B.Assert.Error,Error,{getMessage:function(){return this.message;},toString:function(){return this.name+": "+this.getMessage();},valueOf:function(){return this.toString();}});B.Assert.ComparisonFailure=function(D,C,E){arguments.callee.superclass.constructor.call(this,D);this.expected=C;this.actual=E;this.name="ComparisonFailure";};B.extend(B.Assert.ComparisonFailure,B.Assert.Error,{getMessage:function(){return this.message+"\nExpected: "+this.expected+" ("+(typeof this.expected)+")"+"\nActual: "+this.actual+" ("+(typeof this.actual)+")";}});B.Assert.UnexpectedValue=function(D,C){arguments.callee.superclass.constructor.call(this,D);this.unexpected=C;this.name="UnexpectedValue";};B.extend(B.Assert.UnexpectedValue,B.Assert.Error,{getMessage:function(){return this.message+"\nUnexpected: "+this.unexpected+" ("+(typeof this.unexpected)+") ";}});B.Assert.ShouldFail=function(C){arguments.callee.superclass.constructor.call(this,C||"This test should fail but didn't.");this.name="ShouldFail";};B.extend(B.Assert.ShouldFail,B.Assert.Error);B.Assert.ShouldError=function(C){arguments.callee.superclass.constructor.call(this,C||"This test should have thrown an error but didn't.");this.name="ShouldError";};B.extend(B.Assert.ShouldError,B.Assert.Error);B.Assert.UnexpectedError=function(C){arguments.callee.superclass.constructor.call(this,"Unexpected error: "+C.message);this.cause=C;this.name="UnexpectedError";this.stack=C.stack;};B.extend(B.Assert.UnexpectedError,B.Assert.Error);B.ArrayAssert={contains:function(E,D,C){B.Assert._increment();if(B.Array.indexOf(D,E)==-1){B.Assert.fail(B.Assert._formatMessage(C,"Value "+E+" ("+(typeof E)+") not found in array ["+D+"]."));}},containsItems:function(E,F,D){B.Assert._increment();for(var C=0;C<E.length;C++){if(B.Array.indexOf(F,E[C])==-1){B.Assert.fail(B.Assert._formatMessage(D,"Value "+E[C]+" ("+(typeof E[C])+") not found in array ["+F+"]."));}}},containsMatch:function(E,D,C){B.Assert._increment();if(typeof E!="function"){throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");}if(!B.Array.some(D,E)){B.Assert.fail(B.Assert._formatMessage(C,"No match found in array ["+D+"]."));}},doesNotContain:function(E,D,C){B.Assert._increment();if(B.Array.indexOf(D,E)>-1){B.Assert.fail(B.Assert._formatMessage(C,"Value found in array ["+D+"]."));}},doesNotContainItems:function(E,F,D){B.Assert._increment();for(var C=0;C<E.length;C++){if(B.Array.indexOf(F,E[C])>-1){B.Assert.fail(B.Assert._formatMessage(D,"Value found in array ["+F+"]."));}}},doesNotContainMatch:function(E,D,C){B.Assert._increment();if(typeof E!="function"){throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");}if(B.Array.some(D,E)){B.Assert.fail(B.Assert._formatMessage(C,"Value found in array ["+D+"]."));}},indexOf:function(G,F,C,E){B.Assert._increment();for(var D=0;D<F.length;D++){if(F[D]===G){if(C!=D){B.Assert.fail(B.Assert._formatMessage(E,"Value exists at index "+D+" but should be at index "+C+"."));}return;}}B.Assert.fail(B.Assert._formatMessage(E,"Value doesn't exist in array ["+F+"]."));},itemsAreEqual:function(E,F,D){B.Assert._increment();if(E.length!=F.length){B.Assert.fail(B.Assert._formatMessage(D,"Array should have a length of "+E.length+" but has a length of "+F.length));}for(var C=0;C<E.length;C++){if(E[C]!=F[C]){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(D,"Values in position "+C+" are not equal."),E[C],F[C]);}}},itemsAreEquivalent:function(F,G,C,E){B.Assert._increment();if(typeof C!="function"){throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");}if(F.length!=G.length){B.Assert.fail(B.Assert._formatMessage(E,"Array should have a length of "+F.length+" but has a length of "+G.length));}for(var D=0;D<F.length;D++){if(!C(F[D],G[D])){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(E,"Values in position "+D+" are not equivalent."),F[D],G[D]);}}},isEmpty:function(D,C){B.Assert._increment();if(D.length>0){B.Assert.fail(B.Assert._formatMessage(C,"Array should be empty."));}},isNotEmpty:function(D,C){B.Assert._increment();if(D.length===0){B.Assert.fail(B.Assert._formatMessage(C,"Array should not be empty."));}},itemsAreSame:function(E,F,D){B.Assert._increment();if(E.length!=F.length){B.Assert.fail(B.Assert._formatMessage(D,"Array should have a length of "+E.length+" but has a length of "+F.length));}for(var C=0;C<E.length;C++){if(E[C]!==F[C]){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(D,"Values in position "+C+" are not the same."),E[C],F[C]);}}},lastIndexOf:function(G,F,C,E){for(var D=F.length;D>=0;D--){if(F[D]===G){if(C!=D){B.Assert.fail(B.Assert._formatMessage(E,"Value exists at index "+D+" but should be at index "+C+"."));}return;}}B.Assert.fail(B.Assert._formatMessage(E,"Value doesn't exist in array."));}};B.ObjectAssert={areEqual:function(D,E,C){B.Assert._increment();B.Object.each(D,function(G,F){if(D[F]!=E[F]){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,"Values should be equal for property "+F),D[F],E[F]);}});},hasKey:function(C,D,E){B.Assert._increment();if(!B.Object.hasKey(D,C)){B.fail(B.Assert._formatMessage(E,"Property '"+C+"' not found on object."));}},hasKeys:function(E,C,F){B.Assert._increment();for(var D=0;D<E.length;D++){if(!B.Object.hasKey(C,E[D])){B.fail(B.Assert._formatMessage(F,"Property '"+E[D]+"' not found on object."));}}},ownsKey:function(C,D,E){B.Assert._increment();
+if(!D.hasOwnProperty(C)){B.fail(B.Assert._formatMessage(E,"Property '"+C+"' not found on object instance."));}},ownsKeys:function(E,C,F){B.Assert._increment();for(var D=0;D<E.length;D++){if(!C.hasOwnProperty(E[D])){B.fail(B.Assert._formatMessage(F,"Property '"+E[D]+"' not found on object instance."));}}},ownsNoKeys:function(C,E){B.Assert._increment();var D=B.Object.keys(C);if(D.length>0){B.fail(B.Assert._formatMessage(E,"Object owns "+D.length+" properties but should own none."));}}};B.DateAssert={datesAreEqual:function(D,F,C){B.Assert._increment();if(D instanceof Date&&F instanceof Date){var E="";if(D.getFullYear()!=F.getFullYear()){E="Years should be equal.";}if(D.getMonth()!=F.getMonth()){E="Months should be equal.";}if(D.getDate()!=F.getDate()){E="Days of month should be equal.";}if(E.length){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,E),D,F);}}else{throw new TypeError("Y.Assert.datesAreEqual(): Expected and actual values must be Date objects.");}},timesAreEqual:function(D,F,C){B.Assert._increment();if(D instanceof Date&&F instanceof Date){var E="";if(D.getHours()!=F.getHours()){E="Hours should be equal.";}if(D.getMinutes()!=F.getMinutes()){E="Minutes should be equal.";}if(D.getSeconds()!=F.getSeconds()){E="Seconds should be equal.";}if(E.length){throw new B.Assert.ComparisonFailure(B.Assert._formatMessage(C,E),D,F);}}else{throw new TypeError("DateY.AsserttimesAreEqual(): Expected and actual values must be Date objects.");}}};B.namespace("Test.Format");function A(C){return C.replace(/[<>"'&]/g,function(D){switch(D){case"<":return"<";case">":return">";case'"':return""";case"'":return"'";case"&":return"&";}});}B.Test.Format.JSON=function(C){return B.JSON.stringify(C);};B.Test.Format.XML=function(E){var C=B.Lang;var D="<"+E.type+' name="'+A(E.name)+'"';if(E.type=="test"){D+=' result="'+A(E.result)+'" message="'+A(E.message)+'">';}else{D+=' passed="'+E.passed+'" failed="'+E.failed+'" ignored="'+E.ignored+'" total="'+E.total+'">';B.Object.each(E,function(F,G){if(C.isObject(F)&&!C.isArray(F)){D+=arguments.callee(F);}});}D+="</"+E.type+">";return D;};B.Test.Format.XML=function(D){function C(G){var E=B.Lang,F="<"+G.type+' name="'+A(G.name)+'"';if(E.isNumber(G.duration)){F+=' duration="'+G.duration+'"';}if(G.type=="test"){F+=' result="'+G.result+'" message="'+A(G.message)+'">';}else{F+=' passed="'+G.passed+'" failed="'+G.failed+'" ignored="'+G.ignored+'" total="'+G.total+'">';B.Object.each(G,function(H,I){if(E.isObject(H)&&!E.isArray(H)){F+=C(H);}});}F+="</"+G.type+">";return F;}return'<?xml version="1.0" charset="UTF-8"?>'+C(D);};B.Test.Format.JUnitXML=function(C){function D(G){var E=B.Lang,F="",H;switch(G.type){case"test":if(G.result!="ignore"){F='<testcase name="'+A(G.name)+'">';if(G.result=="fail"){F+='<failure message="'+A(G.message)+'"><![CDATA['+G.message+"]]></failure>";}F+="</testcase>";}break;case"testcase":F='<testsuite name="'+A(G.name)+'" tests="'+G.total+'" failures="'+G.failed+'">';B.Object.each(G,function(I,J){if(E.isObject(I)&&!E.isArray(I)){F+=D(I);}});F+="</testsuite>";break;case"testsuite":B.Object.each(G,function(I,J){if(E.isObject(I)&&!E.isArray(I)){F+=D(I);}});break;case"report":F="<testsuites>";B.Object.each(G,function(I,J){if(E.isObject(I)&&!E.isArray(I)){F+=D(I);}});F+="</testsuites>";}return F;}return'<?xml version="1.0" charset="UTF-8"?>'+D(C);};B.namespace("Test");B.Test.Reporter=function(C,D){this.url=C;this.format=D||B.Test.Format.XML;this._fields=new Object();this._form=null;this._iframe=null;};B.Test.Reporter.prototype={constructor:B.Test.Reporter,addField:function(C,D){this._fields[C]=D;},clearFields:function(){this._fields=new Object();},destroy:function(){if(this._form){this._form.parentNode.removeChild(this._form);this._form=null;}if(this._iframe){this._iframe.parentNode.removeChild(this._iframe);this._iframe=null;}this._fields=null;},report:function(C){if(!this._form){this._form=document.createElement("form");this._form.method="post";this._form.style.visibility="hidden";this._form.style.position="absolute";this._form.style.top=0;document.body.appendChild(this._form);if(B.UA.ie){this._iframe=document.createElement('<iframe name="yuiTestTarget" />');}else{this._iframe=document.createElement("iframe");this._iframe.name="yuiTestTarget";}this._iframe.src="javascript:false";this._iframe.style.visibility="hidden";this._iframe.style.position="absolute";this._iframe.style.top=0;document.body.appendChild(this._iframe);this._form.target="yuiTestTarget";}this._form.action=this.url;while(this._form.hasChildNodes()){this._form.removeChild(this._form.lastChild);}this._fields.results=this.format(C);this._fields.useragent=navigator.userAgent;this._fields.timestamp=(new Date()).toLocaleString();B.Object.each(this._fields,function(E,F){if(typeof E!="function"){var D=document.createElement("input");D.type="hidden";D.name=F;D.value=E;this._form.appendChild(D);}},this);delete this._fields.results;delete this._fields.useragent;delete this._fields.timestamp;if(arguments[1]!==false){this._form.submit();}}};B.Mock=function(E){E=E||{};var C=null;try{C=B.Object(E);}catch(D){C={};B.log("Couldn't create mock with prototype.","warn","Mock");}B.Object.each(E,function(F){if(B.Lang.isFunction(E[F])){C[F]=function(){B.Assert.fail("Method "+F+"() was called but was not expected to be.");};}});return C;};B.Mock.expect=function(D,H){if(!D.__expectations){D.__expectations={};}if(H.method){var G=H.method,F=H.args||H.arguments||[],C=H.returns,J=B.Lang.isNumber(H.callCount)?H.callCount:1,E=H.error,I=H.run||function(){};D.__expectations[G]=H;H.callCount=J;H.actualCallCount=0;B.Array.each(F,function(K,L,M){if(!(M[L] instanceof B.Mock.Value)){M[L]=B.Mock.Value(B.Assert.areSame,[K],"Argument "+L+" of "+G+"() is incorrect.");}});if(J>0){D[G]=function(){try{H.actualCallCount++;B.Assert.areEqual(F.length,arguments.length,"Method "+G+"() passed incorrect number of arguments.");for(var M=0,K=F.length;M<K;M++){F[M].verify(arguments[M]);}I.apply(this,arguments);if(E){throw E;
+}}catch(L){B.Test.Runner._handleError(L);}return C;};}else{D[G]=function(){try{B.Assert.fail("Method "+G+"() should not have been called.");}catch(K){B.Test.Runner._handleError(K);}};}}else{if(H.property){D.__expectations[G]=H;}}};B.Mock.verify=function(C){try{B.Object.each(C.__expectations,function(E){if(E.method){B.Assert.areEqual(E.callCount,E.actualCallCount,"Method "+E.method+"() wasn't called the expected number of times.");}else{if(E.property){B.Assert.areEqual(E.value,C[E.property],"Property "+E.property+" wasn't set to the correct value.");}}});}catch(D){B.Test.Runner._handleError(D);}};B.Mock.Value=function(E,C,D){if(this instanceof B.Mock.Value){this.verify=function(G){var F=[].concat(C||[]);F.push(G);F.push(D);E.apply(null,F);};}else{return new B.Mock.Value(E,C,D);}};B.Mock.Value.Any=B.Mock.Value(function(){});B.Mock.Value.Boolean=B.Mock.Value(B.Assert.isBoolean);B.Mock.Value.Number=B.Mock.Value(B.Assert.isNumber);B.Mock.Value.String=B.Mock.Value(B.Assert.isString);B.Mock.Value.Object=B.Mock.Value(B.Assert.isObject);B.Mock.Value.Function=B.Mock.Value(B.Assert.isFunction);},"3.0.0",{requires:["substitute","event-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('test', function(Y) {
+
+ /**
+ * YUI JavaScript Testing Framework
+ *
+ * @module test
+ */
+
+
+ Y.namespace("Test");
+
+ /**
+ * Test case containing various tests to run.
+ * @param template An object containing any number of test methods, other methods,
+ * an optional name, and anything else the test case needs.
+ * @class Case
+ * @namespace Test
+ * @constructor
+ */
+ Y.Test.Case = function (template) {
+
+ /**
+ * Special rules for the test case. Possible subobjects
+ * are fail, for tests that should fail, and error, for
+ * tests that should throw an error.
+ */
+ this._should = {};
+
+ //copy over all properties from the template to this object
+ for (var prop in template) {
+ this[prop] = template[prop];
+ }
+
+ //check for a valid name
+ if (!Y.Lang.isString(this.name)){
+ /**
+ * Name for the test case.
+ */
+ this.name = "testCase" + Y.guid();
+ }
+
+ };
+
+ Y.Test.Case.prototype = {
+
+ /**
+ * Resumes a paused test and runs the given function.
+ * @param {Function} segment (Optional) The function to run.
+ * If omitted, the test automatically passes.
+ * @return {Void}
+ * @method resume
+ */
+ resume : function (segment) {
+ Y.Test.Runner.resume(segment);
+ },
+
+ /**
+ * Causes the test case to wait a specified amount of time and then
+ * continue executing the given code.
+ * @param {Function} segment (Optional) The function to run after the delay.
+ * If omitted, the TestRunner will wait until resume() is called.
+ * @param {int} delay (Optional) The number of milliseconds to wait before running
+ * the function. If omitted, defaults to zero.
+ * @return {Void}
+ * @method wait
+ */
+ wait : function (segment, delay){
+ var args = arguments;
+ if (Y.Lang.isFunction(args[0])){
+ throw new Y.Test.Wait(args[0], args[1]);
+ } else {
+ throw new Y.Test.Wait(function(){
+ Y.Assert.fail("Timeout: wait() called but resume() never called.");
+ }, (Y.Lang.isNumber(args[0]) ? args[0] : 10000));
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Stub Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Function to run before each test is executed.
+ * @return {Void}
+ * @method setUp
+ */
+ setUp : function () {
+ },
+
+ /**
+ * Function to run after each test is executed.
+ * @return {Void}
+ * @method tearDown
+ */
+ tearDown: function () {
+ }
+ };
+
+ /**
+ * Represents a stoppage in test execution to wait for an amount of time before
+ * continuing.
+ * @param {Function} segment A function to run when the wait is over.
+ * @param {int} delay The number of milliseconds to wait before running the code.
+ * @class Wait
+ * @namespace Test
+ * @constructor
+ *
+ */
+ Y.Test.Wait = function (segment, delay) {
+
+ /**
+ * The segment of code to run when the wait is over.
+ * @type Function
+ * @property segment
+ */
+ this.segment = (Y.Lang.isFunction(segment) ? segment : null);
+
+ /**
+ * The delay before running the segment of code.
+ * @type int
+ * @property delay
+ */
+ this.delay = (Y.Lang.isNumber(delay) ? delay : 0);
+ };
+
+
+
+ Y.namespace("Test");
+
+ /**
+ * A test suite that can contain a collection of TestCase and TestSuite objects.
+ * @param {String||Object} data The name of the test suite or an object containing
+ * a name property as well as setUp and tearDown methods.
+ * @namespace Test
+ * @class Suite
+ * @constructor
+ */
+ Y.Test.Suite = function (data /*:String||Object*/) {
+
+ /**
+ * The name of the test suite.
+ * @type String
+ * @property name
+ */
+ this.name = "";
+
+ /**
+ * Array of test suites and
+ * @private
+ */
+ this.items = [];
+
+ //initialize the properties
+ if (Y.Lang.isString(data)){
+ this.name = data;
+ } else if (Y.Lang.isObject(data)){
+ Y.mix(this, data, true);
+ }
+
+ //double-check name
+ if (this.name === ""){
+ this.name = "testSuite" + Y.guid();
+ }
+
+ };
+
+ Y.Test.Suite.prototype = {
+
+ /**
+ * Adds a test suite or test case to the test suite.
+ * @param {Y.Test.Suite||Y.Test.Case} testObject The test suite or test case to add.
+ * @return {Void}
+ * @method add
+ */
+ add : function (testObject /*:Y.Test.Suite*/) {
+ if (testObject instanceof Y.Test.Suite || testObject instanceof Y.Test.Case) {
+ this.items.push(testObject);
+ }
+ return this;
+ },
+
+ //-------------------------------------------------------------------------
+ // Stub Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Function to run before each test is executed.
+ * @return {Void}
+ * @method setUp
+ */
+ setUp : function () {
+ },
+
+ /**
+ * Function to run after each test is executed.
+ * @return {Void}
+ * @method tearDown
+ */
+ tearDown: function () {
+ }
+
+ };
+
+
+ /*
+ * Runs test suites and test cases, providing events to allowing for the
+ * interpretation of test results.
+ * @namespace Test
+ * @class Runner
+ * @static
+ */
+ Y.Test.Runner = (function(){
+
+ /**
+ * A node in the test tree structure. May represent a TestSuite, TestCase, or
+ * test function.
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
+ * @class TestNode
+ * @constructor
+ * @private
+ */
+ function TestNode(testObject){
+
+ /**
+ * The TestSuite, TestCase, or test function represented by this node.
+ * @type Variant
+ * @property testObject
+ */
+ this.testObject = testObject;
+
+ /**
+ * Pointer to this node's first child.
+ * @type TestNode
+ * @property firstChild
+ */
+ this.firstChild = null;
+
+ /**
+ * Pointer to this node's last child.
+ * @type TestNode
+ * @property lastChild
+ */
+ this.lastChild = null;
+
+ /**
+ * Pointer to this node's parent.
+ * @type TestNode
+ * @property parent
+ */
+ this.parent = null;
+
+ /**
+ * Pointer to this node's next sibling.
+ * @type TestNode
+ * @property next
+ */
+ this.next = null;
+
+ /**
+ * Test results for this test object.
+ * @type object
+ * @property results
+ */
+ this.results = {
+ passed : 0,
+ failed : 0,
+ total : 0,
+ ignored : 0
+ };
+
+ //initialize results
+ if (testObject instanceof Y.Test.Suite){
+ this.results.type = "testsuite";
+ this.results.name = testObject.name;
+ } else if (testObject instanceof Y.Test.Case){
+ this.results.type = "testcase";
+ this.results.name = testObject.name;
+ }
+
+ }
+
+ TestNode.prototype = {
+
+ /**
+ * Appends a new test object (TestSuite, TestCase, or test function name) as a child
+ * of this node.
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
+ * @return {Void}
+ */
+ appendChild : function (testObject){
+ var node = new TestNode(testObject);
+ if (this.firstChild === null){
+ this.firstChild = this.lastChild = node;
+ } else {
+ this.lastChild.next = node;
+ this.lastChild = node;
+ }
+ node.parent = this;
+ return node;
+ }
+ };
+
+ /**
+ * Runs test suites and test cases, providing events to allowing for the
+ * interpretation of test results.
+ * @namespace Test
+ * @class Runner
+ * @static
+ */
+ function TestRunner(){
+
+ //inherit from EventProvider
+ TestRunner.superclass.constructor.apply(this,arguments);
+
+ /**
+ * Suite on which to attach all TestSuites and TestCases to be run.
+ * @type Y.Test.Suite
+ * @property masterSuite
+ * @static
+ * @private
+ */
+ this.masterSuite /*:Y.Test.Suite*/ = new Y.Test.Suite("YUI Test Results");
+
+ /**
+ * Pointer to the current node in the test tree.
+ * @type TestNode
+ * @private
+ * @property _cur
+ * @static
+ */
+ this._cur = null;
+
+ /**
+ * Pointer to the root node in the test tree.
+ * @type TestNode
+ * @private
+ * @property _root
+ * @static
+ */
+ this._root = null;
+
+ /**
+ * Indicates if the TestRunner will log events or not.
+ * @type Boolean
+ * @property _log
+ * @private
+ * @static
+ */
+ this._log = true;
+
+ /**
+ * Indicates if the TestRunner is waiting as a result of
+ * wait() being called.
+ * @type Boolean
+ * @property _waiting
+ * @private
+ * @static
+ */
+ this._waiting = false;
+
+ //create events
+ var events = [
+ this.TEST_CASE_BEGIN_EVENT,
+ this.TEST_CASE_COMPLETE_EVENT,
+ this.TEST_SUITE_BEGIN_EVENT,
+ this.TEST_SUITE_COMPLETE_EVENT,
+ this.TEST_PASS_EVENT,
+ this.TEST_FAIL_EVENT,
+ this.TEST_IGNORE_EVENT,
+ this.COMPLETE_EVENT,
+ this.BEGIN_EVENT
+ ];
+ for (var i=0; i < events.length; i++){
+ this.subscribe(events[i], this._logEvent, this, true);
+ }
+
+ }
+
+ Y.extend(TestRunner, Y.Event.Target, {
+
+ //-------------------------------------------------------------------------
+ // Constants
+ //-------------------------------------------------------------------------
+
+ /**
+ * Fires when a test case is opened but before the first
+ * test is executed.
+ * @event testcasebegin
+ * @static
+ */
+ TEST_CASE_BEGIN_EVENT : "testcasebegin",
+
+ /**
+ * Fires when all tests in a test case have been executed.
+ * @event testcasecomplete
+ * @static
+ */
+ TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
+
+ /**
+ * Fires when a test suite is opened but before the first
+ * test is executed.
+ * @event testsuitebegin
+ * @static
+ */
+ TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
+
+ /**
+ * Fires when all test cases in a test suite have been
+ * completed.
+ * @event testsuitecomplete
+ * @static
+ */
+ TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
+
+ /**
+ * Fires when a test has passed.
+ * @event pass
+ * @static
+ */
+ TEST_PASS_EVENT : "pass",
+
+ /**
+ * Fires when a test has failed.
+ * @event fail
+ * @static
+ */
+ TEST_FAIL_EVENT : "fail",
+
+ /**
+ * Fires when a test has been ignored.
+ * @event ignore
+ * @static
+ */
+ TEST_IGNORE_EVENT : "ignore",
+
+ /**
+ * Fires when all test suites and test cases have been completed.
+ * @event complete
+ * @static
+ */
+ COMPLETE_EVENT : "complete",
+
+ /**
+ * Fires when the run() method is called.
+ * @event begin
+ * @static
+ */
+ BEGIN_EVENT : "begin",
+
+ //-------------------------------------------------------------------------
+ // Logging-Related Methods
+ //-------------------------------------------------------------------------
+
+
+ /**
+ * Disable logging via Y.log(). Test output will not be visible unless
+ * TestRunner events are subscribed to.
+ * @return {Void}
+ * @method disableLogging
+ * @static
+ */
+ disableLogging: function(){
+ this._log = false;
+ },
+
+ /**
+ * Enable logging via Y.log(). Test output is published and can be read via
+ * logreader.
+ * @return {Void}
+ * @method enableLogging
+ * @static
+ */
+ enableLogging: function(){
+ this._log = true;
+ },
+
+ /**
+ * Logs TestRunner events using Y.log().
+ * @param {Object} event The event object for the event.
+ * @return {Void}
+ * @method _logEvent
+ * @private
+ * @static
+ */
+ _logEvent: function(event){
+
+ //data variables
+ var message = "";
+ var messageType = "";
+
+ switch(event.type){
+ case this.BEGIN_EVENT:
+ message = "Testing began at " + (new Date()).toString() + ".";
+ messageType = "info";
+ break;
+
+ case this.COMPLETE_EVENT:
+ message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" +
+ event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
+ messageType = "info";
+ break;
+
+ case this.TEST_FAIL_EVENT:
+ message = event.testName + ": failed.\n" + event.error.getMessage();
+ messageType = "fail";
+ break;
+
+ case this.TEST_IGNORE_EVENT:
+ message = event.testName + ": ignored.";
+ messageType = "ignore";
+ break;
+
+ case this.TEST_PASS_EVENT:
+ message = event.testName + ": passed.";
+ messageType = "pass";
+ break;
+
+ case this.TEST_SUITE_BEGIN_EVENT:
+ message = "Test suite \"" + event.testSuite.name + "\" started.";
+ messageType = "info";
+ break;
+
+ case this.TEST_SUITE_COMPLETE_EVENT:
+ message = "Test suite \"" + event.testSuite.name + "\" completed.\nPassed:" +
+ event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
+ messageType = "info";
+ break;
+
+ case this.TEST_CASE_BEGIN_EVENT:
+ message = "Test case \"" + event.testCase.name + "\" started.";
+ messageType = "info";
+ break;
+
+ case this.TEST_CASE_COMPLETE_EVENT:
+ message = "Test case \"" + event.testCase.name + "\" completed.\nPassed:" +
+ event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
+ messageType = "info";
+ break;
+ default:
+ message = "Unexpected event " + event.type;
+ message = "info";
+ }
+
+ //only log if required
+ if (this._log){
+ Y.log(message, messageType, "TestRunner");
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Test Tree-Related Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Adds a test case to the test tree as a child of the specified node.
+ * @param {TestNode} parentNode The node to add the test case to as a child.
+ * @param {Y.Test.Case} testCase The test case to add.
+ * @return {Void}
+ * @static
+ * @private
+ * @method _addTestCaseToTestTree
+ */
+ _addTestCaseToTestTree : function (parentNode, testCase /*:Y.Test.Case*/){
+
+ //add the test suite
+ var node = parentNode.appendChild(testCase),
+ prop,
+ testName;
+
+ //iterate over the items in the test case
+ for (prop in testCase){
+ if ((prop.indexOf("test") === 0 || (prop.toLowerCase().indexOf("should") > -1 && prop.indexOf(" ") > -1 ))&& Y.Lang.isFunction(testCase[prop])){
+ node.appendChild(prop);
+ }
+ }
+
+ },
+
+ /**
+ * Adds a test suite to the test tree as a child of the specified node.
+ * @param {TestNode} parentNode The node to add the test suite to as a child.
+ * @param {Y.Test.Suite} testSuite The test suite to add.
+ * @return {Void}
+ * @static
+ * @private
+ * @method _addTestSuiteToTestTree
+ */
+ _addTestSuiteToTestTree : function (parentNode, testSuite /*:Y.Test.Suite*/) {
+
+ //add the test suite
+ var node = parentNode.appendChild(testSuite);
+
+ //iterate over the items in the master suite
+ for (var i=0; i < testSuite.items.length; i++){
+ if (testSuite.items[i] instanceof Y.Test.Suite) {
+ this._addTestSuiteToTestTree(node, testSuite.items[i]);
+ } else if (testSuite.items[i] instanceof Y.Test.Case) {
+ this._addTestCaseToTestTree(node, testSuite.items[i]);
+ }
+ }
+ },
+
+ /**
+ * Builds the test tree based on items in the master suite. The tree is a hierarchical
+ * representation of the test suites, test cases, and test functions. The resulting tree
+ * is stored in _root and the pointer _cur is set to the root initially.
+ * @return {Void}
+ * @static
+ * @private
+ * @method _buildTestTree
+ */
+ _buildTestTree : function () {
+
+ this._root = new TestNode(this.masterSuite);
+ this._cur = this._root;
+
+ //iterate over the items in the master suite
+ for (var i=0; i < this.masterSuite.items.length; i++){
+ if (this.masterSuite.items[i] instanceof Y.Test.Suite) {
+ this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
+ } else if (this.masterSuite.items[i] instanceof Y.Test.Case) {
+ this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
+ }
+ }
+
+ },
+
+ //-------------------------------------------------------------------------
+ // Private Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Handles the completion of a test object's tests. Tallies test results
+ * from one level up to the next.
+ * @param {TestNode} node The TestNode representing the test object.
+ * @return {Void}
+ * @method _handleTestObjectComplete
+ * @private
+ */
+ _handleTestObjectComplete : function (node) {
+ if (Y.Lang.isObject(node.testObject)){
+ node.parent.results.passed += node.results.passed;
+ node.parent.results.failed += node.results.failed;
+ node.parent.results.total += node.results.total;
+ node.parent.results.ignored += node.results.ignored;
+ node.parent.results[node.testObject.name] = node.results;
+
+ if (node.testObject instanceof Y.Test.Suite){
+ node.testObject.tearDown();
+ this.fire(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
+ } else if (node.testObject instanceof Y.Test.Case){
+ this.fire(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
+ }
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Navigation Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Retrieves the next node in the test tree.
+ * @return {TestNode} The next node in the test tree or null if the end is reached.
+ * @private
+ * @static
+ * @method _next
+ */
+ _next : function () {
+
+ if (this._cur.firstChild) {
+ this._cur = this._cur.firstChild;
+ } else if (this._cur.next) {
+ this._cur = this._cur.next;
+ } else {
+ while (this._cur && !this._cur.next && this._cur !== this._root){
+ this._handleTestObjectComplete(this._cur);
+ this._cur = this._cur.parent;
+ }
+
+ if (this._cur == this._root){
+ this._cur.results.type = "report";
+ this._cur.results.timestamp = (new Date()).toLocaleString();
+ this._cur.results.duration = (new Date()) - this._cur.results.duration;
+ this.fire(this.COMPLETE_EVENT, { results: this._cur.results});
+ this._cur = null;
+ } else {
+ this._handleTestObjectComplete(this._cur);
+ this._cur = this._cur.next;
+ }
+ }
+
+ return this._cur;
+ },
+
+ /**
+ * Runs a test case or test suite, returning the results.
+ * @param {Y.Test.Case|Y.Test.Suite} testObject The test case or test suite to run.
+ * @return {Object} Results of the execution with properties passed, failed, and total.
+ * @private
+ * @method _run
+ * @static
+ */
+ _run : function () {
+
+ //flag to indicate if the TestRunner should wait before continuing
+ var shouldWait = false;
+
+ //get the next test node
+ var node = this._next();
+
+ if (node !== null) {
+ var testObject = node.testObject;
+
+ //figure out what to do
+ if (Y.Lang.isObject(testObject)){
+ if (testObject instanceof Y.Test.Suite){
+ this.fire(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
+ testObject.setUp();
+ } else if (testObject instanceof Y.Test.Case){
+ this.fire(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
+ }
+
+ //some environments don't support setTimeout
+ if (typeof setTimeout != "undefined"){
+ setTimeout(function(){
+ Y.Test.Runner._run();
+ }, 0);
+ } else {
+ this._run();
+ }
+ } else {
+ this._runTest(node);
+ }
+
+ }
+ },
+
+ _resumeTest : function (segment) {
+
+ //get relevant information
+ var node = this._cur;
+
+ //we know there's no more waiting now
+ this._waiting = false;
+
+ //if there's no node, it probably means a wait() was called after resume()
+ if (!node){
+ //TODO: Handle in some way?
+ //console.log("wait() called after resume()");
+ //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
+ return;
+ }
+
+ var testName = node.testObject;
+ var testCase /*:Y.Test.Case*/ = node.parent.testObject;
+
+ //cancel other waits if available
+ if (testCase.__yui_wait){
+ clearTimeout(testCase.__yui_wait);
+ delete testCase.__yui_wait;
+ }
+
+ //get the "should" test cases
+ var shouldFail = (testCase._should.fail || {})[testName];
+ var shouldError = (testCase._should.error || {})[testName];
+
+ //variable to hold whether or not the test failed
+ var failed = false;
+ var error = null;
+
+ //try the test
+ try {
+
+ //run the test
+ segment.apply(testCase);
+
+ //if it should fail, and it got here, then it's a fail because it didn't
+ if (shouldFail){
+ error = new Y.Assert.ShouldFail();
+ failed = true;
+ } else if (shouldError){
+ error = new Y.Assert.ShouldError();
+ failed = true;
+ }
+
+ } catch (thrown){
+
+ //cancel any pending waits, the test already failed
+ if (testCase.__yui_wait){
+ clearTimeout(testCase.__yui_wait);
+ delete testCase.__yui_wait;
+ }
+
+ //figure out what type of error it was
+ if (thrown instanceof Y.Assert.Error) {
+ if (!shouldFail){
+ error = thrown;
+ failed = true;
+ }
+ } else if (thrown instanceof Y.Test.Wait){
+
+ if (Y.Lang.isFunction(thrown.segment)){
+ if (Y.Lang.isNumber(thrown.delay)){
+
+ //some environments don't support setTimeout
+ if (typeof setTimeout != "undefined"){
+ testCase.__yui_wait = setTimeout(function(){
+ Y.Test.Runner._resumeTest(thrown.segment);
+ }, thrown.delay);
+ this._waiting = true;
+ } else {
+ throw new Error("Asynchronous tests not supported in this environment.");
+ }
+ }
+ }
+
+ return;
+
+ } else {
+ //first check to see if it should error
+ if (!shouldError) {
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ } else {
+ //check to see what type of data we have
+ if (Y.Lang.isString(shouldError)){
+
+ //if it's a string, check the error message
+ if (thrown.message != shouldError){
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ }
+ } else if (Y.Lang.isFunction(shouldError)){
+
+ //if it's a function, see if the error is an instance of it
+ if (!(thrown instanceof shouldError)){
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ }
+
+ } else if (Y.Lang.isObject(shouldError)){
+
+ //if it's an object, check the instance and message
+ if (!(thrown instanceof shouldError.constructor) ||
+ thrown.message != shouldError.message){
+ error = new Y.Assert.UnexpectedError(thrown);
+ failed = true;
+ }
+
+ }
+
+ }
+ }
+
+ }
+
+ //fire appropriate event
+ if (failed) {
+ this.fire(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
+ } else {
+ this.fire(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
+ }
+
+ //run the tear down
+ testCase.tearDown();
+
+ //update results
+ node.parent.results[testName] = {
+ result: failed ? "fail" : "pass",
+ message: error ? error.getMessage() : "Test passed",
+ type: "test",
+ name: testName
+ };
+
+ if (failed){
+ node.parent.results.failed++;
+ } else {
+ node.parent.results.passed++;
+ }
+ node.parent.results.total++;
+
+ //set timeout not supported in all environments
+ if (typeof setTimeout != "undefined"){
+ setTimeout(function(){
+ Y.Test.Runner._run();
+ }, 0);
+ } else {
+ this._run();
+ }
+
+ },
+
+ /**
+ * Handles an error as if it occurred within the currently executing
+ * test. This is for mock methods that may be called asynchronously
+ * and therefore out of the scope of the TestRunner. Previously, this
+ * error would bubble up to the browser. Now, this method is used
+ * to tell TestRunner about the error. This should never be called
+ * by anyplace other than the Mock object.
+ * @param {Error} error The error object.
+ * @return {Void}
+ * @method _handleError
+ * @private
+ * @static
+ */
+ _handleError: function(error){
+
+ if (this._waiting){
+ this._resumeTest(function(){
+ throw error;
+ });
+ } else {
+ throw error;
+ }
+
+ },
+
+ /**
+ * Runs a single test based on the data provided in the node.
+ * @param {TestNode} node The TestNode representing the test to run.
+ * @return {Void}
+ * @static
+ * @private
+ * @name _runTest
+ */
+ _runTest : function (node) {
+
+ //get relevant information
+ var testName = node.testObject;
+ var testCase /*:Y.Test.Case*/ = node.parent.testObject;
+ var test = testCase[testName];
+
+ //get the "should" test cases
+ var shouldIgnore = (testCase._should.ignore || {})[testName];
+
+ //figure out if the test should be ignored or not
+ if (shouldIgnore){
+
+ //update results
+ node.parent.results[testName] = {
+ result: "ignore",
+ message: "Test ignored",
+ type: "test",
+ name: testName
+ };
+
+ node.parent.results.ignored++;
+ node.parent.results.total++;
+
+ this.fire(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
+
+ //some environments don't support setTimeout
+ if (typeof setTimeout != "undefined"){
+ setTimeout(function(){
+ Y.Test.Runner._run();
+ }, 0);
+ } else {
+ this._run();
+ }
+
+ } else {
+
+ //run the setup
+ testCase.setUp();
+
+ //now call the body of the test
+ this._resumeTest(test);
+ }
+
+ },
+
+ //-------------------------------------------------------------------------
+ // Protected Methods
+ //-------------------------------------------------------------------------
+
+ /*
+ * Fires events for the TestRunner. This overrides the default fire()
+ * method from EventProvider to add the type property to the data that is
+ * passed through on each event call.
+ * @param {String} type The type of event to fire.
+ * @param {Object} data (Optional) Data for the event.
+ * @method fire
+ * @static
+ * @protected
+ */
+ fire : function (type, data) {
+ data = data || {};
+ data.type = type;
+ TestRunner.superclass.fire.call(this, type, data);
+ },
+
+ //-------------------------------------------------------------------------
+ // Public Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Adds a test suite or test case to the list of test objects to run.
+ * @param testObject Either a TestCase or a TestSuite that should be run.
+ * @return {Void}
+ * @method add
+ * @static
+ */
+ add : function (testObject) {
+ this.masterSuite.add(testObject);
+ return this;
+ },
+
+ /**
+ * Removes all test objects from the runner.
+ * @return {Void}
+ * @method clear
+ * @static
+ */
+ clear : function () {
+ this.masterSuite.items = [];
+ },
+
+ /**
+ * Indicates if the TestRunner is waiting for a test to resume
+ * @return {Boolean} True if the TestRunner is waiting, false if not.
+ * @method isWaiting
+ * @static
+ */
+ isWaiting: function() {
+ return this._waiting;
+ },
+
+ /**
+ * Resumes the TestRunner after wait() was called.
+ * @param {Function} segment The function to run as the rest
+ * of the haulted test.
+ * @return {Void}
+ * @method resume
+ * @static
+ */
+ resume : function (segment) {
+ this._resumeTest(segment || function(){});
+ },
+
+ /**
+ * Runs the test suite.
+ * @return {Void}
+ * @method run
+ * @static
+ */
+ run : function (testObject) {
+
+ //pointer to runner to avoid scope issues
+ var runner = Y.Test.Runner;
+
+ //build the test tree
+ runner._buildTestTree();
+
+ //set when the test started
+ runner._root.results.duration = (new Date()).valueOf();
+
+ //fire the begin event
+ runner.fire(runner.BEGIN_EVENT);
+
+ //begin the testing
+ runner._run();
+ }
+ });
+
+ return new TestRunner();
+
+ })();
+
+
+ /**
+ * The Assert object provides functions to test JavaScript values against
+ * known and expected results. Whenever a comparison (assertion) fails,
+ * an error is thrown.
+ *
+ * @class Assert
+ * @static
+ */
+ Y.Assert = {
+
+ /**
+ * The number of assertions performed.
+ * @property _asserts
+ * @type int
+ * @private
+ */
+ _asserts: 0,
+
+ //-------------------------------------------------------------------------
+ // Helper Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Formats a message so that it can contain the original assertion message
+ * in addition to the custom message.
+ * @param {String} customMessage The message passed in by the developer.
+ * @param {String} defaultMessage The message created by the error by default.
+ * @return {String} The final error message, containing either or both.
+ * @protected
+ * @static
+ * @method _formatMessage
+ */
+ _formatMessage : function (customMessage, defaultMessage) {
+ var message = customMessage;
+ if (Y.Lang.isString(customMessage) && customMessage.length > 0){
+ return Y.Lang.substitute(customMessage, { message: defaultMessage });
+ } else {
+ return defaultMessage;
+ }
+ },
+
+ /**
+ * Returns the number of assertions that have been performed.
+ * @method _getCount
+ * @protected
+ * @static
+ */
+ _getCount: function(){
+ return this._asserts;
+ },
+
+ /**
+ * Increments the number of assertions that have been performed.
+ * @method _increment
+ * @protected
+ * @static
+ */
+ _increment: function(){
+ this._asserts++;
+ },
+
+ /**
+ * Resets the number of assertions that have been performed to 0.
+ * @method _reset
+ * @protected
+ * @static
+ */
+ _reset: function(){
+ this._asserts = 0;
+ },
+
+ //-------------------------------------------------------------------------
+ // Generic Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Forces an assertion error to occur.
+ * @param {String} message (Optional) The message to display with the failure.
+ * @method fail
+ * @static
+ */
+ fail : function (message) {
+ throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Test force-failed."));
+ },
+
+ //-------------------------------------------------------------------------
+ // Equality Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is equal to another. This uses the double equals sign
+ * so type cohersion may occur.
+ * @param {Object} expected The expected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areEqual
+ * @static
+ */
+ areEqual : function (expected, actual, message) {
+ Y.Assert._increment();
+ if (expected != actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be equal."), expected, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is not equal to another. This uses the double equals sign
+ * so type cohersion may occur.
+ * @param {Object} unexpected The unexpected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areNotEqual
+ * @static
+ */
+ areNotEqual : function (unexpected, actual,
+ message) {
+ Y.Assert._increment();
+ if (unexpected == actual) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be equal."), unexpected);
+ }
+ },
+
+ /**
+ * Asserts that a value is not the same as another. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} unexpected The unexpected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areNotSame
+ * @static
+ */
+ areNotSame : function (unexpected, actual, message) {
+ Y.Assert._increment();
+ if (unexpected === actual) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be the same."), unexpected);
+ }
+ },
+
+ /**
+ * Asserts that a value is the same as another. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} expected The expected value.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method areSame
+ * @static
+ */
+ areSame : function (expected, actual, message) {
+ Y.Assert._increment();
+ if (expected !== actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be the same."), expected, actual);
+ }
+ },
+
+ //-------------------------------------------------------------------------
+ // Boolean Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is false. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isFalse
+ * @static
+ */
+ isFalse : function (actual, message) {
+ Y.Assert._increment();
+ if (false !== actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be false."), false, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is true. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isTrue
+ * @static
+ */
+ isTrue : function (actual, message) {
+ Y.Assert._increment();
+ if (true !== actual) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be true."), true, actual);
+ }
+
+ },
+
+ //-------------------------------------------------------------------------
+ // Special Value Assertion Methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is not a number.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNaN
+ * @static
+ */
+ isNaN : function (actual, message){
+ Y.Assert._increment();
+ if (!isNaN(actual)){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is not the special NaN value.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotNaN
+ * @static
+ */
+ isNotNaN : function (actual, message){
+ Y.Assert._increment();
+ if (isNaN(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be NaN."), NaN);
+ }
+ },
+
+ /**
+ * Asserts that a value is not null. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotNull
+ * @static
+ */
+ isNotNull : function (actual, message) {
+ Y.Assert._increment();
+ if (Y.Lang.isNull(actual)) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be null."), null);
+ }
+ },
+
+ /**
+ * Asserts that a value is not undefined. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotUndefined
+ * @static
+ */
+ isNotUndefined : function (actual, message) {
+ Y.Assert._increment();
+ if (Y.Lang.isUndefined(actual)) {
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should not be undefined."), undefined);
+ }
+ },
+
+ /**
+ * Asserts that a value is null. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNull
+ * @static
+ */
+ isNull : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isNull(actual)) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be null."), null, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is undefined. This uses the triple equals sign
+ * so no type cohersion may occur.
+ * @param {Object} actual The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isUndefined
+ * @static
+ */
+ isUndefined : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isUndefined(actual)) {
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Instance Assertion Methods
+ //--------------------------------------------------------------------------
+
+ /**
+ * Asserts that a value is an array.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isArray
+ * @static
+ */
+ isArray : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isArray(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an array."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a Boolean.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isBoolean
+ * @static
+ */
+ isBoolean : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isBoolean(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a Boolean."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a function.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isFunction
+ * @static
+ */
+ isFunction : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isFunction(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a function."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is an instance of a particular object. This may return
+ * incorrect results when comparing objects from one frame to constructors in
+ * another frame. For best results, don't use in a cross-frame manner.
+ * @param {Function} expected The function that the object should be an instance of.
+ * @param {Object} actual The object to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isInstanceOf
+ * @static
+ */
+ isInstanceOf : function (expected, actual, message) {
+ Y.Assert._increment();
+ if (!(actual instanceof expected)){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a number.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNumber
+ * @static
+ */
+ isNumber : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isNumber(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a number."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is an object.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isObject
+ * @static
+ */
+ isObject : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isObject(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an object."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is a string.
+ * @param {Object} actual The value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isString
+ * @static
+ */
+ isString : function (actual, message) {
+ Y.Assert._increment();
+ if (!Y.Lang.isString(actual)){
+ throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a string."), actual);
+ }
+ },
+
+ /**
+ * Asserts that a value is of a particular type.
+ * @param {String} expectedType The expected type of the variable.
+ * @param {Object} actualValue The actual value to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isTypeOf
+ * @static
+ */
+ isTypeOf : function (expectedType, actualValue, message){
+ Y.Assert._increment();
+ if (typeof actualValue != expectedType){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expected, typeof actualValue);
+ }
+ }
+ };
+
+ /**
+ * Asserts that a given condition is true. If not, then a Y.Assert.Error object is thrown
+ * and the test fails.
+ * @method Y.assert
+ * @param {Boolean} condition The condition to test.
+ * @param {String} message The message to display if the assertion fails.
+ * @static
+ */
+ Y.assert = function(condition, message){
+ Y.Assert._increment();
+ if (!condition){
+ throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Assertion failed."));
+ }
+ };
+
+ /**
+ * Forces an assertion error to occur. Shortcut for Y.Assert.fail().
+ * @method Y.fail
+ * @param {String} message (Optional) The message to display with the failure.
+ * @static
+ */
+ Y.fail = Y.Assert.fail;
+
+ //-----------------------------------------------------------------------------
+ // Assertion errors
+ //-----------------------------------------------------------------------------
+
+ /**
+ * Error is thrown whenever an assertion fails. It provides methods
+ * to more easily get at error information and also provides a base class
+ * from which more specific assertion errors can be derived.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @namespace Assert
+ * @class Error
+ * @constructor
+ */
+ Y.Assert.Error = function (message){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message);
+
+ /*
+ * Error message. Must be duplicated to ensure browser receives it.
+ * @type String
+ * @property message
+ */
+ this.message = message;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "Assert Error";
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.Error, Error, {
+
+ /**
+ * Returns a fully formatted error for an assertion failure. This should
+ * be overridden by all subclasses to provide specific information.
+ * @method getMessage
+ * @return {String} A string describing the error.
+ */
+ getMessage : function () {
+ return this.message;
+ },
+
+ /**
+ * Returns a string representation of the error.
+ * @method toString
+ * @return {String} A string representation of the error.
+ */
+ toString : function () {
+ return this.name + ": " + this.getMessage();
+ },
+
+ /**
+ * Returns a primitive value version of the error. Same as toString().
+ * @method valueOf
+ * @return {String} A primitive value version of the error.
+ */
+ valueOf : function () {
+ return this.toString();
+ }
+
+ });
+
+ /**
+ * ComparisonFailure is subclass of Error that is thrown whenever
+ * a comparison between two values fails. It provides mechanisms to retrieve
+ * both the expected and actual value.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @param {Object} expected The expected value.
+ * @param {Object} actual The actual value that caused the assertion to fail.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class ComparisonFailure
+ * @constructor
+ */
+ Y.Assert.ComparisonFailure = function (message, expected, actual){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message);
+
+ /**
+ * The expected value.
+ * @type Object
+ * @property expected
+ */
+ this.expected = expected;
+
+ /**
+ * The actual value.
+ * @type Object
+ * @property actual
+ */
+ this.actual = actual;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "ComparisonFailure";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.ComparisonFailure, Y.Assert.Error, {
+
+ /**
+ * Returns a fully formatted error for an assertion failure. This message
+ * provides information about the expected and actual values.
+ * @method toString
+ * @return {String} A string describing the error.
+ */
+ getMessage : function () {
+ return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
+ "\nActual: " + this.actual + " (" + (typeof this.actual) + ")";
+ }
+
+ });
+
+ /**
+ * UnexpectedValue is subclass of Error that is thrown whenever
+ * a value was unexpected in its scope. This typically means that a test
+ * was performed to determine that a value was *not* equal to a certain
+ * value.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @param {Object} unexpected The unexpected value.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class UnexpectedValue
+ * @constructor
+ */
+ Y.Assert.UnexpectedValue = function (message, unexpected){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message);
+
+ /**
+ * The unexpected value.
+ * @type Object
+ * @property unexpected
+ */
+ this.unexpected = unexpected;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "UnexpectedValue";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.UnexpectedValue, Y.Assert.Error, {
+
+ /**
+ * Returns a fully formatted error for an assertion failure. The message
+ * contains information about the unexpected value that was encountered.
+ * @method getMessage
+ * @return {String} A string describing the error.
+ */
+ getMessage : function () {
+ return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
+ }
+
+ });
+
+ /**
+ * ShouldFail is subclass of Error that is thrown whenever
+ * a test was expected to fail but did not.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class ShouldFail
+ * @constructor
+ */
+ Y.Assert.ShouldFail = function (message){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message || "This test should fail but didn't.");
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "ShouldFail";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.ShouldFail, Y.Assert.Error);
+
+ /**
+ * ShouldError is subclass of Error that is thrown whenever
+ * a test is expected to throw an error but doesn't.
+ *
+ * @param {String} message The message to display when the error occurs.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class ShouldError
+ * @constructor
+ */
+ Y.Assert.ShouldError = function (message){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, message || "This test should have thrown an error but didn't.");
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "ShouldError";
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.ShouldError, Y.Assert.Error);
+
+ /**
+ * UnexpectedError is subclass of Error that is thrown whenever
+ * an error occurs within the course of a test and the test was not expected
+ * to throw an error.
+ *
+ * @param {Error} cause The unexpected error that caused this error to be
+ * thrown.
+ * @namespace Assert
+ * @extends Assert.Error
+ * @class UnexpectedError
+ * @constructor
+ */
+ Y.Assert.UnexpectedError = function (cause){
+
+ //call superclass
+ arguments.callee.superclass.constructor.call(this, "Unexpected error: " + cause.message);
+
+ /**
+ * The unexpected error that occurred.
+ * @type Error
+ * @property cause
+ */
+ this.cause = cause;
+
+ /**
+ * The name of the error that occurred.
+ * @type String
+ * @property name
+ */
+ this.name = "UnexpectedError";
+
+ /**
+ * Stack information for the error (if provided).
+ * @type String
+ * @property stack
+ */
+ this.stack = cause.stack;
+
+ };
+
+ //inherit methods
+ Y.extend(Y.Assert.UnexpectedError, Y.Assert.Error);
+
+
+
+ /**
+ * The ArrayAssert object provides functions to test JavaScript array objects
+ * for a variety of cases.
+ *
+ * @class ArrayAssert
+ * @static
+ */
+
+ Y.ArrayAssert = {
+
+ /**
+ * Asserts that a value is present in an array. This uses the triple equals
+ * sign so no type cohersion may occur.
+ * @param {Object} needle The value that is expected in the array.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method contains
+ * @static
+ */
+ contains : function (needle, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ if (Y.Array.indexOf(haystack, needle) == -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that a set of values are present in an array. This uses the triple equals
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
+ * be found.
+ * @param {Object[]} needles An array of values that are expected in the array.
+ * @param {Array} haystack An array of values to check.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method containsItems
+ * @static
+ */
+ containsItems : function (needles, haystack,
+ message) {
+ Y.Assert._increment();
+
+ //begin checking values
+ for (var i=0; i < needles.length; i++){
+ if (Y.Array.indexOf(haystack, needles[i]) == -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value " + needles[i] + " (" + (typeof needles[i]) + ") not found in array [" + haystack + "]."));
+ }
+ }
+ },
+
+ /**
+ * Asserts that a value matching some condition is present in an array. This uses
+ * a function to determine a match.
+ * @param {Function} matcher A function that returns true if the items matches or false if not.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method containsMatch
+ * @static
+ */
+ containsMatch : function (matcher, haystack,
+ message) {
+
+ Y.Assert._increment();
+ //check for valid matcher
+ if (typeof matcher != "function"){
+ throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
+ }
+
+ if (!Y.Array.some(haystack, matcher)){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that a value is not present in an array. This uses the triple equals
+ * Asserts that a value is not present in an array. This uses the triple equals
+ * sign so no type cohersion may occur.
+ * @param {Object} needle The value that is expected in the array.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method doesNotContain
+ * @static
+ */
+ doesNotContain : function (needle, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ if (Y.Array.indexOf(haystack, needle) > -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that a set of values are not present in an array. This uses the triple equals
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
+ * not be found.
+ * @param {Object[]} needles An array of values that are not expected in the array.
+ * @param {Array} haystack An array of values to check.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method doesNotContainItems
+ * @static
+ */
+ doesNotContainItems : function (needles, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ for (var i=0; i < needles.length; i++){
+ if (Y.Array.indexOf(haystack, needles[i]) > -1){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
+ }
+ }
+
+ },
+
+ /**
+ * Asserts that no values matching a condition are present in an array. This uses
+ * a function to determine a match.
+ * @param {Function} matcher A function that returns true if the items matches or false if not.
+ * @param {Array} haystack An array of values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method doesNotContainMatch
+ * @static
+ */
+ doesNotContainMatch : function (matcher, haystack,
+ message) {
+
+ Y.Assert._increment();
+
+ //check for valid matcher
+ if (typeof matcher != "function"){
+ throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
+ }
+
+ if (Y.Array.some(haystack, matcher)){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
+ }
+ },
+
+ /**
+ * Asserts that the given value is contained in an array at the specified index.
+ * This uses the triple equals sign so no type cohersion will occur.
+ * @param {Object} needle The value to look for.
+ * @param {Array} haystack The array to search in.
+ * @param {int} index The index at which the value should exist.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method indexOf
+ * @static
+ */
+ indexOf : function (needle, haystack, index, message) {
+
+ Y.Assert._increment();
+
+ //try to find the value in the array
+ for (var i=0; i < haystack.length; i++){
+ if (haystack[i] === needle){
+ if (index != i){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
+ }
+ return;
+ }
+ }
+
+ //if it makes it here, it wasn't found at all
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
+ },
+
+ /**
+ * Asserts that the values in an array are equal, and in the same position,
+ * as values in another array. This uses the double equals sign
+ * so type cohersion may occur. Note that the array objects themselves
+ * need not be the same for this test to pass.
+ * @param {Array} expected An array of the expected values.
+ * @param {Array} actual Any array of the actual values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method itemsAreEqual
+ * @static
+ */
+ itemsAreEqual : function (expected, actual,
+ message) {
+
+ Y.Assert._increment();
+
+ //first check array length
+ if (expected.length != actual.length){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
+ }
+
+ //begin checking values
+ for (var i=0; i < expected.length; i++){
+ if (expected[i] != actual[i]){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not equal."), expected[i], actual[i]);
+ }
+ }
+ },
+
+ /**
+ * Asserts that the values in an array are equivalent, and in the same position,
+ * as values in another array. This uses a function to determine if the values
+ * are equivalent. Note that the array objects themselves
+ * need not be the same for this test to pass.
+ * @param {Array} expected An array of the expected values.
+ * @param {Array} actual Any array of the actual values.
+ * @param {Function} comparator A function that returns true if the values are equivalent
+ * or false if not.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @return {Void}
+ * @method itemsAreEquivalent
+ * @static
+ */
+ itemsAreEquivalent : function (expected, actual,
+ comparator, message) {
+
+ Y.Assert._increment();
+
+ //make sure the comparator is valid
+ if (typeof comparator != "function"){
+ throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
+ }
+
+ //first check array length
+ if (expected.length != actual.length){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
+ }
+
+ //begin checking values
+ for (var i=0; i < expected.length; i++){
+ if (!comparator(expected[i], actual[i])){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
+ }
+ }
+ },
+
+ /**
+ * Asserts that an array is empty.
+ * @param {Array} actual The array to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isEmpty
+ * @static
+ */
+ isEmpty : function (actual, message) {
+ Y.Assert._increment();
+ if (actual.length > 0){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should be empty."));
+ }
+ },
+
+ /**
+ * Asserts that an array is not empty.
+ * @param {Array} actual The array to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method isNotEmpty
+ * @static
+ */
+ isNotEmpty : function (actual, message) {
+ Y.Assert._increment();
+ if (actual.length === 0){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should not be empty."));
+ }
+ },
+
+ /**
+ * Asserts that the values in an array are the same, and in the same position,
+ * as values in another array. This uses the triple equals sign
+ * so no type cohersion will occur. Note that the array objects themselves
+ * need not be the same for this test to pass.
+ * @param {Array} expected An array of the expected values.
+ * @param {Array} actual Any array of the actual values.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method itemsAreSame
+ * @static
+ */
+ itemsAreSame : function (expected, actual,
+ message) {
+
+ Y.Assert._increment();
+
+ //first check array length
+ if (expected.length != actual.length){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
+ }
+
+ //begin checking values
+ for (var i=0; i < expected.length; i++){
+ if (expected[i] !== actual[i]){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not the same."), expected[i], actual[i]);
+ }
+ }
+ },
+
+ /**
+ * Asserts that the given value is contained in an array at the specified index,
+ * starting from the back of the array.
+ * This uses the triple equals sign so no type cohersion will occur.
+ * @param {Object} needle The value to look for.
+ * @param {Array} haystack The array to search in.
+ * @param {int} index The index at which the value should exist.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method lastIndexOf
+ * @static
+ */
+ lastIndexOf : function (needle, haystack, index, message) {
+
+ //try to find the value in the array
+ for (var i=haystack.length; i >= 0; i--){
+ if (haystack[i] === needle){
+ if (index != i){
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
+ }
+ return;
+ }
+ }
+
+ //if it makes it here, it wasn't found at all
+ Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array."));
+ }
+
+ };
+
+
+ /**
+ * The ObjectAssert object provides functions to test JavaScript objects
+ * for a variety of cases.
+ *
+ * @class ObjectAssert
+ * @static
+ */
+ Y.ObjectAssert = {
+
+ areEqual: function(expected, actual, message) {
+ Y.Assert._increment();
+ Y.Object.each(expected, function(value, name){
+ if (expected[name] != actual[name]){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be equal for property " + name), expected[name], actual[name]);
+ }
+ });
+ },
+
+ /**
+ * Asserts that an object has a property with the given name.
+ * @param {String} propertyName The name of the property to test.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method hasKey
+ * @static
+ */
+ hasKey: function (propertyName, object, message) {
+ Y.Assert._increment();
+ if (!Y.Object.hasKey(object, propertyName)){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
+ }
+ },
+
+ /**
+ * Asserts that an object has all properties of a reference object.
+ * @param {Array} properties An array of property names that should be on the object.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method hasKeys
+ * @static
+ */
+ hasKeys: function (properties, object, message) {
+ Y.Assert._increment();
+ for (var i=0; i < properties.length; i++){
+ if (!Y.Object.hasKey(object, properties[i])){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object."));
+ }
+ }
+ },
+
+ /**
+ * Asserts that a property with the given name exists on an object instance (not on its prototype).
+ * @param {String} propertyName The name of the property to test.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method ownsKey
+ * @static
+ */
+ ownsKey: function (propertyName, object, message) {
+ Y.Assert._increment();
+ if (!object.hasOwnProperty(propertyName)){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
+ }
+ },
+
+ /**
+ * Asserts that all properties exist on an object instance (not on its prototype).
+ * @param {Array} properties An array of property names that should be on the object.
+ * @param {Object} object The object to search.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method ownsKeys
+ * @static
+ */
+ ownsKeys: function (properties, object, message) {
+ Y.Assert._increment();
+ for (var i=0; i < properties.length; i++){
+ if (!object.hasOwnProperty(properties[i])){
+ Y.fail(Y.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
+ }
+ }
+ },
+
+ /**
+ * Asserts that an object owns no properties.
+ * @param {Object} object The object to check.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method ownsNoKeys
+ * @static
+ */
+ ownsNoKeys : function (object, message) {
+ Y.Assert._increment();
+
+ var keys = Y.Object.keys(object);
+
+ if (keys.length > 0){
+ Y.fail(Y.Assert._formatMessage(message, "Object owns " + keys.length + " properties but should own none."));
+ }
+
+ }
+ };
+
+
+
+ /**
+ * The DateAssert object provides functions to test JavaScript Date objects
+ * for a variety of cases.
+ *
+ * @class DateAssert
+ * @static
+ */
+
+ Y.DateAssert = {
+
+ /**
+ * Asserts that a date's month, day, and year are equal to another date's.
+ * @param {Date} expected The expected date.
+ * @param {Date} actual The actual date to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method datesAreEqual
+ * @static
+ */
+ datesAreEqual : function (expected, actual, message){
+ Y.Assert._increment();
+ if (expected instanceof Date && actual instanceof Date){
+ var msg = "";
+
+ //check years first
+ if (expected.getFullYear() != actual.getFullYear()){
+ msg = "Years should be equal.";
+ }
+
+ //now check months
+ if (expected.getMonth() != actual.getMonth()){
+ msg = "Months should be equal.";
+ }
+
+ //last, check the day of the month
+ if (expected.getDate() != actual.getDate()){
+ msg = "Days of month should be equal.";
+ }
+
+ if (msg.length){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, msg), expected, actual);
+ }
+ } else {
+ throw new TypeError("Y.Assert.datesAreEqual(): Expected and actual values must be Date objects.");
+ }
+ },
+
+ /**
+ * Asserts that a date's hour, minutes, and seconds are equal to another date's.
+ * @param {Date} expected The expected date.
+ * @param {Date} actual The actual date to test.
+ * @param {String} message (Optional) The message to display if the assertion fails.
+ * @method timesAreEqual
+ * @static
+ */
+ timesAreEqual : function (expected, actual, message){
+ Y.Assert._increment();
+ if (expected instanceof Date && actual instanceof Date){
+ var msg = "";
+
+ //check hours first
+ if (expected.getHours() != actual.getHours()){
+ msg = "Hours should be equal.";
+ }
+
+ //now check minutes
+ if (expected.getMinutes() != actual.getMinutes()){
+ msg = "Minutes should be equal.";
+ }
+
+ //last, check the seconds
+ if (expected.getSeconds() != actual.getSeconds()){
+ msg = "Seconds should be equal.";
+ }
+
+ if (msg.length){
+ throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, msg), expected, actual);
+ }
+ } else {
+ throw new TypeError("DateY.AsserttimesAreEqual(): Expected and actual values must be Date objects.");
+ }
+ }
+
+ };
+
+
+ Y.namespace("Test.Format");
+
+ /* (intentionally not documented)
+ * Basic XML escaping method. Replaces quotes, less-than, greater-than,
+ * apostrophe, and ampersand characters with their corresponding entities.
+ * @param {String} text The text to encode.
+ * @return {String} The XML-escaped text.
+ */
+ function xmlEscape(text){
+
+ return text.replace(/[<>"'&]/g, function(value){
+ switch(value){
+ case "<": return "<";
+ case ">": return ">";
+ case "\"": return """;
+ case "'": return "'";
+ case "&": return "&";
+ }
+ });
+
+ }
+
+ /**
+ * Returns test results formatted as a JSON string. Requires JSON utility.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} A JSON-formatted string of results.
+ * @namespace Test.Format
+ * @method JSON
+ * @static
+ */
+ Y.Test.Format.JSON = function(results) {
+ return Y.JSON.stringify(results);
+ };
+
+ /**
+ * Returns test results formatted as an XML string.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} An XML-formatted string of results.
+ * @namespace Test.Format
+ * @method XML
+ * @static
+ */
+ Y.Test.Format.XML = function(results) {
+
+ var l = Y.Lang;
+ var xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
+
+ if (results.type == "test"){
+ xml += " result=\"" + xmlEscape(results.result) + "\" message=\"" + xmlEscape(results.message) + "\">";
+ } else {
+ xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += arguments.callee(value);
+ }
+ });
+ }
+
+ xml += "</" + results.type + ">";
+
+ return xml;
+
+ };
+
+ /**
+ * Returns test results formatted as an XML string.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} An XML-formatted string of results.
+ * @namespace Test.Format
+ * @method XML
+ * @static
+ */
+ Y.Test.Format.XML = function(results) {
+
+ function serializeToXML(results){
+ var l = Y.Lang,
+ xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
+
+ if (l.isNumber(results.duration)){
+ xml += " duration=\"" + results.duration + "\"";
+ }
+
+ if (results.type == "test"){
+ xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
+ } else {
+ xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToXML(value);
+ }
+ });
+ }
+
+ xml += "</" + results.type + ">";
+
+ return xml;
+ }
+
+ return "<?xml version=\"1.0\" charset=\"UTF-8\"?>" + serializeToXML(results);
+
+ };
+
+
+ /**
+ * Returns test results formatted in JUnit XML format.
+ * @param {Object} result The results object created by TestRunner.
+ * @return {String} An XML-formatted string of results.
+ * @namespace Test.Format
+ * @method JUnitXML
+ * @static
+ */
+ Y.Test.Format.JUnitXML = function(results) {
+
+
+ function serializeToJUnitXML(results){
+ var l = Y.Lang,
+ xml = "",
+ prop;
+
+ switch (results.type){
+ //equivalent to testcase in JUnit
+ case "test":
+ if (results.result != "ignore"){
+ xml = "<testcase name=\"" + xmlEscape(results.name) + "\">";
+ if (results.result == "fail"){
+ xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
+ }
+ xml+= "</testcase>";
+ }
+ break;
+
+ //equivalent to testsuite in JUnit
+ case "testcase":
+
+ xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\">";
+
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToJUnitXML(value);
+ }
+ });
+
+ xml += "</testsuite>";
+ break;
+
+ case "testsuite":
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToJUnitXML(value);
+ }
+ });
+
+ //skip output - no JUnit equivalent
+ break;
+
+ case "report":
+
+ xml = "<testsuites>";
+
+ Y.Object.each(results, function(value, prop){
+ if (l.isObject(value) && !l.isArray(value)){
+ xml += serializeToJUnitXML(value);
+ }
+ });
+
+ xml += "</testsuites>";
+
+ //no default
+ }
+
+ return xml;
+
+ }
+
+ return "<?xml version=\"1.0\" charset=\"UTF-8\"?>" + serializeToJUnitXML(results);
+ };
+
+
+
+ Y.namespace("Test");
+
+ /**
+ * An object capable of sending test results to a server.
+ * @param {String} url The URL to submit the results to.
+ * @param {Function} format (Optiona) A function that outputs the results in a specific format.
+ * Default is Y.Test.Format.XML.
+ * @constructor
+ * @namespace Test
+ * @class Reporter
+ */
+ Y.Test.Reporter = function(url, format) {
+
+ /**
+ * The URL to submit the data to.
+ * @type String
+ * @property url
+ */
+ this.url = url;
+
+ /**
+ * The formatting function to call when submitting the data.
+ * @type Function
+ * @property format
+ */
+ this.format = format || Y.Test.Format.XML;
+
+ /**
+ * Extra fields to submit with the request.
+ * @type Object
+ * @property _fields
+ * @private
+ */
+ this._fields = new Object();
+
+ /**
+ * The form element used to submit the results.
+ * @type HTMLFormElement
+ * @property _form
+ * @private
+ */
+ this._form = null;
+
+ /**
+ * Iframe used as a target for form submission.
+ * @type HTMLIFrameElement
+ * @property _iframe
+ * @private
+ */
+ this._iframe = null;
+ };
+
+ Y.Test.Reporter.prototype = {
+
+ //restore missing constructor
+ constructor: Y.Test.Reporter,
+
+ /**
+ * Adds a field to the form that submits the results.
+ * @param {String} name The name of the field.
+ * @param {Variant} value The value of the field.
+ * @return {Void}
+ * @method addField
+ */
+ addField : function (name, value){
+ this._fields[name] = value;
+ },
+
+ /**
+ * Removes all previous defined fields.
+ * @return {Void}
+ * @method addField
+ */
+ clearFields : function(){
+ this._fields = new Object();
+ },
+
+ /**
+ * Cleans up the memory associated with the TestReporter, removing DOM elements
+ * that were created.
+ * @return {Void}
+ * @method destroy
+ */
+ destroy : function() {
+ if (this._form){
+ this._form.parentNode.removeChild(this._form);
+ this._form = null;
+ }
+ if (this._iframe){
+ this._iframe.parentNode.removeChild(this._iframe);
+ this._iframe = null;
+ }
+ this._fields = null;
+ },
+
+ /**
+ * Sends the report to the server.
+ * @param {Object} results The results object created by TestRunner.
+ * @return {Void}
+ * @method report
+ */
+ report : function(results){
+
+ //if the form hasn't been created yet, create it
+ if (!this._form){
+ this._form = document.createElement("form");
+ this._form.method = "post";
+ this._form.style.visibility = "hidden";
+ this._form.style.position = "absolute";
+ this._form.style.top = 0;
+ document.body.appendChild(this._form);
+
+ //IE won't let you assign a name using the DOM, must do it the hacky way
+ if (Y.UA.ie){
+ this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
+ } else {
+ this._iframe = document.createElement("iframe");
+ this._iframe.name = "yuiTestTarget";
+ }
+
+ this._iframe.src = "javascript:false";
+ this._iframe.style.visibility = "hidden";
+ this._iframe.style.position = "absolute";
+ this._iframe.style.top = 0;
+ document.body.appendChild(this._iframe);
+
+ this._form.target = "yuiTestTarget";
+ }
+
+ //set the form's action
+ this._form.action = this.url;
+
+ //remove any existing fields
+ while(this._form.hasChildNodes()){
+ this._form.removeChild(this._form.lastChild);
+ }
+
+ //create default fields
+ this._fields.results = this.format(results);
+ this._fields.useragent = navigator.userAgent;
+ this._fields.timestamp = (new Date()).toLocaleString();
+
+ //add fields to the form
+ Y.Object.each(this._fields, function(value, prop){
+ if (typeof value != "function"){
+ var input = document.createElement("input");
+ input.type = "hidden";
+ input.name = prop;
+ input.value = value;
+ this._form.appendChild(input);
+ }
+ }, this);
+
+ //remove default fields
+ delete this._fields.results;
+ delete this._fields.useragent;
+ delete this._fields.timestamp;
+
+ if (arguments[1] !== false){
+ this._form.submit();
+ }
+
+ }
+
+ };
+
+ /**
+ * Creates a new mock object.
+ * @class Mock
+ * @constructor
+ * @param {Object} template (Optional) An object whose methods
+ * should be stubbed out on the mock object.
+ */
+ Y.Mock = function(template){
+
+ //use blank object is nothing is passed in
+ template = template || {};
+
+ var mock = null;
+
+ //try to create mock that keeps prototype chain intact
+ try {
+ mock = Y.Object(template);
+ } catch (ex) {
+ mock = {};
+ Y.log("Couldn't create mock with prototype.", "warn", "Mock");
+ }
+
+ //create new versions of the methods so that they don't actually do anything
+ Y.Object.each(template, function(name){
+ if (Y.Lang.isFunction(template[name])){
+ mock[name] = function(){
+ Y.Assert.fail("Method " + name + "() was called but was not expected to be.");
+ };
+ }
+ });
+
+ //return it
+ return mock;
+ };
+
+ /**
+ * Assigns an expectation to a mock object. This is used to create
+ * methods and properties on the mock object that are monitored for
+ * calls and changes, respectively.
+ * @param {Object} mock The object to add the expectation to.
+ * @param {Object} expectation An object defining the expectation. For
+ * a method, the keys "method" and "args" are required with
+ * an optional "returns" key available. For properties, the keys
+ * "property" and "value" are required.
+ * @return {void}
+ * @method expect
+ * @static
+ */
+ Y.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
+
+ //make sure there's a place to store the expectations
+ if (!mock.__expectations) {
+ mock.__expectations = {};
+ }
+
+ //method expectation
+ if (expectation.method){
+ var name = expectation.method,
+ args = expectation.args || expectation.arguments || [],
+ result = expectation.returns,
+ callCount = Y.Lang.isNumber(expectation.callCount) ? expectation.callCount : 1,
+ error = expectation.error,
+ run = expectation.run || function(){};
+
+ //save expectations
+ mock.__expectations[name] = expectation;
+ expectation.callCount = callCount;
+ expectation.actualCallCount = 0;
+
+ //process arguments
+ Y.Array.each(args, function(arg, i, array){
+ if (!(array[i] instanceof Y.Mock.Value)){
+ array[i] = Y.Mock.Value(Y.Assert.areSame, [arg], "Argument " + i + " of " + name + "() is incorrect.");
+ }
+ });
+
+ //if the method is expected to be called
+ if (callCount > 0){
+ mock[name] = function(){
+ try {
+ expectation.actualCallCount++;
+ Y.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
+ for (var i=0, len=args.length; i < len; i++){
+ //if (args[i]){
+ args[i].verify(arguments[i]);
+ //} else {
+ // Y.Assert.fail("Argument " + i + " (" + arguments[i] + ") was not expected to be used.");
+ //}
+
+ }
+
+ run.apply(this, arguments);
+
+ if (error){
+ throw error;
+ }
+ } catch (ex){
+ //route through TestRunner for proper handling
+ Y.Test.Runner._handleError(ex);
+ }
+
+ return result;
+ };
+ } else {
+
+ //method should fail if called when not expected
+ mock[name] = function(){
+ try {
+ Y.Assert.fail("Method " + name + "() should not have been called.");
+ } catch (ex){
+ //route through TestRunner for proper handling
+ Y.Test.Runner._handleError(ex);
+ }
+ };
+ }
+ } else if (expectation.property){
+ //save expectations
+ mock.__expectations[name] = expectation;
+ }
+ };
+
+ /**
+ * Verifies that all expectations of a mock object have been met and
+ * throws an assertion error if not.
+ * @param {Object} mock The object to verify..
+ * @return {void}
+ * @method verify
+ * @static
+ */
+ Y.Mock.verify = function(mock /*:Object*/){
+ try {
+ Y.Object.each(mock.__expectations, function(expectation){
+ if (expectation.method) {
+ Y.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
+ } else if (expectation.property){
+ Y.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value.");
+ }
+ });
+ } catch (ex){
+ //route through TestRunner for proper handling
+ Y.Test.Runner._handleError(ex);
+ }
+ };
+
+ Y.Mock.Value = function(method, originalArgs, message){
+ if (this instanceof Y.Mock.Value){
+ this.verify = function(value){
+ var args = [].concat(originalArgs || []);
+ args.push(value);
+ args.push(message);
+ method.apply(null, args);
+ };
+ } else {
+ return new Y.Mock.Value(method, originalArgs, message);
+ }
+ };
+
+ Y.Mock.Value.Any = Y.Mock.Value(function(){});
+ Y.Mock.Value.Boolean = Y.Mock.Value(Y.Assert.isBoolean);
+ Y.Mock.Value.Number = Y.Mock.Value(Y.Assert.isNumber);
+ Y.Mock.Value.String = Y.Mock.Value(Y.Assert.isString);
+ Y.Mock.Value.Object = Y.Mock.Value(Y.Assert.isObject);
+ Y.Mock.Value.Function = Y.Mock.Value(Y.Assert.isFunction);
+
+
+
+}, '3.0.0' ,{requires:['substitute','event-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-widget-stacked .yui-widget-shim{opacity:0;filter:alpha(opacity=0);position:absolute;border:none;top:0;left:0;padding:0;margin:0;z-index:-1;width:100%;height:100%;_width:0;_height:0;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-widget-hidden{display:none;}
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-widget-hidden {
+ display:none;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+.yui-widget-stacked .yui-widget-shim {
+ opacity:0;
+ filter:alpha(opacity=0);
+ position:absolute;
+ border:none;
+ top:0px;
+ left:0px;
+ padding:0;
+ margin:0;
+ z-index:-1;
+ width:100%;
+ height:100%;
+ /*
+ We'll be setting these programmatically for IE6,
+ to account for cases where height is not set
+ */
+ _width:0;
+ _height:0;
+}
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget', function(Y) {
+
+/**
+ * Provides the base Widget class
+ *
+ * @module widget
+ */
+
+// Local Constants
+var L = Y.Lang,
+ O = Y.Object,
+ Node = Y.Node,
+ ClassNameManager = Y.ClassNameManager,
+
+ WIDGET = "widget",
+ CONTENT = "content",
+ VISIBLE = "visible",
+ HIDDEN = "hidden",
+ DISABLED = "disabled",
+ FOCUSED = "focused",
+ WIDTH = "width",
+ HEIGHT = "height",
+ EMPTY = "",
+ HYPHEN = "-",
+ BOUNDING_BOX = "boundingBox",
+ CONTENT_BOX = "contentBox",
+ PARENT_NODE = "parentNode",
+ FIRST_CHILD = "firstChild",
+ OWNER_DOCUMENT = "ownerDocument",
+ BODY = "body",
+ TAB_INDEX = "tabIndex",
+ LOCALE = "locale",
+ INIT_VALUE = "initValue",
+ ID = "id",
+ RENDER = "render",
+ RENDERED = "rendered",
+ DESTROYED = "destroyed",
+
+ ContentUpdate = "contentUpdate",
+
+ // Widget nodeid-to-instance map for now, 1-to-1.
+ _instances = {};
+
+/**
+ * A base class for widgets, providing:
+ * <ul>
+ * <li>The render lifecycle method, in addition to the init and destroy
+ * lifecycle methods provide by Base</li>
+ * <li>Abstract methods to support consistent MVC structure across
+ * widgets: renderer, renderUI, bindUI, syncUI</li>
+ * <li>Support for common widget attributes, such as boundingBox, contentBox, visible,
+ * disabled, focused, strings</li>
+ * </ul>
+ *
+ * @param config {Object} Object literal specifying widget configuration
+ * properties.
+ *
+ * @class Widget
+ * @constructor
+ * @extends Base
+ */
+function Widget(config) {
+ Y.log('constructor called', 'life', 'widget');
+
+ this._yuid = Y.guid(WIDGET);
+ this._strings = {};
+
+ Widget.superclass.constructor.apply(this, arguments);
+}
+
+/**
+ * The build configuration for the Widget class.
+ * <p>
+ * Defines the static fields which need to be aggregated,
+ * when this class is used as the main class passed to
+ * the <a href="Base.html#method_build">Base.build</a> method.
+ * </p>
+ * @property _buildCfg
+ * @type Object
+ * @static
+ * @final
+ * @private
+ */
+Widget._buildCfg = {
+ aggregates : ["HTML_PARSER"]
+};
+
+/**
+ * Static property provides a string to identify the class.
+ * <p>
+ * Currently used to apply class identifiers to the bounding box
+ * and to classify events fired by the widget.
+ * </p>
+ *
+ * @property Widget.NAME
+ * @type String
+ * @static
+ */
+Widget.NAME = WIDGET;
+
+/**
+ * Constant used to identify state changes originating from
+ * the DOM (as opposed to the JavaScript model).
+ *
+ * @property Widget.UI_SRC
+ * @type String
+ * @static
+ * @final
+ */
+Widget.UI_SRC = "ui";
+
+var UI = Widget.UI_SRC;
+
+/**
+ * Static property used to define the default attribute
+ * configuration for the Widget.
+ *
+ * @property Widget.ATTRS
+ * @type Object
+ * @static
+ */
+Widget.ATTRS = {
+
+ /**
+ * Flag indicating whether or not this object
+ * has been through the render lifecycle phase.
+ *
+ * @attribute rendered
+ * @readOnly
+ * @default false
+ * @type boolean
+ */
+ rendered: {
+ value:false,
+ readOnly:true
+ },
+
+ /**
+ * @attribute boundingBox
+ * @description The outermost DOM node for the Widget, used for sizing and positioning
+ * of a Widget as well as a containing element for any decorator elements used
+ * for skinning.
+ * @type Node
+ */
+ boundingBox: {
+ value:null,
+ setter: function(node) {
+ return this._setBoundingBox(node);
+ },
+ writeOnce: true
+ },
+
+ /**
+ * @attribute contentBox
+ * @description A DOM node that is a direct descendent of a Widget's bounding box that
+ * houses its content.
+ * @type Node
+ */
+ contentBox: {
+ value:null,
+ setter: function(node) {
+ return this._setContentBox(node);
+ },
+ writeOnce: true
+ },
+
+ /**
+ * @attribute tabIndex
+ * @description Number (between -32767 to 32767) indicating the widget's
+ * position in the default tab flow. The value is used to set the
+ * "tabIndex" attribute on the widget's bounding box. Negative values allow
+ * the widget to receive DOM focus programmatically (by calling the focus
+ * method), while being removed from the default tab flow. A value of
+ * null removes the "tabIndex" attribute from the widget's bounding box.
+ * @type Number
+ * @default null
+ */
+ tabIndex: {
+
+ value: 0,
+ validator: function (val) {
+ return (L.isNumber(val) || L.isNull(val));
+ }
+
+ },
+
+ /**
+ * @attribute focused
+ * @description Boolean indicating if the Widget, or one of its descendants,
+ * has focus.
+ * @readOnly
+ * @default false
+ * @type boolean
+ */
+ focused: {
+ value: false,
+ readOnly:true
+ },
+
+ /**
+ * @attribute disabled
+ * @description Boolean indicating if the Widget should be disabled. The disabled implementation
+ * is left to the specific classes extending widget.
+ * @default false
+ * @type boolean
+ */
+ disabled: {
+ value: false
+ },
+
+ /**
+ * @attribute visible
+ * @description Boolean indicating weather or not the Widget is visible.
+ * @default true
+ * @type boolean
+ */
+ visible: {
+ value: true
+ },
+
+ /**
+ * @attribute height
+ * @description String with units, or number, representing the height of the Widget. If a number is provided,
+ * the default unit, defined by the Widgets DEF_UNIT, property is used.
+ * @default ""
+ * @type {String | Number}
+ */
+ height: {
+ value: EMPTY
+ },
+
+ /**
+ * @attribute width
+ * @description String with units, or number, representing the width of the Widget. If a number is provided,
+ * the default unit, defined by the Widgets DEF_UNIT, property is used.
+ * @default ""
+ * @type {String | Number}
+ */
+ width: {
+ value: EMPTY
+ },
+
+ /**
+ * @attribute moveStyles
+ * @description Flag defining whether or not style properties from the content box
+ * should be moved to the bounding box when wrapped (as defined by the WRAP_STYLES property)
+ * @default false
+ * @type boolean
+ */
+ moveStyles: {
+ value: false
+ },
+
+ /**
+ * @attribute locale
+ * @description
+ * The default locale for the widget. NOTE: Using get/set on the "strings" attribute will
+ * return/set strings for this locale.
+ * @default "en"
+ * @type String
+ */
+ locale : {
+ value: "en"
+ },
+
+ /**
+ * @attribute strings
+ * @description Collection of strings used to label elements of the Widget's UI.
+ * @default null
+ * @type Object
+ */
+ strings: {
+ setter: function(val) {
+ return this._setStrings(val, this.get(LOCALE));
+ },
+
+ getter: function() {
+ return this.getStrings(this.get(LOCALE));
+ }
+ }
+};
+
+/**
+ * Cached lowercase version of Widget.NAME
+ *
+ * @property Widget._NAME_LOWERCASE
+ * @private
+ * @static
+ */
+Widget._NAME_LOWERCASE = Widget.NAME.toLowerCase();
+
+/**
+ * Generate a standard prefixed classname for the Widget, prefixed by the default prefix defined
+ * by the <code>Y.config.classNamePrefix</code> attribute used by <code>ClassNameManager</code> and
+ * <code>Widget.NAME.toLowerCase()</code> (e.g. "yui-widget-xxxxx-yyyyy", based on default values for
+ * the prefix and widget class name).
+ * <p>
+ * The instance based version of this method can be used to generate standard prefixed classnames,
+ * based on the instances NAME, as opposed to Widget.NAME. This method should be used when you
+ * need to use a constant class name across different types instances.
+ * </p>
+ * @method getClassName
+ * @param {String*} args* 0..n strings which should be concatenated, using the default separator defined by ClassNameManager, to create the class name
+ */
+Widget.getClassName = function() {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(0, 0, this._NAME_LOWERCASE);
+ return ClassNameManager.getClassName.apply(ClassNameManager, args);
+};
+
+/**
+ * Returns the widget instance whose bounding box contains, or is, the given node.
+ * <p>
+ * In the case of nested widgets, the nearest bounding box ancestor is used to
+ * return the widget instance.
+ * </p>
+ * @method Widget.getByNode
+ * @static
+ * @param node {Node | String} The node for which to return a Widget instance. If a selector
+ * string is passed in, which selects more than one node, the first node found is used.
+ * @return {Widget} Widget instance, or null if not found.
+ */
+Widget.getByNode = function(node) {
+ var widget,
+ bbMarker = Widget.getClassName();
+
+ node = Node.get(node);
+ if (node) {
+ node = (node.hasClass(bbMarker)) ? node : node.ancestor("." + bbMarker);
+ if (node) {
+ widget = _instances[node.get(ID)];
+ }
+ }
+
+ return widget || null;
+};
+
+/**
+ * Object hash, defining how attribute values are to be parsed from
+ * markup contained in the widget's content box. e.g.:
+ * <pre>
+ * {
+ * // Set single Node references using selector syntax
+ * // (selector is run through node.query)
+ * titleNode: "span.yui-title",
+ * // Set NodeList references using selector syntax
+ * // (array indicates selector is to be run through node.queryAll)
+ * listNodes: ["li.yui-item"],
+ * // Set other attribute types, using a parse function.
+ * // Context is set to the widget instance.
+ * label: function(contentBox) {
+ * return contentBox.query("span.title").get("innerHTML");
+ * }
+ * }
+ * </pre>
+ *
+ * @property Widget.HTML_PARSER
+ * @type Object
+ * @static
+ */
+Widget.HTML_PARSER = {};
+
+Y.extend(Widget, Y.Base, {
+
+ /**
+ * Returns a class name prefixed with the the value of the
+ * <code>YUI.config.classNamePrefix</code> attribute + the instances <code>NAME</code> property.
+ * Uses <code>YUI.config.classNameDelimiter</code> attribute to delimit the provided strings.
+ * e.g.
+ * <code>
+ * <pre>
+ * // returns "yui-slider-foo-bar", for a slider instance
+ * var scn = slider.getClassName('foo','bar');
+ *
+ * // returns "yui-overlay-foo-bar", for an overlay instance
+ * var ocn = slider.getClassName('foo','bar');
+ * </pre>
+ * </code>
+ *
+ * @method getClassName
+ * @param {String}+ One or more classname bits to be joined and prefixed
+ */
+ getClassName: function () {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(0, 0, this._name);
+ return ClassNameManager.getClassName.apply(ClassNameManager, args);
+ },
+
+ /**
+ * Initializer lifecycle implementation for the Widget class. Registers the
+ * widget instance, and runs through the Widget's HTML_PARSER definition.
+ *
+ * @method initializer
+ * @protected
+ * @param config {Object} Configuration object literal for the widget
+ */
+ initializer: function(config) {
+ Y.log('initializer called', 'life', 'widget');
+
+ /**
+ * Notification event, which widget implementations can fire, when
+ * they change the content of the widget. This event has no default
+ * behavior and cannot be prevented, so the "on" or "after"
+ * moments are effectively equivalent (with on listeners being invoked before
+ * after listeners).
+ *
+ * @event widget:contentUpdate
+ * @preventable false
+ * @param {EventFacade} e The Event Facade
+ */
+ this.publish(ContentUpdate, { preventable:false });
+
+ this._name = this.constructor.NAME.toLowerCase();
+
+ var nodeId = this.get(BOUNDING_BOX).get(ID);
+ if (nodeId) {
+ _instances[nodeId] = this;
+ }
+
+ var htmlConfig = this._parseHTML(this.get(CONTENT_BOX));
+ if (htmlConfig) {
+ Y.aggregate(config, htmlConfig, false);
+ }
+ },
+
+ /**
+ * Descructor lifecycle implementation for the Widget class. Purges events attached
+ * to the bounding box (and all child nodes) and removes the Widget from the
+ * list of registered widgets.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor: function() {
+ Y.log('destructor called', 'life', 'widget');
+
+ var boundingBox = this.get(BOUNDING_BOX);
+
+ Y.Event.purgeElement(boundingBox, true);
+
+ var nodeId = boundingBox.get(ID);
+ if (nodeId && nodeId in _instances) {
+ delete _instances[nodeId];
+ }
+ },
+
+ /**
+ * Establishes the initial DOM for the widget. Invoking this
+ * method will lead to the creating of all DOM elements for
+ * the widget (or the manipulation of existing DOM elements
+ * for the progressive enhancement use case).
+ * <p>
+ * This method should only be invoked once for an initialized
+ * widget.
+ * </p>
+ * <p>
+ * It delegates to the widget specific renderer method to do
+ * the actual work.
+ * </p>
+ *
+ * @method render
+ * @chainable
+ * @final
+ * @param parentNode {Object | String} Optional. The Node under which the
+ * Widget is to be rendered. This can be a Node instance or a CSS selector string.
+ * <p>
+ * If the selector string returns more than one Node, the first node will be used
+ * as the parentNode. NOTE: This argument is required if both the boundingBox and contentBox
+ * are not currently in the document. If it's not provided, the Widget will be rendered
+ * to the body of the current document in this case.
+ * </p>
+ */
+ render: function(parentNode) {
+
+ if (this.get(DESTROYED)) {
+ Y.log("Render failed; widget has been destroyed", "error", "widget");
+ return;
+ }
+
+ if (!this.get(RENDERED)) {
+ /**
+ * Lifcyle event for the render phase, fired prior to rendering the UI
+ * for the widget (prior to invoking the widgets renderer method).
+ * <p>
+ * Subscribers to the "on" moment of this event, will be notified
+ * before the widget is rendered.
+ * </p>
+ * <p>
+ * Subscribers to the "after" momemt of this event, will be notified
+ * after rendering is complete.
+ * </p>
+ *
+ * @event widget:render
+ * @preventable _defRenderFn
+ * @param {EventFacade} e The Event Facade
+ */
+ this.publish(RENDER, {queuable:false, defaultFn: this._defRenderFn});
+
+ parentNode = (parentNode) ? Node.get(parentNode) : null;
+ if (parentNode && !parentNode.inDoc()) {
+ parentNode = null;
+ }
+
+ this.fire(RENDER, {parentNode: parentNode});
+ }
+
+ return this;
+ },
+
+ /**
+ * Default render handler
+ *
+ * @method _defRenderFn
+ * @protected
+ * @param {EventFacade} e The Event object
+ * @param {Node} parentNode The parent node to render to, if passed in to the <code>render</code> method
+ */
+ _defRenderFn : function(e) {
+
+ this._renderUI(e.parentNode);
+ this._bindUI();
+ this._syncUI();
+
+ this.renderer();
+
+ this._set(RENDERED, true);
+ },
+
+ /**
+ * Creates DOM (or manipulates DOM for progressive enhancement)
+ * This method is invoked by render() and is not chained
+ * automatically for the class hierarchy (like initializer, destructor)
+ * so it should be chained manually for subclasses if required.
+ *
+ * @method renderer
+ * @protected
+ */
+ renderer: function() {
+ this.renderUI();
+ this.bindUI();
+ this.syncUI();
+ },
+
+ /**
+ * Configures/Sets up listeners to bind Widget State to UI/DOM
+ *
+ * This method is not called by framework and is not chained
+ * automatically for the class hierarchy.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI: function() {},
+
+ /**
+ * Adds nodes to the DOM
+ *
+ * This method is not called by framework and is not chained
+ * automatically for the class hierarchy.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI: function() {},
+
+ /**
+ * Refreshes the rendered UI, based on Widget State
+ *
+ * This method is not called by framework and is not chained
+ * automatically for the class hierarchy.
+ *
+ * @method syncUI
+ *
+ */
+ syncUI: function(){},
+
+ /**
+ * @method hide
+ * @description Shows the Module element by setting the "visible" attribute to "false".
+ */
+ hide: function() {
+ return this.set(VISIBLE, false);
+ },
+
+ /**
+ * @method show
+ * @description Shows the Module element by setting the "visible" attribute to "true".
+ */
+ show: function() {
+ return this.set(VISIBLE, true);
+ },
+
+ /**
+ * @method focus
+ * @description Causes the Widget to receive the focus by setting the "focused"
+ * attribute to "true".
+ */
+ focus: function () {
+ return this._set(FOCUSED, true);
+ },
+
+ /**
+ * @method blur
+ * @description Causes the Widget to lose focus by setting the "focused" attribute
+ * to "false"
+ */
+ blur: function () {
+ return this._set(FOCUSED, false);
+ },
+
+ /**
+ * @method enable
+ * @description Set the Widget's "disabled" attribute to "false".
+ */
+ enable: function() {
+ return this.set(DISABLED, false);
+ },
+
+ /**
+ * @method disabled
+ * @description Set the Widget's "disabled" attribute to "true".
+ */
+ disable: function() {
+ return this.set(DISABLED, true);
+ },
+
+ /**
+ * Utilitity method used to apply the <code>HTML_PARSER</code> configuration for the
+ * instance, to retrieve config data values.
+ *
+ * @method _parseHTML
+ * @private
+ * @param node {Node} Root node to use to parse markup for configuration data
+ * @return config {Object} configuration object, with values found in the HTML, populated
+ */
+ _parseHTML : function(node) {
+
+ var schema = this._getHtmlParser(),
+ data,
+ val;
+
+ if (schema && node && node.hasChildNodes()) {
+
+ O.each(schema, function(v, k, o) {
+ val = null;
+
+ if (L.isFunction(v)) {
+ val = v.call(this, node);
+ } else {
+ if (L.isArray(v)) {
+ val = node.queryAll(v[0]);
+ } else {
+ val = node.query(v);
+ }
+ }
+
+ if (val !== null && val !== undefined) {
+ data = data || {};
+ data[k] = val;
+ }
+
+ }, this);
+ }
+
+ return data;
+ },
+
+ /**
+ * Moves a pre-defined set of style rules (WRAP_STYLES) from one node to another.
+ *
+ * @method _moveStyles
+ * @private
+ * @param {Node} nodeFrom The node to gather the styles from
+ * @param {Node} nodeTo The node to apply the styles to
+ */
+ _moveStyles: function(nodeFrom, nodeTo) {
+
+ var styles = this.WRAP_STYLES,
+ pos = nodeFrom.getStyle('position'),
+ contentBox = this.get(CONTENT_BOX),
+ xy = [0,0],
+ h, w;
+
+ if (!this.get('height')) {
+ h = contentBox.get('offsetHeight');
+ }
+
+ if (!this.get('width')) {
+ w = contentBox.get('offsetWidth');
+ }
+
+ if (pos === 'absolute') {
+ xy = nodeFrom.getXY();
+ nodeTo.setStyles({
+ right: 'auto',
+ bottom: 'auto'
+ });
+
+ nodeFrom.setStyles({
+ right: 'auto',
+ bottom: 'auto'
+ });
+ }
+
+ Y.each(styles, function(v, k) {
+ var s = nodeFrom.getStyle(k);
+ nodeTo.setStyle(k, s);
+ if (v === false) {
+ nodeFrom.setStyle(k, '');
+ } else {
+ nodeFrom.setStyle(k, v);
+ }
+ });
+
+ if (pos === 'absolute') {
+ nodeTo.setXY(xy);
+ }
+
+ if (h) {
+ this.set('height', h);
+ }
+
+ if (w) {
+ this.set('width', w);
+ }
+ },
+
+ /**
+ * Helper method to collect the boundingBox and contentBox, set styles and append to the provided parentNode, if not
+ * already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used
+ * as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and
+ * the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered
+ * to the current document's body.
+ *
+ * @method _renderBox
+ * @private
+ * @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and
+ * the contentBox are not currently in the document, the widget will be rendered to the current document's body.
+ */
+ _renderBox: function(parentNode) {
+
+ var contentBox = this.get(CONTENT_BOX),
+ boundingBox = this.get(BOUNDING_BOX),
+ doc = boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT),
+ body;
+
+ if (!boundingBox.compareTo(contentBox.get(PARENT_NODE))) {
+ if (this.get('moveStyles')) {
+ this._moveStyles(contentBox, boundingBox);
+ }
+ // If contentBox box is already in the document, have boundingBox box take it's place
+ if (contentBox.inDoc(doc)) {
+ contentBox.get(PARENT_NODE).replaceChild(boundingBox, contentBox);
+ }
+ boundingBox.appendChild(contentBox);
+ }
+
+ if (!boundingBox.inDoc(doc) && !parentNode) {
+ body = Node.get(BODY);
+ if (body.get(FIRST_CHILD)) {
+ // Special case when handling body as default (no parentNode), always try to insert.
+ body.insertBefore(boundingBox, body.get(FIRST_CHILD));
+ } else {
+ body.appendChild(boundingBox);
+ }
+ } else {
+ if (parentNode && !parentNode.compareTo(boundingBox.get(PARENT_NODE))) {
+ parentNode.appendChild(boundingBox);
+ }
+ }
+ },
+
+ /**
+ * Setter for the boundingBox attribute
+ *
+ * @method _setBoundingBox
+ * @private
+ * @param Node/String
+ * @return Node
+ */
+ _setBoundingBox: function(node) {
+ return this._setBox(node, this.BOUNDING_TEMPLATE);
+ },
+
+ /**
+ * Setter for the contentBox attribute
+ *
+ * @method _setContentBox
+ * @private
+ * @param {Node|String} node
+ * @return Node
+ */
+ _setContentBox: function(node) {
+ return this._setBox(node, this.CONTENT_TEMPLATE);
+ },
+
+ /**
+ * Helper method to set the bounding/content box, or create it from
+ * the provided template if not found.
+ *
+ * @method _setBox
+ * @private
+ *
+ * @param {Node|String} node The node reference
+ * @param {String} template HTML string template for the node
+ * @return {Node} The node
+ */
+ _setBox : function(node, template) {
+ node = Node.get(node) || Node.create(template);
+
+ var sid = Y.stamp(node);
+ if (!node.get(ID)) {
+ node.set(ID, sid);
+ }
+ return node;
+ },
+
+ /**
+ * Initializes the UI state for the Widget's bounding/content boxes.
+ *
+ * @method _renderUI
+ * @protected
+ * @param {Node} The parent node to rendering the widget into
+ */
+ _renderUI: function(parentNode) {
+ this._renderBoxClassNames();
+ this._renderBox(parentNode);
+ },
+
+ /**
+ * Applies standard class names to the boundingBox and contentBox
+ *
+ * @method _renderBoxClassNames
+ * @protected
+ */
+ _renderBoxClassNames : function() {
+ var classes = this._getClasses(),
+ boundingBox = this.get(BOUNDING_BOX),
+ contentBox = this.get(CONTENT_BOX),
+ name, i;
+
+ boundingBox.addClass(Widget.getClassName());
+
+ // Start from Widget Sub Class
+ for (i = classes.length-3; i >= 0; i--) {
+ name = classes[i].NAME;
+ if (name) {
+ boundingBox.addClass(ClassNameManager.getClassName(name.toLowerCase()));
+ }
+ }
+
+ // Use instance based name for content box
+ contentBox.addClass(this.getClassName(CONTENT));
+ },
+
+ /**
+ * Sets up DOM and CustomEvent listeners for the widget.
+ *
+ * @method _bindUI
+ * @protected
+ */
+ _bindUI: function() {
+ this.after('visibleChange', this._afterVisibleChange);
+ this.after('disabledChange', this._afterDisabledChange);
+ this.after('heightChange', this._afterHeightChange);
+ this.after('widthChange', this._afterWidthChange);
+ this.after('focusedChange', this._afterFocusedChange);
+
+ this._bindDOMListeners();
+ },
+
+ /**
+ * Sets up DOM listeners, on elements rendered by the widget.
+ *
+ * @method _bindDOMListeners
+ * @protected
+ */
+ _bindDOMListeners : function() {
+
+ var oDocument = this.get(BOUNDING_BOX).get("ownerDocument");
+
+ oDocument.on("focus", this._onFocus, this);
+
+ // Fix for Webkit:
+ // Document doesn't receive focus in Webkit when the user mouses
+ // down on it, so the "focused" attribute won't get set to the
+ // correct value.
+
+ if (Y.UA.webkit) {
+ oDocument.on("mousedown", this._onDocMouseDown, this);
+ }
+
+ },
+
+ /**
+ * Updates the widget UI to reflect the attribute state.
+ *
+ * @method _syncUI
+ * @protected
+ */
+ _syncUI: function() {
+ this._uiSetVisible(this.get(VISIBLE));
+ this._uiSetDisabled(this.get(DISABLED));
+ this._uiSetHeight(this.get(HEIGHT));
+ this._uiSetWidth(this.get(WIDTH));
+ this._uiSetFocused(this.get(FOCUSED));
+ this._uiSetTabIndex(this.get(TAB_INDEX));
+ },
+
+ /**
+ * Sets the height on the widget's bounding box element
+ *
+ * @method _uiSetHeight
+ * @protected
+ * @param {String | Number} val
+ */
+ _uiSetHeight: function(val) {
+ if (L.isNumber(val)) {
+ val = val + this.DEF_UNIT;
+ }
+ this.get(BOUNDING_BOX).setStyle(HEIGHT, val);
+ },
+
+ /**
+ * Sets the width on the widget's bounding box element
+ *
+ * @method _uiSetWidth
+ * @protected
+ * @param {String | Number} val
+ */
+ _uiSetWidth: function(val) {
+ if (L.isNumber(val)) {
+ val = val + this.DEF_UNIT;
+ }
+ this.get(BOUNDING_BOX).setStyle(WIDTH, val);
+ },
+
+ /**
+ * Sets the visible state for the UI
+ *
+ * @method _uiSetVisible
+ * @protected
+ * @param {boolean} val
+ */
+ _uiSetVisible: function(val) {
+
+ var box = this.get(BOUNDING_BOX),
+ sClassName = this.getClassName(HIDDEN);
+
+ if (val === true) {
+ box.removeClass(sClassName);
+ } else {
+ box.addClass(sClassName);
+ }
+ },
+
+ /**
+ * Sets the disabled state for the UI
+ *
+ * @protected
+ * @param {boolean} val
+ */
+ _uiSetDisabled: function(val) {
+
+ var box = this.get(BOUNDING_BOX),
+ sClassName = this.getClassName(DISABLED);
+
+ if (val === true) {
+ box.addClass(sClassName);
+ } else {
+ box.removeClass(sClassName);
+ }
+ },
+
+
+ /**
+ * Set the tabIndex on the widget's rendered UI
+ *
+ * @method _uiSetTabIndex
+ * @protected
+ * @param Number
+ */
+ _uiSetTabIndex: function(index) {
+
+ var boundingBox = this.get(BOUNDING_BOX);
+
+ if (L.isNumber(index)) {
+ boundingBox.set(TAB_INDEX, index);
+ }
+ else {
+ boundingBox.removeAttribute(TAB_INDEX);
+ }
+
+ },
+
+
+ /**
+ * Sets the focused state for the UI
+ *
+ * @protected
+ * @param {boolean} val
+ * @param {string} src String representing the source that triggered an update to
+ * the UI.
+ */
+ _uiSetFocused: function(val, src) {
+
+ var box = this.get(BOUNDING_BOX),
+ sClassName = this.getClassName(FOCUSED);
+
+ if (val === true) {
+ box.addClass(sClassName);
+ if (src !== UI) {
+ box.focus();
+ }
+ } else {
+ box.removeClass(sClassName);
+ if (src !== UI) {
+ box.blur();
+ }
+ }
+ },
+
+
+
+ /**
+ * Default visible attribute state change handler
+ *
+ * @method _afterVisibleChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterVisibleChange: function(evt) {
+ this._uiSetVisible(evt.newVal);
+ },
+
+ /**
+ * Default disabled attribute state change handler
+ *
+ * @method _afterDisabledChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterDisabledChange: function(evt) {
+ this._uiSetDisabled(evt.newVal);
+ },
+
+ /**
+ * Default height attribute state change handler
+ *
+ * @method _afterHeightChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterHeightChange: function(evt) {
+ this._uiSetHeight(evt.newVal);
+ },
+
+ /**
+ * Default widget attribute state change handler
+ *
+ * @method _afterWidthChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterWidthChange: function(evt) {
+ this._uiSetWidth(evt.newVal);
+ },
+
+ /**
+ * Default focused attribute state change handler
+ *
+ * @method _afterFocusedChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterFocusedChange: function(evt) {
+ this._uiSetFocused(evt.newVal, evt.src);
+ },
+
+ /**
+ * @method _onDocMouseDown
+ * @description "mousedown" event handler for the owner document of the
+ * widget's bounding box.
+ * @protected
+ * @param {EventFacade} evt The event facade for the DOM focus event
+ */
+ _onDocMouseDown: function (evt) {
+
+ if (this._hasDOMFocus) {
+ this._onFocus(evt);
+ }
+
+ },
+
+ /**
+ * DOM focus event handler, used to sync the state of the Widget with the DOM
+ *
+ * @method _onFocus
+ * @protected
+ * @param {EventFacade} evt The event facade for the DOM focus event
+ */
+ _onFocus: function (evt) {
+
+ var target = evt.target,
+ boundingBox = this.get(BOUNDING_BOX),
+ bFocused = (boundingBox.compareTo(target) || boundingBox.contains(target));
+
+ this._hasDOMFocus = bFocused;
+ this._set(FOCUSED, bFocused, { src: UI });
+
+ },
+
+ /**
+ * Generic toString implementation for all widgets.
+ *
+ * @method toString
+ * @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ]
+ */
+ toString: function() {
+ return this.constructor.NAME + "[" + this._yuid + "]";
+ },
+
+ /**
+ * Default unit to use for dimension values
+ *
+ * @property DEF_UNIT
+ */
+ DEF_UNIT : "px",
+
+ /**
+ * Static property defining the markup template for content box.
+ *
+ * @property CONTENT_TEMPLATE
+ * @type String
+ */
+ CONTENT_TEMPLATE : "<div></div>",
+
+ /**
+ * Static property defining the markup template for bounding box.
+ *
+ * @property BOUNDING_TEMPLATE
+ * @type String
+ */
+ BOUNDING_TEMPLATE : "<div></div>",
+
+ /**
+ * Static property listing the styles that are mimiced on the bounding box from the content box.
+ *
+ * @property WRAP_STYLES
+ * @type Object
+ */
+ WRAP_STYLES : {
+ height: '100%',
+ width: '100%',
+ zIndex: false,
+ position: 'static',
+ top: '0',
+ left: '0',
+ bottom: '',
+ right: '',
+ padding: '',
+ margin: ''
+ },
+
+ /**
+ * Sets strings for a particular locale, merging with any existing
+ * strings which may already be defined for the locale.
+ *
+ * @method _setStrings
+ * @protected
+ * @param {Object} strings The hash of string key/values to set
+ * @param {Object} locale The locale for the string values being set
+ */
+ _setStrings : function(strings, locale) {
+ var strs = this._strings;
+ locale = locale.toLowerCase();
+
+ if (!strs[locale]) {
+ strs[locale] = {};
+ }
+
+ Y.aggregate(strs[locale], strings, true);
+ return strs[locale];
+ },
+
+ /**
+ * Returns the strings key/value hash for a paricular locale, without locale lookup applied.
+ *
+ * @method _getStrings
+ * @protected
+ * @param {Object} locale
+ */
+ _getStrings : function(locale) {
+ return this._strings[locale.toLowerCase()];
+ },
+
+ /**
+ * Gets the entire strings hash for a particular locale, performing locale lookup.
+ * <p>
+ * If no values of the key are defined for a particular locale the value for the
+ * default locale (in initial locale set for the class) is returned.
+ * </p>
+ * @method getStrings
+ * @param {String} locale (optional) The locale for which the string value is required. Defaults to the current locale, if not provided.
+ */
+ // TODO: Optimize/Cache. Clear cache on _setStrings call.
+ getStrings : function(locale) {
+
+ locale = (locale || this.get(LOCALE)).toLowerCase();
+
+ Y.log("getStrings: For " + locale, "info", "widget");
+
+ var defLocale = this.getDefaultLocale().toLowerCase(),
+ defStrs = this._getStrings(defLocale),
+ strs = (defStrs) ? Y.merge(defStrs) : {},
+ localeSegments = locale.split(HYPHEN);
+
+ // If locale is different than the default, or needs lookup support
+ if (locale !== defLocale || localeSegments.length > 1) {
+ var lookup = "";
+ for (var i = 0, l = localeSegments.length; i < l; ++i) {
+ lookup += localeSegments[i];
+
+ Y.log("getStrings: Merging in strings from: " + lookup, "info", "widget");
+
+ var localeStrs = this._getStrings(lookup);
+ if (localeStrs) {
+ Y.aggregate(strs, localeStrs, true);
+ }
+ lookup += HYPHEN;
+ }
+ }
+
+ return strs;
+ },
+
+ /**
+ * Gets the string for a particular key, for a particular locale, performing locale lookup.
+ * <p>
+ * If no values if defined for the key, for the given locale, the value for the
+ * default locale (in initial locale set for the class) is returned.
+ * </p>
+ * @method getString
+ * @param {String} key The key.
+ * @param {String} locale (optional) The locale for which the string value is required. Defaults to the current locale, if not provided.
+ */
+ getString : function(key, locale) {
+
+ locale = (locale || this.get(LOCALE)).toLowerCase();
+
+ Y.log("getString: For " + locale, "info", "widget");
+
+ var defLocale = (this.getDefaultLocale()).toLowerCase(),
+ strs = this._getStrings(defLocale) || {},
+ str = strs[key],
+ idx = locale.lastIndexOf(HYPHEN);
+
+ // If locale is different than the default, or needs lookup support
+ if (locale !== defLocale || idx != -1) {
+ do {
+ Y.log("getString: Performing lookup for: " + locale, "info", "widget");
+
+ strs = this._getStrings(locale);
+ if (strs && key in strs) {
+ str = strs[key];
+ break;
+ }
+ idx = locale.lastIndexOf(HYPHEN);
+ // Chop of last locale segment
+ if (idx != -1) {
+ locale = locale.substring(0, idx);
+ }
+
+ } while (idx != -1);
+ }
+
+ return str;
+ },
+
+ /**
+ * Returns the default locale for the widget (the locale value defined by the
+ * widget class, or provided by the user during construction).
+ *
+ * @method getDefaultLocale
+ * @return {String} The default locale for the widget
+ */
+ getDefaultLocale : function() {
+ return this._conf.get(LOCALE, INIT_VALUE);
+ },
+
+ /**
+ * Private stings hash, used to store strings in locale specific buckets.
+ *
+ * @property _strings
+ * @private
+ * @type Object
+ */
+ _strings: null,
+
+ /**
+ * Gets the HTML_PARSER definition for this instance, by merging HTML_PARSER
+ * definitions across the class hierarchy.
+ *
+ * @method _getHtmlParser
+ * @return {Object} HTML_PARSER definition for this instance
+ */
+ _getHtmlParser : function() {
+ if (!this._HTML_PARSER) {
+ var classes = this._getClasses(),
+ parser = {},
+ i, p;
+
+ for (i = classes.length - 1; i >= 0; i--) {
+ p = classes[i].HTML_PARSER;
+ if (p) {
+ Y.mix(parser, p, true);
+ }
+ }
+
+ this._HTML_PARSER = parser;
+ }
+
+ return this._HTML_PARSER;
+ }
+});
+
+Y.Widget = Widget;
+
+
+}, '3.0.0' ,{requires:['attribute', 'event-focus', 'base', 'node', 'classnamemanager']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("widget",function(D){var Q=D.Lang,K=D.Object,g=D.Node,M=D.ClassNameManager,a="widget",J="content",c="visible",e="hidden",f="disabled",T="focused",G="width",X="height",i="",Z="-",h="boundingBox",U="contentBox",P="parentNode",B="firstChild",b="ownerDocument",d="body",C="tabIndex",F="locale",H="initValue",R="id",S="render",A="rendered",W="destroyed",I="contentUpdate",V={};function E(L){this._yuid=D.guid(a);this._strings={};E.superclass.constructor.apply(this,arguments);}E._buildCfg={aggregates:["HTML_PARSER"]};E.NAME=a;E.UI_SRC="ui";var N=E.UI_SRC;E.ATTRS={rendered:{value:false,readOnly:true},boundingBox:{value:null,setter:function(L){return this._setBoundingBox(L);},writeOnce:true},contentBox:{value:null,setter:function(L){return this._setContentBox(L);},writeOnce:true},tabIndex:{value:0,validator:function(L){return(Q.isNumber(L)||Q.isNull(L));}},focused:{value:false,readOnly:true},disabled:{value:false},visible:{value:true},height:{value:i},width:{value:i},moveStyles:{value:false},locale:{value:"en"},strings:{setter:function(L){return this._setStrings(L,this.get(F));},getter:function(){return this.getStrings(this.get(F));}}};E._NAME_LOWERCASE=E.NAME.toLowerCase();E.getClassName=function(){var L=D.Array(arguments,0,true);L.splice(0,0,this._NAME_LOWERCASE);return M.getClassName.apply(M,L);};E.getByNode=function(L){var O,Y=E.getClassName();L=g.get(L);if(L){L=(L.hasClass(Y))?L:L.ancestor("."+Y);if(L){O=V[L.get(R)];}}return O||null;};E.HTML_PARSER={};D.extend(E,D.Base,{getClassName:function(){var L=D.Array(arguments,0,true);L.splice(0,0,this._name);return M.getClassName.apply(M,L);},initializer:function(L){this.publish(I,{preventable:false});this._name=this.constructor.NAME.toLowerCase();var Y=this.get(h).get(R);if(Y){V[Y]=this;}var O=this._parseHTML(this.get(U));if(O){D.aggregate(L,O,false);}},destructor:function(){var L=this.get(h);D.Event.purgeElement(L,true);var O=L.get(R);if(O&&O in V){delete V[O];}},render:function(L){if(this.get(W)){return;}if(!this.get(A)){this.publish(S,{queuable:false,defaultFn:this._defRenderFn});L=(L)?g.get(L):null;if(L&&!L.inDoc()){L=null;}this.fire(S,{parentNode:L});}return this;},_defRenderFn:function(L){this._renderUI(L.parentNode);this._bindUI();this._syncUI();this.renderer();this._set(A,true);},renderer:function(){this.renderUI();this.bindUI();this.syncUI();},bindUI:function(){},renderUI:function(){},syncUI:function(){},hide:function(){return this.set(c,false);},show:function(){return this.set(c,true);},focus:function(){return this._set(T,true);},blur:function(){return this._set(T,false);},enable:function(){return this.set(f,false);},disable:function(){return this.set(f,true);},_parseHTML:function(O){var L=this._getHtmlParser(),Y,j;if(L&&O&&O.hasChildNodes()){K.each(L,function(m,l,n){j=null;if(Q.isFunction(m)){j=m.call(this,O);}else{if(Q.isArray(m)){j=O.queryAll(m[0]);}else{j=O.query(m);}}if(j!==null&&j!==undefined){Y=Y||{};Y[l]=j;}},this);}return Y;},_moveStyles:function(k,m){var j=this.WRAP_STYLES,n=k.getStyle("position"),O=this.get(U),l=[0,0],Y,L;if(!this.get("height")){Y=O.get("offsetHeight");}if(!this.get("width")){L=O.get("offsetWidth");}if(n==="absolute"){l=k.getXY();m.setStyles({right:"auto",bottom:"auto"});k.setStyles({right:"auto",bottom:"auto"});}D.each(j,function(p,o){var q=k.getStyle(o);m.setStyle(o,q);if(p===false){k.setStyle(o,"");}else{k.setStyle(o,p);}});if(n==="absolute"){m.setXY(l);}if(Y){this.set("height",Y);}if(L){this.set("width",L);}},_renderBox:function(O){var Y=this.get(U),j=this.get(h),k=j.get(b)||Y.get(b),L;if(!j.compareTo(Y.get(P))){if(this.get("moveStyles")){this._moveStyles(Y,j);}if(Y.inDoc(k)){Y.get(P).replaceChild(j,Y);}j.appendChild(Y);}if(!j.inDoc(k)&&!O){L=g.get(d);if(L.get(B)){L.insertBefore(j,L.get(B));}else{L.appendChild(j);}}else{if(O&&!O.compareTo(j.get(P))){O.appendChild(j);}}},_setBoundingBox:function(L){return this._setBox(L,this.BOUNDING_TEMPLATE);},_setContentBox:function(L){return this._setBox(L,this.CONTENT_TEMPLATE);},_setBox:function(Y,O){Y=g.get(Y)||g.create(O);var L=D.stamp(Y);if(!Y.get(R)){Y.set(R,L);}return Y;},_renderUI:function(L){this._renderBoxClassNames();this._renderBox(L);},_renderBoxClassNames:function(){var k=this._getClasses(),Y=this.get(h),L=this.get(U),O,j;Y.addClass(E.getClassName());for(j=k.length-3;j>=0;j--){O=k[j].NAME;if(O){Y.addClass(M.getClassName(O.toLowerCase()));}}L.addClass(this.getClassName(J));},_bindUI:function(){this.after("visibleChange",this._afterVisibleChange);this.after("disabledChange",this._afterDisabledChange);this.after("heightChange",this._afterHeightChange);this.after("widthChange",this._afterWidthChange);this.after("focusedChange",this._afterFocusedChange);this._bindDOMListeners();},_bindDOMListeners:function(){var L=this.get(h).get("ownerDocument");L.on("focus",this._onFocus,this);if(D.UA.webkit){L.on("mousedown",this._onDocMouseDown,this);}},_syncUI:function(){this._uiSetVisible(this.get(c));this._uiSetDisabled(this.get(f));this._uiSetHeight(this.get(X));this._uiSetWidth(this.get(G));this._uiSetFocused(this.get(T));this._uiSetTabIndex(this.get(C));},_uiSetHeight:function(L){if(Q.isNumber(L)){L=L+this.DEF_UNIT;}this.get(h).setStyle(X,L);},_uiSetWidth:function(L){if(Q.isNumber(L)){L=L+this.DEF_UNIT;}this.get(h).setStyle(G,L);},_uiSetVisible:function(Y){var O=this.get(h),L=this.getClassName(e);if(Y===true){O.removeClass(L);}else{O.addClass(L);}},_uiSetDisabled:function(Y){var O=this.get(h),L=this.getClassName(f);if(Y===true){O.addClass(L);}else{O.removeClass(L);}},_uiSetTabIndex:function(O){var L=this.get(h);if(Q.isNumber(O)){L.set(C,O);}else{L.removeAttribute(C);}},_uiSetFocused:function(j,Y){var O=this.get(h),L=this.getClassName(T);if(j===true){O.addClass(L);if(Y!==N){O.focus();}}else{O.removeClass(L);if(Y!==N){O.blur();}}},_afterVisibleChange:function(L){this._uiSetVisible(L.newVal);},_afterDisabledChange:function(L){this._uiSetDisabled(L.newVal);},_afterHeightChange:function(L){this._uiSetHeight(L.newVal);},_afterWidthChange:function(L){this._uiSetWidth(L.newVal);
+},_afterFocusedChange:function(L){this._uiSetFocused(L.newVal,L.src);},_onDocMouseDown:function(L){if(this._hasDOMFocus){this._onFocus(L);}},_onFocus:function(O){var j=O.target,Y=this.get(h),L=(Y.compareTo(j)||Y.contains(j));this._hasDOMFocus=L;this._set(T,L,{src:N});},toString:function(){return this.constructor.NAME+"["+this._yuid+"]";},DEF_UNIT:"px",CONTENT_TEMPLATE:"<div></div>",BOUNDING_TEMPLATE:"<div></div>",WRAP_STYLES:{height:"100%",width:"100%",zIndex:false,position:"static",top:"0",left:"0",bottom:"",right:"",padding:"",margin:""},_setStrings:function(O,L){var Y=this._strings;L=L.toLowerCase();if(!Y[L]){Y[L]={};}D.aggregate(Y[L],O,true);return Y[L];},_getStrings:function(L){return this._strings[L.toLowerCase()];},getStrings:function(p){p=(p||this.get(F)).toLowerCase();var n=this.getDefaultLocale().toLowerCase(),O=this._getStrings(n),o=(O)?D.merge(O):{},m=p.split(Z);if(p!==n||m.length>1){var L="";for(var j=0,Y=m.length;j<Y;++j){L+=m[j];var k=this._getStrings(L);if(k){D.aggregate(o,k,true);}L+=Z;}}return o;},getString:function(Y,O){O=(O||this.get(F)).toLowerCase();var j=(this.getDefaultLocale()).toLowerCase(),k=this._getStrings(j)||{},l=k[Y],L=O.lastIndexOf(Z);if(O!==j||L!=-1){do{k=this._getStrings(O);if(k&&Y in k){l=k[Y];break;}L=O.lastIndexOf(Z);if(L!=-1){O=O.substring(0,L);}}while(L!=-1);}return l;},getDefaultLocale:function(){return this._conf.get(F,H);},_strings:null,_getHtmlParser:function(){if(!this._HTML_PARSER){var O=this._getClasses(),j={},L,Y;for(L=O.length-1;L>=0;L--){Y=O[L].HTML_PARSER;if(Y){D.mix(j,Y,true);}}this._HTML_PARSER=j;}return this._HTML_PARSER;}});D.Widget=E;},"3.0.0",{requires:["attribute","event-focus","base","node","classnamemanager"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-position', function(Y) {
+
+/**
+ * Provides basic XY positioning support for Widgets, though an extension
+ *
+ * @module widget-position
+ */
+ var Lang = Y.Lang,
+ Widget = Y.Widget,
+
+ XY_COORD = "xy",
+
+ POSITIONED = "positioned",
+ BOUNDING_BOX = "boundingBox",
+
+ RENDERUI = "renderUI",
+ BINDUI = "bindUI",
+ SYNCUI = "syncUI",
+
+ UI = Widget.UI_SRC,
+
+ XYChange = "xyChange";
+
+ /**
+ * Widget extension, which can be used to add positioning support to the base Widget class,
+ * through the <a href="Base.html#method_build">Base.build</a> method.
+ *
+ * @class WidgetPosition
+ * @param {Object} config User configuration object
+ */
+ function Position(config) {
+ this._posNode = this.get(BOUNDING_BOX);
+
+ // WIDGET METHOD OVERLAP
+ Y.after(this._renderUIPosition, this, RENDERUI);
+ Y.after(this._syncUIPosition, this, SYNCUI);
+ Y.after(this._bindUIPosition, this, BINDUI);
+ }
+
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetPosition.
+ *
+ * @property WidgetPosition.ATTRS
+ * @static
+ * @type Object
+ */
+ Position.ATTRS = {
+
+ /**
+ * @attribute x
+ * @type number
+ * @default 0
+ *
+ * @description Page X co-ordinate for the widget. This attribute acts as a facade for the
+ * xy attribute. Changes in position can be monitored by listening for xyChange events.
+ */
+ x: {
+ setter: function(val) {
+ this._setX(val);
+ },
+ lazyAdd:false,
+ getter: function() {
+ return this._getX();
+ }
+ },
+
+ /**
+ * @attribute y
+ * @type number
+ * @default 0
+ *
+ * @description Page Y co-ordinate for the widget. This attribute acts as a facade for the
+ * xy attribute. Changes in position can be monitored by listening for xyChange events.
+ */
+ y: {
+ setter: function(val) {
+ this._setY(val);
+ },
+ lazyAdd: false,
+ getter: function() {
+ return this._getY();
+ }
+ },
+
+ /**
+ * @attribute xy
+ * @type Array
+ * @default [0,0]
+ *
+ * @description Page XY co-ordinate pair for the widget.
+ */
+ xy: {
+ value:[0,0],
+
+ validator: function(val) {
+ return this._validateXY(val);
+ }
+ }
+ };
+
+ /**
+ * Default class used to mark the boundingBox of a positioned widget.
+ *
+ * @property WidgetPosition.POSITIONED_CLASS_NAME
+ * @type String
+ * @default "yui-widget-positioned"
+ * @static
+ */
+ Position.POSITIONED_CLASS_NAME = Widget.getClassName(POSITIONED);
+
+ Position.prototype = {
+
+ /**
+ * Creates/Initializes the DOM to support xy page positioning.
+ * <p>
+ * This method in invoked after renderUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _renderUIPosition
+ * @protected
+ */
+ _renderUIPosition : function() {
+ this._posNode.addClass(Position.POSITIONED_CLASS_NAME);
+ },
+
+ /**
+ * Synchronizes the UI to match the Widgets xy page position state.
+ * <p>
+ * This method in invoked after syncUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _syncUIPosition
+ * @protected
+ */
+ _syncUIPosition : function() {
+ this._uiSetXY(this.get(XY_COORD));
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget position related state changes.
+ * <p>
+ * This method in invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIPosition
+ * @protected
+ */
+ _bindUIPosition :function() {
+ this.after(XYChange, this._afterXYChange);
+ },
+
+ /**
+ * Moves the Widget to the specified page xy co-ordinate position.
+ *
+ * @method move
+ *
+ * @param {Number} x The new x position
+ * @param {Number} y The new y position
+ * <p>Or</p>
+ * @param {Array} x, y values passed as an array ([x, y]), to support
+ * simple pass through of Node.getXY results
+ */
+ move: function () {
+ var args = arguments,
+ coord = (Lang.isArray(args[0])) ? args[0] : [args[0], args[1]];
+ this.set(XY_COORD, coord);
+ },
+
+ /**
+ * Synchronizes the Panel's "xy", "x", and "y" properties with the
+ * Widget's position in the DOM.
+ *
+ * @method syncXY
+ */
+ syncXY : function () {
+ this.set(XY_COORD, this._posNode.getXY(), {src: UI});
+ },
+
+ /**
+ * Default validator for the XY attribute
+ *
+ * @method _validateXY
+ * @param {Array} val The XY page co-ordinate value which is being set.
+ * @return {boolean} true if valid, false if not.
+ */
+ _validateXY : function(val) {
+ return (Lang.isArray(val) && Lang.isNumber(val[0]) && Lang.isNumber(val[1]));
+ },
+
+ /**
+ * Default setter for the X attribute. The setter passes the X value through
+ * to the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _setX
+ * @param {Number} val The X page co-ordinate value
+ */
+ _setX : function(val) {
+ this.set(XY_COORD, [val, this.get(XY_COORD)[1]]);
+ },
+
+ /**
+ * Default setter for the Y attribute. The setter passes the Y value through
+ * to the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _setY
+ * @param {Number} val The Y page co-ordinate value
+ */
+ _setY : function(val) {
+ this.set(XY_COORD, [this.get(XY_COORD)[0], val]);
+ },
+
+ /**
+ * Default getter for the X attribute. The value is retrieved from
+ * the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _getX
+ * @return {Number} The X page co-ordinate value
+ */
+ _getX : function() {
+ return this.get(XY_COORD)[0];
+ },
+
+ /**
+ * Default getter for the Y attribute. The value is retrieved from
+ * the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _getY
+ * @return {Number} The Y page co-ordinate value
+ */
+ _getY : function() {
+ return this.get(XY_COORD)[1];
+ },
+
+ /**
+ * Default attribute change listener for the xy attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterXYChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterXYChange : function(e) {
+ if (e.src != UI) {
+ this._uiSetXY(e.newVal);
+ }
+ },
+
+ /**
+ * Updates the UI to reflect the XY page co-ordinates passed in.
+ *
+ * @method _uiSetXY
+ * @protected
+ * @param {String} val The XY page co-ordinates value to be reflected in the UI
+ */
+ _uiSetXY : function(val) {
+ this._posNode.setXY(val);
+ }
+ };
+
+ Y.WidgetPosition = Position;
+
+
+}, '3.0.0' ,{requires:['widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-position-ext', function(Y) {
+
+/**
+ * Provides extended/advanced XY positioning support for Widgets, through an extension.
+ *
+ * It builds on top of the widget-position module, to provide alignmentment and centering support.
+ * Future releases aim to add constrained and fixed positioning support.
+ *
+ * @module widget-position-ext
+ */
+ var L = Y.Lang,
+ ALIGN = "align",
+
+ BINDUI = "bindUI",
+ SYNCUI = "syncUI",
+
+ OFFSET_WIDTH = "offsetWidth",
+ OFFSET_HEIGHT = "offsetHeight",
+ VIEWPORT_REGION = "viewportRegion",
+ REGION = "region",
+
+ AlignChange = "alignChange";
+
+ /**
+ * Widget extension, which can be used to add extended XY positioning support to the base Widget class,
+ * through the <a href="Base.html#method_build">Base.build</a> method. This extension requires that
+ * the WidgetPosition extension be added to the Widget (before WidgetPositionExt, if part of the same
+ * extension list passed to Base.build).
+ *
+ * @class WidgetPositionExt
+ * @param {Object} User configuration object
+ */
+ function PositionExt(config) {
+ if (!this._posNode) {
+ Y.error("WidgetPosition needs to be added to the Widget, before WidgetPositionExt is added");
+ }
+ Y.after(this._syncUIPosExtras, this, SYNCUI);
+ Y.after(this._bindUIPosExtras, this, BINDUI);
+ }
+
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetPositionExt.
+ *
+ * @property WidgetPositionExt.ATTRS
+ * @type Object
+ * @static
+ */
+ PositionExt.ATTRS = {
+
+ /**
+ * @attribute align
+ * @type Object
+ * @default null
+ * @desciption The align attribute is used to align a reference point on the widget, with the refernce point on another node, or the viewport.
+ * The object which align expects has the following properties:
+ * <dl>
+ * <dt>node</dt>
+ * <dd>
+ * The node to which the Widget is to be aligned. If set to null, or not provided, the Widget is aligned to the viewport
+ * </dd>
+ * <dt>points</dt>
+ * <dd>
+ * <p>
+ * A two element array, defining the two points on the Widget and node/viewport which are to be aligned. The first element is the point on the Widget, and the second element is the point on the node/viewport.
+ * Supported alignment points are defined as static properties on <code>WidgetPositionExt</code>.
+ * </p>
+ * <p>
+ * e.g. <code>[WidgetPositionExt.TR, WidgetPositionExt.TL]</code> aligns the Top-Right corner of the Widget with the
+ * Top-Left corner of the node/viewport, and <code>[WidgetPositionExt.CC, WidgetPositionExt.TC]</code> aligns the Center of the
+ * Widget with the Top-Center edge of the node/viewport.
+ * </p>
+ * </dd>
+ * </dl>
+ */
+ align: {
+ value:null
+ },
+
+ /**
+ * @attribute centered
+ * @type {boolean | node}
+ * @default false
+ * @description A convenience attribute, which can be used as a shortcut for the align attribute.
+ * If set to true, the Widget is centered in the viewport. If set to a node reference or valid selector string,
+ * the Widget will be centered within the node. If set the false, no center positioning is applied.
+ */
+ centered: {
+ setter: function(val) {
+ return this._setAlignCenter(val);
+ },
+ lazyAdd:false,
+ value:false
+ }
+ };
+
+ /**
+ * Constant used to specify the top-left corner for alignment
+ *
+ * @property WidgetPositionExt.TL
+ * @type String
+ * @static
+ * @value "tl"
+ */
+ PositionExt.TL = "tl";
+ /**
+ * Constant used to specify the top-right corner for alignment
+ *
+ * @property WidgetPositionExt.TR
+ * @type String
+ * @static
+ * @value "tr"
+ */
+ PositionExt.TR = "tr";
+ /**
+ * Constant used to specify the bottom-left corner for alignment
+ *
+ * @property WidgetPositionExt.BL
+ * @type String
+ * @static
+ * @value "bl"
+ */
+ PositionExt.BL = "bl";
+ /**
+ * Constant used to specify the bottom-right corner for alignment
+ *
+ * @property WidgetPositionExt.BR
+ * @type String
+ * @static
+ * @value "br"
+ */
+ PositionExt.BR = "br";
+ /**
+ * Constant used to specify the top edge-center point for alignment
+ *
+ * @property WidgetPositionExt.TC
+ * @type String
+ * @static
+ * @value "tc"
+ */
+ PositionExt.TC = "tc";
+ /**
+ * Constant used to specify the right edge, center point for alignment
+ *
+ * @property WidgetPositionExt.RC
+ * @type String
+ * @static
+ * @value "rc"
+ */
+ PositionExt.RC = "rc";
+ /**
+ * Constant used to specify the bottom edge, center point for alignment
+ *
+ * @property WidgetPositionExt.BC
+ * @type String
+ * @static
+ * @value "bc"
+ */
+ PositionExt.BC = "bc";
+ /**
+ * Constant used to specify the left edge, center point for alignment
+ *
+ * @property WidgetPositionExt.LC
+ * @type String
+ * @static
+ * @value "lc"
+ */
+ PositionExt.LC = "lc";
+ /**
+ * Constant used to specify the center of widget/node/viewport for alignment
+ *
+ * @property WidgetPositionExt.CC
+ * @type String
+ * @static
+ * @value "cc"
+ */
+ PositionExt.CC = "cc";
+
+ PositionExt.prototype = {
+
+ /**
+ * Synchronizes the UI to match the Widgets extended positioning state.
+ * This method in invoked after syncUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ *
+ * @method _syncUIPosExtras
+ * @protected
+ */
+ _syncUIPosExtras : function() {
+ var align = this.get(ALIGN);
+ if (align) {
+ this._uiSetAlign(align.node, align.points);
+ }
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget extended positioning related state changes.
+ * <p>
+ * This method is invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIStack
+ * @protected
+ */
+ _bindUIPosExtras : function() {
+ this.after(AlignChange, this._afterAlignChange);
+ },
+
+ /**
+ * Default setter for center attribute changes. Sets up the appropriate value, and passes
+ * it through the to the align attribute.
+ *
+ * @method _setAlignCenter
+ * @protected
+ * @param {boolean | node} The attribute value being set.
+ * @return {Number} The attribute value being set.
+ */
+ _setAlignCenter : function(val) {
+ if (val) {
+ this.set(ALIGN, {
+ node: val === true ? null : val,
+ points: [PositionExt.CC, PositionExt.CC]
+ });
+ }
+ return val;
+ },
+
+ /**
+ * Default attribute change listener for the align attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterAlignChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterAlignChange : function(e) {
+ if (e.newVal) {
+ this._uiSetAlign(e.newVal.node, e.newVal.points);
+ }
+ },
+
+ /**
+ * Updates the UI to reflect the align value passed in (see the align attribute documentation, for the object stucture expected)
+ * @method _uiSetAlign
+ * @protected
+ * @param {Node | null} The node to align to, or null to indicate the viewport
+ */
+ _uiSetAlign: function (node, points) {
+
+ if (!L.isArray(points) || points.length != 2) {
+ Y.error("align: Invalid Points Arguments");
+ return;
+ }
+
+ var nodeRegion, widgetPoint, nodePoint, xy;
+
+ if (!node) {
+ nodeRegion = this._posNode.get(VIEWPORT_REGION);
+ } else {
+ node = Y.Node.get(node);
+ if (node) {
+ nodeRegion = node.get(REGION);
+ }
+ }
+
+ if (nodeRegion) {
+
+ // TODO: ViewportRegion doesn't have width/height - Workaround until normalized in Node/Dom
+ nodeRegion.width = nodeRegion.width || nodeRegion.right - nodeRegion.left;
+ nodeRegion.height = nodeRegion.height || nodeRegion.bottom - nodeRegion.top;
+
+ widgetPoint = points[0];
+ nodePoint = points[1];
+
+ // TODO: Optimize KWeight - Would lookup table help?
+ switch (nodePoint) {
+ case PositionExt.TL:
+ xy = [nodeRegion.left, nodeRegion.top];
+ break;
+ case PositionExt.TR:
+ xy = [nodeRegion.right, nodeRegion.top];
+ break;
+ case PositionExt.BL:
+ xy = [nodeRegion.left, nodeRegion.bottom];
+ break;
+ case PositionExt.BR:
+ xy = [nodeRegion.right, nodeRegion.bottom];
+ break;
+ case PositionExt.TC:
+ xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.top];
+ break;
+ case PositionExt.BC:
+ xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.bottom];
+ break;
+ case PositionExt.LC:
+ xy = [nodeRegion.left, nodeRegion.top + Math.floor(nodeRegion.height/2)];
+ break;
+ case PositionExt.RC:
+ xy = [nodeRegion.right, nodeRegion.top + Math.floor(nodeRegion.height/2), widgetPoint];
+ break;
+ case PositionExt.CC:
+ xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.top + Math.floor(nodeRegion.height/2), widgetPoint];
+ break;
+ default:
+ Y.log("align: Invalid Points Arguments", "info", "widget-position-extras");
+ break;
+ }
+
+ if (xy) {
+ this._doAlign(widgetPoint, xy[0], xy[1]);
+ }
+ }
+ },
+
+ /**
+ * Helper method, used to align the given point on the widget, with the XY page co-ordinates provided.
+ *
+ * @method _doAlign
+ * @private
+ * @param {String} widgetPoint Supported point constant (e.g. WidgetPositionExt.TL)
+ * @param {Number} x X page co-ordinate to align to
+ * @param {Number} y Y page co-ordinate to align to
+ */
+ _doAlign : function(widgetPoint, x, y) {
+ var widgetNode = this._posNode,
+ xy;
+
+ switch (widgetPoint) {
+ case PositionExt.TL:
+ xy = [x, y];
+ break;
+ case PositionExt.TR:
+ xy = [x - widgetNode.get(OFFSET_WIDTH), y];
+ break;
+ case PositionExt.BL:
+ xy = [x, y - widgetNode.get(OFFSET_HEIGHT)];
+ break;
+ case PositionExt.BR:
+ xy = [x - widgetNode.get(OFFSET_WIDTH), y - widgetNode.get(OFFSET_HEIGHT)];
+ break;
+ case PositionExt.TC:
+ xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y];
+ break;
+ case PositionExt.BC:
+ xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y - widgetNode.get(OFFSET_HEIGHT)];
+ break;
+ case PositionExt.LC:
+ xy = [x, y - (widgetNode.get(OFFSET_HEIGHT)/2)];
+ break;
+ case PositionExt.RC:
+ xy = [(x - widgetNode.get(OFFSET_WIDTH)), y - (widgetNode.get(OFFSET_HEIGHT)/2)];
+ break;
+ case PositionExt.CC:
+ xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y - (widgetNode.get(OFFSET_HEIGHT)/2)];
+ break;
+ default:
+ Y.log("align: Invalid Points Argument", "info", "widget-position-extras");
+ break;
+ }
+
+ if (xy) {
+ this.move(xy);
+ }
+ },
+
+ /**
+ * Aligns the Widget to the provided node (or viewport) using the provided
+ * points. The method can be invoked directly, however it will result in
+ * the align attribute being out of sync with current position of the of Widget.
+ *
+ * @method align
+ * @param {Node | String | null} node A reference (or selector string) for the Node which with the Widget is to be aligned.
+ * If null is passed in, the Widget will be aligned with the viewport.
+ * @param {Array[2]} points A two element array, specifying the points on the Widget and node/viewport which need to be aligned.
+ * The first entry is the point on the Widget, and the second entry is the point on the node/viewport which need to align.
+ * Valid point references are defined as static constants on the WidgetPositionExt class.
+ *
+ * e.g. [WidgetPositionExt.TL, WidgetPositionExt.TR] will align the top-left corner of the Widget with the top-right corner of the node/viewport.
+ */
+ align: function (node, points) {
+ this.set(ALIGN, {node: node, points:points});
+ },
+
+ /**
+ * Centers the container in the viewport, or if a node is passed in,
+ * the node.
+ *
+ * @method centered
+ * @param {Node | String} node Optional. A node reference or selector string defining the node
+ * inside which the Widget is to be centered. If not passed in, the Widget will be centered in the
+ * viewport.
+ */
+ centered: function (node) {
+ this.align(node, [PositionExt.CC, PositionExt.CC]);
+ }
+ };
+
+ Y.WidgetPositionExt = PositionExt;
+
+
+}, '3.0.0' ,{requires:['widget', 'widget-position']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("widget-position-ext",function(A){var H=A.Lang,C="align",E="bindUI",B="syncUI",D="offsetWidth",I="offsetHeight",K="viewportRegion",G="region",J="alignChange";function F(L){if(!this._posNode){A.error("WidgetPosition needs to be added to the Widget, before WidgetPositionExt is added");}A.after(this._syncUIPosExtras,this,B);A.after(this._bindUIPosExtras,this,E);}F.ATTRS={align:{value:null},centered:{setter:function(L){return this._setAlignCenter(L);},lazyAdd:false,value:false}};F.TL="tl";F.TR="tr";F.BL="bl";F.BR="br";F.TC="tc";F.RC="rc";F.BC="bc";F.LC="lc";F.CC="cc";F.prototype={_syncUIPosExtras:function(){var L=this.get(C);if(L){this._uiSetAlign(L.node,L.points);}},_bindUIPosExtras:function(){this.after(J,this._afterAlignChange);},_setAlignCenter:function(L){if(L){this.set(C,{node:L===true?null:L,points:[F.CC,F.CC]});}return L;},_afterAlignChange:function(L){if(L.newVal){this._uiSetAlign(L.newVal.node,L.newVal.points);}},_uiSetAlign:function(O,N){if(!H.isArray(N)||N.length!=2){A.error("align: Invalid Points Arguments");return;}var M,L,P,Q;if(!O){M=this._posNode.get(K);}else{O=A.Node.get(O);if(O){M=O.get(G);}}if(M){M.width=M.width||M.right-M.left;M.height=M.height||M.bottom-M.top;L=N[0];P=N[1];switch(P){case F.TL:Q=[M.left,M.top];break;case F.TR:Q=[M.right,M.top];break;case F.BL:Q=[M.left,M.bottom];break;case F.BR:Q=[M.right,M.bottom];break;case F.TC:Q=[M.left+Math.floor(M.width/2),M.top];break;case F.BC:Q=[M.left+Math.floor(M.width/2),M.bottom];break;case F.LC:Q=[M.left,M.top+Math.floor(M.height/2)];break;case F.RC:Q=[M.right,M.top+Math.floor(M.height/2),L];break;case F.CC:Q=[M.left+Math.floor(M.width/2),M.top+Math.floor(M.height/2),L];break;default:break;}if(Q){this._doAlign(L,Q[0],Q[1]);}}},_doAlign:function(M,L,P){var O=this._posNode,N;switch(M){case F.TL:N=[L,P];break;case F.TR:N=[L-O.get(D),P];break;case F.BL:N=[L,P-O.get(I)];break;case F.BR:N=[L-O.get(D),P-O.get(I)];break;case F.TC:N=[L-(O.get(D)/2),P];break;case F.BC:N=[L-(O.get(D)/2),P-O.get(I)];break;case F.LC:N=[L,P-(O.get(I)/2)];break;case F.RC:N=[(L-O.get(D)),P-(O.get(I)/2)];break;case F.CC:N=[L-(O.get(D)/2),P-(O.get(I)/2)];break;default:break;}if(N){this.move(N);}},align:function(M,L){this.set(C,{node:M,points:L});},centered:function(L){this.align(L,[F.CC,F.CC]);}};A.WidgetPositionExt=F;},"3.0.0",{requires:["widget","widget-position"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-position-ext', function(Y) {
+
+/**
+ * Provides extended/advanced XY positioning support for Widgets, through an extension.
+ *
+ * It builds on top of the widget-position module, to provide alignmentment and centering support.
+ * Future releases aim to add constrained and fixed positioning support.
+ *
+ * @module widget-position-ext
+ */
+ var L = Y.Lang,
+ ALIGN = "align",
+
+ BINDUI = "bindUI",
+ SYNCUI = "syncUI",
+
+ OFFSET_WIDTH = "offsetWidth",
+ OFFSET_HEIGHT = "offsetHeight",
+ VIEWPORT_REGION = "viewportRegion",
+ REGION = "region",
+
+ AlignChange = "alignChange";
+
+ /**
+ * Widget extension, which can be used to add extended XY positioning support to the base Widget class,
+ * through the <a href="Base.html#method_build">Base.build</a> method. This extension requires that
+ * the WidgetPosition extension be added to the Widget (before WidgetPositionExt, if part of the same
+ * extension list passed to Base.build).
+ *
+ * @class WidgetPositionExt
+ * @param {Object} User configuration object
+ */
+ function PositionExt(config) {
+ if (!this._posNode) {
+ Y.error("WidgetPosition needs to be added to the Widget, before WidgetPositionExt is added");
+ }
+ Y.after(this._syncUIPosExtras, this, SYNCUI);
+ Y.after(this._bindUIPosExtras, this, BINDUI);
+ }
+
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetPositionExt.
+ *
+ * @property WidgetPositionExt.ATTRS
+ * @type Object
+ * @static
+ */
+ PositionExt.ATTRS = {
+
+ /**
+ * @attribute align
+ * @type Object
+ * @default null
+ * @desciption The align attribute is used to align a reference point on the widget, with the refernce point on another node, or the viewport.
+ * The object which align expects has the following properties:
+ * <dl>
+ * <dt>node</dt>
+ * <dd>
+ * The node to which the Widget is to be aligned. If set to null, or not provided, the Widget is aligned to the viewport
+ * </dd>
+ * <dt>points</dt>
+ * <dd>
+ * <p>
+ * A two element array, defining the two points on the Widget and node/viewport which are to be aligned. The first element is the point on the Widget, and the second element is the point on the node/viewport.
+ * Supported alignment points are defined as static properties on <code>WidgetPositionExt</code>.
+ * </p>
+ * <p>
+ * e.g. <code>[WidgetPositionExt.TR, WidgetPositionExt.TL]</code> aligns the Top-Right corner of the Widget with the
+ * Top-Left corner of the node/viewport, and <code>[WidgetPositionExt.CC, WidgetPositionExt.TC]</code> aligns the Center of the
+ * Widget with the Top-Center edge of the node/viewport.
+ * </p>
+ * </dd>
+ * </dl>
+ */
+ align: {
+ value:null
+ },
+
+ /**
+ * @attribute centered
+ * @type {boolean | node}
+ * @default false
+ * @description A convenience attribute, which can be used as a shortcut for the align attribute.
+ * If set to true, the Widget is centered in the viewport. If set to a node reference or valid selector string,
+ * the Widget will be centered within the node. If set the false, no center positioning is applied.
+ */
+ centered: {
+ setter: function(val) {
+ return this._setAlignCenter(val);
+ },
+ lazyAdd:false,
+ value:false
+ }
+ };
+
+ /**
+ * Constant used to specify the top-left corner for alignment
+ *
+ * @property WidgetPositionExt.TL
+ * @type String
+ * @static
+ * @value "tl"
+ */
+ PositionExt.TL = "tl";
+ /**
+ * Constant used to specify the top-right corner for alignment
+ *
+ * @property WidgetPositionExt.TR
+ * @type String
+ * @static
+ * @value "tr"
+ */
+ PositionExt.TR = "tr";
+ /**
+ * Constant used to specify the bottom-left corner for alignment
+ *
+ * @property WidgetPositionExt.BL
+ * @type String
+ * @static
+ * @value "bl"
+ */
+ PositionExt.BL = "bl";
+ /**
+ * Constant used to specify the bottom-right corner for alignment
+ *
+ * @property WidgetPositionExt.BR
+ * @type String
+ * @static
+ * @value "br"
+ */
+ PositionExt.BR = "br";
+ /**
+ * Constant used to specify the top edge-center point for alignment
+ *
+ * @property WidgetPositionExt.TC
+ * @type String
+ * @static
+ * @value "tc"
+ */
+ PositionExt.TC = "tc";
+ /**
+ * Constant used to specify the right edge, center point for alignment
+ *
+ * @property WidgetPositionExt.RC
+ * @type String
+ * @static
+ * @value "rc"
+ */
+ PositionExt.RC = "rc";
+ /**
+ * Constant used to specify the bottom edge, center point for alignment
+ *
+ * @property WidgetPositionExt.BC
+ * @type String
+ * @static
+ * @value "bc"
+ */
+ PositionExt.BC = "bc";
+ /**
+ * Constant used to specify the left edge, center point for alignment
+ *
+ * @property WidgetPositionExt.LC
+ * @type String
+ * @static
+ * @value "lc"
+ */
+ PositionExt.LC = "lc";
+ /**
+ * Constant used to specify the center of widget/node/viewport for alignment
+ *
+ * @property WidgetPositionExt.CC
+ * @type String
+ * @static
+ * @value "cc"
+ */
+ PositionExt.CC = "cc";
+
+ PositionExt.prototype = {
+
+ /**
+ * Synchronizes the UI to match the Widgets extended positioning state.
+ * This method in invoked after syncUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ *
+ * @method _syncUIPosExtras
+ * @protected
+ */
+ _syncUIPosExtras : function() {
+ var align = this.get(ALIGN);
+ if (align) {
+ this._uiSetAlign(align.node, align.points);
+ }
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget extended positioning related state changes.
+ * <p>
+ * This method is invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIStack
+ * @protected
+ */
+ _bindUIPosExtras : function() {
+ this.after(AlignChange, this._afterAlignChange);
+ },
+
+ /**
+ * Default setter for center attribute changes. Sets up the appropriate value, and passes
+ * it through the to the align attribute.
+ *
+ * @method _setAlignCenter
+ * @protected
+ * @param {boolean | node} The attribute value being set.
+ * @return {Number} The attribute value being set.
+ */
+ _setAlignCenter : function(val) {
+ if (val) {
+ this.set(ALIGN, {
+ node: val === true ? null : val,
+ points: [PositionExt.CC, PositionExt.CC]
+ });
+ }
+ return val;
+ },
+
+ /**
+ * Default attribute change listener for the align attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterAlignChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterAlignChange : function(e) {
+ if (e.newVal) {
+ this._uiSetAlign(e.newVal.node, e.newVal.points);
+ }
+ },
+
+ /**
+ * Updates the UI to reflect the align value passed in (see the align attribute documentation, for the object stucture expected)
+ * @method _uiSetAlign
+ * @protected
+ * @param {Node | null} The node to align to, or null to indicate the viewport
+ */
+ _uiSetAlign: function (node, points) {
+
+ if (!L.isArray(points) || points.length != 2) {
+ Y.error("align: Invalid Points Arguments");
+ return;
+ }
+
+ var nodeRegion, widgetPoint, nodePoint, xy;
+
+ if (!node) {
+ nodeRegion = this._posNode.get(VIEWPORT_REGION);
+ } else {
+ node = Y.Node.get(node);
+ if (node) {
+ nodeRegion = node.get(REGION);
+ }
+ }
+
+ if (nodeRegion) {
+
+ // TODO: ViewportRegion doesn't have width/height - Workaround until normalized in Node/Dom
+ nodeRegion.width = nodeRegion.width || nodeRegion.right - nodeRegion.left;
+ nodeRegion.height = nodeRegion.height || nodeRegion.bottom - nodeRegion.top;
+
+ widgetPoint = points[0];
+ nodePoint = points[1];
+
+ // TODO: Optimize KWeight - Would lookup table help?
+ switch (nodePoint) {
+ case PositionExt.TL:
+ xy = [nodeRegion.left, nodeRegion.top];
+ break;
+ case PositionExt.TR:
+ xy = [nodeRegion.right, nodeRegion.top];
+ break;
+ case PositionExt.BL:
+ xy = [nodeRegion.left, nodeRegion.bottom];
+ break;
+ case PositionExt.BR:
+ xy = [nodeRegion.right, nodeRegion.bottom];
+ break;
+ case PositionExt.TC:
+ xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.top];
+ break;
+ case PositionExt.BC:
+ xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.bottom];
+ break;
+ case PositionExt.LC:
+ xy = [nodeRegion.left, nodeRegion.top + Math.floor(nodeRegion.height/2)];
+ break;
+ case PositionExt.RC:
+ xy = [nodeRegion.right, nodeRegion.top + Math.floor(nodeRegion.height/2), widgetPoint];
+ break;
+ case PositionExt.CC:
+ xy = [nodeRegion.left + Math.floor(nodeRegion.width/2), nodeRegion.top + Math.floor(nodeRegion.height/2), widgetPoint];
+ break;
+ default:
+ break;
+ }
+
+ if (xy) {
+ this._doAlign(widgetPoint, xy[0], xy[1]);
+ }
+ }
+ },
+
+ /**
+ * Helper method, used to align the given point on the widget, with the XY page co-ordinates provided.
+ *
+ * @method _doAlign
+ * @private
+ * @param {String} widgetPoint Supported point constant (e.g. WidgetPositionExt.TL)
+ * @param {Number} x X page co-ordinate to align to
+ * @param {Number} y Y page co-ordinate to align to
+ */
+ _doAlign : function(widgetPoint, x, y) {
+ var widgetNode = this._posNode,
+ xy;
+
+ switch (widgetPoint) {
+ case PositionExt.TL:
+ xy = [x, y];
+ break;
+ case PositionExt.TR:
+ xy = [x - widgetNode.get(OFFSET_WIDTH), y];
+ break;
+ case PositionExt.BL:
+ xy = [x, y - widgetNode.get(OFFSET_HEIGHT)];
+ break;
+ case PositionExt.BR:
+ xy = [x - widgetNode.get(OFFSET_WIDTH), y - widgetNode.get(OFFSET_HEIGHT)];
+ break;
+ case PositionExt.TC:
+ xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y];
+ break;
+ case PositionExt.BC:
+ xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y - widgetNode.get(OFFSET_HEIGHT)];
+ break;
+ case PositionExt.LC:
+ xy = [x, y - (widgetNode.get(OFFSET_HEIGHT)/2)];
+ break;
+ case PositionExt.RC:
+ xy = [(x - widgetNode.get(OFFSET_WIDTH)), y - (widgetNode.get(OFFSET_HEIGHT)/2)];
+ break;
+ case PositionExt.CC:
+ xy = [x - (widgetNode.get(OFFSET_WIDTH)/2), y - (widgetNode.get(OFFSET_HEIGHT)/2)];
+ break;
+ default:
+ break;
+ }
+
+ if (xy) {
+ this.move(xy);
+ }
+ },
+
+ /**
+ * Aligns the Widget to the provided node (or viewport) using the provided
+ * points. The method can be invoked directly, however it will result in
+ * the align attribute being out of sync with current position of the of Widget.
+ *
+ * @method align
+ * @param {Node | String | null} node A reference (or selector string) for the Node which with the Widget is to be aligned.
+ * If null is passed in, the Widget will be aligned with the viewport.
+ * @param {Array[2]} points A two element array, specifying the points on the Widget and node/viewport which need to be aligned.
+ * The first entry is the point on the Widget, and the second entry is the point on the node/viewport which need to align.
+ * Valid point references are defined as static constants on the WidgetPositionExt class.
+ *
+ * e.g. [WidgetPositionExt.TL, WidgetPositionExt.TR] will align the top-left corner of the Widget with the top-right corner of the node/viewport.
+ */
+ align: function (node, points) {
+ this.set(ALIGN, {node: node, points:points});
+ },
+
+ /**
+ * Centers the container in the viewport, or if a node is passed in,
+ * the node.
+ *
+ * @method centered
+ * @param {Node | String} node Optional. A node reference or selector string defining the node
+ * inside which the Widget is to be centered. If not passed in, the Widget will be centered in the
+ * viewport.
+ */
+ centered: function (node) {
+ this.align(node, [PositionExt.CC, PositionExt.CC]);
+ }
+ };
+
+ Y.WidgetPositionExt = PositionExt;
+
+
+}, '3.0.0' ,{requires:['widget', 'widget-position']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("widget-position",function(A){var H=A.Lang,J=A.Widget,K="xy",F="positioned",I="boundingBox",L="renderUI",G="bindUI",D="syncUI",C=J.UI_SRC,E="xyChange";function B(M){this._posNode=this.get(I);A.after(this._renderUIPosition,this,L);A.after(this._syncUIPosition,this,D);A.after(this._bindUIPosition,this,G);}B.ATTRS={x:{setter:function(M){this._setX(M);},lazyAdd:false,getter:function(){return this._getX();}},y:{setter:function(M){this._setY(M);},lazyAdd:false,getter:function(){return this._getY();}},xy:{value:[0,0],validator:function(M){return this._validateXY(M);}}};B.POSITIONED_CLASS_NAME=J.getClassName(F);B.prototype={_renderUIPosition:function(){this._posNode.addClass(B.POSITIONED_CLASS_NAME);},_syncUIPosition:function(){this._uiSetXY(this.get(K));},_bindUIPosition:function(){this.after(E,this._afterXYChange);},move:function(){var M=arguments,N=(H.isArray(M[0]))?M[0]:[M[0],M[1]];this.set(K,N);},syncXY:function(){this.set(K,this._posNode.getXY(),{src:C});},_validateXY:function(M){return(H.isArray(M)&&H.isNumber(M[0])&&H.isNumber(M[1]));},_setX:function(M){this.set(K,[M,this.get(K)[1]]);},_setY:function(M){this.set(K,[this.get(K)[0],M]);},_getX:function(){return this.get(K)[0];},_getY:function(){return this.get(K)[1];},_afterXYChange:function(M){if(M.src!=C){this._uiSetXY(M.newVal);}},_uiSetXY:function(M){this._posNode.setXY(M);}};A.WidgetPosition=B;},"3.0.0",{requires:["widget"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-position', function(Y) {
+
+/**
+ * Provides basic XY positioning support for Widgets, though an extension
+ *
+ * @module widget-position
+ */
+ var Lang = Y.Lang,
+ Widget = Y.Widget,
+
+ XY_COORD = "xy",
+
+ POSITIONED = "positioned",
+ BOUNDING_BOX = "boundingBox",
+
+ RENDERUI = "renderUI",
+ BINDUI = "bindUI",
+ SYNCUI = "syncUI",
+
+ UI = Widget.UI_SRC,
+
+ XYChange = "xyChange";
+
+ /**
+ * Widget extension, which can be used to add positioning support to the base Widget class,
+ * through the <a href="Base.html#method_build">Base.build</a> method.
+ *
+ * @class WidgetPosition
+ * @param {Object} config User configuration object
+ */
+ function Position(config) {
+ this._posNode = this.get(BOUNDING_BOX);
+
+ // WIDGET METHOD OVERLAP
+ Y.after(this._renderUIPosition, this, RENDERUI);
+ Y.after(this._syncUIPosition, this, SYNCUI);
+ Y.after(this._bindUIPosition, this, BINDUI);
+ }
+
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetPosition.
+ *
+ * @property WidgetPosition.ATTRS
+ * @static
+ * @type Object
+ */
+ Position.ATTRS = {
+
+ /**
+ * @attribute x
+ * @type number
+ * @default 0
+ *
+ * @description Page X co-ordinate for the widget. This attribute acts as a facade for the
+ * xy attribute. Changes in position can be monitored by listening for xyChange events.
+ */
+ x: {
+ setter: function(val) {
+ this._setX(val);
+ },
+ lazyAdd:false,
+ getter: function() {
+ return this._getX();
+ }
+ },
+
+ /**
+ * @attribute y
+ * @type number
+ * @default 0
+ *
+ * @description Page Y co-ordinate for the widget. This attribute acts as a facade for the
+ * xy attribute. Changes in position can be monitored by listening for xyChange events.
+ */
+ y: {
+ setter: function(val) {
+ this._setY(val);
+ },
+ lazyAdd: false,
+ getter: function() {
+ return this._getY();
+ }
+ },
+
+ /**
+ * @attribute xy
+ * @type Array
+ * @default [0,0]
+ *
+ * @description Page XY co-ordinate pair for the widget.
+ */
+ xy: {
+ value:[0,0],
+
+ validator: function(val) {
+ return this._validateXY(val);
+ }
+ }
+ };
+
+ /**
+ * Default class used to mark the boundingBox of a positioned widget.
+ *
+ * @property WidgetPosition.POSITIONED_CLASS_NAME
+ * @type String
+ * @default "yui-widget-positioned"
+ * @static
+ */
+ Position.POSITIONED_CLASS_NAME = Widget.getClassName(POSITIONED);
+
+ Position.prototype = {
+
+ /**
+ * Creates/Initializes the DOM to support xy page positioning.
+ * <p>
+ * This method in invoked after renderUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _renderUIPosition
+ * @protected
+ */
+ _renderUIPosition : function() {
+ this._posNode.addClass(Position.POSITIONED_CLASS_NAME);
+ },
+
+ /**
+ * Synchronizes the UI to match the Widgets xy page position state.
+ * <p>
+ * This method in invoked after syncUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _syncUIPosition
+ * @protected
+ */
+ _syncUIPosition : function() {
+ this._uiSetXY(this.get(XY_COORD));
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget position related state changes.
+ * <p>
+ * This method in invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIPosition
+ * @protected
+ */
+ _bindUIPosition :function() {
+ this.after(XYChange, this._afterXYChange);
+ },
+
+ /**
+ * Moves the Widget to the specified page xy co-ordinate position.
+ *
+ * @method move
+ *
+ * @param {Number} x The new x position
+ * @param {Number} y The new y position
+ * <p>Or</p>
+ * @param {Array} x, y values passed as an array ([x, y]), to support
+ * simple pass through of Node.getXY results
+ */
+ move: function () {
+ var args = arguments,
+ coord = (Lang.isArray(args[0])) ? args[0] : [args[0], args[1]];
+ this.set(XY_COORD, coord);
+ },
+
+ /**
+ * Synchronizes the Panel's "xy", "x", and "y" properties with the
+ * Widget's position in the DOM.
+ *
+ * @method syncXY
+ */
+ syncXY : function () {
+ this.set(XY_COORD, this._posNode.getXY(), {src: UI});
+ },
+
+ /**
+ * Default validator for the XY attribute
+ *
+ * @method _validateXY
+ * @param {Array} val The XY page co-ordinate value which is being set.
+ * @return {boolean} true if valid, false if not.
+ */
+ _validateXY : function(val) {
+ return (Lang.isArray(val) && Lang.isNumber(val[0]) && Lang.isNumber(val[1]));
+ },
+
+ /**
+ * Default setter for the X attribute. The setter passes the X value through
+ * to the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _setX
+ * @param {Number} val The X page co-ordinate value
+ */
+ _setX : function(val) {
+ this.set(XY_COORD, [val, this.get(XY_COORD)[1]]);
+ },
+
+ /**
+ * Default setter for the Y attribute. The setter passes the Y value through
+ * to the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _setY
+ * @param {Number} val The Y page co-ordinate value
+ */
+ _setY : function(val) {
+ this.set(XY_COORD, [this.get(XY_COORD)[0], val]);
+ },
+
+ /**
+ * Default getter for the X attribute. The value is retrieved from
+ * the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _getX
+ * @return {Number} The X page co-ordinate value
+ */
+ _getX : function() {
+ return this.get(XY_COORD)[0];
+ },
+
+ /**
+ * Default getter for the Y attribute. The value is retrieved from
+ * the XY attribute, which is the sole store for the XY state.
+ *
+ * @method _getY
+ * @return {Number} The Y page co-ordinate value
+ */
+ _getY : function() {
+ return this.get(XY_COORD)[1];
+ },
+
+ /**
+ * Default attribute change listener for the xy attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterXYChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterXYChange : function(e) {
+ if (e.src != UI) {
+ this._uiSetXY(e.newVal);
+ }
+ },
+
+ /**
+ * Updates the UI to reflect the XY page co-ordinates passed in.
+ *
+ * @method _uiSetXY
+ * @protected
+ * @param {String} val The XY page co-ordinates value to be reflected in the UI
+ */
+ _uiSetXY : function(val) {
+ this._posNode.setXY(val);
+ }
+ };
+
+ Y.WidgetPosition = Position;
+
+
+}, '3.0.0' ,{requires:['widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-stack', function(Y) {
+
+/**
+ * Provides stackable (z-index) support for Widgets through an extension.
+ *
+ * @module widget-stack
+ */
+ var L = Y.Lang,
+ UA = Y.UA,
+ Node = Y.Node,
+ Widget = Y.Widget,
+
+ ZINDEX = "zIndex",
+ SHIM = "shim",
+ VISIBLE = "visible",
+
+ BOUNDING_BOX = "boundingBox",
+
+ RENDER_UI = "renderUI",
+ BIND_UI = "bindUI",
+ SYNC_UI = "syncUI",
+
+ OFFSET_WIDTH = "offsetWidth",
+ OFFSET_HEIGHT = "offsetHeight",
+ PARENT_NODE = "parentNode",
+ FIRST_CHILD = "firstChild",
+ OWNER_DOCUMENT = "ownerDocument",
+
+ WIDTH = "width",
+ HEIGHT = "height",
+ PX = "px",
+
+ // HANDLE KEYS
+ SHIM_DEFERRED = "shimdeferred",
+ SHIM_RESIZE = "shimresize",
+
+ // Events
+ VisibleChange = "visibleChange",
+ WidthChange = "widthChange",
+ HeightChange = "heightChange",
+ ShimChange = "shimChange",
+ ZIndexChange = "zIndexChange",
+ ContentUpdate = "contentUpdate",
+
+ // CSS
+ STACKED = "stacked";
+
+ /**
+ * Widget extension, which can be used to add stackable (z-index) support to the
+ * base Widget class along with a shimming solution, through the
+ * <a href="Base.html#method_build">Base.build</a> method.
+ *
+ * @class WidgetStack
+ * @param {Object} User configuration object
+ */
+ function Stack(config) {
+ this._stackNode = this.get(BOUNDING_BOX);
+ this._stackHandles = {};
+
+ // WIDGET METHOD OVERLAP
+ Y.after(this._renderUIStack, this, RENDER_UI);
+ Y.after(this._syncUIStack, this, SYNC_UI);
+ Y.after(this._bindUIStack, this, BIND_UI);
+ }
+
+ // Static Properties
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetStack.
+ *
+ * @property WidgetStack.ATTRS
+ * @type Object
+ * @static
+ */
+ Stack.ATTRS = {
+ /**
+ * @attribute shim
+ * @type boolean
+ * @default false, for all browsers other than IE6, for which a shim is enabled by default.
+ *
+ * @description Boolean flag to indicate whether or not a shim should be added to the Widgets
+ * boundingBox, to protect it from select box bleedthrough.
+ */
+ shim: {
+ value: (UA.ie == 6)
+ },
+
+ /**
+ * @attribute zIndex
+ * @type number
+ * @default 0
+ * @description The z-index to apply to the Widgets boundingBox. Non-numerical values for
+ * zIndex will be converted to 0
+ */
+ zIndex: {
+ value:0,
+ setter: function(val) {
+ return this._setZIndex(val);
+ }
+ }
+ };
+
+ /**
+ * The HTML parsing rules for the WidgetStack class.
+ *
+ * @property WidgetStack.HTML_PARSER
+ * @static
+ * @type Object
+ */
+ Stack.HTML_PARSER = {
+ zIndex: function(contentBox) {
+ return contentBox.getStyle(ZINDEX);
+ }
+ };
+
+ /**
+ * Default class used to mark the shim element
+ *
+ * @property WidgetStack.SHIM_CLASS_NAME
+ * @type String
+ * @static
+ * @default "yui-widget-shim"
+ */
+ Stack.SHIM_CLASS_NAME = Widget.getClassName(SHIM);
+
+ /**
+ * Default class used to mark the boundingBox of a stacked widget.
+ *
+ * @property WidgetStack.STACKED_CLASS_NAME
+ * @type String
+ * @static
+ * @default "yui-widget-stacked"
+ */
+ Stack.STACKED_CLASS_NAME = Widget.getClassName(STACKED);
+
+ /**
+ * Default markup template used to generate the shim element.
+ *
+ * @property WidgetStack.SHIM_TEMPLATE
+ * @type String
+ * @static
+ */
+ Stack.SHIM_TEMPLATE = '<iframe class="' + Stack.SHIM_CLASS_NAME + '" frameborder="0" title="Widget Stacking Shim" src="javascript:false" tabindex="-1" role="presentation"></iframe>';
+
+ Stack.prototype = {
+
+ /**
+ * Synchronizes the UI to match the Widgets stack state. This method in
+ * invoked after syncUI is invoked for the Widget class using YUI's aop infrastructure.
+ *
+ * @method _syncUIStack
+ * @protected
+ */
+ _syncUIStack: function() {
+ this._uiSetShim(this.get(SHIM));
+ this._uiSetZIndex(this.get(ZINDEX));
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget stack related state changes.
+ * <p>
+ * This method is invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIStack
+ * @protected
+ */
+ _bindUIStack: function() {
+ this.after(ShimChange, this._afterShimChange);
+ this.after(ZIndexChange, this._afterZIndexChange);
+ },
+
+ /**
+ * Creates/Initializes the DOM to support stackability.
+ * <p>
+ * This method in invoked after renderUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _renderUIStack
+ * @protected
+ */
+ _renderUIStack: function() {
+ this._stackNode.addClass(Stack.STACKED_CLASS_NAME);
+ },
+
+ /**
+ * Default setter for zIndex attribute changes. Normalizes zIndex values to
+ * numbers, converting non-numerical values to 0.
+ *
+ * @method _setZIndex
+ * @protected
+ * @param {String | Number} zIndex
+ * @return {Number} Normalized zIndex
+ */
+ _setZIndex: function(zIndex) {
+ if (L.isString(zIndex)) {
+ zIndex = parseInt(zIndex, 10);
+ }
+ if (!L.isNumber(zIndex)) {
+ zIndex = 0;
+ }
+ return zIndex;
+ },
+
+ /**
+ * Default attribute change listener for the shim attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterShimChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterShimChange : function(e) {
+ this._uiSetShim(e.newVal);
+ },
+
+ /**
+ * Default attribute change listener for the zIndex attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterZIndexChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterZIndexChange : function(e) {
+ this._uiSetZIndex(e.newVal);
+ },
+
+ /**
+ * Updates the UI to reflect the zIndex value passed in.
+ *
+ * @method _uiSetZIndex
+ * @protected
+ * @param {number} zIndex The zindex to be reflected in the UI
+ */
+ _uiSetZIndex: function (zIndex) {
+ this._stackNode.setStyle(ZINDEX, zIndex);
+ },
+
+ /**
+ * Updates the UI to enable/disable the shim. If the widget is not currently visible,
+ * creation of the shim is deferred until it is made visible, for performance reasons.
+ *
+ * @method _uiSetShim
+ * @protected
+ * @param {boolean} enable If true, creates/renders the shim, if false, removes it.
+ */
+ _uiSetShim: function (enable) {
+ if (enable) {
+ // Lazy creation
+ if (this.get(VISIBLE)) {
+ this._renderShim();
+ } else {
+ this._renderShimDeferred();
+ }
+ } else {
+ this._destroyShim();
+ }
+ },
+
+ /**
+ * Sets up change handlers for the visible attribute, to defer shim creation/rendering
+ * until the Widget is made visible.
+ *
+ * @method _renderShimDeferred
+ * @private
+ */
+ _renderShimDeferred : function() {
+
+ this._stackHandles[SHIM_DEFERRED] = this._stackHandles[SHIM_DEFERRED] || [];
+
+ var handles = this._stackHandles[SHIM_DEFERRED],
+ createBeforeVisible = function(e) {
+ if (e.newVal) {
+ this._renderShim();
+ }
+ };
+
+ handles.push(this.on(VisibleChange, createBeforeVisible));
+ },
+
+ /**
+ * Sets up event listeners to resize the shim when the size of the Widget changes.
+ * <p>
+ * NOTE: This method is only used for IE6 currently, since IE6 doesn't support a way to
+ * resize the shim purely through CSS, when the Widget does not have an explicit width/height
+ * set.
+ * </p>
+ * @method _addShimResizeHandlers
+ * @private
+ */
+ _addShimResizeHandlers : function() {
+
+ this._stackHandles[SHIM_RESIZE] = this._stackHandles[SHIM_RESIZE] || [];
+
+ var sizeShim = this.sizeShim,
+ handles = this._stackHandles[SHIM_RESIZE];
+
+ this.sizeShim();
+
+ handles.push(this.after(VisibleChange, sizeShim));
+ handles.push(this.after(WidthChange, sizeShim));
+ handles.push(this.after(HeightChange, sizeShim));
+ handles.push(this.after(ContentUpdate, sizeShim));
+ },
+
+ /**
+ * Detaches any handles stored for the provided key
+ *
+ * @method _detachStackHandles
+ * @param String handleKey The key defining the group of handles which should be detached
+ * @private
+ */
+ _detachStackHandles : function(handleKey) {
+ var handles = this._stackHandles[handleKey],
+ handle;
+
+ if (handles && handles.length > 0) {
+ while((handle = handles.pop())) {
+ handle.detach();
+ }
+ }
+ },
+
+ /**
+ * Creates the shim element and adds it to the DOM
+ *
+ * @method _renderShim
+ * @private
+ */
+ _renderShim : function() {
+ var shimEl = this._shimNode,
+ stackEl = this._stackNode;
+
+ if (!shimEl) {
+ shimEl = this._shimNode = this._getShimTemplate();
+ stackEl.insertBefore(shimEl, stackEl.get(FIRST_CHILD));
+
+ if (UA.ie == 6) {
+ this._addShimResizeHandlers();
+ }
+ this._detachStackHandles(SHIM_DEFERRED);
+ }
+ },
+
+ /**
+ * Removes the shim from the DOM, and detaches any related event
+ * listeners.
+ *
+ * @method _destroyShim
+ * @private
+ */
+ _destroyShim : function() {
+ if (this._shimNode) {
+ this._shimNode.get(PARENT_NODE).removeChild(this._shimNode);
+ this._shimNode = null;
+
+ this._detachStackHandles(SHIM_DEFERRED);
+ this._detachStackHandles(SHIM_RESIZE);
+ }
+ },
+
+ /**
+ * For IE6, synchronizes the size and position of iframe shim to that of
+ * Widget bounding box which it is protecting. For all other browsers,
+ * this method does not do anything.
+ *
+ * @method sizeShim
+ */
+ sizeShim: function () {
+ var shim = this._shimNode,
+ node = this._stackNode;
+
+ if (shim && UA.ie === 6 && this.get(VISIBLE)) {
+ shim.setStyle(WIDTH, node.get(OFFSET_WIDTH) + PX);
+ shim.setStyle(HEIGHT, node.get(OFFSET_HEIGHT) + PX);
+ }
+ },
+
+ /**
+ * Creates a cloned shim node, using the SHIM_TEMPLATE html template, for use on a new instance.
+ *
+ * @method _getShimTemplate
+ * @private
+ * @return {Node} node A new shim Node instance.
+ */
+ _getShimTemplate : function() {
+ return Node.create(Stack.SHIM_TEMPLATE, this._stackNode.get(OWNER_DOCUMENT));
+ }
+ };
+
+ Y.WidgetStack = Stack;
+
+
+}, '3.0.0' ,{requires:['widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("widget-stack",function(E){var N=E.Lang,T=E.UA,d=E.Node,F=E.Widget,c="zIndex",P="shim",a="visible",e="boundingBox",W="renderUI",G="bindUI",S="syncUI",Q="offsetWidth",D="offsetHeight",M="parentNode",A="firstChild",X="ownerDocument",H="width",V="height",K="px",O="shimdeferred",f="shimresize",Z="visibleChange",C="widthChange",J="heightChange",b="shimChange",B="zIndexChange",I="contentUpdate",R="stacked";function U(L){this._stackNode=this.get(e);this._stackHandles={};E.after(this._renderUIStack,this,W);E.after(this._syncUIStack,this,S);E.after(this._bindUIStack,this,G);}U.ATTRS={shim:{value:(T.ie==6)},zIndex:{value:0,setter:function(L){return this._setZIndex(L);}}};U.HTML_PARSER={zIndex:function(L){return L.getStyle(c);}};U.SHIM_CLASS_NAME=F.getClassName(P);U.STACKED_CLASS_NAME=F.getClassName(R);U.SHIM_TEMPLATE='<iframe class="'+U.SHIM_CLASS_NAME+'" frameborder="0" title="Widget Stacking Shim" src="javascript:false" tabindex="-1" role="presentation"></iframe>';U.prototype={_syncUIStack:function(){this._uiSetShim(this.get(P));this._uiSetZIndex(this.get(c));},_bindUIStack:function(){this.after(b,this._afterShimChange);this.after(B,this._afterZIndexChange);},_renderUIStack:function(){this._stackNode.addClass(U.STACKED_CLASS_NAME);},_setZIndex:function(L){if(N.isString(L)){L=parseInt(L,10);}if(!N.isNumber(L)){L=0;}return L;},_afterShimChange:function(L){this._uiSetShim(L.newVal);},_afterZIndexChange:function(L){this._uiSetZIndex(L.newVal);},_uiSetZIndex:function(L){this._stackNode.setStyle(c,L);},_uiSetShim:function(L){if(L){if(this.get(a)){this._renderShim();}else{this._renderShimDeferred();}}else{this._destroyShim();}},_renderShimDeferred:function(){this._stackHandles[O]=this._stackHandles[O]||[];var Y=this._stackHandles[O],L=function(g){if(g.newVal){this._renderShim();}};Y.push(this.on(Z,L));},_addShimResizeHandlers:function(){this._stackHandles[f]=this._stackHandles[f]||[];var Y=this.sizeShim,L=this._stackHandles[f];this.sizeShim();L.push(this.after(Z,Y));L.push(this.after(C,Y));L.push(this.after(J,Y));L.push(this.after(I,Y));},_detachStackHandles:function(L){var Y=this._stackHandles[L],g;if(Y&&Y.length>0){while((g=Y.pop())){g.detach();}}},_renderShim:function(){var L=this._shimNode,Y=this._stackNode;if(!L){L=this._shimNode=this._getShimTemplate();Y.insertBefore(L,Y.get(A));if(T.ie==6){this._addShimResizeHandlers();}this._detachStackHandles(O);}},_destroyShim:function(){if(this._shimNode){this._shimNode.get(M).removeChild(this._shimNode);this._shimNode=null;this._detachStackHandles(O);this._detachStackHandles(f);}},sizeShim:function(){var Y=this._shimNode,L=this._stackNode;if(Y&&T.ie===6&&this.get(a)){Y.setStyle(H,L.get(Q)+K);Y.setStyle(V,L.get(D)+K);}},_getShimTemplate:function(){return d.create(U.SHIM_TEMPLATE,this._stackNode.get(X));}};E.WidgetStack=U;},"3.0.0",{requires:["widget"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-stack', function(Y) {
+
+/**
+ * Provides stackable (z-index) support for Widgets through an extension.
+ *
+ * @module widget-stack
+ */
+ var L = Y.Lang,
+ UA = Y.UA,
+ Node = Y.Node,
+ Widget = Y.Widget,
+
+ ZINDEX = "zIndex",
+ SHIM = "shim",
+ VISIBLE = "visible",
+
+ BOUNDING_BOX = "boundingBox",
+
+ RENDER_UI = "renderUI",
+ BIND_UI = "bindUI",
+ SYNC_UI = "syncUI",
+
+ OFFSET_WIDTH = "offsetWidth",
+ OFFSET_HEIGHT = "offsetHeight",
+ PARENT_NODE = "parentNode",
+ FIRST_CHILD = "firstChild",
+ OWNER_DOCUMENT = "ownerDocument",
+
+ WIDTH = "width",
+ HEIGHT = "height",
+ PX = "px",
+
+ // HANDLE KEYS
+ SHIM_DEFERRED = "shimdeferred",
+ SHIM_RESIZE = "shimresize",
+
+ // Events
+ VisibleChange = "visibleChange",
+ WidthChange = "widthChange",
+ HeightChange = "heightChange",
+ ShimChange = "shimChange",
+ ZIndexChange = "zIndexChange",
+ ContentUpdate = "contentUpdate",
+
+ // CSS
+ STACKED = "stacked";
+
+ /**
+ * Widget extension, which can be used to add stackable (z-index) support to the
+ * base Widget class along with a shimming solution, through the
+ * <a href="Base.html#method_build">Base.build</a> method.
+ *
+ * @class WidgetStack
+ * @param {Object} User configuration object
+ */
+ function Stack(config) {
+ this._stackNode = this.get(BOUNDING_BOX);
+ this._stackHandles = {};
+
+ // WIDGET METHOD OVERLAP
+ Y.after(this._renderUIStack, this, RENDER_UI);
+ Y.after(this._syncUIStack, this, SYNC_UI);
+ Y.after(this._bindUIStack, this, BIND_UI);
+ }
+
+ // Static Properties
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetStack.
+ *
+ * @property WidgetStack.ATTRS
+ * @type Object
+ * @static
+ */
+ Stack.ATTRS = {
+ /**
+ * @attribute shim
+ * @type boolean
+ * @default false, for all browsers other than IE6, for which a shim is enabled by default.
+ *
+ * @description Boolean flag to indicate whether or not a shim should be added to the Widgets
+ * boundingBox, to protect it from select box bleedthrough.
+ */
+ shim: {
+ value: (UA.ie == 6)
+ },
+
+ /**
+ * @attribute zIndex
+ * @type number
+ * @default 0
+ * @description The z-index to apply to the Widgets boundingBox. Non-numerical values for
+ * zIndex will be converted to 0
+ */
+ zIndex: {
+ value:0,
+ setter: function(val) {
+ return this._setZIndex(val);
+ }
+ }
+ };
+
+ /**
+ * The HTML parsing rules for the WidgetStack class.
+ *
+ * @property WidgetStack.HTML_PARSER
+ * @static
+ * @type Object
+ */
+ Stack.HTML_PARSER = {
+ zIndex: function(contentBox) {
+ return contentBox.getStyle(ZINDEX);
+ }
+ };
+
+ /**
+ * Default class used to mark the shim element
+ *
+ * @property WidgetStack.SHIM_CLASS_NAME
+ * @type String
+ * @static
+ * @default "yui-widget-shim"
+ */
+ Stack.SHIM_CLASS_NAME = Widget.getClassName(SHIM);
+
+ /**
+ * Default class used to mark the boundingBox of a stacked widget.
+ *
+ * @property WidgetStack.STACKED_CLASS_NAME
+ * @type String
+ * @static
+ * @default "yui-widget-stacked"
+ */
+ Stack.STACKED_CLASS_NAME = Widget.getClassName(STACKED);
+
+ /**
+ * Default markup template used to generate the shim element.
+ *
+ * @property WidgetStack.SHIM_TEMPLATE
+ * @type String
+ * @static
+ */
+ Stack.SHIM_TEMPLATE = '<iframe class="' + Stack.SHIM_CLASS_NAME + '" frameborder="0" title="Widget Stacking Shim" src="javascript:false" tabindex="-1" role="presentation"></iframe>';
+
+ Stack.prototype = {
+
+ /**
+ * Synchronizes the UI to match the Widgets stack state. This method in
+ * invoked after syncUI is invoked for the Widget class using YUI's aop infrastructure.
+ *
+ * @method _syncUIStack
+ * @protected
+ */
+ _syncUIStack: function() {
+ this._uiSetShim(this.get(SHIM));
+ this._uiSetZIndex(this.get(ZINDEX));
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget stack related state changes.
+ * <p>
+ * This method is invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIStack
+ * @protected
+ */
+ _bindUIStack: function() {
+ this.after(ShimChange, this._afterShimChange);
+ this.after(ZIndexChange, this._afterZIndexChange);
+ },
+
+ /**
+ * Creates/Initializes the DOM to support stackability.
+ * <p>
+ * This method in invoked after renderUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _renderUIStack
+ * @protected
+ */
+ _renderUIStack: function() {
+ this._stackNode.addClass(Stack.STACKED_CLASS_NAME);
+ },
+
+ /**
+ * Default setter for zIndex attribute changes. Normalizes zIndex values to
+ * numbers, converting non-numerical values to 0.
+ *
+ * @method _setZIndex
+ * @protected
+ * @param {String | Number} zIndex
+ * @return {Number} Normalized zIndex
+ */
+ _setZIndex: function(zIndex) {
+ if (L.isString(zIndex)) {
+ zIndex = parseInt(zIndex, 10);
+ }
+ if (!L.isNumber(zIndex)) {
+ zIndex = 0;
+ }
+ return zIndex;
+ },
+
+ /**
+ * Default attribute change listener for the shim attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterShimChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterShimChange : function(e) {
+ this._uiSetShim(e.newVal);
+ },
+
+ /**
+ * Default attribute change listener for the zIndex attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterZIndexChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterZIndexChange : function(e) {
+ this._uiSetZIndex(e.newVal);
+ },
+
+ /**
+ * Updates the UI to reflect the zIndex value passed in.
+ *
+ * @method _uiSetZIndex
+ * @protected
+ * @param {number} zIndex The zindex to be reflected in the UI
+ */
+ _uiSetZIndex: function (zIndex) {
+ this._stackNode.setStyle(ZINDEX, zIndex);
+ },
+
+ /**
+ * Updates the UI to enable/disable the shim. If the widget is not currently visible,
+ * creation of the shim is deferred until it is made visible, for performance reasons.
+ *
+ * @method _uiSetShim
+ * @protected
+ * @param {boolean} enable If true, creates/renders the shim, if false, removes it.
+ */
+ _uiSetShim: function (enable) {
+ if (enable) {
+ // Lazy creation
+ if (this.get(VISIBLE)) {
+ this._renderShim();
+ } else {
+ this._renderShimDeferred();
+ }
+ } else {
+ this._destroyShim();
+ }
+ },
+
+ /**
+ * Sets up change handlers for the visible attribute, to defer shim creation/rendering
+ * until the Widget is made visible.
+ *
+ * @method _renderShimDeferred
+ * @private
+ */
+ _renderShimDeferred : function() {
+
+ this._stackHandles[SHIM_DEFERRED] = this._stackHandles[SHIM_DEFERRED] || [];
+
+ var handles = this._stackHandles[SHIM_DEFERRED],
+ createBeforeVisible = function(e) {
+ if (e.newVal) {
+ this._renderShim();
+ }
+ };
+
+ handles.push(this.on(VisibleChange, createBeforeVisible));
+ },
+
+ /**
+ * Sets up event listeners to resize the shim when the size of the Widget changes.
+ * <p>
+ * NOTE: This method is only used for IE6 currently, since IE6 doesn't support a way to
+ * resize the shim purely through CSS, when the Widget does not have an explicit width/height
+ * set.
+ * </p>
+ * @method _addShimResizeHandlers
+ * @private
+ */
+ _addShimResizeHandlers : function() {
+
+ this._stackHandles[SHIM_RESIZE] = this._stackHandles[SHIM_RESIZE] || [];
+
+ var sizeShim = this.sizeShim,
+ handles = this._stackHandles[SHIM_RESIZE];
+
+ this.sizeShim();
+
+ handles.push(this.after(VisibleChange, sizeShim));
+ handles.push(this.after(WidthChange, sizeShim));
+ handles.push(this.after(HeightChange, sizeShim));
+ handles.push(this.after(ContentUpdate, sizeShim));
+ },
+
+ /**
+ * Detaches any handles stored for the provided key
+ *
+ * @method _detachStackHandles
+ * @param String handleKey The key defining the group of handles which should be detached
+ * @private
+ */
+ _detachStackHandles : function(handleKey) {
+ var handles = this._stackHandles[handleKey],
+ handle;
+
+ if (handles && handles.length > 0) {
+ while((handle = handles.pop())) {
+ handle.detach();
+ }
+ }
+ },
+
+ /**
+ * Creates the shim element and adds it to the DOM
+ *
+ * @method _renderShim
+ * @private
+ */
+ _renderShim : function() {
+ var shimEl = this._shimNode,
+ stackEl = this._stackNode;
+
+ if (!shimEl) {
+ shimEl = this._shimNode = this._getShimTemplate();
+ stackEl.insertBefore(shimEl, stackEl.get(FIRST_CHILD));
+
+ if (UA.ie == 6) {
+ this._addShimResizeHandlers();
+ }
+ this._detachStackHandles(SHIM_DEFERRED);
+ }
+ },
+
+ /**
+ * Removes the shim from the DOM, and detaches any related event
+ * listeners.
+ *
+ * @method _destroyShim
+ * @private
+ */
+ _destroyShim : function() {
+ if (this._shimNode) {
+ this._shimNode.get(PARENT_NODE).removeChild(this._shimNode);
+ this._shimNode = null;
+
+ this._detachStackHandles(SHIM_DEFERRED);
+ this._detachStackHandles(SHIM_RESIZE);
+ }
+ },
+
+ /**
+ * For IE6, synchronizes the size and position of iframe shim to that of
+ * Widget bounding box which it is protecting. For all other browsers,
+ * this method does not do anything.
+ *
+ * @method sizeShim
+ */
+ sizeShim: function () {
+ var shim = this._shimNode,
+ node = this._stackNode;
+
+ if (shim && UA.ie === 6 && this.get(VISIBLE)) {
+ shim.setStyle(WIDTH, node.get(OFFSET_WIDTH) + PX);
+ shim.setStyle(HEIGHT, node.get(OFFSET_HEIGHT) + PX);
+ }
+ },
+
+ /**
+ * Creates a cloned shim node, using the SHIM_TEMPLATE html template, for use on a new instance.
+ *
+ * @method _getShimTemplate
+ * @private
+ * @return {Node} node A new shim Node instance.
+ */
+ _getShimTemplate : function() {
+ return Node.create(Stack.SHIM_TEMPLATE, this._stackNode.get(OWNER_DOCUMENT));
+ }
+ };
+
+ Y.WidgetStack = Stack;
+
+
+}, '3.0.0' ,{requires:['widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-stdmod', function(Y) {
+
+/**
+ * Provides standard module support for Widgets through an extension.
+ *
+ * @module widget-stdmod
+ */
+ var L = Y.Lang,
+ Node = Y.Node,
+ NodeList = Y.NodeList,
+ UA = Y.UA,
+ Widget = Y.Widget,
+
+ EMPTY = "",
+ HD = "hd",
+ BD = "bd",
+ FT = "ft",
+ HEADER = "header",
+ BODY = "body",
+ FOOTER = "footer",
+ FILL_HEIGHT = "fillHeight",
+ STDMOD = "stdmod",
+
+ PX = "px",
+ NODE_SUFFIX = "Node",
+ CONTENT_SUFFIX = "Content",
+ INNER_HTML = "innerHTML",
+ FIRST_CHILD = "firstChild",
+ CHILD_NODES = "childNodes",
+ CREATE_DOCUMENT_FRAGMENT = "createDocumentFragment",
+ OWNER_DOCUMENT = "ownerDocument",
+
+ CONTENT_BOX = "contentBox",
+ BOUNDING_BOX = "boundingBox",
+
+ HEIGHT = "height",
+ OFFSET_HEIGHT = "offsetHeight",
+ AUTO = "auto",
+
+ HeaderChange = "headerContentChange",
+ BodyChange = "bodyContentChange",
+ FooterChange = "footerContentChange",
+ FillHeightChange = "fillHeightChange",
+ HeightChange = "HeightChange",
+ ContentUpdate = "contentUpdate",
+
+ RENDERUI = "renderUI",
+ BINDUI = "bindUI",
+ SYNCUI = "syncUI",
+
+ UI = Y.Widget.UI_SRC;
+
+ /**
+ * Widget extension, which can be used to add Standard Module support to the
+ * base Widget class, through the <a href="Base.html#method_build">Base.build</a>
+ * method.
+ * <p>
+ * The extension adds header, body and footer sections to the Widget's content box and
+ * provides the corresponding methods and attributes to modify the contents of these sections.
+ * </p>
+ * @class WidgetStdMod
+ * @param {Object} The user configuration object
+ */
+ function StdMod(config) {
+
+ this._stdModNode = this.get(CONTENT_BOX);
+
+ Y.after(this._renderUIStdMod, this, RENDERUI);
+ Y.after(this._bindUIStdMod, this, BINDUI);
+ Y.after(this._syncUIStdMod, this, SYNCUI);
+ }
+
+ /**
+ * Constant used to refer the the standard module header, in methods which expect a section specifier
+ *
+ * @property WidgetStdMod.HEADER
+ * @static
+ * @type String
+ */
+ StdMod.HEADER = HEADER;
+ /**
+ * Constant used to refer the the standard module body, in methods which expect a section specifier
+ *
+ * @property WidgetStdMod.BODY
+ * @static
+ * @type String
+ */
+ StdMod.BODY = BODY;
+ /**
+ * Constant used to refer the the standard module footer, in methods which expect a section specifier
+ *
+ * @property WidgetStdMod.FOOTER
+ * @static
+ * @type String
+ */
+ StdMod.FOOTER = FOOTER;
+
+ /**
+ * Constant used to specify insertion position, when adding content to sections of the standard module in
+ * methods which expect a "where" argument.
+ * <p>
+ * Inserts new content <em>before</em> the sections existing content.
+ * </p>
+ * @property WidgetStdMod.AFTER
+ * @static
+ * @type String
+ */
+ StdMod.AFTER = "after";
+
+ /**
+ * Constant used to specify insertion position, when adding content to sections of the standard module in
+ * methods which expect a "where" argument.
+ * <p>
+ * Inserts new content <em>before</em> the sections existing content.
+ * </p>
+ * @property WidgetStdMod.BEFORE
+ * @static
+ * @type String
+ */
+ StdMod.BEFORE = "before";
+ /**
+ * Constant used to specify insertion position, when adding content to sections of the standard module in
+ * methods which expect a "where" argument.
+ * <p>
+ * <em>Replaces</em> the sections existing content, with new content.
+ * </p>
+ * @property WidgetStdMod.REPLACE
+ * @static
+ * @type String
+ */
+ StdMod.REPLACE = "replace";
+
+ var STD_HEADER = StdMod.HEADER,
+ STD_BODY = StdMod.BODY,
+ STD_FOOTER = StdMod.FOOTER,
+ AFTER = StdMod.AFTER,
+ BEFORE = StdMod.BEFORE;
+
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetStdMod.
+ *
+ * @property WidgetStdMod.ATTRS
+ * @type Object
+ * @static
+ */
+ StdMod.ATTRS = {
+
+ /**
+ * @attribute headerContent
+ * @type {String | Node}
+ * @default undefined
+ * @description The content to be added to the header section. This will replace any existing content
+ * in the header. If you want to append, or insert new content, use the <a href="#method_setStdModContent">setStdModContent</a> method.
+ */
+ headerContent: {
+ value:null
+ },
+
+ /**
+ * @attribute footerContent
+ * @type {String | Node}
+ * @default undefined
+ * @description The content to be added to the footer section. This will replace any existing content
+ * in the footer. If you want to append, or insert new content, use the <a href="#method_setStdModContent">setStdModContent</a> method.
+ */
+ footerContent: {
+ value:null
+ },
+
+ /**
+ * @attribute bodyContent
+ * @type {String | Node}
+ * @default undefined
+ * @description The content to be added to the body section. This will replace any existing content
+ * in the body. If you want to append, or insert new content, use the <a href="#method_setStdModContent">setStdModContent</a> method.
+ */
+ bodyContent: {
+ value:null
+ },
+
+ /**
+ * @attribute fillHeight
+ * @type {String}
+ * @default WidgetStdMod.BODY
+ * @description The section (WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER) which should be resized to fill the height of the standard module, when a
+ * height is set on the Widget. If a height is not set on the widget, then all sections are sized based on
+ * their content.
+ */
+ fillHeight: {
+ value: StdMod.BODY,
+ validator: function(val) {
+ return this._validateFillHeight(val);
+ }
+ }
+ };
+
+ /**
+ * The HTML parsing rules for the WidgetStdMod class.
+ *
+ * @property WidgetStdMod.HTML_PARSER
+ * @static
+ * @type Object
+ */
+ StdMod.HTML_PARSER = {
+ headerContent: function(contentBox) {
+ return this._parseStdModHTML(STD_HEADER);
+ },
+
+ bodyContent: function(contentBox) {
+ return this._parseStdModHTML(STD_BODY);
+ },
+
+ footerContent : function(contentBox) {
+ return this._parseStdModHTML(STD_FOOTER);
+ }
+ };
+
+ /**
+ * Static hash of default class names used for the header,
+ * body and footer sections of the standard module, keyed by
+ * the section identifier (WidgetStdMod.STD_HEADER, WidgetStdMod.STD_BODY, WidgetStdMod.STD_FOOTER)
+ *
+ * @property WidgetStdMod.SECTION_CLASS_NAMES
+ * @static
+ * @type Object
+ */
+ StdMod.SECTION_CLASS_NAMES = {
+ header: Widget.getClassName(HD),
+ body: Widget.getClassName(BD),
+ footer: Widget.getClassName(FT)
+ };
+
+ /**
+ * The template HTML strings for each of the standard module sections. Section entries are keyed by the section constants,
+ * WidgetStdMod.HEADER, WidgetStdMod.BODY, WidgetStdMod.FOOTER, and contain the HTML to be added for each section.
+ * e.g.
+ * <pre>
+ * {
+ * header : '<div class="yui-widget-hd"></div>',
+ * body : '<div class="yui-widget-bd"></div>',
+ * footer : '<div class="yui-widget-ft"></div>'
+ * }
+ * </pre>
+ * @property WidgetStdMod.TEMPLATES
+ * @type Object
+ * @static
+ */
+ StdMod.TEMPLATES = {
+ header : '<div class="' + StdMod.SECTION_CLASS_NAMES[STD_HEADER] + '"></div>',
+ body : '<div class="' + StdMod.SECTION_CLASS_NAMES[STD_BODY] + '"></div>',
+ footer : '<div class="' + StdMod.SECTION_CLASS_NAMES[STD_FOOTER] + '"></div>'
+ };
+
+ StdMod.prototype = {
+
+ /**
+ * Synchronizes the UI to match the Widgets standard module state.
+ * <p>
+ * This method is invoked after syncUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _syncUIStdMod
+ * @protected
+ */
+ _syncUIStdMod : function() {
+ this._uiSetStdMod(STD_HEADER, this.get(STD_HEADER + CONTENT_SUFFIX));
+ this._uiSetStdMod(STD_BODY, this.get(STD_BODY + CONTENT_SUFFIX));
+ this._uiSetStdMod(STD_FOOTER, this.get(STD_FOOTER + CONTENT_SUFFIX));
+ this._uiSetFillHeight(this.get(FILL_HEIGHT));
+ },
+
+ /**
+ * Creates/Initializes the DOM for standard module support.
+ * <p>
+ * This method is invoked after renderUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _renderUIStdMod
+ * @protected
+ */
+ _renderUIStdMod : function() {
+ this._stdModNode.addClass(Widget.getClassName(STDMOD));
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget standard module related state changes.
+ * <p>
+ * This method is invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIStdMod
+ * @protected
+ */
+ _bindUIStdMod : function() {
+ this.after(HeaderChange, this._afterHeaderChange);
+ this.after(BodyChange, this._afterBodyChange);
+ this.after(FooterChange, this._afterFooterChange);
+
+ this.after(FillHeightChange, this._afterFillHeightChange);
+ this.after(HeightChange, this._fillHeight);
+ this.after(ContentUpdate, this._fillHeight);
+ },
+
+ /**
+ * Default attribute change listener for the headerContent attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterHeaderChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterHeaderChange : function(e) {
+ if (e.src !== UI) {
+ this._uiSetStdMod(STD_HEADER, e.newVal, e.stdModPosition);
+ }
+ },
+
+ /**
+ * Default attribute change listener for the bodyContent attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterBodyChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterBodyChange : function(e) {
+ if (e.src !== UI) {
+ this._uiSetStdMod(STD_BODY, e.newVal, e.stdModPosition);
+ }
+ },
+
+ /**
+ * Default attribute change listener for the footerContent attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterFooterChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterFooterChange : function(e) {
+ if (e.src !== UI) {
+ this._uiSetStdMod(STD_FOOTER, e.newVal, e.stdModPosition);
+ }
+ },
+
+ /**
+ * Default attribute change listener for the fillHeight attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterFillHeightChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterFillHeightChange: function (e) {
+ this._uiSetFillHeight(e.newVal);
+ },
+
+ /**
+ * Default validator for the fillHeight attribute. Verifies that the
+ * value set is a valid section specifier - one of WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER,
+ * or a falsey value if fillHeight is to be disabled.
+ *
+ * @method _validateFillHeight
+ * @protected
+ * @param {String} val The section which should be setup to fill height, or false/null to disable fillHeight
+ * @return true if valid, false if not
+ */
+ _validateFillHeight : function(val) {
+ return !val || val == StdMod.BODY || val == StdMod.HEADER || val == StdMod.FOOTER;
+ },
+
+ /**
+ * Updates the rendered UI, to resize the provided section so that the standard module fills out
+ * the specified widget height. Note: This method does not check whether or not a height is set
+ * on the Widget.
+ *
+ * @method _uiSetFillHeight
+ * @protected
+ * @param {String} fillSection A valid section specifier - one of WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER
+ */
+ _uiSetFillHeight : function(fillSection) {
+ var fillNode = this.getStdModNode(fillSection);
+ var currNode = this._currFillNode;
+
+ if (currNode && fillNode !== currNode){
+ currNode.setStyle(HEIGHT, EMPTY);
+ }
+
+ if (fillNode) {
+ this._currFillNode = fillNode;
+ }
+
+ this._fillHeight();
+ },
+
+ /**
+ * Updates the rendered UI, to resize the current section specified by the fillHeight attribute, so
+ * that the standard module fills out the Widget height. If a height has not been set on Widget,
+ * the section is not resized (height is set to "auto").
+ *
+ * @method _fillHeight
+ * @private
+ */
+ _fillHeight : function() {
+ if (this.get(FILL_HEIGHT)) {
+ var height = this.get(HEIGHT);
+ if (height != EMPTY && height != AUTO) {
+ this.fillHeight(this._currFillNode);
+ }
+ }
+ },
+
+ /**
+ * Updates the rendered UI, adding the provided content (either an HTML string, or node reference),
+ * to the specified section. The content is either added before, after or replaces existing content
+ * in the section, based on the value of the <code>where</code> argument.
+ *
+ * @method _uiSetStdMod
+ * @protected
+ *
+ * @param {String} section The section to be updated. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @param {String | Node} content The new content (either as an HTML string, or Node reference) to add to the section
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace existing content in the section.
+ */
+ _uiSetStdMod : function(section, content, where) {
+ if (content) {
+ var node = this.getStdModNode(section) || this._renderStdMod(section);
+ if (content instanceof Node || content instanceof NodeList) {
+ this._addNodeRef(node, content, where);
+ } else {
+ this._addNodeHTML(node, content, where);
+ }
+ this.set(section + CONTENT_SUFFIX, this._getStdModContent(section), {src:UI});
+ this.fire(ContentUpdate);
+ }
+ },
+
+ /**
+ * Creates the DOM node for the given section, and inserts it into the correct location in the contentBox.
+ *
+ * @method _renderStdMod
+ * @protected
+ * @param {String} section The section to create/render. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} A reference to the added section node
+ */
+ _renderStdMod : function(section) {
+
+ var contentBox = this.get(CONTENT_BOX),
+ sectionNode = this._findStdModSection(section);
+
+ if (!sectionNode) {
+ sectionNode = this._getStdModTemplate(section);
+ }
+
+ this._insertStdModSection(contentBox, section, sectionNode);
+
+ this[section + NODE_SUFFIX] = sectionNode;
+ return this[section + NODE_SUFFIX];
+ },
+
+ /**
+ * Helper method to insert the Node for the given section into the correct location in the contentBox.
+ *
+ * @method _insertStdModSection
+ * @private
+ * @param {Node} contentBox A reference to the Widgets content box.
+ * @param {String} section The section to create/render. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @param {Node} sectionNode The Node for the section.
+ */
+ _insertStdModSection : function(contentBox, section, sectionNode) {
+ var fc = contentBox.get(FIRST_CHILD);
+
+ if (section === STD_FOOTER || !fc) {
+ contentBox.appendChild(sectionNode);
+ } else {
+ if (section === STD_HEADER) {
+ contentBox.insertBefore(sectionNode, fc);
+ } else {
+ // BODY
+ var footer = this[STD_FOOTER + NODE_SUFFIX];
+ if (footer) {
+ contentBox.insertBefore(sectionNode, footer);
+ } else {
+ contentBox.appendChild(sectionNode);
+ }
+ }
+ }
+ },
+
+ /**
+ * Gets a new Node reference for the given standard module section, by cloning
+ * the stored template node.
+ *
+ * @method _getStdModTemplate
+ * @protected
+ * @param {String} section The section to create a new node for. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The new Node instance for the section
+ */
+ _getStdModTemplate : function(section) {
+ return Node.create(StdMod.TEMPLATES[section], this._stdModNode.get(OWNER_DOCUMENT));
+ },
+
+ /**
+ * Helper method to add the given HTML string to the node reference provided.
+ * The HTML is added either before, after or replaces the existing node content
+ * based on the value of the <code>where</code> argument.
+ *
+ * @method _addNodeHTML
+ * @private
+ *
+ * @param {Node} node The section Node to be updated.
+ * @param {String} html The new content HTML string to be added to the section Node.
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace Nodes existing content.
+ */
+ _addNodeHTML : function(node, html, where) {
+ if (where == AFTER) {
+ node.set(INNER_HTML, node.get(INNER_HTML) + html);
+ } else if (where == BEFORE) {
+ node.set(INNER_HTML, html + node.get(INNER_HTML));
+ } else {
+ node.set(INNER_HTML, html);
+ }
+ },
+
+ /**
+ * Helper method to add nodes, to another node.
+ * The child node(s) are added either before, after or replaces the existing node content
+ * based on the value of the <code>where</code> argument.
+ *
+ * @method _addNodeRef
+ * @private
+ *
+ * @param {Node} node The section Node to be updated.
+ * @param {Node|NodeList} children The new content Node, or NodeList to be added to section Node provided.
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace existing content in the Node.
+ */
+ _addNodeRef : function(node, children, where) {
+ var append = true,
+ i, s;
+
+ if (where == BEFORE) {
+ var n = node.get(FIRST_CHILD);
+ if (n) {
+ if (children instanceof NodeList) {
+ for (i = children.size() - 1; i >=0; --i) {
+ node.insertBefore(children.item(i), n);
+ }
+ } else {
+ node.insertBefore(children, n);
+ }
+ append = false;
+ }
+ } else if (where != AFTER) { // replace
+ node.set(INNER_HTML, EMPTY);
+ }
+
+ if (append) {
+ if (children instanceof NodeList) {
+ for (i = 0, s = children.size(); i < s; ++i) {
+ node.appendChild(children.item(i));
+ }
+ } else {
+ node.appendChild(children);
+ }
+ }
+ },
+
+ /**
+ * Helper method to obtain the precise height of the node provided, including padding and border.
+ * The height could be a sub-pixel value for certain browsers, such as Firefox 3.
+ *
+ * @method _getPreciseHeight
+ * @private
+ * @param {Node} node The node for which the precise height is required.
+ * @return {Number} The height of the Node including borders and padding, possibly a float.
+ */
+ _getPreciseHeight : function(node) {
+ var height = (node) ? node.get(OFFSET_HEIGHT) : 0,
+ getBCR = "getBoundingClientRect";
+
+ if (node && node.hasMethod(getBCR)) {
+ var preciseRegion = node.invoke(getBCR);
+ if (preciseRegion) {
+ height = preciseRegion.bottom - preciseRegion.top;
+ }
+ }
+
+ return height;
+ },
+
+ /**
+ * Helper method to query the rendered contents of the contentBox to find the
+ * node for the given section if it exists.
+ *
+ * @method _findStdModSection
+ * @private
+ * @param {String} section The section for which the render Node is to be found. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The rendered node for the given section, or null if not found.
+ */
+ _findStdModSection: function(section) {
+ return this.get(CONTENT_BOX).query("> ." + StdMod.SECTION_CLASS_NAMES[section]);
+ },
+
+ /**
+ * Utility method, used by WidgetStdMods HTML_PARSER implementation
+ * to extract data for each section from markup.
+ *
+ * @method _parseStdModHTML
+ * @private
+ * @param {String} section
+ * @return {String} Inner HTML string with the contents of the section
+ */
+ _parseStdModHTML : function(section) {
+ var node = this._findStdModSection(section),
+ docFrag, children;
+
+ if (node) {
+ docFrag = node.get(OWNER_DOCUMENT).invoke(CREATE_DOCUMENT_FRAGMENT);
+ children = node.get(CHILD_NODES);
+
+ for (var i = children.size() - 1; i >= 0; i--) {
+ var fc = docFrag.get(FIRST_CHILD);
+ if (fc) {
+ docFrag.insertBefore(children.item(i), fc);
+ } else {
+ docFrag.appendChild(children.item(i));
+ }
+ }
+
+ return docFrag;
+ }
+
+ return null;
+ },
+
+ /**
+ * Retrieves the child nodes (content) of a standard module section
+ *
+ * @method _getStdModContent
+ * @private
+ * @param {String} section The standard module section whose child nodes are to be retrieved. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The child node collection of the standard module section.
+ */
+ _getStdModContent : function(section) {
+ return (this[section + NODE_SUFFIX]) ? this[section + NODE_SUFFIX].get(CHILD_NODES) : null;
+ },
+
+ /**
+ * Updates the body section of the standard module with the content provided (either an HTML string, or node reference).
+ * <p>
+ * This method can be used instead of the corresponding section content attribute if you'd like to retain the current content of the section,
+ * and insert content before or after it, by specifying the <code>where</code> argument.
+ * </p>
+ * @method setStdModContent
+ * @param {String} section The standard module section whose content is to be updated. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @param {String | Node} content The content to be added, either an HTML string or a Node reference.
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace existing content in the section.
+ */
+ setStdModContent : function(section, content, where) {
+ this.set(section + CONTENT_SUFFIX, content, {stdModPosition:where});
+ },
+
+ /**
+ * Returns the node reference for the given section. Note: The DOM is not queried for the node reference. The reference
+ * stored by the widget instance is returned if set.
+ *
+ * @method getStdModNode
+ * @param {String} section The section whose node reference is required. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The node reference for the section, or null if not set.
+ */
+ getStdModNode : function(section) {
+ return this[section + NODE_SUFFIX] || null;
+ },
+
+ /**
+ * Sets the height on the provided header, body or footer element to
+ * fill out the height of the Widget. It determines the height of the
+ * widgets bounding box, based on it's configured height value, and
+ * sets the height of the provided section to fill out any
+ * space remaining after the other standard module section heights
+ * have been accounted for.
+ *
+ * <p><strong>NOTE:</strong> This method is not designed to work if an explicit
+ * height has not been set on the Widget, since for an "auto" height Widget,
+ * the heights of the header/body/footer will drive the height of the Widget.</p>
+ *
+ * @method fillHeight
+ * @param {Node} node The node which should be resized to fill out the height
+ * of the Widget bounding box. Should be a standard module section node which belongs
+ * to the widget.
+ */
+ fillHeight : function(node) {
+ if (node) {
+ var boundingBox = this.get(BOUNDING_BOX),
+ stdModNodes = [this.headerNode, this.bodyNode, this.footerNode],
+ stdModNode,
+ total = 0,
+ filled = 0,
+ remaining = 0,
+ validNode = false;
+
+ for (var i = 0, l = stdModNodes.length; i < l; i++) {
+ stdModNode = stdModNodes[i];
+ if (stdModNode) {
+ if (stdModNode !== node) {
+ filled += this._getPreciseHeight(stdModNode);
+ } else {
+ validNode = true;
+ }
+ }
+ }
+
+ if (validNode) {
+ if (UA.ie || UA.opera) {
+ // Need to set height to 0, to allow height to be reduced
+ node.setStyle(HEIGHT, 0 + PX);
+ }
+
+ total = parseInt(boundingBox.getComputedStyle(HEIGHT), 10);
+ if (L.isNumber(total)) {
+ remaining = total - filled;
+
+ if (remaining >= 0) {
+ node.setStyle(HEIGHT, remaining + PX);
+ }
+
+ // Re-adjust height if required, to account for el padding and border
+ var offsetHeight = this.get(CONTENT_BOX).get(OFFSET_HEIGHT);
+ if (offsetHeight != total) {
+ remaining = remaining - (offsetHeight - total);
+ node.setStyle(HEIGHT, remaining + PX);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ Y.WidgetStdMod = StdMod;
+
+
+}, '3.0.0' ,{requires:['widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("widget-stdmod",function(A){var D=A.Lang,P=A.Node,c=A.NodeList,W=A.UA,C=A.Widget,B="",j="hd",h="bd",H="ft",e="header",m="body",k="footer",q="fillHeight",K="stdmod",t="px",T="Node",i="Content",o="innerHTML",d="firstChild",G="childNodes",l="createDocumentFragment",M="ownerDocument",U="contentBox",p="boundingBox",Z="height",g="offsetHeight",X="auto",J="headerContentChange",b="bodyContentChange",N="footerContentChange",R="fillHeightChange",S="HeightChange",r="contentUpdate",V="renderUI",f="bindUI",E="syncUI",Q=A.Widget.UI_SRC;function s(L){this._stdModNode=this.get(U);A.after(this._renderUIStdMod,this,V);A.after(this._bindUIStdMod,this,f);A.after(this._syncUIStdMod,this,E);}s.HEADER=e;s.BODY=m;s.FOOTER=k;s.AFTER="after";s.BEFORE="before";s.REPLACE="replace";var I=s.HEADER,a=s.BODY,O=s.FOOTER,n=s.AFTER,F=s.BEFORE;s.ATTRS={headerContent:{value:null},footerContent:{value:null},bodyContent:{value:null},fillHeight:{value:s.BODY,validator:function(L){return this._validateFillHeight(L);}}};s.HTML_PARSER={headerContent:function(L){return this._parseStdModHTML(I);},bodyContent:function(L){return this._parseStdModHTML(a);},footerContent:function(L){return this._parseStdModHTML(O);}};s.SECTION_CLASS_NAMES={header:C.getClassName(j),body:C.getClassName(h),footer:C.getClassName(H)};s.TEMPLATES={header:'<div class="'+s.SECTION_CLASS_NAMES[I]+'"></div>',body:'<div class="'+s.SECTION_CLASS_NAMES[a]+'"></div>',footer:'<div class="'+s.SECTION_CLASS_NAMES[O]+'"></div>'};s.prototype={_syncUIStdMod:function(){this._uiSetStdMod(I,this.get(I+i));this._uiSetStdMod(a,this.get(a+i));this._uiSetStdMod(O,this.get(O+i));this._uiSetFillHeight(this.get(q));},_renderUIStdMod:function(){this._stdModNode.addClass(C.getClassName(K));},_bindUIStdMod:function(){this.after(J,this._afterHeaderChange);this.after(b,this._afterBodyChange);this.after(N,this._afterFooterChange);this.after(R,this._afterFillHeightChange);this.after(S,this._fillHeight);this.after(r,this._fillHeight);},_afterHeaderChange:function(L){if(L.src!==Q){this._uiSetStdMod(I,L.newVal,L.stdModPosition);}},_afterBodyChange:function(L){if(L.src!==Q){this._uiSetStdMod(a,L.newVal,L.stdModPosition);}},_afterFooterChange:function(L){if(L.src!==Q){this._uiSetStdMod(O,L.newVal,L.stdModPosition);}},_afterFillHeightChange:function(L){this._uiSetFillHeight(L.newVal);},_validateFillHeight:function(L){return !L||L==s.BODY||L==s.HEADER||L==s.FOOTER;},_uiSetFillHeight:function(u){var Y=this.getStdModNode(u);var L=this._currFillNode;if(L&&Y!==L){L.setStyle(Z,B);}if(Y){this._currFillNode=Y;}this._fillHeight();},_fillHeight:function(){if(this.get(q)){var L=this.get(Z);if(L!=B&&L!=X){this.fillHeight(this._currFillNode);}}},_uiSetStdMod:function(v,u,L){if(u){var Y=this.getStdModNode(v)||this._renderStdMod(v);if(u instanceof P||u instanceof c){this._addNodeRef(Y,u,L);}else{this._addNodeHTML(Y,u,L);}this.set(v+i,this._getStdModContent(v),{src:Q});this.fire(r);}},_renderStdMod:function(u){var L=this.get(U),Y=this._findStdModSection(u);if(!Y){Y=this._getStdModTemplate(u);}this._insertStdModSection(L,u,Y);this[u+T]=Y;return this[u+T];},_insertStdModSection:function(L,v,u){var Y=L.get(d);if(v===O||!Y){L.appendChild(u);}else{if(v===I){L.insertBefore(u,Y);}else{var w=this[O+T];if(w){L.insertBefore(u,w);}else{L.appendChild(u);}}}},_getStdModTemplate:function(L){return P.create(s.TEMPLATES[L],this._stdModNode.get(M));},_addNodeHTML:function(u,Y,L){if(L==n){u.set(o,u.get(o)+Y);}else{if(L==F){u.set(o,Y+u.get(o));}else{u.set(o,Y);}}},_addNodeRef:function(x,v,Y){var L=true,u,w;if(Y==F){var y=x.get(d);if(y){if(v instanceof c){for(u=v.size()-1;u>=0;--u){x.insertBefore(v.item(u),y);}}else{x.insertBefore(v,y);}L=false;}}else{if(Y!=n){x.set(o,B);}}if(L){if(v instanceof c){for(u=0,w=v.size();u<w;++u){x.appendChild(v.item(u));}}else{x.appendChild(v);}}},_getPreciseHeight:function(u){var L=(u)?u.get(g):0,v="getBoundingClientRect";if(u&&u.hasMethod(v)){var Y=u.invoke(v);if(Y){L=Y.bottom-Y.top;}}return L;},_findStdModSection:function(L){return this.get(U).query("> ."+s.SECTION_CLASS_NAMES[L]);},_parseStdModHTML:function(x){var w=this._findStdModSection(x),u,Y;if(w){u=w.get(M).invoke(l);Y=w.get(G);for(var L=Y.size()-1;L>=0;L--){var v=u.get(d);if(v){u.insertBefore(Y.item(L),v);}else{u.appendChild(Y.item(L));}}return u;}return null;},_getStdModContent:function(L){return(this[L+T])?this[L+T].get(G):null;},setStdModContent:function(u,Y,L){this.set(u+i,Y,{stdModPosition:L});},getStdModNode:function(L){return this[L+T]||null;},fillHeight:function(u){if(u){var y=this.get(p),AA=[this.headerNode,this.bodyNode,this.footerNode],Y,AB=0,AC=0,x=0,w=false;for(var z=0,v=AA.length;z<v;z++){Y=AA[z];if(Y){if(Y!==u){AC+=this._getPreciseHeight(Y);}else{w=true;}}}if(w){if(W.ie||W.opera){u.setStyle(Z,0+t);}AB=parseInt(y.getComputedStyle(Z),10);if(D.isNumber(AB)){x=AB-AC;if(x>=0){u.setStyle(Z,x+t);}var L=this.get(U).get(g);if(L!=AB){x=x-(L-AB);u.setStyle(Z,x+t);}}}}}};A.WidgetStdMod=s;},"3.0.0",{requires:["widget"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget-stdmod', function(Y) {
+
+/**
+ * Provides standard module support for Widgets through an extension.
+ *
+ * @module widget-stdmod
+ */
+ var L = Y.Lang,
+ Node = Y.Node,
+ NodeList = Y.NodeList,
+ UA = Y.UA,
+ Widget = Y.Widget,
+
+ EMPTY = "",
+ HD = "hd",
+ BD = "bd",
+ FT = "ft",
+ HEADER = "header",
+ BODY = "body",
+ FOOTER = "footer",
+ FILL_HEIGHT = "fillHeight",
+ STDMOD = "stdmod",
+
+ PX = "px",
+ NODE_SUFFIX = "Node",
+ CONTENT_SUFFIX = "Content",
+ INNER_HTML = "innerHTML",
+ FIRST_CHILD = "firstChild",
+ CHILD_NODES = "childNodes",
+ CREATE_DOCUMENT_FRAGMENT = "createDocumentFragment",
+ OWNER_DOCUMENT = "ownerDocument",
+
+ CONTENT_BOX = "contentBox",
+ BOUNDING_BOX = "boundingBox",
+
+ HEIGHT = "height",
+ OFFSET_HEIGHT = "offsetHeight",
+ AUTO = "auto",
+
+ HeaderChange = "headerContentChange",
+ BodyChange = "bodyContentChange",
+ FooterChange = "footerContentChange",
+ FillHeightChange = "fillHeightChange",
+ HeightChange = "HeightChange",
+ ContentUpdate = "contentUpdate",
+
+ RENDERUI = "renderUI",
+ BINDUI = "bindUI",
+ SYNCUI = "syncUI",
+
+ UI = Y.Widget.UI_SRC;
+
+ /**
+ * Widget extension, which can be used to add Standard Module support to the
+ * base Widget class, through the <a href="Base.html#method_build">Base.build</a>
+ * method.
+ * <p>
+ * The extension adds header, body and footer sections to the Widget's content box and
+ * provides the corresponding methods and attributes to modify the contents of these sections.
+ * </p>
+ * @class WidgetStdMod
+ * @param {Object} The user configuration object
+ */
+ function StdMod(config) {
+
+ this._stdModNode = this.get(CONTENT_BOX);
+
+ Y.after(this._renderUIStdMod, this, RENDERUI);
+ Y.after(this._bindUIStdMod, this, BINDUI);
+ Y.after(this._syncUIStdMod, this, SYNCUI);
+ }
+
+ /**
+ * Constant used to refer the the standard module header, in methods which expect a section specifier
+ *
+ * @property WidgetStdMod.HEADER
+ * @static
+ * @type String
+ */
+ StdMod.HEADER = HEADER;
+ /**
+ * Constant used to refer the the standard module body, in methods which expect a section specifier
+ *
+ * @property WidgetStdMod.BODY
+ * @static
+ * @type String
+ */
+ StdMod.BODY = BODY;
+ /**
+ * Constant used to refer the the standard module footer, in methods which expect a section specifier
+ *
+ * @property WidgetStdMod.FOOTER
+ * @static
+ * @type String
+ */
+ StdMod.FOOTER = FOOTER;
+
+ /**
+ * Constant used to specify insertion position, when adding content to sections of the standard module in
+ * methods which expect a "where" argument.
+ * <p>
+ * Inserts new content <em>before</em> the sections existing content.
+ * </p>
+ * @property WidgetStdMod.AFTER
+ * @static
+ * @type String
+ */
+ StdMod.AFTER = "after";
+
+ /**
+ * Constant used to specify insertion position, when adding content to sections of the standard module in
+ * methods which expect a "where" argument.
+ * <p>
+ * Inserts new content <em>before</em> the sections existing content.
+ * </p>
+ * @property WidgetStdMod.BEFORE
+ * @static
+ * @type String
+ */
+ StdMod.BEFORE = "before";
+ /**
+ * Constant used to specify insertion position, when adding content to sections of the standard module in
+ * methods which expect a "where" argument.
+ * <p>
+ * <em>Replaces</em> the sections existing content, with new content.
+ * </p>
+ * @property WidgetStdMod.REPLACE
+ * @static
+ * @type String
+ */
+ StdMod.REPLACE = "replace";
+
+ var STD_HEADER = StdMod.HEADER,
+ STD_BODY = StdMod.BODY,
+ STD_FOOTER = StdMod.FOOTER,
+ AFTER = StdMod.AFTER,
+ BEFORE = StdMod.BEFORE;
+
+ /**
+ * Static property used to define the default attribute
+ * configuration introduced by WidgetStdMod.
+ *
+ * @property WidgetStdMod.ATTRS
+ * @type Object
+ * @static
+ */
+ StdMod.ATTRS = {
+
+ /**
+ * @attribute headerContent
+ * @type {String | Node}
+ * @default undefined
+ * @description The content to be added to the header section. This will replace any existing content
+ * in the header. If you want to append, or insert new content, use the <a href="#method_setStdModContent">setStdModContent</a> method.
+ */
+ headerContent: {
+ value:null
+ },
+
+ /**
+ * @attribute footerContent
+ * @type {String | Node}
+ * @default undefined
+ * @description The content to be added to the footer section. This will replace any existing content
+ * in the footer. If you want to append, or insert new content, use the <a href="#method_setStdModContent">setStdModContent</a> method.
+ */
+ footerContent: {
+ value:null
+ },
+
+ /**
+ * @attribute bodyContent
+ * @type {String | Node}
+ * @default undefined
+ * @description The content to be added to the body section. This will replace any existing content
+ * in the body. If you want to append, or insert new content, use the <a href="#method_setStdModContent">setStdModContent</a> method.
+ */
+ bodyContent: {
+ value:null
+ },
+
+ /**
+ * @attribute fillHeight
+ * @type {String}
+ * @default WidgetStdMod.BODY
+ * @description The section (WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER) which should be resized to fill the height of the standard module, when a
+ * height is set on the Widget. If a height is not set on the widget, then all sections are sized based on
+ * their content.
+ */
+ fillHeight: {
+ value: StdMod.BODY,
+ validator: function(val) {
+ return this._validateFillHeight(val);
+ }
+ }
+ };
+
+ /**
+ * The HTML parsing rules for the WidgetStdMod class.
+ *
+ * @property WidgetStdMod.HTML_PARSER
+ * @static
+ * @type Object
+ */
+ StdMod.HTML_PARSER = {
+ headerContent: function(contentBox) {
+ return this._parseStdModHTML(STD_HEADER);
+ },
+
+ bodyContent: function(contentBox) {
+ return this._parseStdModHTML(STD_BODY);
+ },
+
+ footerContent : function(contentBox) {
+ return this._parseStdModHTML(STD_FOOTER);
+ }
+ };
+
+ /**
+ * Static hash of default class names used for the header,
+ * body and footer sections of the standard module, keyed by
+ * the section identifier (WidgetStdMod.STD_HEADER, WidgetStdMod.STD_BODY, WidgetStdMod.STD_FOOTER)
+ *
+ * @property WidgetStdMod.SECTION_CLASS_NAMES
+ * @static
+ * @type Object
+ */
+ StdMod.SECTION_CLASS_NAMES = {
+ header: Widget.getClassName(HD),
+ body: Widget.getClassName(BD),
+ footer: Widget.getClassName(FT)
+ };
+
+ /**
+ * The template HTML strings for each of the standard module sections. Section entries are keyed by the section constants,
+ * WidgetStdMod.HEADER, WidgetStdMod.BODY, WidgetStdMod.FOOTER, and contain the HTML to be added for each section.
+ * e.g.
+ * <pre>
+ * {
+ * header : '<div class="yui-widget-hd"></div>',
+ * body : '<div class="yui-widget-bd"></div>',
+ * footer : '<div class="yui-widget-ft"></div>'
+ * }
+ * </pre>
+ * @property WidgetStdMod.TEMPLATES
+ * @type Object
+ * @static
+ */
+ StdMod.TEMPLATES = {
+ header : '<div class="' + StdMod.SECTION_CLASS_NAMES[STD_HEADER] + '"></div>',
+ body : '<div class="' + StdMod.SECTION_CLASS_NAMES[STD_BODY] + '"></div>',
+ footer : '<div class="' + StdMod.SECTION_CLASS_NAMES[STD_FOOTER] + '"></div>'
+ };
+
+ StdMod.prototype = {
+
+ /**
+ * Synchronizes the UI to match the Widgets standard module state.
+ * <p>
+ * This method is invoked after syncUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _syncUIStdMod
+ * @protected
+ */
+ _syncUIStdMod : function() {
+ this._uiSetStdMod(STD_HEADER, this.get(STD_HEADER + CONTENT_SUFFIX));
+ this._uiSetStdMod(STD_BODY, this.get(STD_BODY + CONTENT_SUFFIX));
+ this._uiSetStdMod(STD_FOOTER, this.get(STD_FOOTER + CONTENT_SUFFIX));
+ this._uiSetFillHeight(this.get(FILL_HEIGHT));
+ },
+
+ /**
+ * Creates/Initializes the DOM for standard module support.
+ * <p>
+ * This method is invoked after renderUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _renderUIStdMod
+ * @protected
+ */
+ _renderUIStdMod : function() {
+ this._stdModNode.addClass(Widget.getClassName(STDMOD));
+ },
+
+ /**
+ * Binds event listeners responsible for updating the UI state in response to
+ * Widget standard module related state changes.
+ * <p>
+ * This method is invoked after bindUI is invoked for the Widget class
+ * using YUI's aop infrastructure.
+ * </p>
+ * @method _bindUIStdMod
+ * @protected
+ */
+ _bindUIStdMod : function() {
+ this.after(HeaderChange, this._afterHeaderChange);
+ this.after(BodyChange, this._afterBodyChange);
+ this.after(FooterChange, this._afterFooterChange);
+
+ this.after(FillHeightChange, this._afterFillHeightChange);
+ this.after(HeightChange, this._fillHeight);
+ this.after(ContentUpdate, this._fillHeight);
+ },
+
+ /**
+ * Default attribute change listener for the headerContent attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterHeaderChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterHeaderChange : function(e) {
+ if (e.src !== UI) {
+ this._uiSetStdMod(STD_HEADER, e.newVal, e.stdModPosition);
+ }
+ },
+
+ /**
+ * Default attribute change listener for the bodyContent attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterBodyChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterBodyChange : function(e) {
+ if (e.src !== UI) {
+ this._uiSetStdMod(STD_BODY, e.newVal, e.stdModPosition);
+ }
+ },
+
+ /**
+ * Default attribute change listener for the footerContent attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterFooterChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterFooterChange : function(e) {
+ if (e.src !== UI) {
+ this._uiSetStdMod(STD_FOOTER, e.newVal, e.stdModPosition);
+ }
+ },
+
+ /**
+ * Default attribute change listener for the fillHeight attribute, responsible
+ * for updating the UI, in response to attribute changes.
+ *
+ * @method _afterFillHeightChange
+ * @protected
+ * @param {EventFacade} e The event facade for the attribute change
+ */
+ _afterFillHeightChange: function (e) {
+ this._uiSetFillHeight(e.newVal);
+ },
+
+ /**
+ * Default validator for the fillHeight attribute. Verifies that the
+ * value set is a valid section specifier - one of WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER,
+ * or a falsey value if fillHeight is to be disabled.
+ *
+ * @method _validateFillHeight
+ * @protected
+ * @param {String} val The section which should be setup to fill height, or false/null to disable fillHeight
+ * @return true if valid, false if not
+ */
+ _validateFillHeight : function(val) {
+ return !val || val == StdMod.BODY || val == StdMod.HEADER || val == StdMod.FOOTER;
+ },
+
+ /**
+ * Updates the rendered UI, to resize the provided section so that the standard module fills out
+ * the specified widget height. Note: This method does not check whether or not a height is set
+ * on the Widget.
+ *
+ * @method _uiSetFillHeight
+ * @protected
+ * @param {String} fillSection A valid section specifier - one of WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER
+ */
+ _uiSetFillHeight : function(fillSection) {
+ var fillNode = this.getStdModNode(fillSection);
+ var currNode = this._currFillNode;
+
+ if (currNode && fillNode !== currNode){
+ currNode.setStyle(HEIGHT, EMPTY);
+ }
+
+ if (fillNode) {
+ this._currFillNode = fillNode;
+ }
+
+ this._fillHeight();
+ },
+
+ /**
+ * Updates the rendered UI, to resize the current section specified by the fillHeight attribute, so
+ * that the standard module fills out the Widget height. If a height has not been set on Widget,
+ * the section is not resized (height is set to "auto").
+ *
+ * @method _fillHeight
+ * @private
+ */
+ _fillHeight : function() {
+ if (this.get(FILL_HEIGHT)) {
+ var height = this.get(HEIGHT);
+ if (height != EMPTY && height != AUTO) {
+ this.fillHeight(this._currFillNode);
+ }
+ }
+ },
+
+ /**
+ * Updates the rendered UI, adding the provided content (either an HTML string, or node reference),
+ * to the specified section. The content is either added before, after or replaces existing content
+ * in the section, based on the value of the <code>where</code> argument.
+ *
+ * @method _uiSetStdMod
+ * @protected
+ *
+ * @param {String} section The section to be updated. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @param {String | Node} content The new content (either as an HTML string, or Node reference) to add to the section
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace existing content in the section.
+ */
+ _uiSetStdMod : function(section, content, where) {
+ if (content) {
+ var node = this.getStdModNode(section) || this._renderStdMod(section);
+ if (content instanceof Node || content instanceof NodeList) {
+ this._addNodeRef(node, content, where);
+ } else {
+ this._addNodeHTML(node, content, where);
+ }
+ this.set(section + CONTENT_SUFFIX, this._getStdModContent(section), {src:UI});
+ this.fire(ContentUpdate);
+ }
+ },
+
+ /**
+ * Creates the DOM node for the given section, and inserts it into the correct location in the contentBox.
+ *
+ * @method _renderStdMod
+ * @protected
+ * @param {String} section The section to create/render. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} A reference to the added section node
+ */
+ _renderStdMod : function(section) {
+
+ var contentBox = this.get(CONTENT_BOX),
+ sectionNode = this._findStdModSection(section);
+
+ if (!sectionNode) {
+ sectionNode = this._getStdModTemplate(section);
+ }
+
+ this._insertStdModSection(contentBox, section, sectionNode);
+
+ this[section + NODE_SUFFIX] = sectionNode;
+ return this[section + NODE_SUFFIX];
+ },
+
+ /**
+ * Helper method to insert the Node for the given section into the correct location in the contentBox.
+ *
+ * @method _insertStdModSection
+ * @private
+ * @param {Node} contentBox A reference to the Widgets content box.
+ * @param {String} section The section to create/render. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @param {Node} sectionNode The Node for the section.
+ */
+ _insertStdModSection : function(contentBox, section, sectionNode) {
+ var fc = contentBox.get(FIRST_CHILD);
+
+ if (section === STD_FOOTER || !fc) {
+ contentBox.appendChild(sectionNode);
+ } else {
+ if (section === STD_HEADER) {
+ contentBox.insertBefore(sectionNode, fc);
+ } else {
+ // BODY
+ var footer = this[STD_FOOTER + NODE_SUFFIX];
+ if (footer) {
+ contentBox.insertBefore(sectionNode, footer);
+ } else {
+ contentBox.appendChild(sectionNode);
+ }
+ }
+ }
+ },
+
+ /**
+ * Gets a new Node reference for the given standard module section, by cloning
+ * the stored template node.
+ *
+ * @method _getStdModTemplate
+ * @protected
+ * @param {String} section The section to create a new node for. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The new Node instance for the section
+ */
+ _getStdModTemplate : function(section) {
+ return Node.create(StdMod.TEMPLATES[section], this._stdModNode.get(OWNER_DOCUMENT));
+ },
+
+ /**
+ * Helper method to add the given HTML string to the node reference provided.
+ * The HTML is added either before, after or replaces the existing node content
+ * based on the value of the <code>where</code> argument.
+ *
+ * @method _addNodeHTML
+ * @private
+ *
+ * @param {Node} node The section Node to be updated.
+ * @param {String} html The new content HTML string to be added to the section Node.
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace Nodes existing content.
+ */
+ _addNodeHTML : function(node, html, where) {
+ if (where == AFTER) {
+ node.set(INNER_HTML, node.get(INNER_HTML) + html);
+ } else if (where == BEFORE) {
+ node.set(INNER_HTML, html + node.get(INNER_HTML));
+ } else {
+ node.set(INNER_HTML, html);
+ }
+ },
+
+ /**
+ * Helper method to add nodes, to another node.
+ * The child node(s) are added either before, after or replaces the existing node content
+ * based on the value of the <code>where</code> argument.
+ *
+ * @method _addNodeRef
+ * @private
+ *
+ * @param {Node} node The section Node to be updated.
+ * @param {Node|NodeList} children The new content Node, or NodeList to be added to section Node provided.
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace existing content in the Node.
+ */
+ _addNodeRef : function(node, children, where) {
+ var append = true,
+ i, s;
+
+ if (where == BEFORE) {
+ var n = node.get(FIRST_CHILD);
+ if (n) {
+ if (children instanceof NodeList) {
+ for (i = children.size() - 1; i >=0; --i) {
+ node.insertBefore(children.item(i), n);
+ }
+ } else {
+ node.insertBefore(children, n);
+ }
+ append = false;
+ }
+ } else if (where != AFTER) { // replace
+ node.set(INNER_HTML, EMPTY);
+ }
+
+ if (append) {
+ if (children instanceof NodeList) {
+ for (i = 0, s = children.size(); i < s; ++i) {
+ node.appendChild(children.item(i));
+ }
+ } else {
+ node.appendChild(children);
+ }
+ }
+ },
+
+ /**
+ * Helper method to obtain the precise height of the node provided, including padding and border.
+ * The height could be a sub-pixel value for certain browsers, such as Firefox 3.
+ *
+ * @method _getPreciseHeight
+ * @private
+ * @param {Node} node The node for which the precise height is required.
+ * @return {Number} The height of the Node including borders and padding, possibly a float.
+ */
+ _getPreciseHeight : function(node) {
+ var height = (node) ? node.get(OFFSET_HEIGHT) : 0,
+ getBCR = "getBoundingClientRect";
+
+ if (node && node.hasMethod(getBCR)) {
+ var preciseRegion = node.invoke(getBCR);
+ if (preciseRegion) {
+ height = preciseRegion.bottom - preciseRegion.top;
+ }
+ }
+
+ return height;
+ },
+
+ /**
+ * Helper method to query the rendered contents of the contentBox to find the
+ * node for the given section if it exists.
+ *
+ * @method _findStdModSection
+ * @private
+ * @param {String} section The section for which the render Node is to be found. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The rendered node for the given section, or null if not found.
+ */
+ _findStdModSection: function(section) {
+ return this.get(CONTENT_BOX).query("> ." + StdMod.SECTION_CLASS_NAMES[section]);
+ },
+
+ /**
+ * Utility method, used by WidgetStdMods HTML_PARSER implementation
+ * to extract data for each section from markup.
+ *
+ * @method _parseStdModHTML
+ * @private
+ * @param {String} section
+ * @return {String} Inner HTML string with the contents of the section
+ */
+ _parseStdModHTML : function(section) {
+ var node = this._findStdModSection(section),
+ docFrag, children;
+
+ if (node) {
+ docFrag = node.get(OWNER_DOCUMENT).invoke(CREATE_DOCUMENT_FRAGMENT);
+ children = node.get(CHILD_NODES);
+
+ for (var i = children.size() - 1; i >= 0; i--) {
+ var fc = docFrag.get(FIRST_CHILD);
+ if (fc) {
+ docFrag.insertBefore(children.item(i), fc);
+ } else {
+ docFrag.appendChild(children.item(i));
+ }
+ }
+
+ return docFrag;
+ }
+
+ return null;
+ },
+
+ /**
+ * Retrieves the child nodes (content) of a standard module section
+ *
+ * @method _getStdModContent
+ * @private
+ * @param {String} section The standard module section whose child nodes are to be retrieved. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The child node collection of the standard module section.
+ */
+ _getStdModContent : function(section) {
+ return (this[section + NODE_SUFFIX]) ? this[section + NODE_SUFFIX].get(CHILD_NODES) : null;
+ },
+
+ /**
+ * Updates the body section of the standard module with the content provided (either an HTML string, or node reference).
+ * <p>
+ * This method can be used instead of the corresponding section content attribute if you'd like to retain the current content of the section,
+ * and insert content before or after it, by specifying the <code>where</code> argument.
+ * </p>
+ * @method setStdModContent
+ * @param {String} section The standard module section whose content is to be updated. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @param {String | Node} content The content to be added, either an HTML string or a Node reference.
+ * @param {String} where Optional. Either WidgetStdMod.AFTER, WidgetStdMod.BEFORE or WidgetStdMod.REPLACE.
+ * If not provided, the content will replace existing content in the section.
+ */
+ setStdModContent : function(section, content, where) {
+ this.set(section + CONTENT_SUFFIX, content, {stdModPosition:where});
+ },
+
+ /**
+ * Returns the node reference for the given section. Note: The DOM is not queried for the node reference. The reference
+ * stored by the widget instance is returned if set.
+ *
+ * @method getStdModNode
+ * @param {String} section The section whose node reference is required. Either WidgetStdMod.HEADER, WidgetStdMod.BODY or WidgetStdMod.FOOTER.
+ * @return {Node} The node reference for the section, or null if not set.
+ */
+ getStdModNode : function(section) {
+ return this[section + NODE_SUFFIX] || null;
+ },
+
+ /**
+ * Sets the height on the provided header, body or footer element to
+ * fill out the height of the Widget. It determines the height of the
+ * widgets bounding box, based on it's configured height value, and
+ * sets the height of the provided section to fill out any
+ * space remaining after the other standard module section heights
+ * have been accounted for.
+ *
+ * <p><strong>NOTE:</strong> This method is not designed to work if an explicit
+ * height has not been set on the Widget, since for an "auto" height Widget,
+ * the heights of the header/body/footer will drive the height of the Widget.</p>
+ *
+ * @method fillHeight
+ * @param {Node} node The node which should be resized to fill out the height
+ * of the Widget bounding box. Should be a standard module section node which belongs
+ * to the widget.
+ */
+ fillHeight : function(node) {
+ if (node) {
+ var boundingBox = this.get(BOUNDING_BOX),
+ stdModNodes = [this.headerNode, this.bodyNode, this.footerNode],
+ stdModNode,
+ total = 0,
+ filled = 0,
+ remaining = 0,
+ validNode = false;
+
+ for (var i = 0, l = stdModNodes.length; i < l; i++) {
+ stdModNode = stdModNodes[i];
+ if (stdModNode) {
+ if (stdModNode !== node) {
+ filled += this._getPreciseHeight(stdModNode);
+ } else {
+ validNode = true;
+ }
+ }
+ }
+
+ if (validNode) {
+ if (UA.ie || UA.opera) {
+ // Need to set height to 0, to allow height to be reduced
+ node.setStyle(HEIGHT, 0 + PX);
+ }
+
+ total = parseInt(boundingBox.getComputedStyle(HEIGHT), 10);
+ if (L.isNumber(total)) {
+ remaining = total - filled;
+
+ if (remaining >= 0) {
+ node.setStyle(HEIGHT, remaining + PX);
+ }
+
+ // Re-adjust height if required, to account for el padding and border
+ var offsetHeight = this.get(CONTENT_BOX).get(OFFSET_HEIGHT);
+ if (offsetHeight != total) {
+ remaining = remaining - (offsetHeight - total);
+ node.setStyle(HEIGHT, remaining + PX);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ Y.WidgetStdMod = StdMod;
+
+
+}, '3.0.0' ,{requires:['widget']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('widget', function(Y) {
+
+/**
+ * Provides the base Widget class
+ *
+ * @module widget
+ */
+
+// Local Constants
+var L = Y.Lang,
+ O = Y.Object,
+ Node = Y.Node,
+ ClassNameManager = Y.ClassNameManager,
+
+ WIDGET = "widget",
+ CONTENT = "content",
+ VISIBLE = "visible",
+ HIDDEN = "hidden",
+ DISABLED = "disabled",
+ FOCUSED = "focused",
+ WIDTH = "width",
+ HEIGHT = "height",
+ EMPTY = "",
+ HYPHEN = "-",
+ BOUNDING_BOX = "boundingBox",
+ CONTENT_BOX = "contentBox",
+ PARENT_NODE = "parentNode",
+ FIRST_CHILD = "firstChild",
+ OWNER_DOCUMENT = "ownerDocument",
+ BODY = "body",
+ TAB_INDEX = "tabIndex",
+ LOCALE = "locale",
+ INIT_VALUE = "initValue",
+ ID = "id",
+ RENDER = "render",
+ RENDERED = "rendered",
+ DESTROYED = "destroyed",
+
+ ContentUpdate = "contentUpdate",
+
+ // Widget nodeid-to-instance map for now, 1-to-1.
+ _instances = {};
+
+/**
+ * A base class for widgets, providing:
+ * <ul>
+ * <li>The render lifecycle method, in addition to the init and destroy
+ * lifecycle methods provide by Base</li>
+ * <li>Abstract methods to support consistent MVC structure across
+ * widgets: renderer, renderUI, bindUI, syncUI</li>
+ * <li>Support for common widget attributes, such as boundingBox, contentBox, visible,
+ * disabled, focused, strings</li>
+ * </ul>
+ *
+ * @param config {Object} Object literal specifying widget configuration
+ * properties.
+ *
+ * @class Widget
+ * @constructor
+ * @extends Base
+ */
+function Widget(config) {
+
+ this._yuid = Y.guid(WIDGET);
+ this._strings = {};
+
+ Widget.superclass.constructor.apply(this, arguments);
+}
+
+/**
+ * The build configuration for the Widget class.
+ * <p>
+ * Defines the static fields which need to be aggregated,
+ * when this class is used as the main class passed to
+ * the <a href="Base.html#method_build">Base.build</a> method.
+ * </p>
+ * @property _buildCfg
+ * @type Object
+ * @static
+ * @final
+ * @private
+ */
+Widget._buildCfg = {
+ aggregates : ["HTML_PARSER"]
+};
+
+/**
+ * Static property provides a string to identify the class.
+ * <p>
+ * Currently used to apply class identifiers to the bounding box
+ * and to classify events fired by the widget.
+ * </p>
+ *
+ * @property Widget.NAME
+ * @type String
+ * @static
+ */
+Widget.NAME = WIDGET;
+
+/**
+ * Constant used to identify state changes originating from
+ * the DOM (as opposed to the JavaScript model).
+ *
+ * @property Widget.UI_SRC
+ * @type String
+ * @static
+ * @final
+ */
+Widget.UI_SRC = "ui";
+
+var UI = Widget.UI_SRC;
+
+/**
+ * Static property used to define the default attribute
+ * configuration for the Widget.
+ *
+ * @property Widget.ATTRS
+ * @type Object
+ * @static
+ */
+Widget.ATTRS = {
+
+ /**
+ * Flag indicating whether or not this object
+ * has been through the render lifecycle phase.
+ *
+ * @attribute rendered
+ * @readOnly
+ * @default false
+ * @type boolean
+ */
+ rendered: {
+ value:false,
+ readOnly:true
+ },
+
+ /**
+ * @attribute boundingBox
+ * @description The outermost DOM node for the Widget, used for sizing and positioning
+ * of a Widget as well as a containing element for any decorator elements used
+ * for skinning.
+ * @type Node
+ */
+ boundingBox: {
+ value:null,
+ setter: function(node) {
+ return this._setBoundingBox(node);
+ },
+ writeOnce: true
+ },
+
+ /**
+ * @attribute contentBox
+ * @description A DOM node that is a direct descendent of a Widget's bounding box that
+ * houses its content.
+ * @type Node
+ */
+ contentBox: {
+ value:null,
+ setter: function(node) {
+ return this._setContentBox(node);
+ },
+ writeOnce: true
+ },
+
+ /**
+ * @attribute tabIndex
+ * @description Number (between -32767 to 32767) indicating the widget's
+ * position in the default tab flow. The value is used to set the
+ * "tabIndex" attribute on the widget's bounding box. Negative values allow
+ * the widget to receive DOM focus programmatically (by calling the focus
+ * method), while being removed from the default tab flow. A value of
+ * null removes the "tabIndex" attribute from the widget's bounding box.
+ * @type Number
+ * @default null
+ */
+ tabIndex: {
+
+ value: 0,
+ validator: function (val) {
+ return (L.isNumber(val) || L.isNull(val));
+ }
+
+ },
+
+ /**
+ * @attribute focused
+ * @description Boolean indicating if the Widget, or one of its descendants,
+ * has focus.
+ * @readOnly
+ * @default false
+ * @type boolean
+ */
+ focused: {
+ value: false,
+ readOnly:true
+ },
+
+ /**
+ * @attribute disabled
+ * @description Boolean indicating if the Widget should be disabled. The disabled implementation
+ * is left to the specific classes extending widget.
+ * @default false
+ * @type boolean
+ */
+ disabled: {
+ value: false
+ },
+
+ /**
+ * @attribute visible
+ * @description Boolean indicating weather or not the Widget is visible.
+ * @default true
+ * @type boolean
+ */
+ visible: {
+ value: true
+ },
+
+ /**
+ * @attribute height
+ * @description String with units, or number, representing the height of the Widget. If a number is provided,
+ * the default unit, defined by the Widgets DEF_UNIT, property is used.
+ * @default ""
+ * @type {String | Number}
+ */
+ height: {
+ value: EMPTY
+ },
+
+ /**
+ * @attribute width
+ * @description String with units, or number, representing the width of the Widget. If a number is provided,
+ * the default unit, defined by the Widgets DEF_UNIT, property is used.
+ * @default ""
+ * @type {String | Number}
+ */
+ width: {
+ value: EMPTY
+ },
+
+ /**
+ * @attribute moveStyles
+ * @description Flag defining whether or not style properties from the content box
+ * should be moved to the bounding box when wrapped (as defined by the WRAP_STYLES property)
+ * @default false
+ * @type boolean
+ */
+ moveStyles: {
+ value: false
+ },
+
+ /**
+ * @attribute locale
+ * @description
+ * The default locale for the widget. NOTE: Using get/set on the "strings" attribute will
+ * return/set strings for this locale.
+ * @default "en"
+ * @type String
+ */
+ locale : {
+ value: "en"
+ },
+
+ /**
+ * @attribute strings
+ * @description Collection of strings used to label elements of the Widget's UI.
+ * @default null
+ * @type Object
+ */
+ strings: {
+ setter: function(val) {
+ return this._setStrings(val, this.get(LOCALE));
+ },
+
+ getter: function() {
+ return this.getStrings(this.get(LOCALE));
+ }
+ }
+};
+
+/**
+ * Cached lowercase version of Widget.NAME
+ *
+ * @property Widget._NAME_LOWERCASE
+ * @private
+ * @static
+ */
+Widget._NAME_LOWERCASE = Widget.NAME.toLowerCase();
+
+/**
+ * Generate a standard prefixed classname for the Widget, prefixed by the default prefix defined
+ * by the <code>Y.config.classNamePrefix</code> attribute used by <code>ClassNameManager</code> and
+ * <code>Widget.NAME.toLowerCase()</code> (e.g. "yui-widget-xxxxx-yyyyy", based on default values for
+ * the prefix and widget class name).
+ * <p>
+ * The instance based version of this method can be used to generate standard prefixed classnames,
+ * based on the instances NAME, as opposed to Widget.NAME. This method should be used when you
+ * need to use a constant class name across different types instances.
+ * </p>
+ * @method getClassName
+ * @param {String*} args* 0..n strings which should be concatenated, using the default separator defined by ClassNameManager, to create the class name
+ */
+Widget.getClassName = function() {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(0, 0, this._NAME_LOWERCASE);
+ return ClassNameManager.getClassName.apply(ClassNameManager, args);
+};
+
+/**
+ * Returns the widget instance whose bounding box contains, or is, the given node.
+ * <p>
+ * In the case of nested widgets, the nearest bounding box ancestor is used to
+ * return the widget instance.
+ * </p>
+ * @method Widget.getByNode
+ * @static
+ * @param node {Node | String} The node for which to return a Widget instance. If a selector
+ * string is passed in, which selects more than one node, the first node found is used.
+ * @return {Widget} Widget instance, or null if not found.
+ */
+Widget.getByNode = function(node) {
+ var widget,
+ bbMarker = Widget.getClassName();
+
+ node = Node.get(node);
+ if (node) {
+ node = (node.hasClass(bbMarker)) ? node : node.ancestor("." + bbMarker);
+ if (node) {
+ widget = _instances[node.get(ID)];
+ }
+ }
+
+ return widget || null;
+};
+
+/**
+ * Object hash, defining how attribute values are to be parsed from
+ * markup contained in the widget's content box. e.g.:
+ * <pre>
+ * {
+ * // Set single Node references using selector syntax
+ * // (selector is run through node.query)
+ * titleNode: "span.yui-title",
+ * // Set NodeList references using selector syntax
+ * // (array indicates selector is to be run through node.queryAll)
+ * listNodes: ["li.yui-item"],
+ * // Set other attribute types, using a parse function.
+ * // Context is set to the widget instance.
+ * label: function(contentBox) {
+ * return contentBox.query("span.title").get("innerHTML");
+ * }
+ * }
+ * </pre>
+ *
+ * @property Widget.HTML_PARSER
+ * @type Object
+ * @static
+ */
+Widget.HTML_PARSER = {};
+
+Y.extend(Widget, Y.Base, {
+
+ /**
+ * Returns a class name prefixed with the the value of the
+ * <code>YUI.config.classNamePrefix</code> attribute + the instances <code>NAME</code> property.
+ * Uses <code>YUI.config.classNameDelimiter</code> attribute to delimit the provided strings.
+ * e.g.
+ * <code>
+ * <pre>
+ * // returns "yui-slider-foo-bar", for a slider instance
+ * var scn = slider.getClassName('foo','bar');
+ *
+ * // returns "yui-overlay-foo-bar", for an overlay instance
+ * var ocn = slider.getClassName('foo','bar');
+ * </pre>
+ * </code>
+ *
+ * @method getClassName
+ * @param {String}+ One or more classname bits to be joined and prefixed
+ */
+ getClassName: function () {
+ var args = Y.Array(arguments, 0, true);
+ args.splice(0, 0, this._name);
+ return ClassNameManager.getClassName.apply(ClassNameManager, args);
+ },
+
+ /**
+ * Initializer lifecycle implementation for the Widget class. Registers the
+ * widget instance, and runs through the Widget's HTML_PARSER definition.
+ *
+ * @method initializer
+ * @protected
+ * @param config {Object} Configuration object literal for the widget
+ */
+ initializer: function(config) {
+
+ /**
+ * Notification event, which widget implementations can fire, when
+ * they change the content of the widget. This event has no default
+ * behavior and cannot be prevented, so the "on" or "after"
+ * moments are effectively equivalent (with on listeners being invoked before
+ * after listeners).
+ *
+ * @event widget:contentUpdate
+ * @preventable false
+ * @param {EventFacade} e The Event Facade
+ */
+ this.publish(ContentUpdate, { preventable:false });
+
+ this._name = this.constructor.NAME.toLowerCase();
+
+ var nodeId = this.get(BOUNDING_BOX).get(ID);
+ if (nodeId) {
+ _instances[nodeId] = this;
+ }
+
+ var htmlConfig = this._parseHTML(this.get(CONTENT_BOX));
+ if (htmlConfig) {
+ Y.aggregate(config, htmlConfig, false);
+ }
+ },
+
+ /**
+ * Descructor lifecycle implementation for the Widget class. Purges events attached
+ * to the bounding box (and all child nodes) and removes the Widget from the
+ * list of registered widgets.
+ *
+ * @method destructor
+ * @protected
+ */
+ destructor: function() {
+
+ var boundingBox = this.get(BOUNDING_BOX);
+
+ Y.Event.purgeElement(boundingBox, true);
+
+ var nodeId = boundingBox.get(ID);
+ if (nodeId && nodeId in _instances) {
+ delete _instances[nodeId];
+ }
+ },
+
+ /**
+ * Establishes the initial DOM for the widget. Invoking this
+ * method will lead to the creating of all DOM elements for
+ * the widget (or the manipulation of existing DOM elements
+ * for the progressive enhancement use case).
+ * <p>
+ * This method should only be invoked once for an initialized
+ * widget.
+ * </p>
+ * <p>
+ * It delegates to the widget specific renderer method to do
+ * the actual work.
+ * </p>
+ *
+ * @method render
+ * @chainable
+ * @final
+ * @param parentNode {Object | String} Optional. The Node under which the
+ * Widget is to be rendered. This can be a Node instance or a CSS selector string.
+ * <p>
+ * If the selector string returns more than one Node, the first node will be used
+ * as the parentNode. NOTE: This argument is required if both the boundingBox and contentBox
+ * are not currently in the document. If it's not provided, the Widget will be rendered
+ * to the body of the current document in this case.
+ * </p>
+ */
+ render: function(parentNode) {
+
+ if (this.get(DESTROYED)) {
+ return;
+ }
+
+ if (!this.get(RENDERED)) {
+ /**
+ * Lifcyle event for the render phase, fired prior to rendering the UI
+ * for the widget (prior to invoking the widgets renderer method).
+ * <p>
+ * Subscribers to the "on" moment of this event, will be notified
+ * before the widget is rendered.
+ * </p>
+ * <p>
+ * Subscribers to the "after" momemt of this event, will be notified
+ * after rendering is complete.
+ * </p>
+ *
+ * @event widget:render
+ * @preventable _defRenderFn
+ * @param {EventFacade} e The Event Facade
+ */
+ this.publish(RENDER, {queuable:false, defaultFn: this._defRenderFn});
+
+ parentNode = (parentNode) ? Node.get(parentNode) : null;
+ if (parentNode && !parentNode.inDoc()) {
+ parentNode = null;
+ }
+
+ this.fire(RENDER, {parentNode: parentNode});
+ }
+
+ return this;
+ },
+
+ /**
+ * Default render handler
+ *
+ * @method _defRenderFn
+ * @protected
+ * @param {EventFacade} e The Event object
+ * @param {Node} parentNode The parent node to render to, if passed in to the <code>render</code> method
+ */
+ _defRenderFn : function(e) {
+
+ this._renderUI(e.parentNode);
+ this._bindUI();
+ this._syncUI();
+
+ this.renderer();
+
+ this._set(RENDERED, true);
+ },
+
+ /**
+ * Creates DOM (or manipulates DOM for progressive enhancement)
+ * This method is invoked by render() and is not chained
+ * automatically for the class hierarchy (like initializer, destructor)
+ * so it should be chained manually for subclasses if required.
+ *
+ * @method renderer
+ * @protected
+ */
+ renderer: function() {
+ this.renderUI();
+ this.bindUI();
+ this.syncUI();
+ },
+
+ /**
+ * Configures/Sets up listeners to bind Widget State to UI/DOM
+ *
+ * This method is not called by framework and is not chained
+ * automatically for the class hierarchy.
+ *
+ * @method bindUI
+ * @protected
+ */
+ bindUI: function() {},
+
+ /**
+ * Adds nodes to the DOM
+ *
+ * This method is not called by framework and is not chained
+ * automatically for the class hierarchy.
+ *
+ * @method renderUI
+ * @protected
+ */
+ renderUI: function() {},
+
+ /**
+ * Refreshes the rendered UI, based on Widget State
+ *
+ * This method is not called by framework and is not chained
+ * automatically for the class hierarchy.
+ *
+ * @method syncUI
+ *
+ */
+ syncUI: function(){},
+
+ /**
+ * @method hide
+ * @description Shows the Module element by setting the "visible" attribute to "false".
+ */
+ hide: function() {
+ return this.set(VISIBLE, false);
+ },
+
+ /**
+ * @method show
+ * @description Shows the Module element by setting the "visible" attribute to "true".
+ */
+ show: function() {
+ return this.set(VISIBLE, true);
+ },
+
+ /**
+ * @method focus
+ * @description Causes the Widget to receive the focus by setting the "focused"
+ * attribute to "true".
+ */
+ focus: function () {
+ return this._set(FOCUSED, true);
+ },
+
+ /**
+ * @method blur
+ * @description Causes the Widget to lose focus by setting the "focused" attribute
+ * to "false"
+ */
+ blur: function () {
+ return this._set(FOCUSED, false);
+ },
+
+ /**
+ * @method enable
+ * @description Set the Widget's "disabled" attribute to "false".
+ */
+ enable: function() {
+ return this.set(DISABLED, false);
+ },
+
+ /**
+ * @method disabled
+ * @description Set the Widget's "disabled" attribute to "true".
+ */
+ disable: function() {
+ return this.set(DISABLED, true);
+ },
+
+ /**
+ * Utilitity method used to apply the <code>HTML_PARSER</code> configuration for the
+ * instance, to retrieve config data values.
+ *
+ * @method _parseHTML
+ * @private
+ * @param node {Node} Root node to use to parse markup for configuration data
+ * @return config {Object} configuration object, with values found in the HTML, populated
+ */
+ _parseHTML : function(node) {
+
+ var schema = this._getHtmlParser(),
+ data,
+ val;
+
+ if (schema && node && node.hasChildNodes()) {
+
+ O.each(schema, function(v, k, o) {
+ val = null;
+
+ if (L.isFunction(v)) {
+ val = v.call(this, node);
+ } else {
+ if (L.isArray(v)) {
+ val = node.queryAll(v[0]);
+ } else {
+ val = node.query(v);
+ }
+ }
+
+ if (val !== null && val !== undefined) {
+ data = data || {};
+ data[k] = val;
+ }
+
+ }, this);
+ }
+
+ return data;
+ },
+
+ /**
+ * Moves a pre-defined set of style rules (WRAP_STYLES) from one node to another.
+ *
+ * @method _moveStyles
+ * @private
+ * @param {Node} nodeFrom The node to gather the styles from
+ * @param {Node} nodeTo The node to apply the styles to
+ */
+ _moveStyles: function(nodeFrom, nodeTo) {
+
+ var styles = this.WRAP_STYLES,
+ pos = nodeFrom.getStyle('position'),
+ contentBox = this.get(CONTENT_BOX),
+ xy = [0,0],
+ h, w;
+
+ if (!this.get('height')) {
+ h = contentBox.get('offsetHeight');
+ }
+
+ if (!this.get('width')) {
+ w = contentBox.get('offsetWidth');
+ }
+
+ if (pos === 'absolute') {
+ xy = nodeFrom.getXY();
+ nodeTo.setStyles({
+ right: 'auto',
+ bottom: 'auto'
+ });
+
+ nodeFrom.setStyles({
+ right: 'auto',
+ bottom: 'auto'
+ });
+ }
+
+ Y.each(styles, function(v, k) {
+ var s = nodeFrom.getStyle(k);
+ nodeTo.setStyle(k, s);
+ if (v === false) {
+ nodeFrom.setStyle(k, '');
+ } else {
+ nodeFrom.setStyle(k, v);
+ }
+ });
+
+ if (pos === 'absolute') {
+ nodeTo.setXY(xy);
+ }
+
+ if (h) {
+ this.set('height', h);
+ }
+
+ if (w) {
+ this.set('width', w);
+ }
+ },
+
+ /**
+ * Helper method to collect the boundingBox and contentBox, set styles and append to the provided parentNode, if not
+ * already a child. The owner document of the boundingBox, or the owner document of the contentBox will be used
+ * as the document into which the Widget is rendered if a parentNode is node is not provided. If both the boundingBox and
+ * the contentBox are not currently in the document, and no parentNode is provided, the widget will be rendered
+ * to the current document's body.
+ *
+ * @method _renderBox
+ * @private
+ * @param {Node} parentNode The parentNode to render the widget to. If not provided, and both the boundingBox and
+ * the contentBox are not currently in the document, the widget will be rendered to the current document's body.
+ */
+ _renderBox: function(parentNode) {
+
+ var contentBox = this.get(CONTENT_BOX),
+ boundingBox = this.get(BOUNDING_BOX),
+ doc = boundingBox.get(OWNER_DOCUMENT) || contentBox.get(OWNER_DOCUMENT),
+ body;
+
+ if (!boundingBox.compareTo(contentBox.get(PARENT_NODE))) {
+ if (this.get('moveStyles')) {
+ this._moveStyles(contentBox, boundingBox);
+ }
+ // If contentBox box is already in the document, have boundingBox box take it's place
+ if (contentBox.inDoc(doc)) {
+ contentBox.get(PARENT_NODE).replaceChild(boundingBox, contentBox);
+ }
+ boundingBox.appendChild(contentBox);
+ }
+
+ if (!boundingBox.inDoc(doc) && !parentNode) {
+ body = Node.get(BODY);
+ if (body.get(FIRST_CHILD)) {
+ // Special case when handling body as default (no parentNode), always try to insert.
+ body.insertBefore(boundingBox, body.get(FIRST_CHILD));
+ } else {
+ body.appendChild(boundingBox);
+ }
+ } else {
+ if (parentNode && !parentNode.compareTo(boundingBox.get(PARENT_NODE))) {
+ parentNode.appendChild(boundingBox);
+ }
+ }
+ },
+
+ /**
+ * Setter for the boundingBox attribute
+ *
+ * @method _setBoundingBox
+ * @private
+ * @param Node/String
+ * @return Node
+ */
+ _setBoundingBox: function(node) {
+ return this._setBox(node, this.BOUNDING_TEMPLATE);
+ },
+
+ /**
+ * Setter for the contentBox attribute
+ *
+ * @method _setContentBox
+ * @private
+ * @param {Node|String} node
+ * @return Node
+ */
+ _setContentBox: function(node) {
+ return this._setBox(node, this.CONTENT_TEMPLATE);
+ },
+
+ /**
+ * Helper method to set the bounding/content box, or create it from
+ * the provided template if not found.
+ *
+ * @method _setBox
+ * @private
+ *
+ * @param {Node|String} node The node reference
+ * @param {String} template HTML string template for the node
+ * @return {Node} The node
+ */
+ _setBox : function(node, template) {
+ node = Node.get(node) || Node.create(template);
+
+ var sid = Y.stamp(node);
+ if (!node.get(ID)) {
+ node.set(ID, sid);
+ }
+ return node;
+ },
+
+ /**
+ * Initializes the UI state for the Widget's bounding/content boxes.
+ *
+ * @method _renderUI
+ * @protected
+ * @param {Node} The parent node to rendering the widget into
+ */
+ _renderUI: function(parentNode) {
+ this._renderBoxClassNames();
+ this._renderBox(parentNode);
+ },
+
+ /**
+ * Applies standard class names to the boundingBox and contentBox
+ *
+ * @method _renderBoxClassNames
+ * @protected
+ */
+ _renderBoxClassNames : function() {
+ var classes = this._getClasses(),
+ boundingBox = this.get(BOUNDING_BOX),
+ contentBox = this.get(CONTENT_BOX),
+ name, i;
+
+ boundingBox.addClass(Widget.getClassName());
+
+ // Start from Widget Sub Class
+ for (i = classes.length-3; i >= 0; i--) {
+ name = classes[i].NAME;
+ if (name) {
+ boundingBox.addClass(ClassNameManager.getClassName(name.toLowerCase()));
+ }
+ }
+
+ // Use instance based name for content box
+ contentBox.addClass(this.getClassName(CONTENT));
+ },
+
+ /**
+ * Sets up DOM and CustomEvent listeners for the widget.
+ *
+ * @method _bindUI
+ * @protected
+ */
+ _bindUI: function() {
+ this.after('visibleChange', this._afterVisibleChange);
+ this.after('disabledChange', this._afterDisabledChange);
+ this.after('heightChange', this._afterHeightChange);
+ this.after('widthChange', this._afterWidthChange);
+ this.after('focusedChange', this._afterFocusedChange);
+
+ this._bindDOMListeners();
+ },
+
+ /**
+ * Sets up DOM listeners, on elements rendered by the widget.
+ *
+ * @method _bindDOMListeners
+ * @protected
+ */
+ _bindDOMListeners : function() {
+
+ var oDocument = this.get(BOUNDING_BOX).get("ownerDocument");
+
+ oDocument.on("focus", this._onFocus, this);
+
+ // Fix for Webkit:
+ // Document doesn't receive focus in Webkit when the user mouses
+ // down on it, so the "focused" attribute won't get set to the
+ // correct value.
+
+ if (Y.UA.webkit) {
+ oDocument.on("mousedown", this._onDocMouseDown, this);
+ }
+
+ },
+
+ /**
+ * Updates the widget UI to reflect the attribute state.
+ *
+ * @method _syncUI
+ * @protected
+ */
+ _syncUI: function() {
+ this._uiSetVisible(this.get(VISIBLE));
+ this._uiSetDisabled(this.get(DISABLED));
+ this._uiSetHeight(this.get(HEIGHT));
+ this._uiSetWidth(this.get(WIDTH));
+ this._uiSetFocused(this.get(FOCUSED));
+ this._uiSetTabIndex(this.get(TAB_INDEX));
+ },
+
+ /**
+ * Sets the height on the widget's bounding box element
+ *
+ * @method _uiSetHeight
+ * @protected
+ * @param {String | Number} val
+ */
+ _uiSetHeight: function(val) {
+ if (L.isNumber(val)) {
+ val = val + this.DEF_UNIT;
+ }
+ this.get(BOUNDING_BOX).setStyle(HEIGHT, val);
+ },
+
+ /**
+ * Sets the width on the widget's bounding box element
+ *
+ * @method _uiSetWidth
+ * @protected
+ * @param {String | Number} val
+ */
+ _uiSetWidth: function(val) {
+ if (L.isNumber(val)) {
+ val = val + this.DEF_UNIT;
+ }
+ this.get(BOUNDING_BOX).setStyle(WIDTH, val);
+ },
+
+ /**
+ * Sets the visible state for the UI
+ *
+ * @method _uiSetVisible
+ * @protected
+ * @param {boolean} val
+ */
+ _uiSetVisible: function(val) {
+
+ var box = this.get(BOUNDING_BOX),
+ sClassName = this.getClassName(HIDDEN);
+
+ if (val === true) {
+ box.removeClass(sClassName);
+ } else {
+ box.addClass(sClassName);
+ }
+ },
+
+ /**
+ * Sets the disabled state for the UI
+ *
+ * @protected
+ * @param {boolean} val
+ */
+ _uiSetDisabled: function(val) {
+
+ var box = this.get(BOUNDING_BOX),
+ sClassName = this.getClassName(DISABLED);
+
+ if (val === true) {
+ box.addClass(sClassName);
+ } else {
+ box.removeClass(sClassName);
+ }
+ },
+
+
+ /**
+ * Set the tabIndex on the widget's rendered UI
+ *
+ * @method _uiSetTabIndex
+ * @protected
+ * @param Number
+ */
+ _uiSetTabIndex: function(index) {
+
+ var boundingBox = this.get(BOUNDING_BOX);
+
+ if (L.isNumber(index)) {
+ boundingBox.set(TAB_INDEX, index);
+ }
+ else {
+ boundingBox.removeAttribute(TAB_INDEX);
+ }
+
+ },
+
+
+ /**
+ * Sets the focused state for the UI
+ *
+ * @protected
+ * @param {boolean} val
+ * @param {string} src String representing the source that triggered an update to
+ * the UI.
+ */
+ _uiSetFocused: function(val, src) {
+
+ var box = this.get(BOUNDING_BOX),
+ sClassName = this.getClassName(FOCUSED);
+
+ if (val === true) {
+ box.addClass(sClassName);
+ if (src !== UI) {
+ box.focus();
+ }
+ } else {
+ box.removeClass(sClassName);
+ if (src !== UI) {
+ box.blur();
+ }
+ }
+ },
+
+
+
+ /**
+ * Default visible attribute state change handler
+ *
+ * @method _afterVisibleChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterVisibleChange: function(evt) {
+ this._uiSetVisible(evt.newVal);
+ },
+
+ /**
+ * Default disabled attribute state change handler
+ *
+ * @method _afterDisabledChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterDisabledChange: function(evt) {
+ this._uiSetDisabled(evt.newVal);
+ },
+
+ /**
+ * Default height attribute state change handler
+ *
+ * @method _afterHeightChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterHeightChange: function(evt) {
+ this._uiSetHeight(evt.newVal);
+ },
+
+ /**
+ * Default widget attribute state change handler
+ *
+ * @method _afterWidthChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterWidthChange: function(evt) {
+ this._uiSetWidth(evt.newVal);
+ },
+
+ /**
+ * Default focused attribute state change handler
+ *
+ * @method _afterFocusedChange
+ * @protected
+ * @param {EventFacade} evt The event facade for the attribute change
+ */
+ _afterFocusedChange: function(evt) {
+ this._uiSetFocused(evt.newVal, evt.src);
+ },
+
+ /**
+ * @method _onDocMouseDown
+ * @description "mousedown" event handler for the owner document of the
+ * widget's bounding box.
+ * @protected
+ * @param {EventFacade} evt The event facade for the DOM focus event
+ */
+ _onDocMouseDown: function (evt) {
+
+ if (this._hasDOMFocus) {
+ this._onFocus(evt);
+ }
+
+ },
+
+ /**
+ * DOM focus event handler, used to sync the state of the Widget with the DOM
+ *
+ * @method _onFocus
+ * @protected
+ * @param {EventFacade} evt The event facade for the DOM focus event
+ */
+ _onFocus: function (evt) {
+
+ var target = evt.target,
+ boundingBox = this.get(BOUNDING_BOX),
+ bFocused = (boundingBox.compareTo(target) || boundingBox.contains(target));
+
+ this._hasDOMFocus = bFocused;
+ this._set(FOCUSED, bFocused, { src: UI });
+
+ },
+
+ /**
+ * Generic toString implementation for all widgets.
+ *
+ * @method toString
+ * @return {String} The default string value for the widget [ displays the NAME of the instance, and the unique id ]
+ */
+ toString: function() {
+ return this.constructor.NAME + "[" + this._yuid + "]";
+ },
+
+ /**
+ * Default unit to use for dimension values
+ *
+ * @property DEF_UNIT
+ */
+ DEF_UNIT : "px",
+
+ /**
+ * Static property defining the markup template for content box.
+ *
+ * @property CONTENT_TEMPLATE
+ * @type String
+ */
+ CONTENT_TEMPLATE : "<div></div>",
+
+ /**
+ * Static property defining the markup template for bounding box.
+ *
+ * @property BOUNDING_TEMPLATE
+ * @type String
+ */
+ BOUNDING_TEMPLATE : "<div></div>",
+
+ /**
+ * Static property listing the styles that are mimiced on the bounding box from the content box.
+ *
+ * @property WRAP_STYLES
+ * @type Object
+ */
+ WRAP_STYLES : {
+ height: '100%',
+ width: '100%',
+ zIndex: false,
+ position: 'static',
+ top: '0',
+ left: '0',
+ bottom: '',
+ right: '',
+ padding: '',
+ margin: ''
+ },
+
+ /**
+ * Sets strings for a particular locale, merging with any existing
+ * strings which may already be defined for the locale.
+ *
+ * @method _setStrings
+ * @protected
+ * @param {Object} strings The hash of string key/values to set
+ * @param {Object} locale The locale for the string values being set
+ */
+ _setStrings : function(strings, locale) {
+ var strs = this._strings;
+ locale = locale.toLowerCase();
+
+ if (!strs[locale]) {
+ strs[locale] = {};
+ }
+
+ Y.aggregate(strs[locale], strings, true);
+ return strs[locale];
+ },
+
+ /**
+ * Returns the strings key/value hash for a paricular locale, without locale lookup applied.
+ *
+ * @method _getStrings
+ * @protected
+ * @param {Object} locale
+ */
+ _getStrings : function(locale) {
+ return this._strings[locale.toLowerCase()];
+ },
+
+ /**
+ * Gets the entire strings hash for a particular locale, performing locale lookup.
+ * <p>
+ * If no values of the key are defined for a particular locale the value for the
+ * default locale (in initial locale set for the class) is returned.
+ * </p>
+ * @method getStrings
+ * @param {String} locale (optional) The locale for which the string value is required. Defaults to the current locale, if not provided.
+ */
+ // TODO: Optimize/Cache. Clear cache on _setStrings call.
+ getStrings : function(locale) {
+
+ locale = (locale || this.get(LOCALE)).toLowerCase();
+
+
+ var defLocale = this.getDefaultLocale().toLowerCase(),
+ defStrs = this._getStrings(defLocale),
+ strs = (defStrs) ? Y.merge(defStrs) : {},
+ localeSegments = locale.split(HYPHEN);
+
+ // If locale is different than the default, or needs lookup support
+ if (locale !== defLocale || localeSegments.length > 1) {
+ var lookup = "";
+ for (var i = 0, l = localeSegments.length; i < l; ++i) {
+ lookup += localeSegments[i];
+
+
+ var localeStrs = this._getStrings(lookup);
+ if (localeStrs) {
+ Y.aggregate(strs, localeStrs, true);
+ }
+ lookup += HYPHEN;
+ }
+ }
+
+ return strs;
+ },
+
+ /**
+ * Gets the string for a particular key, for a particular locale, performing locale lookup.
+ * <p>
+ * If no values if defined for the key, for the given locale, the value for the
+ * default locale (in initial locale set for the class) is returned.
+ * </p>
+ * @method getString
+ * @param {String} key The key.
+ * @param {String} locale (optional) The locale for which the string value is required. Defaults to the current locale, if not provided.
+ */
+ getString : function(key, locale) {
+
+ locale = (locale || this.get(LOCALE)).toLowerCase();
+
+
+ var defLocale = (this.getDefaultLocale()).toLowerCase(),
+ strs = this._getStrings(defLocale) || {},
+ str = strs[key],
+ idx = locale.lastIndexOf(HYPHEN);
+
+ // If locale is different than the default, or needs lookup support
+ if (locale !== defLocale || idx != -1) {
+ do {
+
+ strs = this._getStrings(locale);
+ if (strs && key in strs) {
+ str = strs[key];
+ break;
+ }
+ idx = locale.lastIndexOf(HYPHEN);
+ // Chop of last locale segment
+ if (idx != -1) {
+ locale = locale.substring(0, idx);
+ }
+
+ } while (idx != -1);
+ }
+
+ return str;
+ },
+
+ /**
+ * Returns the default locale for the widget (the locale value defined by the
+ * widget class, or provided by the user during construction).
+ *
+ * @method getDefaultLocale
+ * @return {String} The default locale for the widget
+ */
+ getDefaultLocale : function() {
+ return this._conf.get(LOCALE, INIT_VALUE);
+ },
+
+ /**
+ * Private stings hash, used to store strings in locale specific buckets.
+ *
+ * @property _strings
+ * @private
+ * @type Object
+ */
+ _strings: null,
+
+ /**
+ * Gets the HTML_PARSER definition for this instance, by merging HTML_PARSER
+ * definitions across the class hierarchy.
+ *
+ * @method _getHtmlParser
+ * @return {Object} HTML_PARSER definition for this instance
+ */
+ _getHtmlParser : function() {
+ if (!this._HTML_PARSER) {
+ var classes = this._getClasses(),
+ parser = {},
+ i, p;
+
+ for (i = classes.length - 1; i >= 0; i--) {
+ p = classes[i].HTML_PARSER;
+ if (p) {
+ Y.mix(parser, p, true);
+ }
+ }
+
+ this._HTML_PARSER = parser;
+ }
+
+ return this._HTML_PARSER;
+ }
+});
+
+Y.Widget = Widget;
+
+
+}, '3.0.0' ,{requires:['attribute', 'event-focus', 'base', 'node', 'classnamemanager']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+(function() {
+
+ var _instances = {},
+ _startTime = new Date().getTime(),
+ p,
+ i,
+
+ add = function () {
+ if (window.addEventListener) {
+ return function(el, type, fn, capture) {
+ el.addEventListener(type, fn, (!!capture));
+ };
+ } else if (window.attachEvent) {
+ return function(el, type, fn) {
+ el.attachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ remove = function() {
+ if (window.removeEventListener) {
+ return function (el, type, fn, capture) {
+ el.removeEventListener(type, fn, !!capture);
+ };
+ } else if (window.detachEvent) {
+ return function (el, type, fn) {
+ el.detachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ globalListener = function() {
+ YUI.Env.windowLoaded = true;
+ YUI.Env.DOMReady = true;
+ remove(window, 'load', globalListener);
+ },
+
+// @TODO: this needs to be created at build time from module metadata
+
+ _APPLY_TO_WHITE_LIST = {
+ 'io.xdrReady': 1,
+ 'io.start': 1,
+ 'io.success': 1,
+ 'io.failure': 1
+ },
+
+ SLICE = Array.prototype.slice;
+
+// reduce to one or the other
+if (typeof YUI === 'undefined' || !YUI) {
+
+ /**
+ * The YUI global namespace object. If YUI is already defined, the
+ * existing YUI object will not be overwritten so that defined
+ * namespaces are preserved.
+ *
+ * @class YUI
+ * @constructor
+ * @global
+ * @uses EventTarget
+ * @param o* Up to five optional configuration objects. This object is stored
+ * in YUI.config. See config for the list of supported properties.
+ */
+
+ /*global YUI*/
+ // Make a function, disallow direct instantiation
+ YUI = function(o1, o2, o3, o4, o5) {
+
+ var Y = this, a = arguments, i, l = a.length;
+
+ // Allow instantiation without the new operator
+ if (!(Y instanceof YUI)) {
+ return new YUI(o1, o2, o3, o4, o5);
+ } else {
+ // set up the core environment
+ Y._init();
+
+ for (i=0; i<l; i++) {
+ Y._config(a[i]);
+ }
+
+ // bind the specified additional modules for this instance
+ Y._setup();
+
+ return Y;
+ }
+ };
+}
+
+// The prototype contains the functions that are required to allow the external
+// modules to be registered and for the instance to be initialized.
+YUI.prototype = {
+
+ _config: function(o) {
+
+ o = o || {};
+
+ var c = this.config, i, j, m, mods;
+
+ mods = c.modules;
+ for (i in o) {
+ if (mods && i == 'modules') {
+ m = o[i];
+ for (j in m) {
+ if (m.hasOwnProperty(j)) {
+ mods[j] = m[j];
+ }
+ }
+ } else if (i == 'win') {
+ c[i] = o[i].contentWindow || o[i];
+ c.doc = c[i].document;
+ } else {
+ c[i] = o[i];
+ }
+ }
+ },
+
+ /**
+ * Initialize this YUI instance
+ * @param o config options
+ * @private
+ */
+ _init: function(o) {
+
+ // find targeted window/frame
+ // @TODO create facades
+ var v = '3.0.0', Y = this;
+
+ if (v.indexOf('@') > -1) {
+ v = 'test';
+ }
+
+ Y.version = v;
+
+ Y.Env = {
+ // @todo expand the new module metadata
+ mods: {},
+ cdn: 'http://yui.yahooapis.com/' + v + '/build/',
+ bootstrapped: false,
+ _idx: 0,
+ _used: {},
+ _attached: {},
+ _yidx: 0,
+ _uidx: 0,
+ _loaded: {}
+ };
+
+ Y.Env._loaded[v] = {};
+
+ if (YUI.Env) {
+ Y.Env._yidx = (++YUI.Env._yidx);
+ Y.Env._guidp = ('yui_' + v + '-' + Y.Env._yidx + '-' + _startTime).replace(/\./g, '_');
+ Y.id = Y.stamp(Y);
+ _instances[Y.id] = Y;
+ }
+
+ Y.constructor = YUI;
+
+ // configuration defaults
+ Y.config = {
+
+ win: window || {},
+ doc: document,
+ debug: true,
+ useBrowserConsole: true,
+ throwFail: true,
+
+ base: function() {
+ var b, nodes, i, match;
+
+ // get from querystring
+ nodes = document.getElementsByTagName('script');
+
+ for (i=0; i<nodes.length; i=i+1) {
+ match = nodes[i].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);
+ b = match && match[1];
+ if (b) {
+ break;
+ }
+ }
+
+ // use CDN default
+ return b || this.Env.cdn;
+
+ }(),
+
+ loaderPath: 'loader/loader-min.js'
+ };
+
+ },
+
+ /**
+ * Finishes the instance setup. Attaches whatever modules were defined
+ * when the yui modules was registered.
+ * @method _setup
+ * @private
+ */
+ _setup: function(o) {
+ this.use("yui-base");
+ },
+
+ /**
+ * Executes a method on a YUI instance with
+ * the specified id if the specified method is whitelisted.
+ * @method applyTo
+ * @param id {string} the YUI instance id
+ * @param method {string} the name of the method to exectute.
+ * Ex: 'Object.keys'
+ * @param args {Array} the arguments to apply to the method
+ * @return {object} the return value from the applied method or null
+ */
+ applyTo: function(id, method, args) {
+
+ if (!(method in _APPLY_TO_WHITE_LIST)) {
+ this.error(method + ': applyTo not allowed');
+ return null;
+ }
+
+ var instance = _instances[id], nest, m, i;
+
+ if (instance) {
+
+ nest = method.split('.');
+ m = instance;
+
+ for (i=0; i<nest.length; i=i+1) {
+
+ m = m[nest[i]];
+
+ if (!m) {
+ this.error('applyTo not found: ' + method);
+ }
+ }
+
+ return m.apply(instance, args);
+ }
+
+ return null;
+ },
+
+ /**
+ * Register a module
+ * @method add
+ * @param name {string} module name
+ * @param fn {Function} entry point into the module that
+ * is used to bind module to the YUI instance
+ * @param version {string} version string
+ * @param details optional config data:
+ * requires - features that should be present before loading
+ * optional - optional features that should be present if load optional defined
+ * use - features that should be attached automatically
+ * skinnable -
+ * rollup
+ * omit - features that should not be loaded if this module is present
+ * @return {YUI} the YUI instance
+ *
+ */
+ add: function(name, fn, version, details) {
+ // this.log('Adding a new component ' + name);
+ // @todo expand this to include version mapping
+ // @todo may want to restore the build property
+ // @todo fire moduleAvailable event
+
+ YUI.Env.mods[name] = {
+ name: name,
+ fn: fn,
+ version: version,
+ details: details || {}
+ };
+
+ return this; // chain support
+ },
+
+ _attach: function(r, fromLoader) {
+
+ var mods = YUI.Env.mods,
+ attached = this.Env._attached,
+ i, l = r.length, name, m, d, req, use;
+
+ for (i=0; i<l; i=i+1) {
+
+ name = r[i];
+ m = mods[name];
+
+ if (!attached[name] && m) {
+
+ attached[name] = true;
+
+ d = m.details;
+ req = d.requires;
+ use = d.use;
+
+ if (req) {
+ this._attach(this.Array(req));
+ }
+
+ // this.log('attaching ' + name, 'info', 'yui');
+
+ if (m.fn) {
+ m.fn(this);
+ }
+
+ if (use) {
+ this._attach(this.Array(use));
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Bind a module to a YUI instance
+ * @param modules* {string} 1-n modules to bind (uses arguments array)
+ * @param *callback {function} callback function executed when
+ * the instance has the required functionality. If included, it
+ * must be the last parameter.
+ *
+ * @TODO
+ * Implement versioning? loader can load different versions?
+ * Should sub-modules/plugins be normal modules, or do
+ * we add syntax for specifying these?
+ *
+ * YUI().use('dragdrop')
+ * YUI().use('dragdrop:2.4.0'); // specific version
+ * YUI().use('dragdrop:2.4.0-'); // at least this version
+ * YUI().use('dragdrop:2.4.0-2.9999.9999'); // version range
+ * YUI().use('*'); // use all available modules
+ * YUI().use('lang+dump+substitute'); // use lang and some plugins
+ * YUI().use('lang+*'); // use lang and all known plugins
+ *
+ *
+ * @return {YUI} the YUI instance
+ */
+ use: function() {
+
+ if (this._loading) {
+ this._useQueue = this._useQueue || new this.Queue();
+ this._useQueue.add(SLICE.call(arguments, 0));
+ return this;
+ }
+
+ var Y = this,
+ a=SLICE.call(arguments, 0),
+ mods = YUI.Env.mods,
+ used = Y.Env._used,
+ loader,
+ firstArg = a[0],
+ dynamic = false,
+ callback = a[a.length-1],
+ k, i, l, missing = [],
+ r = [],
+ f = function(name) {
+
+ // only attach a module once
+ if (used[name]) {
+ // Y.log(name + ' already used', 'info', 'yui');
+ return;
+ }
+
+ var m = mods[name], j, req, use;
+
+ if (m) {
+
+ // Y.log('USING ' + name, 'info', 'yui');
+
+ used[name] = true;
+
+ req = m.details.requires;
+ use = m.details.use;
+ } else {
+
+ // CSS files don't register themselves, see if it has been loaded
+ if (!YUI.Env._loaded[Y.version][name]) {
+ Y.log('module not found: ' + name, 'info', 'yui');
+ missing.push(name);
+ } else {
+ // probably css
+ // Y.log('module not found BUT HAS BEEN LOADED: ' + name, 'info', 'yui');
+ used[name] = true;
+ }
+ }
+
+ // make sure requirements are attached
+ if (req) {
+ if (Y.Lang.isString(req)) {
+ f(req);
+ } else {
+ for (j = 0; j < req.length; j = j + 1) {
+ // Y.log('using module\'s requirements: ' + name, 'info', 'yui');
+ f(req[j]);
+ }
+ }
+ }
+
+ // add this module to full list of things to attach
+ // Y.log('adding to requires list: ' + name);
+ r.push(name);
+
+ },
+
+ onComplete = function(fromLoader) {
+
+ // Y.log('Use complete');
+
+ fromLoader = fromLoader || {
+ success: true,
+ msg: 'not dynamic'
+ };
+
+ if (Y.Env._callback) {
+
+ var cb = Y.Env._callback;
+ Y.Env._callback = null;
+ cb(Y, fromLoader);
+ }
+
+ if (Y.fire) {
+ Y.fire('yui:load', Y, fromLoader);
+ }
+
+ // process queued use requests as long until done
+ // or dynamic load happens again.
+ Y._loading = false;
+ while (Y._useQueue && Y._useQueue.size() && !Y._loading) {
+ Y.use.apply(Y, Y._useQueue.next());
+ }
+ };
+
+ // Y.log(Y.id + ': use called: ' + a + ' :: ' + callback);
+
+ // The last argument supplied to use can be a load complete callback
+ if (typeof callback === 'function') {
+ a.pop();
+ Y.Env._callback = callback;
+ } else {
+ callback = null;
+ }
+
+ // YUI().use('*'); // bind everything available
+ if (firstArg === "*") {
+ a = [];
+ for (k in mods) {
+ if (mods.hasOwnProperty(k)) {
+ a.push(k);
+ }
+ }
+
+ return Y.use.apply(Y, a);
+ }
+
+ // Y.log('loader before: ' + a.join(','));
+
+ // use loader to expand dependencies and sort the
+ // requirements if it is available.
+ if (Y.Loader) {
+ dynamic = true;
+ loader = new Y.Loader(Y.config);
+ loader.require(a);
+ loader.ignoreRegistered = true;
+ loader.allowRollup = false;
+ loader.calculate();
+ a = loader.sorted;
+ }
+
+ // Y.log('loader after: ' + a.join(','));
+
+ l = a.length;
+
+ // process each requirement and any additional requirements
+ // the module metadata specifies
+ for (i=0; i<l; i=i+1) {
+ f(a[i]);
+ }
+
+ // Y.log('all reqs: ' + r + ' --- missing: ' + missing + ', l: ' + l + ', ' + r[0]);
+
+ // dynamic load
+ if (Y.Loader && missing.length) {
+ Y.log('Attempting to dynamically load the missing modules ' + missing, 'info', 'yui');
+ Y._loading = true;
+ loader = new Y.Loader(Y.config);
+ loader.onSuccess = onComplete;
+ loader.onFailure = onComplete;
+ loader.onTimeout = onComplete;
+ loader.context = Y;
+ loader.attaching = a;
+ loader.require(missing);
+ loader.insert();
+ } else if (Y.Get && missing.length && !Y.Env.bootstrapped) {
+ Y.log('fetching loader: ' + Y.config.base + Y.config.loaderPath, 'info', 'yui');
+ Y._loading = true;
+
+ a = Y.Array(arguments, 0, true);
+ // a.unshift('loader');
+
+ Y.Get.script(Y.config.base + Y.config.loaderPath, {
+ onEnd: function() {
+ Y._loading = false;
+ Y.Env.bootstrapped = true;
+ Y._attach(['loader']);
+ Y.use.apply(Y, a);
+ }
+ });
+
+ return Y;
+
+ } else {
+ Y._attach(r);
+ onComplete();
+ }
+
+ return Y; // chain support var yui = YUI().use('dragdrop');
+ },
+
+
+ /**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YUI.namespace("property.package");
+ * YUI.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YUI.property, then
+ * YUI.property.package (YAHOO is scrubbed out, this is
+ * to remain compatible with YUI2)
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YUI.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @param {string*} arguments 1-n namespaces to create
+ * @return {object} A reference to the last namespace object created
+ */
+ namespace: function() {
+ var a=arguments, o=null, i, j, d;
+ for (i=0; i<a.length; i=i+1) {
+ d = ("" + a[i]).split(".");
+ o = this;
+ for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+ o[d[j]] = o[d[j]] || {};
+ o = o[d[j]];
+ }
+ }
+ return o;
+ },
+
+ // this is replaced if the log module is included
+ log: function() {
+
+ },
+
+ /**
+ * Report an error. The reporting mechanism is controled by
+ * the 'throwFail' configuration attribute. If throwFail is
+ * not specified, the message is written to the Logger, otherwise
+ * a JS error is thrown
+ * @method error
+ * @param msg {string} the error message
+ * @param e {Error} Optional JS error that was caught. If supplied
+ * and throwFail is specified, this error will be re-thrown.
+ * @return {YUI} this YUI instance
+ */
+ error: function(msg, e) {
+ if (this.config.throwFail) {
+ throw (e || new Error(msg));
+ } else {
+ this.message(msg, "error"); // don't scrub this one
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate an id that is unique among all YUI instances
+ * @method guid
+ * @param pre {string} optional guid prefix
+ * @return {string} the guid
+ */
+ guid: function(pre) {
+ var id = this.Env._guidp + (++this.Env._uidx);
+ return (pre) ? (pre + id) : id;
+ },
+
+ /**
+ * Returns a guid associated with an object. If the object
+ * does not have one, a new one is created unless readOnly
+ * is specified.
+ * @method stamp
+ * @param o The object to stamp
+ * @param readOnly {boolean} if true, a valid guid will only
+ * be returned if the object has one assigned to it.
+ * @return {string} The object's guid or null
+ */
+ stamp: function(o, readOnly) {
+
+ if (!o) {
+ return o;
+ }
+
+ var uid = (typeof o === 'string') ? o : o._yuid;
+
+ if (!uid) {
+ uid = this.guid();
+ if (!readOnly) {
+ try {
+ o._yuid = uid;
+ } catch(e) {
+ uid = null;
+ }
+ }
+ }
+
+ return uid;
+ }
+};
+
+// Give the YUI global the same properties as an instance.
+// This makes it so that the YUI global can be used like the YAHOO
+// global was used prior to 3.x. More importantly, the YUI global
+// provides global metadata, so env needs to be configured.
+// @TODO review
+
+ p = YUI.prototype;
+
+ // inheritance utilities are not available yet
+ for (i in p) {
+ // if (1) { // intenionally ignoring hasOwnProperty check
+ YUI[i] = p[i];
+ // }
+ }
+
+ // set up the environment
+ YUI._init();
+
+ // add a window load event at load time so we can capture
+ // the case where it fires before dynamic loading is
+ // complete.
+ add(window, 'load', globalListener);
+
+ YUI.Env.add = add;
+ YUI.Env.remove = remove;
+
+ /*
+ * Subscribe to an event. The signature differs depending on the
+ * type of event you are attaching to.
+ * @method on
+ * @param type {string|function|object} The type of the event. If
+ * this is a function, this is dispatched to the aop system. If an
+ * object, it is parsed for multiple subsription definitions
+ * @param fn {Function} The callback
+ * @param elspec {any} DOM element(s), selector string(s), and or
+ * Node ref(s) to attach DOM related events to (only applies to
+ * DOM events).
+ * @param
+ * @return the event target or a detach handle per 'chain' config
+ */
+
+})();
+
+/**
+ * The config object contains all of the configuration options for
+ * the YUI instance. This object is supplied by the implementer
+ * when instantiating a YUI instance. Some properties have default
+ * values if they are not supplied by the implementer.
+ *
+ * @class config
+ * @static
+ */
+
+/**
+ * Turn debug statements on or off.
+ *
+ * @property debug
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * Log to the browser console if debug is on and the browser has a
+ * supported console.
+ *
+ * @property useBrowserConsole
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * A hash of log sources that should be logged. If specified, only log messages from these sources will be logged.
+ *
+ * @property logInclude
+ * @type object
+ */
+
+/**
+ * A hash of log sources that should be not be logged. If specified, all sources are logged if not on this list.
+ *
+ * @property logExclude
+ * @type object
+ */
+
+/**
+ * Set to true if the yui seed file was dynamically loaded in
+ * order to bootstrap components relying on the window load event
+ * and the 'domready' custom event.
+ *
+ * @property injected
+ * @type object
+ */
+
+/**
+ * If throwFail is set, Y.fail will generate or re-throw a JS Error. Otherwise the failure is logged.
+ *
+ * @property throwFail
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * The window/frame that this instance should operate in.
+ *
+ * @property win
+ * @type Window
+ * @default the window hosting YUI
+ */
+
+/**
+ * The document associated with the 'win' configuration.
+ *
+ * @property doc
+ * @type Document
+ * @default the document hosting YUI
+ */
+
+/**
+ * A list of modules that defines the YUI core (overrides the default).
+ *
+ * @property core
+ * @type string[]
+ */
+
+/**
+ * The default date format
+ *
+ * @property dateFormat
+ * @type string
+ */
+
+/**
+ * The default locale
+ *
+ * @property locale
+ * @type string
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property pollInterval
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The number of dynamic nodes to insert by default before
+ * automatically removing them. This applies to script nodes
+ * because remove the node will not make the evaluated script
+ * unavailable. Dynamic CSS is not auto purged, because removing
+ * a linked style sheet will also remove the style definitions.
+ *
+ * @property purgethreshold
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property windowResizeDelay
+ * @type int
+ * @default 40
+ */
+
+/**
+ * Base directory for dynamic loading
+ *
+ * @property base
+ * @type string
+ */
+
+/**
+ * The secure base dir (not implemented)
+ *
+ * For dynamic loading.
+ *
+ * @property secureBase
+ * @type string
+ */
+
+/**
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?
+ *
+ * For dynamic loading.
+ *
+ * @property comboBase
+ * @type string
+ */
+
+/**
+ * The root path to prepend to module names for the combo service. Ex: 3.0.0b1/build/
+ *
+ * For dynamic loading.
+ *
+ * @property root
+ * @type string
+ */
+
+/**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * For dynamic loading.
+ *
+ * @property filter
+ * @type string|object
+ */
+
+/**
+ * Hash of per-component filter specification. If specified for a given component,
+ * this overrides the filter config
+ *
+ * For dynamic loading.
+ *
+ * @property filters
+ * @type object
+ */
+
+/**
+ * Use the YUI combo service to reduce the number of http connections
+ * required to load your dependencies.
+ *
+ * For dynamic loading.
+ *
+ * @property combine
+ * @type boolean
+ * @default true if 'base' is not supplied, false if it is.
+ */
+
+/**
+ * A list of modules that should never be dynamically loaded
+ *
+ * @property ignore
+ * @type string[]
+ */
+
+/**
+ * A list of modules that should always be loaded when required, even if already
+ * present on the page.
+ *
+ * @property force
+ * @type string[]
+ */
+
+/**
+ * Node or id for a node that should be used as the insertion point for new nodes
+ * For dynamic loading.
+ *
+ * @property insertBefore
+ * @type string
+ */
+
+/**
+ * charset for dynamic nodes
+ *
+ * @property charset
+ * @type string
+ * @deprecated use jsAttributes cssAttributes
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded script nodes.
+ *
+ * @property jsAttributes
+ * @type string
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded link nodes.
+ *
+ * @property cssAttributes
+ * @type string
+ */
+
+/**
+ * Number of milliseconds before a timeout occurs when dynamically
+ * loading nodes. If not set, there is no timeout.
+ *
+ * @property timeout
+ * @type int
+ */
+
+/**
+ * Callback for the 'CSSComplete' event. When dynamically loading YUI
+ * components with CSS, this property fires when the CSS is finished
+ * loading but script loading is still ongoing. This provides an
+ * opportunity to enhance the presentation of a loading page a little
+ * bit before the entire loading process is done.
+ *
+ * @property onCSS
+ * @type function
+ */
+
+/**
+ * A list of module definitions to add to the list of YUI components.
+ * These components can then be dynamically loaded side by side with
+ * YUI via the use() method.See Loader.addModule for the supported
+ * module metadata.
+ *
+ * @property modules
+ * @type function
+ */
+
+/**
+ * The loader 'path' attribute to the loader itself. This is combined
+ * with the 'base' attribute to dynamically load the loader component
+ * when boostrapping with the get utility alone.
+ *
+ * @property loaderPath
+ * @default loader/loader-min.js
+ */
+YUI.add('yui-base', function(Y) {
+
+/*
+ * YUI stub
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1, info: 1, warn: 1, error: 1 },
+ _published;
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var Y = INSTANCE, c = Y.config, bail = false, excl, incl, m, f;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+
+ excl = c.logExclude;
+ incl = c.logInclude;
+
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (excl && (src in excl)) {
+ bail = 1;
+ }
+ }
+
+ if (!bail) {
+
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (Y.fire && !bail && !silent) {
+ if (!_published) {
+ Y.publish(LOGEVENT, {
+ broadcast: 2,
+ emitFacade: 1
+ });
+
+ _published = 1;
+
+ }
+ Y.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+})();
+(function() {
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class Lang
+ * @static
+ */
+Y.Lang = Y.Lang || {};
+
+var L = Y.Lang,
+
+ARRAY = 'array',
+BOOLEAN = 'boolean',
+DATE = 'date',
+ERROR = 'error',
+FUNCTION = 'function',
+NUMBER = 'number',
+NULL = 'null',
+OBJECT = 'object',
+REGEX = 'regexp',
+STRING = 'string',
+TOSTRING = Object.prototype.toString,
+UNDEFINED = 'undefined',
+
+TYPES = {
+ 'undefined' : UNDEFINED,
+ 'number' : NUMBER,
+ 'boolean' : BOOLEAN,
+ 'string' : STRING,
+ '[object Function]' : FUNCTION,
+ '[object RegExp]' : REGEX,
+ '[object Array]' : ARRAY,
+ '[object Date]' : DATE,
+ '[object Error]' : ERROR
+},
+
+TRIMREGEX = /^\s+|\s+$/g,
+EMPTYSTRING = '';
+
+/**
+ * Determines whether or not the provided item is an array.
+ * Returns false for array-like collections such as the
+ * function arguments collection or HTMLElement collection
+ * will return false. You can use @see Array.test if you
+ * want to
+ * @method isArray
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is an array
+ */
+L.isArray = function(o) {
+ return L.type(o) === ARRAY;
+};
+
+/**
+ * Determines whether or not the provided item is a boolean
+ * @method isBoolean
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a boolean
+ */
+L.isBoolean = function(o) {
+ return typeof o === BOOLEAN;
+};
+
+/**
+ * Determines whether or not the provided item is a function
+ * Note: Internet Explorer thinks certain functions are objects:
+ *
+ * var obj = document.createElement("object");
+ * Y.Lang.isFunction(obj.getAttribute) // reports false in IE
+ *
+ * var input = document.createElement("input"); // append to body
+ * Y.Lang.isFunction(input.focus) // reports false in IE
+ *
+ * You will have to implement additional tests if these functions
+ * matter to you.
+ *
+ * @method isFunction
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a function
+ */
+L.isFunction = function(o) {
+ return L.type(o) === FUNCTION;
+};
+
+/**
+ * Determines whether or not the supplied item is a date instance
+ * @method isDate
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a date
+ */
+L.isDate = function(o) {
+ // return o instanceof Date;
+ return L.type(o) === DATE;
+};
+
+/**
+ * Determines whether or not the provided item is null
+ * @method isNull
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is null
+ */
+L.isNull = function(o) {
+ return o === null;
+};
+
+/**
+ * Determines whether or not the provided item is a legal number
+ * @method isNumber
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a number
+ */
+L.isNumber = function(o) {
+ return typeof o === NUMBER && isFinite(o);
+};
+
+/**
+ * Determines whether or not the provided item is of type object
+ * or function
+ * @method isObject
+ * @static
+ * @param o The object to test
+ * @param failfn {boolean} fail if the input is a function
+ * @return {boolean} true if o is an object
+ */
+L.isObject = function(o, failfn) {
+return (o && (typeof o === OBJECT || (!failfn && L.isFunction(o)))) || false;
+};
+
+/**
+ * Determines whether or not the provided item is a string
+ * @method isString
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a string
+ */
+L.isString = function(o) {
+ return typeof o === STRING;
+};
+
+/**
+ * Determines whether or not the provided item is undefined
+ * @method isUndefined
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is undefined
+ */
+L.isUndefined = function(o) {
+ return typeof o === UNDEFINED;
+};
+
+/**
+ * Returns a string without any leading or trailing whitespace. If
+ * the input is not a string, the input will be returned untouched.
+ * @method trim
+ * @static
+ * @param s {string} the string to trim
+ * @return {string} the trimmed string
+ */
+L.trim = function(s){
+ try {
+ return s.replace(TRIMREGEX, EMPTYSTRING);
+ } catch(e) {
+ return s;
+ }
+};
+
+/**
+ * A convenience method for detecting a legitimate non-null value.
+ * Returns false for null/undefined/NaN, true for other values,
+ * including 0/false/''
+ * @method isValue
+ * @static
+ * @param o The item to test
+ * @return {boolean} true if it is not null/undefined/NaN || false
+ */
+L.isValue = function(o) {
+ var t = L.type(o);
+ switch (t) {
+ case NUMBER:
+ return isFinite(o);
+ case NULL:
+ case UNDEFINED:
+ return false;
+ default:
+ return !!(t);
+ }
+};
+
+/**
+ * Returns a string representing the type of the item passed in.
+ * @method type
+ * @param o the item to test
+ * @return {string} the detected type
+ */
+L.type = function (o) {
+ return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? OBJECT : NULL);
+};
+
+})();
+
+/*
+ * Provides information about the environment hosting YUI
+ * @module yui
+ * @submodule Array
+ */
+
+(function() {
+
+var L = Y.Lang, Native = Array.prototype,
+
+/**
+ * Adds the following array utilities to the YUI instance. Additional
+ * array helpers can be found in the collection component.
+ * @class Array
+ */
+
+/**
+ * Y.Array(o) returns an array:
+ * - Arrays are return unmodified unless the start position is specified.
+ * - "Array-like" collections (@see Array.test) are converted to arrays
+ * - For everything else, a new array is created with the input as the sole item
+ * - The start position is used if the input is or is like an array to return
+ * a subset of the collection.
+ *
+ * @TODO this will not automatically convert elements that are also collections
+ * such as forms and selects. Passing true as the third param will
+ * force a conversion.
+ *
+ * @method ()
+ * @static
+ * @param o the item to arrayify
+ * @param i {int} if an array or array-like, this is the start index
+ * @param al {boolean} if true, it forces the array-like fork. This
+ * can be used to avoid multiple array.test calls.
+ * @return {Array} the resulting array
+ */
+YArray = function(o, startIdx, al) {
+ var t = (al) ? 2 : Y.Array.test(o), i, l, a;
+
+ // switch (t) {
+ // case 1:
+ // // return (startIdx) ? o.slice(startIdx) : o;
+ // case 2:
+ // return Native.slice.call(o, startIdx || 0);
+ // default:
+ // return [o];
+ // }
+
+ if (t) {
+ try {
+ return Native.slice.call(o, startIdx || 0);
+ // IE errors when trying to slice element collections
+ } catch(e) {
+ a=[];
+ for (i=0, l=o.length; i<l; i=i+1) {
+ a.push(o[i]);
+ }
+ return a;
+ }
+ } else {
+ return [o];
+ }
+
+};
+
+Y.Array = YArray;
+
+/**
+ * Evaluates the input to determine if it is an array, array-like, or
+ * something else. This is used to handle the arguments collection
+ * available within functions, and HTMLElement collections
+ *
+ * @method test
+ * @static
+ *
+ * @todo current implementation (intenionally) will not implicitly
+ * handle html elements that are array-like (forms, selects, etc).
+ *
+ * @return {int} a number indicating the results:
+ * 0: Not an array or an array-like collection
+ * 1: A real array.
+ * 2: array-like collection.
+ */
+YArray.test = function(o) {
+ var r = 0;
+ if (L.isObject(o)) {
+ if (L.isArray(o)) {
+ r = 1;
+ } else {
+ try {
+ // indexed, but no tagName (element) or alert (window)
+ if ("length" in o && !("tagName" in o) && !("alert" in o) &&
+ (!Y.Lang.isFunction(o.size) || o.size() > 1)) {
+ r = 2;
+ }
+
+ } catch(e) {}
+ }
+ }
+ return r;
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * @method each
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item
+ * @param o Optional context object
+ * @static
+ * @return {YUI} the YUI instance
+ */
+YArray.each = (Native.forEach) ?
+ function (a, f, o) {
+ Native.forEach.call(a || [], f, o || Y);
+ return Y;
+ } :
+ function (a, f, o) {
+ var l = (a && a.length) || 0, i;
+ for (i = 0; i < l; i=i+1) {
+ f.call(o || Y, a[i], i, a);
+ }
+ return Y;
+ };
+
+/**
+ * Returns an object using the first array as keys, and
+ * the second as values. If the second array is not
+ * provided the value is set to true for each.
+ * @method hash
+ * @static
+ * @param k {Array} keyset
+ * @param v {Array} optional valueset
+ * @return {object} the hash
+ */
+YArray.hash = function(k, v) {
+ var o = {}, l = k.length, vl = v && v.length, i;
+ for (i=0; i<l; i=i+1) {
+ o[k[i]] = (vl && vl > i) ? v[i] : true;
+ }
+
+ return o;
+};
+
+/**
+ * Returns the index of the first item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * @method indexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of the item that contains the value or -1
+ */
+YArray.indexOf = (Native.indexOf) ?
+ function(a, val) {
+ return Native.indexOf.call(a, val);
+ } :
+ function(a, val) {
+ for (var i=0; i<a.length; i=i+1) {
+ if (a[i] === val) {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+/**
+ * Numeric sort convenience function.
+ * Y.ArrayAssert.itemsAreEqual([1, 2, 3], [3, 1, 2].sort(Y.Array.numericSort));
+ * @method numericSort
+ */
+YArray.numericSort = function(a, b) {
+ return (a - b);
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * Returning true from the processing function will stop the
+ * processing of the remaining
+ * items.
+ * @method some
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item
+ * @param o Optional context object
+ * @static
+ * @return {boolean} true if the function returns true on
+ * any of the items in the array
+ */
+ YArray.some = (Native.some) ?
+ function (a, f, o) {
+ return Native.some.call(a, f, o);
+ } :
+ function (a, f, o) {
+ var l = a.length, i;
+ for (i=0; i<l; i=i+1) {
+ if (f.call(o, a[i], i, a)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+})();
+(function() {
+
+var L = Y.Lang,
+DELIMITER = '__',
+// FROZEN = {
+// 'prototype': 1,
+// '_yuid': 1
+// },
+
+/*
+ * IE will not enumerate native functions in a derived object even if the
+ * function was overridden. This is a workaround for specific functions
+ * we care about on the Object prototype.
+ * @property _iefix
+ * @for YUI
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @private
+ */
+_iefix = function(r, s) {
+ var fn = s.toString;
+ if (L.isFunction(fn) && fn != Object.prototype.toString) {
+ r.toString = fn;
+ }
+};
+
+
+/**
+ * Returns a new object containing all of the properties of
+ * all the supplied objects. The properties from later objects
+ * will overwrite those in earlier objects. Passing in a
+ * single object will create a shallow copy of it. For a deep
+ * copy, use clone.
+ * @method merge
+ * @for YUI
+ * @param arguments {Object*} the objects to merge
+ * @return {object} the new merged object
+ */
+Y.merge = function() {
+ var a = arguments, o = {}, i, l = a.length;
+ for (i=0; i<l; i=i+1) {
+ Y.mix(o, a[i], true);
+ }
+ return o;
+};
+
+/**
+ * Applies the supplier's properties to the receiver. By default
+ * all prototype and static propertes on the supplier are applied
+ * to the corresponding spot on the receiver. By default all
+ * properties are applied, and a property that is already on the
+ * reciever will not be overwritten. The default behavior can
+ * be modified by supplying the appropriate parameters.
+ *
+ * @TODO add constants for the modes
+ *
+ * @method mix
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param {int} mode what should be copies, and to where
+ * default(0): object to object
+ * 1: prototype to prototype (old augment)
+ * 2: prototype to prototype and object props (new augment)
+ * 3: prototype to object
+ * 4: object to prototype
+ * @param merge {boolean} merge objects instead of overwriting/ignoring
+ * Used by Y.aggregate
+ * @return {object} the augmented object
+ */
+Y.mix = function(r, s, ov, wl, mode, merge) {
+
+ if (!s||!r) {
+ return r || Y;
+ }
+
+ if (mode) {
+ switch (mode) {
+ case 1: // proto to proto
+ return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ case 2: // object to object and proto to proto
+ Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ break; // pass through
+ case 3: // proto to static
+ return Y.mix(r, s.prototype, ov, wl, 0, merge);
+ case 4: // static to proto
+ return Y.mix(r.prototype, s, ov, wl, 0, merge);
+ default: // object to object is what happens below
+ }
+ }
+
+ // Maybe don't even need this wl && wl.length check anymore??
+ var arr = merge && L.isArray(r), i, l, p;
+
+ if (wl && wl.length) {
+ for (i = 0, l = wl.length; i < l; ++i) {
+ p = wl[i];
+ if (p in s) {
+ if (merge && L.isObject(r[p], true)) {
+ Y.mix(r[p], s[p]);
+ } else if (!arr && (ov || !(p in r))) {
+ r[p] = s[p];
+ } else if (arr) {
+ r.push(s[p]);
+ }
+ }
+ }
+ } else {
+ for (i in s) {
+ // if (s.hasOwnProperty(i) && !(i in FROZEN)) {
+ // check white list if it was supplied
+ // if the receiver has this property, it is an object,
+ // and merge is specified, merge the two objects.
+ if (merge && L.isObject(r[i], true)) {
+ Y.mix(r[i], s[i]); // recursive
+ // otherwise apply the property only if overwrite
+ // is specified or the receiver doesn't have one.
+ } else if (!arr && (ov || !(i in r))) {
+ r[i] = s[i];
+ // if merge is specified and the receiver is an array,
+ // append the array item
+ } else if (arr) {
+ r.push(s[i]);
+ }
+ // }
+ }
+
+ if (Y.UA.ie) {
+ _iefix(r, s);
+ }
+ }
+
+ return r;
+};
+
+/**
+ * Returns a wrapper for a function which caches the
+ * return value of that function, keyed off of the combined
+ * argument values.
+ * @function cached
+ * @param source {function} the function to memoize
+ * @param cache an optional cache seed
+ * @param refetch if supplied, this value is tested against the cached
+ * value. If the values are equal, the wrapped function is executed again.
+ * @return {Function} the wrapped function
+ */
+Y.cached = function(source, cache, refetch){
+ cache = cache || {};
+
+ return function(arg1, arg2) {
+
+ var k = (arg2) ? Array.prototype.join.call(arguments, DELIMITER) : arg1,
+ v = cache[k];
+
+ if (!(k in cache) || (refetch && cache[k] == refetch)) {
+ cache[k] = source.apply(source, arguments);
+ }
+
+ return cache[k];
+ };
+
+};
+
+})();
+
+(function() {
+
+/**
+ * Adds the following Object utilities to the YUI instance
+ * @class Object
+ */
+
+/**
+ * Y.Object(o) returns a new object based upon the supplied object.
+ * @TODO Use native Object.create() when available
+ * @method ()
+ * @static
+ * @param o the supplier object
+ * @return {Object} the new object
+ */
+Y.Object = function(o) {
+ var F = function() {};
+ F.prototype = o;
+ return new F();
+};
+
+var O = Y.Object,
+
+UNDEFINED = undefined,
+
+/**
+ * Extracts the keys, values, or size from an object
+ *
+ * @method _extract
+ * @param o the object
+ * @param what what to extract (0: keys, 1: values, 2: size)
+ * @return {boolean|Array} the extracted info
+ * @static
+ * @private
+ */
+_extract = function(o, what) {
+ var count = (what === 2), out = (count) ? 0 : [], i;
+
+ for (i in o) {
+ if (count) {
+ out++;
+ } else {
+ if (o.hasOwnProperty(i)) {
+ out.push((what) ? o[i] : i);
+ }
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Returns an array containing the object's keys
+ * @TODO use native Object.keys() if available
+ * @method keys
+ * @static
+ * @param o an object
+ * @return {string[]} the keys
+ */
+O.keys = function(o) {
+ return _extract(o);
+};
+
+/**
+ * Returns an array containing the object's values
+ * @TODO use native Object.values() if available
+ * @method values
+ * @static
+ * @param o an object
+ * @return {Array} the values
+ */
+O.values = function(o) {
+ return _extract(o, 1);
+};
+
+/**
+ * Returns the size of an object
+ * @TODO use native Object.size() if available
+ * @method size
+ * @static
+ * @param o an object
+ * @return {int} the size
+ */
+O.size = function(o) {
+ return _extract(o, 2);
+};
+
+/**
+ * Returns true if the object contains a given key
+ * @method hasKey
+ * @static
+ * @param o an object
+ * @param k the key to query
+ * @return {boolean} true if the object contains the key
+ */
+O.hasKey = function(o, k) {
+ // return (o.hasOwnProperty(k));
+ return (k in o);
+};
+
+/**
+ * Returns true if the object contains a given value
+ * @method hasValue
+ * @static
+ * @param o an object
+ * @param v the value to query
+ * @return {boolean} true if the object contains the value
+ */
+O.hasValue = function(o, v) {
+ return (Y.Array.indexOf(O.values(o), v) > -1);
+};
+
+/**
+ * Determines whether or not the property was added
+ * to the object instance. Returns false if the property is not present
+ * in the object, or was inherited from the prototype.
+ *
+ * @deprecated Safari 1.x support has been removed, so this is simply a
+ * wrapper for the native implementation. Use the native implementation
+ * directly instead.
+ *
+ * @TODO Remove in B1
+ *
+ * @method owns
+ * @static
+ * @param o {any} The object being testing
+ * @param p {string} the property to look for
+ * @return {boolean} true if the object has the property on the instance
+ */
+O.owns = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+/**
+ * Executes a function on each item. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method each
+ * @static
+ * @param o the object to iterate
+ * @param f {function} the function to execute
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {YUI} the YUI instance
+ */
+O.each = function (o, f, c, proto) {
+ var s = c || Y, i;
+
+ for (i in o) {
+ if (proto || o.hasOwnProperty(i)) {
+ f.call(s, o[i], i, o);
+ }
+ }
+ return Y;
+};
+
+/**
+ * Retrieves the sub value at the provided path,
+ * from the value object provided.
+ *
+ * @method getValue
+ * @param o The object from which to extract the property value
+ * @param path {Array} A path array, specifying the object traversal path
+ * from which to obtain the sub value.
+ * @return {Any} The value stored in the path, undefined if not found.
+ * Returns the source object if an empty path is provided.
+ */
+O.getValue = function (o, path) {
+ var p=Y.Array(path), l=p.length, i;
+
+ for (i=0; o !== UNDEFINED && i < l; i=i+1) {
+ o = o[p[i]];
+ }
+
+ return o;
+};
+
+/**
+ * Sets the sub-attribute value at the provided path on the
+ * value object. Returns the modified value object, or
+ * undefined if the path is invalid.
+ *
+ * @method setValue
+ * @param o The object on which to set the sub value.
+ * @param path {Array} A path array, specifying the object traversal path
+ * at which to set the sub value.
+ * @param val {Any} The new value for the sub-attribute.
+ * @return {Object} The modified object, with the new sub value set, or
+ * undefined, if the path was invalid.
+ */
+O.setValue = function(o, path, val) {
+
+ var p=Y.Array(path), leafIdx=p.length-1, i, ref=o;
+
+ if (leafIdx >= 0) {
+ for (i=0; ref !== UNDEFINED && i < leafIdx; i=i+1) {
+ ref = ref[p[i]];
+ }
+
+ if (ref !== UNDEFINED) {
+ ref[p[i]] = val;
+ } else {
+ return UNDEFINED;
+ }
+ }
+
+ return o;
+};
+
+
+})();
+
+/*
+ * Provides information about the environment hosting YUI
+ * @module yui
+ * @submodule UA
+ */
+/**
+ * YUI user agent detection.
+ * Do not fork for a browser if it can be avoided. Use feature detection when
+ * you can. Use the user agent as a last resort. UA stores a version
+ * number for the browser engine, 0 otherwise. This value may or may not map
+ * to the version number of the browser using the engine. The value is
+ * presented as a float so that it can easily be used for boolean evaluation
+ * as well as for looking for a particular range of versions. Because of this,
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9
+ * reports 1.8).
+ * @class UA
+ * @static
+ */
+Y.UA = function() {
+
+ var numberfy = function(s) {
+ var c = 0;
+ return parseFloat(s.replace(/\./g, function() {
+ return (c++ == 1) ? '' : '.';
+ }));
+ },
+
+ nav = navigator,
+
+ o = {
+
+ /**
+ * Internet Explorer version number or 0. Example: 6
+ * @property ie
+ * @type float
+ * @static
+ */
+ ie: 0,
+
+ /**
+ * Opera version number or 0. Example: 9.2
+ * @property opera
+ * @type float
+ * @static
+ */
+ opera: 0,
+
+ /**
+ * Gecko engine revision number. Will evaluate to 1 if Gecko
+ * is detected but the revision could not be found. Other browsers
+ * will be 0. Example: 1.8
+ * <pre>
+ * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7
+ * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+ * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+ * Firefox 3 alpha: 1.9a4 <-- Reports 1.9
+ * </pre>
+ * @property gecko
+ * @type float
+ * @static
+ */
+ gecko: 0,
+
+ /**
+ * AppleWebKit version. KHTML browsers that are not WebKit browsers
+ * will evaluate to 1, other browsers 0. Example: 418.9
+ * <pre>
+ * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
+ * latest available for Mac OSX 10.3.
+ * Safari 2.0.2: 416 <-- hasOwnProperty introduced
+ * Safari 2.0.4: 418 <-- preventDefault fixed
+ * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+ * different versions of webkit
+ * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been
+ * updated, but not updated
+ * to the latest patch.
+ * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native SVG
+ * and many major issues fixed).
+ * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic update
+ * from 2.x via the 10.4.11 OS patch
+ * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event.
+ * yahoo.com user agent hack removed.
+ * </pre>
+ * http://en.wikipedia.org/wiki/Safari_(web_browser)#Version_history
+ * @property webkit
+ * @type float
+ * @static
+ */
+ webkit: 0,
+
+ /**
+ * The mobile property will be set to a string containing any relevant
+ * user agent information when a modern mobile browser is detected.
+ * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+ * devices with the WebKit-based browser, and Opera Mini.
+ * @property mobile
+ * @type string
+ * @static
+ */
+ mobile: null,
+
+ /**
+ * Adobe AIR version number or 0. Only populated if webkit is detected.
+ * Example: 1.0
+ * @property air
+ * @type float
+ */
+ air: 0,
+
+ /**
+ * Google Caja version number or 0.
+ * @property caja
+ * @type float
+ */
+ caja: nav.cajaVersion,
+
+ /**
+ * Set to true if the page appears to be in SSL
+ * @property secure
+ * @type boolean
+ * @static
+ */
+ secure: false,
+
+ /**
+ * The operating system. Currently only detecting windows or macintosh
+ * @property os
+ * @type string
+ * @static
+ */
+ os: null
+
+ },
+
+ ua = nav && nav.userAgent,
+
+ loc = Y.config.win.location,
+
+ href = loc && loc.href,
+
+ m;
+
+ o.secure = href && (href.toLowerCase().indexOf("https") === 0);
+
+ if (ua) {
+
+ if ((/windows|win32/i).test(ua)) {
+ o.os = 'windows';
+ } else if ((/macintosh/i).test(ua)) {
+ o.os = 'macintosh';
+ }
+
+ // Modern KHTML browsers should qualify as Safari X-Grade
+ if ((/KHTML/).test(ua)) {
+ o.webkit=1;
+ }
+ // Modern WebKit browsers are at least X-Grade
+ m=ua.match(/AppleWebKit\/([^\s]*)/);
+ if (m&&m[1]) {
+ o.webkit=numberfy(m[1]);
+
+ // Mobile browser check
+ if (/ Mobile\//.test(ua)) {
+ o.mobile = "Apple"; // iPhone or iPod Touch
+ } else {
+ m=ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
+ if (m) {
+ o.mobile = m[0]; // Nokia N-series, Android, webOS, ex: NokiaN95
+ }
+ }
+
+ m=ua.match(/AdobeAIR\/([^\s]*)/);
+ if (m) {
+ o.air = m[0]; // Adobe AIR 1.0 or better
+ }
+
+ }
+
+ if (!o.webkit) { // not webkit
+ // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+ m=ua.match(/Opera[\s\/]([^\s]*)/);
+ if (m&&m[1]) {
+ o.opera=numberfy(m[1]);
+ m=ua.match(/Opera Mini[^;]*/);
+ if (m) {
+ o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+ }
+ } else { // not opera or webkit
+ m=ua.match(/MSIE\s([^;]*)/);
+ if (m&&m[1]) {
+ o.ie=numberfy(m[1]);
+ } else { // not opera, webkit, or ie
+ m=ua.match(/Gecko\/([^\s]*)/);
+ if (m) {
+ o.gecko=1; // Gecko detected, look for revision
+ m=ua.match(/rv:([^\s\)]*)/);
+ if (m&&m[1]) {
+ o.gecko=numberfy(m[1]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return o;
+}();
+(function() {
+ var L = Y.Lang,
+
+ /**
+ * Executes the supplied function in the context of the supplied
+ * object 'when' milliseconds later. Executes the function a
+ * single time unless periodic is set to true.
+ * @method later
+ * @for YUI
+ * @param when {int} the number of milliseconds to wait until the fn
+ * is executed.
+ * @param o the context object.
+ * @param fn {Function|String} the function to execute or the name of
+ * the method in the 'o' object to execute.
+ * @param data [Array] data that is provided to the function. This accepts
+ * either a single item or an array. If an array is provided, the
+ * function is executed with one parameter for each array item. If
+ * you need to pass a single array parameter, it needs to be wrapped in
+ * an array [myarray].
+ * @param periodic {boolean} if true, executes continuously at supplied
+ * interval until canceled.
+ * @return {object} a timer object. Call the cancel() method on this object to
+ * stop the timer.
+ */
+ later = function(when, o, fn, data, periodic) {
+ when = when || 0;
+ o = o || {};
+ var m=fn, d=data, f, r;
+
+ if (L.isString(fn)) {
+ m = o[fn];
+ }
+
+ if (!m) {
+ Y.error("method undefined");
+ }
+
+ if (!L.isArray(d)) {
+ d = [data];
+ }
+
+ f = function() {
+ m.apply(o, d);
+ };
+
+ r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+ return {
+ id: r,
+ interval: periodic,
+ cancel: function() {
+ if (this.interval) {
+ clearInterval(r);
+ } else {
+ clearTimeout(r);
+ }
+ }
+ };
+ };
+
+ Y.later = later;
+ L.later = later;
+
+})();
+(function() {
+
+ var min = ['yui-base'], core, C = Y.config, LOADER='loader';
+
+ // apply the minimal required functionality
+ Y.use.apply(Y, min);
+
+ Y.log(Y.id + ' initialized', 'info', 'yui');
+
+ if (C.core) {
+ core = C.core;
+ } else {
+ core = ['queue-base', 'get'];
+ if (YUI.Env.mods[LOADER]) {
+ core.push(LOADER);
+ }
+ }
+
+ Y.use.apply(Y, core);
+
+})();
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+(function(){var I={},B=new Date().getTime(),A,E,H=function(){if(window.addEventListener){return function(M,L,K,J){M.addEventListener(L,K,(!!J));};}else{if(window.attachEvent){return function(L,K,J){L.attachEvent("on"+K,J);};}else{return function(){};}}}(),F=function(){if(window.removeEventListener){return function(M,L,K,J){M.removeEventListener(L,K,!!J);};}else{if(window.detachEvent){return function(L,K,J){L.detachEvent("on"+K,J);};}else{return function(){};}}}(),D=function(){YUI.Env.windowLoaded=true;YUI.Env.DOMReady=true;F(window,"load",D);},C={"io.xdrReady":1,"io.start":1,"io.success":1,"io.failure":1},G=Array.prototype.slice;if(typeof YUI==="undefined"||!YUI){YUI=function(O,N,M,L,J){var K=this,R=arguments,Q,P=R.length;if(!(K instanceof YUI)){return new YUI(O,N,M,L,J);}else{K._init();for(Q=0;Q<P;Q++){K._config(R[Q]);}K._setup();return K;}};}YUI.prototype={_config:function(N){N=N||{};var O=this.config,L,K,J,M;M=O.modules;for(L in N){if(M&&L=="modules"){J=N[L];for(K in J){if(J.hasOwnProperty(K)){M[K]=J[K];}}}else{if(L=="win"){O[L]=N[L].contentWindow||N[L];O.doc=O[L].document;}else{O[L]=N[L];}}}},_init:function(L){var J="3.0.0",K=this;if(J.indexOf("@")>-1){J="test";}K.version=J;K.Env={mods:{},cdn:"http://yui.yahooapis.com/"+J+"/build/",bootstrapped:false,_idx:0,_used:{},_attached:{},_yidx:0,_uidx:0,_loaded:{}};K.Env._loaded[J]={};if(YUI.Env){K.Env._yidx=(++YUI.Env._yidx);K.Env._guidp=("yui_"+J+"-"+K.Env._yidx+"-"+B).replace(/\./g,"_");K.id=K.stamp(K);I[K.id]=K;}K.constructor=YUI;K.config={win:window||{},doc:document,debug:true,useBrowserConsole:true,throwFail:true,base:function(){var M,N,P,O;N=document.getElementsByTagName("script");for(P=0;P<N.length;P=P+1){O=N[P].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);M=O&&O[1];if(M){break;}}return M||this.Env.cdn;}(),loaderPath:"loader/loader-min.js"};},_setup:function(J){this.use("yui-base");},applyTo:function(P,O,L){if(!(O in C)){this.error(O+": applyTo not allowed");return null;}var K=I[P],N,J,M;if(K){N=O.split(".");J=K;for(M=0;M<N.length;M=M+1){J=J[N[M]];if(!J){this.error("applyTo not found: "+O);}}return J.apply(K,L);}return null;},add:function(K,M,J,L){YUI.Env.mods[K]={name:K,fn:M,version:J,details:L||{}};return this;},_attach:function(K,O){var T=YUI.Env.mods,L=this.Env._attached,Q,P=K.length,M,N,R,S,J;for(Q=0;Q<P;Q=Q+1){M=K[Q];N=T[M];if(!L[M]&&N){L[M]=true;R=N.details;S=R.requires;J=R.use;if(S){this._attach(this.Array(S));}if(N.fn){N.fn(this);}if(J){this._attach(this.Array(J));}}}},use:function(){if(this._loading){this._useQueue=this._useQueue||new this.Queue();this._useQueue.add(G.call(arguments,0));return this;}var K=this,T=G.call(arguments,0),W=YUI.Env.mods,X=K.Env._used,U,O=T[0],M=false,V=T[T.length-1],P,R,N,Q=[],J=[],S=function(b){if(X[b]){return;}var Y=W[b],a,c,Z;if(Y){X[b]=true;c=Y.details.requires;Z=Y.details.use;}else{if(!YUI.Env._loaded[K.version][b]){Q.push(b);}else{X[b]=true;}}if(c){if(K.Lang.isString(c)){S(c);}else{for(a=0;a<c.length;a=a+1){S(c[a]);}}}J.push(b);},L=function(Z){Z=Z||{success:true,msg:"not dynamic"};if(K.Env._callback){var Y=K.Env._callback;K.Env._callback=null;Y(K,Z);}if(K.fire){K.fire("yui:load",K,Z);}K._loading=false;while(K._useQueue&&K._useQueue.size()&&!K._loading){K.use.apply(K,K._useQueue.next());}};if(typeof V==="function"){T.pop();K.Env._callback=V;}else{V=null;}if(O==="*"){T=[];for(P in W){if(W.hasOwnProperty(P)){T.push(P);}}return K.use.apply(K,T);}if(K.Loader){M=true;U=new K.Loader(K.config);U.require(T);U.ignoreRegistered=true;U.allowRollup=false;U.calculate();T=U.sorted;}N=T.length;for(R=0;R<N;R=R+1){S(T[R]);}if(K.Loader&&Q.length){K._loading=true;U=new K.Loader(K.config);U.onSuccess=L;U.onFailure=L;U.onTimeout=L;U.context=K;U.attaching=T;U.require(Q);U.insert();}else{if(K.Get&&Q.length&&!K.Env.bootstrapped){K._loading=true;T=K.Array(arguments,0,true);K.Get.script(K.config.base+K.config.loaderPath,{onEnd:function(){K._loading=false;K.Env.bootstrapped=true;K._attach(["loader"]);K.use.apply(K,T);}});return K;}else{K._attach(J);L();}}return K;},namespace:function(){var J=arguments,N=null,L,K,M;for(L=0;L<J.length;L=L+1){M=(""+J[L]).split(".");N=this;for(K=(M[0]=="YAHOO")?1:0;K<M.length;K=K+1){N[M[K]]=N[M[K]]||{};N=N[M[K]];}}return N;},log:function(){},error:function(K,J){if(this.config.throwFail){throw (J||new Error(K));}else{this.message(K,"error");}return this;},guid:function(J){var K=this.Env._guidp+(++this.Env._uidx);return(J)?(J+K):K;},stamp:function(L,M){if(!L){return L;}var J=(typeof L==="string")?L:L._yuid;if(!J){J=this.guid();if(!M){try{L._yuid=J;}catch(K){J=null;}}}return J;}};A=YUI.prototype;for(E in A){YUI[E]=A[E];}YUI._init();H(window,"load",D);YUI.Env.add=H;YUI.Env.remove=F;})();YUI.add("yui-base",function(A){(function(){var D=A,F="yui:log",B="undefined",C={debug:1,info:1,warn:1,error:1},E;D.log=function(I,Q,G,O){var H=D,P=H.config,K=false,N,L,J,M;if(P.debug){if(G){N=P.logExclude;L=P.logInclude;if(L&&!(G in L)){K=1;}else{if(N&&(G in N)){K=1;}}}if(!K){if(P.useBrowserConsole){J=(G)?G+": "+I:I;if(typeof console!=B&&console.log){M=(Q&&console[Q]&&(Q in C))?Q:"log";console[M](J);}else{if(typeof opera!=B){opera.postError(J);}}}if(H.fire&&!K&&!O){if(!E){H.publish(F,{broadcast:2,emitFacade:1});E=1;}H.fire(F,{msg:I,cat:Q,src:G});}}}return H;};D.message=function(){return D.log.apply(D,arguments);};})();(function(){A.Lang=A.Lang||{};var Q=A.Lang,F="array",H="boolean",C="date",K="error",R="function",G="number",J="null",E="object",N="regexp",M="string",B=Object.prototype.toString,O="undefined",D={"undefined":O,"number":G,"boolean":H,"string":M,"[object Function]":R,"[object RegExp]":N,"[object Array]":F,"[object Date]":C,"[object Error]":K},I=/^\s+|\s+$/g,P="";Q.isArray=function(L){return Q.type(L)===F;};Q.isBoolean=function(L){return typeof L===H;};Q.isFunction=function(L){return Q.type(L)===R;};Q.isDate=function(L){return Q.type(L)===C;};Q.isNull=function(L){return L===null;};Q.isNumber=function(L){return typeof L===G&&isFinite(L);};Q.isObject=function(S,L){return(S&&(typeof S===E||(!L&&Q.isFunction(S))))||false;
+};Q.isString=function(L){return typeof L===M;};Q.isUndefined=function(L){return typeof L===O;};Q.trim=function(L){try{return L.replace(I,P);}catch(S){return L;}};Q.isValue=function(S){var L=Q.type(S);switch(L){case G:return isFinite(S);case J:case O:return false;default:return !!(L);}};Q.type=function(L){return D[typeof L]||D[B.call(L)]||(L?E:J);};})();(function(){var B=A.Lang,C=Array.prototype,D=function(L,I,K){var H=(K)?2:A.Array.test(L),G,F,E;if(H){try{return C.slice.call(L,I||0);}catch(J){E=[];for(G=0,F=L.length;G<F;G=G+1){E.push(L[G]);}return E;}}else{return[L];}};A.Array=D;D.test=function(G){var E=0;if(B.isObject(G)){if(B.isArray(G)){E=1;}else{try{if("length" in G&&!("tagName" in G)&&!("alert" in G)&&(!A.Lang.isFunction(G.size)||G.size()>1)){E=2;}}catch(F){}}}return E;};D.each=(C.forEach)?function(E,F,G){C.forEach.call(E||[],F,G||A);return A;}:function(F,H,I){var E=(F&&F.length)||0,G;for(G=0;G<E;G=G+1){H.call(I||A,F[G],G,F);}return A;};D.hash=function(G,F){var J={},E=G.length,I=F&&F.length,H;for(H=0;H<E;H=H+1){J[G[H]]=(I&&I>H)?F[H]:true;}return J;};D.indexOf=(C.indexOf)?function(E,F){return C.indexOf.call(E,F);}:function(E,G){for(var F=0;F<E.length;F=F+1){if(E[F]===G){return F;}}return -1;};D.numericSort=function(F,E){return(F-E);};D.some=(C.some)?function(E,F,G){return C.some.call(E,F,G);}:function(F,H,I){var E=F.length,G;for(G=0;G<E;G=G+1){if(H.call(I,F[G],G,F)){return true;}}return false;};})();(function(){var C=A.Lang,B="__",D=function(G,F){var E=F.toString;if(C.isFunction(E)&&E!=Object.prototype.toString){G.toString=E;}};A.merge=function(){var F=arguments,H={},G,E=F.length;for(G=0;G<E;G=G+1){A.mix(H,F[G],true);}return H;};A.mix=function(E,N,G,M,K,L){if(!N||!E){return E||A;}if(K){switch(K){case 1:return A.mix(E.prototype,N.prototype,G,M,0,L);case 2:A.mix(E.prototype,N.prototype,G,M,0,L);break;case 3:return A.mix(E,N.prototype,G,M,0,L);case 4:return A.mix(E.prototype,N,G,M,0,L);default:}}var J=L&&C.isArray(E),I,H,F;if(M&&M.length){for(I=0,H=M.length;I<H;++I){F=M[I];if(F in N){if(L&&C.isObject(E[F],true)){A.mix(E[F],N[F]);}else{if(!J&&(G||!(F in E))){E[F]=N[F];}else{if(J){E.push(N[F]);}}}}}}else{for(I in N){if(L&&C.isObject(E[I],true)){A.mix(E[I],N[I]);}else{if(!J&&(G||!(I in E))){E[I]=N[I];}else{if(J){E.push(N[I]);}}}}if(A.UA.ie){D(E,N);}}return E;};A.cached=function(G,E,F){E=E||{};return function(K,J){var I=(J)?Array.prototype.join.call(arguments,B):K,H=E[I];if(!(I in E)||(F&&E[I]==F)){E[I]=G.apply(G,arguments);}return E[I];};};})();(function(){A.Object=function(G){var E=function(){};E.prototype=G;return new E();};var D=A.Object,C=undefined,B=function(I,H){var G=(H===2),E=(G)?0:[],F;for(F in I){if(G){E++;}else{if(I.hasOwnProperty(F)){E.push((H)?I[F]:F);}}}return E;};D.keys=function(E){return B(E);};D.values=function(E){return B(E,1);};D.size=function(E){return B(E,2);};D.hasKey=function(F,E){return(E in F);};D.hasValue=function(F,E){return(A.Array.indexOf(D.values(F),E)>-1);};D.owns=function(F,E){return(F.hasOwnProperty(E));};D.each=function(I,H,J,G){var F=J||A,E;for(E in I){if(G||I.hasOwnProperty(E)){H.call(F,I[E],E,I);}}return A;};D.getValue=function(I,H){var G=A.Array(H),E=G.length,F;for(F=0;I!==C&&F<E;F=F+1){I=I[G[F]];}return I;};D.setValue=function(K,I,J){var H=A.Array(I),G=H.length-1,E,F=K;if(G>=0){for(E=0;F!==C&&E<G;E=E+1){F=F[H[E]];}if(F!==C){F[H[E]]=J;}else{return C;}}return K;};})();A.UA=function(){var E=function(I){var J=0;return parseFloat(I.replace(/\./g,function(){return(J++==1)?"":".";}));},H=navigator,G={ie:0,opera:0,gecko:0,webkit:0,mobile:null,air:0,caja:H.cajaVersion,secure:false,os:null},D=H&&H.userAgent,F=A.config.win.location,C=F&&F.href,B;G.secure=C&&(C.toLowerCase().indexOf("https")===0);if(D){if((/windows|win32/i).test(D)){G.os="windows";}else{if((/macintosh/i).test(D)){G.os="macintosh";}}if((/KHTML/).test(D)){G.webkit=1;}B=D.match(/AppleWebKit\/([^\s]*)/);if(B&&B[1]){G.webkit=E(B[1]);if(/ Mobile\//.test(D)){G.mobile="Apple";}else{B=D.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);if(B){G.mobile=B[0];}}B=D.match(/AdobeAIR\/([^\s]*)/);if(B){G.air=B[0];}}if(!G.webkit){B=D.match(/Opera[\s\/]([^\s]*)/);if(B&&B[1]){G.opera=E(B[1]);B=D.match(/Opera Mini[^;]*/);if(B){G.mobile=B[0];}}else{B=D.match(/MSIE\s([^;]*)/);if(B&&B[1]){G.ie=E(B[1]);}else{B=D.match(/Gecko\/([^\s]*)/);if(B){G.gecko=1;B=D.match(/rv:([^\s\)]*)/);if(B&&B[1]){G.gecko=E(B[1]);}}}}}}return G;}();(function(){var B=A.Lang,C=function(K,E,L,G,H){K=K||0;E=E||{};var F=L,J=G,I,D;if(B.isString(L)){F=E[L];}if(!F){A.error("method undefined");}if(!B.isArray(J)){J=[G];}I=function(){F.apply(E,J);};D=(H)?setInterval(I,K):setTimeout(I,K);return{id:D,interval:H,cancel:function(){if(this.interval){clearInterval(D);}else{clearTimeout(D);}}};};A.later=C;B.later=C;})();(function(){var D=["yui-base"],B,E=A.config,F="loader";A.use.apply(A,D);if(E.core){B=E.core;}else{B=["queue-base","get"];if(YUI.Env.mods[F]){B.push(F);}}A.use.apply(A,B);})();},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+(function() {
+
+ var _instances = {},
+ _startTime = new Date().getTime(),
+ p,
+ i,
+
+ add = function () {
+ if (window.addEventListener) {
+ return function(el, type, fn, capture) {
+ el.addEventListener(type, fn, (!!capture));
+ };
+ } else if (window.attachEvent) {
+ return function(el, type, fn) {
+ el.attachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ remove = function() {
+ if (window.removeEventListener) {
+ return function (el, type, fn, capture) {
+ el.removeEventListener(type, fn, !!capture);
+ };
+ } else if (window.detachEvent) {
+ return function (el, type, fn) {
+ el.detachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ globalListener = function() {
+ YUI.Env.windowLoaded = true;
+ YUI.Env.DOMReady = true;
+ remove(window, 'load', globalListener);
+ },
+
+// @TODO: this needs to be created at build time from module metadata
+
+ _APPLY_TO_WHITE_LIST = {
+ 'io.xdrReady': 1,
+ 'io.start': 1,
+ 'io.success': 1,
+ 'io.failure': 1
+ },
+
+ SLICE = Array.prototype.slice;
+
+// reduce to one or the other
+if (typeof YUI === 'undefined' || !YUI) {
+
+ /**
+ * The YUI global namespace object. If YUI is already defined, the
+ * existing YUI object will not be overwritten so that defined
+ * namespaces are preserved.
+ *
+ * @class YUI
+ * @constructor
+ * @global
+ * @uses EventTarget
+ * @param o* Up to five optional configuration objects. This object is stored
+ * in YUI.config. See config for the list of supported properties.
+ */
+
+ /*global YUI*/
+ // Make a function, disallow direct instantiation
+ YUI = function(o1, o2, o3, o4, o5) {
+
+ var Y = this, a = arguments, i, l = a.length;
+
+ // Allow instantiation without the new operator
+ if (!(Y instanceof YUI)) {
+ return new YUI(o1, o2, o3, o4, o5);
+ } else {
+ // set up the core environment
+ Y._init();
+
+ for (i=0; i<l; i++) {
+ Y._config(a[i]);
+ }
+
+ // bind the specified additional modules for this instance
+ Y._setup();
+
+ return Y;
+ }
+ };
+}
+
+// The prototype contains the functions that are required to allow the external
+// modules to be registered and for the instance to be initialized.
+YUI.prototype = {
+
+ _config: function(o) {
+
+ o = o || {};
+
+ var c = this.config, i, j, m, mods;
+
+ mods = c.modules;
+ for (i in o) {
+ if (mods && i == 'modules') {
+ m = o[i];
+ for (j in m) {
+ if (m.hasOwnProperty(j)) {
+ mods[j] = m[j];
+ }
+ }
+ } else if (i == 'win') {
+ c[i] = o[i].contentWindow || o[i];
+ c.doc = c[i].document;
+ } else {
+ c[i] = o[i];
+ }
+ }
+ },
+
+ /**
+ * Initialize this YUI instance
+ * @param o config options
+ * @private
+ */
+ _init: function(o) {
+
+ // find targeted window/frame
+ // @TODO create facades
+ var v = '3.0.0', Y = this;
+
+ if (v.indexOf('@') > -1) {
+ v = 'test';
+ }
+
+ Y.version = v;
+
+ Y.Env = {
+ // @todo expand the new module metadata
+ mods: {},
+ cdn: 'http://yui.yahooapis.com/' + v + '/build/',
+ bootstrapped: false,
+ _idx: 0,
+ _used: {},
+ _attached: {},
+ _yidx: 0,
+ _uidx: 0,
+ _loaded: {}
+ };
+
+ Y.Env._loaded[v] = {};
+
+ if (YUI.Env) {
+ Y.Env._yidx = (++YUI.Env._yidx);
+ Y.Env._guidp = ('yui_' + v + '-' + Y.Env._yidx + '-' + _startTime).replace(/\./g, '_');
+ Y.id = Y.stamp(Y);
+ _instances[Y.id] = Y;
+ }
+
+ Y.constructor = YUI;
+
+ // configuration defaults
+ Y.config = {
+
+ win: window || {},
+ doc: document,
+ debug: true,
+ useBrowserConsole: true,
+ throwFail: true,
+
+ base: function() {
+ var b, nodes, i, match;
+
+ // get from querystring
+ nodes = document.getElementsByTagName('script');
+
+ for (i=0; i<nodes.length; i=i+1) {
+ match = nodes[i].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);
+ b = match && match[1];
+ if (b) {
+ break;
+ }
+ }
+
+ // use CDN default
+ return b || this.Env.cdn;
+
+ }(),
+
+ loaderPath: 'loader/loader-min.js'
+ };
+
+ },
+
+ /**
+ * Finishes the instance setup. Attaches whatever modules were defined
+ * when the yui modules was registered.
+ * @method _setup
+ * @private
+ */
+ _setup: function(o) {
+ this.use("yui-base");
+ },
+
+ /**
+ * Executes a method on a YUI instance with
+ * the specified id if the specified method is whitelisted.
+ * @method applyTo
+ * @param id {string} the YUI instance id
+ * @param method {string} the name of the method to exectute.
+ * Ex: 'Object.keys'
+ * @param args {Array} the arguments to apply to the method
+ * @return {object} the return value from the applied method or null
+ */
+ applyTo: function(id, method, args) {
+
+ if (!(method in _APPLY_TO_WHITE_LIST)) {
+ this.error(method + ': applyTo not allowed');
+ return null;
+ }
+
+ var instance = _instances[id], nest, m, i;
+
+ if (instance) {
+
+ nest = method.split('.');
+ m = instance;
+
+ for (i=0; i<nest.length; i=i+1) {
+
+ m = m[nest[i]];
+
+ if (!m) {
+ this.error('applyTo not found: ' + method);
+ }
+ }
+
+ return m.apply(instance, args);
+ }
+
+ return null;
+ },
+
+ /**
+ * Register a module
+ * @method add
+ * @param name {string} module name
+ * @param fn {Function} entry point into the module that
+ * is used to bind module to the YUI instance
+ * @param version {string} version string
+ * @param details optional config data:
+ * requires - features that should be present before loading
+ * optional - optional features that should be present if load optional defined
+ * use - features that should be attached automatically
+ * skinnable -
+ * rollup
+ * omit - features that should not be loaded if this module is present
+ * @return {YUI} the YUI instance
+ *
+ */
+ add: function(name, fn, version, details) {
+ // this.log('Adding a new component ' + name);
+ // @todo expand this to include version mapping
+ // @todo may want to restore the build property
+ // @todo fire moduleAvailable event
+
+ YUI.Env.mods[name] = {
+ name: name,
+ fn: fn,
+ version: version,
+ details: details || {}
+ };
+
+ return this; // chain support
+ },
+
+ _attach: function(r, fromLoader) {
+
+ var mods = YUI.Env.mods,
+ attached = this.Env._attached,
+ i, l = r.length, name, m, d, req, use;
+
+ for (i=0; i<l; i=i+1) {
+
+ name = r[i];
+ m = mods[name];
+
+ if (!attached[name] && m) {
+
+ attached[name] = true;
+
+ d = m.details;
+ req = d.requires;
+ use = d.use;
+
+ if (req) {
+ this._attach(this.Array(req));
+ }
+
+ // this.log('attaching ' + name, 'info', 'yui');
+
+ if (m.fn) {
+ m.fn(this);
+ }
+
+ if (use) {
+ this._attach(this.Array(use));
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Bind a module to a YUI instance
+ * @param modules* {string} 1-n modules to bind (uses arguments array)
+ * @param *callback {function} callback function executed when
+ * the instance has the required functionality. If included, it
+ * must be the last parameter.
+ *
+ * @TODO
+ * Implement versioning? loader can load different versions?
+ * Should sub-modules/plugins be normal modules, or do
+ * we add syntax for specifying these?
+ *
+ * YUI().use('dragdrop')
+ * YUI().use('dragdrop:2.4.0'); // specific version
+ * YUI().use('dragdrop:2.4.0-'); // at least this version
+ * YUI().use('dragdrop:2.4.0-2.9999.9999'); // version range
+ * YUI().use('*'); // use all available modules
+ * YUI().use('lang+dump+substitute'); // use lang and some plugins
+ * YUI().use('lang+*'); // use lang and all known plugins
+ *
+ *
+ * @return {YUI} the YUI instance
+ */
+ use: function() {
+
+ if (this._loading) {
+ this._useQueue = this._useQueue || new this.Queue();
+ this._useQueue.add(SLICE.call(arguments, 0));
+ return this;
+ }
+
+ var Y = this,
+ a=SLICE.call(arguments, 0),
+ mods = YUI.Env.mods,
+ used = Y.Env._used,
+ loader,
+ firstArg = a[0],
+ dynamic = false,
+ callback = a[a.length-1],
+ k, i, l, missing = [],
+ r = [],
+ f = function(name) {
+
+ // only attach a module once
+ if (used[name]) {
+ return;
+ }
+
+ var m = mods[name], j, req, use;
+
+ if (m) {
+
+
+ used[name] = true;
+
+ req = m.details.requires;
+ use = m.details.use;
+ } else {
+
+ // CSS files don't register themselves, see if it has been loaded
+ if (!YUI.Env._loaded[Y.version][name]) {
+ missing.push(name);
+ } else {
+ // probably css
+ used[name] = true;
+ }
+ }
+
+ // make sure requirements are attached
+ if (req) {
+ if (Y.Lang.isString(req)) {
+ f(req);
+ } else {
+ for (j = 0; j < req.length; j = j + 1) {
+ f(req[j]);
+ }
+ }
+ }
+
+ // add this module to full list of things to attach
+ r.push(name);
+
+ },
+
+ onComplete = function(fromLoader) {
+
+
+ fromLoader = fromLoader || {
+ success: true,
+ msg: 'not dynamic'
+ };
+
+ if (Y.Env._callback) {
+
+ var cb = Y.Env._callback;
+ Y.Env._callback = null;
+ cb(Y, fromLoader);
+ }
+
+ if (Y.fire) {
+ Y.fire('yui:load', Y, fromLoader);
+ }
+
+ // process queued use requests as long until done
+ // or dynamic load happens again.
+ Y._loading = false;
+ while (Y._useQueue && Y._useQueue.size() && !Y._loading) {
+ Y.use.apply(Y, Y._useQueue.next());
+ }
+ };
+
+
+ // The last argument supplied to use can be a load complete callback
+ if (typeof callback === 'function') {
+ a.pop();
+ Y.Env._callback = callback;
+ } else {
+ callback = null;
+ }
+
+ // YUI().use('*'); // bind everything available
+ if (firstArg === "*") {
+ a = [];
+ for (k in mods) {
+ if (mods.hasOwnProperty(k)) {
+ a.push(k);
+ }
+ }
+
+ return Y.use.apply(Y, a);
+ }
+
+
+ // use loader to expand dependencies and sort the
+ // requirements if it is available.
+ if (Y.Loader) {
+ dynamic = true;
+ loader = new Y.Loader(Y.config);
+ loader.require(a);
+ loader.ignoreRegistered = true;
+ loader.allowRollup = false;
+ loader.calculate();
+ a = loader.sorted;
+ }
+
+
+ l = a.length;
+
+ // process each requirement and any additional requirements
+ // the module metadata specifies
+ for (i=0; i<l; i=i+1) {
+ f(a[i]);
+ }
+
+
+ // dynamic load
+ if (Y.Loader && missing.length) {
+ Y._loading = true;
+ loader = new Y.Loader(Y.config);
+ loader.onSuccess = onComplete;
+ loader.onFailure = onComplete;
+ loader.onTimeout = onComplete;
+ loader.context = Y;
+ loader.attaching = a;
+ loader.require(missing);
+ loader.insert();
+ } else if (Y.Get && missing.length && !Y.Env.bootstrapped) {
+ Y._loading = true;
+
+ a = Y.Array(arguments, 0, true);
+ // a.unshift('loader');
+
+ Y.Get.script(Y.config.base + Y.config.loaderPath, {
+ onEnd: function() {
+ Y._loading = false;
+ Y.Env.bootstrapped = true;
+ Y._attach(['loader']);
+ Y.use.apply(Y, a);
+ }
+ });
+
+ return Y;
+
+ } else {
+ Y._attach(r);
+ onComplete();
+ }
+
+ return Y; // chain support var yui = YUI().use('dragdrop');
+ },
+
+
+ /**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YUI.namespace("property.package");
+ * YUI.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YUI.property, then
+ * YUI.property.package (YAHOO is scrubbed out, this is
+ * to remain compatible with YUI2)
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YUI.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @param {string*} arguments 1-n namespaces to create
+ * @return {object} A reference to the last namespace object created
+ */
+ namespace: function() {
+ var a=arguments, o=null, i, j, d;
+ for (i=0; i<a.length; i=i+1) {
+ d = ("" + a[i]).split(".");
+ o = this;
+ for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+ o[d[j]] = o[d[j]] || {};
+ o = o[d[j]];
+ }
+ }
+ return o;
+ },
+
+ // this is replaced if the log module is included
+ log: function() {
+
+ },
+
+ /**
+ * Report an error. The reporting mechanism is controled by
+ * the 'throwFail' configuration attribute. If throwFail is
+ * not specified, the message is written to the Logger, otherwise
+ * a JS error is thrown
+ * @method error
+ * @param msg {string} the error message
+ * @param e {Error} Optional JS error that was caught. If supplied
+ * and throwFail is specified, this error will be re-thrown.
+ * @return {YUI} this YUI instance
+ */
+ error: function(msg, e) {
+ if (this.config.throwFail) {
+ throw (e || new Error(msg));
+ } else {
+ this.message(msg, "error"); // don't scrub this one
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate an id that is unique among all YUI instances
+ * @method guid
+ * @param pre {string} optional guid prefix
+ * @return {string} the guid
+ */
+ guid: function(pre) {
+ var id = this.Env._guidp + (++this.Env._uidx);
+ return (pre) ? (pre + id) : id;
+ },
+
+ /**
+ * Returns a guid associated with an object. If the object
+ * does not have one, a new one is created unless readOnly
+ * is specified.
+ * @method stamp
+ * @param o The object to stamp
+ * @param readOnly {boolean} if true, a valid guid will only
+ * be returned if the object has one assigned to it.
+ * @return {string} The object's guid or null
+ */
+ stamp: function(o, readOnly) {
+
+ if (!o) {
+ return o;
+ }
+
+ var uid = (typeof o === 'string') ? o : o._yuid;
+
+ if (!uid) {
+ uid = this.guid();
+ if (!readOnly) {
+ try {
+ o._yuid = uid;
+ } catch(e) {
+ uid = null;
+ }
+ }
+ }
+
+ return uid;
+ }
+};
+
+// Give the YUI global the same properties as an instance.
+// This makes it so that the YUI global can be used like the YAHOO
+// global was used prior to 3.x. More importantly, the YUI global
+// provides global metadata, so env needs to be configured.
+// @TODO review
+
+ p = YUI.prototype;
+
+ // inheritance utilities are not available yet
+ for (i in p) {
+ // if (1) { // intenionally ignoring hasOwnProperty check
+ YUI[i] = p[i];
+ // }
+ }
+
+ // set up the environment
+ YUI._init();
+
+ // add a window load event at load time so we can capture
+ // the case where it fires before dynamic loading is
+ // complete.
+ add(window, 'load', globalListener);
+
+ YUI.Env.add = add;
+ YUI.Env.remove = remove;
+
+ /*
+ * Subscribe to an event. The signature differs depending on the
+ * type of event you are attaching to.
+ * @method on
+ * @param type {string|function|object} The type of the event. If
+ * this is a function, this is dispatched to the aop system. If an
+ * object, it is parsed for multiple subsription definitions
+ * @param fn {Function} The callback
+ * @param elspec {any} DOM element(s), selector string(s), and or
+ * Node ref(s) to attach DOM related events to (only applies to
+ * DOM events).
+ * @param
+ * @return the event target or a detach handle per 'chain' config
+ */
+
+})();
+
+/**
+ * The config object contains all of the configuration options for
+ * the YUI instance. This object is supplied by the implementer
+ * when instantiating a YUI instance. Some properties have default
+ * values if they are not supplied by the implementer.
+ *
+ * @class config
+ * @static
+ */
+
+/**
+ * Turn debug statements on or off.
+ *
+ * @property debug
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * Log to the browser console if debug is on and the browser has a
+ * supported console.
+ *
+ * @property useBrowserConsole
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * A hash of log sources that should be logged. If specified, only log messages from these sources will be logged.
+ *
+ * @property logInclude
+ * @type object
+ */
+
+/**
+ * A hash of log sources that should be not be logged. If specified, all sources are logged if not on this list.
+ *
+ * @property logExclude
+ * @type object
+ */
+
+/**
+ * Set to true if the yui seed file was dynamically loaded in
+ * order to bootstrap components relying on the window load event
+ * and the 'domready' custom event.
+ *
+ * @property injected
+ * @type object
+ */
+
+/**
+ * If throwFail is set, Y.fail will generate or re-throw a JS Error. Otherwise the failure is logged.
+ *
+ * @property throwFail
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * The window/frame that this instance should operate in.
+ *
+ * @property win
+ * @type Window
+ * @default the window hosting YUI
+ */
+
+/**
+ * The document associated with the 'win' configuration.
+ *
+ * @property doc
+ * @type Document
+ * @default the document hosting YUI
+ */
+
+/**
+ * A list of modules that defines the YUI core (overrides the default).
+ *
+ * @property core
+ * @type string[]
+ */
+
+/**
+ * The default date format
+ *
+ * @property dateFormat
+ * @type string
+ */
+
+/**
+ * The default locale
+ *
+ * @property locale
+ * @type string
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property pollInterval
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The number of dynamic nodes to insert by default before
+ * automatically removing them. This applies to script nodes
+ * because remove the node will not make the evaluated script
+ * unavailable. Dynamic CSS is not auto purged, because removing
+ * a linked style sheet will also remove the style definitions.
+ *
+ * @property purgethreshold
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property windowResizeDelay
+ * @type int
+ * @default 40
+ */
+
+/**
+ * Base directory for dynamic loading
+ *
+ * @property base
+ * @type string
+ */
+
+/**
+ * The secure base dir (not implemented)
+ *
+ * For dynamic loading.
+ *
+ * @property secureBase
+ * @type string
+ */
+
+/**
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?
+ *
+ * For dynamic loading.
+ *
+ * @property comboBase
+ * @type string
+ */
+
+/**
+ * The root path to prepend to module names for the combo service. Ex: 3.0.0b1/build/
+ *
+ * For dynamic loading.
+ *
+ * @property root
+ * @type string
+ */
+
+/**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * For dynamic loading.
+ *
+ * @property filter
+ * @type string|object
+ */
+
+/**
+ * Hash of per-component filter specification. If specified for a given component,
+ * this overrides the filter config
+ *
+ * For dynamic loading.
+ *
+ * @property filters
+ * @type object
+ */
+
+/**
+ * Use the YUI combo service to reduce the number of http connections
+ * required to load your dependencies.
+ *
+ * For dynamic loading.
+ *
+ * @property combine
+ * @type boolean
+ * @default true if 'base' is not supplied, false if it is.
+ */
+
+/**
+ * A list of modules that should never be dynamically loaded
+ *
+ * @property ignore
+ * @type string[]
+ */
+
+/**
+ * A list of modules that should always be loaded when required, even if already
+ * present on the page.
+ *
+ * @property force
+ * @type string[]
+ */
+
+/**
+ * Node or id for a node that should be used as the insertion point for new nodes
+ * For dynamic loading.
+ *
+ * @property insertBefore
+ * @type string
+ */
+
+/**
+ * charset for dynamic nodes
+ *
+ * @property charset
+ * @type string
+ * @deprecated use jsAttributes cssAttributes
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded script nodes.
+ *
+ * @property jsAttributes
+ * @type string
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded link nodes.
+ *
+ * @property cssAttributes
+ * @type string
+ */
+
+/**
+ * Number of milliseconds before a timeout occurs when dynamically
+ * loading nodes. If not set, there is no timeout.
+ *
+ * @property timeout
+ * @type int
+ */
+
+/**
+ * Callback for the 'CSSComplete' event. When dynamically loading YUI
+ * components with CSS, this property fires when the CSS is finished
+ * loading but script loading is still ongoing. This provides an
+ * opportunity to enhance the presentation of a loading page a little
+ * bit before the entire loading process is done.
+ *
+ * @property onCSS
+ * @type function
+ */
+
+/**
+ * A list of module definitions to add to the list of YUI components.
+ * These components can then be dynamically loaded side by side with
+ * YUI via the use() method.See Loader.addModule for the supported
+ * module metadata.
+ *
+ * @property modules
+ * @type function
+ */
+
+/**
+ * The loader 'path' attribute to the loader itself. This is combined
+ * with the 'base' attribute to dynamically load the loader component
+ * when boostrapping with the get utility alone.
+ *
+ * @property loaderPath
+ * @default loader/loader-min.js
+ */
+YUI.add('yui-base', function(Y) {
+
+/*
+ * YUI stub
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1, info: 1, warn: 1, error: 1 },
+ _published;
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var Y = INSTANCE, c = Y.config, bail = false, excl, incl, m, f;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+
+ excl = c.logExclude;
+ incl = c.logInclude;
+
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (excl && (src in excl)) {
+ bail = 1;
+ }
+ }
+
+ if (!bail) {
+
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (Y.fire && !bail && !silent) {
+ if (!_published) {
+ Y.publish(LOGEVENT, {
+ broadcast: 2,
+ emitFacade: 1
+ });
+
+ _published = 1;
+
+ }
+ Y.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+})();
+(function() {
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class Lang
+ * @static
+ */
+Y.Lang = Y.Lang || {};
+
+var L = Y.Lang,
+
+ARRAY = 'array',
+BOOLEAN = 'boolean',
+DATE = 'date',
+ERROR = 'error',
+FUNCTION = 'function',
+NUMBER = 'number',
+NULL = 'null',
+OBJECT = 'object',
+REGEX = 'regexp',
+STRING = 'string',
+TOSTRING = Object.prototype.toString,
+UNDEFINED = 'undefined',
+
+TYPES = {
+ 'undefined' : UNDEFINED,
+ 'number' : NUMBER,
+ 'boolean' : BOOLEAN,
+ 'string' : STRING,
+ '[object Function]' : FUNCTION,
+ '[object RegExp]' : REGEX,
+ '[object Array]' : ARRAY,
+ '[object Date]' : DATE,
+ '[object Error]' : ERROR
+},
+
+TRIMREGEX = /^\s+|\s+$/g,
+EMPTYSTRING = '';
+
+/**
+ * Determines whether or not the provided item is an array.
+ * Returns false for array-like collections such as the
+ * function arguments collection or HTMLElement collection
+ * will return false. You can use @see Array.test if you
+ * want to
+ * @method isArray
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is an array
+ */
+L.isArray = function(o) {
+ return L.type(o) === ARRAY;
+};
+
+/**
+ * Determines whether or not the provided item is a boolean
+ * @method isBoolean
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a boolean
+ */
+L.isBoolean = function(o) {
+ return typeof o === BOOLEAN;
+};
+
+/**
+ * Determines whether or not the provided item is a function
+ * Note: Internet Explorer thinks certain functions are objects:
+ *
+ * var obj = document.createElement("object");
+ * Y.Lang.isFunction(obj.getAttribute) // reports false in IE
+ *
+ * var input = document.createElement("input"); // append to body
+ * Y.Lang.isFunction(input.focus) // reports false in IE
+ *
+ * You will have to implement additional tests if these functions
+ * matter to you.
+ *
+ * @method isFunction
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a function
+ */
+L.isFunction = function(o) {
+ return L.type(o) === FUNCTION;
+};
+
+/**
+ * Determines whether or not the supplied item is a date instance
+ * @method isDate
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a date
+ */
+L.isDate = function(o) {
+ // return o instanceof Date;
+ return L.type(o) === DATE;
+};
+
+/**
+ * Determines whether or not the provided item is null
+ * @method isNull
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is null
+ */
+L.isNull = function(o) {
+ return o === null;
+};
+
+/**
+ * Determines whether or not the provided item is a legal number
+ * @method isNumber
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a number
+ */
+L.isNumber = function(o) {
+ return typeof o === NUMBER && isFinite(o);
+};
+
+/**
+ * Determines whether or not the provided item is of type object
+ * or function
+ * @method isObject
+ * @static
+ * @param o The object to test
+ * @param failfn {boolean} fail if the input is a function
+ * @return {boolean} true if o is an object
+ */
+L.isObject = function(o, failfn) {
+return (o && (typeof o === OBJECT || (!failfn && L.isFunction(o)))) || false;
+};
+
+/**
+ * Determines whether or not the provided item is a string
+ * @method isString
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a string
+ */
+L.isString = function(o) {
+ return typeof o === STRING;
+};
+
+/**
+ * Determines whether or not the provided item is undefined
+ * @method isUndefined
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is undefined
+ */
+L.isUndefined = function(o) {
+ return typeof o === UNDEFINED;
+};
+
+/**
+ * Returns a string without any leading or trailing whitespace. If
+ * the input is not a string, the input will be returned untouched.
+ * @method trim
+ * @static
+ * @param s {string} the string to trim
+ * @return {string} the trimmed string
+ */
+L.trim = function(s){
+ try {
+ return s.replace(TRIMREGEX, EMPTYSTRING);
+ } catch(e) {
+ return s;
+ }
+};
+
+/**
+ * A convenience method for detecting a legitimate non-null value.
+ * Returns false for null/undefined/NaN, true for other values,
+ * including 0/false/''
+ * @method isValue
+ * @static
+ * @param o The item to test
+ * @return {boolean} true if it is not null/undefined/NaN || false
+ */
+L.isValue = function(o) {
+ var t = L.type(o);
+ switch (t) {
+ case NUMBER:
+ return isFinite(o);
+ case NULL:
+ case UNDEFINED:
+ return false;
+ default:
+ return !!(t);
+ }
+};
+
+/**
+ * Returns a string representing the type of the item passed in.
+ * @method type
+ * @param o the item to test
+ * @return {string} the detected type
+ */
+L.type = function (o) {
+ return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? OBJECT : NULL);
+};
+
+})();
+
+/*
+ * Provides information about the environment hosting YUI
+ * @module yui
+ * @submodule Array
+ */
+
+(function() {
+
+var L = Y.Lang, Native = Array.prototype,
+
+/**
+ * Adds the following array utilities to the YUI instance. Additional
+ * array helpers can be found in the collection component.
+ * @class Array
+ */
+
+/**
+ * Y.Array(o) returns an array:
+ * - Arrays are return unmodified unless the start position is specified.
+ * - "Array-like" collections (@see Array.test) are converted to arrays
+ * - For everything else, a new array is created with the input as the sole item
+ * - The start position is used if the input is or is like an array to return
+ * a subset of the collection.
+ *
+ * @TODO this will not automatically convert elements that are also collections
+ * such as forms and selects. Passing true as the third param will
+ * force a conversion.
+ *
+ * @method ()
+ * @static
+ * @param o the item to arrayify
+ * @param i {int} if an array or array-like, this is the start index
+ * @param al {boolean} if true, it forces the array-like fork. This
+ * can be used to avoid multiple array.test calls.
+ * @return {Array} the resulting array
+ */
+YArray = function(o, startIdx, al) {
+ var t = (al) ? 2 : Y.Array.test(o), i, l, a;
+
+ // switch (t) {
+ // case 1:
+ // // return (startIdx) ? o.slice(startIdx) : o;
+ // case 2:
+ // return Native.slice.call(o, startIdx || 0);
+ // default:
+ // return [o];
+ // }
+
+ if (t) {
+ try {
+ return Native.slice.call(o, startIdx || 0);
+ // IE errors when trying to slice element collections
+ } catch(e) {
+ a=[];
+ for (i=0, l=o.length; i<l; i=i+1) {
+ a.push(o[i]);
+ }
+ return a;
+ }
+ } else {
+ return [o];
+ }
+
+};
+
+Y.Array = YArray;
+
+/**
+ * Evaluates the input to determine if it is an array, array-like, or
+ * something else. This is used to handle the arguments collection
+ * available within functions, and HTMLElement collections
+ *
+ * @method test
+ * @static
+ *
+ * @todo current implementation (intenionally) will not implicitly
+ * handle html elements that are array-like (forms, selects, etc).
+ *
+ * @return {int} a number indicating the results:
+ * 0: Not an array or an array-like collection
+ * 1: A real array.
+ * 2: array-like collection.
+ */
+YArray.test = function(o) {
+ var r = 0;
+ if (L.isObject(o)) {
+ if (L.isArray(o)) {
+ r = 1;
+ } else {
+ try {
+ // indexed, but no tagName (element) or alert (window)
+ if ("length" in o && !("tagName" in o) && !("alert" in o) &&
+ (!Y.Lang.isFunction(o.size) || o.size() > 1)) {
+ r = 2;
+ }
+
+ } catch(e) {}
+ }
+ }
+ return r;
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * @method each
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item
+ * @param o Optional context object
+ * @static
+ * @return {YUI} the YUI instance
+ */
+YArray.each = (Native.forEach) ?
+ function (a, f, o) {
+ Native.forEach.call(a || [], f, o || Y);
+ return Y;
+ } :
+ function (a, f, o) {
+ var l = (a && a.length) || 0, i;
+ for (i = 0; i < l; i=i+1) {
+ f.call(o || Y, a[i], i, a);
+ }
+ return Y;
+ };
+
+/**
+ * Returns an object using the first array as keys, and
+ * the second as values. If the second array is not
+ * provided the value is set to true for each.
+ * @method hash
+ * @static
+ * @param k {Array} keyset
+ * @param v {Array} optional valueset
+ * @return {object} the hash
+ */
+YArray.hash = function(k, v) {
+ var o = {}, l = k.length, vl = v && v.length, i;
+ for (i=0; i<l; i=i+1) {
+ o[k[i]] = (vl && vl > i) ? v[i] : true;
+ }
+
+ return o;
+};
+
+/**
+ * Returns the index of the first item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * @method indexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of the item that contains the value or -1
+ */
+YArray.indexOf = (Native.indexOf) ?
+ function(a, val) {
+ return Native.indexOf.call(a, val);
+ } :
+ function(a, val) {
+ for (var i=0; i<a.length; i=i+1) {
+ if (a[i] === val) {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+/**
+ * Numeric sort convenience function.
+ * Y.ArrayAssert.itemsAreEqual([1, 2, 3], [3, 1, 2].sort(Y.Array.numericSort));
+ * @method numericSort
+ */
+YArray.numericSort = function(a, b) {
+ return (a - b);
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * Returning true from the processing function will stop the
+ * processing of the remaining
+ * items.
+ * @method some
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item
+ * @param o Optional context object
+ * @static
+ * @return {boolean} true if the function returns true on
+ * any of the items in the array
+ */
+ YArray.some = (Native.some) ?
+ function (a, f, o) {
+ return Native.some.call(a, f, o);
+ } :
+ function (a, f, o) {
+ var l = a.length, i;
+ for (i=0; i<l; i=i+1) {
+ if (f.call(o, a[i], i, a)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+})();
+(function() {
+
+var L = Y.Lang,
+DELIMITER = '__',
+// FROZEN = {
+// 'prototype': 1,
+// '_yuid': 1
+// },
+
+/*
+ * IE will not enumerate native functions in a derived object even if the
+ * function was overridden. This is a workaround for specific functions
+ * we care about on the Object prototype.
+ * @property _iefix
+ * @for YUI
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @private
+ */
+_iefix = function(r, s) {
+ var fn = s.toString;
+ if (L.isFunction(fn) && fn != Object.prototype.toString) {
+ r.toString = fn;
+ }
+};
+
+
+/**
+ * Returns a new object containing all of the properties of
+ * all the supplied objects. The properties from later objects
+ * will overwrite those in earlier objects. Passing in a
+ * single object will create a shallow copy of it. For a deep
+ * copy, use clone.
+ * @method merge
+ * @for YUI
+ * @param arguments {Object*} the objects to merge
+ * @return {object} the new merged object
+ */
+Y.merge = function() {
+ var a = arguments, o = {}, i, l = a.length;
+ for (i=0; i<l; i=i+1) {
+ Y.mix(o, a[i], true);
+ }
+ return o;
+};
+
+/**
+ * Applies the supplier's properties to the receiver. By default
+ * all prototype and static propertes on the supplier are applied
+ * to the corresponding spot on the receiver. By default all
+ * properties are applied, and a property that is already on the
+ * reciever will not be overwritten. The default behavior can
+ * be modified by supplying the appropriate parameters.
+ *
+ * @TODO add constants for the modes
+ *
+ * @method mix
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param {int} mode what should be copies, and to where
+ * default(0): object to object
+ * 1: prototype to prototype (old augment)
+ * 2: prototype to prototype and object props (new augment)
+ * 3: prototype to object
+ * 4: object to prototype
+ * @param merge {boolean} merge objects instead of overwriting/ignoring
+ * Used by Y.aggregate
+ * @return {object} the augmented object
+ */
+Y.mix = function(r, s, ov, wl, mode, merge) {
+
+ if (!s||!r) {
+ return r || Y;
+ }
+
+ if (mode) {
+ switch (mode) {
+ case 1: // proto to proto
+ return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ case 2: // object to object and proto to proto
+ Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ break; // pass through
+ case 3: // proto to static
+ return Y.mix(r, s.prototype, ov, wl, 0, merge);
+ case 4: // static to proto
+ return Y.mix(r.prototype, s, ov, wl, 0, merge);
+ default: // object to object is what happens below
+ }
+ }
+
+ // Maybe don't even need this wl && wl.length check anymore??
+ var arr = merge && L.isArray(r), i, l, p;
+
+ if (wl && wl.length) {
+ for (i = 0, l = wl.length; i < l; ++i) {
+ p = wl[i];
+ if (p in s) {
+ if (merge && L.isObject(r[p], true)) {
+ Y.mix(r[p], s[p]);
+ } else if (!arr && (ov || !(p in r))) {
+ r[p] = s[p];
+ } else if (arr) {
+ r.push(s[p]);
+ }
+ }
+ }
+ } else {
+ for (i in s) {
+ // if (s.hasOwnProperty(i) && !(i in FROZEN)) {
+ // check white list if it was supplied
+ // if the receiver has this property, it is an object,
+ // and merge is specified, merge the two objects.
+ if (merge && L.isObject(r[i], true)) {
+ Y.mix(r[i], s[i]); // recursive
+ // otherwise apply the property only if overwrite
+ // is specified or the receiver doesn't have one.
+ } else if (!arr && (ov || !(i in r))) {
+ r[i] = s[i];
+ // if merge is specified and the receiver is an array,
+ // append the array item
+ } else if (arr) {
+ r.push(s[i]);
+ }
+ // }
+ }
+
+ if (Y.UA.ie) {
+ _iefix(r, s);
+ }
+ }
+
+ return r;
+};
+
+/**
+ * Returns a wrapper for a function which caches the
+ * return value of that function, keyed off of the combined
+ * argument values.
+ * @function cached
+ * @param source {function} the function to memoize
+ * @param cache an optional cache seed
+ * @param refetch if supplied, this value is tested against the cached
+ * value. If the values are equal, the wrapped function is executed again.
+ * @return {Function} the wrapped function
+ */
+Y.cached = function(source, cache, refetch){
+ cache = cache || {};
+
+ return function(arg1, arg2) {
+
+ var k = (arg2) ? Array.prototype.join.call(arguments, DELIMITER) : arg1,
+ v = cache[k];
+
+ if (!(k in cache) || (refetch && cache[k] == refetch)) {
+ cache[k] = source.apply(source, arguments);
+ }
+
+ return cache[k];
+ };
+
+};
+
+})();
+
+(function() {
+
+/**
+ * Adds the following Object utilities to the YUI instance
+ * @class Object
+ */
+
+/**
+ * Y.Object(o) returns a new object based upon the supplied object.
+ * @TODO Use native Object.create() when available
+ * @method ()
+ * @static
+ * @param o the supplier object
+ * @return {Object} the new object
+ */
+Y.Object = function(o) {
+ var F = function() {};
+ F.prototype = o;
+ return new F();
+};
+
+var O = Y.Object,
+
+UNDEFINED = undefined,
+
+/**
+ * Extracts the keys, values, or size from an object
+ *
+ * @method _extract
+ * @param o the object
+ * @param what what to extract (0: keys, 1: values, 2: size)
+ * @return {boolean|Array} the extracted info
+ * @static
+ * @private
+ */
+_extract = function(o, what) {
+ var count = (what === 2), out = (count) ? 0 : [], i;
+
+ for (i in o) {
+ if (count) {
+ out++;
+ } else {
+ if (o.hasOwnProperty(i)) {
+ out.push((what) ? o[i] : i);
+ }
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Returns an array containing the object's keys
+ * @TODO use native Object.keys() if available
+ * @method keys
+ * @static
+ * @param o an object
+ * @return {string[]} the keys
+ */
+O.keys = function(o) {
+ return _extract(o);
+};
+
+/**
+ * Returns an array containing the object's values
+ * @TODO use native Object.values() if available
+ * @method values
+ * @static
+ * @param o an object
+ * @return {Array} the values
+ */
+O.values = function(o) {
+ return _extract(o, 1);
+};
+
+/**
+ * Returns the size of an object
+ * @TODO use native Object.size() if available
+ * @method size
+ * @static
+ * @param o an object
+ * @return {int} the size
+ */
+O.size = function(o) {
+ return _extract(o, 2);
+};
+
+/**
+ * Returns true if the object contains a given key
+ * @method hasKey
+ * @static
+ * @param o an object
+ * @param k the key to query
+ * @return {boolean} true if the object contains the key
+ */
+O.hasKey = function(o, k) {
+ // return (o.hasOwnProperty(k));
+ return (k in o);
+};
+
+/**
+ * Returns true if the object contains a given value
+ * @method hasValue
+ * @static
+ * @param o an object
+ * @param v the value to query
+ * @return {boolean} true if the object contains the value
+ */
+O.hasValue = function(o, v) {
+ return (Y.Array.indexOf(O.values(o), v) > -1);
+};
+
+/**
+ * Determines whether or not the property was added
+ * to the object instance. Returns false if the property is not present
+ * in the object, or was inherited from the prototype.
+ *
+ * @deprecated Safari 1.x support has been removed, so this is simply a
+ * wrapper for the native implementation. Use the native implementation
+ * directly instead.
+ *
+ * @TODO Remove in B1
+ *
+ * @method owns
+ * @static
+ * @param o {any} The object being testing
+ * @param p {string} the property to look for
+ * @return {boolean} true if the object has the property on the instance
+ */
+O.owns = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+/**
+ * Executes a function on each item. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method each
+ * @static
+ * @param o the object to iterate
+ * @param f {function} the function to execute
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {YUI} the YUI instance
+ */
+O.each = function (o, f, c, proto) {
+ var s = c || Y, i;
+
+ for (i in o) {
+ if (proto || o.hasOwnProperty(i)) {
+ f.call(s, o[i], i, o);
+ }
+ }
+ return Y;
+};
+
+/**
+ * Retrieves the sub value at the provided path,
+ * from the value object provided.
+ *
+ * @method getValue
+ * @param o The object from which to extract the property value
+ * @param path {Array} A path array, specifying the object traversal path
+ * from which to obtain the sub value.
+ * @return {Any} The value stored in the path, undefined if not found.
+ * Returns the source object if an empty path is provided.
+ */
+O.getValue = function (o, path) {
+ var p=Y.Array(path), l=p.length, i;
+
+ for (i=0; o !== UNDEFINED && i < l; i=i+1) {
+ o = o[p[i]];
+ }
+
+ return o;
+};
+
+/**
+ * Sets the sub-attribute value at the provided path on the
+ * value object. Returns the modified value object, or
+ * undefined if the path is invalid.
+ *
+ * @method setValue
+ * @param o The object on which to set the sub value.
+ * @param path {Array} A path array, specifying the object traversal path
+ * at which to set the sub value.
+ * @param val {Any} The new value for the sub-attribute.
+ * @return {Object} The modified object, with the new sub value set, or
+ * undefined, if the path was invalid.
+ */
+O.setValue = function(o, path, val) {
+
+ var p=Y.Array(path), leafIdx=p.length-1, i, ref=o;
+
+ if (leafIdx >= 0) {
+ for (i=0; ref !== UNDEFINED && i < leafIdx; i=i+1) {
+ ref = ref[p[i]];
+ }
+
+ if (ref !== UNDEFINED) {
+ ref[p[i]] = val;
+ } else {
+ return UNDEFINED;
+ }
+ }
+
+ return o;
+};
+
+
+})();
+
+/*
+ * Provides information about the environment hosting YUI
+ * @module yui
+ * @submodule UA
+ */
+/**
+ * YUI user agent detection.
+ * Do not fork for a browser if it can be avoided. Use feature detection when
+ * you can. Use the user agent as a last resort. UA stores a version
+ * number for the browser engine, 0 otherwise. This value may or may not map
+ * to the version number of the browser using the engine. The value is
+ * presented as a float so that it can easily be used for boolean evaluation
+ * as well as for looking for a particular range of versions. Because of this,
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9
+ * reports 1.8).
+ * @class UA
+ * @static
+ */
+Y.UA = function() {
+
+ var numberfy = function(s) {
+ var c = 0;
+ return parseFloat(s.replace(/\./g, function() {
+ return (c++ == 1) ? '' : '.';
+ }));
+ },
+
+ nav = navigator,
+
+ o = {
+
+ /**
+ * Internet Explorer version number or 0. Example: 6
+ * @property ie
+ * @type float
+ * @static
+ */
+ ie: 0,
+
+ /**
+ * Opera version number or 0. Example: 9.2
+ * @property opera
+ * @type float
+ * @static
+ */
+ opera: 0,
+
+ /**
+ * Gecko engine revision number. Will evaluate to 1 if Gecko
+ * is detected but the revision could not be found. Other browsers
+ * will be 0. Example: 1.8
+ * <pre>
+ * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7
+ * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+ * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+ * Firefox 3 alpha: 1.9a4 <-- Reports 1.9
+ * </pre>
+ * @property gecko
+ * @type float
+ * @static
+ */
+ gecko: 0,
+
+ /**
+ * AppleWebKit version. KHTML browsers that are not WebKit browsers
+ * will evaluate to 1, other browsers 0. Example: 418.9
+ * <pre>
+ * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
+ * latest available for Mac OSX 10.3.
+ * Safari 2.0.2: 416 <-- hasOwnProperty introduced
+ * Safari 2.0.4: 418 <-- preventDefault fixed
+ * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+ * different versions of webkit
+ * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been
+ * updated, but not updated
+ * to the latest patch.
+ * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native SVG
+ * and many major issues fixed).
+ * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic update
+ * from 2.x via the 10.4.11 OS patch
+ * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event.
+ * yahoo.com user agent hack removed.
+ * </pre>
+ * http://en.wikipedia.org/wiki/Safari_(web_browser)#Version_history
+ * @property webkit
+ * @type float
+ * @static
+ */
+ webkit: 0,
+
+ /**
+ * The mobile property will be set to a string containing any relevant
+ * user agent information when a modern mobile browser is detected.
+ * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+ * devices with the WebKit-based browser, and Opera Mini.
+ * @property mobile
+ * @type string
+ * @static
+ */
+ mobile: null,
+
+ /**
+ * Adobe AIR version number or 0. Only populated if webkit is detected.
+ * Example: 1.0
+ * @property air
+ * @type float
+ */
+ air: 0,
+
+ /**
+ * Google Caja version number or 0.
+ * @property caja
+ * @type float
+ */
+ caja: nav.cajaVersion,
+
+ /**
+ * Set to true if the page appears to be in SSL
+ * @property secure
+ * @type boolean
+ * @static
+ */
+ secure: false,
+
+ /**
+ * The operating system. Currently only detecting windows or macintosh
+ * @property os
+ * @type string
+ * @static
+ */
+ os: null
+
+ },
+
+ ua = nav && nav.userAgent,
+
+ loc = Y.config.win.location,
+
+ href = loc && loc.href,
+
+ m;
+
+ o.secure = href && (href.toLowerCase().indexOf("https") === 0);
+
+ if (ua) {
+
+ if ((/windows|win32/i).test(ua)) {
+ o.os = 'windows';
+ } else if ((/macintosh/i).test(ua)) {
+ o.os = 'macintosh';
+ }
+
+ // Modern KHTML browsers should qualify as Safari X-Grade
+ if ((/KHTML/).test(ua)) {
+ o.webkit=1;
+ }
+ // Modern WebKit browsers are at least X-Grade
+ m=ua.match(/AppleWebKit\/([^\s]*)/);
+ if (m&&m[1]) {
+ o.webkit=numberfy(m[1]);
+
+ // Mobile browser check
+ if (/ Mobile\//.test(ua)) {
+ o.mobile = "Apple"; // iPhone or iPod Touch
+ } else {
+ m=ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
+ if (m) {
+ o.mobile = m[0]; // Nokia N-series, Android, webOS, ex: NokiaN95
+ }
+ }
+
+ m=ua.match(/AdobeAIR\/([^\s]*)/);
+ if (m) {
+ o.air = m[0]; // Adobe AIR 1.0 or better
+ }
+
+ }
+
+ if (!o.webkit) { // not webkit
+ // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+ m=ua.match(/Opera[\s\/]([^\s]*)/);
+ if (m&&m[1]) {
+ o.opera=numberfy(m[1]);
+ m=ua.match(/Opera Mini[^;]*/);
+ if (m) {
+ o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+ }
+ } else { // not opera or webkit
+ m=ua.match(/MSIE\s([^;]*)/);
+ if (m&&m[1]) {
+ o.ie=numberfy(m[1]);
+ } else { // not opera, webkit, or ie
+ m=ua.match(/Gecko\/([^\s]*)/);
+ if (m) {
+ o.gecko=1; // Gecko detected, look for revision
+ m=ua.match(/rv:([^\s\)]*)/);
+ if (m&&m[1]) {
+ o.gecko=numberfy(m[1]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return o;
+}();
+(function() {
+ var L = Y.Lang,
+
+ /**
+ * Executes the supplied function in the context of the supplied
+ * object 'when' milliseconds later. Executes the function a
+ * single time unless periodic is set to true.
+ * @method later
+ * @for YUI
+ * @param when {int} the number of milliseconds to wait until the fn
+ * is executed.
+ * @param o the context object.
+ * @param fn {Function|String} the function to execute or the name of
+ * the method in the 'o' object to execute.
+ * @param data [Array] data that is provided to the function. This accepts
+ * either a single item or an array. If an array is provided, the
+ * function is executed with one parameter for each array item. If
+ * you need to pass a single array parameter, it needs to be wrapped in
+ * an array [myarray].
+ * @param periodic {boolean} if true, executes continuously at supplied
+ * interval until canceled.
+ * @return {object} a timer object. Call the cancel() method on this object to
+ * stop the timer.
+ */
+ later = function(when, o, fn, data, periodic) {
+ when = when || 0;
+ o = o || {};
+ var m=fn, d=data, f, r;
+
+ if (L.isString(fn)) {
+ m = o[fn];
+ }
+
+ if (!m) {
+ Y.error("method undefined");
+ }
+
+ if (!L.isArray(d)) {
+ d = [data];
+ }
+
+ f = function() {
+ m.apply(o, d);
+ };
+
+ r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+ return {
+ id: r,
+ interval: periodic,
+ cancel: function() {
+ if (this.interval) {
+ clearInterval(r);
+ } else {
+ clearTimeout(r);
+ }
+ }
+ };
+ };
+
+ Y.later = later;
+ L.later = later;
+
+})();
+(function() {
+
+ var min = ['yui-base'], core, C = Y.config, LOADER='loader';
+
+ // apply the minimal required functionality
+ Y.use.apply(Y, min);
+
+
+ if (C.core) {
+ core = C.core;
+ } else {
+ core = ['queue-base', 'get'];
+ if (YUI.Env.mods[LOADER]) {
+ core.push(LOADER);
+ }
+ }
+
+ Y.use.apply(Y, core);
+
+})();
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('get', function(Y) {
+
+(function() {
+
+/**
+ * Provides a mechanism to fetch remote resources and
+ * insert them into a document.
+ * @module yui
+ * @submodule get
+ */
+
+var ua = Y.UA,
+ L = Y.Lang,
+ // PREFIX = Y.guid(),
+ TYPE_JS = "text/javascript",
+ TYPE_CSS = "text/css",
+ STYLESHEET = "stylesheet";
+
+/**
+ * Fetches and inserts one or more script or link nodes into the document
+ * @class Get
+ * @static
+ */
+Y.Get = function() {
+
+ /**
+ * hash of queues to manage multiple requests
+ * @property queues
+ * @private
+ */
+ var queues={},
+
+ /**
+ * queue index used to generate transaction ids
+ * @property qidx
+ * @type int
+ * @private
+ */
+ qidx=0,
+
+ /**
+ * interal property used to prevent multiple simultaneous purge
+ * processes
+ * @property purging
+ * @type boolean
+ * @private
+ */
+ purging=false,
+
+
+ /**
+ * Generates an HTML element, this is not appended to a document
+ * @method _node
+ * @param type {string} the type of element
+ * @param attr {string} the attributes
+ * @param win {Window} optional window to create the element in
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _node = function(type, attr, win) {
+ var w = win || Y.config.win, d=w.document, n=d.createElement(type),
+ i;
+
+ for (i in attr) {
+ if (attr[i] && attr.hasOwnProperty(i)) {
+ n.setAttribute(i, attr[i]);
+ }
+ }
+
+ return n;
+ },
+
+ /**
+ * Generates a link node
+ * @method _linkNode
+ * @param url {string} the url for the css file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _linkNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_CSS,
+ rel: STYLESHEET,
+ href: url
+ };
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+ return _node("link", o, win);
+ },
+
+ /**
+ * Generates a script node
+ * @method _scriptNode
+ * @param url {string} the url for the script file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _scriptNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_JS,
+ src: url
+ };
+
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+
+ return _node("script", o, win);
+ },
+
+ /**
+ * Removes the nodes for the specified queue
+ * @method _purge
+ * @private
+ */
+ _purge = function(tId) {
+ var q=queues[tId], n, l, d, h, s, i, node, attr;
+ if (q) {
+ n = q.nodes;
+ l = n.length;
+ d = q.win.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, tId);
+ if (s) {
+ h = s.parentNode;
+ }
+ }
+
+ for (i=0; i<l; i=i+1) {
+ node = n[i];
+ if (node.clearAttributes) {
+ node.clearAttributes();
+ } else {
+ // This is a hostile delete
+ // operation attempting to improve
+ // memory performance. As such, the
+ // hasOwnProperty check is intentionally
+ // ommitted.
+ for (attr in node) {
+ delete node[attr];
+ }
+ }
+
+ h.removeChild(node);
+ }
+ }
+ q.nodes = [];
+ },
+
+ /**
+ * Returns the data payload for callback functions
+ * @method _returnData
+ * @private
+ */
+ _returnData = function(q, msg, result) {
+ return {
+ tId: q.tId,
+ win: q.win,
+ data: q.data,
+ nodes: q.nodes,
+ msg: msg,
+ statusText: result,
+ purge: function() {
+ _purge(this.tId);
+ }
+ };
+ },
+
+ /**
+ * The transaction is finished
+ * @method _end
+ * @param id {string} the id of the request
+ * @private
+ */
+ _end = function(id, msg, result) {
+ var q = queues[id], sc;
+ if (q && q.onEnd) {
+ sc = q.context || q;
+ q.onEnd.call(sc, _returnData(q, msg, result));
+ }
+ },
+
+ /*
+ * The request failed, execute fail handler with whatever
+ * was accomplished. There isn't a failure case at the
+ * moment unless you count aborted transactions
+ * @method _fail
+ * @param id {string} the id of the request
+ * @private
+ */
+ _fail = function(id, msg) {
+
+ Y.log("get failure: " + msg, "warn", "get");
+
+ var q = queues[id], sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ // execute failure callback
+ if (q.onFailure) {
+ sc = q.context || q;
+ q.onFailure.call(sc, _returnData(q, msg));
+ }
+
+ _end(id, msg, 'failure');
+ },
+
+ _get = function(nId, tId) {
+ var q = queues[tId],
+ n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
+ if (!n) {
+ _fail(tId, "target node not found: " + nId);
+ }
+
+ return n;
+ },
+
+ /**
+ * The request is complete, so executing the requester's callback
+ * @method _finish
+ * @param id {string} the id of the request
+ * @private
+ */
+ _finish = function(id) {
+ Y.log("Finishing transaction " + id, "info", "get");
+ var q = queues[id], msg, sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+ q.finished = true;
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ // execute success callback
+ if (q.onSuccess) {
+ sc = q.context || q;
+ q.onSuccess.call(sc, _returnData(q));
+ }
+
+ _end(id, msg, 'OK');
+ },
+
+ /**
+ * Timeout detected
+ * @method _timeout
+ * @param id {string} the id of the request
+ * @private
+ */
+ _timeout = function(id) {
+ Y.log("Timeout " + id, "info", "get");
+ var q = queues[id], sc;
+ if (q.onTimeout) {
+ sc = q.context || q;
+ q.onTimeout.call(sc, _returnData(q));
+ }
+
+ _end(id, 'timeout', 'timeout');
+ },
+
+
+ /**
+ * Loads the next item for a given request
+ * @method _next
+ * @param id {string} the id of the request
+ * @param loaded {string} the url that was just loaded, if any
+ * @private
+ */
+ _next = function(id, loaded) {
+
+ Y.log("_next: " + id + ", loaded: " + (loaded || "nothing"), "info", "get");
+
+ var q = queues[id], msg, w, d, h, n, url, s;
+
+ if (q.timer) {
+ // Y.log('cancel timer');
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ if (loaded) {
+ q.url.shift();
+ if (q.varName) {
+ q.varName.shift();
+ }
+ } else {
+ // This is the first pass: make sure the url is an array
+ q.url = (L.isString(q.url)) ? [q.url] : q.url;
+ if (q.varName) {
+ q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
+ }
+ }
+
+ w = q.win;
+ d = w.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.url.length === 0) {
+ _finish(id);
+ return;
+ }
+
+ url = q.url[0];
+
+ // if the url is undefined, this is probably a trailing comma problem in IE
+ if (!url) {
+ q.url.shift();
+ Y.log('skipping empty url');
+ return _next(id);
+ }
+
+ Y.log("attempting to load " + url, "info", "get");
+
+ if (q.timeout) {
+ // Y.log('create timer');
+ // q.timer = L.later(q.timeout, q, _timeout, id);
+ q.timer = setTimeout(function() {
+ _timeout(id);
+ }, q.timeout);
+ }
+
+ if (q.type === "script") {
+ n = _scriptNode(url, w, q.attributes);
+ } else {
+ n = _linkNode(url, w, q.attributes);
+ }
+
+ // track this node's load progress
+ _track(q.type, n, id, url, w, q.url.length);
+
+ // add the node to the queue so we can return it to the user supplied callback
+ q.nodes.push(n);
+
+ // add it to the head or insert it before 'insertBefore'
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, id);
+ if (s) {
+ s.parentNode.insertBefore(n, s);
+ }
+ } else {
+ h.appendChild(n);
+ }
+
+ Y.log("Appending node: " + url, "info", "get");
+
+ // FireFox does not support the onload event for link nodes, so there is
+ // no way to make the css requests synchronous. This means that the css
+ // rules in multiple files could be applied out of order in this browser
+ // if a later request returns before an earlier one. Safari too.
+ if ((ua.webkit || ua.gecko) && q.type === "css") {
+ _next(id, url);
+ }
+ },
+
+ /**
+ * Removes processed queues and corresponding nodes
+ * @method _autoPurge
+ * @private
+ */
+ _autoPurge = function() {
+
+ if (purging) {
+ return;
+ }
+
+ purging = true;
+
+ var i, q;
+
+ for (i in queues) {
+ if (queues.hasOwnProperty(i)) {
+ q = queues[i];
+ if (q.autopurge && q.finished) {
+ _purge(q.tId);
+ delete queues[i];
+ }
+ }
+ }
+
+ purging = false;
+ },
+
+ /**
+ * Saves the state for the request and begins loading
+ * the requested urls
+ * @method queue
+ * @param type {string} the type of node to insert
+ * @param url {string} the url to load
+ * @param opts the hash of options for this request
+ * @private
+ */
+ _queue = function(type, url, opts) {
+
+ opts = opts || {};
+
+ var id = "q" + (qidx++), q,
+ thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
+
+ if (qidx % thresh === 0) {
+ _autoPurge();
+ }
+
+ queues[id] = Y.merge(opts, {
+ tId: id,
+ type: type,
+ url: url,
+ finished: false,
+ nodes: []
+ });
+
+ q = queues[id];
+ q.win = q.win || Y.config.win;
+ q.context = q.context || q;
+ q.autopurge = ("autopurge" in q) ? q.autopurge :
+ (type === "script") ? true : false;
+
+ if (opts.charset) {
+ q.attributes = q.attributes || {};
+ q.attributes.charset = opts.charset;
+ }
+
+ // L.later(0, q, _next, id);
+ setTimeout(function() {
+ _next(id);
+ }, 0);
+
+ return {
+ tId: id
+ };
+ },
+
+ /**
+ * Detects when a node has been loaded. In the case of
+ * script nodes, this does not guarantee that contained
+ * script is ready to use.
+ * @method _track
+ * @param type {string} the type of node to track
+ * @param n {HTMLElement} the node to track
+ * @param id {string} the id of the request
+ * @param url {string} the url that is being loaded
+ * @param win {Window} the targeted window
+ * @param qlength the number of remaining items in the queue,
+ * including this one
+ * @param trackfn {Function} function to execute when finished
+ * the default is _next
+ * @private
+ */
+ _track = function(type, n, id, url, win, qlength, trackfn) {
+ var f = trackfn || _next;
+
+ // IE supports the readystatechange event for script and css nodes
+ // Opera only for script nodes. Opera support onload for script
+ // nodes, but this doesn't fire when there is a load failure.
+ // The onreadystatechange appears to be a better way to respond
+ // to both success and failure.
+ if (ua.ie) {
+ n.onreadystatechange = function() {
+ var rs = this.readyState;
+ if ("loaded" === rs || "complete" === rs) {
+ Y.log(id + " onreadstatechange " + url, "info", "get");
+ n.onreadystatechange = null;
+ f(id, url);
+ }
+ };
+
+ // webkit prior to 3.x is no longer supported
+ } else if (ua.webkit) {
+
+ if (type === "script") {
+ // Safari 3.x supports the load event for script nodes (DOM2)
+ n.addEventListener("load", function() {
+ Y.log(id + " DOM2 onload " + url, "info", "get");
+ f(id, url);
+ });
+ }
+
+ // FireFox and Opera support onload (but not DOM2 in FF) handlers for
+ // script nodes. Opera, but not FF, supports the onload event for link
+ // nodes.
+ } else {
+
+ n.onload = function() {
+ Y.log(id + " onload " + url, "info", "get");
+ f(id, url);
+ };
+
+ n.onerror = function(e) {
+ _fail(id, e + ": " + url);
+ };
+ }
+ };
+
+ return {
+
+ /**
+ * The number of request required before an automatic purge.
+ * Can be configured via the 'purgethreshold' config
+ * property PURGE_THRESH
+ * @static
+ * @type int
+ * @default 20
+ * @private
+ */
+ PURGE_THRESH: 20,
+
+ /**
+ * Called by the the helper for detecting script load in Safari
+ * @method _finalize
+ * @static
+ * @param id {string} the transaction id
+ * @private
+ */
+ _finalize: function(id) {
+ Y.log(id + " finalized ", "info", "get");
+ // L.later(0, null, _finish, id);
+ setTimeout(function() {
+ _finish(id);
+ }, 0);
+ },
+
+ /**
+ * Abort a transaction
+ * @method abort
+ * @static
+ * @param o {string|object} Either the tId or the object returned from
+ * script() or css()
+ */
+ abort: function(o) {
+ var id = (L.isString(o)) ? o : o.tId,
+ q = queues[id];
+ if (q) {
+ Y.log("Aborting " + id, "info", "get");
+ q.aborted = true;
+ }
+ },
+
+ /**
+ * Fetches and inserts one or more script nodes into the head
+ * of the current document or the document in a specified window.
+ *
+ * @method script
+ * @static
+ * @param url {string|string[]} the url or urls to the script(s)
+ * @param opts {object} Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the script(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onTimeout</dt>
+ * <dd>
+ * callback to execute when a timeout occurs.
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onEnd</dt>
+ * <dd>a function that executes when the transaction finishes, regardless of the exit path</dd>
+ * <dt>onFailure</dt>
+ * <dd>
+ * callback to execute when the script load operation fails
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted successfully</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove any nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>autopurge</dt>
+ * <dd>
+ * setting to true will let the utilities cleanup routine purge
+ * the script once loaded
+ * </dd>
+ * <dt>purgethreshold</dt>
+ * <dd>
+ * The number of transaction before autopurge should be initiated
+ * </dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callback when the script(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * </dl>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * <dt>timeout</dt>
+ * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd>
+ * <pre>
+ * Y.Get.script(
+ * ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
+ * "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"], {
+ * onSuccess: function(o) {
+ * this.log("won't cause error because Y is the context");
+ * Y.log(o.data); // foo
+ * Y.log(o.nodes.length === 2) // true
+ * // o.purge(); // optionally remove the script nodes immediately
+ * },
+ * onFailure: function(o) {
+ * Y.log("transaction failed");
+ * },
+ * onTimeout: function(o) {
+ * Y.log("transaction timed out");
+ * },
+ * data: "foo",
+ * timeout: 10000, // 10 second timeout
+ * context: Y, // make the YUI instance
+ * // win: otherframe // target another window/frame
+ * autopurge: true // allow the utility to choose when to remove the nodes
+ * purgetheshold: 1 // purge previous transaction before next transaction
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ script: function(url, opts) {
+ return _queue("script", url, opts);
+ },
+
+ /**
+ * Fetches and inserts one or more css link nodes into the
+ * head of the current document or the document in a specified
+ * window.
+ * @method css
+ * @static
+ * @param url {string} the url or urls to the css file(s)
+ * @param opts Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the css file(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>win</dl>
+ * <dd>the window the link nodes(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callbacks when the nodes(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * </dl>
+ * <pre>
+ * Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+ * </pre>
+ * <pre>
+ * Y.Get.css(
+ * ["http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css",
+ * "http://yui.yahooapis.com/2.3.1/build/logger/assets/skins/sam/logger.css"], {
+ * insertBefore: 'custom-styles' // nodes will be inserted before the specified node
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ css: function(url, opts) {
+ return _queue("css", url, opts);
+ }
+ };
+}();
+
+})();
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("get",function(A){(function(){var C=A.UA,B=A.Lang,E="text/javascript",F="text/css",D="stylesheet";A.Get=function(){var M={},K=0,U=false,W=function(a,X,b){var Y=b||A.config.win,c=Y.document,e=c.createElement(a),Z;for(Z in X){if(X[Z]&&X.hasOwnProperty(Z)){e.setAttribute(Z,X[Z]);}}return e;},T=function(Y,Z,X){var a={id:A.guid(),type:F,rel:D,href:Y};if(X){A.mix(a,X);}return W("link",a,Z);},S=function(Y,Z,X){var a={id:A.guid(),type:E,src:Y};if(X){A.mix(a,X);}return W("script",a,Z);},N=function(c){var X=M[c],Y,a,g,e,j,b,Z,f;if(X){Y=X.nodes;a=Y.length;g=X.win.document;e=g.getElementsByTagName("head")[0];if(X.insertBefore){j=L(X.insertBefore,c);if(j){e=j.parentNode;}}for(b=0;b<a;b=b+1){Z=Y[b];if(Z.clearAttributes){Z.clearAttributes();}else{for(f in Z){delete Z[f];}}e.removeChild(Z);}}X.nodes=[];},P=function(Y,Z,X){return{tId:Y.tId,win:Y.win,data:Y.data,nodes:Y.nodes,msg:Z,statusText:X,purge:function(){N(this.tId);}};},O=function(b,a,X){var Y=M[b],Z;if(Y&&Y.onEnd){Z=Y.context||Y;Y.onEnd.call(Z,P(Y,a,X));}},V=function(a,Z){var X=M[a],Y;if(X.timer){clearTimeout(X.timer);}if(X.onFailure){Y=X.context||X;X.onFailure.call(Y,P(X,Z));}O(a,Z,"failure");},L=function(X,a){var Y=M[a],Z=(B.isString(X))?Y.win.document.getElementById(X):X;if(!Z){V(a,"target node not found: "+X);}return Z;},I=function(a){var X=M[a],Z,Y;if(X.timer){clearTimeout(X.timer);}X.finished=true;if(X.aborted){Z="transaction "+a+" was aborted";V(a,Z);return;}if(X.onSuccess){Y=X.context||X;X.onSuccess.call(Y,P(X));}O(a,Z,"OK");},Q=function(Z){var X=M[Z],Y;if(X.onTimeout){Y=X.context||X;X.onTimeout.call(Y,P(X));}O(Z,"timeout","timeout");},H=function(Z,c){var Y=M[Z],b,g,f,e,a,X,i;if(Y.timer){clearTimeout(Y.timer);}if(Y.aborted){b="transaction "+Z+" was aborted";V(Z,b);return;}if(c){Y.url.shift();if(Y.varName){Y.varName.shift();}}else{Y.url=(B.isString(Y.url))?[Y.url]:Y.url;if(Y.varName){Y.varName=(B.isString(Y.varName))?[Y.varName]:Y.varName;}}g=Y.win;f=g.document;e=f.getElementsByTagName("head")[0];if(Y.url.length===0){I(Z);return;}X=Y.url[0];if(!X){Y.url.shift();return H(Z);}if(Y.timeout){Y.timer=setTimeout(function(){Q(Z);},Y.timeout);}if(Y.type==="script"){a=S(X,g,Y.attributes);}else{a=T(X,g,Y.attributes);}J(Y.type,a,Z,X,g,Y.url.length);Y.nodes.push(a);if(Y.insertBefore){i=L(Y.insertBefore,Z);if(i){i.parentNode.insertBefore(a,i);}}else{e.appendChild(a);}if((C.webkit||C.gecko)&&Y.type==="css"){H(Z,X);}},G=function(){if(U){return;}U=true;var X,Y;for(X in M){if(M.hasOwnProperty(X)){Y=M[X];if(Y.autopurge&&Y.finished){N(Y.tId);delete M[X];}}}U=false;},R=function(Y,X,Z){Z=Z||{};var c="q"+(K++),a,b=Z.purgethreshold||A.Get.PURGE_THRESH;if(K%b===0){G();}M[c]=A.merge(Z,{tId:c,type:Y,url:X,finished:false,nodes:[]});a=M[c];a.win=a.win||A.config.win;a.context=a.context||a;a.autopurge=("autopurge" in a)?a.autopurge:(Y==="script")?true:false;if(Z.charset){a.attributes=a.attributes||{};a.attributes.charset=Z.charset;}setTimeout(function(){H(c);},0);return{tId:c};},J=function(Z,e,d,Y,c,b,X){var a=X||H;if(C.ie){e.onreadystatechange=function(){var f=this.readyState;if("loaded"===f||"complete"===f){e.onreadystatechange=null;a(d,Y);}};}else{if(C.webkit){if(Z==="script"){e.addEventListener("load",function(){a(d,Y);});}}else{e.onload=function(){a(d,Y);};e.onerror=function(f){V(d,f+": "+Y);};}}};return{PURGE_THRESH:20,_finalize:function(X){setTimeout(function(){I(X);},0);},abort:function(Y){var Z=(B.isString(Y))?Y:Y.tId,X=M[Z];if(X){X.aborted=true;}},script:function(X,Y){return R("script",X,Y);},css:function(X,Y){return R("css",X,Y);}};}();})();},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('get', function(Y) {
+
+(function() {
+
+/**
+ * Provides a mechanism to fetch remote resources and
+ * insert them into a document.
+ * @module yui
+ * @submodule get
+ */
+
+var ua = Y.UA,
+ L = Y.Lang,
+ // PREFIX = Y.guid(),
+ TYPE_JS = "text/javascript",
+ TYPE_CSS = "text/css",
+ STYLESHEET = "stylesheet";
+
+/**
+ * Fetches and inserts one or more script or link nodes into the document
+ * @class Get
+ * @static
+ */
+Y.Get = function() {
+
+ /**
+ * hash of queues to manage multiple requests
+ * @property queues
+ * @private
+ */
+ var queues={},
+
+ /**
+ * queue index used to generate transaction ids
+ * @property qidx
+ * @type int
+ * @private
+ */
+ qidx=0,
+
+ /**
+ * interal property used to prevent multiple simultaneous purge
+ * processes
+ * @property purging
+ * @type boolean
+ * @private
+ */
+ purging=false,
+
+
+ /**
+ * Generates an HTML element, this is not appended to a document
+ * @method _node
+ * @param type {string} the type of element
+ * @param attr {string} the attributes
+ * @param win {Window} optional window to create the element in
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _node = function(type, attr, win) {
+ var w = win || Y.config.win, d=w.document, n=d.createElement(type),
+ i;
+
+ for (i in attr) {
+ if (attr[i] && attr.hasOwnProperty(i)) {
+ n.setAttribute(i, attr[i]);
+ }
+ }
+
+ return n;
+ },
+
+ /**
+ * Generates a link node
+ * @method _linkNode
+ * @param url {string} the url for the css file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _linkNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_CSS,
+ rel: STYLESHEET,
+ href: url
+ };
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+ return _node("link", o, win);
+ },
+
+ /**
+ * Generates a script node
+ * @method _scriptNode
+ * @param url {string} the url for the script file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _scriptNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_JS,
+ src: url
+ };
+
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+
+ return _node("script", o, win);
+ },
+
+ /**
+ * Removes the nodes for the specified queue
+ * @method _purge
+ * @private
+ */
+ _purge = function(tId) {
+ var q=queues[tId], n, l, d, h, s, i, node, attr;
+ if (q) {
+ n = q.nodes;
+ l = n.length;
+ d = q.win.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, tId);
+ if (s) {
+ h = s.parentNode;
+ }
+ }
+
+ for (i=0; i<l; i=i+1) {
+ node = n[i];
+ if (node.clearAttributes) {
+ node.clearAttributes();
+ } else {
+ // This is a hostile delete
+ // operation attempting to improve
+ // memory performance. As such, the
+ // hasOwnProperty check is intentionally
+ // ommitted.
+ for (attr in node) {
+ delete node[attr];
+ }
+ }
+
+ h.removeChild(node);
+ }
+ }
+ q.nodes = [];
+ },
+
+ /**
+ * Returns the data payload for callback functions
+ * @method _returnData
+ * @private
+ */
+ _returnData = function(q, msg, result) {
+ return {
+ tId: q.tId,
+ win: q.win,
+ data: q.data,
+ nodes: q.nodes,
+ msg: msg,
+ statusText: result,
+ purge: function() {
+ _purge(this.tId);
+ }
+ };
+ },
+
+ /**
+ * The transaction is finished
+ * @method _end
+ * @param id {string} the id of the request
+ * @private
+ */
+ _end = function(id, msg, result) {
+ var q = queues[id], sc;
+ if (q && q.onEnd) {
+ sc = q.context || q;
+ q.onEnd.call(sc, _returnData(q, msg, result));
+ }
+ },
+
+ /*
+ * The request failed, execute fail handler with whatever
+ * was accomplished. There isn't a failure case at the
+ * moment unless you count aborted transactions
+ * @method _fail
+ * @param id {string} the id of the request
+ * @private
+ */
+ _fail = function(id, msg) {
+
+
+ var q = queues[id], sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ // execute failure callback
+ if (q.onFailure) {
+ sc = q.context || q;
+ q.onFailure.call(sc, _returnData(q, msg));
+ }
+
+ _end(id, msg, 'failure');
+ },
+
+ _get = function(nId, tId) {
+ var q = queues[tId],
+ n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
+ if (!n) {
+ _fail(tId, "target node not found: " + nId);
+ }
+
+ return n;
+ },
+
+ /**
+ * The request is complete, so executing the requester's callback
+ * @method _finish
+ * @param id {string} the id of the request
+ * @private
+ */
+ _finish = function(id) {
+ var q = queues[id], msg, sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+ q.finished = true;
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ // execute success callback
+ if (q.onSuccess) {
+ sc = q.context || q;
+ q.onSuccess.call(sc, _returnData(q));
+ }
+
+ _end(id, msg, 'OK');
+ },
+
+ /**
+ * Timeout detected
+ * @method _timeout
+ * @param id {string} the id of the request
+ * @private
+ */
+ _timeout = function(id) {
+ var q = queues[id], sc;
+ if (q.onTimeout) {
+ sc = q.context || q;
+ q.onTimeout.call(sc, _returnData(q));
+ }
+
+ _end(id, 'timeout', 'timeout');
+ },
+
+
+ /**
+ * Loads the next item for a given request
+ * @method _next
+ * @param id {string} the id of the request
+ * @param loaded {string} the url that was just loaded, if any
+ * @private
+ */
+ _next = function(id, loaded) {
+
+
+ var q = queues[id], msg, w, d, h, n, url, s;
+
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ if (loaded) {
+ q.url.shift();
+ if (q.varName) {
+ q.varName.shift();
+ }
+ } else {
+ // This is the first pass: make sure the url is an array
+ q.url = (L.isString(q.url)) ? [q.url] : q.url;
+ if (q.varName) {
+ q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
+ }
+ }
+
+ w = q.win;
+ d = w.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.url.length === 0) {
+ _finish(id);
+ return;
+ }
+
+ url = q.url[0];
+
+ // if the url is undefined, this is probably a trailing comma problem in IE
+ if (!url) {
+ q.url.shift();
+ return _next(id);
+ }
+
+
+ if (q.timeout) {
+ // q.timer = L.later(q.timeout, q, _timeout, id);
+ q.timer = setTimeout(function() {
+ _timeout(id);
+ }, q.timeout);
+ }
+
+ if (q.type === "script") {
+ n = _scriptNode(url, w, q.attributes);
+ } else {
+ n = _linkNode(url, w, q.attributes);
+ }
+
+ // track this node's load progress
+ _track(q.type, n, id, url, w, q.url.length);
+
+ // add the node to the queue so we can return it to the user supplied callback
+ q.nodes.push(n);
+
+ // add it to the head or insert it before 'insertBefore'
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, id);
+ if (s) {
+ s.parentNode.insertBefore(n, s);
+ }
+ } else {
+ h.appendChild(n);
+ }
+
+
+ // FireFox does not support the onload event for link nodes, so there is
+ // no way to make the css requests synchronous. This means that the css
+ // rules in multiple files could be applied out of order in this browser
+ // if a later request returns before an earlier one. Safari too.
+ if ((ua.webkit || ua.gecko) && q.type === "css") {
+ _next(id, url);
+ }
+ },
+
+ /**
+ * Removes processed queues and corresponding nodes
+ * @method _autoPurge
+ * @private
+ */
+ _autoPurge = function() {
+
+ if (purging) {
+ return;
+ }
+
+ purging = true;
+
+ var i, q;
+
+ for (i in queues) {
+ if (queues.hasOwnProperty(i)) {
+ q = queues[i];
+ if (q.autopurge && q.finished) {
+ _purge(q.tId);
+ delete queues[i];
+ }
+ }
+ }
+
+ purging = false;
+ },
+
+ /**
+ * Saves the state for the request and begins loading
+ * the requested urls
+ * @method queue
+ * @param type {string} the type of node to insert
+ * @param url {string} the url to load
+ * @param opts the hash of options for this request
+ * @private
+ */
+ _queue = function(type, url, opts) {
+
+ opts = opts || {};
+
+ var id = "q" + (qidx++), q,
+ thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
+
+ if (qidx % thresh === 0) {
+ _autoPurge();
+ }
+
+ queues[id] = Y.merge(opts, {
+ tId: id,
+ type: type,
+ url: url,
+ finished: false,
+ nodes: []
+ });
+
+ q = queues[id];
+ q.win = q.win || Y.config.win;
+ q.context = q.context || q;
+ q.autopurge = ("autopurge" in q) ? q.autopurge :
+ (type === "script") ? true : false;
+
+ if (opts.charset) {
+ q.attributes = q.attributes || {};
+ q.attributes.charset = opts.charset;
+ }
+
+ // L.later(0, q, _next, id);
+ setTimeout(function() {
+ _next(id);
+ }, 0);
+
+ return {
+ tId: id
+ };
+ },
+
+ /**
+ * Detects when a node has been loaded. In the case of
+ * script nodes, this does not guarantee that contained
+ * script is ready to use.
+ * @method _track
+ * @param type {string} the type of node to track
+ * @param n {HTMLElement} the node to track
+ * @param id {string} the id of the request
+ * @param url {string} the url that is being loaded
+ * @param win {Window} the targeted window
+ * @param qlength the number of remaining items in the queue,
+ * including this one
+ * @param trackfn {Function} function to execute when finished
+ * the default is _next
+ * @private
+ */
+ _track = function(type, n, id, url, win, qlength, trackfn) {
+ var f = trackfn || _next;
+
+ // IE supports the readystatechange event for script and css nodes
+ // Opera only for script nodes. Opera support onload for script
+ // nodes, but this doesn't fire when there is a load failure.
+ // The onreadystatechange appears to be a better way to respond
+ // to both success and failure.
+ if (ua.ie) {
+ n.onreadystatechange = function() {
+ var rs = this.readyState;
+ if ("loaded" === rs || "complete" === rs) {
+ n.onreadystatechange = null;
+ f(id, url);
+ }
+ };
+
+ // webkit prior to 3.x is no longer supported
+ } else if (ua.webkit) {
+
+ if (type === "script") {
+ // Safari 3.x supports the load event for script nodes (DOM2)
+ n.addEventListener("load", function() {
+ f(id, url);
+ });
+ }
+
+ // FireFox and Opera support onload (but not DOM2 in FF) handlers for
+ // script nodes. Opera, but not FF, supports the onload event for link
+ // nodes.
+ } else {
+
+ n.onload = function() {
+ f(id, url);
+ };
+
+ n.onerror = function(e) {
+ _fail(id, e + ": " + url);
+ };
+ }
+ };
+
+ return {
+
+ /**
+ * The number of request required before an automatic purge.
+ * Can be configured via the 'purgethreshold' config
+ * property PURGE_THRESH
+ * @static
+ * @type int
+ * @default 20
+ * @private
+ */
+ PURGE_THRESH: 20,
+
+ /**
+ * Called by the the helper for detecting script load in Safari
+ * @method _finalize
+ * @static
+ * @param id {string} the transaction id
+ * @private
+ */
+ _finalize: function(id) {
+ // L.later(0, null, _finish, id);
+ setTimeout(function() {
+ _finish(id);
+ }, 0);
+ },
+
+ /**
+ * Abort a transaction
+ * @method abort
+ * @static
+ * @param o {string|object} Either the tId or the object returned from
+ * script() or css()
+ */
+ abort: function(o) {
+ var id = (L.isString(o)) ? o : o.tId,
+ q = queues[id];
+ if (q) {
+ q.aborted = true;
+ }
+ },
+
+ /**
+ * Fetches and inserts one or more script nodes into the head
+ * of the current document or the document in a specified window.
+ *
+ * @method script
+ * @static
+ * @param url {string|string[]} the url or urls to the script(s)
+ * @param opts {object} Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the script(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onTimeout</dt>
+ * <dd>
+ * callback to execute when a timeout occurs.
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onEnd</dt>
+ * <dd>a function that executes when the transaction finishes, regardless of the exit path</dd>
+ * <dt>onFailure</dt>
+ * <dd>
+ * callback to execute when the script load operation fails
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted successfully</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove any nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>autopurge</dt>
+ * <dd>
+ * setting to true will let the utilities cleanup routine purge
+ * the script once loaded
+ * </dd>
+ * <dt>purgethreshold</dt>
+ * <dd>
+ * The number of transaction before autopurge should be initiated
+ * </dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callback when the script(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * </dl>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * <dt>timeout</dt>
+ * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd>
+ * <pre>
+ * Y.Get.script(
+ * ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
+ * "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"], {
+ * onSuccess: function(o) {
+ * this.log("won't cause error because Y is the context");
+ * },
+ * onFailure: function(o) {
+ * },
+ * onTimeout: function(o) {
+ * },
+ * data: "foo",
+ * timeout: 10000, // 10 second timeout
+ * context: Y, // make the YUI instance
+ * // win: otherframe // target another window/frame
+ * autopurge: true // allow the utility to choose when to remove the nodes
+ * purgetheshold: 1 // purge previous transaction before next transaction
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ script: function(url, opts) {
+ return _queue("script", url, opts);
+ },
+
+ /**
+ * Fetches and inserts one or more css link nodes into the
+ * head of the current document or the document in a specified
+ * window.
+ * @method css
+ * @static
+ * @param url {string} the url or urls to the css file(s)
+ * @param opts Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the css file(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>win</dl>
+ * <dd>the window the link nodes(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callbacks when the nodes(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * </dl>
+ * <pre>
+ * Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+ * </pre>
+ * <pre>
+ * Y.Get.css(
+ * ["http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css",
+ * insertBefore: 'custom-styles' // nodes will be inserted before the specified node
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ css: function(url, opts) {
+ return _queue("css", url, opts);
+ }
+ };
+}();
+
+})();
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var _instances = {},
+ _startTime = new Date().getTime(),
+ p,
+ i,
+
+ add = function () {
+ if (window.addEventListener) {
+ return function(el, type, fn, capture) {
+ el.addEventListener(type, fn, (!!capture));
+ };
+ } else if (window.attachEvent) {
+ return function(el, type, fn) {
+ el.attachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ remove = function() {
+ if (window.removeEventListener) {
+ return function (el, type, fn, capture) {
+ el.removeEventListener(type, fn, !!capture);
+ };
+ } else if (window.detachEvent) {
+ return function (el, type, fn) {
+ el.detachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ globalListener = function() {
+ YUI.Env.windowLoaded = true;
+ YUI.Env.DOMReady = true;
+ remove(window, 'load', globalListener);
+ },
+
+// @TODO: this needs to be created at build time from module metadata
+
+ _APPLY_TO_WHITE_LIST = {
+ 'io.xdrReady': 1,
+ 'io.xdrResponse':1
+ },
+
+ SLICE = Array.prototype.slice;
+
+// reduce to one or the other
+if (typeof YUI === 'undefined' || !YUI) {
+
+ /**
+ * The YUI global namespace object. If YUI is already defined, the
+ * existing YUI object will not be overwritten so that defined
+ * namespaces are preserved.
+ *
+ * @class YUI
+ * @constructor
+ * @global
+ * @uses EventTarget
+ * @param o* Up to five optional configuration objects. This object is stored
+ * in YUI.config. See config for the list of supported properties.
+ */
+
+ /*global YUI*/
+ // Make a function, disallow direct instantiation
+ YUI = function(o1, o2, o3, o4, o5) {
+
+ var Y = this, a = arguments, i, l = a.length;
+
+ // Allow instantiation without the new operator
+ if (!(Y instanceof YUI)) {
+ return new YUI(o1, o2, o3, o4, o5);
+ } else {
+ // set up the core environment
+ Y._init();
+
+ for (i=0; i<l; i++) {
+ Y._config(a[i]);
+ }
+
+ // bind the specified additional modules for this instance
+ Y._setup();
+
+ return Y;
+ }
+ };
+}
+
+// The prototype contains the functions that are required to allow the external
+// modules to be registered and for the instance to be initialized.
+YUI.prototype = {
+
+ _config: function(o) {
+
+ o = o || {};
+
+ var c = this.config, i, j, m, mods;
+
+ mods = c.modules;
+ for (i in o) {
+ if (mods && i == 'modules') {
+ m = o[i];
+ for (j in m) {
+ if (m.hasOwnProperty(j)) {
+ mods[j] = m[j];
+ }
+ }
+ } else if (i == 'win') {
+ c[i] = o[i].contentWindow || o[i];
+ c.doc = c[i].document;
+ } else {
+ c[i] = o[i];
+ }
+ }
+ },
+
+ /**
+ * Initialize this YUI instance
+ * @private
+ */
+ _init: function() {
+
+ // find targeted window/frame
+ // @TODO create facades
+ var v = '3.0.0', Y = this;
+
+ if (v.indexOf('@') > -1) {
+ v = 'test';
+ }
+
+ Y.version = v;
+
+ Y.Env = {
+ // @todo expand the new module metadata
+ mods: {},
+ cdn: 'http://yui.yahooapis.com/' + v + '/build/',
+ bootstrapped: false,
+ _idx: 0,
+ _used: {},
+ _attached: {},
+ _yidx: 0,
+ _uidx: 0,
+ _loaded: {}
+ };
+
+ Y.Env._loaded[v] = {};
+
+ if (YUI.Env) {
+ Y.Env._yidx = (++YUI.Env._yidx);
+ Y.Env._guidp = ('yui_' + v + '-' + Y.Env._yidx + '-' + _startTime).replace(/\./g, '_');
+ Y.id = Y.stamp(Y);
+ _instances[Y.id] = Y;
+ }
+
+ Y.constructor = YUI;
+
+ // configuration defaults
+ Y.config = {
+
+ win: window || {},
+ doc: document,
+ debug: true,
+ useBrowserConsole: true,
+ throwFail: true,
+ bootstrap: true,
+ fetchCSS: true,
+
+ base: function() {
+ var b, nodes, i, match;
+
+ // get from querystring
+ nodes = document.getElementsByTagName('script');
+
+ for (i=0; i<nodes.length; i=i+1) {
+ match = nodes[i].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);
+ b = match && match[1];
+ if (b) {
+ break;
+ }
+ }
+
+ // use CDN default
+ return b || Y.Env.cdn;
+
+ }(),
+
+ loaderPath: 'loader/loader-min.js'
+ };
+
+ },
+
+ /**
+ * Finishes the instance setup. Attaches whatever modules were defined
+ * when the yui modules was registered.
+ * @method _setup
+ * @private
+ */
+ _setup: function(o) {
+ this.use("yui-base");
+ },
+
+ /**
+ * Executes a method on a YUI instance with
+ * the specified id if the specified method is whitelisted.
+ * @method applyTo
+ * @param id {string} the YUI instance id
+ * @param method {string} the name of the method to exectute.
+ * Ex: 'Object.keys'
+ * @param args {Array} the arguments to apply to the method
+ * @return {object} the return value from the applied method or null
+ */
+ applyTo: function(id, method, args) {
+
+ if (!(method in _APPLY_TO_WHITE_LIST)) {
+ this.log(method + ': applyTo not allowed', 'warn', 'yui');
+ return null;
+ }
+
+ var instance = _instances[id], nest, m, i;
+
+ if (instance) {
+
+ nest = method.split('.');
+ m = instance;
+
+ for (i=0; i<nest.length; i=i+1) {
+
+ m = m[nest[i]];
+
+ if (!m) {
+ this.log('applyTo not found: ' + method, 'warn', 'yui');
+ }
+ }
+
+ return m.apply(instance, args);
+ }
+
+ return null;
+ },
+
+ /**
+ * Register a module
+ * @method add
+ * @param name {string} module name
+ * @param fn {Function} entry point into the module that
+ * is used to bind module to the YUI instance
+ * @param version {string} version string
+ * @param details optional config data:
+ * requires - features that should be present before loading
+ * optional - optional features that should be present if load optional defined
+ * use - features that should be attached automatically
+ * skinnable -
+ * rollup
+ * omit - features that should not be loaded if this module is present
+ * @return {YUI} the YUI instance
+ *
+ */
+ add: function(name, fn, version, details) {
+ // this.log('Adding a new component ' + name);
+ // @todo expand this to include version mapping
+ // @todo may want to restore the build property
+ // @todo fire moduleAvailable event
+
+ YUI.Env.mods[name] = {
+ name: name,
+ fn: fn,
+ version: version,
+ details: details || {}
+ };
+
+ return this; // chain support
+ },
+
+ _attach: function(r, fromLoader) {
+
+ var mods = YUI.Env.mods,
+ attached = this.Env._attached,
+ i, l = r.length, name, m, d, req, use;
+
+ for (i=0; i<l; i=i+1) {
+
+ name = r[i];
+ m = mods[name];
+
+ if (!attached[name] && m) {
+
+ attached[name] = true;
+
+ d = m.details;
+ req = d.requires;
+ use = d.use;
+
+ if (req) {
+ this._attach(this.Array(req));
+ }
+
+ // this.log('attaching ' + name, 'info', 'yui');
+
+ if (m.fn) {
+ m.fn(this);
+ }
+
+ if (use) {
+ this._attach(this.Array(use));
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Bind a module to a YUI instance
+ * @param modules* {string} 1-n modules to bind (uses arguments array)
+ * @param *callback {function} callback function executed when
+ * the instance has the required functionality. If included, it
+ * must be the last parameter.
+ *
+ * @TODO
+ * Implement versioning? loader can load different versions?
+ * Should sub-modules/plugins be normal modules, or do
+ * we add syntax for specifying these?
+ *
+ * YUI().use('dragdrop')
+ * YUI().use('dragdrop:2.4.0'); // specific version
+ * YUI().use('dragdrop:2.4.0-'); // at least this version
+ * YUI().use('dragdrop:2.4.0-2.9999.9999'); // version range
+ * YUI().use('*'); // use all available modules
+ * YUI().use('lang+dump+substitute'); // use lang and some plugins
+ * YUI().use('lang+*'); // use lang and all known plugins
+ *
+ *
+ * @return {YUI} the YUI instance
+ */
+ use: function() {
+
+ if (this._loading) {
+ this._useQueue = this._useQueue || new this.Queue();
+ this._useQueue.add(SLICE.call(arguments, 0));
+ return this;
+ }
+
+ var Y = this,
+ a=SLICE.call(arguments, 0),
+ mods = YUI.Env.mods,
+ used = Y.Env._used,
+ loader,
+ firstArg = a[0],
+ dynamic = false,
+ callback = a[a.length-1],
+ boot = Y.config.bootstrap,
+ k, i, l, missing = [],
+ r = [],
+ css = Y.config.fetchCSS,
+ f = function(name) {
+
+ // only attach a module once
+ if (used[name]) {
+ // Y.log(name + ' already used', 'info', 'yui');
+ return;
+ }
+
+ var m = mods[name], j, req, use;
+
+ if (m) {
+
+ // Y.log('USING ' + name, 'info', 'yui');
+
+ used[name] = true;
+
+ req = m.details.requires;
+ use = m.details.use;
+ } else {
+
+ // CSS files don't register themselves, see if it has been loaded
+ if (!YUI.Env._loaded[Y.version][name]) {
+ // Y.log('module not found: ' + name, 'info', 'yui');
+ missing.push(name);
+ } else {
+ // probably css
+ // Y.log('module not found BUT HAS BEEN LOADED: ' + name, 'info', 'yui');
+ used[name] = true;
+ }
+ }
+
+ // make sure requirements are attached
+ if (req) {
+ if (Y.Lang.isString(req)) {
+ f(req);
+ } else {
+ for (j = 0; j < req.length; j = j + 1) {
+ // Y.log('using module\'s requirements: ' + name, 'info', 'yui');
+ f(req[j]);
+ }
+ }
+ }
+
+ // add this module to full list of things to attach
+ // Y.log('adding to requires list: ' + name);
+ r.push(name);
+
+ },
+
+ onComplete;
+
+ // Y.log(Y.id + ': use called: ' + a + ' :: ' + callback);
+
+ // The last argument supplied to use can be a load complete callback
+ if (typeof callback === 'function') {
+ a.pop();
+ } else {
+ callback = null;
+ }
+
+ onComplete = function(fromLoader) {
+
+ // Y.log('Use complete');
+
+ fromLoader = fromLoader || {
+ success: true,
+ msg: 'not dynamic'
+ };
+
+ if (callback) {
+ callback(Y, fromLoader);
+ }
+
+ if (Y.fire) {
+ Y.fire('yui:load', Y, fromLoader);
+ }
+
+ // process queued use requests as long until done
+ // or dynamic load happens again.
+ Y._loading = false;
+
+ if (Y._useQueue && Y._useQueue.size() && !Y._loading) {
+ Y.use.apply(Y, Y._useQueue.next());
+ }
+ };
+
+
+ // YUI().use('*'); // bind everything available
+ if (firstArg === "*") {
+ a = [];
+ for (k in mods) {
+ if (mods.hasOwnProperty(k)) {
+ a.push(k);
+ }
+ }
+
+ if (callback) {
+ a.push(callback);
+ }
+
+ return Y.use.apply(Y, a);
+ }
+
+ // Y.log('loader before: ' + a.join(','));
+
+ // use loader to expand dependencies and sort the
+ // requirements if it is available.
+ if (Y.Loader) {
+ dynamic = true;
+ loader = new Y.Loader(Y.config);
+ loader.require(a);
+ loader.ignoreRegistered = true;
+ loader.allowRollup = false;
+ // loader.calculate(null, (css && css == 'force') ? null : 'js');
+ // loader.calculate();
+ loader.calculate(null, (css) ? null : 'js');
+ a = loader.sorted;
+ }
+
+ // Y.log('loader after: ' + a.join(','));
+
+ l = a.length;
+
+ // process each requirement and any additional requirements
+ // the module metadata specifies
+ for (i=0; i<l; i=i+1) {
+ f(a[i]);
+ }
+
+ l = missing.length;
+
+ Y.log('Module requirements: ' + a, 'info', 'yui');
+
+ if (l) {
+ missing = Y.Object.keys(Y.Array.hash(missing));
+ Y.log('Modules missing: ' + missing, 'info', 'yui');
+ }
+
+ // dynamic load
+ if (boot && l && Y.Loader) {
+ Y.log('Using loader to fetch missing dependencies.', 'info', 'yui');
+ Y._loading = true;
+ loader = new Y.Loader(Y.config);
+ loader.onSuccess = onComplete;
+ loader.onFailure = onComplete;
+ loader.onTimeout = onComplete;
+ loader.context = Y;
+ loader.attaching = a;
+ // loader.require(missing);
+ loader.require((css) ? missing : a);
+ loader.insert(null, (css) ? null : 'js');
+ } else if (boot && l && Y.Get && !Y.Env.bootstrapped) {
+ Y.log('Fetching loader: ' + Y.config.base + Y.config.loaderPath, 'info', 'yui');
+ Y._loading = true;
+
+ a = Y.Array(arguments, 0, true);
+ // a.unshift('loader');
+
+ Y.Get.script(Y.config.base + Y.config.loaderPath, {
+ onEnd: function() {
+ Y._loading = false;
+ Y.Env.bootstrapped = true;
+ Y._attach(['loader']);
+ Y.use.apply(Y, a);
+ }
+ });
+
+ return Y;
+
+ } else {
+ if (l) {
+ Y.log('Unable or not configured to fetch missing modules.', 'info', 'yui');
+ }
+ Y.log('Attaching available dependencies.', 'info', 'yui');
+ Y._attach(r);
+ onComplete();
+ }
+
+ return Y; // chain support var yui = YUI().use('dragdrop');
+ },
+
+
+ /**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YUI.namespace("property.package");
+ * YUI.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YUI.property, then
+ * YUI.property.package (YAHOO is scrubbed out, this is
+ * to remain compatible with YUI2)
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YUI.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @param {string*} arguments 1-n namespaces to create
+ * @return {object} A reference to the last namespace object created
+ */
+ namespace: function() {
+ var a=arguments, o=null, i, j, d;
+ for (i=0; i<a.length; i=i+1) {
+ d = ("" + a[i]).split(".");
+ o = this;
+ for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+ o[d[j]] = o[d[j]] || {};
+ o = o[d[j]];
+ }
+ }
+ return o;
+ },
+
+ // this is replaced if the log module is included
+ log: function() {
+
+ },
+
+ /**
+ * Report an error. The reporting mechanism is controled by
+ * the 'throwFail' configuration attribute. If throwFail is
+ * not specified, the message is written to the Logger, otherwise
+ * a JS error is thrown
+ * @method error
+ * @param msg {string} the error message
+ * @param e {Error} Optional JS error that was caught. If supplied
+ * and throwFail is specified, this error will be re-thrown.
+ * @return {YUI} this YUI instance
+ */
+ error: function(msg, e) {
+ if (this.config.throwFail) {
+ throw (e || new Error(msg));
+ } else {
+ this.message(msg, "error"); // don't scrub this one
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate an id that is unique among all YUI instances
+ * @method guid
+ * @param pre {string} optional guid prefix
+ * @return {string} the guid
+ */
+ guid: function(pre) {
+ var id = this.Env._guidp + (++this.Env._uidx);
+ return (pre) ? (pre + id) : id;
+ },
+
+ /**
+ * Returns a guid associated with an object. If the object
+ * does not have one, a new one is created unless readOnly
+ * is specified.
+ * @method stamp
+ * @param o The object to stamp
+ * @param readOnly {boolean} if true, a valid guid will only
+ * be returned if the object has one assigned to it.
+ * @return {string} The object's guid or null
+ */
+ stamp: function(o, readOnly) {
+
+ if (!o) {
+ return o;
+ }
+
+ var uid = (typeof o === 'string') ? o : o._yuid;
+
+ if (!uid) {
+ uid = this.guid();
+ if (!readOnly) {
+ try {
+ o._yuid = uid;
+ } catch(e) {
+ uid = null;
+ }
+ }
+ }
+
+ return uid;
+ }
+};
+
+// Give the YUI global the same properties as an instance.
+// This makes it so that the YUI global can be used like the YAHOO
+// global was used prior to 3.x. More importantly, the YUI global
+// provides global metadata, so env needs to be configured.
+// @TODO review
+
+ p = YUI.prototype;
+
+ // inheritance utilities are not available yet
+ for (i in p) {
+ // if (1) { // intenionally ignoring hasOwnProperty check
+ YUI[i] = p[i];
+ // }
+ }
+
+ // set up the environment
+ YUI._init();
+
+ // add a window load event at load time so we can capture
+ // the case where it fires before dynamic loading is
+ // complete.
+ add(window, 'load', globalListener);
+
+ YUI.Env.add = add;
+ YUI.Env.remove = remove;
+
+ /*
+ * Subscribe to an event. The signature differs depending on the
+ * type of event you are attaching to.
+ * @method on
+ * @param type {string|function|object} The type of the event. If
+ * this is a function, this is dispatched to the aop system. If an
+ * object, it is parsed for multiple subsription definitions
+ * @param fn {Function} The callback
+ * @param elspec {any} DOM element(s), selector string(s), and or
+ * Node ref(s) to attach DOM related events to (only applies to
+ * DOM events).
+ * @param
+ * @return the event target or a detach handle per 'chain' config
+ */
+
+})();
+
+/**
+ * The config object contains all of the configuration options for
+ * the YUI instance. This object is supplied by the implementer
+ * when instantiating a YUI instance. Some properties have default
+ * values if they are not supplied by the implementer.
+ *
+ * @class config
+ * @static
+ */
+
+/**
+ * Allows the YUI seed file to fetch the loader component and library
+ * metadata to dynamically load additional dependencies.
+ *
+ * @property bootstrap
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * Log to the browser console if debug is on and the browser has a
+ * supported console.
+ *
+ * @property useBrowserConsole
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * A hash of log sources that should be logged. If specified, only log messages from these sources will be logged.
+ *
+ * @property logInclude
+ * @type object
+ */
+
+/**
+ * A hash of log sources that should be not be logged. If specified, all sources are logged if not on this list.
+ *
+ * @property logExclude
+ * @type object
+ */
+
+/**
+ * Set to true if the yui seed file was dynamically loaded in
+ * order to bootstrap components relying on the window load event
+ * and the 'domready' custom event.
+ *
+ * @property injected
+ * @type object
+ */
+
+/**
+ * If throwFail is set, Y.fail will generate or re-throw a JS Error. Otherwise the failure is logged.
+ *
+ * @property throwFail
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * The window/frame that this instance should operate in.
+ *
+ * @property win
+ * @type Window
+ * @default the window hosting YUI
+ */
+
+/**
+ * The document associated with the 'win' configuration.
+ *
+ * @property doc
+ * @type Document
+ * @default the document hosting YUI
+ */
+
+/**
+ * A list of modules that defines the YUI core (overrides the default).
+ *
+ * @property core
+ * @type string[]
+ */
+
+/**
+ * The default date format
+ *
+ * @property dateFormat
+ * @type string
+ */
+
+/**
+ * The default locale
+ *
+ * @property locale
+ * @type string
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property pollInterval
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The number of dynamic nodes to insert by default before
+ * automatically removing them. This applies to script nodes
+ * because remove the node will not make the evaluated script
+ * unavailable. Dynamic CSS is not auto purged, because removing
+ * a linked style sheet will also remove the style definitions.
+ *
+ * @property purgethreshold
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property windowResizeDelay
+ * @type int
+ * @default 40
+ */
+
+/**
+ * Base directory for dynamic loading
+ *
+ * @property base
+ * @type string
+ */
+
+/**
+ * The secure base dir (not implemented)
+ *
+ * For dynamic loading.
+ *
+ * @property secureBase
+ * @type string
+ */
+
+/**
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?
+ *
+ * For dynamic loading.
+ *
+ * @property comboBase
+ * @type string
+ */
+
+/**
+ * The root path to prepend to module names for the combo service. Ex: 3.0.0b1/build/
+ *
+ * For dynamic loading.
+ *
+ * @property root
+ * @type string
+ */
+
+/**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * For dynamic loading.
+ *
+ * @property filter
+ * @type string|object
+ */
+
+/**
+ * Hash of per-component filter specification. If specified for a given component,
+ * this overrides the filter config
+ *
+ * For dynamic loading.
+ *
+ * @property filters
+ * @type object
+ */
+
+/**
+ * Use the YUI combo service to reduce the number of http connections
+ * required to load your dependencies.
+ *
+ * For dynamic loading.
+ *
+ * @property combine
+ * @type boolean
+ * @default true if 'base' is not supplied, false if it is.
+ */
+
+/**
+ * A list of modules that should never be dynamically loaded
+ *
+ * @property ignore
+ * @type string[]
+ */
+
+/**
+ * A list of modules that should always be loaded when required, even if already
+ * present on the page.
+ *
+ * @property force
+ * @type string[]
+ */
+
+/**
+ * Node or id for a node that should be used as the insertion point for new nodes
+ * For dynamic loading.
+ *
+ * @property insertBefore
+ * @type string
+ */
+
+/**
+ * charset for dynamic nodes
+ *
+ * @property charset
+ * @type string
+ * @deprecated use jsAttributes cssAttributes
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded script nodes.
+ *
+ * @property jsAttributes
+ * @type string
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded link nodes.
+ *
+ * @property cssAttributes
+ * @type string
+ */
+
+/**
+ * Number of milliseconds before a timeout occurs when dynamically
+ * loading nodes. If not set, there is no timeout.
+ *
+ * @property timeout
+ * @type int
+ */
+
+/**
+ * Callback for the 'CSSComplete' event. When dynamically loading YUI
+ * components with CSS, this property fires when the CSS is finished
+ * loading but script loading is still ongoing. This provides an
+ * opportunity to enhance the presentation of a loading page a little
+ * bit before the entire loading process is done.
+ *
+ * @property onCSS
+ * @type function
+ */
+
+/**
+ * A list of module definitions to add to the list of YUI components.
+ * These components can then be dynamically loaded side by side with
+ * YUI via the use() method.See Loader.addModule for the supported
+ * module metadata.
+ *
+ * @property modules
+ * @type function
+ */
+
+/**
+ * The loader 'path' attribute to the loader itself. This is combined
+ * with the 'base' attribute to dynamically load the loader component
+ * when boostrapping with the get utility alone.
+ *
+ * @property loaderPath
+ * @default loader/loader-min.js
+ */
+
+/**
+ *
+ * Specifies whether or not YUI().use(...) will attempt to load CSS
+ * resources at all. Any truthy value will cause CSS dependencies
+ * to load when fetching script. The special value 'force' will
+ * cause CSS dependencies to be loaded even if no script is needed.
+ *
+ * @property fetchCSS
+ * @default true
+ */
+YUI.add('yui-base', function(Y) {
+
+/*
+ * YUI stub
+ * @module yui
+ * @submodule yui-base
+ */
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * A simple FIFO queue. Items are added to the Queue with add(1..n items) and
+ * removed using next().
+ *
+ * @class Queue
+ * @param item* {MIXED} 0..n items to seed the queue
+ */
+function Queue() {
+ this._init();
+ this.add.apply(this, arguments);
+}
+
+Queue.prototype = {
+ /**
+ * Initialize the queue
+ *
+ * @method _init
+ * @protected
+ */
+ _init : function () {
+ /**
+ * The collection of enqueued items
+ *
+ * @property _q
+ * @type {Array}
+ * @protected
+ */
+ this._q = [];
+ },
+
+ /**
+ * Get the next item in the queue.
+ *
+ * @method next
+ * @return {MIXED} the next item in the queue
+ */
+ next : function () {
+ return this._q.shift();
+ },
+
+ /**
+ * Add 0..n items to the end of the queue
+ *
+ * @method add
+ * @param item* {MIXED} 0..n items
+ */
+ add : function () {
+ Y.Array.each(Y.Array(arguments,0,true),function (fn) {
+ this._q.push(fn);
+ },this);
+
+ return this;
+ },
+
+ /**
+ * Returns the current number of queued items
+ *
+ * @method size
+ * @return {Number}
+ */
+ size : function () {
+ return this._q.length;
+ }
+};
+
+Y.Queue = Queue;
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class Lang
+ * @static
+ */
+Y.Lang = Y.Lang || {};
+
+var L = Y.Lang,
+
+ARRAY = 'array',
+BOOLEAN = 'boolean',
+DATE = 'date',
+ERROR = 'error',
+FUNCTION = 'function',
+NUMBER = 'number',
+NULL = 'null',
+OBJECT = 'object',
+REGEX = 'regexp',
+STRING = 'string',
+TOSTRING = Object.prototype.toString,
+UNDEFINED = 'undefined',
+
+TYPES = {
+ 'undefined' : UNDEFINED,
+ 'number' : NUMBER,
+ 'boolean' : BOOLEAN,
+ 'string' : STRING,
+ '[object Function]' : FUNCTION,
+ '[object RegExp]' : REGEX,
+ '[object Array]' : ARRAY,
+ '[object Date]' : DATE,
+ '[object Error]' : ERROR
+},
+
+TRIMREGEX = /^\s+|\s+$/g,
+EMPTYSTRING = '';
+
+/**
+ * Determines whether or not the provided item is an array.
+ * Returns false for array-like collections such as the
+ * function arguments collection or HTMLElement collection
+ * will return false. You can use @see Array.test if you
+ * want to
+ * @method isArray
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is an array
+ */
+L.isArray = function(o) {
+ return L.type(o) === ARRAY;
+};
+
+/**
+ * Determines whether or not the provided item is a boolean
+ * @method isBoolean
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a boolean
+ */
+L.isBoolean = function(o) {
+ return typeof o === BOOLEAN;
+};
+
+/**
+ * Determines whether or not the provided item is a function
+ * Note: Internet Explorer thinks certain functions are objects:
+ *
+ * var obj = document.createElement("object");
+ * Y.Lang.isFunction(obj.getAttribute) // reports false in IE
+ *
+ * var input = document.createElement("input"); // append to body
+ * Y.Lang.isFunction(input.focus) // reports false in IE
+ *
+ * You will have to implement additional tests if these functions
+ * matter to you.
+ *
+ * @method isFunction
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a function
+ */
+L.isFunction = function(o) {
+ return L.type(o) === FUNCTION;
+};
+
+/**
+ * Determines whether or not the supplied item is a date instance
+ * @method isDate
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a date
+ */
+L.isDate = function(o) {
+ // return o instanceof Date;
+ return L.type(o) === DATE;
+};
+
+/**
+ * Determines whether or not the provided item is null
+ * @method isNull
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is null
+ */
+L.isNull = function(o) {
+ return o === null;
+};
+
+/**
+ * Determines whether or not the provided item is a legal number
+ * @method isNumber
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a number
+ */
+L.isNumber = function(o) {
+ return typeof o === NUMBER && isFinite(o);
+};
+
+/**
+ * Determines whether or not the provided item is of type object
+ * or function
+ * @method isObject
+ * @static
+ * @param o The object to test
+ * @param failfn {boolean} fail if the input is a function
+ * @return {boolean} true if o is an object
+ */
+L.isObject = function(o, failfn) {
+return (o && (typeof o === OBJECT || (!failfn && L.isFunction(o)))) || false;
+};
+
+/**
+ * Determines whether or not the provided item is a string
+ * @method isString
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a string
+ */
+L.isString = function(o) {
+ return typeof o === STRING;
+};
+
+/**
+ * Determines whether or not the provided item is undefined
+ * @method isUndefined
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is undefined
+ */
+L.isUndefined = function(o) {
+ return typeof o === UNDEFINED;
+};
+
+/**
+ * Returns a string without any leading or trailing whitespace. If
+ * the input is not a string, the input will be returned untouched.
+ * @method trim
+ * @static
+ * @param s {string} the string to trim
+ * @return {string} the trimmed string
+ */
+L.trim = function(s){
+ try {
+ return s.replace(TRIMREGEX, EMPTYSTRING);
+ } catch(e) {
+ return s;
+ }
+};
+
+/**
+ * A convenience method for detecting a legitimate non-null value.
+ * Returns false for null/undefined/NaN, true for other values,
+ * including 0/false/''
+ * @method isValue
+ * @static
+ * @param o The item to test
+ * @return {boolean} true if it is not null/undefined/NaN || false
+ */
+L.isValue = function(o) {
+ var t = L.type(o);
+ switch (t) {
+ case NUMBER:
+ return isFinite(o);
+ case NULL:
+ case UNDEFINED:
+ return false;
+ default:
+ return !!(t);
+ }
+};
+
+/**
+ * Returns a string representing the type of the item passed in.
+ * @method type
+ * @param o the item to test
+ * @return {string} the detected type
+ */
+L.type = function (o) {
+ return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? OBJECT : NULL);
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang, Native = Array.prototype,
+
+/**
+ * Adds the following array utilities to the YUI instance. Additional
+ * array helpers can be found in the collection component.
+ * @class Array
+ */
+
+/**
+ * Y.Array(o) returns an array:
+ * - Arrays are return unmodified unless the start position is specified.
+ * - "Array-like" collections (@see Array.test) are converted to arrays
+ * - For everything else, a new array is created with the input as the sole item
+ * - The start position is used if the input is or is like an array to return
+ * a subset of the collection.
+ *
+ * @TODO this will not automatically convert elements that are also collections
+ * such as forms and selects. Passing true as the third param will
+ * force a conversion.
+ *
+ * @method ()
+ * @static
+ * @param o the item to arrayify
+ * @param i {int} if an array or array-like, this is the start index
+ * @param al {boolean} if true, it forces the array-like fork. This
+ * can be used to avoid multiple array.test calls.
+ * @return {Array} the resulting array
+ */
+YArray = function(o, startIdx, al) {
+ var t = (al) ? 2 : Y.Array.test(o), i, l, a;
+
+ // switch (t) {
+ // case 1:
+ // // return (startIdx) ? o.slice(startIdx) : o;
+ // case 2:
+ // return Native.slice.call(o, startIdx || 0);
+ // default:
+ // return [o];
+ // }
+
+ if (t) {
+ try {
+ return Native.slice.call(o, startIdx || 0);
+ // IE errors when trying to slice element collections
+ } catch(e) {
+ a=[];
+ for (i=0, l=o.length; i<l; i=i+1) {
+ a.push(o[i]);
+ }
+ return a;
+ }
+ } else {
+ return [o];
+ }
+
+};
+
+Y.Array = YArray;
+
+/**
+ * Evaluates the input to determine if it is an array, array-like, or
+ * something else. This is used to handle the arguments collection
+ * available within functions, and HTMLElement collections
+ *
+ * @method test
+ * @static
+ *
+ * @todo current implementation (intenionally) will not implicitly
+ * handle html elements that are array-like (forms, selects, etc).
+ *
+ * @return {int} a number indicating the results:
+ * 0: Not an array or an array-like collection
+ * 1: A real array.
+ * 2: array-like collection.
+ */
+YArray.test = function(o) {
+ var r = 0;
+ if (L.isObject(o)) {
+ if (L.isArray(o)) {
+ r = 1;
+ } else {
+ try {
+ // indexed, but no tagName (element) or alert (window)
+ if ("length" in o && !("tagName" in o) && !("alert" in o) &&
+ (!Y.Lang.isFunction(o.size) || o.size() > 1)) {
+ r = 2;
+ }
+
+ } catch(e) {}
+ }
+ }
+ return r;
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * @method each
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The
+ * function receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {YUI} the YUI instance
+ */
+YArray.each = (Native.forEach) ?
+ function (a, f, o) {
+ Native.forEach.call(a || [], f, o || Y);
+ return Y;
+ } :
+ function (a, f, o) {
+ var l = (a && a.length) || 0, i;
+ for (i = 0; i < l; i=i+1) {
+ f.call(o || Y, a[i], i, a);
+ }
+ return Y;
+ };
+
+/**
+ * Returns an object using the first array as keys, and
+ * the second as values. If the second array is not
+ * provided the value is set to true for each.
+ * @method hash
+ * @static
+ * @param k {Array} keyset
+ * @param v {Array} optional valueset
+ * @return {object} the hash
+ */
+YArray.hash = function(k, v) {
+ var o = {}, l = k.length, vl = v && v.length, i;
+ for (i=0; i<l; i=i+1) {
+ o[k[i]] = (vl && vl > i) ? v[i] : true;
+ }
+
+ return o;
+};
+
+/**
+ * Returns the index of the first item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * @method indexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of the item that contains the value or -1
+ */
+YArray.indexOf = (Native.indexOf) ?
+ function(a, val) {
+ return Native.indexOf.call(a, val);
+ } :
+ function(a, val) {
+ for (var i=0; i<a.length; i=i+1) {
+ if (a[i] === val) {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+/**
+ * Numeric sort convenience function.
+ * Y.ArrayAssert.itemsAreEqual([1, 2, 3], [3, 1, 2].sort(Y.Array.numericSort));
+ * @method numericSort
+ */
+YArray.numericSort = function(a, b) {
+ return (a - b);
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * Returning true from the processing function will stop the
+ * processing of the remaining
+ * items.
+ * @method some
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {boolean} true if the function returns true on
+ * any of the items in the array
+ */
+ YArray.some = (Native.some) ?
+ function (a, f, o) {
+ return Native.some.call(a, f, o);
+ } :
+ function (a, f, o) {
+ var l = a.length, i;
+ for (i=0; i<l; i=i+1) {
+ if (f.call(o, a[i], i, a)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+})();
+
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang,
+DELIMITER = '__',
+// FROZEN = {
+// 'prototype': 1,
+// '_yuid': 1
+// },
+
+/*
+ * IE will not enumerate native functions in a derived object even if the
+ * function was overridden. This is a workaround for specific functions
+ * we care about on the Object prototype.
+ * @property _iefix
+ * @for YUI
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @private
+ */
+_iefix = function(r, s) {
+ var fn = s.toString;
+ if (L.isFunction(fn) && fn != Object.prototype.toString) {
+ r.toString = fn;
+ }
+};
+
+
+/**
+ * Returns a new object containing all of the properties of
+ * all the supplied objects. The properties from later objects
+ * will overwrite those in earlier objects. Passing in a
+ * single object will create a shallow copy of it. For a deep
+ * copy, use clone.
+ * @method merge
+ * @for YUI
+ * @param arguments {Object*} the objects to merge
+ * @return {object} the new merged object
+ */
+Y.merge = function() {
+ var a = arguments, o = {}, i, l = a.length;
+ for (i=0; i<l; i=i+1) {
+ Y.mix(o, a[i], true);
+ }
+ return o;
+};
+
+/**
+ * Applies the supplier's properties to the receiver. By default
+ * all prototype and static propertes on the supplier are applied
+ * to the corresponding spot on the receiver. By default all
+ * properties are applied, and a property that is already on the
+ * reciever will not be overwritten. The default behavior can
+ * be modified by supplying the appropriate parameters.
+ *
+ * @TODO add constants for the modes
+ *
+ * @method mix
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param {int} mode what should be copies, and to where
+ * default(0): object to object
+ * 1: prototype to prototype (old augment)
+ * 2: prototype to prototype and object props (new augment)
+ * 3: prototype to object
+ * 4: object to prototype
+ * @param merge {boolean} merge objects instead of overwriting/ignoring
+ * Used by Y.aggregate
+ * @return {object} the augmented object
+ */
+Y.mix = function(r, s, ov, wl, mode, merge) {
+
+ if (!s||!r) {
+ return r || Y;
+ }
+
+ if (mode) {
+ switch (mode) {
+ case 1: // proto to proto
+ return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ case 2: // object to object and proto to proto
+ Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ break; // pass through
+ case 3: // proto to static
+ return Y.mix(r, s.prototype, ov, wl, 0, merge);
+ case 4: // static to proto
+ return Y.mix(r.prototype, s, ov, wl, 0, merge);
+ default: // object to object is what happens below
+ }
+ }
+
+ // Maybe don't even need this wl && wl.length check anymore??
+ var arr = merge && L.isArray(r), i, l, p;
+
+ if (wl && wl.length) {
+ for (i = 0, l = wl.length; i < l; ++i) {
+ p = wl[i];
+ if (p in s) {
+ if (merge && L.isObject(r[p], true)) {
+ Y.mix(r[p], s[p]);
+ } else if (!arr && (ov || !(p in r))) {
+ r[p] = s[p];
+ } else if (arr) {
+ r.push(s[p]);
+ }
+ }
+ }
+ } else {
+ for (i in s) {
+ // if (s.hasOwnProperty(i) && !(i in FROZEN)) {
+ // check white list if it was supplied
+ // if the receiver has this property, it is an object,
+ // and merge is specified, merge the two objects.
+ if (merge && L.isObject(r[i], true)) {
+ Y.mix(r[i], s[i]); // recursive
+ // otherwise apply the property only if overwrite
+ // is specified or the receiver doesn't have one.
+ } else if (!arr && (ov || !(i in r))) {
+ r[i] = s[i];
+ // if merge is specified and the receiver is an array,
+ // append the array item
+ } else if (arr) {
+ r.push(s[i]);
+ }
+ // }
+ }
+
+ if (Y.UA.ie) {
+ _iefix(r, s);
+ }
+ }
+
+ return r;
+};
+
+/**
+ * Returns a wrapper for a function which caches the
+ * return value of that function, keyed off of the combined
+ * argument values.
+ * @function cached
+ * @param source {function} the function to memoize
+ * @param cache an optional cache seed
+ * @param refetch if supplied, this value is tested against the cached
+ * value. If the values are equal, the wrapped function is executed again.
+ * @return {Function} the wrapped function
+ */
+Y.cached = function(source, cache, refetch){
+ cache = cache || {};
+
+ return function(arg1, arg2) {
+
+ var k = (arg2) ? Array.prototype.join.call(arguments, DELIMITER) : arg1,
+ v = cache[k];
+
+ if (!(k in cache) || (refetch && cache[k] == refetch)) {
+ cache[k] = source.apply(source, arguments);
+ }
+
+ return cache[k];
+ };
+
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+
+/**
+ * Adds the following Object utilities to the YUI instance
+ * @class Object
+ */
+
+/**
+ * Y.Object(o) returns a new object based upon the supplied object.
+ * @TODO Use native Object.create() when available
+ * @method ()
+ * @static
+ * @param o the supplier object
+ * @return {Object} the new object
+ */
+Y.Object = function(o) {
+ var F = function() {};
+ F.prototype = o;
+ return new F();
+};
+
+var O = Y.Object,
+
+UNDEFINED = undefined,
+
+/**
+ * Extracts the keys, values, or size from an object
+ *
+ * @method _extract
+ * @param o the object
+ * @param what what to extract (0: keys, 1: values, 2: size)
+ * @return {boolean|Array} the extracted info
+ * @static
+ * @private
+ */
+_extract = function(o, what) {
+ var count = (what === 2), out = (count) ? 0 : [], i;
+
+ for (i in o) {
+ if (count) {
+ out++;
+ } else {
+ if (o.hasOwnProperty(i)) {
+ out.push((what) ? o[i] : i);
+ }
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Returns an array containing the object's keys
+ * @TODO use native Object.keys() if available
+ * @method keys
+ * @static
+ * @param o an object
+ * @return {string[]} the keys
+ */
+O.keys = function(o) {
+ return _extract(o);
+};
+
+/**
+ * Returns an array containing the object's values
+ * @TODO use native Object.values() if available
+ * @method values
+ * @static
+ * @param o an object
+ * @return {Array} the values
+ */
+O.values = function(o) {
+ return _extract(o, 1);
+};
+
+/**
+ * Returns the size of an object
+ * @TODO use native Object.size() if available
+ * @method size
+ * @static
+ * @param o an object
+ * @return {int} the size
+ */
+O.size = function(o) {
+ return _extract(o, 2);
+};
+
+/**
+ * Returns true if the object contains a given key
+ * @method hasKey
+ * @static
+ * @param o an object
+ * @param k the key to query
+ * @return {boolean} true if the object contains the key
+ */
+O.hasKey = function(o, k) {
+ // return (o.hasOwnProperty(k));
+ return (k in o);
+};
+
+/**
+ * Returns true if the object contains a given value
+ * @method hasValue
+ * @static
+ * @param o an object
+ * @param v the value to query
+ * @return {boolean} true if the object contains the value
+ */
+O.hasValue = function(o, v) {
+ return (Y.Array.indexOf(O.values(o), v) > -1);
+};
+
+/**
+ * Determines whether or not the property was added
+ * to the object instance. Returns false if the property is not present
+ * in the object, or was inherited from the prototype.
+ *
+ * @deprecated Safari 1.x support has been removed, so this is simply a
+ * wrapper for the native implementation. Use the native implementation
+ * directly instead.
+ *
+ * @TODO Remove in B1
+ *
+ * @method owns
+ * @static
+ * @param o {any} The object being testing
+ * @param p {string} the property to look for
+ * @return {boolean} true if the object has the property on the instance
+ */
+O.owns = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+/**
+ * Executes a function on each item. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method each
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {YUI} the YUI instance
+ */
+O.each = function (o, f, c, proto) {
+ var s = c || Y, i;
+
+ for (i in o) {
+ if (proto || o.hasOwnProperty(i)) {
+ f.call(s, o[i], i, o);
+ }
+ }
+ return Y;
+};
+
+/*
+ * Executes a function on each item, but halts if the
+ * function returns true. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method some
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {boolean} true if any execution of the function returns true, false otherwise
+ */
+// O.some = function (o, f, c, proto) {
+// var s = c || Y, i;
+//
+// for (i in o) {
+// if (proto || o.hasOwnProperty(i)) {
+// if (f.call(s, o[i], i, o)) {
+// return true;
+// }
+// }
+// }
+// return false;
+// };
+
+/**
+ * Retrieves the sub value at the provided path,
+ * from the value object provided.
+ *
+ * @method getValue
+ * @param o The object from which to extract the property value
+ * @param path {Array} A path array, specifying the object traversal path
+ * from which to obtain the sub value.
+ * @return {Any} The value stored in the path, undefined if not found.
+ * Returns the source object if an empty path is provided.
+ */
+O.getValue = function (o, path) {
+ var p=Y.Array(path), l=p.length, i;
+
+ for (i=0; o !== UNDEFINED && i < l; i=i+1) {
+ o = o[p[i]];
+ }
+
+ return o;
+};
+
+/**
+ * Sets the sub-attribute value at the provided path on the
+ * value object. Returns the modified value object, or
+ * undefined if the path is invalid.
+ *
+ * @method setValue
+ * @param o The object on which to set the sub value.
+ * @param path {Array} A path array, specifying the object traversal path
+ * at which to set the sub value.
+ * @param val {Any} The new value for the sub-attribute.
+ * @return {Object} The modified object, with the new sub value set, or
+ * undefined, if the path was invalid.
+ */
+O.setValue = function(o, path, val) {
+
+ var p=Y.Array(path), leafIdx=p.length-1, i, ref=o;
+
+ if (leafIdx >= 0) {
+ for (i=0; ref !== UNDEFINED && i < leafIdx; i=i+1) {
+ ref = ref[p[i]];
+ }
+
+ if (ref !== UNDEFINED) {
+ ref[p[i]] = val;
+ } else {
+ return UNDEFINED;
+ }
+ }
+
+ return o;
+};
+
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * YUI user agent detection.
+ * Do not fork for a browser if it can be avoided. Use feature detection when
+ * you can. Use the user agent as a last resort. UA stores a version
+ * number for the browser engine, 0 otherwise. This value may or may not map
+ * to the version number of the browser using the engine. The value is
+ * presented as a float so that it can easily be used for boolean evaluation
+ * as well as for looking for a particular range of versions. Because of this,
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9
+ * reports 1.8).
+ * @class UA
+ * @static
+ */
+Y.UA = function() {
+
+ var numberfy = function(s) {
+ var c = 0;
+ return parseFloat(s.replace(/\./g, function() {
+ return (c++ == 1) ? '' : '.';
+ }));
+ },
+
+ nav = navigator,
+
+ o = {
+
+ /**
+ * Internet Explorer version number or 0. Example: 6
+ * @property ie
+ * @type float
+ * @static
+ */
+ ie: 0,
+
+ /**
+ * Opera version number or 0. Example: 9.2
+ * @property opera
+ * @type float
+ * @static
+ */
+ opera: 0,
+
+ /**
+ * Gecko engine revision number. Will evaluate to 1 if Gecko
+ * is detected but the revision could not be found. Other browsers
+ * will be 0. Example: 1.8
+ * <pre>
+ * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7
+ * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+ * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+ * Firefox 3 alpha: 1.9a4 <-- Reports 1.9
+ * </pre>
+ * @property gecko
+ * @type float
+ * @static
+ */
+ gecko: 0,
+
+ /**
+ * AppleWebKit version. KHTML browsers that are not WebKit browsers
+ * will evaluate to 1, other browsers 0. Example: 418.9
+ * <pre>
+ * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
+ * latest available for Mac OSX 10.3.
+ * Safari 2.0.2: 416 <-- hasOwnProperty introduced
+ * Safari 2.0.4: 418 <-- preventDefault fixed
+ * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+ * different versions of webkit
+ * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been
+ * updated, but not updated
+ * to the latest patch.
+ * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native SVG
+ * and many major issues fixed).
+ * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic update
+ * from 2.x via the 10.4.11 OS patch
+ * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event.
+ * yahoo.com user agent hack removed.
+ * </pre>
+ * http://en.wikipedia.org/wiki/Safari_(web_browser)#Version_history
+ * @property webkit
+ * @type float
+ * @static
+ */
+ webkit: 0,
+
+ /**
+ * The mobile property will be set to a string containing any relevant
+ * user agent information when a modern mobile browser is detected.
+ * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+ * devices with the WebKit-based browser, and Opera Mini.
+ * @property mobile
+ * @type string
+ * @static
+ */
+ mobile: null,
+
+ /**
+ * Adobe AIR version number or 0. Only populated if webkit is detected.
+ * Example: 1.0
+ * @property air
+ * @type float
+ */
+ air: 0,
+
+ /**
+ * Google Caja version number or 0.
+ * @property caja
+ * @type float
+ */
+ caja: nav.cajaVersion,
+
+ /**
+ * Set to true if the page appears to be in SSL
+ * @property secure
+ * @type boolean
+ * @static
+ */
+ secure: false,
+
+ /**
+ * The operating system. Currently only detecting windows or macintosh
+ * @property os
+ * @type string
+ * @static
+ */
+ os: null
+
+ },
+
+ ua = nav && nav.userAgent,
+
+ loc = Y.config.win.location,
+
+ href = loc && loc.href,
+
+ m;
+
+ o.secure = href && (href.toLowerCase().indexOf("https") === 0);
+
+ if (ua) {
+
+ if ((/windows|win32/i).test(ua)) {
+ o.os = 'windows';
+ } else if ((/macintosh/i).test(ua)) {
+ o.os = 'macintosh';
+ }
+
+ // Modern KHTML browsers should qualify as Safari X-Grade
+ if ((/KHTML/).test(ua)) {
+ o.webkit=1;
+ }
+ // Modern WebKit browsers are at least X-Grade
+ m=ua.match(/AppleWebKit\/([^\s]*)/);
+ if (m&&m[1]) {
+ o.webkit=numberfy(m[1]);
+
+ // Mobile browser check
+ if (/ Mobile\//.test(ua)) {
+ o.mobile = "Apple"; // iPhone or iPod Touch
+ } else {
+ m=ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
+ if (m) {
+ o.mobile = m[0]; // Nokia N-series, Android, webOS, ex: NokiaN95
+ }
+ }
+
+ m=ua.match(/AdobeAIR\/([^\s]*)/);
+ if (m) {
+ o.air = m[0]; // Adobe AIR 1.0 or better
+ }
+
+ }
+
+ if (!o.webkit) { // not webkit
+ // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+ m=ua.match(/Opera[\s\/]([^\s]*)/);
+ if (m&&m[1]) {
+ o.opera=numberfy(m[1]);
+ m=ua.match(/Opera Mini[^;]*/);
+ if (m) {
+ o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+ }
+ } else { // not opera or webkit
+ m=ua.match(/MSIE\s([^;]*)/);
+ if (m&&m[1]) {
+ o.ie=numberfy(m[1]);
+ } else { // not opera, webkit, or ie
+ m=ua.match(/Gecko\/([^\s]*)/);
+ if (m) {
+ o.gecko=1; // Gecko detected, look for revision
+ m=ua.match(/rv:([^\s\)]*)/);
+ if (m&&m[1]) {
+ o.gecko=numberfy(m[1]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return o;
+}();
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var min = ['yui-base'], core, C = Y.config, mods = YUI.Env.mods,
+ extras, i;
+
+ // apply the minimal required functionality
+ Y.use.apply(Y, min);
+
+ Y.log(Y.id + ' initialized', 'info', 'yui');
+
+ if (C.core) {
+ core = C.core;
+ } else {
+ core = [];
+ extras = ['get', 'loader', 'yui-log', 'yui-later'];
+
+ for (i=0; i<extras.length; i++) {
+ if (mods[extras[i]]) {
+ core.push(extras[i]);
+ }
+ }
+ }
+
+ Y.use.apply(Y, core);
+
+})();
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+(function(){var I={},B=new Date().getTime(),A,E,H=function(){if(window.addEventListener){return function(M,L,K,J){M.addEventListener(L,K,(!!J));};}else{if(window.attachEvent){return function(L,K,J){L.attachEvent("on"+K,J);};}else{return function(){};}}}(),F=function(){if(window.removeEventListener){return function(M,L,K,J){M.removeEventListener(L,K,!!J);};}else{if(window.detachEvent){return function(L,K,J){L.detachEvent("on"+K,J);};}else{return function(){};}}}(),D=function(){YUI.Env.windowLoaded=true;YUI.Env.DOMReady=true;F(window,"load",D);},C={"io.xdrReady":1,"io.xdrResponse":1},G=Array.prototype.slice;if(typeof YUI==="undefined"||!YUI){YUI=function(O,N,M,L,J){var K=this,R=arguments,Q,P=R.length;if(!(K instanceof YUI)){return new YUI(O,N,M,L,J);}else{K._init();for(Q=0;Q<P;Q++){K._config(R[Q]);}K._setup();return K;}};}YUI.prototype={_config:function(N){N=N||{};var O=this.config,L,K,J,M;M=O.modules;for(L in N){if(M&&L=="modules"){J=N[L];for(K in J){if(J.hasOwnProperty(K)){M[K]=J[K];}}}else{if(L=="win"){O[L]=N[L].contentWindow||N[L];O.doc=O[L].document;}else{O[L]=N[L];}}}},_init:function(){var J="3.0.0",K=this;if(J.indexOf("@")>-1){J="test";}K.version=J;K.Env={mods:{},cdn:"http://yui.yahooapis.com/"+J+"/build/",bootstrapped:false,_idx:0,_used:{},_attached:{},_yidx:0,_uidx:0,_loaded:{}};K.Env._loaded[J]={};if(YUI.Env){K.Env._yidx=(++YUI.Env._yidx);K.Env._guidp=("yui_"+J+"-"+K.Env._yidx+"-"+B).replace(/\./g,"_");K.id=K.stamp(K);I[K.id]=K;}K.constructor=YUI;K.config={win:window||{},doc:document,debug:true,useBrowserConsole:true,throwFail:true,bootstrap:true,fetchCSS:true,base:function(){var L,M,O,N;M=document.getElementsByTagName("script");for(O=0;O<M.length;O=O+1){N=M[O].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);L=N&&N[1];if(L){break;}}return L||K.Env.cdn;}(),loaderPath:"loader/loader-min.js"};},_setup:function(J){this.use("yui-base");},applyTo:function(P,O,L){if(!(O in C)){this.log(O+": applyTo not allowed","warn","yui");return null;}var K=I[P],N,J,M;if(K){N=O.split(".");J=K;for(M=0;M<N.length;M=M+1){J=J[N[M]];if(!J){this.log("applyTo not found: "+O,"warn","yui");}}return J.apply(K,L);}return null;},add:function(K,M,J,L){YUI.Env.mods[K]={name:K,fn:M,version:J,details:L||{}};return this;},_attach:function(K,O){var T=YUI.Env.mods,L=this.Env._attached,Q,P=K.length,M,N,R,S,J;for(Q=0;Q<P;Q=Q+1){M=K[Q];N=T[M];if(!L[M]&&N){L[M]=true;R=N.details;S=R.requires;J=R.use;if(S){this._attach(this.Array(S));}if(N.fn){N.fn(this);}if(J){this._attach(this.Array(J));}}}},use:function(){if(this._loading){this._useQueue=this._useQueue||new this.Queue();this._useQueue.add(G.call(arguments,0));return this;}var K=this,U=G.call(arguments,0),Z=YUI.Env.mods,b=K.Env._used,V,O=U[0],M=false,X=U[U.length-1],W=K.config.bootstrap,P,R,N,Q=[],J=[],S=K.config.fetchCSS,T=function(d){if(b[d]){return;}var Y=Z[d],c,e,a;if(Y){b[d]=true;e=Y.details.requires;a=Y.details.use;}else{if(!YUI.Env._loaded[K.version][d]){Q.push(d);}else{b[d]=true;}}if(e){if(K.Lang.isString(e)){T(e);}else{for(c=0;c<e.length;c=c+1){T(e[c]);}}}J.push(d);},L;if(typeof X==="function"){U.pop();}else{X=null;}L=function(Y){Y=Y||{success:true,msg:"not dynamic"};if(X){X(K,Y);}if(K.fire){K.fire("yui:load",K,Y);}K._loading=false;if(K._useQueue&&K._useQueue.size()&&!K._loading){K.use.apply(K,K._useQueue.next());}};if(O==="*"){U=[];for(P in Z){if(Z.hasOwnProperty(P)){U.push(P);}}if(X){U.push(X);}return K.use.apply(K,U);}if(K.Loader){M=true;V=new K.Loader(K.config);V.require(U);V.ignoreRegistered=true;V.allowRollup=false;V.calculate(null,(S)?null:"js");U=V.sorted;}N=U.length;for(R=0;R<N;R=R+1){T(U[R]);}N=Q.length;if(N){Q=K.Object.keys(K.Array.hash(Q));}if(W&&N&&K.Loader){K._loading=true;V=new K.Loader(K.config);V.onSuccess=L;V.onFailure=L;V.onTimeout=L;V.context=K;V.attaching=U;V.require((S)?Q:U);V.insert(null,(S)?null:"js");}else{if(W&&N&&K.Get&&!K.Env.bootstrapped){K._loading=true;U=K.Array(arguments,0,true);K.Get.script(K.config.base+K.config.loaderPath,{onEnd:function(){K._loading=false;K.Env.bootstrapped=true;K._attach(["loader"]);K.use.apply(K,U);}});return K;}else{if(N){}K._attach(J);L();}}return K;},namespace:function(){var J=arguments,N=null,L,K,M;for(L=0;L<J.length;L=L+1){M=(""+J[L]).split(".");N=this;for(K=(M[0]=="YAHOO")?1:0;K<M.length;K=K+1){N[M[K]]=N[M[K]]||{};N=N[M[K]];}}return N;},log:function(){},error:function(K,J){if(this.config.throwFail){throw (J||new Error(K));}else{this.message(K,"error");}return this;},guid:function(J){var K=this.Env._guidp+(++this.Env._uidx);return(J)?(J+K):K;},stamp:function(L,M){if(!L){return L;}var J=(typeof L==="string")?L:L._yuid;if(!J){J=this.guid();if(!M){try{L._yuid=J;}catch(K){J=null;}}}return J;}};A=YUI.prototype;for(E in A){YUI[E]=A[E];}YUI._init();H(window,"load",D);YUI.Env.add=H;YUI.Env.remove=F;})();YUI.add("yui-base",function(B){function A(){this._init();this.add.apply(this,arguments);}A.prototype={_init:function(){this._q=[];},next:function(){return this._q.shift();},add:function(){B.Array.each(B.Array(arguments,0,true),function(C){this._q.push(C);},this);return this;},size:function(){return this._q.length;}};B.Queue=A;(function(){B.Lang=B.Lang||{};var R=B.Lang,G="array",I="boolean",D="date",M="error",S="function",H="number",K="null",F="object",O="regexp",N="string",C=Object.prototype.toString,P="undefined",E={"undefined":P,"number":H,"boolean":I,"string":N,"[object Function]":S,"[object RegExp]":O,"[object Array]":G,"[object Date]":D,"[object Error]":M},J=/^\s+|\s+$/g,Q="";R.isArray=function(L){return R.type(L)===G;};R.isBoolean=function(L){return typeof L===I;};R.isFunction=function(L){return R.type(L)===S;};R.isDate=function(L){return R.type(L)===D;};R.isNull=function(L){return L===null;};R.isNumber=function(L){return typeof L===H&&isFinite(L);};R.isObject=function(T,L){return(T&&(typeof T===F||(!L&&R.isFunction(T))))||false;};R.isString=function(L){return typeof L===N;};R.isUndefined=function(L){return typeof L===P;};R.trim=function(L){try{return L.replace(J,Q);}catch(T){return L;}};R.isValue=function(T){var L=R.type(T);
+switch(L){case H:return isFinite(T);case K:case P:return false;default:return !!(L);}};R.type=function(L){return E[typeof L]||E[C.call(L)]||(L?F:K);};})();(function(){var C=B.Lang,D=Array.prototype,E=function(M,J,L){var I=(L)?2:B.Array.test(M),H,G,F;if(I){try{return D.slice.call(M,J||0);}catch(K){F=[];for(H=0,G=M.length;H<G;H=H+1){F.push(M[H]);}return F;}}else{return[M];}};B.Array=E;E.test=function(H){var F=0;if(C.isObject(H)){if(C.isArray(H)){F=1;}else{try{if("length" in H&&!("tagName" in H)&&!("alert" in H)&&(!B.Lang.isFunction(H.size)||H.size()>1)){F=2;}}catch(G){}}}return F;};E.each=(D.forEach)?function(F,G,H){D.forEach.call(F||[],G,H||B);return B;}:function(G,I,J){var F=(G&&G.length)||0,H;for(H=0;H<F;H=H+1){I.call(J||B,G[H],H,G);}return B;};E.hash=function(H,G){var K={},F=H.length,J=G&&G.length,I;for(I=0;I<F;I=I+1){K[H[I]]=(J&&J>I)?G[I]:true;}return K;};E.indexOf=(D.indexOf)?function(F,G){return D.indexOf.call(F,G);}:function(F,H){for(var G=0;G<F.length;G=G+1){if(F[G]===H){return G;}}return -1;};E.numericSort=function(G,F){return(G-F);};E.some=(D.some)?function(F,G,H){return D.some.call(F,G,H);}:function(G,I,J){var F=G.length,H;for(H=0;H<F;H=H+1){if(I.call(J,G[H],H,G)){return true;}}return false;};})();(function(){var D=B.Lang,C="__",E=function(H,G){var F=G.toString;if(D.isFunction(F)&&F!=Object.prototype.toString){H.toString=F;}};B.merge=function(){var G=arguments,I={},H,F=G.length;for(H=0;H<F;H=H+1){B.mix(I,G[H],true);}return I;};B.mix=function(F,O,H,N,L,M){if(!O||!F){return F||B;}if(L){switch(L){case 1:return B.mix(F.prototype,O.prototype,H,N,0,M);case 2:B.mix(F.prototype,O.prototype,H,N,0,M);break;case 3:return B.mix(F,O.prototype,H,N,0,M);case 4:return B.mix(F.prototype,O,H,N,0,M);default:}}var K=M&&D.isArray(F),J,I,G;if(N&&N.length){for(J=0,I=N.length;J<I;++J){G=N[J];if(G in O){if(M&&D.isObject(F[G],true)){B.mix(F[G],O[G]);}else{if(!K&&(H||!(G in F))){F[G]=O[G];}else{if(K){F.push(O[G]);}}}}}}else{for(J in O){if(M&&D.isObject(F[J],true)){B.mix(F[J],O[J]);}else{if(!K&&(H||!(J in F))){F[J]=O[J];}else{if(K){F.push(O[J]);}}}}if(B.UA.ie){E(F,O);}}return F;};B.cached=function(H,F,G){F=F||{};return function(L,K){var J=(K)?Array.prototype.join.call(arguments,C):L,I=F[J];if(!(J in F)||(G&&F[J]==G)){F[J]=H.apply(H,arguments);}return F[J];};};})();(function(){B.Object=function(H){var G=function(){};G.prototype=H;return new G();};var E=B.Object,D=undefined,C=function(J,I){var H=(I===2),F=(H)?0:[],G;for(G in J){if(H){F++;}else{if(J.hasOwnProperty(G)){F.push((I)?J[G]:G);}}}return F;};E.keys=function(F){return C(F);};E.values=function(F){return C(F,1);};E.size=function(F){return C(F,2);};E.hasKey=function(G,F){return(F in G);};E.hasValue=function(G,F){return(B.Array.indexOf(E.values(G),F)>-1);};E.owns=function(G,F){return(G.hasOwnProperty(F));};E.each=function(J,I,K,H){var G=K||B,F;for(F in J){if(H||J.hasOwnProperty(F)){I.call(G,J[F],F,J);}}return B;};E.getValue=function(J,I){var H=B.Array(I),F=H.length,G;for(G=0;J!==D&&G<F;G=G+1){J=J[H[G]];}return J;};E.setValue=function(L,J,K){var I=B.Array(J),H=I.length-1,F,G=L;if(H>=0){for(F=0;G!==D&&F<H;F=F+1){G=G[I[F]];}if(G!==D){G[I[F]]=K;}else{return D;}}return L;};})();B.UA=function(){var F=function(J){var K=0;return parseFloat(J.replace(/\./g,function(){return(K++==1)?"":".";}));},I=navigator,H={ie:0,opera:0,gecko:0,webkit:0,mobile:null,air:0,caja:I.cajaVersion,secure:false,os:null},E=I&&I.userAgent,G=B.config.win.location,D=G&&G.href,C;H.secure=D&&(D.toLowerCase().indexOf("https")===0);if(E){if((/windows|win32/i).test(E)){H.os="windows";}else{if((/macintosh/i).test(E)){H.os="macintosh";}}if((/KHTML/).test(E)){H.webkit=1;}C=E.match(/AppleWebKit\/([^\s]*)/);if(C&&C[1]){H.webkit=F(C[1]);if(/ Mobile\//.test(E)){H.mobile="Apple";}else{C=E.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);if(C){H.mobile=C[0];}}C=E.match(/AdobeAIR\/([^\s]*)/);if(C){H.air=C[0];}}if(!H.webkit){C=E.match(/Opera[\s\/]([^\s]*)/);if(C&&C[1]){H.opera=F(C[1]);C=E.match(/Opera Mini[^;]*/);if(C){H.mobile=C[0];}}else{C=E.match(/MSIE\s([^;]*)/);if(C&&C[1]){H.ie=F(C[1]);}else{C=E.match(/Gecko\/([^\s]*)/);if(C){H.gecko=1;C=E.match(/rv:([^\s\)]*)/);if(C&&C[1]){H.gecko=F(C[1]);}}}}}}return H;}();(function(){var F=["yui-base"],D,I=B.config,H=YUI.Env.mods,G,E;B.use.apply(B,F);if(I.core){D=I.core;}else{D=[];G=["get","loader","yui-log","yui-later"];for(E=0;E<G.length;E++){if(H[G[E]]){D.push(G[E]);}}}B.use.apply(B,D);})();},"3.0.0");
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var _instances = {},
+ _startTime = new Date().getTime(),
+ p,
+ i,
+
+ add = function () {
+ if (window.addEventListener) {
+ return function(el, type, fn, capture) {
+ el.addEventListener(type, fn, (!!capture));
+ };
+ } else if (window.attachEvent) {
+ return function(el, type, fn) {
+ el.attachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ remove = function() {
+ if (window.removeEventListener) {
+ return function (el, type, fn, capture) {
+ el.removeEventListener(type, fn, !!capture);
+ };
+ } else if (window.detachEvent) {
+ return function (el, type, fn) {
+ el.detachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ globalListener = function() {
+ YUI.Env.windowLoaded = true;
+ YUI.Env.DOMReady = true;
+ remove(window, 'load', globalListener);
+ },
+
+// @TODO: this needs to be created at build time from module metadata
+
+ _APPLY_TO_WHITE_LIST = {
+ 'io.xdrReady': 1,
+ 'io.xdrResponse':1
+ },
+
+ SLICE = Array.prototype.slice;
+
+// reduce to one or the other
+if (typeof YUI === 'undefined' || !YUI) {
+
+ /**
+ * The YUI global namespace object. If YUI is already defined, the
+ * existing YUI object will not be overwritten so that defined
+ * namespaces are preserved.
+ *
+ * @class YUI
+ * @constructor
+ * @global
+ * @uses EventTarget
+ * @param o* Up to five optional configuration objects. This object is stored
+ * in YUI.config. See config for the list of supported properties.
+ */
+
+ /*global YUI*/
+ // Make a function, disallow direct instantiation
+ YUI = function(o1, o2, o3, o4, o5) {
+
+ var Y = this, a = arguments, i, l = a.length;
+
+ // Allow instantiation without the new operator
+ if (!(Y instanceof YUI)) {
+ return new YUI(o1, o2, o3, o4, o5);
+ } else {
+ // set up the core environment
+ Y._init();
+
+ for (i=0; i<l; i++) {
+ Y._config(a[i]);
+ }
+
+ // bind the specified additional modules for this instance
+ Y._setup();
+
+ return Y;
+ }
+ };
+}
+
+// The prototype contains the functions that are required to allow the external
+// modules to be registered and for the instance to be initialized.
+YUI.prototype = {
+
+ _config: function(o) {
+
+ o = o || {};
+
+ var c = this.config, i, j, m, mods;
+
+ mods = c.modules;
+ for (i in o) {
+ if (mods && i == 'modules') {
+ m = o[i];
+ for (j in m) {
+ if (m.hasOwnProperty(j)) {
+ mods[j] = m[j];
+ }
+ }
+ } else if (i == 'win') {
+ c[i] = o[i].contentWindow || o[i];
+ c.doc = c[i].document;
+ } else {
+ c[i] = o[i];
+ }
+ }
+ },
+
+ /**
+ * Initialize this YUI instance
+ * @private
+ */
+ _init: function() {
+
+ // find targeted window/frame
+ // @TODO create facades
+ var v = '3.0.0', Y = this;
+
+ if (v.indexOf('@') > -1) {
+ v = 'test';
+ }
+
+ Y.version = v;
+
+ Y.Env = {
+ // @todo expand the new module metadata
+ mods: {},
+ cdn: 'http://yui.yahooapis.com/' + v + '/build/',
+ bootstrapped: false,
+ _idx: 0,
+ _used: {},
+ _attached: {},
+ _yidx: 0,
+ _uidx: 0,
+ _loaded: {}
+ };
+
+ Y.Env._loaded[v] = {};
+
+ if (YUI.Env) {
+ Y.Env._yidx = (++YUI.Env._yidx);
+ Y.Env._guidp = ('yui_' + v + '-' + Y.Env._yidx + '-' + _startTime).replace(/\./g, '_');
+ Y.id = Y.stamp(Y);
+ _instances[Y.id] = Y;
+ }
+
+ Y.constructor = YUI;
+
+ // configuration defaults
+ Y.config = {
+
+ win: window || {},
+ doc: document,
+ debug: true,
+ useBrowserConsole: true,
+ throwFail: true,
+ bootstrap: true,
+ fetchCSS: true,
+
+ base: function() {
+ var b, nodes, i, match;
+
+ // get from querystring
+ nodes = document.getElementsByTagName('script');
+
+ for (i=0; i<nodes.length; i=i+1) {
+ match = nodes[i].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);
+ b = match && match[1];
+ if (b) {
+ break;
+ }
+ }
+
+ // use CDN default
+ return b || Y.Env.cdn;
+
+ }(),
+
+ loaderPath: 'loader/loader-min.js'
+ };
+
+ },
+
+ /**
+ * Finishes the instance setup. Attaches whatever modules were defined
+ * when the yui modules was registered.
+ * @method _setup
+ * @private
+ */
+ _setup: function(o) {
+ this.use("yui-base");
+ },
+
+ /**
+ * Executes a method on a YUI instance with
+ * the specified id if the specified method is whitelisted.
+ * @method applyTo
+ * @param id {string} the YUI instance id
+ * @param method {string} the name of the method to exectute.
+ * Ex: 'Object.keys'
+ * @param args {Array} the arguments to apply to the method
+ * @return {object} the return value from the applied method or null
+ */
+ applyTo: function(id, method, args) {
+
+ if (!(method in _APPLY_TO_WHITE_LIST)) {
+ this.log(method + ': applyTo not allowed', 'warn', 'yui');
+ return null;
+ }
+
+ var instance = _instances[id], nest, m, i;
+
+ if (instance) {
+
+ nest = method.split('.');
+ m = instance;
+
+ for (i=0; i<nest.length; i=i+1) {
+
+ m = m[nest[i]];
+
+ if (!m) {
+ this.log('applyTo not found: ' + method, 'warn', 'yui');
+ }
+ }
+
+ return m.apply(instance, args);
+ }
+
+ return null;
+ },
+
+ /**
+ * Register a module
+ * @method add
+ * @param name {string} module name
+ * @param fn {Function} entry point into the module that
+ * is used to bind module to the YUI instance
+ * @param version {string} version string
+ * @param details optional config data:
+ * requires - features that should be present before loading
+ * optional - optional features that should be present if load optional defined
+ * use - features that should be attached automatically
+ * skinnable -
+ * rollup
+ * omit - features that should not be loaded if this module is present
+ * @return {YUI} the YUI instance
+ *
+ */
+ add: function(name, fn, version, details) {
+ // this.log('Adding a new component ' + name);
+ // @todo expand this to include version mapping
+ // @todo may want to restore the build property
+ // @todo fire moduleAvailable event
+
+ YUI.Env.mods[name] = {
+ name: name,
+ fn: fn,
+ version: version,
+ details: details || {}
+ };
+
+ return this; // chain support
+ },
+
+ _attach: function(r, fromLoader) {
+
+ var mods = YUI.Env.mods,
+ attached = this.Env._attached,
+ i, l = r.length, name, m, d, req, use;
+
+ for (i=0; i<l; i=i+1) {
+
+ name = r[i];
+ m = mods[name];
+
+ if (!attached[name] && m) {
+
+ attached[name] = true;
+
+ d = m.details;
+ req = d.requires;
+ use = d.use;
+
+ if (req) {
+ this._attach(this.Array(req));
+ }
+
+ // this.log('attaching ' + name, 'info', 'yui');
+
+ if (m.fn) {
+ m.fn(this);
+ }
+
+ if (use) {
+ this._attach(this.Array(use));
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Bind a module to a YUI instance
+ * @param modules* {string} 1-n modules to bind (uses arguments array)
+ * @param *callback {function} callback function executed when
+ * the instance has the required functionality. If included, it
+ * must be the last parameter.
+ *
+ * @TODO
+ * Implement versioning? loader can load different versions?
+ * Should sub-modules/plugins be normal modules, or do
+ * we add syntax for specifying these?
+ *
+ * YUI().use('dragdrop')
+ * YUI().use('dragdrop:2.4.0'); // specific version
+ * YUI().use('dragdrop:2.4.0-'); // at least this version
+ * YUI().use('dragdrop:2.4.0-2.9999.9999'); // version range
+ * YUI().use('*'); // use all available modules
+ * YUI().use('lang+dump+substitute'); // use lang and some plugins
+ * YUI().use('lang+*'); // use lang and all known plugins
+ *
+ *
+ * @return {YUI} the YUI instance
+ */
+ use: function() {
+
+ if (this._loading) {
+ this._useQueue = this._useQueue || new this.Queue();
+ this._useQueue.add(SLICE.call(arguments, 0));
+ return this;
+ }
+
+ var Y = this,
+ a=SLICE.call(arguments, 0),
+ mods = YUI.Env.mods,
+ used = Y.Env._used,
+ loader,
+ firstArg = a[0],
+ dynamic = false,
+ callback = a[a.length-1],
+ boot = Y.config.bootstrap,
+ k, i, l, missing = [],
+ r = [],
+ css = Y.config.fetchCSS,
+ f = function(name) {
+
+ // only attach a module once
+ if (used[name]) {
+ return;
+ }
+
+ var m = mods[name], j, req, use;
+
+ if (m) {
+
+
+ used[name] = true;
+
+ req = m.details.requires;
+ use = m.details.use;
+ } else {
+
+ // CSS files don't register themselves, see if it has been loaded
+ if (!YUI.Env._loaded[Y.version][name]) {
+ missing.push(name);
+ } else {
+ // probably css
+ used[name] = true;
+ }
+ }
+
+ // make sure requirements are attached
+ if (req) {
+ if (Y.Lang.isString(req)) {
+ f(req);
+ } else {
+ for (j = 0; j < req.length; j = j + 1) {
+ f(req[j]);
+ }
+ }
+ }
+
+ // add this module to full list of things to attach
+ r.push(name);
+
+ },
+
+ onComplete;
+
+
+ // The last argument supplied to use can be a load complete callback
+ if (typeof callback === 'function') {
+ a.pop();
+ } else {
+ callback = null;
+ }
+
+ onComplete = function(fromLoader) {
+
+
+ fromLoader = fromLoader || {
+ success: true,
+ msg: 'not dynamic'
+ };
+
+ if (callback) {
+ callback(Y, fromLoader);
+ }
+
+ if (Y.fire) {
+ Y.fire('yui:load', Y, fromLoader);
+ }
+
+ // process queued use requests as long until done
+ // or dynamic load happens again.
+ Y._loading = false;
+
+ if (Y._useQueue && Y._useQueue.size() && !Y._loading) {
+ Y.use.apply(Y, Y._useQueue.next());
+ }
+ };
+
+
+ // YUI().use('*'); // bind everything available
+ if (firstArg === "*") {
+ a = [];
+ for (k in mods) {
+ if (mods.hasOwnProperty(k)) {
+ a.push(k);
+ }
+ }
+
+ if (callback) {
+ a.push(callback);
+ }
+
+ return Y.use.apply(Y, a);
+ }
+
+
+ // use loader to expand dependencies and sort the
+ // requirements if it is available.
+ if (Y.Loader) {
+ dynamic = true;
+ loader = new Y.Loader(Y.config);
+ loader.require(a);
+ loader.ignoreRegistered = true;
+ loader.allowRollup = false;
+ // loader.calculate(null, (css && css == 'force') ? null : 'js');
+ // loader.calculate();
+ loader.calculate(null, (css) ? null : 'js');
+ a = loader.sorted;
+ }
+
+
+ l = a.length;
+
+ // process each requirement and any additional requirements
+ // the module metadata specifies
+ for (i=0; i<l; i=i+1) {
+ f(a[i]);
+ }
+
+ l = missing.length;
+
+
+ if (l) {
+ missing = Y.Object.keys(Y.Array.hash(missing));
+ }
+
+ // dynamic load
+ if (boot && l && Y.Loader) {
+ Y._loading = true;
+ loader = new Y.Loader(Y.config);
+ loader.onSuccess = onComplete;
+ loader.onFailure = onComplete;
+ loader.onTimeout = onComplete;
+ loader.context = Y;
+ loader.attaching = a;
+ // loader.require(missing);
+ loader.require((css) ? missing : a);
+ loader.insert(null, (css) ? null : 'js');
+ } else if (boot && l && Y.Get && !Y.Env.bootstrapped) {
+ Y._loading = true;
+
+ a = Y.Array(arguments, 0, true);
+ // a.unshift('loader');
+
+ Y.Get.script(Y.config.base + Y.config.loaderPath, {
+ onEnd: function() {
+ Y._loading = false;
+ Y.Env.bootstrapped = true;
+ Y._attach(['loader']);
+ Y.use.apply(Y, a);
+ }
+ });
+
+ return Y;
+
+ } else {
+ if (l) {
+ }
+ Y._attach(r);
+ onComplete();
+ }
+
+ return Y; // chain support var yui = YUI().use('dragdrop');
+ },
+
+
+ /**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YUI.namespace("property.package");
+ * YUI.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YUI.property, then
+ * YUI.property.package (YAHOO is scrubbed out, this is
+ * to remain compatible with YUI2)
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YUI.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @param {string*} arguments 1-n namespaces to create
+ * @return {object} A reference to the last namespace object created
+ */
+ namespace: function() {
+ var a=arguments, o=null, i, j, d;
+ for (i=0; i<a.length; i=i+1) {
+ d = ("" + a[i]).split(".");
+ o = this;
+ for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+ o[d[j]] = o[d[j]] || {};
+ o = o[d[j]];
+ }
+ }
+ return o;
+ },
+
+ // this is replaced if the log module is included
+ log: function() {
+
+ },
+
+ /**
+ * Report an error. The reporting mechanism is controled by
+ * the 'throwFail' configuration attribute. If throwFail is
+ * not specified, the message is written to the Logger, otherwise
+ * a JS error is thrown
+ * @method error
+ * @param msg {string} the error message
+ * @param e {Error} Optional JS error that was caught. If supplied
+ * and throwFail is specified, this error will be re-thrown.
+ * @return {YUI} this YUI instance
+ */
+ error: function(msg, e) {
+ if (this.config.throwFail) {
+ throw (e || new Error(msg));
+ } else {
+ this.message(msg, "error"); // don't scrub this one
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate an id that is unique among all YUI instances
+ * @method guid
+ * @param pre {string} optional guid prefix
+ * @return {string} the guid
+ */
+ guid: function(pre) {
+ var id = this.Env._guidp + (++this.Env._uidx);
+ return (pre) ? (pre + id) : id;
+ },
+
+ /**
+ * Returns a guid associated with an object. If the object
+ * does not have one, a new one is created unless readOnly
+ * is specified.
+ * @method stamp
+ * @param o The object to stamp
+ * @param readOnly {boolean} if true, a valid guid will only
+ * be returned if the object has one assigned to it.
+ * @return {string} The object's guid or null
+ */
+ stamp: function(o, readOnly) {
+
+ if (!o) {
+ return o;
+ }
+
+ var uid = (typeof o === 'string') ? o : o._yuid;
+
+ if (!uid) {
+ uid = this.guid();
+ if (!readOnly) {
+ try {
+ o._yuid = uid;
+ } catch(e) {
+ uid = null;
+ }
+ }
+ }
+
+ return uid;
+ }
+};
+
+// Give the YUI global the same properties as an instance.
+// This makes it so that the YUI global can be used like the YAHOO
+// global was used prior to 3.x. More importantly, the YUI global
+// provides global metadata, so env needs to be configured.
+// @TODO review
+
+ p = YUI.prototype;
+
+ // inheritance utilities are not available yet
+ for (i in p) {
+ // if (1) { // intenionally ignoring hasOwnProperty check
+ YUI[i] = p[i];
+ // }
+ }
+
+ // set up the environment
+ YUI._init();
+
+ // add a window load event at load time so we can capture
+ // the case where it fires before dynamic loading is
+ // complete.
+ add(window, 'load', globalListener);
+
+ YUI.Env.add = add;
+ YUI.Env.remove = remove;
+
+ /*
+ * Subscribe to an event. The signature differs depending on the
+ * type of event you are attaching to.
+ * @method on
+ * @param type {string|function|object} The type of the event. If
+ * this is a function, this is dispatched to the aop system. If an
+ * object, it is parsed for multiple subsription definitions
+ * @param fn {Function} The callback
+ * @param elspec {any} DOM element(s), selector string(s), and or
+ * Node ref(s) to attach DOM related events to (only applies to
+ * DOM events).
+ * @param
+ * @return the event target or a detach handle per 'chain' config
+ */
+
+})();
+
+/**
+ * The config object contains all of the configuration options for
+ * the YUI instance. This object is supplied by the implementer
+ * when instantiating a YUI instance. Some properties have default
+ * values if they are not supplied by the implementer.
+ *
+ * @class config
+ * @static
+ */
+
+/**
+ * Allows the YUI seed file to fetch the loader component and library
+ * metadata to dynamically load additional dependencies.
+ *
+ * @property bootstrap
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * Log to the browser console if debug is on and the browser has a
+ * supported console.
+ *
+ * @property useBrowserConsole
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * A hash of log sources that should be logged. If specified, only log messages from these sources will be logged.
+ *
+ * @property logInclude
+ * @type object
+ */
+
+/**
+ * A hash of log sources that should be not be logged. If specified, all sources are logged if not on this list.
+ *
+ * @property logExclude
+ * @type object
+ */
+
+/**
+ * Set to true if the yui seed file was dynamically loaded in
+ * order to bootstrap components relying on the window load event
+ * and the 'domready' custom event.
+ *
+ * @property injected
+ * @type object
+ */
+
+/**
+ * If throwFail is set, Y.fail will generate or re-throw a JS Error. Otherwise the failure is logged.
+ *
+ * @property throwFail
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * The window/frame that this instance should operate in.
+ *
+ * @property win
+ * @type Window
+ * @default the window hosting YUI
+ */
+
+/**
+ * The document associated with the 'win' configuration.
+ *
+ * @property doc
+ * @type Document
+ * @default the document hosting YUI
+ */
+
+/**
+ * A list of modules that defines the YUI core (overrides the default).
+ *
+ * @property core
+ * @type string[]
+ */
+
+/**
+ * The default date format
+ *
+ * @property dateFormat
+ * @type string
+ */
+
+/**
+ * The default locale
+ *
+ * @property locale
+ * @type string
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property pollInterval
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The number of dynamic nodes to insert by default before
+ * automatically removing them. This applies to script nodes
+ * because remove the node will not make the evaluated script
+ * unavailable. Dynamic CSS is not auto purged, because removing
+ * a linked style sheet will also remove the style definitions.
+ *
+ * @property purgethreshold
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property windowResizeDelay
+ * @type int
+ * @default 40
+ */
+
+/**
+ * Base directory for dynamic loading
+ *
+ * @property base
+ * @type string
+ */
+
+/**
+ * The secure base dir (not implemented)
+ *
+ * For dynamic loading.
+ *
+ * @property secureBase
+ * @type string
+ */
+
+/**
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?
+ *
+ * For dynamic loading.
+ *
+ * @property comboBase
+ * @type string
+ */
+
+/**
+ * The root path to prepend to module names for the combo service. Ex: 3.0.0b1/build/
+ *
+ * For dynamic loading.
+ *
+ * @property root
+ * @type string
+ */
+
+/**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * For dynamic loading.
+ *
+ * @property filter
+ * @type string|object
+ */
+
+/**
+ * Hash of per-component filter specification. If specified for a given component,
+ * this overrides the filter config
+ *
+ * For dynamic loading.
+ *
+ * @property filters
+ * @type object
+ */
+
+/**
+ * Use the YUI combo service to reduce the number of http connections
+ * required to load your dependencies.
+ *
+ * For dynamic loading.
+ *
+ * @property combine
+ * @type boolean
+ * @default true if 'base' is not supplied, false if it is.
+ */
+
+/**
+ * A list of modules that should never be dynamically loaded
+ *
+ * @property ignore
+ * @type string[]
+ */
+
+/**
+ * A list of modules that should always be loaded when required, even if already
+ * present on the page.
+ *
+ * @property force
+ * @type string[]
+ */
+
+/**
+ * Node or id for a node that should be used as the insertion point for new nodes
+ * For dynamic loading.
+ *
+ * @property insertBefore
+ * @type string
+ */
+
+/**
+ * charset for dynamic nodes
+ *
+ * @property charset
+ * @type string
+ * @deprecated use jsAttributes cssAttributes
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded script nodes.
+ *
+ * @property jsAttributes
+ * @type string
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded link nodes.
+ *
+ * @property cssAttributes
+ * @type string
+ */
+
+/**
+ * Number of milliseconds before a timeout occurs when dynamically
+ * loading nodes. If not set, there is no timeout.
+ *
+ * @property timeout
+ * @type int
+ */
+
+/**
+ * Callback for the 'CSSComplete' event. When dynamically loading YUI
+ * components with CSS, this property fires when the CSS is finished
+ * loading but script loading is still ongoing. This provides an
+ * opportunity to enhance the presentation of a loading page a little
+ * bit before the entire loading process is done.
+ *
+ * @property onCSS
+ * @type function
+ */
+
+/**
+ * A list of module definitions to add to the list of YUI components.
+ * These components can then be dynamically loaded side by side with
+ * YUI via the use() method.See Loader.addModule for the supported
+ * module metadata.
+ *
+ * @property modules
+ * @type function
+ */
+
+/**
+ * The loader 'path' attribute to the loader itself. This is combined
+ * with the 'base' attribute to dynamically load the loader component
+ * when boostrapping with the get utility alone.
+ *
+ * @property loaderPath
+ * @default loader/loader-min.js
+ */
+
+/**
+ *
+ * Specifies whether or not YUI().use(...) will attempt to load CSS
+ * resources at all. Any truthy value will cause CSS dependencies
+ * to load when fetching script. The special value 'force' will
+ * cause CSS dependencies to be loaded even if no script is needed.
+ *
+ * @property fetchCSS
+ * @default true
+ */
+YUI.add('yui-base', function(Y) {
+
+/*
+ * YUI stub
+ * @module yui
+ * @submodule yui-base
+ */
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * A simple FIFO queue. Items are added to the Queue with add(1..n items) and
+ * removed using next().
+ *
+ * @class Queue
+ * @param item* {MIXED} 0..n items to seed the queue
+ */
+function Queue() {
+ this._init();
+ this.add.apply(this, arguments);
+}
+
+Queue.prototype = {
+ /**
+ * Initialize the queue
+ *
+ * @method _init
+ * @protected
+ */
+ _init : function () {
+ /**
+ * The collection of enqueued items
+ *
+ * @property _q
+ * @type {Array}
+ * @protected
+ */
+ this._q = [];
+ },
+
+ /**
+ * Get the next item in the queue.
+ *
+ * @method next
+ * @return {MIXED} the next item in the queue
+ */
+ next : function () {
+ return this._q.shift();
+ },
+
+ /**
+ * Add 0..n items to the end of the queue
+ *
+ * @method add
+ * @param item* {MIXED} 0..n items
+ */
+ add : function () {
+ Y.Array.each(Y.Array(arguments,0,true),function (fn) {
+ this._q.push(fn);
+ },this);
+
+ return this;
+ },
+
+ /**
+ * Returns the current number of queued items
+ *
+ * @method size
+ * @return {Number}
+ */
+ size : function () {
+ return this._q.length;
+ }
+};
+
+Y.Queue = Queue;
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class Lang
+ * @static
+ */
+Y.Lang = Y.Lang || {};
+
+var L = Y.Lang,
+
+ARRAY = 'array',
+BOOLEAN = 'boolean',
+DATE = 'date',
+ERROR = 'error',
+FUNCTION = 'function',
+NUMBER = 'number',
+NULL = 'null',
+OBJECT = 'object',
+REGEX = 'regexp',
+STRING = 'string',
+TOSTRING = Object.prototype.toString,
+UNDEFINED = 'undefined',
+
+TYPES = {
+ 'undefined' : UNDEFINED,
+ 'number' : NUMBER,
+ 'boolean' : BOOLEAN,
+ 'string' : STRING,
+ '[object Function]' : FUNCTION,
+ '[object RegExp]' : REGEX,
+ '[object Array]' : ARRAY,
+ '[object Date]' : DATE,
+ '[object Error]' : ERROR
+},
+
+TRIMREGEX = /^\s+|\s+$/g,
+EMPTYSTRING = '';
+
+/**
+ * Determines whether or not the provided item is an array.
+ * Returns false for array-like collections such as the
+ * function arguments collection or HTMLElement collection
+ * will return false. You can use @see Array.test if you
+ * want to
+ * @method isArray
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is an array
+ */
+L.isArray = function(o) {
+ return L.type(o) === ARRAY;
+};
+
+/**
+ * Determines whether or not the provided item is a boolean
+ * @method isBoolean
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a boolean
+ */
+L.isBoolean = function(o) {
+ return typeof o === BOOLEAN;
+};
+
+/**
+ * Determines whether or not the provided item is a function
+ * Note: Internet Explorer thinks certain functions are objects:
+ *
+ * var obj = document.createElement("object");
+ * Y.Lang.isFunction(obj.getAttribute) // reports false in IE
+ *
+ * var input = document.createElement("input"); // append to body
+ * Y.Lang.isFunction(input.focus) // reports false in IE
+ *
+ * You will have to implement additional tests if these functions
+ * matter to you.
+ *
+ * @method isFunction
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a function
+ */
+L.isFunction = function(o) {
+ return L.type(o) === FUNCTION;
+};
+
+/**
+ * Determines whether or not the supplied item is a date instance
+ * @method isDate
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a date
+ */
+L.isDate = function(o) {
+ // return o instanceof Date;
+ return L.type(o) === DATE;
+};
+
+/**
+ * Determines whether or not the provided item is null
+ * @method isNull
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is null
+ */
+L.isNull = function(o) {
+ return o === null;
+};
+
+/**
+ * Determines whether or not the provided item is a legal number
+ * @method isNumber
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a number
+ */
+L.isNumber = function(o) {
+ return typeof o === NUMBER && isFinite(o);
+};
+
+/**
+ * Determines whether or not the provided item is of type object
+ * or function
+ * @method isObject
+ * @static
+ * @param o The object to test
+ * @param failfn {boolean} fail if the input is a function
+ * @return {boolean} true if o is an object
+ */
+L.isObject = function(o, failfn) {
+return (o && (typeof o === OBJECT || (!failfn && L.isFunction(o)))) || false;
+};
+
+/**
+ * Determines whether or not the provided item is a string
+ * @method isString
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a string
+ */
+L.isString = function(o) {
+ return typeof o === STRING;
+};
+
+/**
+ * Determines whether or not the provided item is undefined
+ * @method isUndefined
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is undefined
+ */
+L.isUndefined = function(o) {
+ return typeof o === UNDEFINED;
+};
+
+/**
+ * Returns a string without any leading or trailing whitespace. If
+ * the input is not a string, the input will be returned untouched.
+ * @method trim
+ * @static
+ * @param s {string} the string to trim
+ * @return {string} the trimmed string
+ */
+L.trim = function(s){
+ try {
+ return s.replace(TRIMREGEX, EMPTYSTRING);
+ } catch(e) {
+ return s;
+ }
+};
+
+/**
+ * A convenience method for detecting a legitimate non-null value.
+ * Returns false for null/undefined/NaN, true for other values,
+ * including 0/false/''
+ * @method isValue
+ * @static
+ * @param o The item to test
+ * @return {boolean} true if it is not null/undefined/NaN || false
+ */
+L.isValue = function(o) {
+ var t = L.type(o);
+ switch (t) {
+ case NUMBER:
+ return isFinite(o);
+ case NULL:
+ case UNDEFINED:
+ return false;
+ default:
+ return !!(t);
+ }
+};
+
+/**
+ * Returns a string representing the type of the item passed in.
+ * @method type
+ * @param o the item to test
+ * @return {string} the detected type
+ */
+L.type = function (o) {
+ return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? OBJECT : NULL);
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang, Native = Array.prototype,
+
+/**
+ * Adds the following array utilities to the YUI instance. Additional
+ * array helpers can be found in the collection component.
+ * @class Array
+ */
+
+/**
+ * Y.Array(o) returns an array:
+ * - Arrays are return unmodified unless the start position is specified.
+ * - "Array-like" collections (@see Array.test) are converted to arrays
+ * - For everything else, a new array is created with the input as the sole item
+ * - The start position is used if the input is or is like an array to return
+ * a subset of the collection.
+ *
+ * @TODO this will not automatically convert elements that are also collections
+ * such as forms and selects. Passing true as the third param will
+ * force a conversion.
+ *
+ * @method ()
+ * @static
+ * @param o the item to arrayify
+ * @param i {int} if an array or array-like, this is the start index
+ * @param al {boolean} if true, it forces the array-like fork. This
+ * can be used to avoid multiple array.test calls.
+ * @return {Array} the resulting array
+ */
+YArray = function(o, startIdx, al) {
+ var t = (al) ? 2 : Y.Array.test(o), i, l, a;
+
+ // switch (t) {
+ // case 1:
+ // // return (startIdx) ? o.slice(startIdx) : o;
+ // case 2:
+ // return Native.slice.call(o, startIdx || 0);
+ // default:
+ // return [o];
+ // }
+
+ if (t) {
+ try {
+ return Native.slice.call(o, startIdx || 0);
+ // IE errors when trying to slice element collections
+ } catch(e) {
+ a=[];
+ for (i=0, l=o.length; i<l; i=i+1) {
+ a.push(o[i]);
+ }
+ return a;
+ }
+ } else {
+ return [o];
+ }
+
+};
+
+Y.Array = YArray;
+
+/**
+ * Evaluates the input to determine if it is an array, array-like, or
+ * something else. This is used to handle the arguments collection
+ * available within functions, and HTMLElement collections
+ *
+ * @method test
+ * @static
+ *
+ * @todo current implementation (intenionally) will not implicitly
+ * handle html elements that are array-like (forms, selects, etc).
+ *
+ * @return {int} a number indicating the results:
+ * 0: Not an array or an array-like collection
+ * 1: A real array.
+ * 2: array-like collection.
+ */
+YArray.test = function(o) {
+ var r = 0;
+ if (L.isObject(o)) {
+ if (L.isArray(o)) {
+ r = 1;
+ } else {
+ try {
+ // indexed, but no tagName (element) or alert (window)
+ if ("length" in o && !("tagName" in o) && !("alert" in o) &&
+ (!Y.Lang.isFunction(o.size) || o.size() > 1)) {
+ r = 2;
+ }
+
+ } catch(e) {}
+ }
+ }
+ return r;
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * @method each
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The
+ * function receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {YUI} the YUI instance
+ */
+YArray.each = (Native.forEach) ?
+ function (a, f, o) {
+ Native.forEach.call(a || [], f, o || Y);
+ return Y;
+ } :
+ function (a, f, o) {
+ var l = (a && a.length) || 0, i;
+ for (i = 0; i < l; i=i+1) {
+ f.call(o || Y, a[i], i, a);
+ }
+ return Y;
+ };
+
+/**
+ * Returns an object using the first array as keys, and
+ * the second as values. If the second array is not
+ * provided the value is set to true for each.
+ * @method hash
+ * @static
+ * @param k {Array} keyset
+ * @param v {Array} optional valueset
+ * @return {object} the hash
+ */
+YArray.hash = function(k, v) {
+ var o = {}, l = k.length, vl = v && v.length, i;
+ for (i=0; i<l; i=i+1) {
+ o[k[i]] = (vl && vl > i) ? v[i] : true;
+ }
+
+ return o;
+};
+
+/**
+ * Returns the index of the first item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * @method indexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of the item that contains the value or -1
+ */
+YArray.indexOf = (Native.indexOf) ?
+ function(a, val) {
+ return Native.indexOf.call(a, val);
+ } :
+ function(a, val) {
+ for (var i=0; i<a.length; i=i+1) {
+ if (a[i] === val) {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+/**
+ * Numeric sort convenience function.
+ * Y.ArrayAssert.itemsAreEqual([1, 2, 3], [3, 1, 2].sort(Y.Array.numericSort));
+ * @method numericSort
+ */
+YArray.numericSort = function(a, b) {
+ return (a - b);
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * Returning true from the processing function will stop the
+ * processing of the remaining
+ * items.
+ * @method some
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {boolean} true if the function returns true on
+ * any of the items in the array
+ */
+ YArray.some = (Native.some) ?
+ function (a, f, o) {
+ return Native.some.call(a, f, o);
+ } :
+ function (a, f, o) {
+ var l = a.length, i;
+ for (i=0; i<l; i=i+1) {
+ if (f.call(o, a[i], i, a)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+})();
+
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang,
+DELIMITER = '__',
+// FROZEN = {
+// 'prototype': 1,
+// '_yuid': 1
+// },
+
+/*
+ * IE will not enumerate native functions in a derived object even if the
+ * function was overridden. This is a workaround for specific functions
+ * we care about on the Object prototype.
+ * @property _iefix
+ * @for YUI
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @private
+ */
+_iefix = function(r, s) {
+ var fn = s.toString;
+ if (L.isFunction(fn) && fn != Object.prototype.toString) {
+ r.toString = fn;
+ }
+};
+
+
+/**
+ * Returns a new object containing all of the properties of
+ * all the supplied objects. The properties from later objects
+ * will overwrite those in earlier objects. Passing in a
+ * single object will create a shallow copy of it. For a deep
+ * copy, use clone.
+ * @method merge
+ * @for YUI
+ * @param arguments {Object*} the objects to merge
+ * @return {object} the new merged object
+ */
+Y.merge = function() {
+ var a = arguments, o = {}, i, l = a.length;
+ for (i=0; i<l; i=i+1) {
+ Y.mix(o, a[i], true);
+ }
+ return o;
+};
+
+/**
+ * Applies the supplier's properties to the receiver. By default
+ * all prototype and static propertes on the supplier are applied
+ * to the corresponding spot on the receiver. By default all
+ * properties are applied, and a property that is already on the
+ * reciever will not be overwritten. The default behavior can
+ * be modified by supplying the appropriate parameters.
+ *
+ * @TODO add constants for the modes
+ *
+ * @method mix
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param {int} mode what should be copies, and to where
+ * default(0): object to object
+ * 1: prototype to prototype (old augment)
+ * 2: prototype to prototype and object props (new augment)
+ * 3: prototype to object
+ * 4: object to prototype
+ * @param merge {boolean} merge objects instead of overwriting/ignoring
+ * Used by Y.aggregate
+ * @return {object} the augmented object
+ */
+Y.mix = function(r, s, ov, wl, mode, merge) {
+
+ if (!s||!r) {
+ return r || Y;
+ }
+
+ if (mode) {
+ switch (mode) {
+ case 1: // proto to proto
+ return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ case 2: // object to object and proto to proto
+ Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ break; // pass through
+ case 3: // proto to static
+ return Y.mix(r, s.prototype, ov, wl, 0, merge);
+ case 4: // static to proto
+ return Y.mix(r.prototype, s, ov, wl, 0, merge);
+ default: // object to object is what happens below
+ }
+ }
+
+ // Maybe don't even need this wl && wl.length check anymore??
+ var arr = merge && L.isArray(r), i, l, p;
+
+ if (wl && wl.length) {
+ for (i = 0, l = wl.length; i < l; ++i) {
+ p = wl[i];
+ if (p in s) {
+ if (merge && L.isObject(r[p], true)) {
+ Y.mix(r[p], s[p]);
+ } else if (!arr && (ov || !(p in r))) {
+ r[p] = s[p];
+ } else if (arr) {
+ r.push(s[p]);
+ }
+ }
+ }
+ } else {
+ for (i in s) {
+ // if (s.hasOwnProperty(i) && !(i in FROZEN)) {
+ // check white list if it was supplied
+ // if the receiver has this property, it is an object,
+ // and merge is specified, merge the two objects.
+ if (merge && L.isObject(r[i], true)) {
+ Y.mix(r[i], s[i]); // recursive
+ // otherwise apply the property only if overwrite
+ // is specified or the receiver doesn't have one.
+ } else if (!arr && (ov || !(i in r))) {
+ r[i] = s[i];
+ // if merge is specified and the receiver is an array,
+ // append the array item
+ } else if (arr) {
+ r.push(s[i]);
+ }
+ // }
+ }
+
+ if (Y.UA.ie) {
+ _iefix(r, s);
+ }
+ }
+
+ return r;
+};
+
+/**
+ * Returns a wrapper for a function which caches the
+ * return value of that function, keyed off of the combined
+ * argument values.
+ * @function cached
+ * @param source {function} the function to memoize
+ * @param cache an optional cache seed
+ * @param refetch if supplied, this value is tested against the cached
+ * value. If the values are equal, the wrapped function is executed again.
+ * @return {Function} the wrapped function
+ */
+Y.cached = function(source, cache, refetch){
+ cache = cache || {};
+
+ return function(arg1, arg2) {
+
+ var k = (arg2) ? Array.prototype.join.call(arguments, DELIMITER) : arg1,
+ v = cache[k];
+
+ if (!(k in cache) || (refetch && cache[k] == refetch)) {
+ cache[k] = source.apply(source, arguments);
+ }
+
+ return cache[k];
+ };
+
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+
+/**
+ * Adds the following Object utilities to the YUI instance
+ * @class Object
+ */
+
+/**
+ * Y.Object(o) returns a new object based upon the supplied object.
+ * @TODO Use native Object.create() when available
+ * @method ()
+ * @static
+ * @param o the supplier object
+ * @return {Object} the new object
+ */
+Y.Object = function(o) {
+ var F = function() {};
+ F.prototype = o;
+ return new F();
+};
+
+var O = Y.Object,
+
+UNDEFINED = undefined,
+
+/**
+ * Extracts the keys, values, or size from an object
+ *
+ * @method _extract
+ * @param o the object
+ * @param what what to extract (0: keys, 1: values, 2: size)
+ * @return {boolean|Array} the extracted info
+ * @static
+ * @private
+ */
+_extract = function(o, what) {
+ var count = (what === 2), out = (count) ? 0 : [], i;
+
+ for (i in o) {
+ if (count) {
+ out++;
+ } else {
+ if (o.hasOwnProperty(i)) {
+ out.push((what) ? o[i] : i);
+ }
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Returns an array containing the object's keys
+ * @TODO use native Object.keys() if available
+ * @method keys
+ * @static
+ * @param o an object
+ * @return {string[]} the keys
+ */
+O.keys = function(o) {
+ return _extract(o);
+};
+
+/**
+ * Returns an array containing the object's values
+ * @TODO use native Object.values() if available
+ * @method values
+ * @static
+ * @param o an object
+ * @return {Array} the values
+ */
+O.values = function(o) {
+ return _extract(o, 1);
+};
+
+/**
+ * Returns the size of an object
+ * @TODO use native Object.size() if available
+ * @method size
+ * @static
+ * @param o an object
+ * @return {int} the size
+ */
+O.size = function(o) {
+ return _extract(o, 2);
+};
+
+/**
+ * Returns true if the object contains a given key
+ * @method hasKey
+ * @static
+ * @param o an object
+ * @param k the key to query
+ * @return {boolean} true if the object contains the key
+ */
+O.hasKey = function(o, k) {
+ // return (o.hasOwnProperty(k));
+ return (k in o);
+};
+
+/**
+ * Returns true if the object contains a given value
+ * @method hasValue
+ * @static
+ * @param o an object
+ * @param v the value to query
+ * @return {boolean} true if the object contains the value
+ */
+O.hasValue = function(o, v) {
+ return (Y.Array.indexOf(O.values(o), v) > -1);
+};
+
+/**
+ * Determines whether or not the property was added
+ * to the object instance. Returns false if the property is not present
+ * in the object, or was inherited from the prototype.
+ *
+ * @deprecated Safari 1.x support has been removed, so this is simply a
+ * wrapper for the native implementation. Use the native implementation
+ * directly instead.
+ *
+ * @TODO Remove in B1
+ *
+ * @method owns
+ * @static
+ * @param o {any} The object being testing
+ * @param p {string} the property to look for
+ * @return {boolean} true if the object has the property on the instance
+ */
+O.owns = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+/**
+ * Executes a function on each item. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method each
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {YUI} the YUI instance
+ */
+O.each = function (o, f, c, proto) {
+ var s = c || Y, i;
+
+ for (i in o) {
+ if (proto || o.hasOwnProperty(i)) {
+ f.call(s, o[i], i, o);
+ }
+ }
+ return Y;
+};
+
+/*
+ * Executes a function on each item, but halts if the
+ * function returns true. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method some
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {boolean} true if any execution of the function returns true, false otherwise
+ */
+// O.some = function (o, f, c, proto) {
+// var s = c || Y, i;
+//
+// for (i in o) {
+// if (proto || o.hasOwnProperty(i)) {
+// if (f.call(s, o[i], i, o)) {
+// return true;
+// }
+// }
+// }
+// return false;
+// };
+
+/**
+ * Retrieves the sub value at the provided path,
+ * from the value object provided.
+ *
+ * @method getValue
+ * @param o The object from which to extract the property value
+ * @param path {Array} A path array, specifying the object traversal path
+ * from which to obtain the sub value.
+ * @return {Any} The value stored in the path, undefined if not found.
+ * Returns the source object if an empty path is provided.
+ */
+O.getValue = function (o, path) {
+ var p=Y.Array(path), l=p.length, i;
+
+ for (i=0; o !== UNDEFINED && i < l; i=i+1) {
+ o = o[p[i]];
+ }
+
+ return o;
+};
+
+/**
+ * Sets the sub-attribute value at the provided path on the
+ * value object. Returns the modified value object, or
+ * undefined if the path is invalid.
+ *
+ * @method setValue
+ * @param o The object on which to set the sub value.
+ * @param path {Array} A path array, specifying the object traversal path
+ * at which to set the sub value.
+ * @param val {Any} The new value for the sub-attribute.
+ * @return {Object} The modified object, with the new sub value set, or
+ * undefined, if the path was invalid.
+ */
+O.setValue = function(o, path, val) {
+
+ var p=Y.Array(path), leafIdx=p.length-1, i, ref=o;
+
+ if (leafIdx >= 0) {
+ for (i=0; ref !== UNDEFINED && i < leafIdx; i=i+1) {
+ ref = ref[p[i]];
+ }
+
+ if (ref !== UNDEFINED) {
+ ref[p[i]] = val;
+ } else {
+ return UNDEFINED;
+ }
+ }
+
+ return o;
+};
+
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * YUI user agent detection.
+ * Do not fork for a browser if it can be avoided. Use feature detection when
+ * you can. Use the user agent as a last resort. UA stores a version
+ * number for the browser engine, 0 otherwise. This value may or may not map
+ * to the version number of the browser using the engine. The value is
+ * presented as a float so that it can easily be used for boolean evaluation
+ * as well as for looking for a particular range of versions. Because of this,
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9
+ * reports 1.8).
+ * @class UA
+ * @static
+ */
+Y.UA = function() {
+
+ var numberfy = function(s) {
+ var c = 0;
+ return parseFloat(s.replace(/\./g, function() {
+ return (c++ == 1) ? '' : '.';
+ }));
+ },
+
+ nav = navigator,
+
+ o = {
+
+ /**
+ * Internet Explorer version number or 0. Example: 6
+ * @property ie
+ * @type float
+ * @static
+ */
+ ie: 0,
+
+ /**
+ * Opera version number or 0. Example: 9.2
+ * @property opera
+ * @type float
+ * @static
+ */
+ opera: 0,
+
+ /**
+ * Gecko engine revision number. Will evaluate to 1 if Gecko
+ * is detected but the revision could not be found. Other browsers
+ * will be 0. Example: 1.8
+ * <pre>
+ * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7
+ * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+ * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+ * Firefox 3 alpha: 1.9a4 <-- Reports 1.9
+ * </pre>
+ * @property gecko
+ * @type float
+ * @static
+ */
+ gecko: 0,
+
+ /**
+ * AppleWebKit version. KHTML browsers that are not WebKit browsers
+ * will evaluate to 1, other browsers 0. Example: 418.9
+ * <pre>
+ * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
+ * latest available for Mac OSX 10.3.
+ * Safari 2.0.2: 416 <-- hasOwnProperty introduced
+ * Safari 2.0.4: 418 <-- preventDefault fixed
+ * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+ * different versions of webkit
+ * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been
+ * updated, but not updated
+ * to the latest patch.
+ * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native SVG
+ * and many major issues fixed).
+ * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic update
+ * from 2.x via the 10.4.11 OS patch
+ * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event.
+ * yahoo.com user agent hack removed.
+ * </pre>
+ * http://en.wikipedia.org/wiki/Safari_(web_browser)#Version_history
+ * @property webkit
+ * @type float
+ * @static
+ */
+ webkit: 0,
+
+ /**
+ * The mobile property will be set to a string containing any relevant
+ * user agent information when a modern mobile browser is detected.
+ * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+ * devices with the WebKit-based browser, and Opera Mini.
+ * @property mobile
+ * @type string
+ * @static
+ */
+ mobile: null,
+
+ /**
+ * Adobe AIR version number or 0. Only populated if webkit is detected.
+ * Example: 1.0
+ * @property air
+ * @type float
+ */
+ air: 0,
+
+ /**
+ * Google Caja version number or 0.
+ * @property caja
+ * @type float
+ */
+ caja: nav.cajaVersion,
+
+ /**
+ * Set to true if the page appears to be in SSL
+ * @property secure
+ * @type boolean
+ * @static
+ */
+ secure: false,
+
+ /**
+ * The operating system. Currently only detecting windows or macintosh
+ * @property os
+ * @type string
+ * @static
+ */
+ os: null
+
+ },
+
+ ua = nav && nav.userAgent,
+
+ loc = Y.config.win.location,
+
+ href = loc && loc.href,
+
+ m;
+
+ o.secure = href && (href.toLowerCase().indexOf("https") === 0);
+
+ if (ua) {
+
+ if ((/windows|win32/i).test(ua)) {
+ o.os = 'windows';
+ } else if ((/macintosh/i).test(ua)) {
+ o.os = 'macintosh';
+ }
+
+ // Modern KHTML browsers should qualify as Safari X-Grade
+ if ((/KHTML/).test(ua)) {
+ o.webkit=1;
+ }
+ // Modern WebKit browsers are at least X-Grade
+ m=ua.match(/AppleWebKit\/([^\s]*)/);
+ if (m&&m[1]) {
+ o.webkit=numberfy(m[1]);
+
+ // Mobile browser check
+ if (/ Mobile\//.test(ua)) {
+ o.mobile = "Apple"; // iPhone or iPod Touch
+ } else {
+ m=ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
+ if (m) {
+ o.mobile = m[0]; // Nokia N-series, Android, webOS, ex: NokiaN95
+ }
+ }
+
+ m=ua.match(/AdobeAIR\/([^\s]*)/);
+ if (m) {
+ o.air = m[0]; // Adobe AIR 1.0 or better
+ }
+
+ }
+
+ if (!o.webkit) { // not webkit
+ // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+ m=ua.match(/Opera[\s\/]([^\s]*)/);
+ if (m&&m[1]) {
+ o.opera=numberfy(m[1]);
+ m=ua.match(/Opera Mini[^;]*/);
+ if (m) {
+ o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+ }
+ } else { // not opera or webkit
+ m=ua.match(/MSIE\s([^;]*)/);
+ if (m&&m[1]) {
+ o.ie=numberfy(m[1]);
+ } else { // not opera, webkit, or ie
+ m=ua.match(/Gecko\/([^\s]*)/);
+ if (m) {
+ o.gecko=1; // Gecko detected, look for revision
+ m=ua.match(/rv:([^\s\)]*)/);
+ if (m&&m[1]) {
+ o.gecko=numberfy(m[1]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return o;
+}();
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var min = ['yui-base'], core, C = Y.config, mods = YUI.Env.mods,
+ extras, i;
+
+ // apply the minimal required functionality
+ Y.use.apply(Y, min);
+
+
+ if (C.core) {
+ core = C.core;
+ } else {
+ core = [];
+ extras = ['get', 'loader', 'yui-log', 'yui-later'];
+
+ for (i=0; i<extras.length; i++) {
+ if (mods[extras[i]]) {
+ core.push(extras[i]);
+ }
+ }
+ }
+
+ Y.use.apply(Y, core);
+
+})();
+
+
+
+}, '3.0.0' );
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var _instances = {},
+ _startTime = new Date().getTime(),
+ p,
+ i,
+
+ add = function () {
+ if (window.addEventListener) {
+ return function(el, type, fn, capture) {
+ el.addEventListener(type, fn, (!!capture));
+ };
+ } else if (window.attachEvent) {
+ return function(el, type, fn) {
+ el.attachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ remove = function() {
+ if (window.removeEventListener) {
+ return function (el, type, fn, capture) {
+ el.removeEventListener(type, fn, !!capture);
+ };
+ } else if (window.detachEvent) {
+ return function (el, type, fn) {
+ el.detachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ globalListener = function() {
+ YUI.Env.windowLoaded = true;
+ YUI.Env.DOMReady = true;
+ remove(window, 'load', globalListener);
+ },
+
+// @TODO: this needs to be created at build time from module metadata
+
+ _APPLY_TO_WHITE_LIST = {
+ 'io.xdrReady': 1,
+ 'io.xdrResponse':1
+ },
+
+ SLICE = Array.prototype.slice;
+
+// reduce to one or the other
+if (typeof YUI === 'undefined' || !YUI) {
+
+ /**
+ * The YUI global namespace object. If YUI is already defined, the
+ * existing YUI object will not be overwritten so that defined
+ * namespaces are preserved.
+ *
+ * @class YUI
+ * @constructor
+ * @global
+ * @uses EventTarget
+ * @param o* Up to five optional configuration objects. This object is stored
+ * in YUI.config. See config for the list of supported properties.
+ */
+
+ /*global YUI*/
+ // Make a function, disallow direct instantiation
+ YUI = function(o1, o2, o3, o4, o5) {
+
+ var Y = this, a = arguments, i, l = a.length;
+
+ // Allow instantiation without the new operator
+ if (!(Y instanceof YUI)) {
+ return new YUI(o1, o2, o3, o4, o5);
+ } else {
+ // set up the core environment
+ Y._init();
+
+ for (i=0; i<l; i++) {
+ Y._config(a[i]);
+ }
+
+ // bind the specified additional modules for this instance
+ Y._setup();
+
+ return Y;
+ }
+ };
+}
+
+// The prototype contains the functions that are required to allow the external
+// modules to be registered and for the instance to be initialized.
+YUI.prototype = {
+
+ _config: function(o) {
+
+ o = o || {};
+
+ var c = this.config, i, j, m, mods;
+
+ mods = c.modules;
+ for (i in o) {
+ if (mods && i == 'modules') {
+ m = o[i];
+ for (j in m) {
+ if (m.hasOwnProperty(j)) {
+ mods[j] = m[j];
+ }
+ }
+ } else if (i == 'win') {
+ c[i] = o[i].contentWindow || o[i];
+ c.doc = c[i].document;
+ } else {
+ c[i] = o[i];
+ }
+ }
+ },
+
+ /**
+ * Initialize this YUI instance
+ * @private
+ */
+ _init: function() {
+
+ // find targeted window/frame
+ // @TODO create facades
+ var v = '3.0.0', Y = this;
+
+ if (v.indexOf('@') > -1) {
+ v = 'test';
+ }
+
+ Y.version = v;
+
+ Y.Env = {
+ // @todo expand the new module metadata
+ mods: {},
+ cdn: 'http://yui.yahooapis.com/' + v + '/build/',
+ bootstrapped: false,
+ _idx: 0,
+ _used: {},
+ _attached: {},
+ _yidx: 0,
+ _uidx: 0,
+ _loaded: {}
+ };
+
+ Y.Env._loaded[v] = {};
+
+ if (YUI.Env) {
+ Y.Env._yidx = (++YUI.Env._yidx);
+ Y.Env._guidp = ('yui_' + v + '-' + Y.Env._yidx + '-' + _startTime).replace(/\./g, '_');
+ Y.id = Y.stamp(Y);
+ _instances[Y.id] = Y;
+ }
+
+ Y.constructor = YUI;
+
+ // configuration defaults
+ Y.config = {
+
+ win: window || {},
+ doc: document,
+ debug: true,
+ useBrowserConsole: true,
+ throwFail: true,
+ bootstrap: true,
+ fetchCSS: true,
+
+ base: function() {
+ var b, nodes, i, match;
+
+ // get from querystring
+ nodes = document.getElementsByTagName('script');
+
+ for (i=0; i<nodes.length; i=i+1) {
+ match = nodes[i].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);
+ b = match && match[1];
+ if (b) {
+ break;
+ }
+ }
+
+ // use CDN default
+ return b || Y.Env.cdn;
+
+ }(),
+
+ loaderPath: 'loader/loader-min.js'
+ };
+
+ },
+
+ /**
+ * Finishes the instance setup. Attaches whatever modules were defined
+ * when the yui modules was registered.
+ * @method _setup
+ * @private
+ */
+ _setup: function(o) {
+ this.use("yui-base");
+ },
+
+ /**
+ * Executes a method on a YUI instance with
+ * the specified id if the specified method is whitelisted.
+ * @method applyTo
+ * @param id {string} the YUI instance id
+ * @param method {string} the name of the method to exectute.
+ * Ex: 'Object.keys'
+ * @param args {Array} the arguments to apply to the method
+ * @return {object} the return value from the applied method or null
+ */
+ applyTo: function(id, method, args) {
+
+ if (!(method in _APPLY_TO_WHITE_LIST)) {
+ this.log(method + ': applyTo not allowed', 'warn', 'yui');
+ return null;
+ }
+
+ var instance = _instances[id], nest, m, i;
+
+ if (instance) {
+
+ nest = method.split('.');
+ m = instance;
+
+ for (i=0; i<nest.length; i=i+1) {
+
+ m = m[nest[i]];
+
+ if (!m) {
+ this.log('applyTo not found: ' + method, 'warn', 'yui');
+ }
+ }
+
+ return m.apply(instance, args);
+ }
+
+ return null;
+ },
+
+ /**
+ * Register a module
+ * @method add
+ * @param name {string} module name
+ * @param fn {Function} entry point into the module that
+ * is used to bind module to the YUI instance
+ * @param version {string} version string
+ * @param details optional config data:
+ * requires - features that should be present before loading
+ * optional - optional features that should be present if load optional defined
+ * use - features that should be attached automatically
+ * skinnable -
+ * rollup
+ * omit - features that should not be loaded if this module is present
+ * @return {YUI} the YUI instance
+ *
+ */
+ add: function(name, fn, version, details) {
+ // this.log('Adding a new component ' + name);
+ // @todo expand this to include version mapping
+ // @todo may want to restore the build property
+ // @todo fire moduleAvailable event
+
+ YUI.Env.mods[name] = {
+ name: name,
+ fn: fn,
+ version: version,
+ details: details || {}
+ };
+
+ return this; // chain support
+ },
+
+ _attach: function(r, fromLoader) {
+
+ var mods = YUI.Env.mods,
+ attached = this.Env._attached,
+ i, l = r.length, name, m, d, req, use;
+
+ for (i=0; i<l; i=i+1) {
+
+ name = r[i];
+ m = mods[name];
+
+ if (!attached[name] && m) {
+
+ attached[name] = true;
+
+ d = m.details;
+ req = d.requires;
+ use = d.use;
+
+ if (req) {
+ this._attach(this.Array(req));
+ }
+
+ // this.log('attaching ' + name, 'info', 'yui');
+
+ if (m.fn) {
+ m.fn(this);
+ }
+
+ if (use) {
+ this._attach(this.Array(use));
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Bind a module to a YUI instance
+ * @param modules* {string} 1-n modules to bind (uses arguments array)
+ * @param *callback {function} callback function executed when
+ * the instance has the required functionality. If included, it
+ * must be the last parameter.
+ *
+ * @TODO
+ * Implement versioning? loader can load different versions?
+ * Should sub-modules/plugins be normal modules, or do
+ * we add syntax for specifying these?
+ *
+ * YUI().use('dragdrop')
+ * YUI().use('dragdrop:2.4.0'); // specific version
+ * YUI().use('dragdrop:2.4.0-'); // at least this version
+ * YUI().use('dragdrop:2.4.0-2.9999.9999'); // version range
+ * YUI().use('*'); // use all available modules
+ * YUI().use('lang+dump+substitute'); // use lang and some plugins
+ * YUI().use('lang+*'); // use lang and all known plugins
+ *
+ *
+ * @return {YUI} the YUI instance
+ */
+ use: function() {
+
+ if (this._loading) {
+ this._useQueue = this._useQueue || new this.Queue();
+ this._useQueue.add(SLICE.call(arguments, 0));
+ return this;
+ }
+
+ var Y = this,
+ a=SLICE.call(arguments, 0),
+ mods = YUI.Env.mods,
+ used = Y.Env._used,
+ loader,
+ firstArg = a[0],
+ dynamic = false,
+ callback = a[a.length-1],
+ boot = Y.config.bootstrap,
+ k, i, l, missing = [],
+ r = [],
+ css = Y.config.fetchCSS,
+ f = function(name) {
+
+ // only attach a module once
+ if (used[name]) {
+ // Y.log(name + ' already used', 'info', 'yui');
+ return;
+ }
+
+ var m = mods[name], j, req, use;
+
+ if (m) {
+
+ // Y.log('USING ' + name, 'info', 'yui');
+
+ used[name] = true;
+
+ req = m.details.requires;
+ use = m.details.use;
+ } else {
+
+ // CSS files don't register themselves, see if it has been loaded
+ if (!YUI.Env._loaded[Y.version][name]) {
+ // Y.log('module not found: ' + name, 'info', 'yui');
+ missing.push(name);
+ } else {
+ // probably css
+ // Y.log('module not found BUT HAS BEEN LOADED: ' + name, 'info', 'yui');
+ used[name] = true;
+ }
+ }
+
+ // make sure requirements are attached
+ if (req) {
+ if (Y.Lang.isString(req)) {
+ f(req);
+ } else {
+ for (j = 0; j < req.length; j = j + 1) {
+ // Y.log('using module\'s requirements: ' + name, 'info', 'yui');
+ f(req[j]);
+ }
+ }
+ }
+
+ // add this module to full list of things to attach
+ // Y.log('adding to requires list: ' + name);
+ r.push(name);
+
+ },
+
+ onComplete;
+
+ // Y.log(Y.id + ': use called: ' + a + ' :: ' + callback);
+
+ // The last argument supplied to use can be a load complete callback
+ if (typeof callback === 'function') {
+ a.pop();
+ } else {
+ callback = null;
+ }
+
+ onComplete = function(fromLoader) {
+
+ // Y.log('Use complete');
+
+ fromLoader = fromLoader || {
+ success: true,
+ msg: 'not dynamic'
+ };
+
+ if (callback) {
+ callback(Y, fromLoader);
+ }
+
+ if (Y.fire) {
+ Y.fire('yui:load', Y, fromLoader);
+ }
+
+ // process queued use requests as long until done
+ // or dynamic load happens again.
+ Y._loading = false;
+
+ if (Y._useQueue && Y._useQueue.size() && !Y._loading) {
+ Y.use.apply(Y, Y._useQueue.next());
+ }
+ };
+
+
+ // YUI().use('*'); // bind everything available
+ if (firstArg === "*") {
+ a = [];
+ for (k in mods) {
+ if (mods.hasOwnProperty(k)) {
+ a.push(k);
+ }
+ }
+
+ if (callback) {
+ a.push(callback);
+ }
+
+ return Y.use.apply(Y, a);
+ }
+
+ // Y.log('loader before: ' + a.join(','));
+
+ // use loader to expand dependencies and sort the
+ // requirements if it is available.
+ if (Y.Loader) {
+ dynamic = true;
+ loader = new Y.Loader(Y.config);
+ loader.require(a);
+ loader.ignoreRegistered = true;
+ loader.allowRollup = false;
+ // loader.calculate(null, (css && css == 'force') ? null : 'js');
+ // loader.calculate();
+ loader.calculate(null, (css) ? null : 'js');
+ a = loader.sorted;
+ }
+
+ // Y.log('loader after: ' + a.join(','));
+
+ l = a.length;
+
+ // process each requirement and any additional requirements
+ // the module metadata specifies
+ for (i=0; i<l; i=i+1) {
+ f(a[i]);
+ }
+
+ l = missing.length;
+
+ Y.log('Module requirements: ' + a, 'info', 'yui');
+
+ if (l) {
+ missing = Y.Object.keys(Y.Array.hash(missing));
+ Y.log('Modules missing: ' + missing, 'info', 'yui');
+ }
+
+ // dynamic load
+ if (boot && l && Y.Loader) {
+ Y.log('Using loader to fetch missing dependencies.', 'info', 'yui');
+ Y._loading = true;
+ loader = new Y.Loader(Y.config);
+ loader.onSuccess = onComplete;
+ loader.onFailure = onComplete;
+ loader.onTimeout = onComplete;
+ loader.context = Y;
+ loader.attaching = a;
+ // loader.require(missing);
+ loader.require((css) ? missing : a);
+ loader.insert(null, (css) ? null : 'js');
+ } else if (boot && l && Y.Get && !Y.Env.bootstrapped) {
+ Y.log('Fetching loader: ' + Y.config.base + Y.config.loaderPath, 'info', 'yui');
+ Y._loading = true;
+
+ a = Y.Array(arguments, 0, true);
+ // a.unshift('loader');
+
+ Y.Get.script(Y.config.base + Y.config.loaderPath, {
+ onEnd: function() {
+ Y._loading = false;
+ Y.Env.bootstrapped = true;
+ Y._attach(['loader']);
+ Y.use.apply(Y, a);
+ }
+ });
+
+ return Y;
+
+ } else {
+ if (l) {
+ Y.log('Unable or not configured to fetch missing modules.', 'info', 'yui');
+ }
+ Y.log('Attaching available dependencies.', 'info', 'yui');
+ Y._attach(r);
+ onComplete();
+ }
+
+ return Y; // chain support var yui = YUI().use('dragdrop');
+ },
+
+
+ /**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YUI.namespace("property.package");
+ * YUI.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YUI.property, then
+ * YUI.property.package (YAHOO is scrubbed out, this is
+ * to remain compatible with YUI2)
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YUI.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @param {string*} arguments 1-n namespaces to create
+ * @return {object} A reference to the last namespace object created
+ */
+ namespace: function() {
+ var a=arguments, o=null, i, j, d;
+ for (i=0; i<a.length; i=i+1) {
+ d = ("" + a[i]).split(".");
+ o = this;
+ for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+ o[d[j]] = o[d[j]] || {};
+ o = o[d[j]];
+ }
+ }
+ return o;
+ },
+
+ // this is replaced if the log module is included
+ log: function() {
+
+ },
+
+ /**
+ * Report an error. The reporting mechanism is controled by
+ * the 'throwFail' configuration attribute. If throwFail is
+ * not specified, the message is written to the Logger, otherwise
+ * a JS error is thrown
+ * @method error
+ * @param msg {string} the error message
+ * @param e {Error} Optional JS error that was caught. If supplied
+ * and throwFail is specified, this error will be re-thrown.
+ * @return {YUI} this YUI instance
+ */
+ error: function(msg, e) {
+ if (this.config.throwFail) {
+ throw (e || new Error(msg));
+ } else {
+ this.message(msg, "error"); // don't scrub this one
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate an id that is unique among all YUI instances
+ * @method guid
+ * @param pre {string} optional guid prefix
+ * @return {string} the guid
+ */
+ guid: function(pre) {
+ var id = this.Env._guidp + (++this.Env._uidx);
+ return (pre) ? (pre + id) : id;
+ },
+
+ /**
+ * Returns a guid associated with an object. If the object
+ * does not have one, a new one is created unless readOnly
+ * is specified.
+ * @method stamp
+ * @param o The object to stamp
+ * @param readOnly {boolean} if true, a valid guid will only
+ * be returned if the object has one assigned to it.
+ * @return {string} The object's guid or null
+ */
+ stamp: function(o, readOnly) {
+
+ if (!o) {
+ return o;
+ }
+
+ var uid = (typeof o === 'string') ? o : o._yuid;
+
+ if (!uid) {
+ uid = this.guid();
+ if (!readOnly) {
+ try {
+ o._yuid = uid;
+ } catch(e) {
+ uid = null;
+ }
+ }
+ }
+
+ return uid;
+ }
+};
+
+// Give the YUI global the same properties as an instance.
+// This makes it so that the YUI global can be used like the YAHOO
+// global was used prior to 3.x. More importantly, the YUI global
+// provides global metadata, so env needs to be configured.
+// @TODO review
+
+ p = YUI.prototype;
+
+ // inheritance utilities are not available yet
+ for (i in p) {
+ // if (1) { // intenionally ignoring hasOwnProperty check
+ YUI[i] = p[i];
+ // }
+ }
+
+ // set up the environment
+ YUI._init();
+
+ // add a window load event at load time so we can capture
+ // the case where it fires before dynamic loading is
+ // complete.
+ add(window, 'load', globalListener);
+
+ YUI.Env.add = add;
+ YUI.Env.remove = remove;
+
+ /*
+ * Subscribe to an event. The signature differs depending on the
+ * type of event you are attaching to.
+ * @method on
+ * @param type {string|function|object} The type of the event. If
+ * this is a function, this is dispatched to the aop system. If an
+ * object, it is parsed for multiple subsription definitions
+ * @param fn {Function} The callback
+ * @param elspec {any} DOM element(s), selector string(s), and or
+ * Node ref(s) to attach DOM related events to (only applies to
+ * DOM events).
+ * @param
+ * @return the event target or a detach handle per 'chain' config
+ */
+
+})();
+
+/**
+ * The config object contains all of the configuration options for
+ * the YUI instance. This object is supplied by the implementer
+ * when instantiating a YUI instance. Some properties have default
+ * values if they are not supplied by the implementer.
+ *
+ * @class config
+ * @static
+ */
+
+/**
+ * Allows the YUI seed file to fetch the loader component and library
+ * metadata to dynamically load additional dependencies.
+ *
+ * @property bootstrap
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * Log to the browser console if debug is on and the browser has a
+ * supported console.
+ *
+ * @property useBrowserConsole
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * A hash of log sources that should be logged. If specified, only log messages from these sources will be logged.
+ *
+ * @property logInclude
+ * @type object
+ */
+
+/**
+ * A hash of log sources that should be not be logged. If specified, all sources are logged if not on this list.
+ *
+ * @property logExclude
+ * @type object
+ */
+
+/**
+ * Set to true if the yui seed file was dynamically loaded in
+ * order to bootstrap components relying on the window load event
+ * and the 'domready' custom event.
+ *
+ * @property injected
+ * @type object
+ */
+
+/**
+ * If throwFail is set, Y.fail will generate or re-throw a JS Error. Otherwise the failure is logged.
+ *
+ * @property throwFail
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * The window/frame that this instance should operate in.
+ *
+ * @property win
+ * @type Window
+ * @default the window hosting YUI
+ */
+
+/**
+ * The document associated with the 'win' configuration.
+ *
+ * @property doc
+ * @type Document
+ * @default the document hosting YUI
+ */
+
+/**
+ * A list of modules that defines the YUI core (overrides the default).
+ *
+ * @property core
+ * @type string[]
+ */
+
+/**
+ * The default date format
+ *
+ * @property dateFormat
+ * @type string
+ */
+
+/**
+ * The default locale
+ *
+ * @property locale
+ * @type string
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property pollInterval
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The number of dynamic nodes to insert by default before
+ * automatically removing them. This applies to script nodes
+ * because remove the node will not make the evaluated script
+ * unavailable. Dynamic CSS is not auto purged, because removing
+ * a linked style sheet will also remove the style definitions.
+ *
+ * @property purgethreshold
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property windowResizeDelay
+ * @type int
+ * @default 40
+ */
+
+/**
+ * Base directory for dynamic loading
+ *
+ * @property base
+ * @type string
+ */
+
+/**
+ * The secure base dir (not implemented)
+ *
+ * For dynamic loading.
+ *
+ * @property secureBase
+ * @type string
+ */
+
+/**
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?
+ *
+ * For dynamic loading.
+ *
+ * @property comboBase
+ * @type string
+ */
+
+/**
+ * The root path to prepend to module names for the combo service. Ex: 3.0.0b1/build/
+ *
+ * For dynamic loading.
+ *
+ * @property root
+ * @type string
+ */
+
+/**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * For dynamic loading.
+ *
+ * @property filter
+ * @type string|object
+ */
+
+/**
+ * Hash of per-component filter specification. If specified for a given component,
+ * this overrides the filter config
+ *
+ * For dynamic loading.
+ *
+ * @property filters
+ * @type object
+ */
+
+/**
+ * Use the YUI combo service to reduce the number of http connections
+ * required to load your dependencies.
+ *
+ * For dynamic loading.
+ *
+ * @property combine
+ * @type boolean
+ * @default true if 'base' is not supplied, false if it is.
+ */
+
+/**
+ * A list of modules that should never be dynamically loaded
+ *
+ * @property ignore
+ * @type string[]
+ */
+
+/**
+ * A list of modules that should always be loaded when required, even if already
+ * present on the page.
+ *
+ * @property force
+ * @type string[]
+ */
+
+/**
+ * Node or id for a node that should be used as the insertion point for new nodes
+ * For dynamic loading.
+ *
+ * @property insertBefore
+ * @type string
+ */
+
+/**
+ * charset for dynamic nodes
+ *
+ * @property charset
+ * @type string
+ * @deprecated use jsAttributes cssAttributes
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded script nodes.
+ *
+ * @property jsAttributes
+ * @type string
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded link nodes.
+ *
+ * @property cssAttributes
+ * @type string
+ */
+
+/**
+ * Number of milliseconds before a timeout occurs when dynamically
+ * loading nodes. If not set, there is no timeout.
+ *
+ * @property timeout
+ * @type int
+ */
+
+/**
+ * Callback for the 'CSSComplete' event. When dynamically loading YUI
+ * components with CSS, this property fires when the CSS is finished
+ * loading but script loading is still ongoing. This provides an
+ * opportunity to enhance the presentation of a loading page a little
+ * bit before the entire loading process is done.
+ *
+ * @property onCSS
+ * @type function
+ */
+
+/**
+ * A list of module definitions to add to the list of YUI components.
+ * These components can then be dynamically loaded side by side with
+ * YUI via the use() method.See Loader.addModule for the supported
+ * module metadata.
+ *
+ * @property modules
+ * @type function
+ */
+
+/**
+ * The loader 'path' attribute to the loader itself. This is combined
+ * with the 'base' attribute to dynamically load the loader component
+ * when boostrapping with the get utility alone.
+ *
+ * @property loaderPath
+ * @default loader/loader-min.js
+ */
+
+/**
+ *
+ * Specifies whether or not YUI().use(...) will attempt to load CSS
+ * resources at all. Any truthy value will cause CSS dependencies
+ * to load when fetching script. The special value 'force' will
+ * cause CSS dependencies to be loaded even if no script is needed.
+ *
+ * @property fetchCSS
+ * @default true
+ */
+YUI.add('yui-base', function(Y) {
+
+/*
+ * YUI stub
+ * @module yui
+ * @submodule yui-base
+ */
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * A simple FIFO queue. Items are added to the Queue with add(1..n items) and
+ * removed using next().
+ *
+ * @class Queue
+ * @param item* {MIXED} 0..n items to seed the queue
+ */
+function Queue() {
+ this._init();
+ this.add.apply(this, arguments);
+}
+
+Queue.prototype = {
+ /**
+ * Initialize the queue
+ *
+ * @method _init
+ * @protected
+ */
+ _init : function () {
+ /**
+ * The collection of enqueued items
+ *
+ * @property _q
+ * @type {Array}
+ * @protected
+ */
+ this._q = [];
+ },
+
+ /**
+ * Get the next item in the queue.
+ *
+ * @method next
+ * @return {MIXED} the next item in the queue
+ */
+ next : function () {
+ return this._q.shift();
+ },
+
+ /**
+ * Add 0..n items to the end of the queue
+ *
+ * @method add
+ * @param item* {MIXED} 0..n items
+ */
+ add : function () {
+ Y.Array.each(Y.Array(arguments,0,true),function (fn) {
+ this._q.push(fn);
+ },this);
+
+ return this;
+ },
+
+ /**
+ * Returns the current number of queued items
+ *
+ * @method size
+ * @return {Number}
+ */
+ size : function () {
+ return this._q.length;
+ }
+};
+
+Y.Queue = Queue;
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class Lang
+ * @static
+ */
+Y.Lang = Y.Lang || {};
+
+var L = Y.Lang,
+
+ARRAY = 'array',
+BOOLEAN = 'boolean',
+DATE = 'date',
+ERROR = 'error',
+FUNCTION = 'function',
+NUMBER = 'number',
+NULL = 'null',
+OBJECT = 'object',
+REGEX = 'regexp',
+STRING = 'string',
+TOSTRING = Object.prototype.toString,
+UNDEFINED = 'undefined',
+
+TYPES = {
+ 'undefined' : UNDEFINED,
+ 'number' : NUMBER,
+ 'boolean' : BOOLEAN,
+ 'string' : STRING,
+ '[object Function]' : FUNCTION,
+ '[object RegExp]' : REGEX,
+ '[object Array]' : ARRAY,
+ '[object Date]' : DATE,
+ '[object Error]' : ERROR
+},
+
+TRIMREGEX = /^\s+|\s+$/g,
+EMPTYSTRING = '';
+
+/**
+ * Determines whether or not the provided item is an array.
+ * Returns false for array-like collections such as the
+ * function arguments collection or HTMLElement collection
+ * will return false. You can use @see Array.test if you
+ * want to
+ * @method isArray
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is an array
+ */
+L.isArray = function(o) {
+ return L.type(o) === ARRAY;
+};
+
+/**
+ * Determines whether or not the provided item is a boolean
+ * @method isBoolean
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a boolean
+ */
+L.isBoolean = function(o) {
+ return typeof o === BOOLEAN;
+};
+
+/**
+ * Determines whether or not the provided item is a function
+ * Note: Internet Explorer thinks certain functions are objects:
+ *
+ * var obj = document.createElement("object");
+ * Y.Lang.isFunction(obj.getAttribute) // reports false in IE
+ *
+ * var input = document.createElement("input"); // append to body
+ * Y.Lang.isFunction(input.focus) // reports false in IE
+ *
+ * You will have to implement additional tests if these functions
+ * matter to you.
+ *
+ * @method isFunction
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a function
+ */
+L.isFunction = function(o) {
+ return L.type(o) === FUNCTION;
+};
+
+/**
+ * Determines whether or not the supplied item is a date instance
+ * @method isDate
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a date
+ */
+L.isDate = function(o) {
+ // return o instanceof Date;
+ return L.type(o) === DATE;
+};
+
+/**
+ * Determines whether or not the provided item is null
+ * @method isNull
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is null
+ */
+L.isNull = function(o) {
+ return o === null;
+};
+
+/**
+ * Determines whether or not the provided item is a legal number
+ * @method isNumber
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a number
+ */
+L.isNumber = function(o) {
+ return typeof o === NUMBER && isFinite(o);
+};
+
+/**
+ * Determines whether or not the provided item is of type object
+ * or function
+ * @method isObject
+ * @static
+ * @param o The object to test
+ * @param failfn {boolean} fail if the input is a function
+ * @return {boolean} true if o is an object
+ */
+L.isObject = function(o, failfn) {
+return (o && (typeof o === OBJECT || (!failfn && L.isFunction(o)))) || false;
+};
+
+/**
+ * Determines whether or not the provided item is a string
+ * @method isString
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a string
+ */
+L.isString = function(o) {
+ return typeof o === STRING;
+};
+
+/**
+ * Determines whether or not the provided item is undefined
+ * @method isUndefined
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is undefined
+ */
+L.isUndefined = function(o) {
+ return typeof o === UNDEFINED;
+};
+
+/**
+ * Returns a string without any leading or trailing whitespace. If
+ * the input is not a string, the input will be returned untouched.
+ * @method trim
+ * @static
+ * @param s {string} the string to trim
+ * @return {string} the trimmed string
+ */
+L.trim = function(s){
+ try {
+ return s.replace(TRIMREGEX, EMPTYSTRING);
+ } catch(e) {
+ return s;
+ }
+};
+
+/**
+ * A convenience method for detecting a legitimate non-null value.
+ * Returns false for null/undefined/NaN, true for other values,
+ * including 0/false/''
+ * @method isValue
+ * @static
+ * @param o The item to test
+ * @return {boolean} true if it is not null/undefined/NaN || false
+ */
+L.isValue = function(o) {
+ var t = L.type(o);
+ switch (t) {
+ case NUMBER:
+ return isFinite(o);
+ case NULL:
+ case UNDEFINED:
+ return false;
+ default:
+ return !!(t);
+ }
+};
+
+/**
+ * Returns a string representing the type of the item passed in.
+ * @method type
+ * @param o the item to test
+ * @return {string} the detected type
+ */
+L.type = function (o) {
+ return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? OBJECT : NULL);
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang, Native = Array.prototype,
+
+/**
+ * Adds the following array utilities to the YUI instance. Additional
+ * array helpers can be found in the collection component.
+ * @class Array
+ */
+
+/**
+ * Y.Array(o) returns an array:
+ * - Arrays are return unmodified unless the start position is specified.
+ * - "Array-like" collections (@see Array.test) are converted to arrays
+ * - For everything else, a new array is created with the input as the sole item
+ * - The start position is used if the input is or is like an array to return
+ * a subset of the collection.
+ *
+ * @TODO this will not automatically convert elements that are also collections
+ * such as forms and selects. Passing true as the third param will
+ * force a conversion.
+ *
+ * @method ()
+ * @static
+ * @param o the item to arrayify
+ * @param i {int} if an array or array-like, this is the start index
+ * @param al {boolean} if true, it forces the array-like fork. This
+ * can be used to avoid multiple array.test calls.
+ * @return {Array} the resulting array
+ */
+YArray = function(o, startIdx, al) {
+ var t = (al) ? 2 : Y.Array.test(o), i, l, a;
+
+ // switch (t) {
+ // case 1:
+ // // return (startIdx) ? o.slice(startIdx) : o;
+ // case 2:
+ // return Native.slice.call(o, startIdx || 0);
+ // default:
+ // return [o];
+ // }
+
+ if (t) {
+ try {
+ return Native.slice.call(o, startIdx || 0);
+ // IE errors when trying to slice element collections
+ } catch(e) {
+ a=[];
+ for (i=0, l=o.length; i<l; i=i+1) {
+ a.push(o[i]);
+ }
+ return a;
+ }
+ } else {
+ return [o];
+ }
+
+};
+
+Y.Array = YArray;
+
+/**
+ * Evaluates the input to determine if it is an array, array-like, or
+ * something else. This is used to handle the arguments collection
+ * available within functions, and HTMLElement collections
+ *
+ * @method test
+ * @static
+ *
+ * @todo current implementation (intenionally) will not implicitly
+ * handle html elements that are array-like (forms, selects, etc).
+ *
+ * @return {int} a number indicating the results:
+ * 0: Not an array or an array-like collection
+ * 1: A real array.
+ * 2: array-like collection.
+ */
+YArray.test = function(o) {
+ var r = 0;
+ if (L.isObject(o)) {
+ if (L.isArray(o)) {
+ r = 1;
+ } else {
+ try {
+ // indexed, but no tagName (element) or alert (window)
+ if ("length" in o && !("tagName" in o) && !("alert" in o) &&
+ (!Y.Lang.isFunction(o.size) || o.size() > 1)) {
+ r = 2;
+ }
+
+ } catch(e) {}
+ }
+ }
+ return r;
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * @method each
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The
+ * function receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {YUI} the YUI instance
+ */
+YArray.each = (Native.forEach) ?
+ function (a, f, o) {
+ Native.forEach.call(a || [], f, o || Y);
+ return Y;
+ } :
+ function (a, f, o) {
+ var l = (a && a.length) || 0, i;
+ for (i = 0; i < l; i=i+1) {
+ f.call(o || Y, a[i], i, a);
+ }
+ return Y;
+ };
+
+/**
+ * Returns an object using the first array as keys, and
+ * the second as values. If the second array is not
+ * provided the value is set to true for each.
+ * @method hash
+ * @static
+ * @param k {Array} keyset
+ * @param v {Array} optional valueset
+ * @return {object} the hash
+ */
+YArray.hash = function(k, v) {
+ var o = {}, l = k.length, vl = v && v.length, i;
+ for (i=0; i<l; i=i+1) {
+ o[k[i]] = (vl && vl > i) ? v[i] : true;
+ }
+
+ return o;
+};
+
+/**
+ * Returns the index of the first item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * @method indexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of the item that contains the value or -1
+ */
+YArray.indexOf = (Native.indexOf) ?
+ function(a, val) {
+ return Native.indexOf.call(a, val);
+ } :
+ function(a, val) {
+ for (var i=0; i<a.length; i=i+1) {
+ if (a[i] === val) {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+/**
+ * Numeric sort convenience function.
+ * Y.ArrayAssert.itemsAreEqual([1, 2, 3], [3, 1, 2].sort(Y.Array.numericSort));
+ * @method numericSort
+ */
+YArray.numericSort = function(a, b) {
+ return (a - b);
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * Returning true from the processing function will stop the
+ * processing of the remaining
+ * items.
+ * @method some
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {boolean} true if the function returns true on
+ * any of the items in the array
+ */
+ YArray.some = (Native.some) ?
+ function (a, f, o) {
+ return Native.some.call(a, f, o);
+ } :
+ function (a, f, o) {
+ var l = a.length, i;
+ for (i=0; i<l; i=i+1) {
+ if (f.call(o, a[i], i, a)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+})();
+
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang,
+DELIMITER = '__',
+// FROZEN = {
+// 'prototype': 1,
+// '_yuid': 1
+// },
+
+/*
+ * IE will not enumerate native functions in a derived object even if the
+ * function was overridden. This is a workaround for specific functions
+ * we care about on the Object prototype.
+ * @property _iefix
+ * @for YUI
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @private
+ */
+_iefix = function(r, s) {
+ var fn = s.toString;
+ if (L.isFunction(fn) && fn != Object.prototype.toString) {
+ r.toString = fn;
+ }
+};
+
+
+/**
+ * Returns a new object containing all of the properties of
+ * all the supplied objects. The properties from later objects
+ * will overwrite those in earlier objects. Passing in a
+ * single object will create a shallow copy of it. For a deep
+ * copy, use clone.
+ * @method merge
+ * @for YUI
+ * @param arguments {Object*} the objects to merge
+ * @return {object} the new merged object
+ */
+Y.merge = function() {
+ var a = arguments, o = {}, i, l = a.length;
+ for (i=0; i<l; i=i+1) {
+ Y.mix(o, a[i], true);
+ }
+ return o;
+};
+
+/**
+ * Applies the supplier's properties to the receiver. By default
+ * all prototype and static propertes on the supplier are applied
+ * to the corresponding spot on the receiver. By default all
+ * properties are applied, and a property that is already on the
+ * reciever will not be overwritten. The default behavior can
+ * be modified by supplying the appropriate parameters.
+ *
+ * @TODO add constants for the modes
+ *
+ * @method mix
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param {int} mode what should be copies, and to where
+ * default(0): object to object
+ * 1: prototype to prototype (old augment)
+ * 2: prototype to prototype and object props (new augment)
+ * 3: prototype to object
+ * 4: object to prototype
+ * @param merge {boolean} merge objects instead of overwriting/ignoring
+ * Used by Y.aggregate
+ * @return {object} the augmented object
+ */
+Y.mix = function(r, s, ov, wl, mode, merge) {
+
+ if (!s||!r) {
+ return r || Y;
+ }
+
+ if (mode) {
+ switch (mode) {
+ case 1: // proto to proto
+ return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ case 2: // object to object and proto to proto
+ Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ break; // pass through
+ case 3: // proto to static
+ return Y.mix(r, s.prototype, ov, wl, 0, merge);
+ case 4: // static to proto
+ return Y.mix(r.prototype, s, ov, wl, 0, merge);
+ default: // object to object is what happens below
+ }
+ }
+
+ // Maybe don't even need this wl && wl.length check anymore??
+ var arr = merge && L.isArray(r), i, l, p;
+
+ if (wl && wl.length) {
+ for (i = 0, l = wl.length; i < l; ++i) {
+ p = wl[i];
+ if (p in s) {
+ if (merge && L.isObject(r[p], true)) {
+ Y.mix(r[p], s[p]);
+ } else if (!arr && (ov || !(p in r))) {
+ r[p] = s[p];
+ } else if (arr) {
+ r.push(s[p]);
+ }
+ }
+ }
+ } else {
+ for (i in s) {
+ // if (s.hasOwnProperty(i) && !(i in FROZEN)) {
+ // check white list if it was supplied
+ // if the receiver has this property, it is an object,
+ // and merge is specified, merge the two objects.
+ if (merge && L.isObject(r[i], true)) {
+ Y.mix(r[i], s[i]); // recursive
+ // otherwise apply the property only if overwrite
+ // is specified or the receiver doesn't have one.
+ } else if (!arr && (ov || !(i in r))) {
+ r[i] = s[i];
+ // if merge is specified and the receiver is an array,
+ // append the array item
+ } else if (arr) {
+ r.push(s[i]);
+ }
+ // }
+ }
+
+ if (Y.UA.ie) {
+ _iefix(r, s);
+ }
+ }
+
+ return r;
+};
+
+/**
+ * Returns a wrapper for a function which caches the
+ * return value of that function, keyed off of the combined
+ * argument values.
+ * @function cached
+ * @param source {function} the function to memoize
+ * @param cache an optional cache seed
+ * @param refetch if supplied, this value is tested against the cached
+ * value. If the values are equal, the wrapped function is executed again.
+ * @return {Function} the wrapped function
+ */
+Y.cached = function(source, cache, refetch){
+ cache = cache || {};
+
+ return function(arg1, arg2) {
+
+ var k = (arg2) ? Array.prototype.join.call(arguments, DELIMITER) : arg1,
+ v = cache[k];
+
+ if (!(k in cache) || (refetch && cache[k] == refetch)) {
+ cache[k] = source.apply(source, arguments);
+ }
+
+ return cache[k];
+ };
+
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+
+/**
+ * Adds the following Object utilities to the YUI instance
+ * @class Object
+ */
+
+/**
+ * Y.Object(o) returns a new object based upon the supplied object.
+ * @TODO Use native Object.create() when available
+ * @method ()
+ * @static
+ * @param o the supplier object
+ * @return {Object} the new object
+ */
+Y.Object = function(o) {
+ var F = function() {};
+ F.prototype = o;
+ return new F();
+};
+
+var O = Y.Object,
+
+UNDEFINED = undefined,
+
+/**
+ * Extracts the keys, values, or size from an object
+ *
+ * @method _extract
+ * @param o the object
+ * @param what what to extract (0: keys, 1: values, 2: size)
+ * @return {boolean|Array} the extracted info
+ * @static
+ * @private
+ */
+_extract = function(o, what) {
+ var count = (what === 2), out = (count) ? 0 : [], i;
+
+ for (i in o) {
+ if (count) {
+ out++;
+ } else {
+ if (o.hasOwnProperty(i)) {
+ out.push((what) ? o[i] : i);
+ }
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Returns an array containing the object's keys
+ * @TODO use native Object.keys() if available
+ * @method keys
+ * @static
+ * @param o an object
+ * @return {string[]} the keys
+ */
+O.keys = function(o) {
+ return _extract(o);
+};
+
+/**
+ * Returns an array containing the object's values
+ * @TODO use native Object.values() if available
+ * @method values
+ * @static
+ * @param o an object
+ * @return {Array} the values
+ */
+O.values = function(o) {
+ return _extract(o, 1);
+};
+
+/**
+ * Returns the size of an object
+ * @TODO use native Object.size() if available
+ * @method size
+ * @static
+ * @param o an object
+ * @return {int} the size
+ */
+O.size = function(o) {
+ return _extract(o, 2);
+};
+
+/**
+ * Returns true if the object contains a given key
+ * @method hasKey
+ * @static
+ * @param o an object
+ * @param k the key to query
+ * @return {boolean} true if the object contains the key
+ */
+O.hasKey = function(o, k) {
+ // return (o.hasOwnProperty(k));
+ return (k in o);
+};
+
+/**
+ * Returns true if the object contains a given value
+ * @method hasValue
+ * @static
+ * @param o an object
+ * @param v the value to query
+ * @return {boolean} true if the object contains the value
+ */
+O.hasValue = function(o, v) {
+ return (Y.Array.indexOf(O.values(o), v) > -1);
+};
+
+/**
+ * Determines whether or not the property was added
+ * to the object instance. Returns false if the property is not present
+ * in the object, or was inherited from the prototype.
+ *
+ * @deprecated Safari 1.x support has been removed, so this is simply a
+ * wrapper for the native implementation. Use the native implementation
+ * directly instead.
+ *
+ * @TODO Remove in B1
+ *
+ * @method owns
+ * @static
+ * @param o {any} The object being testing
+ * @param p {string} the property to look for
+ * @return {boolean} true if the object has the property on the instance
+ */
+O.owns = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+/**
+ * Executes a function on each item. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method each
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {YUI} the YUI instance
+ */
+O.each = function (o, f, c, proto) {
+ var s = c || Y, i;
+
+ for (i in o) {
+ if (proto || o.hasOwnProperty(i)) {
+ f.call(s, o[i], i, o);
+ }
+ }
+ return Y;
+};
+
+/*
+ * Executes a function on each item, but halts if the
+ * function returns true. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method some
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {boolean} true if any execution of the function returns true, false otherwise
+ */
+// O.some = function (o, f, c, proto) {
+// var s = c || Y, i;
+//
+// for (i in o) {
+// if (proto || o.hasOwnProperty(i)) {
+// if (f.call(s, o[i], i, o)) {
+// return true;
+// }
+// }
+// }
+// return false;
+// };
+
+/**
+ * Retrieves the sub value at the provided path,
+ * from the value object provided.
+ *
+ * @method getValue
+ * @param o The object from which to extract the property value
+ * @param path {Array} A path array, specifying the object traversal path
+ * from which to obtain the sub value.
+ * @return {Any} The value stored in the path, undefined if not found.
+ * Returns the source object if an empty path is provided.
+ */
+O.getValue = function (o, path) {
+ var p=Y.Array(path), l=p.length, i;
+
+ for (i=0; o !== UNDEFINED && i < l; i=i+1) {
+ o = o[p[i]];
+ }
+
+ return o;
+};
+
+/**
+ * Sets the sub-attribute value at the provided path on the
+ * value object. Returns the modified value object, or
+ * undefined if the path is invalid.
+ *
+ * @method setValue
+ * @param o The object on which to set the sub value.
+ * @param path {Array} A path array, specifying the object traversal path
+ * at which to set the sub value.
+ * @param val {Any} The new value for the sub-attribute.
+ * @return {Object} The modified object, with the new sub value set, or
+ * undefined, if the path was invalid.
+ */
+O.setValue = function(o, path, val) {
+
+ var p=Y.Array(path), leafIdx=p.length-1, i, ref=o;
+
+ if (leafIdx >= 0) {
+ for (i=0; ref !== UNDEFINED && i < leafIdx; i=i+1) {
+ ref = ref[p[i]];
+ }
+
+ if (ref !== UNDEFINED) {
+ ref[p[i]] = val;
+ } else {
+ return UNDEFINED;
+ }
+ }
+
+ return o;
+};
+
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * YUI user agent detection.
+ * Do not fork for a browser if it can be avoided. Use feature detection when
+ * you can. Use the user agent as a last resort. UA stores a version
+ * number for the browser engine, 0 otherwise. This value may or may not map
+ * to the version number of the browser using the engine. The value is
+ * presented as a float so that it can easily be used for boolean evaluation
+ * as well as for looking for a particular range of versions. Because of this,
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9
+ * reports 1.8).
+ * @class UA
+ * @static
+ */
+Y.UA = function() {
+
+ var numberfy = function(s) {
+ var c = 0;
+ return parseFloat(s.replace(/\./g, function() {
+ return (c++ == 1) ? '' : '.';
+ }));
+ },
+
+ nav = navigator,
+
+ o = {
+
+ /**
+ * Internet Explorer version number or 0. Example: 6
+ * @property ie
+ * @type float
+ * @static
+ */
+ ie: 0,
+
+ /**
+ * Opera version number or 0. Example: 9.2
+ * @property opera
+ * @type float
+ * @static
+ */
+ opera: 0,
+
+ /**
+ * Gecko engine revision number. Will evaluate to 1 if Gecko
+ * is detected but the revision could not be found. Other browsers
+ * will be 0. Example: 1.8
+ * <pre>
+ * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7
+ * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+ * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+ * Firefox 3 alpha: 1.9a4 <-- Reports 1.9
+ * </pre>
+ * @property gecko
+ * @type float
+ * @static
+ */
+ gecko: 0,
+
+ /**
+ * AppleWebKit version. KHTML browsers that are not WebKit browsers
+ * will evaluate to 1, other browsers 0. Example: 418.9
+ * <pre>
+ * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
+ * latest available for Mac OSX 10.3.
+ * Safari 2.0.2: 416 <-- hasOwnProperty introduced
+ * Safari 2.0.4: 418 <-- preventDefault fixed
+ * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+ * different versions of webkit
+ * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been
+ * updated, but not updated
+ * to the latest patch.
+ * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native SVG
+ * and many major issues fixed).
+ * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic update
+ * from 2.x via the 10.4.11 OS patch
+ * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event.
+ * yahoo.com user agent hack removed.
+ * </pre>
+ * http://en.wikipedia.org/wiki/Safari_(web_browser)#Version_history
+ * @property webkit
+ * @type float
+ * @static
+ */
+ webkit: 0,
+
+ /**
+ * The mobile property will be set to a string containing any relevant
+ * user agent information when a modern mobile browser is detected.
+ * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+ * devices with the WebKit-based browser, and Opera Mini.
+ * @property mobile
+ * @type string
+ * @static
+ */
+ mobile: null,
+
+ /**
+ * Adobe AIR version number or 0. Only populated if webkit is detected.
+ * Example: 1.0
+ * @property air
+ * @type float
+ */
+ air: 0,
+
+ /**
+ * Google Caja version number or 0.
+ * @property caja
+ * @type float
+ */
+ caja: nav.cajaVersion,
+
+ /**
+ * Set to true if the page appears to be in SSL
+ * @property secure
+ * @type boolean
+ * @static
+ */
+ secure: false,
+
+ /**
+ * The operating system. Currently only detecting windows or macintosh
+ * @property os
+ * @type string
+ * @static
+ */
+ os: null
+
+ },
+
+ ua = nav && nav.userAgent,
+
+ loc = Y.config.win.location,
+
+ href = loc && loc.href,
+
+ m;
+
+ o.secure = href && (href.toLowerCase().indexOf("https") === 0);
+
+ if (ua) {
+
+ if ((/windows|win32/i).test(ua)) {
+ o.os = 'windows';
+ } else if ((/macintosh/i).test(ua)) {
+ o.os = 'macintosh';
+ }
+
+ // Modern KHTML browsers should qualify as Safari X-Grade
+ if ((/KHTML/).test(ua)) {
+ o.webkit=1;
+ }
+ // Modern WebKit browsers are at least X-Grade
+ m=ua.match(/AppleWebKit\/([^\s]*)/);
+ if (m&&m[1]) {
+ o.webkit=numberfy(m[1]);
+
+ // Mobile browser check
+ if (/ Mobile\//.test(ua)) {
+ o.mobile = "Apple"; // iPhone or iPod Touch
+ } else {
+ m=ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
+ if (m) {
+ o.mobile = m[0]; // Nokia N-series, Android, webOS, ex: NokiaN95
+ }
+ }
+
+ m=ua.match(/AdobeAIR\/([^\s]*)/);
+ if (m) {
+ o.air = m[0]; // Adobe AIR 1.0 or better
+ }
+
+ }
+
+ if (!o.webkit) { // not webkit
+ // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+ m=ua.match(/Opera[\s\/]([^\s]*)/);
+ if (m&&m[1]) {
+ o.opera=numberfy(m[1]);
+ m=ua.match(/Opera Mini[^;]*/);
+ if (m) {
+ o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+ }
+ } else { // not opera or webkit
+ m=ua.match(/MSIE\s([^;]*)/);
+ if (m&&m[1]) {
+ o.ie=numberfy(m[1]);
+ } else { // not opera, webkit, or ie
+ m=ua.match(/Gecko\/([^\s]*)/);
+ if (m) {
+ o.gecko=1; // Gecko detected, look for revision
+ m=ua.match(/rv:([^\s\)]*)/);
+ if (m&&m[1]) {
+ o.gecko=numberfy(m[1]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return o;
+}();
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var min = ['yui-base'], core, C = Y.config, mods = YUI.Env.mods,
+ extras, i;
+
+ // apply the minimal required functionality
+ Y.use.apply(Y, min);
+
+ Y.log(Y.id + ' initialized', 'info', 'yui');
+
+ if (C.core) {
+ core = C.core;
+ } else {
+ core = [];
+ extras = ['get', 'loader', 'yui-log', 'yui-later'];
+
+ for (i=0; i<extras.length; i++) {
+ if (mods[extras[i]]) {
+ core.push(extras[i]);
+ }
+ }
+ }
+
+ Y.use.apply(Y, core);
+
+})();
+
+
+
+}, '3.0.0' );
+YUI.add('get', function(Y) {
+
+(function() {
+
+/**
+ * Provides a mechanism to fetch remote resources and
+ * insert them into a document.
+ * @module yui
+ * @submodule get
+ */
+
+var ua = Y.UA,
+ L = Y.Lang,
+ // PREFIX = Y.guid(),
+ TYPE_JS = "text/javascript",
+ TYPE_CSS = "text/css",
+ STYLESHEET = "stylesheet";
+
+/**
+ * Fetches and inserts one or more script or link nodes into the document
+ * @class Get
+ * @static
+ */
+Y.Get = function() {
+
+ /**
+ * hash of queues to manage multiple requests
+ * @property queues
+ * @private
+ */
+ var queues={},
+
+ /**
+ * queue index used to generate transaction ids
+ * @property qidx
+ * @type int
+ * @private
+ */
+ qidx=0,
+
+ /**
+ * interal property used to prevent multiple simultaneous purge
+ * processes
+ * @property purging
+ * @type boolean
+ * @private
+ */
+ purging=false,
+
+
+ /**
+ * Generates an HTML element, this is not appended to a document
+ * @method _node
+ * @param type {string} the type of element
+ * @param attr {string} the attributes
+ * @param win {Window} optional window to create the element in
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _node = function(type, attr, win) {
+ var w = win || Y.config.win, d=w.document, n=d.createElement(type),
+ i;
+
+ for (i in attr) {
+ if (attr[i] && attr.hasOwnProperty(i)) {
+ n.setAttribute(i, attr[i]);
+ }
+ }
+
+ return n;
+ },
+
+ /**
+ * Generates a link node
+ * @method _linkNode
+ * @param url {string} the url for the css file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _linkNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_CSS,
+ rel: STYLESHEET,
+ href: url
+ };
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+ return _node("link", o, win);
+ },
+
+ /**
+ * Generates a script node
+ * @method _scriptNode
+ * @param url {string} the url for the script file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _scriptNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_JS,
+ src: url
+ };
+
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+
+ return _node("script", o, win);
+ },
+
+ /**
+ * Removes the nodes for the specified queue
+ * @method _purge
+ * @private
+ */
+ _purge = function(tId) {
+ var q=queues[tId], n, l, d, h, s, i, node, attr;
+ if (q) {
+ n = q.nodes;
+ l = n.length;
+ d = q.win.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, tId);
+ if (s) {
+ h = s.parentNode;
+ }
+ }
+
+ for (i=0; i<l; i=i+1) {
+ node = n[i];
+ if (node.clearAttributes) {
+ node.clearAttributes();
+ } else {
+ // This is a hostile delete
+ // operation attempting to improve
+ // memory performance. As such, the
+ // hasOwnProperty check is intentionally
+ // ommitted.
+ for (attr in node) {
+ delete node[attr];
+ }
+ }
+
+ h.removeChild(node);
+ }
+ }
+ q.nodes = [];
+ },
+
+ /**
+ * Returns the data payload for callback functions
+ * @method _returnData
+ * @private
+ */
+ _returnData = function(q, msg, result) {
+ return {
+ tId: q.tId,
+ win: q.win,
+ data: q.data,
+ nodes: q.nodes,
+ msg: msg,
+ statusText: result,
+ purge: function() {
+ _purge(this.tId);
+ }
+ };
+ },
+
+ /**
+ * The transaction is finished
+ * @method _end
+ * @param id {string} the id of the request
+ * @private
+ */
+ _end = function(id, msg, result) {
+ var q = queues[id], sc;
+ if (q && q.onEnd) {
+ sc = q.context || q;
+ q.onEnd.call(sc, _returnData(q, msg, result));
+ }
+ },
+
+ /*
+ * The request failed, execute fail handler with whatever
+ * was accomplished. There isn't a failure case at the
+ * moment unless you count aborted transactions
+ * @method _fail
+ * @param id {string} the id of the request
+ * @private
+ */
+ _fail = function(id, msg) {
+
+ Y.log("get failure: " + msg, "warn", "get");
+
+ var q = queues[id], sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ // execute failure callback
+ if (q.onFailure) {
+ sc = q.context || q;
+ q.onFailure.call(sc, _returnData(q, msg));
+ }
+
+ _end(id, msg, 'failure');
+ },
+
+ _get = function(nId, tId) {
+ var q = queues[tId],
+ n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
+ if (!n) {
+ _fail(tId, "target node not found: " + nId);
+ }
+
+ return n;
+ },
+
+ /**
+ * The request is complete, so executing the requester's callback
+ * @method _finish
+ * @param id {string} the id of the request
+ * @private
+ */
+ _finish = function(id) {
+ Y.log("Finishing transaction " + id, "info", "get");
+ var q = queues[id], msg, sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+ q.finished = true;
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ // execute success callback
+ if (q.onSuccess) {
+ sc = q.context || q;
+ q.onSuccess.call(sc, _returnData(q));
+ }
+
+ _end(id, msg, 'OK');
+ },
+
+ /**
+ * Timeout detected
+ * @method _timeout
+ * @param id {string} the id of the request
+ * @private
+ */
+ _timeout = function(id) {
+ Y.log("Timeout " + id, "info", "get");
+ var q = queues[id], sc;
+ if (q.onTimeout) {
+ sc = q.context || q;
+ q.onTimeout.call(sc, _returnData(q));
+ }
+
+ _end(id, 'timeout', 'timeout');
+ },
+
+
+ /**
+ * Loads the next item for a given request
+ * @method _next
+ * @param id {string} the id of the request
+ * @param loaded {string} the url that was just loaded, if any
+ * @private
+ */
+ _next = function(id, loaded) {
+
+ Y.log("_next: " + id + ", loaded: " + (loaded || "nothing"), "info", "get");
+
+ var q = queues[id], msg, w, d, h, n, url, s;
+
+ if (q.timer) {
+ // Y.log('cancel timer');
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ if (loaded) {
+ q.url.shift();
+ if (q.varName) {
+ q.varName.shift();
+ }
+ } else {
+ // This is the first pass: make sure the url is an array
+ q.url = (L.isString(q.url)) ? [q.url] : q.url;
+ if (q.varName) {
+ q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
+ }
+ }
+
+ w = q.win;
+ d = w.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.url.length === 0) {
+ _finish(id);
+ return;
+ }
+
+ url = q.url[0];
+
+ // if the url is undefined, this is probably a trailing comma problem in IE
+ if (!url) {
+ q.url.shift();
+ Y.log('skipping empty url');
+ return _next(id);
+ }
+
+ Y.log("attempting to load " + url, "info", "get");
+
+ if (q.timeout) {
+ // Y.log('create timer');
+ // q.timer = L.later(q.timeout, q, _timeout, id);
+ q.timer = setTimeout(function() {
+ _timeout(id);
+ }, q.timeout);
+ }
+
+ if (q.type === "script") {
+ n = _scriptNode(url, w, q.attributes);
+ } else {
+ n = _linkNode(url, w, q.attributes);
+ }
+
+ // track this node's load progress
+ _track(q.type, n, id, url, w, q.url.length);
+
+ // add the node to the queue so we can return it to the user supplied callback
+ q.nodes.push(n);
+
+ // add it to the head or insert it before 'insertBefore'
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, id);
+ if (s) {
+ s.parentNode.insertBefore(n, s);
+ }
+ } else {
+ h.appendChild(n);
+ }
+
+ Y.log("Appending node: " + url, "info", "get");
+
+ // FireFox does not support the onload event for link nodes, so there is
+ // no way to make the css requests synchronous. This means that the css
+ // rules in multiple files could be applied out of order in this browser
+ // if a later request returns before an earlier one. Safari too.
+ if ((ua.webkit || ua.gecko) && q.type === "css") {
+ _next(id, url);
+ }
+ },
+
+ /**
+ * Removes processed queues and corresponding nodes
+ * @method _autoPurge
+ * @private
+ */
+ _autoPurge = function() {
+
+ if (purging) {
+ return;
+ }
+
+ purging = true;
+
+ var i, q;
+
+ for (i in queues) {
+ if (queues.hasOwnProperty(i)) {
+ q = queues[i];
+ if (q.autopurge && q.finished) {
+ _purge(q.tId);
+ delete queues[i];
+ }
+ }
+ }
+
+ purging = false;
+ },
+
+ /**
+ * Saves the state for the request and begins loading
+ * the requested urls
+ * @method queue
+ * @param type {string} the type of node to insert
+ * @param url {string} the url to load
+ * @param opts the hash of options for this request
+ * @private
+ */
+ _queue = function(type, url, opts) {
+
+ opts = opts || {};
+
+ var id = "q" + (qidx++), q,
+ thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
+
+ if (qidx % thresh === 0) {
+ _autoPurge();
+ }
+
+ queues[id] = Y.merge(opts, {
+ tId: id,
+ type: type,
+ url: url,
+ finished: false,
+ nodes: []
+ });
+
+ q = queues[id];
+ q.win = q.win || Y.config.win;
+ q.context = q.context || q;
+ q.autopurge = ("autopurge" in q) ? q.autopurge :
+ (type === "script") ? true : false;
+
+ if (opts.charset) {
+ q.attributes = q.attributes || {};
+ q.attributes.charset = opts.charset;
+ }
+
+ // L.later(0, q, _next, id);
+ setTimeout(function() {
+ _next(id);
+ }, 0);
+
+ return {
+ tId: id
+ };
+ },
+
+ /**
+ * Detects when a node has been loaded. In the case of
+ * script nodes, this does not guarantee that contained
+ * script is ready to use.
+ * @method _track
+ * @param type {string} the type of node to track
+ * @param n {HTMLElement} the node to track
+ * @param id {string} the id of the request
+ * @param url {string} the url that is being loaded
+ * @param win {Window} the targeted window
+ * @param qlength the number of remaining items in the queue,
+ * including this one
+ * @param trackfn {Function} function to execute when finished
+ * the default is _next
+ * @private
+ */
+ _track = function(type, n, id, url, win, qlength, trackfn) {
+ var f = trackfn || _next;
+
+ // IE supports the readystatechange event for script and css nodes
+ // Opera only for script nodes. Opera support onload for script
+ // nodes, but this doesn't fire when there is a load failure.
+ // The onreadystatechange appears to be a better way to respond
+ // to both success and failure.
+ if (ua.ie) {
+ n.onreadystatechange = function() {
+ var rs = this.readyState;
+ if ("loaded" === rs || "complete" === rs) {
+ Y.log(id + " onreadstatechange " + url, "info", "get");
+ n.onreadystatechange = null;
+ f(id, url);
+ }
+ };
+
+ // webkit prior to 3.x is no longer supported
+ } else if (ua.webkit) {
+
+ if (type === "script") {
+ // Safari 3.x supports the load event for script nodes (DOM2)
+ n.addEventListener("load", function() {
+ Y.log(id + " DOM2 onload " + url, "info", "get");
+ f(id, url);
+ });
+ }
+
+ // FireFox and Opera support onload (but not DOM2 in FF) handlers for
+ // script nodes. Opera, but not FF, supports the onload event for link
+ // nodes.
+ } else {
+
+ n.onload = function() {
+ Y.log(id + " onload " + url, "info", "get");
+ f(id, url);
+ };
+
+ n.onerror = function(e) {
+ _fail(id, e + ": " + url);
+ };
+ }
+ };
+
+ return {
+
+ /**
+ * The number of request required before an automatic purge.
+ * Can be configured via the 'purgethreshold' config
+ * property PURGE_THRESH
+ * @static
+ * @type int
+ * @default 20
+ * @private
+ */
+ PURGE_THRESH: 20,
+
+ /**
+ * Called by the the helper for detecting script load in Safari
+ * @method _finalize
+ * @static
+ * @param id {string} the transaction id
+ * @private
+ */
+ _finalize: function(id) {
+ Y.log(id + " finalized ", "info", "get");
+ // L.later(0, null, _finish, id);
+ setTimeout(function() {
+ _finish(id);
+ }, 0);
+ },
+
+ /**
+ * Abort a transaction
+ * @method abort
+ * @static
+ * @param o {string|object} Either the tId or the object returned from
+ * script() or css()
+ */
+ abort: function(o) {
+ var id = (L.isString(o)) ? o : o.tId,
+ q = queues[id];
+ if (q) {
+ Y.log("Aborting " + id, "info", "get");
+ q.aborted = true;
+ }
+ },
+
+ /**
+ * Fetches and inserts one or more script nodes into the head
+ * of the current document or the document in a specified window.
+ *
+ * @method script
+ * @static
+ * @param url {string|string[]} the url or urls to the script(s)
+ * @param opts {object} Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the script(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onTimeout</dt>
+ * <dd>
+ * callback to execute when a timeout occurs.
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onEnd</dt>
+ * <dd>a function that executes when the transaction finishes, regardless of the exit path</dd>
+ * <dt>onFailure</dt>
+ * <dd>
+ * callback to execute when the script load operation fails
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted successfully</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove any nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>autopurge</dt>
+ * <dd>
+ * setting to true will let the utilities cleanup routine purge
+ * the script once loaded
+ * </dd>
+ * <dt>purgethreshold</dt>
+ * <dd>
+ * The number of transaction before autopurge should be initiated
+ * </dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callback when the script(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * </dl>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * <dt>timeout</dt>
+ * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd>
+ * <pre>
+ * Y.Get.script(
+ * ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
+ * "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"], {
+ * onSuccess: function(o) {
+ * this.log("won't cause error because Y is the context");
+ * Y.log(o.data); // foo
+ * Y.log(o.nodes.length === 2) // true
+ * // o.purge(); // optionally remove the script nodes immediately
+ * },
+ * onFailure: function(o) {
+ * Y.log("transaction failed");
+ * },
+ * onTimeout: function(o) {
+ * Y.log("transaction timed out");
+ * },
+ * data: "foo",
+ * timeout: 10000, // 10 second timeout
+ * context: Y, // make the YUI instance
+ * // win: otherframe // target another window/frame
+ * autopurge: true // allow the utility to choose when to remove the nodes
+ * purgetheshold: 1 // purge previous transaction before next transaction
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ script: function(url, opts) {
+ return _queue("script", url, opts);
+ },
+
+ /**
+ * Fetches and inserts one or more css link nodes into the
+ * head of the current document or the document in a specified
+ * window.
+ * @method css
+ * @static
+ * @param url {string} the url or urls to the css file(s)
+ * @param opts Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the css file(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>win</dl>
+ * <dd>the window the link nodes(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callbacks when the nodes(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * </dl>
+ * <pre>
+ * Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+ * </pre>
+ * <pre>
+ * Y.Get.css(
+ * ["http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css",
+ * "http://yui.yahooapis.com/2.3.1/build/logger/assets/skins/sam/logger.css"], {
+ * insertBefore: 'custom-styles' // nodes will be inserted before the specified node
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ css: function(url, opts) {
+ return _queue("css", url, opts);
+ }
+ };
+}();
+
+})();
+
+
+}, '3.0.0' );
+YUI.add('yui-log', function(Y) {
+
+/**
+ * Provides console log capability and exposes a custom event for
+ * console implementations.
+ * @module yui
+ * @submodule yui-log
+ */
+(function() {
+
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1, info: 1, warn: 1, error: 1 },
+ _published;
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var Y = INSTANCE, c = Y.config, bail = false, excl, incl, m, f;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+ excl = c.logExclude;
+ incl = c.logInclude;
+
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (excl && (src in excl)) {
+ bail = 1;
+ }
+ }
+
+ if (!bail) {
+
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (Y.fire && !silent) {
+ if (!_published) {
+ Y.publish(LOGEVENT, {
+ broadcast: 2,
+ emitFacade: 1
+ });
+
+ _published = 1;
+
+ }
+ Y.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
+YUI.add('yui-later', function(Y) {
+
+/**
+ * Provides a setTimeout/setInterval wrapper
+ * @module yui
+ * @submodule yui-later
+ */
+(function() {
+ var L = Y.Lang,
+
+ /**
+ * Executes the supplied function in the context of the supplied
+ * object 'when' milliseconds later. Executes the function a
+ * single time unless periodic is set to true.
+ * @method later
+ * @for YUI
+ * @param when {int} the number of milliseconds to wait until the fn
+ * is executed.
+ * @param o the context object.
+ * @param fn {Function|String} the function to execute or the name of
+ * the method in the 'o' object to execute.
+ * @param data [Array] data that is provided to the function. This accepts
+ * either a single item or an array. If an array is provided, the
+ * function is executed with one parameter for each array item. If
+ * you need to pass a single array parameter, it needs to be wrapped in
+ * an array [myarray].
+ * @param periodic {boolean} if true, executes continuously at supplied
+ * interval until canceled.
+ * @return {object} a timer object. Call the cancel() method on this object to
+ * stop the timer.
+ */
+ later = function(when, o, fn, data, periodic) {
+ when = when || 0;
+ o = o || {};
+ var m=fn, d=Y.Array(data), f, r;
+
+ if (L.isString(fn)) {
+ m = o[fn];
+ }
+
+ if (!m) {
+ Y.log("method undefined");
+ }
+
+ f = function() {
+ m.apply(o, d);
+ };
+
+ r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+ return {
+ id: r,
+ interval: periodic,
+ cancel: function() {
+ if (this.interval) {
+ clearInterval(r);
+ } else {
+ clearTimeout(r);
+ }
+ }
+ };
+ };
+
+ Y.later = later;
+ L.later = later;
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
+
+
+YUI.add('yui', function(Y){}, '3.0.0' ,{use:['yui-base','get','yui-log','yui-later']});
+
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('yui-later', function(Y) {
+
+/**
+ * Provides a setTimeout/setInterval wrapper
+ * @module yui
+ * @submodule yui-later
+ */
+(function() {
+ var L = Y.Lang,
+
+ /**
+ * Executes the supplied function in the context of the supplied
+ * object 'when' milliseconds later. Executes the function a
+ * single time unless periodic is set to true.
+ * @method later
+ * @for YUI
+ * @param when {int} the number of milliseconds to wait until the fn
+ * is executed.
+ * @param o the context object.
+ * @param fn {Function|String} the function to execute or the name of
+ * the method in the 'o' object to execute.
+ * @param data [Array] data that is provided to the function. This accepts
+ * either a single item or an array. If an array is provided, the
+ * function is executed with one parameter for each array item. If
+ * you need to pass a single array parameter, it needs to be wrapped in
+ * an array [myarray].
+ * @param periodic {boolean} if true, executes continuously at supplied
+ * interval until canceled.
+ * @return {object} a timer object. Call the cancel() method on this object to
+ * stop the timer.
+ */
+ later = function(when, o, fn, data, periodic) {
+ when = when || 0;
+ o = o || {};
+ var m=fn, d=Y.Array(data), f, r;
+
+ if (L.isString(fn)) {
+ m = o[fn];
+ }
+
+ if (!m) {
+ Y.log("method undefined");
+ }
+
+ f = function() {
+ m.apply(o, d);
+ };
+
+ r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+ return {
+ id: r,
+ interval: periodic,
+ cancel: function() {
+ if (this.interval) {
+ clearInterval(r);
+ } else {
+ clearTimeout(r);
+ }
+ }
+ };
+ };
+
+ Y.later = later;
+ L.later = later;
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("yui-later",function(A){(function(){var B=A.Lang,C=function(K,E,L,G,H){K=K||0;E=E||{};var F=L,J=A.Array(G),I,D;if(B.isString(L)){F=E[L];}if(!F){}I=function(){F.apply(E,J);};D=(H)?setInterval(I,K):setTimeout(I,K);return{id:D,interval:H,cancel:function(){if(this.interval){clearInterval(D);}else{clearTimeout(D);}}};};A.later=C;B.later=C;})();},"3.0.0",{requires:["yui-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('yui-later', function(Y) {
+
+/**
+ * Provides a setTimeout/setInterval wrapper
+ * @module yui
+ * @submodule yui-later
+ */
+(function() {
+ var L = Y.Lang,
+
+ /**
+ * Executes the supplied function in the context of the supplied
+ * object 'when' milliseconds later. Executes the function a
+ * single time unless periodic is set to true.
+ * @method later
+ * @for YUI
+ * @param when {int} the number of milliseconds to wait until the fn
+ * is executed.
+ * @param o the context object.
+ * @param fn {Function|String} the function to execute or the name of
+ * the method in the 'o' object to execute.
+ * @param data [Array] data that is provided to the function. This accepts
+ * either a single item or an array. If an array is provided, the
+ * function is executed with one parameter for each array item. If
+ * you need to pass a single array parameter, it needs to be wrapped in
+ * an array [myarray].
+ * @param periodic {boolean} if true, executes continuously at supplied
+ * interval until canceled.
+ * @return {object} a timer object. Call the cancel() method on this object to
+ * stop the timer.
+ */
+ later = function(when, o, fn, data, periodic) {
+ when = when || 0;
+ o = o || {};
+ var m=fn, d=Y.Array(data), f, r;
+
+ if (L.isString(fn)) {
+ m = o[fn];
+ }
+
+ if (!m) {
+ }
+
+ f = function() {
+ m.apply(o, d);
+ };
+
+ r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+ return {
+ id: r,
+ interval: periodic,
+ cancel: function() {
+ if (this.interval) {
+ clearInterval(r);
+ } else {
+ clearTimeout(r);
+ }
+ }
+ };
+ };
+
+ Y.later = later;
+ L.later = later;
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('yui-log', function(Y) {
+
+/**
+ * Provides console log capability and exposes a custom event for
+ * console implementations.
+ * @module yui
+ * @submodule yui-log
+ */
+(function() {
+
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1, info: 1, warn: 1, error: 1 },
+ _published;
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var Y = INSTANCE, c = Y.config, bail = false, excl, incl, m, f;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+ excl = c.logExclude;
+ incl = c.logInclude;
+
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (excl && (src in excl)) {
+ bail = 1;
+ }
+ }
+
+ if (!bail) {
+
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (Y.fire && !silent) {
+ if (!_published) {
+ Y.publish(LOGEVENT, {
+ broadcast: 2,
+ emitFacade: 1
+ });
+
+ _published = 1;
+
+ }
+ Y.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add("yui-log",function(A){(function(){var D=A,F="yui:log",B="undefined",C={debug:1,info:1,warn:1,error:1},E;D.log=function(I,Q,G,O){var H=D,P=H.config,K=false,N,L,J,M;if(P.debug){if(G){N=P.logExclude;L=P.logInclude;if(L&&!(G in L)){K=1;}else{if(N&&(G in N)){K=1;}}}if(!K){if(P.useBrowserConsole){J=(G)?G+": "+I:I;if(typeof console!=B&&console.log){M=(Q&&console[Q]&&(Q in C))?Q:"log";console[M](J);}else{if(typeof opera!=B){opera.postError(J);}}}if(H.fire&&!O){if(!E){H.publish(F,{broadcast:2,emitFacade:1});E=1;}H.fire(F,{msg:I,cat:Q,src:G});}}}return H;};D.message=function(){return D.log.apply(D,arguments);};})();},"3.0.0",{requires:["yui-base"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+YUI.add('yui-log', function(Y) {
+
+/**
+ * Provides console log capability and exposes a custom event for
+ * console implementations.
+ * @module yui
+ * @submodule yui-log
+ */
+(function() {
+
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1, info: 1, warn: 1, error: 1 },
+ _published;
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var Y = INSTANCE, c = Y.config, bail = false, excl, incl, m, f;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+ excl = c.logExclude;
+ incl = c.logInclude;
+
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (excl && (src in excl)) {
+ bail = 1;
+ }
+ }
+
+ if (!bail) {
+
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (Y.fire && !silent) {
+ if (!_published) {
+ Y.publish(LOGEVENT, {
+ broadcast: 2,
+ emitFacade: 1
+ });
+
+ _published = 1;
+
+ }
+ Y.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+(function(){var I={},B=new Date().getTime(),A,E,H=function(){if(window.addEventListener){return function(M,L,K,J){M.addEventListener(L,K,(!!J));};}else{if(window.attachEvent){return function(L,K,J){L.attachEvent("on"+K,J);};}else{return function(){};}}}(),F=function(){if(window.removeEventListener){return function(M,L,K,J){M.removeEventListener(L,K,!!J);};}else{if(window.detachEvent){return function(L,K,J){L.detachEvent("on"+K,J);};}else{return function(){};}}}(),D=function(){YUI.Env.windowLoaded=true;YUI.Env.DOMReady=true;F(window,"load",D);},C={"io.xdrReady":1,"io.xdrResponse":1},G=Array.prototype.slice;if(typeof YUI==="undefined"||!YUI){YUI=function(O,N,M,L,J){var K=this,R=arguments,Q,P=R.length;if(!(K instanceof YUI)){return new YUI(O,N,M,L,J);}else{K._init();for(Q=0;Q<P;Q++){K._config(R[Q]);}K._setup();return K;}};}YUI.prototype={_config:function(N){N=N||{};var O=this.config,L,K,J,M;M=O.modules;for(L in N){if(M&&L=="modules"){J=N[L];for(K in J){if(J.hasOwnProperty(K)){M[K]=J[K];}}}else{if(L=="win"){O[L]=N[L].contentWindow||N[L];O.doc=O[L].document;}else{O[L]=N[L];}}}},_init:function(){var J="3.0.0",K=this;if(J.indexOf("@")>-1){J="test";}K.version=J;K.Env={mods:{},cdn:"http://yui.yahooapis.com/"+J+"/build/",bootstrapped:false,_idx:0,_used:{},_attached:{},_yidx:0,_uidx:0,_loaded:{}};K.Env._loaded[J]={};if(YUI.Env){K.Env._yidx=(++YUI.Env._yidx);K.Env._guidp=("yui_"+J+"-"+K.Env._yidx+"-"+B).replace(/\./g,"_");K.id=K.stamp(K);I[K.id]=K;}K.constructor=YUI;K.config={win:window||{},doc:document,debug:true,useBrowserConsole:true,throwFail:true,bootstrap:true,fetchCSS:true,base:function(){var L,M,O,N;M=document.getElementsByTagName("script");for(O=0;O<M.length;O=O+1){N=M[O].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);L=N&&N[1];if(L){break;}}return L||K.Env.cdn;}(),loaderPath:"loader/loader-min.js"};},_setup:function(J){this.use("yui-base");},applyTo:function(P,O,L){if(!(O in C)){this.log(O+": applyTo not allowed","warn","yui");return null;}var K=I[P],N,J,M;if(K){N=O.split(".");J=K;for(M=0;M<N.length;M=M+1){J=J[N[M]];if(!J){this.log("applyTo not found: "+O,"warn","yui");}}return J.apply(K,L);}return null;},add:function(K,M,J,L){YUI.Env.mods[K]={name:K,fn:M,version:J,details:L||{}};return this;},_attach:function(K,O){var T=YUI.Env.mods,L=this.Env._attached,Q,P=K.length,M,N,R,S,J;for(Q=0;Q<P;Q=Q+1){M=K[Q];N=T[M];if(!L[M]&&N){L[M]=true;R=N.details;S=R.requires;J=R.use;if(S){this._attach(this.Array(S));}if(N.fn){N.fn(this);}if(J){this._attach(this.Array(J));}}}},use:function(){if(this._loading){this._useQueue=this._useQueue||new this.Queue();this._useQueue.add(G.call(arguments,0));return this;}var K=this,U=G.call(arguments,0),Z=YUI.Env.mods,b=K.Env._used,V,O=U[0],M=false,X=U[U.length-1],W=K.config.bootstrap,P,R,N,Q=[],J=[],S=K.config.fetchCSS,T=function(d){if(b[d]){return;}var Y=Z[d],c,e,a;if(Y){b[d]=true;e=Y.details.requires;a=Y.details.use;}else{if(!YUI.Env._loaded[K.version][d]){Q.push(d);}else{b[d]=true;}}if(e){if(K.Lang.isString(e)){T(e);}else{for(c=0;c<e.length;c=c+1){T(e[c]);}}}J.push(d);},L;if(typeof X==="function"){U.pop();}else{X=null;}L=function(Y){Y=Y||{success:true,msg:"not dynamic"};if(X){X(K,Y);}if(K.fire){K.fire("yui:load",K,Y);}K._loading=false;if(K._useQueue&&K._useQueue.size()&&!K._loading){K.use.apply(K,K._useQueue.next());}};if(O==="*"){U=[];for(P in Z){if(Z.hasOwnProperty(P)){U.push(P);}}if(X){U.push(X);}return K.use.apply(K,U);}if(K.Loader){M=true;V=new K.Loader(K.config);V.require(U);V.ignoreRegistered=true;V.allowRollup=false;V.calculate(null,(S)?null:"js");U=V.sorted;}N=U.length;for(R=0;R<N;R=R+1){T(U[R]);}N=Q.length;if(N){Q=K.Object.keys(K.Array.hash(Q));}if(W&&N&&K.Loader){K._loading=true;V=new K.Loader(K.config);V.onSuccess=L;V.onFailure=L;V.onTimeout=L;V.context=K;V.attaching=U;V.require((S)?Q:U);V.insert(null,(S)?null:"js");}else{if(W&&N&&K.Get&&!K.Env.bootstrapped){K._loading=true;U=K.Array(arguments,0,true);K.Get.script(K.config.base+K.config.loaderPath,{onEnd:function(){K._loading=false;K.Env.bootstrapped=true;K._attach(["loader"]);K.use.apply(K,U);}});return K;}else{if(N){}K._attach(J);L();}}return K;},namespace:function(){var J=arguments,N=null,L,K,M;for(L=0;L<J.length;L=L+1){M=(""+J[L]).split(".");N=this;for(K=(M[0]=="YAHOO")?1:0;K<M.length;K=K+1){N[M[K]]=N[M[K]]||{};N=N[M[K]];}}return N;},log:function(){},error:function(K,J){if(this.config.throwFail){throw (J||new Error(K));}else{this.message(K,"error");}return this;},guid:function(J){var K=this.Env._guidp+(++this.Env._uidx);return(J)?(J+K):K;},stamp:function(L,M){if(!L){return L;}var J=(typeof L==="string")?L:L._yuid;if(!J){J=this.guid();if(!M){try{L._yuid=J;}catch(K){J=null;}}}return J;}};A=YUI.prototype;for(E in A){YUI[E]=A[E];}YUI._init();H(window,"load",D);YUI.Env.add=H;YUI.Env.remove=F;})();YUI.add("yui-base",function(B){function A(){this._init();this.add.apply(this,arguments);}A.prototype={_init:function(){this._q=[];},next:function(){return this._q.shift();},add:function(){B.Array.each(B.Array(arguments,0,true),function(C){this._q.push(C);},this);return this;},size:function(){return this._q.length;}};B.Queue=A;(function(){B.Lang=B.Lang||{};var R=B.Lang,G="array",I="boolean",D="date",M="error",S="function",H="number",K="null",F="object",O="regexp",N="string",C=Object.prototype.toString,P="undefined",E={"undefined":P,"number":H,"boolean":I,"string":N,"[object Function]":S,"[object RegExp]":O,"[object Array]":G,"[object Date]":D,"[object Error]":M},J=/^\s+|\s+$/g,Q="";R.isArray=function(L){return R.type(L)===G;};R.isBoolean=function(L){return typeof L===I;};R.isFunction=function(L){return R.type(L)===S;};R.isDate=function(L){return R.type(L)===D;};R.isNull=function(L){return L===null;};R.isNumber=function(L){return typeof L===H&&isFinite(L);};R.isObject=function(T,L){return(T&&(typeof T===F||(!L&&R.isFunction(T))))||false;};R.isString=function(L){return typeof L===N;};R.isUndefined=function(L){return typeof L===P;};R.trim=function(L){try{return L.replace(J,Q);}catch(T){return L;}};R.isValue=function(T){var L=R.type(T);
+switch(L){case H:return isFinite(T);case K:case P:return false;default:return !!(L);}};R.type=function(L){return E[typeof L]||E[C.call(L)]||(L?F:K);};})();(function(){var C=B.Lang,D=Array.prototype,E=function(M,J,L){var I=(L)?2:B.Array.test(M),H,G,F;if(I){try{return D.slice.call(M,J||0);}catch(K){F=[];for(H=0,G=M.length;H<G;H=H+1){F.push(M[H]);}return F;}}else{return[M];}};B.Array=E;E.test=function(H){var F=0;if(C.isObject(H)){if(C.isArray(H)){F=1;}else{try{if("length" in H&&!("tagName" in H)&&!("alert" in H)&&(!B.Lang.isFunction(H.size)||H.size()>1)){F=2;}}catch(G){}}}return F;};E.each=(D.forEach)?function(F,G,H){D.forEach.call(F||[],G,H||B);return B;}:function(G,I,J){var F=(G&&G.length)||0,H;for(H=0;H<F;H=H+1){I.call(J||B,G[H],H,G);}return B;};E.hash=function(H,G){var K={},F=H.length,J=G&&G.length,I;for(I=0;I<F;I=I+1){K[H[I]]=(J&&J>I)?G[I]:true;}return K;};E.indexOf=(D.indexOf)?function(F,G){return D.indexOf.call(F,G);}:function(F,H){for(var G=0;G<F.length;G=G+1){if(F[G]===H){return G;}}return -1;};E.numericSort=function(G,F){return(G-F);};E.some=(D.some)?function(F,G,H){return D.some.call(F,G,H);}:function(G,I,J){var F=G.length,H;for(H=0;H<F;H=H+1){if(I.call(J,G[H],H,G)){return true;}}return false;};})();(function(){var D=B.Lang,C="__",E=function(H,G){var F=G.toString;if(D.isFunction(F)&&F!=Object.prototype.toString){H.toString=F;}};B.merge=function(){var G=arguments,I={},H,F=G.length;for(H=0;H<F;H=H+1){B.mix(I,G[H],true);}return I;};B.mix=function(F,O,H,N,L,M){if(!O||!F){return F||B;}if(L){switch(L){case 1:return B.mix(F.prototype,O.prototype,H,N,0,M);case 2:B.mix(F.prototype,O.prototype,H,N,0,M);break;case 3:return B.mix(F,O.prototype,H,N,0,M);case 4:return B.mix(F.prototype,O,H,N,0,M);default:}}var K=M&&D.isArray(F),J,I,G;if(N&&N.length){for(J=0,I=N.length;J<I;++J){G=N[J];if(G in O){if(M&&D.isObject(F[G],true)){B.mix(F[G],O[G]);}else{if(!K&&(H||!(G in F))){F[G]=O[G];}else{if(K){F.push(O[G]);}}}}}}else{for(J in O){if(M&&D.isObject(F[J],true)){B.mix(F[J],O[J]);}else{if(!K&&(H||!(J in F))){F[J]=O[J];}else{if(K){F.push(O[J]);}}}}if(B.UA.ie){E(F,O);}}return F;};B.cached=function(H,F,G){F=F||{};return function(L,K){var J=(K)?Array.prototype.join.call(arguments,C):L,I=F[J];if(!(J in F)||(G&&F[J]==G)){F[J]=H.apply(H,arguments);}return F[J];};};})();(function(){B.Object=function(H){var G=function(){};G.prototype=H;return new G();};var E=B.Object,D=undefined,C=function(J,I){var H=(I===2),F=(H)?0:[],G;for(G in J){if(H){F++;}else{if(J.hasOwnProperty(G)){F.push((I)?J[G]:G);}}}return F;};E.keys=function(F){return C(F);};E.values=function(F){return C(F,1);};E.size=function(F){return C(F,2);};E.hasKey=function(G,F){return(F in G);};E.hasValue=function(G,F){return(B.Array.indexOf(E.values(G),F)>-1);};E.owns=function(G,F){return(G.hasOwnProperty(F));};E.each=function(J,I,K,H){var G=K||B,F;for(F in J){if(H||J.hasOwnProperty(F)){I.call(G,J[F],F,J);}}return B;};E.getValue=function(J,I){var H=B.Array(I),F=H.length,G;for(G=0;J!==D&&G<F;G=G+1){J=J[H[G]];}return J;};E.setValue=function(L,J,K){var I=B.Array(J),H=I.length-1,F,G=L;if(H>=0){for(F=0;G!==D&&F<H;F=F+1){G=G[I[F]];}if(G!==D){G[I[F]]=K;}else{return D;}}return L;};})();B.UA=function(){var F=function(J){var K=0;return parseFloat(J.replace(/\./g,function(){return(K++==1)?"":".";}));},I=navigator,H={ie:0,opera:0,gecko:0,webkit:0,mobile:null,air:0,caja:I.cajaVersion,secure:false,os:null},E=I&&I.userAgent,G=B.config.win.location,D=G&&G.href,C;H.secure=D&&(D.toLowerCase().indexOf("https")===0);if(E){if((/windows|win32/i).test(E)){H.os="windows";}else{if((/macintosh/i).test(E)){H.os="macintosh";}}if((/KHTML/).test(E)){H.webkit=1;}C=E.match(/AppleWebKit\/([^\s]*)/);if(C&&C[1]){H.webkit=F(C[1]);if(/ Mobile\//.test(E)){H.mobile="Apple";}else{C=E.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);if(C){H.mobile=C[0];}}C=E.match(/AdobeAIR\/([^\s]*)/);if(C){H.air=C[0];}}if(!H.webkit){C=E.match(/Opera[\s\/]([^\s]*)/);if(C&&C[1]){H.opera=F(C[1]);C=E.match(/Opera Mini[^;]*/);if(C){H.mobile=C[0];}}else{C=E.match(/MSIE\s([^;]*)/);if(C&&C[1]){H.ie=F(C[1]);}else{C=E.match(/Gecko\/([^\s]*)/);if(C){H.gecko=1;C=E.match(/rv:([^\s\)]*)/);if(C&&C[1]){H.gecko=F(C[1]);}}}}}}return H;}();(function(){var F=["yui-base"],D,I=B.config,H=YUI.Env.mods,G,E;B.use.apply(B,F);if(I.core){D=I.core;}else{D=[];G=["get","loader","yui-log","yui-later"];for(E=0;E<G.length;E++){if(H[G[E]]){D.push(G[E]);}}}B.use.apply(B,D);})();},"3.0.0");YUI.add("get",function(A){(function(){var C=A.UA,B=A.Lang,E="text/javascript",F="text/css",D="stylesheet";A.Get=function(){var M={},K=0,U=false,W=function(a,X,b){var Y=b||A.config.win,c=Y.document,e=c.createElement(a),Z;for(Z in X){if(X[Z]&&X.hasOwnProperty(Z)){e.setAttribute(Z,X[Z]);}}return e;},T=function(Y,Z,X){var a={id:A.guid(),type:F,rel:D,href:Y};if(X){A.mix(a,X);}return W("link",a,Z);},S=function(Y,Z,X){var a={id:A.guid(),type:E,src:Y};if(X){A.mix(a,X);}return W("script",a,Z);},N=function(c){var X=M[c],Y,a,g,e,j,b,Z,f;if(X){Y=X.nodes;a=Y.length;g=X.win.document;e=g.getElementsByTagName("head")[0];if(X.insertBefore){j=L(X.insertBefore,c);if(j){e=j.parentNode;}}for(b=0;b<a;b=b+1){Z=Y[b];if(Z.clearAttributes){Z.clearAttributes();}else{for(f in Z){delete Z[f];}}e.removeChild(Z);}}X.nodes=[];},P=function(Y,Z,X){return{tId:Y.tId,win:Y.win,data:Y.data,nodes:Y.nodes,msg:Z,statusText:X,purge:function(){N(this.tId);}};},O=function(b,a,X){var Y=M[b],Z;if(Y&&Y.onEnd){Z=Y.context||Y;Y.onEnd.call(Z,P(Y,a,X));}},V=function(a,Z){var X=M[a],Y;if(X.timer){clearTimeout(X.timer);}if(X.onFailure){Y=X.context||X;X.onFailure.call(Y,P(X,Z));}O(a,Z,"failure");},L=function(X,a){var Y=M[a],Z=(B.isString(X))?Y.win.document.getElementById(X):X;if(!Z){V(a,"target node not found: "+X);}return Z;},I=function(a){var X=M[a],Z,Y;if(X.timer){clearTimeout(X.timer);}X.finished=true;if(X.aborted){Z="transaction "+a+" was aborted";V(a,Z);return;}if(X.onSuccess){Y=X.context||X;X.onSuccess.call(Y,P(X));}O(a,Z,"OK");},Q=function(Z){var X=M[Z],Y;if(X.onTimeout){Y=X.context||X;X.onTimeout.call(Y,P(X));
+}O(Z,"timeout","timeout");},H=function(Z,c){var Y=M[Z],b,g,f,e,a,X,i;if(Y.timer){clearTimeout(Y.timer);}if(Y.aborted){b="transaction "+Z+" was aborted";V(Z,b);return;}if(c){Y.url.shift();if(Y.varName){Y.varName.shift();}}else{Y.url=(B.isString(Y.url))?[Y.url]:Y.url;if(Y.varName){Y.varName=(B.isString(Y.varName))?[Y.varName]:Y.varName;}}g=Y.win;f=g.document;e=f.getElementsByTagName("head")[0];if(Y.url.length===0){I(Z);return;}X=Y.url[0];if(!X){Y.url.shift();return H(Z);}if(Y.timeout){Y.timer=setTimeout(function(){Q(Z);},Y.timeout);}if(Y.type==="script"){a=S(X,g,Y.attributes);}else{a=T(X,g,Y.attributes);}J(Y.type,a,Z,X,g,Y.url.length);Y.nodes.push(a);if(Y.insertBefore){i=L(Y.insertBefore,Z);if(i){i.parentNode.insertBefore(a,i);}}else{e.appendChild(a);}if((C.webkit||C.gecko)&&Y.type==="css"){H(Z,X);}},G=function(){if(U){return;}U=true;var X,Y;for(X in M){if(M.hasOwnProperty(X)){Y=M[X];if(Y.autopurge&&Y.finished){N(Y.tId);delete M[X];}}}U=false;},R=function(Y,X,Z){Z=Z||{};var c="q"+(K++),a,b=Z.purgethreshold||A.Get.PURGE_THRESH;if(K%b===0){G();}M[c]=A.merge(Z,{tId:c,type:Y,url:X,finished:false,nodes:[]});a=M[c];a.win=a.win||A.config.win;a.context=a.context||a;a.autopurge=("autopurge" in a)?a.autopurge:(Y==="script")?true:false;if(Z.charset){a.attributes=a.attributes||{};a.attributes.charset=Z.charset;}setTimeout(function(){H(c);},0);return{tId:c};},J=function(Z,e,d,Y,c,b,X){var a=X||H;if(C.ie){e.onreadystatechange=function(){var f=this.readyState;if("loaded"===f||"complete"===f){e.onreadystatechange=null;a(d,Y);}};}else{if(C.webkit){if(Z==="script"){e.addEventListener("load",function(){a(d,Y);});}}else{e.onload=function(){a(d,Y);};e.onerror=function(f){V(d,f+": "+Y);};}}};return{PURGE_THRESH:20,_finalize:function(X){setTimeout(function(){I(X);},0);},abort:function(Y){var Z=(B.isString(Y))?Y:Y.tId,X=M[Z];if(X){X.aborted=true;}},script:function(X,Y){return R("script",X,Y);},css:function(X,Y){return R("css",X,Y);}};}();})();},"3.0.0");YUI.add("yui-log",function(A){(function(){var D=A,F="yui:log",B="undefined",C={debug:1,info:1,warn:1,error:1},E;D.log=function(I,Q,G,O){var H=D,P=H.config,K=false,N,L,J,M;if(P.debug){if(G){N=P.logExclude;L=P.logInclude;if(L&&!(G in L)){K=1;}else{if(N&&(G in N)){K=1;}}}if(!K){if(P.useBrowserConsole){J=(G)?G+": "+I:I;if(typeof console!=B&&console.log){M=(Q&&console[Q]&&(Q in C))?Q:"log";console[M](J);}else{if(typeof opera!=B){opera.postError(J);}}}if(H.fire&&!O){if(!E){H.publish(F,{broadcast:2,emitFacade:1});E=1;}H.fire(F,{msg:I,cat:Q,src:G});}}}return H;};D.message=function(){return D.log.apply(D,arguments);};})();},"3.0.0",{requires:["yui-base"]});YUI.add("yui-later",function(A){(function(){var B=A.Lang,C=function(K,E,L,G,H){K=K||0;E=E||{};var F=L,J=A.Array(G),I,D;if(B.isString(L)){F=E[L];}if(!F){}I=function(){F.apply(E,J);};D=(H)?setInterval(I,K):setTimeout(I,K);return{id:D,interval:H,cancel:function(){if(this.interval){clearInterval(D);}else{clearTimeout(D);}}};};A.later=C;B.later=C;})();},"3.0.0",{requires:["yui-base"]});YUI.add("yui",function(A){},"3.0.0",{use:["yui-base","get","yui-log","yui-later"]});
\ No newline at end of file
--- /dev/null
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 3.0.0
+build: 1549
+*/
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var _instances = {},
+ _startTime = new Date().getTime(),
+ p,
+ i,
+
+ add = function () {
+ if (window.addEventListener) {
+ return function(el, type, fn, capture) {
+ el.addEventListener(type, fn, (!!capture));
+ };
+ } else if (window.attachEvent) {
+ return function(el, type, fn) {
+ el.attachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ remove = function() {
+ if (window.removeEventListener) {
+ return function (el, type, fn, capture) {
+ el.removeEventListener(type, fn, !!capture);
+ };
+ } else if (window.detachEvent) {
+ return function (el, type, fn) {
+ el.detachEvent("on" + type, fn);
+ };
+ } else {
+ return function(){};
+ }
+ }(),
+
+ globalListener = function() {
+ YUI.Env.windowLoaded = true;
+ YUI.Env.DOMReady = true;
+ remove(window, 'load', globalListener);
+ },
+
+// @TODO: this needs to be created at build time from module metadata
+
+ _APPLY_TO_WHITE_LIST = {
+ 'io.xdrReady': 1,
+ 'io.xdrResponse':1
+ },
+
+ SLICE = Array.prototype.slice;
+
+// reduce to one or the other
+if (typeof YUI === 'undefined' || !YUI) {
+
+ /**
+ * The YUI global namespace object. If YUI is already defined, the
+ * existing YUI object will not be overwritten so that defined
+ * namespaces are preserved.
+ *
+ * @class YUI
+ * @constructor
+ * @global
+ * @uses EventTarget
+ * @param o* Up to five optional configuration objects. This object is stored
+ * in YUI.config. See config for the list of supported properties.
+ */
+
+ /*global YUI*/
+ // Make a function, disallow direct instantiation
+ YUI = function(o1, o2, o3, o4, o5) {
+
+ var Y = this, a = arguments, i, l = a.length;
+
+ // Allow instantiation without the new operator
+ if (!(Y instanceof YUI)) {
+ return new YUI(o1, o2, o3, o4, o5);
+ } else {
+ // set up the core environment
+ Y._init();
+
+ for (i=0; i<l; i++) {
+ Y._config(a[i]);
+ }
+
+ // bind the specified additional modules for this instance
+ Y._setup();
+
+ return Y;
+ }
+ };
+}
+
+// The prototype contains the functions that are required to allow the external
+// modules to be registered and for the instance to be initialized.
+YUI.prototype = {
+
+ _config: function(o) {
+
+ o = o || {};
+
+ var c = this.config, i, j, m, mods;
+
+ mods = c.modules;
+ for (i in o) {
+ if (mods && i == 'modules') {
+ m = o[i];
+ for (j in m) {
+ if (m.hasOwnProperty(j)) {
+ mods[j] = m[j];
+ }
+ }
+ } else if (i == 'win') {
+ c[i] = o[i].contentWindow || o[i];
+ c.doc = c[i].document;
+ } else {
+ c[i] = o[i];
+ }
+ }
+ },
+
+ /**
+ * Initialize this YUI instance
+ * @private
+ */
+ _init: function() {
+
+ // find targeted window/frame
+ // @TODO create facades
+ var v = '3.0.0', Y = this;
+
+ if (v.indexOf('@') > -1) {
+ v = 'test';
+ }
+
+ Y.version = v;
+
+ Y.Env = {
+ // @todo expand the new module metadata
+ mods: {},
+ cdn: 'http://yui.yahooapis.com/' + v + '/build/',
+ bootstrapped: false,
+ _idx: 0,
+ _used: {},
+ _attached: {},
+ _yidx: 0,
+ _uidx: 0,
+ _loaded: {}
+ };
+
+ Y.Env._loaded[v] = {};
+
+ if (YUI.Env) {
+ Y.Env._yidx = (++YUI.Env._yidx);
+ Y.Env._guidp = ('yui_' + v + '-' + Y.Env._yidx + '-' + _startTime).replace(/\./g, '_');
+ Y.id = Y.stamp(Y);
+ _instances[Y.id] = Y;
+ }
+
+ Y.constructor = YUI;
+
+ // configuration defaults
+ Y.config = {
+
+ win: window || {},
+ doc: document,
+ debug: true,
+ useBrowserConsole: true,
+ throwFail: true,
+ bootstrap: true,
+ fetchCSS: true,
+
+ base: function() {
+ var b, nodes, i, match;
+
+ // get from querystring
+ nodes = document.getElementsByTagName('script');
+
+ for (i=0; i<nodes.length; i=i+1) {
+ match = nodes[i].src.match(/^(.*)yui\/yui[\.\-].*js(\?.*)?$/);
+ b = match && match[1];
+ if (b) {
+ break;
+ }
+ }
+
+ // use CDN default
+ return b || Y.Env.cdn;
+
+ }(),
+
+ loaderPath: 'loader/loader-min.js'
+ };
+
+ },
+
+ /**
+ * Finishes the instance setup. Attaches whatever modules were defined
+ * when the yui modules was registered.
+ * @method _setup
+ * @private
+ */
+ _setup: function(o) {
+ this.use("yui-base");
+ },
+
+ /**
+ * Executes a method on a YUI instance with
+ * the specified id if the specified method is whitelisted.
+ * @method applyTo
+ * @param id {string} the YUI instance id
+ * @param method {string} the name of the method to exectute.
+ * Ex: 'Object.keys'
+ * @param args {Array} the arguments to apply to the method
+ * @return {object} the return value from the applied method or null
+ */
+ applyTo: function(id, method, args) {
+
+ if (!(method in _APPLY_TO_WHITE_LIST)) {
+ this.log(method + ': applyTo not allowed', 'warn', 'yui');
+ return null;
+ }
+
+ var instance = _instances[id], nest, m, i;
+
+ if (instance) {
+
+ nest = method.split('.');
+ m = instance;
+
+ for (i=0; i<nest.length; i=i+1) {
+
+ m = m[nest[i]];
+
+ if (!m) {
+ this.log('applyTo not found: ' + method, 'warn', 'yui');
+ }
+ }
+
+ return m.apply(instance, args);
+ }
+
+ return null;
+ },
+
+ /**
+ * Register a module
+ * @method add
+ * @param name {string} module name
+ * @param fn {Function} entry point into the module that
+ * is used to bind module to the YUI instance
+ * @param version {string} version string
+ * @param details optional config data:
+ * requires - features that should be present before loading
+ * optional - optional features that should be present if load optional defined
+ * use - features that should be attached automatically
+ * skinnable -
+ * rollup
+ * omit - features that should not be loaded if this module is present
+ * @return {YUI} the YUI instance
+ *
+ */
+ add: function(name, fn, version, details) {
+ // this.log('Adding a new component ' + name);
+ // @todo expand this to include version mapping
+ // @todo may want to restore the build property
+ // @todo fire moduleAvailable event
+
+ YUI.Env.mods[name] = {
+ name: name,
+ fn: fn,
+ version: version,
+ details: details || {}
+ };
+
+ return this; // chain support
+ },
+
+ _attach: function(r, fromLoader) {
+
+ var mods = YUI.Env.mods,
+ attached = this.Env._attached,
+ i, l = r.length, name, m, d, req, use;
+
+ for (i=0; i<l; i=i+1) {
+
+ name = r[i];
+ m = mods[name];
+
+ if (!attached[name] && m) {
+
+ attached[name] = true;
+
+ d = m.details;
+ req = d.requires;
+ use = d.use;
+
+ if (req) {
+ this._attach(this.Array(req));
+ }
+
+ // this.log('attaching ' + name, 'info', 'yui');
+
+ if (m.fn) {
+ m.fn(this);
+ }
+
+ if (use) {
+ this._attach(this.Array(use));
+ }
+ }
+ }
+
+ },
+
+ /**
+ * Bind a module to a YUI instance
+ * @param modules* {string} 1-n modules to bind (uses arguments array)
+ * @param *callback {function} callback function executed when
+ * the instance has the required functionality. If included, it
+ * must be the last parameter.
+ *
+ * @TODO
+ * Implement versioning? loader can load different versions?
+ * Should sub-modules/plugins be normal modules, or do
+ * we add syntax for specifying these?
+ *
+ * YUI().use('dragdrop')
+ * YUI().use('dragdrop:2.4.0'); // specific version
+ * YUI().use('dragdrop:2.4.0-'); // at least this version
+ * YUI().use('dragdrop:2.4.0-2.9999.9999'); // version range
+ * YUI().use('*'); // use all available modules
+ * YUI().use('lang+dump+substitute'); // use lang and some plugins
+ * YUI().use('lang+*'); // use lang and all known plugins
+ *
+ *
+ * @return {YUI} the YUI instance
+ */
+ use: function() {
+
+ if (this._loading) {
+ this._useQueue = this._useQueue || new this.Queue();
+ this._useQueue.add(SLICE.call(arguments, 0));
+ return this;
+ }
+
+ var Y = this,
+ a=SLICE.call(arguments, 0),
+ mods = YUI.Env.mods,
+ used = Y.Env._used,
+ loader,
+ firstArg = a[0],
+ dynamic = false,
+ callback = a[a.length-1],
+ boot = Y.config.bootstrap,
+ k, i, l, missing = [],
+ r = [],
+ css = Y.config.fetchCSS,
+ f = function(name) {
+
+ // only attach a module once
+ if (used[name]) {
+ return;
+ }
+
+ var m = mods[name], j, req, use;
+
+ if (m) {
+
+
+ used[name] = true;
+
+ req = m.details.requires;
+ use = m.details.use;
+ } else {
+
+ // CSS files don't register themselves, see if it has been loaded
+ if (!YUI.Env._loaded[Y.version][name]) {
+ missing.push(name);
+ } else {
+ // probably css
+ used[name] = true;
+ }
+ }
+
+ // make sure requirements are attached
+ if (req) {
+ if (Y.Lang.isString(req)) {
+ f(req);
+ } else {
+ for (j = 0; j < req.length; j = j + 1) {
+ f(req[j]);
+ }
+ }
+ }
+
+ // add this module to full list of things to attach
+ r.push(name);
+
+ },
+
+ onComplete;
+
+
+ // The last argument supplied to use can be a load complete callback
+ if (typeof callback === 'function') {
+ a.pop();
+ } else {
+ callback = null;
+ }
+
+ onComplete = function(fromLoader) {
+
+
+ fromLoader = fromLoader || {
+ success: true,
+ msg: 'not dynamic'
+ };
+
+ if (callback) {
+ callback(Y, fromLoader);
+ }
+
+ if (Y.fire) {
+ Y.fire('yui:load', Y, fromLoader);
+ }
+
+ // process queued use requests as long until done
+ // or dynamic load happens again.
+ Y._loading = false;
+
+ if (Y._useQueue && Y._useQueue.size() && !Y._loading) {
+ Y.use.apply(Y, Y._useQueue.next());
+ }
+ };
+
+
+ // YUI().use('*'); // bind everything available
+ if (firstArg === "*") {
+ a = [];
+ for (k in mods) {
+ if (mods.hasOwnProperty(k)) {
+ a.push(k);
+ }
+ }
+
+ if (callback) {
+ a.push(callback);
+ }
+
+ return Y.use.apply(Y, a);
+ }
+
+
+ // use loader to expand dependencies and sort the
+ // requirements if it is available.
+ if (Y.Loader) {
+ dynamic = true;
+ loader = new Y.Loader(Y.config);
+ loader.require(a);
+ loader.ignoreRegistered = true;
+ loader.allowRollup = false;
+ // loader.calculate(null, (css && css == 'force') ? null : 'js');
+ // loader.calculate();
+ loader.calculate(null, (css) ? null : 'js');
+ a = loader.sorted;
+ }
+
+
+ l = a.length;
+
+ // process each requirement and any additional requirements
+ // the module metadata specifies
+ for (i=0; i<l; i=i+1) {
+ f(a[i]);
+ }
+
+ l = missing.length;
+
+
+ if (l) {
+ missing = Y.Object.keys(Y.Array.hash(missing));
+ }
+
+ // dynamic load
+ if (boot && l && Y.Loader) {
+ Y._loading = true;
+ loader = new Y.Loader(Y.config);
+ loader.onSuccess = onComplete;
+ loader.onFailure = onComplete;
+ loader.onTimeout = onComplete;
+ loader.context = Y;
+ loader.attaching = a;
+ // loader.require(missing);
+ loader.require((css) ? missing : a);
+ loader.insert(null, (css) ? null : 'js');
+ } else if (boot && l && Y.Get && !Y.Env.bootstrapped) {
+ Y._loading = true;
+
+ a = Y.Array(arguments, 0, true);
+ // a.unshift('loader');
+
+ Y.Get.script(Y.config.base + Y.config.loaderPath, {
+ onEnd: function() {
+ Y._loading = false;
+ Y.Env.bootstrapped = true;
+ Y._attach(['loader']);
+ Y.use.apply(Y, a);
+ }
+ });
+
+ return Y;
+
+ } else {
+ if (l) {
+ }
+ Y._attach(r);
+ onComplete();
+ }
+
+ return Y; // chain support var yui = YUI().use('dragdrop');
+ },
+
+
+ /**
+ * Returns the namespace specified and creates it if it doesn't exist
+ * <pre>
+ * YUI.namespace("property.package");
+ * YUI.namespace("YAHOO.property.package");
+ * </pre>
+ * Either of the above would create YUI.property, then
+ * YUI.property.package (YAHOO is scrubbed out, this is
+ * to remain compatible with YUI2)
+ *
+ * Be careful when naming packages. Reserved words may work in some browsers
+ * and not others. For instance, the following will fail in Safari:
+ * <pre>
+ * YUI.namespace("really.long.nested.namespace");
+ * </pre>
+ * This fails because "long" is a future reserved word in ECMAScript
+ *
+ * @method namespace
+ * @param {string*} arguments 1-n namespaces to create
+ * @return {object} A reference to the last namespace object created
+ */
+ namespace: function() {
+ var a=arguments, o=null, i, j, d;
+ for (i=0; i<a.length; i=i+1) {
+ d = ("" + a[i]).split(".");
+ o = this;
+ for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) {
+ o[d[j]] = o[d[j]] || {};
+ o = o[d[j]];
+ }
+ }
+ return o;
+ },
+
+ // this is replaced if the log module is included
+ log: function() {
+
+ },
+
+ /**
+ * Report an error. The reporting mechanism is controled by
+ * the 'throwFail' configuration attribute. If throwFail is
+ * not specified, the message is written to the Logger, otherwise
+ * a JS error is thrown
+ * @method error
+ * @param msg {string} the error message
+ * @param e {Error} Optional JS error that was caught. If supplied
+ * and throwFail is specified, this error will be re-thrown.
+ * @return {YUI} this YUI instance
+ */
+ error: function(msg, e) {
+ if (this.config.throwFail) {
+ throw (e || new Error(msg));
+ } else {
+ this.message(msg, "error"); // don't scrub this one
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate an id that is unique among all YUI instances
+ * @method guid
+ * @param pre {string} optional guid prefix
+ * @return {string} the guid
+ */
+ guid: function(pre) {
+ var id = this.Env._guidp + (++this.Env._uidx);
+ return (pre) ? (pre + id) : id;
+ },
+
+ /**
+ * Returns a guid associated with an object. If the object
+ * does not have one, a new one is created unless readOnly
+ * is specified.
+ * @method stamp
+ * @param o The object to stamp
+ * @param readOnly {boolean} if true, a valid guid will only
+ * be returned if the object has one assigned to it.
+ * @return {string} The object's guid or null
+ */
+ stamp: function(o, readOnly) {
+
+ if (!o) {
+ return o;
+ }
+
+ var uid = (typeof o === 'string') ? o : o._yuid;
+
+ if (!uid) {
+ uid = this.guid();
+ if (!readOnly) {
+ try {
+ o._yuid = uid;
+ } catch(e) {
+ uid = null;
+ }
+ }
+ }
+
+ return uid;
+ }
+};
+
+// Give the YUI global the same properties as an instance.
+// This makes it so that the YUI global can be used like the YAHOO
+// global was used prior to 3.x. More importantly, the YUI global
+// provides global metadata, so env needs to be configured.
+// @TODO review
+
+ p = YUI.prototype;
+
+ // inheritance utilities are not available yet
+ for (i in p) {
+ // if (1) { // intenionally ignoring hasOwnProperty check
+ YUI[i] = p[i];
+ // }
+ }
+
+ // set up the environment
+ YUI._init();
+
+ // add a window load event at load time so we can capture
+ // the case where it fires before dynamic loading is
+ // complete.
+ add(window, 'load', globalListener);
+
+ YUI.Env.add = add;
+ YUI.Env.remove = remove;
+
+ /*
+ * Subscribe to an event. The signature differs depending on the
+ * type of event you are attaching to.
+ * @method on
+ * @param type {string|function|object} The type of the event. If
+ * this is a function, this is dispatched to the aop system. If an
+ * object, it is parsed for multiple subsription definitions
+ * @param fn {Function} The callback
+ * @param elspec {any} DOM element(s), selector string(s), and or
+ * Node ref(s) to attach DOM related events to (only applies to
+ * DOM events).
+ * @param
+ * @return the event target or a detach handle per 'chain' config
+ */
+
+})();
+
+/**
+ * The config object contains all of the configuration options for
+ * the YUI instance. This object is supplied by the implementer
+ * when instantiating a YUI instance. Some properties have default
+ * values if they are not supplied by the implementer.
+ *
+ * @class config
+ * @static
+ */
+
+/**
+ * Allows the YUI seed file to fetch the loader component and library
+ * metadata to dynamically load additional dependencies.
+ *
+ * @property bootstrap
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * Log to the browser console if debug is on and the browser has a
+ * supported console.
+ *
+ * @property useBrowserConsole
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * A hash of log sources that should be logged. If specified, only log messages from these sources will be logged.
+ *
+ * @property logInclude
+ * @type object
+ */
+
+/**
+ * A hash of log sources that should be not be logged. If specified, all sources are logged if not on this list.
+ *
+ * @property logExclude
+ * @type object
+ */
+
+/**
+ * Set to true if the yui seed file was dynamically loaded in
+ * order to bootstrap components relying on the window load event
+ * and the 'domready' custom event.
+ *
+ * @property injected
+ * @type object
+ */
+
+/**
+ * If throwFail is set, Y.fail will generate or re-throw a JS Error. Otherwise the failure is logged.
+ *
+ * @property throwFail
+ * @type boolean
+ * @default true
+ */
+
+/**
+ * The window/frame that this instance should operate in.
+ *
+ * @property win
+ * @type Window
+ * @default the window hosting YUI
+ */
+
+/**
+ * The document associated with the 'win' configuration.
+ *
+ * @property doc
+ * @type Document
+ * @default the document hosting YUI
+ */
+
+/**
+ * A list of modules that defines the YUI core (overrides the default).
+ *
+ * @property core
+ * @type string[]
+ */
+
+/**
+ * The default date format
+ *
+ * @property dateFormat
+ * @type string
+ */
+
+/**
+ * The default locale
+ *
+ * @property locale
+ * @type string
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property pollInterval
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The number of dynamic nodes to insert by default before
+ * automatically removing them. This applies to script nodes
+ * because remove the node will not make the evaluated script
+ * unavailable. Dynamic CSS is not auto purged, because removing
+ * a linked style sheet will also remove the style definitions.
+ *
+ * @property purgethreshold
+ * @type int
+ * @default 20
+ */
+
+/**
+ * The default interval when polling in milliseconds.
+ *
+ * @property windowResizeDelay
+ * @type int
+ * @default 40
+ */
+
+/**
+ * Base directory for dynamic loading
+ *
+ * @property base
+ * @type string
+ */
+
+/**
+ * The secure base dir (not implemented)
+ *
+ * For dynamic loading.
+ *
+ * @property secureBase
+ * @type string
+ */
+
+/**
+ * The YUI combo service base dir. Ex: http://yui.yahooapis.com/combo?
+ *
+ * For dynamic loading.
+ *
+ * @property comboBase
+ * @type string
+ */
+
+/**
+ * The root path to prepend to module names for the combo service. Ex: 3.0.0b1/build/
+ *
+ * For dynamic loading.
+ *
+ * @property root
+ * @type string
+ */
+
+/**
+ * A filter to apply to result urls. This filter will modify the default
+ * path for all modules. The default path for the YUI library is the
+ * minified version of the files (e.g., event-min.js). The filter property
+ * can be a predefined filter or a custom filter. The valid predefined
+ * filters are:
+ * <dl>
+ * <dt>DEBUG</dt>
+ * <dd>Selects the debug versions of the library (e.g., event-debug.js).
+ * This option will automatically include the Logger widget</dd>
+ * <dt>RAW</dt>
+ * <dd>Selects the non-minified version of the library (e.g., event.js).</dd>
+ * </dl>
+ * You can also define a custom filter, which must be an object literal
+ * containing a search expression and a replace string:
+ * <pre>
+ * myFilter: {
+ * 'searchExp': "-min\\.js",
+ * 'replaceStr': "-debug.js"
+ * }
+ * </pre>
+ *
+ * For dynamic loading.
+ *
+ * @property filter
+ * @type string|object
+ */
+
+/**
+ * Hash of per-component filter specification. If specified for a given component,
+ * this overrides the filter config
+ *
+ * For dynamic loading.
+ *
+ * @property filters
+ * @type object
+ */
+
+/**
+ * Use the YUI combo service to reduce the number of http connections
+ * required to load your dependencies.
+ *
+ * For dynamic loading.
+ *
+ * @property combine
+ * @type boolean
+ * @default true if 'base' is not supplied, false if it is.
+ */
+
+/**
+ * A list of modules that should never be dynamically loaded
+ *
+ * @property ignore
+ * @type string[]
+ */
+
+/**
+ * A list of modules that should always be loaded when required, even if already
+ * present on the page.
+ *
+ * @property force
+ * @type string[]
+ */
+
+/**
+ * Node or id for a node that should be used as the insertion point for new nodes
+ * For dynamic loading.
+ *
+ * @property insertBefore
+ * @type string
+ */
+
+/**
+ * charset for dynamic nodes
+ *
+ * @property charset
+ * @type string
+ * @deprecated use jsAttributes cssAttributes
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded script nodes.
+ *
+ * @property jsAttributes
+ * @type string
+ */
+
+/**
+ * Object literal containing attributes to add to dynamically loaded link nodes.
+ *
+ * @property cssAttributes
+ * @type string
+ */
+
+/**
+ * Number of milliseconds before a timeout occurs when dynamically
+ * loading nodes. If not set, there is no timeout.
+ *
+ * @property timeout
+ * @type int
+ */
+
+/**
+ * Callback for the 'CSSComplete' event. When dynamically loading YUI
+ * components with CSS, this property fires when the CSS is finished
+ * loading but script loading is still ongoing. This provides an
+ * opportunity to enhance the presentation of a loading page a little
+ * bit before the entire loading process is done.
+ *
+ * @property onCSS
+ * @type function
+ */
+
+/**
+ * A list of module definitions to add to the list of YUI components.
+ * These components can then be dynamically loaded side by side with
+ * YUI via the use() method.See Loader.addModule for the supported
+ * module metadata.
+ *
+ * @property modules
+ * @type function
+ */
+
+/**
+ * The loader 'path' attribute to the loader itself. This is combined
+ * with the 'base' attribute to dynamically load the loader component
+ * when boostrapping with the get utility alone.
+ *
+ * @property loaderPath
+ * @default loader/loader-min.js
+ */
+
+/**
+ *
+ * Specifies whether or not YUI().use(...) will attempt to load CSS
+ * resources at all. Any truthy value will cause CSS dependencies
+ * to load when fetching script. The special value 'force' will
+ * cause CSS dependencies to be loaded even if no script is needed.
+ *
+ * @property fetchCSS
+ * @default true
+ */
+YUI.add('yui-base', function(Y) {
+
+/*
+ * YUI stub
+ * @module yui
+ * @submodule yui-base
+ */
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * A simple FIFO queue. Items are added to the Queue with add(1..n items) and
+ * removed using next().
+ *
+ * @class Queue
+ * @param item* {MIXED} 0..n items to seed the queue
+ */
+function Queue() {
+ this._init();
+ this.add.apply(this, arguments);
+}
+
+Queue.prototype = {
+ /**
+ * Initialize the queue
+ *
+ * @method _init
+ * @protected
+ */
+ _init : function () {
+ /**
+ * The collection of enqueued items
+ *
+ * @property _q
+ * @type {Array}
+ * @protected
+ */
+ this._q = [];
+ },
+
+ /**
+ * Get the next item in the queue.
+ *
+ * @method next
+ * @return {MIXED} the next item in the queue
+ */
+ next : function () {
+ return this._q.shift();
+ },
+
+ /**
+ * Add 0..n items to the end of the queue
+ *
+ * @method add
+ * @param item* {MIXED} 0..n items
+ */
+ add : function () {
+ Y.Array.each(Y.Array(arguments,0,true),function (fn) {
+ this._q.push(fn);
+ },this);
+
+ return this;
+ },
+
+ /**
+ * Returns the current number of queued items
+ *
+ * @method size
+ * @return {Number}
+ */
+ size : function () {
+ return this._q.length;
+ }
+};
+
+Y.Queue = Queue;
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+/**
+ * Provides the language utilites and extensions used by the library
+ * @class Lang
+ * @static
+ */
+Y.Lang = Y.Lang || {};
+
+var L = Y.Lang,
+
+ARRAY = 'array',
+BOOLEAN = 'boolean',
+DATE = 'date',
+ERROR = 'error',
+FUNCTION = 'function',
+NUMBER = 'number',
+NULL = 'null',
+OBJECT = 'object',
+REGEX = 'regexp',
+STRING = 'string',
+TOSTRING = Object.prototype.toString,
+UNDEFINED = 'undefined',
+
+TYPES = {
+ 'undefined' : UNDEFINED,
+ 'number' : NUMBER,
+ 'boolean' : BOOLEAN,
+ 'string' : STRING,
+ '[object Function]' : FUNCTION,
+ '[object RegExp]' : REGEX,
+ '[object Array]' : ARRAY,
+ '[object Date]' : DATE,
+ '[object Error]' : ERROR
+},
+
+TRIMREGEX = /^\s+|\s+$/g,
+EMPTYSTRING = '';
+
+/**
+ * Determines whether or not the provided item is an array.
+ * Returns false for array-like collections such as the
+ * function arguments collection or HTMLElement collection
+ * will return false. You can use @see Array.test if you
+ * want to
+ * @method isArray
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is an array
+ */
+L.isArray = function(o) {
+ return L.type(o) === ARRAY;
+};
+
+/**
+ * Determines whether or not the provided item is a boolean
+ * @method isBoolean
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a boolean
+ */
+L.isBoolean = function(o) {
+ return typeof o === BOOLEAN;
+};
+
+/**
+ * Determines whether or not the provided item is a function
+ * Note: Internet Explorer thinks certain functions are objects:
+ *
+ * var obj = document.createElement("object");
+ * Y.Lang.isFunction(obj.getAttribute) // reports false in IE
+ *
+ * var input = document.createElement("input"); // append to body
+ * Y.Lang.isFunction(input.focus) // reports false in IE
+ *
+ * You will have to implement additional tests if these functions
+ * matter to you.
+ *
+ * @method isFunction
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a function
+ */
+L.isFunction = function(o) {
+ return L.type(o) === FUNCTION;
+};
+
+/**
+ * Determines whether or not the supplied item is a date instance
+ * @method isDate
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a date
+ */
+L.isDate = function(o) {
+ // return o instanceof Date;
+ return L.type(o) === DATE;
+};
+
+/**
+ * Determines whether or not the provided item is null
+ * @method isNull
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is null
+ */
+L.isNull = function(o) {
+ return o === null;
+};
+
+/**
+ * Determines whether or not the provided item is a legal number
+ * @method isNumber
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a number
+ */
+L.isNumber = function(o) {
+ return typeof o === NUMBER && isFinite(o);
+};
+
+/**
+ * Determines whether or not the provided item is of type object
+ * or function
+ * @method isObject
+ * @static
+ * @param o The object to test
+ * @param failfn {boolean} fail if the input is a function
+ * @return {boolean} true if o is an object
+ */
+L.isObject = function(o, failfn) {
+return (o && (typeof o === OBJECT || (!failfn && L.isFunction(o)))) || false;
+};
+
+/**
+ * Determines whether or not the provided item is a string
+ * @method isString
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is a string
+ */
+L.isString = function(o) {
+ return typeof o === STRING;
+};
+
+/**
+ * Determines whether or not the provided item is undefined
+ * @method isUndefined
+ * @static
+ * @param o The object to test
+ * @return {boolean} true if o is undefined
+ */
+L.isUndefined = function(o) {
+ return typeof o === UNDEFINED;
+};
+
+/**
+ * Returns a string without any leading or trailing whitespace. If
+ * the input is not a string, the input will be returned untouched.
+ * @method trim
+ * @static
+ * @param s {string} the string to trim
+ * @return {string} the trimmed string
+ */
+L.trim = function(s){
+ try {
+ return s.replace(TRIMREGEX, EMPTYSTRING);
+ } catch(e) {
+ return s;
+ }
+};
+
+/**
+ * A convenience method for detecting a legitimate non-null value.
+ * Returns false for null/undefined/NaN, true for other values,
+ * including 0/false/''
+ * @method isValue
+ * @static
+ * @param o The item to test
+ * @return {boolean} true if it is not null/undefined/NaN || false
+ */
+L.isValue = function(o) {
+ var t = L.type(o);
+ switch (t) {
+ case NUMBER:
+ return isFinite(o);
+ case NULL:
+ case UNDEFINED:
+ return false;
+ default:
+ return !!(t);
+ }
+};
+
+/**
+ * Returns a string representing the type of the item passed in.
+ * @method type
+ * @param o the item to test
+ * @return {string} the detected type
+ */
+L.type = function (o) {
+ return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? OBJECT : NULL);
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang, Native = Array.prototype,
+
+/**
+ * Adds the following array utilities to the YUI instance. Additional
+ * array helpers can be found in the collection component.
+ * @class Array
+ */
+
+/**
+ * Y.Array(o) returns an array:
+ * - Arrays are return unmodified unless the start position is specified.
+ * - "Array-like" collections (@see Array.test) are converted to arrays
+ * - For everything else, a new array is created with the input as the sole item
+ * - The start position is used if the input is or is like an array to return
+ * a subset of the collection.
+ *
+ * @TODO this will not automatically convert elements that are also collections
+ * such as forms and selects. Passing true as the third param will
+ * force a conversion.
+ *
+ * @method ()
+ * @static
+ * @param o the item to arrayify
+ * @param i {int} if an array or array-like, this is the start index
+ * @param al {boolean} if true, it forces the array-like fork. This
+ * can be used to avoid multiple array.test calls.
+ * @return {Array} the resulting array
+ */
+YArray = function(o, startIdx, al) {
+ var t = (al) ? 2 : Y.Array.test(o), i, l, a;
+
+ // switch (t) {
+ // case 1:
+ // // return (startIdx) ? o.slice(startIdx) : o;
+ // case 2:
+ // return Native.slice.call(o, startIdx || 0);
+ // default:
+ // return [o];
+ // }
+
+ if (t) {
+ try {
+ return Native.slice.call(o, startIdx || 0);
+ // IE errors when trying to slice element collections
+ } catch(e) {
+ a=[];
+ for (i=0, l=o.length; i<l; i=i+1) {
+ a.push(o[i]);
+ }
+ return a;
+ }
+ } else {
+ return [o];
+ }
+
+};
+
+Y.Array = YArray;
+
+/**
+ * Evaluates the input to determine if it is an array, array-like, or
+ * something else. This is used to handle the arguments collection
+ * available within functions, and HTMLElement collections
+ *
+ * @method test
+ * @static
+ *
+ * @todo current implementation (intenionally) will not implicitly
+ * handle html elements that are array-like (forms, selects, etc).
+ *
+ * @return {int} a number indicating the results:
+ * 0: Not an array or an array-like collection
+ * 1: A real array.
+ * 2: array-like collection.
+ */
+YArray.test = function(o) {
+ var r = 0;
+ if (L.isObject(o)) {
+ if (L.isArray(o)) {
+ r = 1;
+ } else {
+ try {
+ // indexed, but no tagName (element) or alert (window)
+ if ("length" in o && !("tagName" in o) && !("alert" in o) &&
+ (!Y.Lang.isFunction(o.size) || o.size() > 1)) {
+ r = 2;
+ }
+
+ } catch(e) {}
+ }
+ }
+ return r;
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * @method each
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The
+ * function receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {YUI} the YUI instance
+ */
+YArray.each = (Native.forEach) ?
+ function (a, f, o) {
+ Native.forEach.call(a || [], f, o || Y);
+ return Y;
+ } :
+ function (a, f, o) {
+ var l = (a && a.length) || 0, i;
+ for (i = 0; i < l; i=i+1) {
+ f.call(o || Y, a[i], i, a);
+ }
+ return Y;
+ };
+
+/**
+ * Returns an object using the first array as keys, and
+ * the second as values. If the second array is not
+ * provided the value is set to true for each.
+ * @method hash
+ * @static
+ * @param k {Array} keyset
+ * @param v {Array} optional valueset
+ * @return {object} the hash
+ */
+YArray.hash = function(k, v) {
+ var o = {}, l = k.length, vl = v && v.length, i;
+ for (i=0; i<l; i=i+1) {
+ o[k[i]] = (vl && vl > i) ? v[i] : true;
+ }
+
+ return o;
+};
+
+/**
+ * Returns the index of the first item in the array
+ * that contains the specified value, -1 if the
+ * value isn't found.
+ * @method indexOf
+ * @static
+ * @param a {Array} the array to search
+ * @param val the value to search for
+ * @return {int} the index of the item that contains the value or -1
+ */
+YArray.indexOf = (Native.indexOf) ?
+ function(a, val) {
+ return Native.indexOf.call(a, val);
+ } :
+ function(a, val) {
+ for (var i=0; i<a.length; i=i+1) {
+ if (a[i] === val) {
+ return i;
+ }
+ }
+
+ return -1;
+ };
+
+/**
+ * Numeric sort convenience function.
+ * Y.ArrayAssert.itemsAreEqual([1, 2, 3], [3, 1, 2].sort(Y.Array.numericSort));
+ * @method numericSort
+ */
+YArray.numericSort = function(a, b) {
+ return (a - b);
+};
+
+/**
+ * Executes the supplied function on each item in the array.
+ * Returning true from the processing function will stop the
+ * processing of the remaining
+ * items.
+ * @method some
+ * @param a {Array} the array to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the index, the full array.
+ * @param o Optional context object
+ * @static
+ * @return {boolean} true if the function returns true on
+ * any of the items in the array
+ */
+ YArray.some = (Native.some) ?
+ function (a, f, o) {
+ return Native.some.call(a, f, o);
+ } :
+ function (a, f, o) {
+ var l = a.length, i;
+ for (i=0; i<l; i=i+1) {
+ if (f.call(o, a[i], i, a)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+})();
+
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+var L = Y.Lang,
+DELIMITER = '__',
+// FROZEN = {
+// 'prototype': 1,
+// '_yuid': 1
+// },
+
+/*
+ * IE will not enumerate native functions in a derived object even if the
+ * function was overridden. This is a workaround for specific functions
+ * we care about on the Object prototype.
+ * @property _iefix
+ * @for YUI
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @private
+ */
+_iefix = function(r, s) {
+ var fn = s.toString;
+ if (L.isFunction(fn) && fn != Object.prototype.toString) {
+ r.toString = fn;
+ }
+};
+
+
+/**
+ * Returns a new object containing all of the properties of
+ * all the supplied objects. The properties from later objects
+ * will overwrite those in earlier objects. Passing in a
+ * single object will create a shallow copy of it. For a deep
+ * copy, use clone.
+ * @method merge
+ * @for YUI
+ * @param arguments {Object*} the objects to merge
+ * @return {object} the new merged object
+ */
+Y.merge = function() {
+ var a = arguments, o = {}, i, l = a.length;
+ for (i=0; i<l; i=i+1) {
+ Y.mix(o, a[i], true);
+ }
+ return o;
+};
+
+/**
+ * Applies the supplier's properties to the receiver. By default
+ * all prototype and static propertes on the supplier are applied
+ * to the corresponding spot on the receiver. By default all
+ * properties are applied, and a property that is already on the
+ * reciever will not be overwritten. The default behavior can
+ * be modified by supplying the appropriate parameters.
+ *
+ * @TODO add constants for the modes
+ *
+ * @method mix
+ * @param {Function} r the object to receive the augmentation
+ * @param {Function} s the object that supplies the properties to augment
+ * @param ov {boolean} if true, properties already on the receiver
+ * will be overwritten if found on the supplier.
+ * @param wl {string[]} a whitelist. If supplied, only properties in
+ * this list will be applied to the receiver.
+ * @param {int} mode what should be copies, and to where
+ * default(0): object to object
+ * 1: prototype to prototype (old augment)
+ * 2: prototype to prototype and object props (new augment)
+ * 3: prototype to object
+ * 4: object to prototype
+ * @param merge {boolean} merge objects instead of overwriting/ignoring
+ * Used by Y.aggregate
+ * @return {object} the augmented object
+ */
+Y.mix = function(r, s, ov, wl, mode, merge) {
+
+ if (!s||!r) {
+ return r || Y;
+ }
+
+ if (mode) {
+ switch (mode) {
+ case 1: // proto to proto
+ return Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ case 2: // object to object and proto to proto
+ Y.mix(r.prototype, s.prototype, ov, wl, 0, merge);
+ break; // pass through
+ case 3: // proto to static
+ return Y.mix(r, s.prototype, ov, wl, 0, merge);
+ case 4: // static to proto
+ return Y.mix(r.prototype, s, ov, wl, 0, merge);
+ default: // object to object is what happens below
+ }
+ }
+
+ // Maybe don't even need this wl && wl.length check anymore??
+ var arr = merge && L.isArray(r), i, l, p;
+
+ if (wl && wl.length) {
+ for (i = 0, l = wl.length; i < l; ++i) {
+ p = wl[i];
+ if (p in s) {
+ if (merge && L.isObject(r[p], true)) {
+ Y.mix(r[p], s[p]);
+ } else if (!arr && (ov || !(p in r))) {
+ r[p] = s[p];
+ } else if (arr) {
+ r.push(s[p]);
+ }
+ }
+ }
+ } else {
+ for (i in s) {
+ // if (s.hasOwnProperty(i) && !(i in FROZEN)) {
+ // check white list if it was supplied
+ // if the receiver has this property, it is an object,
+ // and merge is specified, merge the two objects.
+ if (merge && L.isObject(r[i], true)) {
+ Y.mix(r[i], s[i]); // recursive
+ // otherwise apply the property only if overwrite
+ // is specified or the receiver doesn't have one.
+ } else if (!arr && (ov || !(i in r))) {
+ r[i] = s[i];
+ // if merge is specified and the receiver is an array,
+ // append the array item
+ } else if (arr) {
+ r.push(s[i]);
+ }
+ // }
+ }
+
+ if (Y.UA.ie) {
+ _iefix(r, s);
+ }
+ }
+
+ return r;
+};
+
+/**
+ * Returns a wrapper for a function which caches the
+ * return value of that function, keyed off of the combined
+ * argument values.
+ * @function cached
+ * @param source {function} the function to memoize
+ * @param cache an optional cache seed
+ * @param refetch if supplied, this value is tested against the cached
+ * value. If the values are equal, the wrapped function is executed again.
+ * @return {Function} the wrapped function
+ */
+Y.cached = function(source, cache, refetch){
+ cache = cache || {};
+
+ return function(arg1, arg2) {
+
+ var k = (arg2) ? Array.prototype.join.call(arguments, DELIMITER) : arg1,
+ v = cache[k];
+
+ if (!(k in cache) || (refetch && cache[k] == refetch)) {
+ cache[k] = source.apply(source, arguments);
+ }
+
+ return cache[k];
+ };
+
+};
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+(function() {
+
+/**
+ * Adds the following Object utilities to the YUI instance
+ * @class Object
+ */
+
+/**
+ * Y.Object(o) returns a new object based upon the supplied object.
+ * @TODO Use native Object.create() when available
+ * @method ()
+ * @static
+ * @param o the supplier object
+ * @return {Object} the new object
+ */
+Y.Object = function(o) {
+ var F = function() {};
+ F.prototype = o;
+ return new F();
+};
+
+var O = Y.Object,
+
+UNDEFINED = undefined,
+
+/**
+ * Extracts the keys, values, or size from an object
+ *
+ * @method _extract
+ * @param o the object
+ * @param what what to extract (0: keys, 1: values, 2: size)
+ * @return {boolean|Array} the extracted info
+ * @static
+ * @private
+ */
+_extract = function(o, what) {
+ var count = (what === 2), out = (count) ? 0 : [], i;
+
+ for (i in o) {
+ if (count) {
+ out++;
+ } else {
+ if (o.hasOwnProperty(i)) {
+ out.push((what) ? o[i] : i);
+ }
+ }
+ }
+
+ return out;
+};
+
+/**
+ * Returns an array containing the object's keys
+ * @TODO use native Object.keys() if available
+ * @method keys
+ * @static
+ * @param o an object
+ * @return {string[]} the keys
+ */
+O.keys = function(o) {
+ return _extract(o);
+};
+
+/**
+ * Returns an array containing the object's values
+ * @TODO use native Object.values() if available
+ * @method values
+ * @static
+ * @param o an object
+ * @return {Array} the values
+ */
+O.values = function(o) {
+ return _extract(o, 1);
+};
+
+/**
+ * Returns the size of an object
+ * @TODO use native Object.size() if available
+ * @method size
+ * @static
+ * @param o an object
+ * @return {int} the size
+ */
+O.size = function(o) {
+ return _extract(o, 2);
+};
+
+/**
+ * Returns true if the object contains a given key
+ * @method hasKey
+ * @static
+ * @param o an object
+ * @param k the key to query
+ * @return {boolean} true if the object contains the key
+ */
+O.hasKey = function(o, k) {
+ // return (o.hasOwnProperty(k));
+ return (k in o);
+};
+
+/**
+ * Returns true if the object contains a given value
+ * @method hasValue
+ * @static
+ * @param o an object
+ * @param v the value to query
+ * @return {boolean} true if the object contains the value
+ */
+O.hasValue = function(o, v) {
+ return (Y.Array.indexOf(O.values(o), v) > -1);
+};
+
+/**
+ * Determines whether or not the property was added
+ * to the object instance. Returns false if the property is not present
+ * in the object, or was inherited from the prototype.
+ *
+ * @deprecated Safari 1.x support has been removed, so this is simply a
+ * wrapper for the native implementation. Use the native implementation
+ * directly instead.
+ *
+ * @TODO Remove in B1
+ *
+ * @method owns
+ * @static
+ * @param o {any} The object being testing
+ * @param p {string} the property to look for
+ * @return {boolean} true if the object has the property on the instance
+ */
+O.owns = function(o, k) {
+ return (o.hasOwnProperty(k));
+};
+
+/**
+ * Executes a function on each item. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method each
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {YUI} the YUI instance
+ */
+O.each = function (o, f, c, proto) {
+ var s = c || Y, i;
+
+ for (i in o) {
+ if (proto || o.hasOwnProperty(i)) {
+ f.call(s, o[i], i, o);
+ }
+ }
+ return Y;
+};
+
+/*
+ * Executes a function on each item, but halts if the
+ * function returns true. The function
+ * receives the value, the key, and the object
+ * as paramters (in that order).
+ * @method some
+ * @static
+ * @param o the object to iterate
+ * @param f {Function} the function to execute on each item. The function
+ * receives three arguments: the value, the the key, the full object.
+ * @param c the execution context
+ * @param proto {boolean} include proto
+ * @return {boolean} true if any execution of the function returns true, false otherwise
+ */
+// O.some = function (o, f, c, proto) {
+// var s = c || Y, i;
+//
+// for (i in o) {
+// if (proto || o.hasOwnProperty(i)) {
+// if (f.call(s, o[i], i, o)) {
+// return true;
+// }
+// }
+// }
+// return false;
+// };
+
+/**
+ * Retrieves the sub value at the provided path,
+ * from the value object provided.
+ *
+ * @method getValue
+ * @param o The object from which to extract the property value
+ * @param path {Array} A path array, specifying the object traversal path
+ * from which to obtain the sub value.
+ * @return {Any} The value stored in the path, undefined if not found.
+ * Returns the source object if an empty path is provided.
+ */
+O.getValue = function (o, path) {
+ var p=Y.Array(path), l=p.length, i;
+
+ for (i=0; o !== UNDEFINED && i < l; i=i+1) {
+ o = o[p[i]];
+ }
+
+ return o;
+};
+
+/**
+ * Sets the sub-attribute value at the provided path on the
+ * value object. Returns the modified value object, or
+ * undefined if the path is invalid.
+ *
+ * @method setValue
+ * @param o The object on which to set the sub value.
+ * @param path {Array} A path array, specifying the object traversal path
+ * at which to set the sub value.
+ * @param val {Any} The new value for the sub-attribute.
+ * @return {Object} The modified object, with the new sub value set, or
+ * undefined, if the path was invalid.
+ */
+O.setValue = function(o, path, val) {
+
+ var p=Y.Array(path), leafIdx=p.length-1, i, ref=o;
+
+ if (leafIdx >= 0) {
+ for (i=0; ref !== UNDEFINED && i < leafIdx; i=i+1) {
+ ref = ref[p[i]];
+ }
+
+ if (ref !== UNDEFINED) {
+ ref[p[i]] = val;
+ } else {
+ return UNDEFINED;
+ }
+ }
+
+ return o;
+};
+
+
+})();
+
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+/**
+ * YUI user agent detection.
+ * Do not fork for a browser if it can be avoided. Use feature detection when
+ * you can. Use the user agent as a last resort. UA stores a version
+ * number for the browser engine, 0 otherwise. This value may or may not map
+ * to the version number of the browser using the engine. The value is
+ * presented as a float so that it can easily be used for boolean evaluation
+ * as well as for looking for a particular range of versions. Because of this,
+ * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9
+ * reports 1.8).
+ * @class UA
+ * @static
+ */
+Y.UA = function() {
+
+ var numberfy = function(s) {
+ var c = 0;
+ return parseFloat(s.replace(/\./g, function() {
+ return (c++ == 1) ? '' : '.';
+ }));
+ },
+
+ nav = navigator,
+
+ o = {
+
+ /**
+ * Internet Explorer version number or 0. Example: 6
+ * @property ie
+ * @type float
+ * @static
+ */
+ ie: 0,
+
+ /**
+ * Opera version number or 0. Example: 9.2
+ * @property opera
+ * @type float
+ * @static
+ */
+ opera: 0,
+
+ /**
+ * Gecko engine revision number. Will evaluate to 1 if Gecko
+ * is detected but the revision could not be found. Other browsers
+ * will be 0. Example: 1.8
+ * <pre>
+ * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7
+ * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
+ * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
+ * Firefox 3 alpha: 1.9a4 <-- Reports 1.9
+ * </pre>
+ * @property gecko
+ * @type float
+ * @static
+ */
+ gecko: 0,
+
+ /**
+ * AppleWebKit version. KHTML browsers that are not WebKit browsers
+ * will evaluate to 1, other browsers 0. Example: 418.9
+ * <pre>
+ * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the
+ * latest available for Mac OSX 10.3.
+ * Safari 2.0.2: 416 <-- hasOwnProperty introduced
+ * Safari 2.0.4: 418 <-- preventDefault fixed
+ * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
+ * different versions of webkit
+ * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been
+ * updated, but not updated
+ * to the latest patch.
+ * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native SVG
+ * and many major issues fixed).
+ * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic update
+ * from 2.x via the 10.4.11 OS patch
+ * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event.
+ * yahoo.com user agent hack removed.
+ * </pre>
+ * http://en.wikipedia.org/wiki/Safari_(web_browser)#Version_history
+ * @property webkit
+ * @type float
+ * @static
+ */
+ webkit: 0,
+
+ /**
+ * The mobile property will be set to a string containing any relevant
+ * user agent information when a modern mobile browser is detected.
+ * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
+ * devices with the WebKit-based browser, and Opera Mini.
+ * @property mobile
+ * @type string
+ * @static
+ */
+ mobile: null,
+
+ /**
+ * Adobe AIR version number or 0. Only populated if webkit is detected.
+ * Example: 1.0
+ * @property air
+ * @type float
+ */
+ air: 0,
+
+ /**
+ * Google Caja version number or 0.
+ * @property caja
+ * @type float
+ */
+ caja: nav.cajaVersion,
+
+ /**
+ * Set to true if the page appears to be in SSL
+ * @property secure
+ * @type boolean
+ * @static
+ */
+ secure: false,
+
+ /**
+ * The operating system. Currently only detecting windows or macintosh
+ * @property os
+ * @type string
+ * @static
+ */
+ os: null
+
+ },
+
+ ua = nav && nav.userAgent,
+
+ loc = Y.config.win.location,
+
+ href = loc && loc.href,
+
+ m;
+
+ o.secure = href && (href.toLowerCase().indexOf("https") === 0);
+
+ if (ua) {
+
+ if ((/windows|win32/i).test(ua)) {
+ o.os = 'windows';
+ } else if ((/macintosh/i).test(ua)) {
+ o.os = 'macintosh';
+ }
+
+ // Modern KHTML browsers should qualify as Safari X-Grade
+ if ((/KHTML/).test(ua)) {
+ o.webkit=1;
+ }
+ // Modern WebKit browsers are at least X-Grade
+ m=ua.match(/AppleWebKit\/([^\s]*)/);
+ if (m&&m[1]) {
+ o.webkit=numberfy(m[1]);
+
+ // Mobile browser check
+ if (/ Mobile\//.test(ua)) {
+ o.mobile = "Apple"; // iPhone or iPod Touch
+ } else {
+ m=ua.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/);
+ if (m) {
+ o.mobile = m[0]; // Nokia N-series, Android, webOS, ex: NokiaN95
+ }
+ }
+
+ m=ua.match(/AdobeAIR\/([^\s]*)/);
+ if (m) {
+ o.air = m[0]; // Adobe AIR 1.0 or better
+ }
+
+ }
+
+ if (!o.webkit) { // not webkit
+ // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
+ m=ua.match(/Opera[\s\/]([^\s]*)/);
+ if (m&&m[1]) {
+ o.opera=numberfy(m[1]);
+ m=ua.match(/Opera Mini[^;]*/);
+ if (m) {
+ o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
+ }
+ } else { // not opera or webkit
+ m=ua.match(/MSIE\s([^;]*)/);
+ if (m&&m[1]) {
+ o.ie=numberfy(m[1]);
+ } else { // not opera, webkit, or ie
+ m=ua.match(/Gecko\/([^\s]*)/);
+ if (m) {
+ o.gecko=1; // Gecko detected, look for revision
+ m=ua.match(/rv:([^\s\)]*)/);
+ if (m&&m[1]) {
+ o.gecko=numberfy(m[1]);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return o;
+}();
+/**
+ * The YUI module contains the components required for building the YUI seed file.
+ * This includes the script loading mechanism, a simple queue, and the core utilities for the library.
+ * @module yui
+ * @submodule yui-base
+ */
+
+(function() {
+
+ var min = ['yui-base'], core, C = Y.config, mods = YUI.Env.mods,
+ extras, i;
+
+ // apply the minimal required functionality
+ Y.use.apply(Y, min);
+
+
+ if (C.core) {
+ core = C.core;
+ } else {
+ core = [];
+ extras = ['get', 'loader', 'yui-log', 'yui-later'];
+
+ for (i=0; i<extras.length; i++) {
+ if (mods[extras[i]]) {
+ core.push(extras[i]);
+ }
+ }
+ }
+
+ Y.use.apply(Y, core);
+
+})();
+
+
+
+}, '3.0.0' );
+YUI.add('get', function(Y) {
+
+(function() {
+
+/**
+ * Provides a mechanism to fetch remote resources and
+ * insert them into a document.
+ * @module yui
+ * @submodule get
+ */
+
+var ua = Y.UA,
+ L = Y.Lang,
+ // PREFIX = Y.guid(),
+ TYPE_JS = "text/javascript",
+ TYPE_CSS = "text/css",
+ STYLESHEET = "stylesheet";
+
+/**
+ * Fetches and inserts one or more script or link nodes into the document
+ * @class Get
+ * @static
+ */
+Y.Get = function() {
+
+ /**
+ * hash of queues to manage multiple requests
+ * @property queues
+ * @private
+ */
+ var queues={},
+
+ /**
+ * queue index used to generate transaction ids
+ * @property qidx
+ * @type int
+ * @private
+ */
+ qidx=0,
+
+ /**
+ * interal property used to prevent multiple simultaneous purge
+ * processes
+ * @property purging
+ * @type boolean
+ * @private
+ */
+ purging=false,
+
+
+ /**
+ * Generates an HTML element, this is not appended to a document
+ * @method _node
+ * @param type {string} the type of element
+ * @param attr {string} the attributes
+ * @param win {Window} optional window to create the element in
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _node = function(type, attr, win) {
+ var w = win || Y.config.win, d=w.document, n=d.createElement(type),
+ i;
+
+ for (i in attr) {
+ if (attr[i] && attr.hasOwnProperty(i)) {
+ n.setAttribute(i, attr[i]);
+ }
+ }
+
+ return n;
+ },
+
+ /**
+ * Generates a link node
+ * @method _linkNode
+ * @param url {string} the url for the css file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _linkNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_CSS,
+ rel: STYLESHEET,
+ href: url
+ };
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+ return _node("link", o, win);
+ },
+
+ /**
+ * Generates a script node
+ * @method _scriptNode
+ * @param url {string} the url for the script file
+ * @param win {Window} optional window to create the node in
+ * @param attributes optional attributes collection to apply to the new node
+ * @return {HTMLElement} the generated node
+ * @private
+ */
+ _scriptNode = function(url, win, attributes) {
+ var o = {
+ id: Y.guid(),
+ type: TYPE_JS,
+ src: url
+ };
+
+ if (attributes) {
+ Y.mix(o, attributes);
+ }
+
+ return _node("script", o, win);
+ },
+
+ /**
+ * Removes the nodes for the specified queue
+ * @method _purge
+ * @private
+ */
+ _purge = function(tId) {
+ var q=queues[tId], n, l, d, h, s, i, node, attr;
+ if (q) {
+ n = q.nodes;
+ l = n.length;
+ d = q.win.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, tId);
+ if (s) {
+ h = s.parentNode;
+ }
+ }
+
+ for (i=0; i<l; i=i+1) {
+ node = n[i];
+ if (node.clearAttributes) {
+ node.clearAttributes();
+ } else {
+ // This is a hostile delete
+ // operation attempting to improve
+ // memory performance. As such, the
+ // hasOwnProperty check is intentionally
+ // ommitted.
+ for (attr in node) {
+ delete node[attr];
+ }
+ }
+
+ h.removeChild(node);
+ }
+ }
+ q.nodes = [];
+ },
+
+ /**
+ * Returns the data payload for callback functions
+ * @method _returnData
+ * @private
+ */
+ _returnData = function(q, msg, result) {
+ return {
+ tId: q.tId,
+ win: q.win,
+ data: q.data,
+ nodes: q.nodes,
+ msg: msg,
+ statusText: result,
+ purge: function() {
+ _purge(this.tId);
+ }
+ };
+ },
+
+ /**
+ * The transaction is finished
+ * @method _end
+ * @param id {string} the id of the request
+ * @private
+ */
+ _end = function(id, msg, result) {
+ var q = queues[id], sc;
+ if (q && q.onEnd) {
+ sc = q.context || q;
+ q.onEnd.call(sc, _returnData(q, msg, result));
+ }
+ },
+
+ /*
+ * The request failed, execute fail handler with whatever
+ * was accomplished. There isn't a failure case at the
+ * moment unless you count aborted transactions
+ * @method _fail
+ * @param id {string} the id of the request
+ * @private
+ */
+ _fail = function(id, msg) {
+
+
+ var q = queues[id], sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ // execute failure callback
+ if (q.onFailure) {
+ sc = q.context || q;
+ q.onFailure.call(sc, _returnData(q, msg));
+ }
+
+ _end(id, msg, 'failure');
+ },
+
+ _get = function(nId, tId) {
+ var q = queues[tId],
+ n = (L.isString(nId)) ? q.win.document.getElementById(nId) : nId;
+ if (!n) {
+ _fail(tId, "target node not found: " + nId);
+ }
+
+ return n;
+ },
+
+ /**
+ * The request is complete, so executing the requester's callback
+ * @method _finish
+ * @param id {string} the id of the request
+ * @private
+ */
+ _finish = function(id) {
+ var q = queues[id], msg, sc;
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+ q.finished = true;
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ // execute success callback
+ if (q.onSuccess) {
+ sc = q.context || q;
+ q.onSuccess.call(sc, _returnData(q));
+ }
+
+ _end(id, msg, 'OK');
+ },
+
+ /**
+ * Timeout detected
+ * @method _timeout
+ * @param id {string} the id of the request
+ * @private
+ */
+ _timeout = function(id) {
+ var q = queues[id], sc;
+ if (q.onTimeout) {
+ sc = q.context || q;
+ q.onTimeout.call(sc, _returnData(q));
+ }
+
+ _end(id, 'timeout', 'timeout');
+ },
+
+
+ /**
+ * Loads the next item for a given request
+ * @method _next
+ * @param id {string} the id of the request
+ * @param loaded {string} the url that was just loaded, if any
+ * @private
+ */
+ _next = function(id, loaded) {
+
+
+ var q = queues[id], msg, w, d, h, n, url, s;
+
+ if (q.timer) {
+ // q.timer.cancel();
+ clearTimeout(q.timer);
+ }
+
+ if (q.aborted) {
+ msg = "transaction " + id + " was aborted";
+ _fail(id, msg);
+ return;
+ }
+
+ if (loaded) {
+ q.url.shift();
+ if (q.varName) {
+ q.varName.shift();
+ }
+ } else {
+ // This is the first pass: make sure the url is an array
+ q.url = (L.isString(q.url)) ? [q.url] : q.url;
+ if (q.varName) {
+ q.varName = (L.isString(q.varName)) ? [q.varName] : q.varName;
+ }
+ }
+
+ w = q.win;
+ d = w.document;
+ h = d.getElementsByTagName("head")[0];
+
+ if (q.url.length === 0) {
+ _finish(id);
+ return;
+ }
+
+ url = q.url[0];
+
+ // if the url is undefined, this is probably a trailing comma problem in IE
+ if (!url) {
+ q.url.shift();
+ return _next(id);
+ }
+
+
+ if (q.timeout) {
+ // q.timer = L.later(q.timeout, q, _timeout, id);
+ q.timer = setTimeout(function() {
+ _timeout(id);
+ }, q.timeout);
+ }
+
+ if (q.type === "script") {
+ n = _scriptNode(url, w, q.attributes);
+ } else {
+ n = _linkNode(url, w, q.attributes);
+ }
+
+ // track this node's load progress
+ _track(q.type, n, id, url, w, q.url.length);
+
+ // add the node to the queue so we can return it to the user supplied callback
+ q.nodes.push(n);
+
+ // add it to the head or insert it before 'insertBefore'
+ if (q.insertBefore) {
+ s = _get(q.insertBefore, id);
+ if (s) {
+ s.parentNode.insertBefore(n, s);
+ }
+ } else {
+ h.appendChild(n);
+ }
+
+
+ // FireFox does not support the onload event for link nodes, so there is
+ // no way to make the css requests synchronous. This means that the css
+ // rules in multiple files could be applied out of order in this browser
+ // if a later request returns before an earlier one. Safari too.
+ if ((ua.webkit || ua.gecko) && q.type === "css") {
+ _next(id, url);
+ }
+ },
+
+ /**
+ * Removes processed queues and corresponding nodes
+ * @method _autoPurge
+ * @private
+ */
+ _autoPurge = function() {
+
+ if (purging) {
+ return;
+ }
+
+ purging = true;
+
+ var i, q;
+
+ for (i in queues) {
+ if (queues.hasOwnProperty(i)) {
+ q = queues[i];
+ if (q.autopurge && q.finished) {
+ _purge(q.tId);
+ delete queues[i];
+ }
+ }
+ }
+
+ purging = false;
+ },
+
+ /**
+ * Saves the state for the request and begins loading
+ * the requested urls
+ * @method queue
+ * @param type {string} the type of node to insert
+ * @param url {string} the url to load
+ * @param opts the hash of options for this request
+ * @private
+ */
+ _queue = function(type, url, opts) {
+
+ opts = opts || {};
+
+ var id = "q" + (qidx++), q,
+ thresh = opts.purgethreshold || Y.Get.PURGE_THRESH;
+
+ if (qidx % thresh === 0) {
+ _autoPurge();
+ }
+
+ queues[id] = Y.merge(opts, {
+ tId: id,
+ type: type,
+ url: url,
+ finished: false,
+ nodes: []
+ });
+
+ q = queues[id];
+ q.win = q.win || Y.config.win;
+ q.context = q.context || q;
+ q.autopurge = ("autopurge" in q) ? q.autopurge :
+ (type === "script") ? true : false;
+
+ if (opts.charset) {
+ q.attributes = q.attributes || {};
+ q.attributes.charset = opts.charset;
+ }
+
+ // L.later(0, q, _next, id);
+ setTimeout(function() {
+ _next(id);
+ }, 0);
+
+ return {
+ tId: id
+ };
+ },
+
+ /**
+ * Detects when a node has been loaded. In the case of
+ * script nodes, this does not guarantee that contained
+ * script is ready to use.
+ * @method _track
+ * @param type {string} the type of node to track
+ * @param n {HTMLElement} the node to track
+ * @param id {string} the id of the request
+ * @param url {string} the url that is being loaded
+ * @param win {Window} the targeted window
+ * @param qlength the number of remaining items in the queue,
+ * including this one
+ * @param trackfn {Function} function to execute when finished
+ * the default is _next
+ * @private
+ */
+ _track = function(type, n, id, url, win, qlength, trackfn) {
+ var f = trackfn || _next;
+
+ // IE supports the readystatechange event for script and css nodes
+ // Opera only for script nodes. Opera support onload for script
+ // nodes, but this doesn't fire when there is a load failure.
+ // The onreadystatechange appears to be a better way to respond
+ // to both success and failure.
+ if (ua.ie) {
+ n.onreadystatechange = function() {
+ var rs = this.readyState;
+ if ("loaded" === rs || "complete" === rs) {
+ n.onreadystatechange = null;
+ f(id, url);
+ }
+ };
+
+ // webkit prior to 3.x is no longer supported
+ } else if (ua.webkit) {
+
+ if (type === "script") {
+ // Safari 3.x supports the load event for script nodes (DOM2)
+ n.addEventListener("load", function() {
+ f(id, url);
+ });
+ }
+
+ // FireFox and Opera support onload (but not DOM2 in FF) handlers for
+ // script nodes. Opera, but not FF, supports the onload event for link
+ // nodes.
+ } else {
+
+ n.onload = function() {
+ f(id, url);
+ };
+
+ n.onerror = function(e) {
+ _fail(id, e + ": " + url);
+ };
+ }
+ };
+
+ return {
+
+ /**
+ * The number of request required before an automatic purge.
+ * Can be configured via the 'purgethreshold' config
+ * property PURGE_THRESH
+ * @static
+ * @type int
+ * @default 20
+ * @private
+ */
+ PURGE_THRESH: 20,
+
+ /**
+ * Called by the the helper for detecting script load in Safari
+ * @method _finalize
+ * @static
+ * @param id {string} the transaction id
+ * @private
+ */
+ _finalize: function(id) {
+ // L.later(0, null, _finish, id);
+ setTimeout(function() {
+ _finish(id);
+ }, 0);
+ },
+
+ /**
+ * Abort a transaction
+ * @method abort
+ * @static
+ * @param o {string|object} Either the tId or the object returned from
+ * script() or css()
+ */
+ abort: function(o) {
+ var id = (L.isString(o)) ? o : o.tId,
+ q = queues[id];
+ if (q) {
+ q.aborted = true;
+ }
+ },
+
+ /**
+ * Fetches and inserts one or more script nodes into the head
+ * of the current document or the document in a specified window.
+ *
+ * @method script
+ * @static
+ * @param url {string|string[]} the url or urls to the script(s)
+ * @param opts {object} Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the script(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onTimeout</dt>
+ * <dd>
+ * callback to execute when a timeout occurs.
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>onEnd</dt>
+ * <dd>a function that executes when the transaction finishes, regardless of the exit path</dd>
+ * <dt>onFailure</dt>
+ * <dd>
+ * callback to execute when the script load operation fails
+ * The callback receives an object back with the following
+ * data:
+ * <dl>
+ * <dt>win</dt>
+ * <dd>the window the script(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted successfully</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove any nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>autopurge</dt>
+ * <dd>
+ * setting to true will let the utilities cleanup routine purge
+ * the script once loaded
+ * </dd>
+ * <dt>purgethreshold</dt>
+ * <dd>
+ * The number of transaction before autopurge should be initiated
+ * </dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callback when the script(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * </dl>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * <dt>timeout</dt>
+ * <dd>Number of milliseconds to wait before aborting and firing the timeout event</dd>
+ * <pre>
+ * Y.Get.script(
+ * ["http://yui.yahooapis.com/2.5.2/build/yahoo/yahoo-min.js",
+ * "http://yui.yahooapis.com/2.5.2/build/event/event-min.js"], {
+ * onSuccess: function(o) {
+ * this.log("won't cause error because Y is the context");
+ * },
+ * onFailure: function(o) {
+ * },
+ * onTimeout: function(o) {
+ * },
+ * data: "foo",
+ * timeout: 10000, // 10 second timeout
+ * context: Y, // make the YUI instance
+ * // win: otherframe // target another window/frame
+ * autopurge: true // allow the utility to choose when to remove the nodes
+ * purgetheshold: 1 // purge previous transaction before next transaction
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ script: function(url, opts) {
+ return _queue("script", url, opts);
+ },
+
+ /**
+ * Fetches and inserts one or more css link nodes into the
+ * head of the current document or the document in a specified
+ * window.
+ * @method css
+ * @static
+ * @param url {string} the url or urls to the css file(s)
+ * @param opts Options:
+ * <dl>
+ * <dt>onSuccess</dt>
+ * <dd>
+ * callback to execute when the css file(s) are finished loading
+ * The callback receives an object back with the following
+ * data:
+ * <dl>win</dl>
+ * <dd>the window the link nodes(s) were inserted into</dd>
+ * <dt>data</dt>
+ * <dd>the data object passed in when the request was made</dd>
+ * <dt>nodes</dt>
+ * <dd>An array containing references to the nodes that were
+ * inserted</dd>
+ * <dt>purge</dt>
+ * <dd>A function that, when executed, will remove the nodes
+ * that were inserted</dd>
+ * <dt>
+ * </dl>
+ * </dd>
+ * <dt>context</dt>
+ * <dd>the execution context for the callbacks</dd>
+ * <dt>win</dt>
+ * <dd>a window other than the one the utility occupies</dd>
+ * <dt>data</dt>
+ * <dd>
+ * data that is supplied to the callbacks when the nodes(s) are
+ * loaded.
+ * </dd>
+ * <dt>insertBefore</dt>
+ * <dd>node or node id that will become the new node's nextSibling</dd>
+ * <dt>charset</dt>
+ * <dd>Node charset, default utf-8 (deprecated, use the attributes config)</dd>
+ * <dt>attributes</dt>
+ * <dd>An object literal containing additional attributes to add to the link tags</dd>
+ * </dl>
+ * <pre>
+ * Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+ * </pre>
+ * <pre>
+ * Y.Get.css(
+ * ["http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css",
+ * insertBefore: 'custom-styles' // nodes will be inserted before the specified node
+ * });
+ * </pre>
+ * @return {tId: string} an object containing info about the transaction
+ */
+ css: function(url, opts) {
+ return _queue("css", url, opts);
+ }
+ };
+}();
+
+})();
+
+
+}, '3.0.0' );
+YUI.add('yui-log', function(Y) {
+
+/**
+ * Provides console log capability and exposes a custom event for
+ * console implementations.
+ * @module yui
+ * @submodule yui-log
+ */
+(function() {
+
+var INSTANCE = Y,
+ LOGEVENT = 'yui:log',
+ UNDEFINED = 'undefined',
+ LEVELS = { debug: 1, info: 1, warn: 1, error: 1 },
+ _published;
+
+/**
+ * If the 'debug' config is true, a 'yui:log' event will be
+ * dispatched, which the Console widget and anything else
+ * can consume. If the 'useBrowserConsole' config is true, it will
+ * write to the browser console if available. YUI-specific log
+ * messages will only be present in the -debug versions of the
+ * JS files. The build system is supposed to remove log statements
+ * from the raw and minified versions of the files.
+ *
+ * @method log
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.log = function(msg, cat, src, silent) {
+ var Y = INSTANCE, c = Y.config, bail = false, excl, incl, m, f;
+ // suppress log message if the config is off or the event stack
+ // or the event call stack contains a consumer of the yui:log event
+ if (c.debug) {
+ // apply source filters
+ if (src) {
+ excl = c.logExclude;
+ incl = c.logInclude;
+
+ if (incl && !(src in incl)) {
+ bail = 1;
+ } else if (excl && (src in excl)) {
+ bail = 1;
+ }
+ }
+
+ if (!bail) {
+
+ if (c.useBrowserConsole) {
+ m = (src) ? src + ': ' + msg : msg;
+ if (typeof console != UNDEFINED && console.log) {
+ f = (cat && console[cat] && (cat in LEVELS)) ? cat : 'log';
+ console[f](m);
+ } else if (typeof opera != UNDEFINED) {
+ opera.postError(m);
+ }
+ }
+
+ if (Y.fire && !silent) {
+ if (!_published) {
+ Y.publish(LOGEVENT, {
+ broadcast: 2,
+ emitFacade: 1
+ });
+
+ _published = 1;
+
+ }
+ Y.fire(LOGEVENT, {
+ msg: msg,
+ cat: cat,
+ src: src
+ });
+ }
+ }
+ }
+
+ return Y;
+};
+
+/**
+ * Write a system message. This message will be preserved in the
+ * minified and raw versions of the YUI files, unlike log statements.
+ * @method message
+ * @for YUI
+ * @param {String} msg The message to log.
+ * @param {String} cat The log category for the message. Default
+ * categories are "info", "warn", "error", time".
+ * Custom categories can be used as well. (opt)
+ * @param {String} src The source of the the message (opt)
+ * @param {boolean} silent If true, the log event won't fire
+ * @return {YUI} YUI instance
+ */
+INSTANCE.message = function() {
+ return INSTANCE.log.apply(INSTANCE, arguments);
+};
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
+YUI.add('yui-later', function(Y) {
+
+/**
+ * Provides a setTimeout/setInterval wrapper
+ * @module yui
+ * @submodule yui-later
+ */
+(function() {
+ var L = Y.Lang,
+
+ /**
+ * Executes the supplied function in the context of the supplied
+ * object 'when' milliseconds later. Executes the function a
+ * single time unless periodic is set to true.
+ * @method later
+ * @for YUI
+ * @param when {int} the number of milliseconds to wait until the fn
+ * is executed.
+ * @param o the context object.
+ * @param fn {Function|String} the function to execute or the name of
+ * the method in the 'o' object to execute.
+ * @param data [Array] data that is provided to the function. This accepts
+ * either a single item or an array. If an array is provided, the
+ * function is executed with one parameter for each array item. If
+ * you need to pass a single array parameter, it needs to be wrapped in
+ * an array [myarray].
+ * @param periodic {boolean} if true, executes continuously at supplied
+ * interval until canceled.
+ * @return {object} a timer object. Call the cancel() method on this object to
+ * stop the timer.
+ */
+ later = function(when, o, fn, data, periodic) {
+ when = when || 0;
+ o = o || {};
+ var m=fn, d=Y.Array(data), f, r;
+
+ if (L.isString(fn)) {
+ m = o[fn];
+ }
+
+ if (!m) {
+ }
+
+ f = function() {
+ m.apply(o, d);
+ };
+
+ r = (periodic) ? setInterval(f, when) : setTimeout(f, when);
+
+ return {
+ id: r,
+ interval: periodic,
+ cancel: function() {
+ if (this.interval) {
+ clearInterval(r);
+ } else {
+ clearTimeout(r);
+ }
+ }
+ };
+ };
+
+ Y.later = later;
+ L.later = later;
+
+})();
+
+
+}, '3.0.0' ,{requires:['yui-base']});
+
+
+YUI.add('yui', function(Y){}, '3.0.0' ,{use:['yui-base','get','yui-log','yui-later']});
+
Updated to YUI 2.5.2, 25 July 2008
Updated to YUI 2.6.0, 03 October 2008
Updated to YUI 2.7.0, 14 May 2009 (2.0dev only)
-Updated to YUI 2.8.0r4, 12 December 2009 (2.0dev only)
+Updated to YUI 2.8.0r4+3.0.0, 12 December 2009 (2.0dev only)