From: Petr Skoda Date: Wed, 16 Dec 2009 19:45:12 +0000 (+0000) Subject: MDL-20796 importing yui 3.0.0 X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=bee934afa3f218f456d50da996905530978dee5a;p=moodle.git MDL-20796 importing yui 3.0.0 --- diff --git a/lib/setup.php b/lib/setup.php index 7755ac3808..80f9d53bd4 100644 --- a/lib/setup.php +++ b/lib/setup.php @@ -247,6 +247,7 @@ if (!isset($CFG->prefix)) { // Just in case it isn't defined in config.php // 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 diff --git a/lib/yui/3.0.0/anim/anim-base-debug.js b/lib/yui/3.0.0/anim/anim-base-debug.js new file mode 100644 index 0000000000..9b23387cc7 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-base-debug.js @@ -0,0 +1,603 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/anim/anim-base-min.js b/lib/yui/3.0.0/anim/anim-base-min.js new file mode 100644 index 0000000000..c5bf0560ea --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-base-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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= 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']}); diff --git a/lib/yui/3.0.0/anim/anim-color-debug.js b/lib/yui/3.0.0/anim/anim-color-debug.js new file mode 100644 index 0000000000..f646bd8c4e --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-color-debug.js @@ -0,0 +1,55 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 to + * and from 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']}); diff --git a/lib/yui/3.0.0/anim/anim-color-min.js b/lib/yui/3.0.0/anim/anim-color-min.js new file mode 100644 index 0000000000..f1e8aa3806 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-color-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/anim/anim-color.js b/lib/yui/3.0.0/anim/anim-color.js new file mode 100644 index 0000000000..f646bd8c4e --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-color.js @@ -0,0 +1,55 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 to + * and from 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']}); diff --git a/lib/yui/3.0.0/anim/anim-curve-debug.js b/lib/yui/3.0.0/anim/anim-curve-debug.js new file mode 100644 index 0000000000..be3ead850b --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-curve-debug.js @@ -0,0 +1,64 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 curve property for the to + * 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']}); diff --git a/lib/yui/3.0.0/anim/anim-curve-min.js b/lib/yui/3.0.0/anim/anim-curve-min.js new file mode 100644 index 0000000000..d2d89f3ad9 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-curve-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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;Ccurve property for the to + * 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']}); diff --git a/lib/yui/3.0.0/anim/anim-debug.js b/lib/yui/3.0.0/anim/anim-debug.js new file mode 100644 index 0000000000..82b5a33436 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-debug.js @@ -0,0 +1,1150 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 to + * and from 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 curve property for the to + * 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 scroll property in to + * and from 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 xy property in from and + * to 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}); + diff --git a/lib/yui/3.0.0/anim/anim-easing-debug.js b/lib/yui/3.0.0/anim/anim-easing-debug.js new file mode 100644 index 0000000000..fb211e7c6b --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-easing-debug.js @@ -0,0 +1,355 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/anim/anim-easing-min.js b/lib/yui/3.0.0/anim/anim-easing-min.js new file mode 100644 index 0000000000..32f633a7ae --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-easing-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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"||Rscroll property in to + * and from 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']}); diff --git a/lib/yui/3.0.0/anim/anim-scroll-min.js b/lib/yui/3.0.0/anim/anim-scroll-min.js new file mode 100644 index 0000000000..ddc1e4eeb5 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-scroll-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/anim/anim-scroll.js b/lib/yui/3.0.0/anim/anim-scroll.js new file mode 100644 index 0000000000..d6d0364c42 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-scroll.js @@ -0,0 +1,45 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 scroll property in to + * and from 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']}); diff --git a/lib/yui/3.0.0/anim/anim-xy-debug.js b/lib/yui/3.0.0/anim/anim-xy-debug.js new file mode 100644 index 0000000000..c67e268112 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-xy-debug.js @@ -0,0 +1,33 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 xy property in from and + * to 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']}); diff --git a/lib/yui/3.0.0/anim/anim-xy-min.js b/lib/yui/3.0.0/anim/anim-xy-min.js new file mode 100644 index 0000000000..bdf8cf0e5a --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-xy-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/anim/anim-xy.js b/lib/yui/3.0.0/anim/anim-xy.js new file mode 100644 index 0000000000..c67e268112 --- /dev/null +++ b/lib/yui/3.0.0/anim/anim-xy.js @@ -0,0 +1,33 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 xy property in from and + * to 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']}); diff --git a/lib/yui/3.0.0/anim/anim.js b/lib/yui/3.0.0/anim/anim.js new file mode 100644 index 0000000000..02d66d273d --- /dev/null +++ b/lib/yui/3.0.0/anim/anim.js @@ -0,0 +1,1149 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 to + * and from 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 curve property for the to + * 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 scroll property in to + * and from 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 xy property in from and + * to 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}); + diff --git a/lib/yui/3.0.0/assets/skins/sam/bg.png b/lib/yui/3.0.0/assets/skins/sam/bg.png new file mode 100644 index 0000000000..652fce6ccd Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/bg.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/console-filters.css b/lib/yui/3.0.0/assets/skins/sam/console-filters.css new file mode 100644 index 0000000000..cb9fb4193e --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/console-filters.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/assets/skins/sam/console.css b/lib/yui/3.0.0/assets/skins/sam/console.css new file mode 100644 index 0000000000..5688517efc --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/console.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/assets/skins/sam/horizontal-menu-submenu-indicator.png b/lib/yui/3.0.0/assets/skins/sam/horizontal-menu-submenu-indicator.png new file mode 100644 index 0000000000..2ef3454cac Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/horizontal-menu-submenu-indicator.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/horizontal-menu-submenu-toggle.png b/lib/yui/3.0.0/assets/skins/sam/horizontal-menu-submenu-toggle.png new file mode 100644 index 0000000000..e776af4ba3 Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/horizontal-menu-submenu-toggle.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/node-menunav.css b/lib/yui/3.0.0/assets/skins/sam/node-menunav.css new file mode 100644 index 0000000000..7761e3e71f --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/node-menunav.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/assets/skins/sam/overlay.css b/lib/yui/3.0.0/assets/skins/sam/overlay.css new file mode 100644 index 0000000000..bd58643e1b --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/overlay.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/assets/skins/sam/rail-classic-x.png b/lib/yui/3.0.0/assets/skins/sam/rail-classic-x.png new file mode 100644 index 0000000000..553c823682 Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/rail-classic-x.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/rail-classic-y.png b/lib/yui/3.0.0/assets/skins/sam/rail-classic-y.png new file mode 100644 index 0000000000..dae35afbfc Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/rail-classic-y.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/skin.css b/lib/yui/3.0.0/assets/skins/sam/skin.css new file mode 100644 index 0000000000..acd7fb488e --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/skin.css @@ -0,0 +1,14 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/assets/skins/sam/slider.css b/lib/yui/3.0.0/assets/skins/sam/slider.css new file mode 100644 index 0000000000..1b2c4a72b7 --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/slider.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/assets/skins/sam/sprite.png b/lib/yui/3.0.0/assets/skins/sam/sprite.png new file mode 100644 index 0000000000..afd65e05aa Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/sprite.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/thumb-classic-x.png b/lib/yui/3.0.0/assets/skins/sam/thumb-classic-x.png new file mode 100644 index 0000000000..8d284d4ddc Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/thumb-classic-x.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/thumb-classic-y.png b/lib/yui/3.0.0/assets/skins/sam/thumb-classic-y.png new file mode 100644 index 0000000000..b6c331f435 Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/thumb-classic-y.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/vertical-menu-submenu-indicator.png b/lib/yui/3.0.0/assets/skins/sam/vertical-menu-submenu-indicator.png new file mode 100644 index 0000000000..4f4cf998b2 Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/vertical-menu-submenu-indicator.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/warn_error.png b/lib/yui/3.0.0/assets/skins/sam/warn_error.png new file mode 100644 index 0000000000..db4e55483e Binary files /dev/null and b/lib/yui/3.0.0/assets/skins/sam/warn_error.png differ diff --git a/lib/yui/3.0.0/assets/skins/sam/widget-stack.css b/lib/yui/3.0.0/assets/skins/sam/widget-stack.css new file mode 100644 index 0000000000..d7396945a5 --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/widget-stack.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/assets/skins/sam/widget.css b/lib/yui/3.0.0/assets/skins/sam/widget.css new file mode 100644 index 0000000000..eb73a02c79 --- /dev/null +++ b/lib/yui/3.0.0/assets/skins/sam/widget.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/async-queue/async-queue-debug.js b/lib/yui/3.0.0/async-queue/async-queue-debug.js new file mode 100644 index 0000000000..1025fe6fd1 --- /dev/null +++ b/lib/yui/3.0.0/async-queue/async-queue-debug.js @@ -0,0 +1,536 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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) { + +/** + *

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.

+ * + * @module async-queue + */ + +/** + *

A specialized queue class that supports scheduling callbacks to execute + * sequentially, iteratively, even asynchronously.

+ * + *

Callbacks can be function refs or objects with the following keys. Only + * the fn key is required.

+ * + *
    + *
  • fn -- The callback function
  • + *
  • context -- The execution context for the callbackFn.
  • + *
  • args -- Arguments to pass to callbackFn.
  • + *
  • timeout -- Millisecond delay before executing callbackFn. + * (Applies to each iterative execution of callback)
  • + *
  • iterations -- Number of times to repeat the callback. + *
  • until -- Repeat the callback until this function returns + * true. This setting trumps iterations.
  • + *
  • autoContinue -- Set to false to prevent the AsyncQueue from + * executing the next callback in the Queue after + * the callback completes.
  • + *
  • id -- Name that can be used to get, promote, get the + * indexOf, or delete this callback.
  • + *
+ * + * @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; + +/** + *

Static default values used to populate callback configuration properties. + * Preconfigured defaults include:

+ * + *
    + *
  • autoContinue: true
  • + *
  • iterations: 1
  • + *
  • timeout: 10 (10ms between callbacks)
  • + *
  • until: (function to run until iterations <= 0)
  • + *
+ * + * @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 callback 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 added 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; + }, + + /** + *

Default functionality for the "promote" event. Promotes the + * named callback to the head of the queue.

+ * + *

The event object will contain a property "callback", which + * holds the id of a callback or the callback object itself.

+ * + * @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; + }, + + /** + *

Default functionality for the "remove" event. Removes the + * callback from the queue.

+ * + *

The event object will contain a property "callback", which + * holds the id of a callback or the callback object itself.

+ * + * @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']}); diff --git a/lib/yui/3.0.0/async-queue/async-queue-min.js b/lib/yui/3.0.0/async-queue/async-queue-min.js new file mode 100644 index 0000000000..1287ae94eb --- /dev/null +++ b/lib/yui/3.0.0/async-queue/async-queue-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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-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 diff --git a/lib/yui/3.0.0/async-queue/async-queue.js b/lib/yui/3.0.0/async-queue/async-queue.js new file mode 100644 index 0000000000..1025fe6fd1 --- /dev/null +++ b/lib/yui/3.0.0/async-queue/async-queue.js @@ -0,0 +1,536 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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) { + +/** + *

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.

+ * + * @module async-queue + */ + +/** + *

A specialized queue class that supports scheduling callbacks to execute + * sequentially, iteratively, even asynchronously.

+ * + *

Callbacks can be function refs or objects with the following keys. Only + * the fn key is required.

+ * + *
    + *
  • fn -- The callback function
  • + *
  • context -- The execution context for the callbackFn.
  • + *
  • args -- Arguments to pass to callbackFn.
  • + *
  • timeout -- Millisecond delay before executing callbackFn. + * (Applies to each iterative execution of callback)
  • + *
  • iterations -- Number of times to repeat the callback. + *
  • until -- Repeat the callback until this function returns + * true. This setting trumps iterations.
  • + *
  • autoContinue -- Set to false to prevent the AsyncQueue from + * executing the next callback in the Queue after + * the callback completes.
  • + *
  • id -- Name that can be used to get, promote, get the + * indexOf, or delete this callback.
  • + *
+ * + * @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; + +/** + *

Static default values used to populate callback configuration properties. + * Preconfigured defaults include:

+ * + *
    + *
  • autoContinue: true
  • + *
  • iterations: 1
  • + *
  • timeout: 10 (10ms between callbacks)
  • + *
  • until: (function to run until iterations <= 0)
  • + *
+ * + * @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 callback 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 added 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; + }, + + /** + *

Default functionality for the "promote" event. Promotes the + * named callback to the head of the queue.

+ * + *

The event object will contain a property "callback", which + * holds the id of a callback or the callback object itself.

+ * + * @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; + }, + + /** + *

Default functionality for the "remove" event. Removes the + * callback from the queue.

+ * + *

The event object will contain a property "callback", which + * holds the id of a callback or the callback object itself.

+ * + * @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']}); diff --git a/lib/yui/3.0.0/attribute/attribute-base-debug.js b/lib/yui/3.0.0/attribute/attribute-base-debug.js new file mode 100644 index 0000000000..d4f503fed5 --- /dev/null +++ b/lib/yui/3.0.0/attribute/attribute-base-debug.js @@ -0,0 +1,1079 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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; + + /** + *

+ * 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. + *

+ *

For example, attributes added to the host can be configured:

+ *
    + *
  • As read only.
  • + *
  • As write once.
  • + *
  • With a setter function, which can be used to manipulate + * values passed to Attribute's set method, before they are stored.
  • + *
  • With a getter function, which can be used to manipulate stored values, + * before they are returned by Attribute's get method.
  • + *
  • With a validator function, to validate values before they are stored.
  • + *
+ * + *

See the addAttr method, for the complete set of configuration + * options available for attributes

. + * + *

NOTE: Most implementations will be better off extending the Base 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.

+ * + * @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)); + } + } + + /** + *

The value to return from an attribute setter in order to prevent the set from going through.

+ * + *

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.

+ * + * @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 = { + /** + *

+ * Adds an attribute with the provided configuration to the host object. + *

+ *

+ * The config argument object supports the following properties: + *

+ * + *
+ *
value <Any>
+ *
The initial value to set on the attribute
+ * + *
valueFn <Function>
+ *
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.
+ * + *
readOnly <boolean>
+ *
Whether or not the attribute is read only. Attributes having readOnly set to true + * cannot be modified by invoking the set method.
+ * + *
writeOnce <boolean>
+ *
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.
+ * + *
setter <Function>
+ *
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 + * Attribute.INVALID_VALUE, from the setter will prevent + * the value from being stored.
+ * + *
getter <Function>
+ *
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.
+ * + *
validator <Function>
+ *
The validator function invoked prior to setting the stored value. Returning + * false from the validator function will prevent the value from being stored.
+ * + *
broadcast <int>
+ *
If and how attribute change events for this attribute should be broadcast. See CustomEvent's broadcast property for + * valid values. By default attribute change events are not broadcast.
+ * + *
lazyAdd <boolean>
+ *
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 addAttrs method.
+ * + *
+ * + *

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.

+ * + *

Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.

+ * + * @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. + * + *

+ * NOTE: 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. + *

+ * + * @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. + *

+ * 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. + *

+ * @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. get("x.y.z")) + * + * @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. set("x.y.z", 5)). + * + * @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 set 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 get 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 set 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. + * + *

+ * NOTE: This method does not isolate the configuration object by merging/cloning. + * The caller is responsible for merging/cloning the configuration object if required. + *

+ * + * @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 addAttr. + * + * @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 addAttr. + */ + _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']}); diff --git a/lib/yui/3.0.0/attribute/attribute-base-min.js b/lib/yui/3.0.0/attribute/attribute-base-min.js new file mode 100644 index 0000000000..70a47dee12 --- /dev/null +++ b/lib/yui/3.0.0/attribute/attribute-base-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + * 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. + *

+ *

For example, attributes added to the host can be configured:

+ *
    + *
  • As read only.
  • + *
  • As write once.
  • + *
  • With a setter function, which can be used to manipulate + * values passed to Attribute's set method, before they are stored.
  • + *
  • With a getter function, which can be used to manipulate stored values, + * before they are returned by Attribute's get method.
  • + *
  • With a validator function, to validate values before they are stored.
  • + *
+ * + *

See the addAttr method, for the complete set of configuration + * options available for attributes

. + * + *

NOTE: Most implementations will be better off extending the Base 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.

+ * + * @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)); + } + } + + /** + *

The value to return from an attribute setter in order to prevent the set from going through.

+ * + *

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.

+ * + * @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 = { + /** + *

+ * Adds an attribute with the provided configuration to the host object. + *

+ *

+ * The config argument object supports the following properties: + *

+ * + *
+ *
value <Any>
+ *
The initial value to set on the attribute
+ * + *
valueFn <Function>
+ *
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.
+ * + *
readOnly <boolean>
+ *
Whether or not the attribute is read only. Attributes having readOnly set to true + * cannot be modified by invoking the set method.
+ * + *
writeOnce <boolean>
+ *
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.
+ * + *
setter <Function>
+ *
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 + * Attribute.INVALID_VALUE, from the setter will prevent + * the value from being stored.
+ * + *
getter <Function>
+ *
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.
+ * + *
validator <Function>
+ *
The validator function invoked prior to setting the stored value. Returning + * false from the validator function will prevent the value from being stored.
+ * + *
broadcast <int>
+ *
If and how attribute change events for this attribute should be broadcast. See CustomEvent's broadcast property for + * valid values. By default attribute change events are not broadcast.
+ * + *
lazyAdd <boolean>
+ *
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 addAttrs method.
+ * + *
+ * + *

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.

+ * + *

Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.

+ * + * @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. + * + *

+ * NOTE: 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. + *

+ * + * @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. + *

+ * 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. + *

+ * @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. get("x.y.z")) + * + * @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. set("x.y.z", 5)). + * + * @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 set 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 get 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 set 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. + * + *

+ * NOTE: This method does not isolate the configuration object by merging/cloning. + * The caller is responsible for merging/cloning the configuration object if required. + *

+ * + * @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 addAttr. + * + * @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 addAttr. + */ + _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']}); diff --git a/lib/yui/3.0.0/attribute/attribute-complex-debug.js b/lib/yui/3.0.0/attribute/attribute-complex-debug.js new file mode 100644 index 0000000000..2299c87f1c --- /dev/null +++ b/lib/yui/3.0.0/attribute/attribute-complex-debug.js @@ -0,0 +1,120 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/attribute/attribute-complex-min.js b/lib/yui/3.0.0/attribute/attribute-complex-min.js new file mode 100644 index 0000000000..3e07463646 --- /dev/null +++ b/lib/yui/3.0.0/attribute/attribute-complex-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + * 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. + *

+ *

For example, attributes added to the host can be configured:

+ *
    + *
  • As read only.
  • + *
  • As write once.
  • + *
  • With a setter function, which can be used to manipulate + * values passed to Attribute's set method, before they are stored.
  • + *
  • With a getter function, which can be used to manipulate stored values, + * before they are returned by Attribute's get method.
  • + *
  • With a validator function, to validate values before they are stored.
  • + *
+ * + *

See the addAttr method, for the complete set of configuration + * options available for attributes

. + * + *

NOTE: Most implementations will be better off extending the Base 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.

+ * + * @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)); + } + } + + /** + *

The value to return from an attribute setter in order to prevent the set from going through.

+ * + *

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.

+ * + * @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 = { + /** + *

+ * Adds an attribute with the provided configuration to the host object. + *

+ *

+ * The config argument object supports the following properties: + *

+ * + *
+ *
value <Any>
+ *
The initial value to set on the attribute
+ * + *
valueFn <Function>
+ *
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.
+ * + *
readOnly <boolean>
+ *
Whether or not the attribute is read only. Attributes having readOnly set to true + * cannot be modified by invoking the set method.
+ * + *
writeOnce <boolean>
+ *
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.
+ * + *
setter <Function>
+ *
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 + * Attribute.INVALID_VALUE, from the setter will prevent + * the value from being stored.
+ * + *
getter <Function>
+ *
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.
+ * + *
validator <Function>
+ *
The validator function invoked prior to setting the stored value. Returning + * false from the validator function will prevent the value from being stored.
+ * + *
broadcast <int>
+ *
If and how attribute change events for this attribute should be broadcast. See CustomEvent's broadcast property for + * valid values. By default attribute change events are not broadcast.
+ * + *
lazyAdd <boolean>
+ *
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 addAttrs method.
+ * + *
+ * + *

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.

+ * + *

Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.

+ * + * @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. + * + *

+ * NOTE: 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. + *

+ * + * @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. + *

+ * 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. + *

+ * @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. get("x.y.z")) + * + * @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. set("x.y.z", 5)). + * + * @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 set 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 get 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 set 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. + * + *

+ * NOTE: This method does not isolate the configuration object by merging/cloning. + * The caller is responsible for merging/cloning the configuration object if required. + *

+ * + * @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 addAttr. + * + * @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 addAttr. + */ + _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']}); + diff --git a/lib/yui/3.0.0/attribute/attribute-min.js b/lib/yui/3.0.0/attribute/attribute-min.js new file mode 100644 index 0000000000..c5a63783b2 --- /dev/null +++ b/lib/yui/3.0.0/attribute/attribute-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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 + * 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. + *

+ *

For example, attributes added to the host can be configured:

+ *
    + *
  • As read only.
  • + *
  • As write once.
  • + *
  • With a setter function, which can be used to manipulate + * values passed to Attribute's set method, before they are stored.
  • + *
  • With a getter function, which can be used to manipulate stored values, + * before they are returned by Attribute's get method.
  • + *
  • With a validator function, to validate values before they are stored.
  • + *
+ * + *

See the addAttr method, for the complete set of configuration + * options available for attributes

. + * + *

NOTE: Most implementations will be better off extending the Base 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.

+ * + * @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)); + } + } + + /** + *

The value to return from an attribute setter in order to prevent the set from going through.

+ * + *

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.

+ * + * @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 = { + /** + *

+ * Adds an attribute with the provided configuration to the host object. + *

+ *

+ * The config argument object supports the following properties: + *

+ * + *
+ *
value <Any>
+ *
The initial value to set on the attribute
+ * + *
valueFn <Function>
+ *
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.
+ * + *
readOnly <boolean>
+ *
Whether or not the attribute is read only. Attributes having readOnly set to true + * cannot be modified by invoking the set method.
+ * + *
writeOnce <boolean>
+ *
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.
+ * + *
setter <Function>
+ *
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 + * Attribute.INVALID_VALUE, from the setter will prevent + * the value from being stored.
+ * + *
getter <Function>
+ *
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.
+ * + *
validator <Function>
+ *
The validator function invoked prior to setting the stored value. Returning + * false from the validator function will prevent the value from being stored.
+ * + *
broadcast <int>
+ *
If and how attribute change events for this attribute should be broadcast. See CustomEvent's broadcast property for + * valid values. By default attribute change events are not broadcast.
+ * + *
lazyAdd <boolean>
+ *
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 addAttrs method.
+ * + *
+ * + *

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.

+ * + *

Configuration properties outside of the list mentioned above are considered private properties used internally by attribute, and are not intended for public use.

+ * + * @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. + * + *

+ * NOTE: 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. + *

+ * + * @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. + *

+ * 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. + *

+ * @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. get("x.y.z")) + * + * @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. set("x.y.z", 5)). + * + * @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 set 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 get 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 set 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. + * + *

+ * NOTE: This method does not isolate the configuration object by merging/cloning. + * The caller is responsible for merging/cloning the configuration object if required. + *

+ * + * @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 addAttr. + * + * @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 addAttr. + */ + _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']}); + diff --git a/lib/yui/3.0.0/base/base-base-debug.js b/lib/yui/3.0.0/base/base-base-debug.js new file mode 100644 index 0000000000..36c00088ff --- /dev/null +++ b/lib/yui/3.0.0/base/base-base-debug.js @@ -0,0 +1,536 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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; + + /** + *

+ * 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 ATTRS property for each class + * in the hierarchy will be initialized by Base. + *

+ * + *

+ * The static NAME 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. + *

+ * @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"); + + /** + *

+ * The string to be used to identify instances of + * this class, for example in prefixing events. + *

+ *

+ * Classes extending Base, should define their own + * static NAME property, which should be camelCase by + * convention (e.g. MyClass.NAME = "myClass";). + *

+ * @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 addAttr 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; + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" momemt of this event, will be notified + * after initialization of the object is complete (and therefore + * cannot prevent initialization). + *

+ * + * @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; + }, + + /** + *

+ * Destroy lifecycle method. Fires the destroy + * event, prior to invoking destructors for the + * class hierarchy. + *

+ *

+ * Subscribers to the destroy + * event can invoke preventDefault on the event object, to prevent destruction + * from proceeding. + *

+ * @method destroy + * @return {Base} A reference to this object + * @final + * @chainable + */ + destroy: function() { + Y.log('destroy called', 'life', 'base'); + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" moment of this event, will be notified + * after destruction is complete (and as a result cannot prevent + * destruction). + *

+ * @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 ATTRS 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']}); diff --git a/lib/yui/3.0.0/base/base-base-min.js b/lib/yui/3.0.0/base/base-base-min.js new file mode 100644 index 0000000000..a35d0a8498 --- /dev/null +++ b/lib/yui/3.0.0/base/base-base-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + * 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 ATTRS property for each class + * in the hierarchy will be initialized by Base. + *

+ * + *

+ * The static NAME 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. + *

+ * @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"); + + /** + *

+ * The string to be used to identify instances of + * this class, for example in prefixing events. + *

+ *

+ * Classes extending Base, should define their own + * static NAME property, which should be camelCase by + * convention (e.g. MyClass.NAME = "myClass";). + *

+ * @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 addAttr 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; + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" momemt of this event, will be notified + * after initialization of the object is complete (and therefore + * cannot prevent initialization). + *

+ * + * @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; + }, + + /** + *

+ * Destroy lifecycle method. Fires the destroy + * event, prior to invoking destructors for the + * class hierarchy. + *

+ *

+ * Subscribers to the destroy + * event can invoke preventDefault on the event object, to prevent destruction + * from proceeding. + *

+ * @method destroy + * @return {Base} A reference to this object + * @final + * @chainable + */ + destroy: function() { + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" moment of this event, will be notified + * after destruction is complete (and as a result cannot prevent + * destruction). + *

+ * @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 ATTRS 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']}); diff --git a/lib/yui/3.0.0/base/base-build-debug.js b/lib/yui/3.0.0/base/base-build-debug.js new file mode 100644 index 0000000000..e907453806 --- /dev/null +++ b/lib/yui/3.0.0/base/base-build-debug.js @@ -0,0 +1,201 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 Base.build method. + * + * @property Base._buildCfg + * @type Object + * @static + * @final + * @private + */ + Base._buildCfg = { + aggregates : ["ATTRS", "_PLUG", "_UNPLUG"] + }; + + /** + *

+ * 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. + *

+ *

+ * The cfg object supports the following properties + *

+ *
+ *
dynamic <boolean>
+ *
+ *

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.

+ *

If false, the extensions classes are augmented directly to + * the main class, modifying the main class' prototype.

+ *
+ *
aggregates <String[]>
+ *
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. + *
+ *
+ * + * @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']}); diff --git a/lib/yui/3.0.0/base/base-build-min.js b/lib/yui/3.0.0/base/base-build-min.js new file mode 100644 index 0000000000..174c0625ae --- /dev/null +++ b/lib/yui/3.0.0/base/base-build-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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;JBase.build method. + * + * @property Base._buildCfg + * @type Object + * @static + * @final + * @private + */ + Base._buildCfg = { + aggregates : ["ATTRS", "_PLUG", "_UNPLUG"] + }; + + /** + *

+ * 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. + *

+ *

+ * The cfg object supports the following properties + *

+ *
+ *
dynamic <boolean>
+ *
+ *

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.

+ *

If false, the extensions classes are augmented directly to + * the main class, modifying the main class' prototype.

+ *
+ *
aggregates <String[]>
+ *
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. + *
+ *
+ * + * @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']}); diff --git a/lib/yui/3.0.0/base/base-debug.js b/lib/yui/3.0.0/base/base-debug.js new file mode 100644 index 0000000000..ff65c1cf66 --- /dev/null +++ b/lib/yui/3.0.0/base/base-debug.js @@ -0,0 +1,770 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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; + + /** + *

+ * 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 ATTRS property for each class + * in the hierarchy will be initialized by Base. + *

+ * + *

+ * The static NAME 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. + *

+ * @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"); + + /** + *

+ * The string to be used to identify instances of + * this class, for example in prefixing events. + *

+ *

+ * Classes extending Base, should define their own + * static NAME property, which should be camelCase by + * convention (e.g. MyClass.NAME = "myClass";). + *

+ * @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 addAttr 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; + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" momemt of this event, will be notified + * after initialization of the object is complete (and therefore + * cannot prevent initialization). + *

+ * + * @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; + }, + + /** + *

+ * Destroy lifecycle method. Fires the destroy + * event, prior to invoking destructors for the + * class hierarchy. + *

+ *

+ * Subscribers to the destroy + * event can invoke preventDefault on the event object, to prevent destruction + * from proceeding. + *

+ * @method destroy + * @return {Base} A reference to this object + * @final + * @chainable + */ + destroy: function() { + Y.log('destroy called', 'life', 'base'); + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" moment of this event, will be notified + * after destruction is complete (and as a result cannot prevent + * destruction). + *

+ * @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 ATTRS 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 Plugin.Host.plug. See aliased + * method for argument and return value details. + * + * @method Base.plug + * @static + */ + Base.plug = PluginHost.plug; + + /** + * Alias for Plugin.Host.unplug. 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 Base.build method. + * + * @property Base._buildCfg + * @type Object + * @static + * @final + * @private + */ + Base._buildCfg = { + aggregates : ["ATTRS", "_PLUG", "_UNPLUG"] + }; + + /** + *

+ * 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. + *

+ *

+ * The cfg object supports the following properties + *

+ *
+ *
dynamic <boolean>
+ *
+ *

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.

+ *

If false, the extensions classes are augmented directly to + * the main class, modifying the main class' prototype.

+ *
+ *
aggregates <String[]>
+ *
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. + *
+ *
+ * + * @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']}); + diff --git a/lib/yui/3.0.0/base/base-min.js b/lib/yui/3.0.0/base/base-min.js new file mode 100644 index 0000000000..d2181dc578 --- /dev/null +++ b/lib/yui/3.0.0/base/base-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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;SPlugin.Host.plug. See aliased + * method for argument and return value details. + * + * @method Base.plug + * @static + */ + Base.plug = PluginHost.plug; + + /** + * Alias for Plugin.Host.unplug. 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']}); diff --git a/lib/yui/3.0.0/base/base-pluginhost-min.js b/lib/yui/3.0.0/base/base-pluginhost-min.js new file mode 100644 index 0000000000..ac6efdd6da --- /dev/null +++ b/lib/yui/3.0.0/base/base-pluginhost-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/base/base-pluginhost.js b/lib/yui/3.0.0/base/base-pluginhost.js new file mode 100644 index 0000000000..6b29f67913 --- /dev/null +++ b/lib/yui/3.0.0/base/base-pluginhost.js @@ -0,0 +1,43 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 Plugin.Host.plug. See aliased + * method for argument and return value details. + * + * @method Base.plug + * @static + */ + Base.plug = PluginHost.plug; + + /** + * Alias for Plugin.Host.unplug. 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']}); diff --git a/lib/yui/3.0.0/base/base.js b/lib/yui/3.0.0/base/base.js new file mode 100644 index 0000000000..9f98c7969c --- /dev/null +++ b/lib/yui/3.0.0/base/base.js @@ -0,0 +1,765 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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; + + /** + *

+ * 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 ATTRS property for each class + * in the hierarchy will be initialized by Base. + *

+ * + *

+ * The static NAME 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. + *

+ * @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"); + + /** + *

+ * The string to be used to identify instances of + * this class, for example in prefixing events. + *

+ *

+ * Classes extending Base, should define their own + * static NAME property, which should be camelCase by + * convention (e.g. MyClass.NAME = "myClass";). + *

+ * @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 addAttr 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; + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" momemt of this event, will be notified + * after initialization of the object is complete (and therefore + * cannot prevent initialization). + *

+ * + * @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; + }, + + /** + *

+ * Destroy lifecycle method. Fires the destroy + * event, prior to invoking destructors for the + * class hierarchy. + *

+ *

+ * Subscribers to the destroy + * event can invoke preventDefault on the event object, to prevent destruction + * from proceeding. + *

+ * @method destroy + * @return {Base} A reference to this object + * @final + * @chainable + */ + destroy: function() { + + /** + *

+ * 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. + *

+ *

+ * Subscribers to the "after" moment of this event, will be notified + * after destruction is complete (and as a result cannot prevent + * destruction). + *

+ * @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 ATTRS 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 Plugin.Host.plug. See aliased + * method for argument and return value details. + * + * @method Base.plug + * @static + */ + Base.plug = PluginHost.plug; + + /** + * Alias for Plugin.Host.unplug. 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 Base.build method. + * + * @property Base._buildCfg + * @type Object + * @static + * @final + * @private + */ + Base._buildCfg = { + aggregates : ["ATTRS", "_PLUG", "_UNPLUG"] + }; + + /** + *

+ * 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. + *

+ *

+ * The cfg object supports the following properties + *

+ *
+ *
dynamic <boolean>
+ *
+ *

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.

+ *

If false, the extensions classes are augmented directly to + * the main class, modifying the main class' prototype.

+ *
+ *
aggregates <String[]>
+ *
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. + *
+ *
+ * + * @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']}); + diff --git a/lib/yui/3.0.0/cache/cache-debug.js b/lib/yui/3.0.0/cache/cache-debug.js new file mode 100644 index 0000000000..d9d7ca3cc0 --- /dev/null +++ b/lib/yui/3.0.0/cache/cache-debug.js @@ -0,0 +1,366 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
entry (Object)
The cached entry.
+ *
+ * @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: + *
+ *
request (Object)
The request object.
+ *
+ */ + + /** + * @event retrieve + * @description Fired when an entry is retrieved from the cache. + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
entry (Object)
The retrieved entry.
+ *
+ */ + + // 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: + *
+ *
entry (Object)
The cached entry.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/cache/cache-min.js b/lib/yui/3.0.0/cache/cache-min.js new file mode 100644 index 0000000000..e776f1154b --- /dev/null +++ b/lib/yui/3.0.0/cache/cache-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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: + *
+ *
entry (Object)
The cached entry.
+ *
+ * @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: + *
+ *
request (Object)
The request object.
+ *
+ */ + + /** + * @event retrieve + * @description Fired when an entry is retrieved from the cache. + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
entry (Object)
The retrieved entry.
+ *
+ */ + + // 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: + *
+ *
entry (Object)
The cached entry.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/classnamemanager/classnamemanager-debug.js b/lib/yui/3.0.0/classnamemanager/classnamemanager-debug.js new file mode 100644 index 0000000000..b2c5d25f4e --- /dev/null +++ b/lib/yui/3.0.0/classnamemanager/classnamemanager-debug.js @@ -0,0 +1,87 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + * + *
    + *
  • Easy creation of prefixed class names
  • + *
  • Caching of previously created class names for improved performance.
  • + *
+ * + * @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 + * Y.config.classNamePrefix attribute + the provided strings. + * Uses the Y.config.classNameDelimiter 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' ); diff --git a/lib/yui/3.0.0/classnamemanager/classnamemanager-min.js b/lib/yui/3.0.0/classnamemanager/classnamemanager-min.js new file mode 100644 index 0000000000..15c4019e67 --- /dev/null +++ b/lib/yui/3.0.0/classnamemanager/classnamemanager-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/classnamemanager/classnamemanager.js b/lib/yui/3.0.0/classnamemanager/classnamemanager.js new file mode 100644 index 0000000000..b2c5d25f4e --- /dev/null +++ b/lib/yui/3.0.0/classnamemanager/classnamemanager.js @@ -0,0 +1,87 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + * + *
    + *
  • Easy creation of prefixed class names
  • + *
  • Caching of previously created class names for improved performance.
  • + *
+ * + * @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 + * Y.config.classNamePrefix attribute + the provided strings. + * Uses the Y.config.classNameDelimiter 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' ); diff --git a/lib/yui/3.0.0/collection/collection-debug.js b/lib/yui/3.0.0/collection/collection-debug.js new file mode 100644 index 0000000000..d7fdeea14d --- /dev/null +++ b/lib/yui/3.0.0/collection/collection-debug.js @@ -0,0 +1,294 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/collection/collection-min.js b/lib/yui/3.0.0/collection/collection-min.js new file mode 100644 index 0000000000..7ce8dbeb69 --- /dev/null +++ b/lib/yui/3.0.0/collection/collection-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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' ); diff --git a/lib/yui/3.0.0/compat/compat-debug.js b/lib/yui/3.0.0/compat/compat-debug.js new file mode 100644 index 0000000000..b289d0bb55 --- /dev/null +++ b/lib/yui/3.0.0/compat/compat-debug.js @@ -0,0 +1,900 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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= 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'); diff --git a/lib/yui/3.0.0/compat/compat-min.js b/lib/yui/3.0.0/compat/compat-min.js new file mode 100644 index 0000000000..1dcc703b9b --- /dev/null +++ b/lib/yui/3.0.0/compat/compat-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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=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 diff --git a/lib/yui/3.0.0/compat/compat.js b/lib/yui/3.0.0/compat/compat.js new file mode 100644 index 0000000000..12528df9c6 --- /dev/null +++ b/lib/yui/3.0.0/compat/compat.js @@ -0,0 +1,896 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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= 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'); diff --git a/lib/yui/3.0.0/console/assets/console-core.css b/lib/yui/3.0.0/console/assets/console-core.css new file mode 100644 index 0000000000..dbc8374a30 --- /dev/null +++ b/lib/yui/3.0.0/console/assets/console-core.css @@ -0,0 +1,7 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 3.0.0 +build: 1549 +*/ diff --git a/lib/yui/3.0.0/console/assets/console-filters-core.css b/lib/yui/3.0.0/console/assets/console-filters-core.css new file mode 100644 index 0000000000..dbc8374a30 --- /dev/null +++ b/lib/yui/3.0.0/console/assets/console-filters-core.css @@ -0,0 +1,7 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 3.0.0 +build: 1549 +*/ diff --git a/lib/yui/3.0.0/console/assets/skins/sam/bg.png b/lib/yui/3.0.0/console/assets/skins/sam/bg.png new file mode 100644 index 0000000000..652fce6ccd Binary files /dev/null and b/lib/yui/3.0.0/console/assets/skins/sam/bg.png differ diff --git a/lib/yui/3.0.0/console/assets/skins/sam/console-filters-skin.css b/lib/yui/3.0.0/console/assets/skins/sam/console-filters-skin.css new file mode 100644 index 0000000000..dc870f3ac4 --- /dev/null +++ b/lib/yui/3.0.0/console/assets/skins/sam/console-filters-skin.css @@ -0,0 +1,34 @@ +/* +Copyright (c) 2009, Yahoo! 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; +} diff --git a/lib/yui/3.0.0/console/assets/skins/sam/console-filters.css b/lib/yui/3.0.0/console/assets/skins/sam/console-filters.css new file mode 100644 index 0000000000..cb9fb4193e --- /dev/null +++ b/lib/yui/3.0.0/console/assets/skins/sam/console-filters.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/console/assets/skins/sam/console-skin.css b/lib/yui/3.0.0/console/assets/skins/sam/console-skin.css new file mode 100644 index 0000000000..829cd572a8 --- /dev/null +++ b/lib/yui/3.0.0/console/assets/skins/sam/console-skin.css @@ -0,0 +1,192 @@ +/* +Copyright (c) 2009, Yahoo! 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; } diff --git a/lib/yui/3.0.0/console/assets/skins/sam/console.css b/lib/yui/3.0.0/console/assets/skins/sam/console.css new file mode 100644 index 0000000000..5688517efc --- /dev/null +++ b/lib/yui/3.0.0/console/assets/skins/sam/console.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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;} diff --git a/lib/yui/3.0.0/console/assets/skins/sam/warn_error.png b/lib/yui/3.0.0/console/assets/skins/sam/warn_error.png new file mode 100644 index 0000000000..db4e55483e Binary files /dev/null and b/lib/yui/3.0.0/console/assets/skins/sam/warn_error.png differ diff --git a/lib/yui/3.0.0/console/assets/warn_error.png b/lib/yui/3.0.0/console/assets/warn_error.png new file mode 100644 index 0000000000..db4e55483e Binary files /dev/null and b/lib/yui/3.0.0/console/assets/warn_error.png differ diff --git a/lib/yui/3.0.0/console/console-debug.js b/lib/yui/3.0.0/console/console-debug.js new file mode 100644 index 0000000000..0d5f61e68d --- /dev/null +++ b/lib/yui/3.0.0/console/console-debug.js @@ -0,0 +1,1478 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 Y.log( message, category, source ) 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 = /'+ + '

'+ + ''+ + '{sourceAndDetail}'+ + ''+ + ''+ + '{category}'+ + ''+ + ' {totalTime}ms (+{elapsedTime}) {localTime}'+ + ''+ + '

'+ + '
{message}
'+ + '', + + 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. + * + *

By default, the keys contained in the object are:

+ *
    + *
  • entry_class
  • + *
  • entry_meta_class
  • + *
  • entry_cat_class
  • + *
  • entry_src_class
  • + *
  • entry_time_class
  • + *
  • entry_content_class
  • + *
+ * + * @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. + * + *

By default, the keys contained in the object are:

+ *
    + *
  • console_hd_class
  • + *
  • console_bd_class
  • + *
  • console_ft_class
  • + *
  • console_controls_class
  • + *
  • console_checkbox_class
  • + *
  • console_pause_class
  • + *
  • console_pause_label_class
  • + *
  • console_button_class
  • + *
  • console_clear_class
  • + *
  • console_collapse_class
  • + *
  • console_title_class
  • + *
+ * + * @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: + * + *
    + *
  • console_button_class - contributed by Console.CHROME_CLASSES
  • + *
  • console_collapse_class - contributed by Console.CHROME_CLASSES
  • + *
  • console_hd_class - contributed by Console.CHROME_CLASSES
  • + *
  • console_title_class - contributed by Console.CHROME_CLASSES
  • + *
  • str_collapse - pulled from attribute strings.collapse
  • + *
  • str_title - pulled from attribute strings.title
  • + *
+ * + * @property Console.HEADER_TEMPLATE + * @type String + * @static + */ + HEADER_TEMPLATE : + '
'+ + '

{str_title}

'+ + ''+ + '
', + + /** + * 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 : '
', + + /** + * 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: + * + *
    + *
  • id_guid - generated unique id, relates the label and checkbox
  • + *
  • str_pause - pulled from attribute strings.pause
  • + *
  • str_clear - pulled from attribute strings.clear
  • + *
+ * + * @property Console.FOOTER_TEMPLATE + * @type String + * @static + */ + FOOTER_TEMPLATE : + '
'+ + '
'+ + '' + + ''+ + '
'+ + '
', + + /** + * 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: + * + *
    + *
  • cat_class
  • + *
  • src_class
  • + *
  • totalTime
  • + *
  • elapsedTime
  • + *
  • localTime
  • + *
  • sourceAndDetail
  • + *
  • message
  • + *
+ * + * @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: + * + *
    + *
  • title : "Log Console"
  • + *
  • pause : "Pause"
  • + *
  • clear : "Clear"
  • + *
  • collapse : "Collapse"
  • + *
  • expand : "Expand"
  • + *
+ * + * @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 used 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 used + */ + 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 used + */ + 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 Y.later 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 Y.log. + * + * @method log + * @param arg* {MIXED} (all arguments passed through to Y.log) + * @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: + *
+ *
message
+ *
The message data normalized into an object literal (see _normalizeMessage)
+ *
+ * @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: + *
    + *
  • time - this moment
  • + *
  • message - leg message
  • + *
  • category - logLevel or custom category for the message
  • + *
  • source - when provided, the widget or util calling Y.log
  • + *
  • sourceAndDetail - same as source but can include instance info
  • + *
  • localTime - readable version of time
  • + *
  • elapsedTime - ms since last entry
  • + *
  • totalTime - ms since Console was instantiated or reset
  • + *
+ * + * @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 + * this.clearConsole(). + * + * @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']}); diff --git a/lib/yui/3.0.0/console/console-filters-debug.js b/lib/yui/3.0.0/console/console-filters-debug.js new file mode 100644 index 0000000000..126e6a665e --- /dev/null +++ b/lib/yui/3.0.0/console/console-filters-debug.js @@ -0,0 +1,710 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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) { + +/** + *

Provides Plugin.ConsoleFilters plugin class.

+ * + *

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.

+ * + * @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 : + '
', + + /** + * Markup template used to create the container for the source filters. + * + * @property ConsoleFilters.SOURCES_TEMPLATE + * @type String + * @static + */ + SOURCES_TEMPLATE : + '
', + + /** + * 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. + ' ', + + /** + * 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 + }, + + /** + *

Map of entry categories to their visibility status. Update a + * particular category's visibility by setting the subattribute to true + * (visible) or false (hidden).

+ * + *

For example, yconsole.filter.set('category.info', false) to hide + * log entries with the category/logLevel of 'info'.

+ * + *

Similarly, yconsole.filter.get('category.warn') will return a + * boolean indicating whether that category is currently being included + * in the UI.

+ * + *

Unlike the YUI instance configuration's logInclude and logExclude + * properties, filtered entries are only hidden from the UI, but + * can be made visible again.

+ * + * @attribute category + * @type Object + */ + category : { + value : {}, + validator : function (v,k) { + return this._validateCategory(k,v); + } + }, + + /** + *

Map of entry sources to their visibility status. Update a + * particular sources's visibility by setting the subattribute to true + * (visible) or false (hidden).

+ * + *

For example, yconsole.filter.set('sources.slider', false) to hide + * log entries originating from Y.Slider.

+ * + * @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']}); diff --git a/lib/yui/3.0.0/console/console-filters-min.js b/lib/yui/3.0.0/console/console-filters-min.js new file mode 100644 index 0000000000..36ca861d4b --- /dev/null +++ b/lib/yui/3.0.0/console/console-filters-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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:'
',SOURCES_TEMPLATE:'
',FILTER_TEMPLATE:' ",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 diff --git a/lib/yui/3.0.0/console/console-filters.js b/lib/yui/3.0.0/console/console-filters.js new file mode 100644 index 0000000000..126e6a665e --- /dev/null +++ b/lib/yui/3.0.0/console/console-filters.js @@ -0,0 +1,710 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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) { + +/** + *

Provides Plugin.ConsoleFilters plugin class.

+ * + *

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.

+ * + * @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 : + '
', + + /** + * Markup template used to create the container for the source filters. + * + * @property ConsoleFilters.SOURCES_TEMPLATE + * @type String + * @static + */ + SOURCES_TEMPLATE : + '
', + + /** + * 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. + ' ', + + /** + * 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 + }, + + /** + *

Map of entry categories to their visibility status. Update a + * particular category's visibility by setting the subattribute to true + * (visible) or false (hidden).

+ * + *

For example, yconsole.filter.set('category.info', false) to hide + * log entries with the category/logLevel of 'info'.

+ * + *

Similarly, yconsole.filter.get('category.warn') will return a + * boolean indicating whether that category is currently being included + * in the UI.

+ * + *

Unlike the YUI instance configuration's logInclude and logExclude + * properties, filtered entries are only hidden from the UI, but + * can be made visible again.

+ * + * @attribute category + * @type Object + */ + category : { + value : {}, + validator : function (v,k) { + return this._validateCategory(k,v); + } + }, + + /** + *

Map of entry sources to their visibility status. Update a + * particular sources's visibility by setting the subattribute to true + * (visible) or false (hidden).

+ * + *

For example, yconsole.filter.set('sources.slider', false) to hide + * log entries originating from Y.Slider.

+ * + * @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']}); diff --git a/lib/yui/3.0.0/console/console-min.js b/lib/yui/3.0.0/console/console-min.js new file mode 100644 index 0000000000..2a2b7954c9 --- /dev/null +++ b/lib/yui/3.0.0/console/console-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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=/'+'

'+''+"{sourceAndDetail}"+""+''+"{category}"+''+" {totalTime}ms (+{elapsedTime}) {localTime}"+""+"

"+'
{message}
'+"",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:'
'+'

{str_title}

'+'"+"
",BODY_TEMPLATE:'
',FOOTER_TEMPLATE:'
'+'
'+'"+'"+"
"+"
",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;AI0){if(this.get("newestOnTop")){AH=AF;Y=L.size();}else{AH=0;}this._body.setStyle("display","none");for(;AHY.log( message, category, source ) 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 = /'+ + '

'+ + ''+ + '{sourceAndDetail}'+ + ''+ + ''+ + '{category}'+ + ''+ + ' {totalTime}ms (+{elapsedTime}) {localTime}'+ + ''+ + '

'+ + '
{message}
'+ + '', + + 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. + * + *

By default, the keys contained in the object are:

+ *
    + *
  • entry_class
  • + *
  • entry_meta_class
  • + *
  • entry_cat_class
  • + *
  • entry_src_class
  • + *
  • entry_time_class
  • + *
  • entry_content_class
  • + *
+ * + * @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. + * + *

By default, the keys contained in the object are:

+ *
    + *
  • console_hd_class
  • + *
  • console_bd_class
  • + *
  • console_ft_class
  • + *
  • console_controls_class
  • + *
  • console_checkbox_class
  • + *
  • console_pause_class
  • + *
  • console_pause_label_class
  • + *
  • console_button_class
  • + *
  • console_clear_class
  • + *
  • console_collapse_class
  • + *
  • console_title_class
  • + *
+ * + * @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: + * + *
    + *
  • console_button_class - contributed by Console.CHROME_CLASSES
  • + *
  • console_collapse_class - contributed by Console.CHROME_CLASSES
  • + *
  • console_hd_class - contributed by Console.CHROME_CLASSES
  • + *
  • console_title_class - contributed by Console.CHROME_CLASSES
  • + *
  • str_collapse - pulled from attribute strings.collapse
  • + *
  • str_title - pulled from attribute strings.title
  • + *
+ * + * @property Console.HEADER_TEMPLATE + * @type String + * @static + */ + HEADER_TEMPLATE : + '
'+ + '

{str_title}

'+ + ''+ + '
', + + /** + * 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 : '
', + + /** + * 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: + * + *
    + *
  • id_guid - generated unique id, relates the label and checkbox
  • + *
  • str_pause - pulled from attribute strings.pause
  • + *
  • str_clear - pulled from attribute strings.clear
  • + *
+ * + * @property Console.FOOTER_TEMPLATE + * @type String + * @static + */ + FOOTER_TEMPLATE : + '
'+ + '
'+ + '' + + ''+ + '
'+ + '
', + + /** + * 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: + * + *
    + *
  • cat_class
  • + *
  • src_class
  • + *
  • totalTime
  • + *
  • elapsedTime
  • + *
  • localTime
  • + *
  • sourceAndDetail
  • + *
  • message
  • + *
+ * + * @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: + * + *
    + *
  • title : "Log Console"
  • + *
  • pause : "Pause"
  • + *
  • clear : "Clear"
  • + *
  • collapse : "Collapse"
  • + *
  • expand : "Expand"
  • + *
+ * + * @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 used 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 used + */ + 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 used + */ + 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 Y.later 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 Y.log. + * + * @method log + * @param arg* {MIXED} (all arguments passed through to Y.log) + * @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: + *
+ *
message
+ *
The message data normalized into an object literal (see _normalizeMessage)
+ *
+ * @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: + *
    + *
  • time - this moment
  • + *
  • message - leg message
  • + *
  • category - logLevel or custom category for the message
  • + *
  • source - when provided, the widget or util calling Y.log
  • + *
  • sourceAndDetail - same as source but can include instance info
  • + *
  • localTime - readable version of time
  • + *
  • elapsedTime - ms since last entry
  • + *
  • totalTime - ms since Console was instantiated or reset
  • + *
+ * + * @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 + * this.clearConsole(). + * + * @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']}); diff --git a/lib/yui/3.0.0/cookie/cookie-debug.js b/lib/yui/3.0.0/cookie/cookie-debug.js new file mode 100644 index 0000000000..4190ef4f4b --- /dev/null +++ b/lib/yui/3.0.0/cookie/cookie-debug.js @@ -0,0 +1,491 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/cookie/cookie-min.js b/lib/yui/3.0.0/cookie/cookie-min.js new file mode 100644 index 0000000000..e80ed476b8 --- /dev/null +++ b/lib/yui/3.0.0/cookie/cookie-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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;O0){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 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']}); diff --git a/lib/yui/3.0.0/cssbase/base-context-min.css b/lib/yui/3.0.0/cssbase/base-context-min.css new file mode 100644 index 0000000000..f92c93e5eb --- /dev/null +++ b/lib/yui/3.0.0/cssbase/base-context-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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 diff --git a/lib/yui/3.0.0/cssbase/base-context.css b/lib/yui/3.0.0/cssbase/base-context.css new file mode 100644 index 0000000000..afef2b2004 --- /dev/null +++ b/lib/yui/3.0.0/cssbase/base-context.css @@ -0,0 +1,80 @@ +/* +Copyright (c) 2009, 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;} diff --git a/lib/yui/3.0.0/cssbase/base-min.css b/lib/yui/3.0.0/cssbase/base-min.css new file mode 100644 index 0000000000..ba9fc00a2e --- /dev/null +++ b/lib/yui/3.0.0/cssbase/base-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssbase/base.css b/lib/yui/3.0.0/cssbase/base.css new file mode 100644 index 0000000000..3bdce2a7c0 --- /dev/null +++ b/lib/yui/3.0.0/cssbase/base.css @@ -0,0 +1,80 @@ +/* +Copyright (c) 2009, 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;} diff --git a/lib/yui/3.0.0/cssfonts/fonts-context-min.css b/lib/yui/3.0.0/cssfonts/fonts-context-min.css new file mode 100644 index 0000000000..176d243561 --- /dev/null +++ b/lib/yui/3.0.0/cssfonts/fonts-context-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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 diff --git a/lib/yui/3.0.0/cssfonts/fonts-context.css b/lib/yui/3.0.0/cssfonts/fonts-context.css new file mode 100644 index 0000000000..5d0f8abbcf --- /dev/null +++ b/lib/yui/3.0.0/cssfonts/fonts-context.css @@ -0,0 +1,47 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssfonts/fonts-min.css b/lib/yui/3.0.0/cssfonts/fonts-min.css new file mode 100644 index 0000000000..aad94dc7ca --- /dev/null +++ b/lib/yui/3.0.0/cssfonts/fonts-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssfonts/fonts.css b/lib/yui/3.0.0/cssfonts/fonts.css new file mode 100644 index 0000000000..23fb353ffb --- /dev/null +++ b/lib/yui/3.0.0/cssfonts/fonts.css @@ -0,0 +1,47 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssgrids/grids-context-min.css b/lib/yui/3.0.0/cssgrids/grids-context-min.css new file mode 100644 index 0000000000..b19a3af6cd --- /dev/null +++ b/lib/yui/3.0.0/cssgrids/grids-context-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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 diff --git a/lib/yui/3.0.0/cssgrids/grids-context.css b/lib/yui/3.0.0/cssgrids/grids-context.css new file mode 100644 index 0000000000..ae6725a85a --- /dev/null +++ b/lib/yui/3.0.0/cssgrids/grids-context.css @@ -0,0 +1,491 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssgrids/grids-min.css b/lib/yui/3.0.0/cssgrids/grids-min.css new file mode 100644 index 0000000000..f614a63552 --- /dev/null +++ b/lib/yui/3.0.0/cssgrids/grids-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssgrids/grids.css b/lib/yui/3.0.0/cssgrids/grids.css new file mode 100644 index 0000000000..584394b026 --- /dev/null +++ b/lib/yui/3.0.0/cssgrids/grids.css @@ -0,0 +1,481 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssreset/reset-context-min.css b/lib/yui/3.0.0/cssreset/reset-context-min.css new file mode 100644 index 0000000000..fdc4dbf230 --- /dev/null +++ b/lib/yui/3.0.0/cssreset/reset-context-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! 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 diff --git a/lib/yui/3.0.0/cssreset/reset-context.css b/lib/yui/3.0.0/cssreset/reset-context.css new file mode 100644 index 0000000000..6233f21fc2 --- /dev/null +++ b/lib/yui/3.0.0/cssreset/reset-context.css @@ -0,0 +1,126 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssreset/reset-min.css b/lib/yui/3.0.0/cssreset/reset-min.css new file mode 100644 index 0000000000..fb264a0c5a --- /dev/null +++ b/lib/yui/3.0.0/cssreset/reset-min.css @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/cssreset/reset.css b/lib/yui/3.0.0/cssreset/reset.css new file mode 100644 index 0000000000..ccc5479eac --- /dev/null +++ b/lib/yui/3.0.0/cssreset/reset.css @@ -0,0 +1,126 @@ +/* +Copyright (c) 2009, 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 diff --git a/lib/yui/3.0.0/dataschema/dataschema-array-debug.js b/lib/yui/3.0.0/dataschema/dataschema-array-debug.js new file mode 100644 index 0000000000..883a55b25c --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-array-debug.js @@ -0,0 +1,110 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-array-min.js b/lib/yui/3.0.0/dataschema/dataschema-array-min.js new file mode 100644 index 0000000000..c28b3d9c28 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-array-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dataschema/dataschema-array.js b/lib/yui/3.0.0/dataschema/dataschema-array.js new file mode 100644 index 0000000000..ab8008f349 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-array.js @@ -0,0 +1,107 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-base-debug.js b/lib/yui/3.0.0/dataschema/dataschema-base-debug.js new file mode 100644 index 0000000000..11835b1ae8 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-base-debug.js @@ -0,0 +1,74 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-base-min.js b/lib/yui/3.0.0/dataschema/dataschema-base-min.js new file mode 100644 index 0000000000..4e3f2da80c --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-base-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dataschema/dataschema-base.js b/lib/yui/3.0.0/dataschema/dataschema-base.js new file mode 100644 index 0000000000..940155e673 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-base.js @@ -0,0 +1,73 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-debug.js b/lib/yui/3.0.0/dataschema/dataschema-debug.js new file mode 100644 index 0000000000..4a730f6278 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-debug.js @@ -0,0 +1,745 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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']}); + diff --git a/lib/yui/3.0.0/dataschema/dataschema-json-debug.js b/lib/yui/3.0.0/dataschema/dataschema-json-debug.js new file mode 100644 index 0000000000..b1df20ae23 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-json-debug.js @@ -0,0 +1,298 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-json-min.js b/lib/yui/3.0.0/dataschema/dataschema-json-min.js new file mode 100644 index 0000000000..2f21817235 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-json-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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 diff --git a/lib/yui/3.0.0/dataschema/dataschema-json.js b/lib/yui/3.0.0/dataschema/dataschema-json.js new file mode 100644 index 0000000000..7bfe4879ea --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-json.js @@ -0,0 +1,294 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-min.js b/lib/yui/3.0.0/dataschema/dataschema-min.js new file mode 100644 index 0000000000..d0e6a4795d --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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 diff --git a/lib/yui/3.0.0/dataschema/dataschema-text-debug.js b/lib/yui/3.0.0/dataschema/dataschema-text-debug.js new file mode 100644 index 0000000000..2ef36b4f91 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-text-debug.js @@ -0,0 +1,117 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-text-min.js b/lib/yui/3.0.0/dataschema/dataschema-text-min.js new file mode 100644 index 0000000000..59b5d93dd1 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-text-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dataschema/dataschema-text.js b/lib/yui/3.0.0/dataschema/dataschema-text.js new file mode 100644 index 0000000000..417517cf92 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-text.js @@ -0,0 +1,116 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-xml-debug.js b/lib/yui/3.0.0/dataschema/dataschema-xml-debug.js new file mode 100644 index 0000000000..60347d9f14 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-xml-debug.js @@ -0,0 +1,165 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema-xml-min.js b/lib/yui/3.0.0/dataschema/dataschema-xml-min.js new file mode 100644 index 0000000000..e20dd832a3 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-xml-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dataschema/dataschema-xml.js b/lib/yui/3.0.0/dataschema/dataschema-xml.js new file mode 100644 index 0000000000..be3d818201 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema-xml.js @@ -0,0 +1,164 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dataschema/dataschema.js b/lib/yui/3.0.0/dataschema/dataschema.js new file mode 100644 index 0000000000..5090f9e816 --- /dev/null +++ b/lib/yui/3.0.0/dataschema/dataschema.js @@ -0,0 +1,735 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=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']}); + diff --git a/lib/yui/3.0.0/datasource/datasource-arrayschema-debug.js b/lib/yui/3.0.0/datasource/datasource-arrayschema-debug.js new file mode 100644 index 0000000000..7011cc2e2d --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-arrayschema-debug.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-arrayschema-min.js b/lib/yui/3.0.0/datasource/datasource-arrayschema-min.js new file mode 100644 index 0000000000..6c229d14b9 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-arrayschema-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-arrayschema.js b/lib/yui/3.0.0/datasource/datasource-arrayschema.js new file mode 100644 index 0000000000..7011cc2e2d --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-arrayschema.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-cache-debug.js b/lib/yui/3.0.0/datasource/datasource-cache-debug.js new file mode 100644 index 0000000000..19c96694c3 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-cache-debug.js @@ -0,0 +1,136 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
cached (Object)
True when response is cached.
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-cache-min.js b/lib/yui/3.0.0/datasource/datasource-cache-min.js new file mode 100644 index 0000000000..dd5888f419 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-cache-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-cache.js b/lib/yui/3.0.0/datasource/datasource-cache.js new file mode 100644 index 0000000000..19c96694c3 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-cache.js @@ -0,0 +1,136 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
cached (Object)
True when response is cached.
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-debug.js b/lib/yui/3.0.0/datasource/datasource-debug.js new file mode 100644 index 0000000000..ad6b218bfb --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-debug.js @@ -0,0 +1,1471 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
+ */ + + }, + + /** + * Manages request/response transaction. Must fire response + * 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @protected + */ + _defResponseFn: function(e) { + // Send the response back to the callback + DSLocal.issueCallback(e); + }, + + /** + * Generates a unique transaction ID and fires request event. + * + * @method sendRequest + * @param request {Object} Request. + * @param callback {Object} An object literal with the following properties: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data payload that will be passed back to the success and failure handlers.
+ *
+ * @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): + *
+ *
interval {Number}
+ *
Interval ID of in-progress queue.
+ *
conn
+ *
In-progress connection identifier (if applicable).
+ *
requests {Object[]}
+ *
Array of queued request objects: {request:request, callback:callback}.
+ *
+ * @type Object + * @default {interval:null, conn:null, requests:[]} + * @private + */ + _queue: null, + + /** + * Passes query string to IO. Fires response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ * + *
ignoreStaleResponses
+ *
Send all requests, but handle only the response for the most recently sent request.
+ *
allowAll
+ *
Send all requests and handle all responses.
+ *
+ * + * @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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
cached (Object)
True when response is cached.
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data that will be passed back to the success and failure handlers.
+ *
+ * @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']}); + diff --git a/lib/yui/3.0.0/datasource/datasource-function-debug.js b/lib/yui/3.0.0/datasource/datasource-function-debug.js new file mode 100644 index 0000000000..2b8ff9260f --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-function-debug.js @@ -0,0 +1,115 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-function-min.js b/lib/yui/3.0.0/datasource/datasource-function-min.js new file mode 100644 index 0000000000..5bdc92e611 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-function-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-function.js b/lib/yui/3.0.0/datasource/datasource-function.js new file mode 100644 index 0000000000..2b8ff9260f --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-function.js @@ -0,0 +1,115 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-get-debug.js b/lib/yui/3.0.0/datasource/datasource-get-debug.js new file mode 100644 index 0000000000..3b8c4b61ec --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-get-debug.js @@ -0,0 +1,228 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ * + *
ignoreStaleResponses
+ *
Send all requests, but handle only the response for the most recently sent request.
+ *
allowAll
+ *
Send all requests and handle all responses.
+ *
+ * + * @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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-get-min.js b/lib/yui/3.0.0/datasource/datasource-get-min.js new file mode 100644 index 0000000000..91f00b3208 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-get-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-get.js b/lib/yui/3.0.0/datasource/datasource-get.js new file mode 100644 index 0000000000..5fd0ee1c30 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-get.js @@ -0,0 +1,226 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ * + *
ignoreStaleResponses
+ *
Send all requests, but handle only the response for the most recently sent request.
+ *
allowAll
+ *
Send all requests and handle all responses.
+ *
+ * + * @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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-io-debug.js b/lib/yui/3.0.0/datasource/datasource-io-debug.js new file mode 100644 index 0000000000..3a431ee4b8 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-io-debug.js @@ -0,0 +1,156 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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): + *
+ *
interval {Number}
+ *
Interval ID of in-progress queue.
+ *
conn
+ *
In-progress connection identifier (if applicable).
+ *
requests {Object[]}
+ *
Array of queued request objects: {request:request, callback:callback}.
+ *
+ * @type Object + * @default {interval:null, conn:null, requests:[]} + * @private + */ + _queue: null, + + /** + * Passes query string to IO. Fires response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-io-min.js b/lib/yui/3.0.0/datasource/datasource-io-min.js new file mode 100644 index 0000000000..200ad45349 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-io-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-io.js b/lib/yui/3.0.0/datasource/datasource-io.js new file mode 100644 index 0000000000..a3883bea69 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-io.js @@ -0,0 +1,154 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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): + *
+ *
interval {Number}
+ *
Interval ID of in-progress queue.
+ *
conn
+ *
In-progress connection identifier (if applicable).
+ *
requests {Object[]}
+ *
Array of queued request objects: {request:request, callback:callback}.
+ *
+ * @type Object + * @default {interval:null, conn:null, requests:[]} + * @private + */ + _queue: null, + + /** + * Passes query string to IO. Fires response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-jsonschema-debug.js b/lib/yui/3.0.0/datasource/datasource-jsonschema-debug.js new file mode 100644 index 0000000000..afc316f406 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-jsonschema-debug.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-jsonschema-min.js b/lib/yui/3.0.0/datasource/datasource-jsonschema-min.js new file mode 100644 index 0000000000..930917be65 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-jsonschema-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-jsonschema.js b/lib/yui/3.0.0/datasource/datasource-jsonschema.js new file mode 100644 index 0000000000..afc316f406 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-jsonschema.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-local-debug.js b/lib/yui/3.0.0/datasource/datasource-local-debug.js new file mode 100644 index 0000000000..9c5d846a95 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-local-debug.js @@ -0,0 +1,340 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
+ */ + + }, + + /** + * Manages request/response transaction. Must fire response + * 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @protected + */ + _defResponseFn: function(e) { + // Send the response back to the callback + DSLocal.issueCallback(e); + }, + + /** + * Generates a unique transaction ID and fires request event. + * + * @method sendRequest + * @param request {Object} Request. + * @param callback {Object} An object literal with the following properties: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data payload that will be passed back to the success and failure handlers.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-local-min.js b/lib/yui/3.0.0/datasource/datasource-local-min.js new file mode 100644 index 0000000000..091dbe73e4 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-local-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-local.js b/lib/yui/3.0.0/datasource/datasource-local.js new file mode 100644 index 0000000000..25de7c2df0 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-local.js @@ -0,0 +1,336 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
+ */ + + }, + + /** + * Manages request/response transaction. Must fire response + * 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @protected + */ + _defResponseFn: function(e) { + // Send the response back to the callback + DSLocal.issueCallback(e); + }, + + /** + * Generates a unique transaction ID and fires request event. + * + * @method sendRequest + * @param request {Object} Request. + * @param callback {Object} An object literal with the following properties: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data payload that will be passed back to the success and failure handlers.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-min.js b/lib/yui/3.0.0/datasource/datasource-min.js new file mode 100644 index 0000000000..45122817b7 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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 diff --git a/lib/yui/3.0.0/datasource/datasource-polling-debug.js b/lib/yui/3.0.0/datasource/datasource-polling-debug.js new file mode 100644 index 0000000000..cb7297b5b7 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-polling-debug.js @@ -0,0 +1,93 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data that will be passed back to the success and failure handlers.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-polling-min.js b/lib/yui/3.0.0/datasource/datasource-polling-min.js new file mode 100644 index 0000000000..538b07cd54 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-polling-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-polling.js b/lib/yui/3.0.0/datasource/datasource-polling.js new file mode 100644 index 0000000000..cb7297b5b7 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-polling.js @@ -0,0 +1,93 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data that will be passed back to the success and failure handlers.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-textschema-debug.js b/lib/yui/3.0.0/datasource/datasource-textschema-debug.js new file mode 100644 index 0000000000..3f38090897 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-textschema-debug.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-textschema-min.js b/lib/yui/3.0.0/datasource/datasource-textschema-min.js new file mode 100644 index 0000000000..1e68b5b77f --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-textschema-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-textschema.js b/lib/yui/3.0.0/datasource/datasource-textschema.js new file mode 100644 index 0000000000..3f38090897 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-textschema.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-xmlschema-debug.js b/lib/yui/3.0.0/datasource/datasource-xmlschema-debug.js new file mode 100644 index 0000000000..83d3f8eeda --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-xmlschema-debug.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource-xmlschema-min.js b/lib/yui/3.0.0/datasource/datasource-xmlschema-min.js new file mode 100644 index 0000000000..4173020523 --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-xmlschema-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datasource/datasource-xmlschema.js b/lib/yui/3.0.0/datasource/datasource-xmlschema.js new file mode 100644 index 0000000000..83d3f8eeda --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource-xmlschema.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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']}); diff --git a/lib/yui/3.0.0/datasource/datasource.js b/lib/yui/3.0.0/datasource/datasource.js new file mode 100644 index 0000000000..38c400660b --- /dev/null +++ b/lib/yui/3.0.0/datasource/datasource.js @@ -0,0 +1,1463 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
+ */ + + }, + + /** + * Manages request/response transaction. Must fire response + * 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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Boolean)
Error flag.
+ *
+ *
+ *
+ * @protected + */ + _defResponseFn: function(e) { + // Send the response back to the callback + DSLocal.issueCallback(e); + }, + + /** + * Generates a unique transaction ID and fires request event. + * + * @method sendRequest + * @param request {Object} Request. + * @param callback {Object} An object literal with the following properties: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data payload that will be passed back to the success and failure handlers.
+ *
+ * @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): + *
+ *
interval {Number}
+ *
Interval ID of in-progress queue.
+ *
conn
+ *
In-progress connection identifier (if applicable).
+ *
requests {Object[]}
+ *
Array of queued request objects: {request:request, callback:callback}.
+ *
+ * @type Object + * @default {interval:null, conn:null, requests:[]} + * @private + */ + _queue: null, + + /** + * Passes query string to IO. Fires response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ * + *
ignoreStaleResponses
+ *
Send all requests, but handle only the response for the most recently sent request.
+ *
allowAll
+ *
Send all requests and handle all responses.
+ *
+ * + * @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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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 response event when + * response is received asynchronously. + * + * @method _defRequestFn + * @param e {Event.Facade} Event Facade with the following properties: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object.
+ *
cfg (Object)
Configuration object.
+ *
+ * @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: + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
response (Object)
Normalized response object with the following properties: + *
+ *
cached (Object)
True when response is cached.
+ *
results (Object)
Parsed results.
+ *
meta (Object)
Parsed meta data.
+ *
error (Object)
Error object.
+ *
+ *
+ *
cfg (Object)
Configuration object.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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 + *
+ *
tId (Number)
Unique transaction ID.
+ *
request (Object)
The request.
+ *
callback (Object)
The callback object with the following properties: + *
+ *
success (Function)
Success handler.
+ *
failure (Function)
Failure handler.
+ *
+ *
+ *
data (Object)
Raw data.
+ *
+ * @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: + *
+ *
success
+ *
The function to call when the data is ready.
+ *
failure
+ *
The function to call upon a response failure condition.
+ *
argument
+ *
Arbitrary data that will be passed back to the success and failure handlers.
+ *
+ * @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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype-date-debug.js b/lib/yui/3.0.0/datatype/datatype-date-debug.js new file mode 100644 index 0000000000..4f07bcb28d --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-debug.js @@ -0,0 +1,500 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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)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: + *
+ *
format {String} (Optional)
+ *
+ *

+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at + * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html + * PHP added a few of its own, defined at http://www.php.net/strftime + *

+ *

+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below. + *

+ *

+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat + *

+ *
+ *
%a
abbreviated weekday name according to the current locale
+ *
%A
full weekday name according to the current locale
+ *
%b
abbreviated month name according to the current locale
+ *
%B
full month name according to the current locale
+ *
%c
preferred date and time representation for the current locale
+ *
%C
century number (the year divided by 100 and truncated to an integer, range 00 to 99)
+ *
%d
day of the month as a decimal number (range 01 to 31)
+ *
%D
same as %m/%d/%y
+ *
%e
day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")
+ *
%F
same as %Y-%m-%d (ISO 8601 date format)
+ *
%g
like %G, but without the century
+ *
%G
The 4-digit year corresponding to the ISO week number
+ *
%h
same as %b
+ *
%H
hour as a decimal number using a 24-hour clock (range 00 to 23)
+ *
%I
hour as a decimal number using a 12-hour clock (range 01 to 12)
+ *
%j
day of the year as a decimal number (range 001 to 366)
+ *
%k
hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)
+ *
%l
hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.)
+ *
%m
month as a decimal number (range 01 to 12)
+ *
%M
minute as a decimal number
+ *
%n
newline character
+ *
%p
either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale
+ *
%P
like %p, but lower case
+ *
%r
time in a.m. and p.m. notation equal to %I:%M:%S %p
+ *
%R
time in 24 hour notation equal to %H:%M
+ *
%s
number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC
+ *
%S
second as a decimal number
+ *
%t
tab character
+ *
%T
current time, equal to %H:%M:%S
+ *
%u
weekday as a decimal number [1,7], with 1 representing Monday
+ *
%U
week number of the current year as a decimal number, starting with the + * first Sunday as the first day of the first week
+ *
%V
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.
+ *
%w
day of the week as a decimal, Sunday being 0
+ *
%W
week number of the current year as a decimal number, starting with the + * first Monday as the first day of the first week
+ *
%x
preferred date representation for the current locale without the time
+ *
%X
preferred time representation for the current locale without the date
+ *
%y
year as a decimal number without a century (range 00 to 99)
+ *
%Y
year as a decimal number including the century
+ *
%z
numerical time zone representation
+ *
%Z
time zone name or abbreviation
+ *
%%
a literal "%" character
+ *
+ *
+ *
locale {String} (Optional)
+ *
+ * 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: + *
+ *
en
+ *
English
+ *
en-US
+ *
US English
+ *
en-GB
+ *
British English
+ *
en-AU
+ *
Australian English (identical to British English)
+ *
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"]. + * See Y.DataType.Date.Locale for more information. + *
+ *
+ * @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: + *
    + *
  1. Find an existing locale that matches closely with your needs
  2. + *
  3. Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing + * matches.
  4. + *
  5. Create your own class as an extension of the base class using + * Y.merge, and add your own localisations where needed.
  6. + *
+ * 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: + *
    + *
  1. For French french, we have no existing similar locale, so use + * Y.DataType.Date.Locale["en"] as the base, and extend it: + *
    + *      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"
    + *      });
    + *   
    + *
  2. + *
  3. For Canadian french, we start with French french and change the meaning of \%x: + *
    + *      Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
    + *          x: "%Y-%m-%d"
    + *      });
    + *   
    + *
  4. + *
+ * + * With that, you can use your new locales: + *
+ *    var d = new Date("2008/04/22");
+ *    Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * 
+ * will return: + *
+ *    mardi, 22 avril == 22.04.2008
+ * 
+ * And + *
+ *    Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * 
+ * Will return: + *
+ *   mardi, 22 avril == 2008-04-22
+ * 
+ * @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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype-date-format-debug.js b/lib/yui/3.0.0/datatype/datatype-date-format-debug.js new file mode 100644 index 0000000000..7a8cad8296 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-format-debug.js @@ -0,0 +1,447 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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)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: + *
+ *
format {String} (Optional)
+ *
+ *

+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at + * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html + * PHP added a few of its own, defined at http://www.php.net/strftime + *

+ *

+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below. + *

+ *

+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat + *

+ *
+ *
%a
abbreviated weekday name according to the current locale
+ *
%A
full weekday name according to the current locale
+ *
%b
abbreviated month name according to the current locale
+ *
%B
full month name according to the current locale
+ *
%c
preferred date and time representation for the current locale
+ *
%C
century number (the year divided by 100 and truncated to an integer, range 00 to 99)
+ *
%d
day of the month as a decimal number (range 01 to 31)
+ *
%D
same as %m/%d/%y
+ *
%e
day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")
+ *
%F
same as %Y-%m-%d (ISO 8601 date format)
+ *
%g
like %G, but without the century
+ *
%G
The 4-digit year corresponding to the ISO week number
+ *
%h
same as %b
+ *
%H
hour as a decimal number using a 24-hour clock (range 00 to 23)
+ *
%I
hour as a decimal number using a 12-hour clock (range 01 to 12)
+ *
%j
day of the year as a decimal number (range 001 to 366)
+ *
%k
hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)
+ *
%l
hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.)
+ *
%m
month as a decimal number (range 01 to 12)
+ *
%M
minute as a decimal number
+ *
%n
newline character
+ *
%p
either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale
+ *
%P
like %p, but lower case
+ *
%r
time in a.m. and p.m. notation equal to %I:%M:%S %p
+ *
%R
time in 24 hour notation equal to %H:%M
+ *
%s
number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC
+ *
%S
second as a decimal number
+ *
%t
tab character
+ *
%T
current time, equal to %H:%M:%S
+ *
%u
weekday as a decimal number [1,7], with 1 representing Monday
+ *
%U
week number of the current year as a decimal number, starting with the + * first Sunday as the first day of the first week
+ *
%V
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.
+ *
%w
day of the week as a decimal, Sunday being 0
+ *
%W
week number of the current year as a decimal number, starting with the + * first Monday as the first day of the first week
+ *
%x
preferred date representation for the current locale without the time
+ *
%X
preferred time representation for the current locale without the date
+ *
%y
year as a decimal number without a century (range 00 to 99)
+ *
%Y
year as a decimal number including the century
+ *
%z
numerical time zone representation
+ *
%Z
time zone name or abbreviation
+ *
%%
a literal "%" character
+ *
+ *
+ *
locale {String} (Optional)
+ *
+ * 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: + *
+ *
en
+ *
English
+ *
en-US
+ *
US English
+ *
en-GB
+ *
British English
+ *
en-AU
+ *
Australian English (identical to British English)
+ *
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"]. + * See Y.DataType.Date.Locale for more information. + *
+ *
+ * @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: + *
    + *
  1. Find an existing locale that matches closely with your needs
  2. + *
  3. Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing + * matches.
  4. + *
  5. Create your own class as an extension of the base class using + * Y.merge, and add your own localisations where needed.
  6. + *
+ * 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: + *
    + *
  1. For French french, we have no existing similar locale, so use + * Y.DataType.Date.Locale["en"] as the base, and extend it: + *
    + *      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"
    + *      });
    + *   
    + *
  2. + *
  3. For Canadian french, we start with French french and change the meaning of \%x: + *
    + *      Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
    + *          x: "%Y-%m-%d"
    + *      });
    + *   
    + *
  4. + *
+ * + * With that, you can use your new locales: + *
+ *    var d = new Date("2008/04/22");
+ *    Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * 
+ * will return: + *
+ *    mardi, 22 avril == 22.04.2008
+ * 
+ * And + *
+ *    Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * 
+ * Will return: + *
+ *   mardi, 22 avril == 2008-04-22
+ * 
+ * @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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-date-format-min.js b/lib/yui/3.0.0/datatype/datatype-date-format-min.js new file mode 100644 index 0000000000..1812c6ca86 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-format-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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)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 diff --git a/lib/yui/3.0.0/datatype/datatype-date-format.js b/lib/yui/3.0.0/datatype/datatype-date-format.js new file mode 100644 index 0000000000..45ecc3ec08 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-format.js @@ -0,0 +1,443 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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)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: + *
+ *
format {String} (Optional)
+ *
+ *

+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at + * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html + * PHP added a few of its own, defined at http://www.php.net/strftime + *

+ *

+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below. + *

+ *

+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat + *

+ *
+ *
%a
abbreviated weekday name according to the current locale
+ *
%A
full weekday name according to the current locale
+ *
%b
abbreviated month name according to the current locale
+ *
%B
full month name according to the current locale
+ *
%c
preferred date and time representation for the current locale
+ *
%C
century number (the year divided by 100 and truncated to an integer, range 00 to 99)
+ *
%d
day of the month as a decimal number (range 01 to 31)
+ *
%D
same as %m/%d/%y
+ *
%e
day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")
+ *
%F
same as %Y-%m-%d (ISO 8601 date format)
+ *
%g
like %G, but without the century
+ *
%G
The 4-digit year corresponding to the ISO week number
+ *
%h
same as %b
+ *
%H
hour as a decimal number using a 24-hour clock (range 00 to 23)
+ *
%I
hour as a decimal number using a 12-hour clock (range 01 to 12)
+ *
%j
day of the year as a decimal number (range 001 to 366)
+ *
%k
hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)
+ *
%l
hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.)
+ *
%m
month as a decimal number (range 01 to 12)
+ *
%M
minute as a decimal number
+ *
%n
newline character
+ *
%p
either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale
+ *
%P
like %p, but lower case
+ *
%r
time in a.m. and p.m. notation equal to %I:%M:%S %p
+ *
%R
time in 24 hour notation equal to %H:%M
+ *
%s
number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC
+ *
%S
second as a decimal number
+ *
%t
tab character
+ *
%T
current time, equal to %H:%M:%S
+ *
%u
weekday as a decimal number [1,7], with 1 representing Monday
+ *
%U
week number of the current year as a decimal number, starting with the + * first Sunday as the first day of the first week
+ *
%V
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.
+ *
%w
day of the week as a decimal, Sunday being 0
+ *
%W
week number of the current year as a decimal number, starting with the + * first Monday as the first day of the first week
+ *
%x
preferred date representation for the current locale without the time
+ *
%X
preferred time representation for the current locale without the date
+ *
%y
year as a decimal number without a century (range 00 to 99)
+ *
%Y
year as a decimal number including the century
+ *
%z
numerical time zone representation
+ *
%Z
time zone name or abbreviation
+ *
%%
a literal "%" character
+ *
+ *
+ *
locale {String} (Optional)
+ *
+ * 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: + *
+ *
en
+ *
English
+ *
en-US
+ *
US English
+ *
en-GB
+ *
British English
+ *
en-AU
+ *
Australian English (identical to British English)
+ *
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"]. + * See Y.DataType.Date.Locale for more information. + *
+ *
+ * @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: + *
    + *
  1. Find an existing locale that matches closely with your needs
  2. + *
  3. Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing + * matches.
  4. + *
  5. Create your own class as an extension of the base class using + * Y.merge, and add your own localisations where needed.
  6. + *
+ * 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: + *
    + *
  1. For French french, we have no existing similar locale, so use + * Y.DataType.Date.Locale["en"] as the base, and extend it: + *
    + *      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"
    + *      });
    + *   
    + *
  2. + *
  3. For Canadian french, we start with French french and change the meaning of \%x: + *
    + *      Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
    + *          x: "%Y-%m-%d"
    + *      });
    + *   
    + *
  4. + *
+ * + * With that, you can use your new locales: + *
+ *    var d = new Date("2008/04/22");
+ *    Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * 
+ * will return: + *
+ *    mardi, 22 avril == 22.04.2008
+ * 
+ * And + *
+ *    Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * 
+ * Will return: + *
+ *   mardi, 22 avril == 2008-04-22
+ * 
+ * @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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-date-min.js b/lib/yui/3.0.0/datatype/datatype-date-min.js new file mode 100644 index 0000000000..05deb84bd2 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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)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 diff --git a/lib/yui/3.0.0/datatype/datatype-date-parse-debug.js b/lib/yui/3.0.0/datatype/datatype-date-parse-debug.js new file mode 100644 index 0000000000..85df3f02f4 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-parse-debug.js @@ -0,0 +1,54 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-date-parse-min.js b/lib/yui/3.0.0/datatype/datatype-date-parse-min.js new file mode 100644 index 0000000000..2137c092a7 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-parse-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datatype/datatype-date-parse.js b/lib/yui/3.0.0/datatype/datatype-date-parse.js new file mode 100644 index 0000000000..5ff1a4cb92 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date-parse.js @@ -0,0 +1,53 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-date.js b/lib/yui/3.0.0/datatype/datatype-date.js new file mode 100644 index 0000000000..d5f13c25bf --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-date.js @@ -0,0 +1,495 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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)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: + *
+ *
format {String} (Optional)
+ *
+ *

+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at + * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html + * PHP added a few of its own, defined at http://www.php.net/strftime + *

+ *

+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below. + *

+ *

+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat + *

+ *
+ *
%a
abbreviated weekday name according to the current locale
+ *
%A
full weekday name according to the current locale
+ *
%b
abbreviated month name according to the current locale
+ *
%B
full month name according to the current locale
+ *
%c
preferred date and time representation for the current locale
+ *
%C
century number (the year divided by 100 and truncated to an integer, range 00 to 99)
+ *
%d
day of the month as a decimal number (range 01 to 31)
+ *
%D
same as %m/%d/%y
+ *
%e
day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")
+ *
%F
same as %Y-%m-%d (ISO 8601 date format)
+ *
%g
like %G, but without the century
+ *
%G
The 4-digit year corresponding to the ISO week number
+ *
%h
same as %b
+ *
%H
hour as a decimal number using a 24-hour clock (range 00 to 23)
+ *
%I
hour as a decimal number using a 12-hour clock (range 01 to 12)
+ *
%j
day of the year as a decimal number (range 001 to 366)
+ *
%k
hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)
+ *
%l
hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.)
+ *
%m
month as a decimal number (range 01 to 12)
+ *
%M
minute as a decimal number
+ *
%n
newline character
+ *
%p
either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale
+ *
%P
like %p, but lower case
+ *
%r
time in a.m. and p.m. notation equal to %I:%M:%S %p
+ *
%R
time in 24 hour notation equal to %H:%M
+ *
%s
number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC
+ *
%S
second as a decimal number
+ *
%t
tab character
+ *
%T
current time, equal to %H:%M:%S
+ *
%u
weekday as a decimal number [1,7], with 1 representing Monday
+ *
%U
week number of the current year as a decimal number, starting with the + * first Sunday as the first day of the first week
+ *
%V
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.
+ *
%w
day of the week as a decimal, Sunday being 0
+ *
%W
week number of the current year as a decimal number, starting with the + * first Monday as the first day of the first week
+ *
%x
preferred date representation for the current locale without the time
+ *
%X
preferred time representation for the current locale without the date
+ *
%y
year as a decimal number without a century (range 00 to 99)
+ *
%Y
year as a decimal number including the century
+ *
%z
numerical time zone representation
+ *
%Z
time zone name or abbreviation
+ *
%%
a literal "%" character
+ *
+ *
+ *
locale {String} (Optional)
+ *
+ * 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: + *
+ *
en
+ *
English
+ *
en-US
+ *
US English
+ *
en-GB
+ *
British English
+ *
en-AU
+ *
Australian English (identical to British English)
+ *
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"]. + * See Y.DataType.Date.Locale for more information. + *
+ *
+ * @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: + *
    + *
  1. Find an existing locale that matches closely with your needs
  2. + *
  3. Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing + * matches.
  4. + *
  5. Create your own class as an extension of the base class using + * Y.merge, and add your own localisations where needed.
  6. + *
+ * 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: + *
    + *
  1. For French french, we have no existing similar locale, so use + * Y.DataType.Date.Locale["en"] as the base, and extend it: + *
    + *      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"
    + *      });
    + *   
    + *
  2. + *
  3. For Canadian french, we start with French french and change the meaning of \%x: + *
    + *      Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
    + *          x: "%Y-%m-%d"
    + *      });
    + *   
    + *
  4. + *
+ * + * With that, you can use your new locales: + *
+ *    var d = new Date("2008/04/22");
+ *    Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * 
+ * will return: + *
+ *    mardi, 22 avril == 22.04.2008
+ * 
+ * And + *
+ *    Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * 
+ * Will return: + *
+ *   mardi, 22 avril == 2008-04-22
+ * 
+ * @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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype-debug.js b/lib/yui/3.0.0/datatype/datatype-debug.js new file mode 100644 index 0000000000..64452b367f --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-debug.js @@ -0,0 +1,775 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
prefix {String} + *
String prepended before each number, like a currency designator "$"
+ *
decimalPlaces {Number} + *
Number of decimal places to round. Must be a number 0 to 20.
+ *
decimalSeparator {String} + *
Decimal separator
+ *
thousandsSeparator {String} + *
Thousands separator
+ *
suffix {String} + *
String appended after each number, like " items" (note the space)
+ *
+ * @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)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: + *
+ *
format {String} (Optional)
+ *
+ *

+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at + * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html + * PHP added a few of its own, defined at http://www.php.net/strftime + *

+ *

+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below. + *

+ *

+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat + *

+ *
+ *
%a
abbreviated weekday name according to the current locale
+ *
%A
full weekday name according to the current locale
+ *
%b
abbreviated month name according to the current locale
+ *
%B
full month name according to the current locale
+ *
%c
preferred date and time representation for the current locale
+ *
%C
century number (the year divided by 100 and truncated to an integer, range 00 to 99)
+ *
%d
day of the month as a decimal number (range 01 to 31)
+ *
%D
same as %m/%d/%y
+ *
%e
day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")
+ *
%F
same as %Y-%m-%d (ISO 8601 date format)
+ *
%g
like %G, but without the century
+ *
%G
The 4-digit year corresponding to the ISO week number
+ *
%h
same as %b
+ *
%H
hour as a decimal number using a 24-hour clock (range 00 to 23)
+ *
%I
hour as a decimal number using a 12-hour clock (range 01 to 12)
+ *
%j
day of the year as a decimal number (range 001 to 366)
+ *
%k
hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)
+ *
%l
hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.)
+ *
%m
month as a decimal number (range 01 to 12)
+ *
%M
minute as a decimal number
+ *
%n
newline character
+ *
%p
either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale
+ *
%P
like %p, but lower case
+ *
%r
time in a.m. and p.m. notation equal to %I:%M:%S %p
+ *
%R
time in 24 hour notation equal to %H:%M
+ *
%s
number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC
+ *
%S
second as a decimal number
+ *
%t
tab character
+ *
%T
current time, equal to %H:%M:%S
+ *
%u
weekday as a decimal number [1,7], with 1 representing Monday
+ *
%U
week number of the current year as a decimal number, starting with the + * first Sunday as the first day of the first week
+ *
%V
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.
+ *
%w
day of the week as a decimal, Sunday being 0
+ *
%W
week number of the current year as a decimal number, starting with the + * first Monday as the first day of the first week
+ *
%x
preferred date representation for the current locale without the time
+ *
%X
preferred time representation for the current locale without the date
+ *
%y
year as a decimal number without a century (range 00 to 99)
+ *
%Y
year as a decimal number including the century
+ *
%z
numerical time zone representation
+ *
%Z
time zone name or abbreviation
+ *
%%
a literal "%" character
+ *
+ *
+ *
locale {String} (Optional)
+ *
+ * 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: + *
+ *
en
+ *
English
+ *
en-US
+ *
US English
+ *
en-GB
+ *
British English
+ *
en-AU
+ *
Australian English (identical to British English)
+ *
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"]. + * See Y.DataType.Date.Locale for more information. + *
+ *
+ * @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: + *
    + *
  1. Find an existing locale that matches closely with your needs
  2. + *
  3. Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing + * matches.
  4. + *
  5. Create your own class as an extension of the base class using + * Y.merge, and add your own localisations where needed.
  6. + *
+ * 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: + *
    + *
  1. For French french, we have no existing similar locale, so use + * Y.DataType.Date.Locale["en"] as the base, and extend it: + *
    + *      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"
    + *      });
    + *   
    + *
  2. + *
  3. For Canadian french, we start with French french and change the meaning of \%x: + *
    + *      Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
    + *          x: "%Y-%m-%d"
    + *      });
    + *   
    + *
  4. + *
+ * + * With that, you can use your new locales: + *
+ *    var d = new Date("2008/04/22");
+ *    Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * 
+ * will return: + *
+ *    mardi, 22 avril == 22.04.2008
+ * 
+ * And + *
+ *    Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * 
+ * Will return: + *
+ *   mardi, 22 avril == 2008-04-22
+ * 
+ * @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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype-min.js b/lib/yui/3.0.0/datatype/datatype-min.js new file mode 100644 index 0000000000..0ad772bafd --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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)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 diff --git a/lib/yui/3.0.0/datatype/datatype-number-debug.js b/lib/yui/3.0.0/datatype/datatype-number-debug.js new file mode 100644 index 0000000000..32b264bf48 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-debug.js @@ -0,0 +1,158 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
prefix {String} + *
String prepended before each number, like a currency designator "$"
+ *
decimalPlaces {Number} + *
Number of decimal places to round. Must be a number 0 to 20.
+ *
decimalSeparator {String} + *
Decimal separator
+ *
thousandsSeparator {String} + *
Thousands separator
+ *
suffix {String} + *
String appended after each number, like " items" (note the space)
+ *
+ * @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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype-number-format-debug.js b/lib/yui/3.0.0/datatype/datatype-number-format-debug.js new file mode 100644 index 0000000000..f19e730ddb --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-format-debug.js @@ -0,0 +1,113 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
prefix {String} + *
String prepended before each number, like a currency designator "$"
+ *
decimalPlaces {Number} + *
Number of decimal places to round. Must be a number 0 to 20.
+ *
decimalSeparator {String} + *
Decimal separator
+ *
thousandsSeparator {String} + *
Thousands separator
+ *
suffix {String} + *
String appended after each number, like " items" (note the space)
+ *
+ * @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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-number-format-min.js b/lib/yui/3.0.0/datatype/datatype-number-format-min.js new file mode 100644 index 0000000000..7ea3d09ad6 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-format-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datatype/datatype-number-format.js b/lib/yui/3.0.0/datatype/datatype-number-format.js new file mode 100644 index 0000000000..da865073b0 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-format.js @@ -0,0 +1,112 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
prefix {String} + *
String prepended before each number, like a currency designator "$"
+ *
decimalPlaces {Number} + *
Number of decimal places to round. Must be a number 0 to 20.
+ *
decimalSeparator {String} + *
Decimal separator
+ *
thousandsSeparator {String} + *
Thousands separator
+ *
suffix {String} + *
String appended after each number, like " items" (note the space)
+ *
+ * @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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-number-min.js b/lib/yui/3.0.0/datatype/datatype-number-min.js new file mode 100644 index 0000000000..756077da8e --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datatype/datatype-number-parse-debug.js b/lib/yui/3.0.0/datatype/datatype-number-parse-debug.js new file mode 100644 index 0000000000..3feeb5673c --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-parse-debug.js @@ -0,0 +1,46 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-number-parse-min.js b/lib/yui/3.0.0/datatype/datatype-number-parse-min.js new file mode 100644 index 0000000000..5bbb60e627 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-parse-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datatype/datatype-number-parse.js b/lib/yui/3.0.0/datatype/datatype-number-parse.js new file mode 100644 index 0000000000..24c36dba69 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number-parse.js @@ -0,0 +1,45 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-number.js b/lib/yui/3.0.0/datatype/datatype-number.js new file mode 100644 index 0000000000..ff05f076c6 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-number.js @@ -0,0 +1,156 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
prefix {String} + *
String prepended before each number, like a currency designator "$"
+ *
decimalPlaces {Number} + *
Number of decimal places to round. Must be a number 0 to 20.
+ *
decimalSeparator {String} + *
Decimal separator
+ *
thousandsSeparator {String} + *
Thousands separator
+ *
suffix {String} + *
String appended after each number, like " items" (note the space)
+ *
+ * @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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype-xml-debug.js b/lib/yui/3.0.0/datatype/datatype-xml-debug.js new file mode 100644 index 0000000000..fc399a4a10 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-debug.js @@ -0,0 +1,124 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype-xml-format-debug.js b/lib/yui/3.0.0/datatype/datatype-xml-format-debug.js new file mode 100644 index 0000000000..13ddeb69c3 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-format-debug.js @@ -0,0 +1,61 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-xml-format-min.js b/lib/yui/3.0.0/datatype/datatype-xml-format-min.js new file mode 100644 index 0000000000..95561dc95e --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-format-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datatype/datatype-xml-format.js b/lib/yui/3.0.0/datatype/datatype-xml-format.js new file mode 100644 index 0000000000..cb85be2411 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-format.js @@ -0,0 +1,60 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-xml-min.js b/lib/yui/3.0.0/datatype/datatype-xml-min.js new file mode 100644 index 0000000000..bba724b72f --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datatype/datatype-xml-parse-debug.js b/lib/yui/3.0.0/datatype/datatype-xml-parse-debug.js new file mode 100644 index 0000000000..4cdbd562e9 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-parse-debug.js @@ -0,0 +1,64 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-xml-parse-min.js b/lib/yui/3.0.0/datatype/datatype-xml-parse-min.js new file mode 100644 index 0000000000..51fd328f63 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-parse-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/datatype/datatype-xml-parse.js b/lib/yui/3.0.0/datatype/datatype-xml-parse.js new file mode 100644 index 0000000000..b083ee6752 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml-parse.js @@ -0,0 +1,62 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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' ); diff --git a/lib/yui/3.0.0/datatype/datatype-xml.js b/lib/yui/3.0.0/datatype/datatype-xml.js new file mode 100644 index 0000000000..2c69ad9df1 --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype-xml.js @@ -0,0 +1,121 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); + diff --git a/lib/yui/3.0.0/datatype/datatype.js b/lib/yui/3.0.0/datatype/datatype.js new file mode 100644 index 0000000000..51d808c85f --- /dev/null +++ b/lib/yui/3.0.0/datatype/datatype.js @@ -0,0 +1,765 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
+ *
prefix {String} + *
String prepended before each number, like a currency designator "$"
+ *
decimalPlaces {Number} + *
Number of decimal places to round. Must be a number 0 to 20.
+ *
decimalSeparator {String} + *
Decimal separator
+ *
thousandsSeparator {String} + *
Thousands separator
+ *
suffix {String} + *
String appended after each number, like " items" (note the space)
+ *
+ * @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)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: + *
+ *
format {String} (Optional)
+ *
+ *

+ * Any strftime string is supported, such as "%I:%M:%S %p". strftime has several format specifiers defined by the Open group at + * http://www.opengroup.org/onlinepubs/007908799/xsh/strftime.html + * PHP added a few of its own, defined at http://www.php.net/strftime + *

+ *

+ * This javascript implementation supports all the PHP specifiers and a few more. The full list is below. + *

+ *

+ * If not specified, it defaults to the ISO8601 standard date format: %Y-%m-%d. This may be overridden by changing Y.config.dateFormat + *

+ *
+ *
%a
abbreviated weekday name according to the current locale
+ *
%A
full weekday name according to the current locale
+ *
%b
abbreviated month name according to the current locale
+ *
%B
full month name according to the current locale
+ *
%c
preferred date and time representation for the current locale
+ *
%C
century number (the year divided by 100 and truncated to an integer, range 00 to 99)
+ *
%d
day of the month as a decimal number (range 01 to 31)
+ *
%D
same as %m/%d/%y
+ *
%e
day of the month as a decimal number, a single digit is preceded by a space (range " 1" to "31")
+ *
%F
same as %Y-%m-%d (ISO 8601 date format)
+ *
%g
like %G, but without the century
+ *
%G
The 4-digit year corresponding to the ISO week number
+ *
%h
same as %b
+ *
%H
hour as a decimal number using a 24-hour clock (range 00 to 23)
+ *
%I
hour as a decimal number using a 12-hour clock (range 01 to 12)
+ *
%j
day of the year as a decimal number (range 001 to 366)
+ *
%k
hour as a decimal number using a 24-hour clock (range 0 to 23); single digits are preceded by a blank. (See also %H.)
+ *
%l
hour as a decimal number using a 12-hour clock (range 1 to 12); single digits are preceded by a blank. (See also %I.)
+ *
%m
month as a decimal number (range 01 to 12)
+ *
%M
minute as a decimal number
+ *
%n
newline character
+ *
%p
either "AM" or "PM" according to the given time value, or the corresponding strings for the current locale
+ *
%P
like %p, but lower case
+ *
%r
time in a.m. and p.m. notation equal to %I:%M:%S %p
+ *
%R
time in 24 hour notation equal to %H:%M
+ *
%s
number of seconds since the Epoch, ie, since 1970-01-01 00:00:00 UTC
+ *
%S
second as a decimal number
+ *
%t
tab character
+ *
%T
current time, equal to %H:%M:%S
+ *
%u
weekday as a decimal number [1,7], with 1 representing Monday
+ *
%U
week number of the current year as a decimal number, starting with the + * first Sunday as the first day of the first week
+ *
%V
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.
+ *
%w
day of the week as a decimal, Sunday being 0
+ *
%W
week number of the current year as a decimal number, starting with the + * first Monday as the first day of the first week
+ *
%x
preferred date representation for the current locale without the time
+ *
%X
preferred time representation for the current locale without the date
+ *
%y
year as a decimal number without a century (range 00 to 99)
+ *
%Y
year as a decimal number including the century
+ *
%z
numerical time zone representation
+ *
%Z
time zone name or abbreviation
+ *
%%
a literal "%" character
+ *
+ *
+ *
locale {String} (Optional)
+ *
+ * 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: + *
+ *
en
+ *
English
+ *
en-US
+ *
US English
+ *
en-GB
+ *
British English
+ *
en-AU
+ *
Australian English (identical to British English)
+ *
+ * More locales may be added by subclassing of Y.DataType.Date.Locale["en"]. + * See Y.DataType.Date.Locale for more information. + *
+ *
+ * @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: + *
    + *
  1. Find an existing locale that matches closely with your needs
  2. + *
  3. Use this as your base class. Use Y.DataType.Date.Locale["en"] if nothing + * matches.
  4. + *
  5. Create your own class as an extension of the base class using + * Y.merge, and add your own localisations where needed.
  6. + *
+ * 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: + *
    + *
  1. For French french, we have no existing similar locale, so use + * Y.DataType.Date.Locale["en"] as the base, and extend it: + *
    + *      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"
    + *      });
    + *   
    + *
  2. + *
  3. For Canadian french, we start with French french and change the meaning of \%x: + *
    + *      Y.DataType.Date.Locale["fr-CA"] = Y.merge(Y.DataType.Date.Locale["fr"], {
    + *          x: "%Y-%m-%d"
    + *      });
    + *   
    + *
  4. + *
+ * + * With that, you can use your new locales: + *
+ *    var d = new Date("2008/04/22");
+ *    Y.DataType.Date.format(d, { format: "%A, %d %B == %x", locale: "fr" });
+ * 
+ * will return: + *
+ *    mardi, 22 avril == 22.04.2008
+ * 
+ * And + *
+ *    Y.DataType.Date.format(d, {format: "%A, %d %B == %x", locale: "fr-CA" });
+ * 
+ * Will return: + *
+ *   mardi, 22 avril == 2008-04-22
+ * 
+ * @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']}); + diff --git a/lib/yui/3.0.0/dd/dd-constrain-debug.js b/lib/yui/3.0.0/dd/dd-constrain-debug.js new file mode 100644 index 0000000000..7d2b5e1459 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-constrain-debug.js @@ -0,0 +1,425 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-constrain-min.js b/lib/yui/3.0.0/dd/dd-constrain-min.js new file mode 100644 index 0000000000..5f3f7fb96c --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-constrain-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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(XU){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=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 diff --git a/lib/yui/3.0.0/dd/dd-constrain.js b/lib/yui/3.0.0/dd/dd-constrain.js new file mode 100644 index 0000000000..7d2b5e1459 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-constrain.js @@ -0,0 +1,425 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-ddm-base-debug.js b/lib/yui/3.0.0/dd/dd-ddm-base-debug.js new file mode 100644 index 0000000000..2d39630a90 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-base-debug.js @@ -0,0 +1,289 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-ddm-base-min.js b/lib/yui/3.0.0/dd/dd-ddm-base-min.js new file mode 100644 index 0000000000..a1539c2d84 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-base-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dd/dd-ddm-base.js b/lib/yui/3.0.0/dd/dd-ddm-base.js new file mode 100644 index 0000000000..2d39630a90 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-base.js @@ -0,0 +1,289 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-ddm-debug.js b/lib/yui/3.0.0/dd/dd-ddm-debug.js new file mode 100644 index 0000000000..b7bc373bf8 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-debug.js @@ -0,0 +1,135 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'), + 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}); diff --git a/lib/yui/3.0.0/dd/dd-ddm-drop-debug.js b/lib/yui/3.0.0/dd/dd-ddm-drop-debug.js new file mode 100644 index 0000000000..525524295b --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-drop-debug.js @@ -0,0 +1,411 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-ddm-drop-min.js b/lib/yui/3.0.0/dd/dd-ddm-drop-min.js new file mode 100644 index 0000000000..88713a806f --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-drop-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dd/dd-ddm-drop.js b/lib/yui/3.0.0/dd/dd-ddm-drop.js new file mode 100644 index 0000000000..525524295b --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-drop.js @@ -0,0 +1,411 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-ddm-min.js b/lib/yui/3.0.0/dd/dd-ddm-min.js new file mode 100644 index 0000000000..37c7f07ef6 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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("
"),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 diff --git a/lib/yui/3.0.0/dd/dd-ddm.js b/lib/yui/3.0.0/dd/dd-ddm.js new file mode 100644 index 0000000000..b7bc373bf8 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-ddm.js @@ -0,0 +1,135 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'), + 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}); diff --git a/lib/yui/3.0.0/dd/dd-debug.js b/lib/yui/3.0.0/dd/dd-debug.js new file mode 100644 index 0000000000..69282f2057 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-debug.js @@ -0,0 +1,3529 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'), + 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('
'), + 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('
'); + + 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}); + diff --git a/lib/yui/3.0.0/dd/dd-drag-debug.js b/lib/yui/3.0.0/dd/dd-drag-debug.js new file mode 100644 index 0000000000..41df2211de --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drag-debug.js @@ -0,0 +1,1100 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-drag-min.js b/lib/yui/3.0.0/dd/dd-drag-min.js new file mode 100644 index 0000000000..e8af792dfa --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drag-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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 diff --git a/lib/yui/3.0.0/dd/dd-drag.js b/lib/yui/3.0.0/dd/dd-drag.js new file mode 100644 index 0000000000..41df2211de --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drag.js @@ -0,0 +1,1100 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-drop-debug.js b/lib/yui/3.0.0/dd/dd-drop-debug.js new file mode 100644 index 0000000000..633a267232 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drop-debug.js @@ -0,0 +1,485 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'); + + 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}); diff --git a/lib/yui/3.0.0/dd/dd-drop-min.js b/lib/yui/3.0.0/dd/dd-drop-min.js new file mode 100644 index 0000000000..5b5d5342f2 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drop-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
');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 diff --git a/lib/yui/3.0.0/dd/dd-drop-plugin-debug.js b/lib/yui/3.0.0/dd/dd-drop-plugin-debug.js new file mode 100644 index 0000000000..68b21b90cd --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drop-plugin-debug.js @@ -0,0 +1,52 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-drop-plugin-min.js b/lib/yui/3.0.0/dd/dd-drop-plugin-min.js new file mode 100644 index 0000000000..33da7e25ec --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drop-plugin-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dd/dd-drop-plugin.js b/lib/yui/3.0.0/dd/dd-drop-plugin.js new file mode 100644 index 0000000000..68b21b90cd --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drop-plugin.js @@ -0,0 +1,52 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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}); diff --git a/lib/yui/3.0.0/dd/dd-drop.js b/lib/yui/3.0.0/dd/dd-drop.js new file mode 100644 index 0000000000..633a267232 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-drop.js @@ -0,0 +1,485 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'); + + 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}); diff --git a/lib/yui/3.0.0/dd/dd-min.js b/lib/yui/3.0.0/dd/dd-min.js new file mode 100644 index 0000000000..9ad1a52417 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-min.js @@ -0,0 +1,12 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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("
"),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("
"),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(XU){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=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('
');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 diff --git a/lib/yui/3.0.0/dd/dd-plugin-debug.js b/lib/yui/3.0.0/dd/dd-plugin-debug.js new file mode 100644 index 0000000000..c45fb88b81 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-plugin-debug.js @@ -0,0 +1,53 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dd/dd-plugin-min.js b/lib/yui/3.0.0/dd/dd-plugin-min.js new file mode 100644 index 0000000000..209cf6b73d --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-plugin-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dd/dd-plugin.js b/lib/yui/3.0.0/dd/dd-plugin.js new file mode 100644 index 0000000000..c45fb88b81 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-plugin.js @@ -0,0 +1,53 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dd/dd-proxy-debug.js b/lib/yui/3.0.0/dd/dd-proxy-debug.js new file mode 100644 index 0000000000..7d49d55647 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-proxy-debug.js @@ -0,0 +1,225 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'), + 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}); diff --git a/lib/yui/3.0.0/dd/dd-proxy-min.js b/lib/yui/3.0.0/dd/dd-proxy-min.js new file mode 100644 index 0000000000..02d0ec02a8 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-proxy-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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("
"),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 diff --git a/lib/yui/3.0.0/dd/dd-proxy.js b/lib/yui/3.0.0/dd/dd-proxy.js new file mode 100644 index 0000000000..7d49d55647 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-proxy.js @@ -0,0 +1,225 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'), + 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}); diff --git a/lib/yui/3.0.0/dd/dd-scroll-debug.js b/lib/yui/3.0.0/dd/dd-scroll-debug.js new file mode 100644 index 0000000000..4a45666a37 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-scroll-debug.js @@ -0,0 +1,413 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dd/dd-scroll-min.js b/lib/yui/3.0.0/dd/dd-scroll-min.js new file mode 100644 index 0000000000..7de0479e17 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-scroll-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dd/dd-scroll.js b/lib/yui/3.0.0/dd/dd-scroll.js new file mode 100644 index 0000000000..4a45666a37 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd-scroll.js @@ -0,0 +1,413 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dd/dd.js b/lib/yui/3.0.0/dd/dd.js new file mode 100644 index 0000000000..69282f2057 --- /dev/null +++ b/lib/yui/3.0.0/dd/dd.js @@ -0,0 +1,3529 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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('
'), + 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('
'), + 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('
'); + + 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}); + diff --git a/lib/yui/3.0.0/dom/dom-base-debug.js b/lib/yui/3.0.0/dom/dom-base-debug.js new file mode 100644 index 0000000000..c7dcc253ef --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-base-debug.js @@ -0,0 +1,740 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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* 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('', doc); + }, + + tr: function(html, doc) { + return create('' + html + '', doc); + }, + + td: function(html, doc) { + return create('' + html + '', 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']}); diff --git a/lib/yui/3.0.0/dom/dom-base-min.js b/lib/yui/3.0.0/dom/dom-base-min.js new file mode 100644 index 0000000000..c1c847efd7 --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-base-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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*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("",Z);},tr:function(Y,Z){return S(""+Y+"",Z);},td:function(Y,Z){return S(""+Y+"",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 diff --git a/lib/yui/3.0.0/dom/dom-base.js b/lib/yui/3.0.0/dom/dom-base.js new file mode 100644 index 0000000000..80181ff6cb --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-base.js @@ -0,0 +1,737 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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* 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('', doc); + }, + + tr: function(html, doc) { + return create('' + html + '', doc); + }, + + td: function(html, doc) { + return create('' + html + '', 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']}); diff --git a/lib/yui/3.0.0/dom/dom-debug.js b/lib/yui/3.0.0/dom/dom-debug.js new file mode 100644 index 0000000000..a594ea5ca3 --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-debug.js @@ -0,0 +1,2486 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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* 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('', doc); + }, + + tr: function(html, doc) { + return create('' + html + '', doc); + }, + + td: function(html, doc) { + return create('' + html + '', 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']}); + diff --git a/lib/yui/3.0.0/dom/dom-min.js b/lib/yui/3.0.0/dom/dom-min.js new file mode 100644 index 0000000000..496452ab59 --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-min.js @@ -0,0 +1,11 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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*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("",Z);},tr:function(Y,Z){return S(""+Y+"",Z);},td:function(Y,Z){return S(""+Y+"",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-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":{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 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']}); diff --git a/lib/yui/3.0.0/dom/dom-screen-min.js b/lib/yui/3.0.0/dom/dom-screen-min.js new file mode 100644 index 0000000000..7e8786c917 --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-screen-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/dom/dom-screen.js b/lib/yui/3.0.0/dom/dom-screen.js new file mode 100644 index 0000000000..de02ad8214 --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-screen.js @@ -0,0 +1,567 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dom/dom-style-debug.js b/lib/yui/3.0.0/dom/dom-style-debug.js new file mode 100644 index 0000000000..638b6ddffb --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-style-debug.js @@ -0,0 +1,511 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dom/dom-style-min.js b/lib/yui/3.0.0/dom/dom-style-min.js new file mode 100644 index 0000000000..9406790941 --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-style-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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-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 diff --git a/lib/yui/3.0.0/dom/dom-style.js b/lib/yui/3.0.0/dom/dom-style.js new file mode 100644 index 0000000000..9b774b3ecc --- /dev/null +++ b/lib/yui/3.0.0/dom/dom-style.js @@ -0,0 +1,506 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dom/dom.js b/lib/yui/3.0.0/dom/dom.js new file mode 100644 index 0000000000..c41260b6d5 --- /dev/null +++ b/lib/yui/3.0.0/dom/dom.js @@ -0,0 +1,2466 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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* 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('', doc); + }, + + tr: function(html, doc) { + return create('' + html + '', doc); + }, + + td: function(html, doc) { + return create('' + html + '', 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']}); + diff --git a/lib/yui/3.0.0/dom/selector-css2-debug.js b/lib/yui/3.0.0/dom/selector-css2-debug.js new file mode 100644 index 0000000000..0f2c11b718 --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-css2-debug.js @@ -0,0 +1,450 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dom/selector-css2-min.js b/lib/yui/3.0.0/dom/selector-css2-min.js new file mode 100644 index 0000000000..28e798522d --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-css2-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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': { + 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']}); diff --git a/lib/yui/3.0.0/dom/selector-css3-debug.js b/lib/yui/3.0.0/dom/selector-css3-debug.js new file mode 100644 index 0000000000..7149667533 --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-css3-debug.js @@ -0,0 +1,151 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dom/selector-css3-min.js b/lib/yui/3.0.0/dom/selector-css3-min.js new file mode 100644 index 0000000000..2a0deb7767 --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-css3-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=0&&J[D]===C){return true;}}}else{for(var D=J.length-I,F=J.length;D>=0;D-=K){if(D-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 diff --git a/lib/yui/3.0.0/dom/selector-css3.js b/lib/yui/3.0.0/dom/selector-css3.js new file mode 100644 index 0000000000..7149667533 --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-css3.js @@ -0,0 +1,151 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/dom/selector-debug.js b/lib/yui/3.0.0/dom/selector-debug.js new file mode 100644 index 0000000000..692f5a620a --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-debug.js @@ -0,0 +1,679 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); + diff --git a/lib/yui/3.0.0/dom/selector-min.js b/lib/yui/3.0.0/dom/selector-min.js new file mode 100644 index 0000000000..ca2f869539 --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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":{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 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']}); diff --git a/lib/yui/3.0.0/dom/selector-native-min.js b/lib/yui/3.0.0/dom/selector-native-min.js new file mode 100644 index 0000000000..66aaad134e --- /dev/null +++ b/lib/yui/3.0.0/dom/selector-native-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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']}); diff --git a/lib/yui/3.0.0/dom/selector.js b/lib/yui/3.0.0/dom/selector.js new file mode 100644 index 0000000000..5210626726 --- /dev/null +++ b/lib/yui/3.0.0/dom/selector.js @@ -0,0 +1,673 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); + diff --git a/lib/yui/3.0.0/dump/dump-debug.js b/lib/yui/3.0.0/dump/dump-debug.js new file mode 100644 index 0000000000..66aa7595fa --- /dev/null +++ b/lib/yui/3.0.0/dump/dump-debug.js @@ -0,0 +1,119 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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' ); diff --git a/lib/yui/3.0.0/dump/dump-min.js b/lib/yui/3.0.0/dump/dump-min.js new file mode 100644 index 0000000000..65f3b66f9c --- /dev/null +++ b/lib/yui/3.0.0/dump/dump-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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;I0)?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 diff --git a/lib/yui/3.0.0/dump/dump.js b/lib/yui/3.0.0/dump/dump.js new file mode 100644 index 0000000000..66aa7595fa --- /dev/null +++ b/lib/yui/3.0.0/dump/dump.js @@ -0,0 +1,119 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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' ); diff --git a/lib/yui/3.0.0/event-custom/event-custom-base-debug.js b/lib/yui/3.0.0/event-custom/event-custom-base-debug.js new file mode 100644 index 0000000000..e821070504 --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-base-debug.js @@ -0,0 +1,1732 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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: + *
    + *
  • The type of event
  • + *
  • All of the arguments fire() was executed with as an array
  • + *
  • The custom object (if any) that was passed into the subscribe() + * method
  • + *
+ * @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: + * + *
    + *
  • + * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false) + *
  • + *
  • + * 'bubbles': whether or not this event bubbles (true) + *
  • + *
  • + * 'context': the default execution context for the listeners (this) + *
  • + *
  • + * 'defaultFn': the default function to execute when this event fires if preventDefault was not called + *
  • + *
  • + * 'emitFacade': whether or not this event emits a facade (false) + *
  • + *
  • + * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' + *
  • + *
  • + * 'fireOnce': if an event is configured to fire once, new subscribers after + * the fire will be notified immediately. + *
  • + *
  • + * 'preventable': whether or not preventDefault() has an effect (true) + *
  • + *
  • + * 'preventedFn': a function that is executed when preventDefault is called + *
  • + *
  • + * 'queuable': whether or not this event can be queued during bubbling (false) + *
  • + *
  • + * 'silent': if silent is true, debug messages are not provided for this event. + *
  • + *
  • + * 'stoppedFn': a function that is executed when stopPropagation is called + *
  • + *
  • + * 'type': the event type (valid option if not provided as the first parameter to publish) + *
  • + *
+ * + * @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? + +})(); + + +/** + * YUI's on method is a unified interface for subscribing to + * most events exposed by YUI. This includes custom events, DOM events, and + * function events. detach is also provided to remove listeners + * serviced by this function. + * + * The signature that on 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. + * + *
    + *
  • Custom events. These events are defined by various + * modules in the library. This type of event is delegated to + * EventTarget's on method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('domready', function() { // start work }); + *
  • + *
  • DOM events. These are moments reported by the browser related + * to browser functionality and user interaction. + * This type of event is delegated to Event's + * attach method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • The specification for the Node(s) to attach the listener + * to. This can be a selector, collections, or Node/Element + * refereces.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('click', function(e) { // something was clicked }, '#someelement'); + *
  • + *
  • Function events. These events can be used to react before or after a + * function is executed. This type of event is delegated to Event.Do's + * before method. + *
      + *
    • The callback to execute
    • + *
    • The object that has the function that will be listened for.
    • + *
    • The name of the function to listen for.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname'); + *
  • + *
+ * + * on corresponds to the moment before any default behavior of + * the event. after works the same way, but these listeners + * execute after the event's default behavior. before is an + * alias for on. + * + * @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 on 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']}); diff --git a/lib/yui/3.0.0/event-custom/event-custom-base-min.js b/lib/yui/3.0.0/event-custom/event-custom-base-min.js new file mode 100644 index 0000000000..05975d934f --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-base-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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;G2)?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 diff --git a/lib/yui/3.0.0/event-custom/event-custom-base.js b/lib/yui/3.0.0/event-custom/event-custom-base.js new file mode 100644 index 0000000000..88c6723a32 --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-base.js @@ -0,0 +1,1722 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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: + *
    + *
  • The type of event
  • + *
  • All of the arguments fire() was executed with as an array
  • + *
  • The custom object (if any) that was passed into the subscribe() + * method
  • + *
+ * @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: + * + *
    + *
  • + * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false) + *
  • + *
  • + * 'bubbles': whether or not this event bubbles (true) + *
  • + *
  • + * 'context': the default execution context for the listeners (this) + *
  • + *
  • + * 'defaultFn': the default function to execute when this event fires if preventDefault was not called + *
  • + *
  • + * 'emitFacade': whether or not this event emits a facade (false) + *
  • + *
  • + * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' + *
  • + *
  • + * 'fireOnce': if an event is configured to fire once, new subscribers after + * the fire will be notified immediately. + *
  • + *
  • + * 'preventable': whether or not preventDefault() has an effect (true) + *
  • + *
  • + * 'preventedFn': a function that is executed when preventDefault is called + *
  • + *
  • + * 'queuable': whether or not this event can be queued during bubbling (false) + *
  • + *
  • + * 'silent': if silent is true, debug messages are not provided for this event. + *
  • + *
  • + * 'stoppedFn': a function that is executed when stopPropagation is called + *
  • + *
  • + * 'type': the event type (valid option if not provided as the first parameter to publish) + *
  • + *
+ * + * @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? + +})(); + + +/** + * YUI's on method is a unified interface for subscribing to + * most events exposed by YUI. This includes custom events, DOM events, and + * function events. detach is also provided to remove listeners + * serviced by this function. + * + * The signature that on 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. + * + *
    + *
  • Custom events. These events are defined by various + * modules in the library. This type of event is delegated to + * EventTarget's on method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('domready', function() { // start work }); + *
  • + *
  • DOM events. These are moments reported by the browser related + * to browser functionality and user interaction. + * This type of event is delegated to Event's + * attach method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • The specification for the Node(s) to attach the listener + * to. This can be a selector, collections, or Node/Element + * refereces.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('click', function(e) { // something was clicked }, '#someelement'); + *
  • + *
  • Function events. These events can be used to react before or after a + * function is executed. This type of event is delegated to Event.Do's + * before method. + *
      + *
    • The callback to execute
    • + *
    • The object that has the function that will be listened for.
    • + *
    • The name of the function to listen for.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname'); + *
  • + *
+ * + * on corresponds to the moment before any default behavior of + * the event. after works the same way, but these listeners + * execute after the event's default behavior. before is an + * alias for on. + * + * @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 on 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']}); diff --git a/lib/yui/3.0.0/event-custom/event-custom-complex-debug.js b/lib/yui/3.0.0/event-custom/event-custom-complex-debug.js new file mode 100644 index 0000000000..753d18cbcc --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-complex-debug.js @@ -0,0 +1,364 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/event-custom/event-custom-complex-min.js b/lib/yui/3.0.0/event-custom/event-custom-complex-min.js new file mode 100644 index 0000000000..9270ba0746 --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-complex-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/event-custom/event-custom-complex.js b/lib/yui/3.0.0/event-custom/event-custom-complex.js new file mode 100644 index 0000000000..5c36d49545 --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-complex.js @@ -0,0 +1,363 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/event-custom/event-custom-debug.js b/lib/yui/3.0.0/event-custom/event-custom-debug.js new file mode 100644 index 0000000000..f800951015 --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-debug.js @@ -0,0 +1,2093 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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: + *
    + *
  • The type of event
  • + *
  • All of the arguments fire() was executed with as an array
  • + *
  • The custom object (if any) that was passed into the subscribe() + * method
  • + *
+ * @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: + * + *
    + *
  • + * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false) + *
  • + *
  • + * 'bubbles': whether or not this event bubbles (true) + *
  • + *
  • + * 'context': the default execution context for the listeners (this) + *
  • + *
  • + * 'defaultFn': the default function to execute when this event fires if preventDefault was not called + *
  • + *
  • + * 'emitFacade': whether or not this event emits a facade (false) + *
  • + *
  • + * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' + *
  • + *
  • + * 'fireOnce': if an event is configured to fire once, new subscribers after + * the fire will be notified immediately. + *
  • + *
  • + * 'preventable': whether or not preventDefault() has an effect (true) + *
  • + *
  • + * 'preventedFn': a function that is executed when preventDefault is called + *
  • + *
  • + * 'queuable': whether or not this event can be queued during bubbling (false) + *
  • + *
  • + * 'silent': if silent is true, debug messages are not provided for this event. + *
  • + *
  • + * 'stoppedFn': a function that is executed when stopPropagation is called + *
  • + *
  • + * 'type': the event type (valid option if not provided as the first parameter to publish) + *
  • + *
+ * + * @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? + +})(); + + +/** + * YUI's on method is a unified interface for subscribing to + * most events exposed by YUI. This includes custom events, DOM events, and + * function events. detach is also provided to remove listeners + * serviced by this function. + * + * The signature that on 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. + * + *
    + *
  • Custom events. These events are defined by various + * modules in the library. This type of event is delegated to + * EventTarget's on method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('domready', function() { // start work }); + *
  • + *
  • DOM events. These are moments reported by the browser related + * to browser functionality and user interaction. + * This type of event is delegated to Event's + * attach method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • The specification for the Node(s) to attach the listener + * to. This can be a selector, collections, or Node/Element + * refereces.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('click', function(e) { // something was clicked }, '#someelement'); + *
  • + *
  • Function events. These events can be used to react before or after a + * function is executed. This type of event is delegated to Event.Do's + * before method. + *
      + *
    • The callback to execute
    • + *
    • The object that has the function that will be listened for.
    • + *
    • The name of the function to listen for.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname'); + *
  • + *
+ * + * on corresponds to the moment before any default behavior of + * the event. after works the same way, but these listeners + * execute after the event's default behavior. before is an + * alias for on. + * + * @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 on 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']}); + diff --git a/lib/yui/3.0.0/event-custom/event-custom-min.js b/lib/yui/3.0.0/event-custom/event-custom-min.js new file mode 100644 index 0000000000..f3e1bda5b8 --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom-min.js @@ -0,0 +1,10 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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;G2)?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 diff --git a/lib/yui/3.0.0/event-custom/event-custom.js b/lib/yui/3.0.0/event-custom/event-custom.js new file mode 100644 index 0000000000..997a85d2a1 --- /dev/null +++ b/lib/yui/3.0.0/event-custom/event-custom.js @@ -0,0 +1,2082 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 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: + *
    + *
  • The type of event
  • + *
  • All of the arguments fire() was executed with as an array
  • + *
  • The custom object (if any) that was passed into the subscribe() + * method
  • + *
+ * @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: + * + *
    + *
  • + * 'broadcast': whether or not the YUI instance and YUI global are notified when the event is fired (false) + *
  • + *
  • + * 'bubbles': whether or not this event bubbles (true) + *
  • + *
  • + * 'context': the default execution context for the listeners (this) + *
  • + *
  • + * 'defaultFn': the default function to execute when this event fires if preventDefault was not called + *
  • + *
  • + * 'emitFacade': whether or not this event emits a facade (false) + *
  • + *
  • + * 'prefix': the prefix for this targets events, e.g., 'menu' in 'menu:click' + *
  • + *
  • + * 'fireOnce': if an event is configured to fire once, new subscribers after + * the fire will be notified immediately. + *
  • + *
  • + * 'preventable': whether or not preventDefault() has an effect (true) + *
  • + *
  • + * 'preventedFn': a function that is executed when preventDefault is called + *
  • + *
  • + * 'queuable': whether or not this event can be queued during bubbling (false) + *
  • + *
  • + * 'silent': if silent is true, debug messages are not provided for this event. + *
  • + *
  • + * 'stoppedFn': a function that is executed when stopPropagation is called + *
  • + *
  • + * 'type': the event type (valid option if not provided as the first parameter to publish) + *
  • + *
+ * + * @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? + +})(); + + +/** + * YUI's on method is a unified interface for subscribing to + * most events exposed by YUI. This includes custom events, DOM events, and + * function events. detach is also provided to remove listeners + * serviced by this function. + * + * The signature that on 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. + * + *
    + *
  • Custom events. These events are defined by various + * modules in the library. This type of event is delegated to + * EventTarget's on method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('domready', function() { // start work }); + *
  • + *
  • DOM events. These are moments reported by the browser related + * to browser functionality and user interaction. + * This type of event is delegated to Event's + * attach method. + *
      + *
    • The type of the event
    • + *
    • The callback to execute
    • + *
    • The specification for the Node(s) to attach the listener + * to. This can be a selector, collections, or Node/Element + * refereces.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example: + * Y.on('click', function(e) { // something was clicked }, '#someelement'); + *
  • + *
  • Function events. These events can be used to react before or after a + * function is executed. This type of event is delegated to Event.Do's + * before method. + *
      + *
    • The callback to execute
    • + *
    • The object that has the function that will be listened for.
    • + *
    • The name of the function to listen for.
    • + *
    • An optional context object
    • + *
    • 0..n additional arguments to supply the callback.
    • + *
    + * Example Y.on(function(arg1, arg2, etc) { // obj.methodname was executed }, obj 'methodname'); + *
  • + *
+ * + * on corresponds to the moment before any default behavior of + * the event. after works the same way, but these listeners + * execute after the event's default behavior. before is an + * alias for on. + * + * @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 on 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']}); + diff --git a/lib/yui/3.0.0/event-simulate/event-simulate-debug.js b/lib/yui/3.0.0/event-simulate/event-simulate-debug.js new file mode 100644 index 0000000000..ca052a80c8 --- /dev/null +++ b/lib/yui/3.0.0/event-simulate/event-simulate-debug.js @@ -0,0 +1,490 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/event-simulate/event-simulate-min.js b/lib/yui/3.0.0/event-simulate/event-simulate-min.js new file mode 100644 index 0000000000..ffde0c85c7 --- /dev/null +++ b/lib/yui/3.0.0/event-simulate/event-simulate-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/event-simulate/event-simulate.js b/lib/yui/3.0.0/event-simulate/event-simulate.js new file mode 100644 index 0000000000..ca052a80c8 --- /dev/null +++ b/lib/yui/3.0.0/event-simulate/event-simulate.js @@ -0,0 +1,490 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/event/event-base-debug.js b/lib/yui/3.0.0/event/event-base-debug.js new file mode 100644 index 0000000000..a7905852bd --- /dev/null +++ b/lib/yui/3.0.0/event/event-base-debug.js @@ -0,0 +1,1388 @@ +/* +Copyright (c) 2009, 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. + * + *

The callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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; iThe callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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 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 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']}); diff --git a/lib/yui/3.0.0/event/event-base-min.js b/lib/yui/3.0.0/event/event-base-min.js new file mode 100644 index 0000000000..863aa7e7f0 --- /dev/null +++ b/lib/yui/3.0.0/event/event-base-min.js @@ -0,0 +1,11 @@ +/* +Copyright (c) 2009, 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;S4)?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;V0);}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;R4?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 diff --git a/lib/yui/3.0.0/event/event-base.js b/lib/yui/3.0.0/event/event-base.js new file mode 100644 index 0000000000..43cf77db31 --- /dev/null +++ b/lib/yui/3.0.0/event/event-base.js @@ -0,0 +1,1378 @@ +/* +Copyright (c) 2009, 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. + * + *

The callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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; iThe callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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 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 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']}); diff --git a/lib/yui/3.0.0/event/event-debug.js b/lib/yui/3.0.0/event/event-debug.js new file mode 100644 index 0000000000..9202179e22 --- /dev/null +++ b/lib/yui/3.0.0/event/event-debug.js @@ -0,0 +1,2242 @@ +/* +Copyright (c) 2009, 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. + * + *

The callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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; iThe callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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 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 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; i4)?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;V0);}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;R4?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;NThe callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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; iThe callback is executed with a single parameter: + * the custom object parameter, if provided.

+ * + * @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 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 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 + *
onSuccess
+ *
+ * callback to execute when the script(s) are finished loading + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onTimeout
+ *
+ * callback to execute when a timeout occurs. + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onEnd
+ *
a function that executes when the transaction finishes, regardless of the exit path
+ *
onFailure
+ *
+ * callback to execute when the script load operation fails + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted successfully
+ *
purge
+ *
A function that, when executed, will remove any nodes + * that were inserted
+ *
+ *
+ *
+ *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
autopurge
+ *
+ * setting to true will let the utilities cleanup routine purge + * the script once loaded + *
+ *
purgethreshold
+ *
+ * The number of transaction before autopurge should be initiated + *
+ *
data
+ *
+ * data that is supplied to the callback when the script(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ * + *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ *
timeout
+ *
Number of milliseconds to wait before aborting and firing the timeout event
+ *
+         *   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
+         *   });
+         * 
+ * @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: + *
+ *
onSuccess
+ *
+ * callback to execute when the css file(s) are finished loading + * The callback receives an object back with the following + * data: + *
win
+ *
the window the link nodes(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ * + *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
data
+ *
+ * data that is supplied to the callbacks when the nodes(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ * + *
+         *      Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+         * 
+ *
+         *   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
+         *   });
+         * 
+ * @return {tId: string} an object containing info about the transaction + */ + css: function(url, opts) { + return _queue("css", url, opts); + } + }; +}(); + +})(); + + +}, '3.0.0' ); diff --git a/lib/yui/3.0.0/get/get-min.js b/lib/yui/3.0.0/get/get-min.js new file mode 100644 index 0000000000..cc7e1fd4c4 --- /dev/null +++ b/lib/yui/3.0.0/get/get-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
onSuccess
+ *
+ * callback to execute when the script(s) are finished loading + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onTimeout
+ *
+ * callback to execute when a timeout occurs. + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onEnd
+ *
a function that executes when the transaction finishes, regardless of the exit path
+ *
onFailure
+ *
+ * callback to execute when the script load operation fails + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted successfully
+ *
purge
+ *
A function that, when executed, will remove any nodes + * that were inserted
+ *
+ *
+ *
+ *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
autopurge
+ *
+ * setting to true will let the utilities cleanup routine purge + * the script once loaded + *
+ *
purgethreshold
+ *
+ * The number of transaction before autopurge should be initiated + *
+ *
data
+ *
+ * data that is supplied to the callback when the script(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ * + *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ *
timeout
+ *
Number of milliseconds to wait before aborting and firing the timeout event
+ *
+         *   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
+         *   });
+         * 
+ * @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: + *
+ *
onSuccess
+ *
+ * callback to execute when the css file(s) are finished loading + * The callback receives an object back with the following + * data: + *
win
+ *
the window the link nodes(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ * + *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
data
+ *
+ * data that is supplied to the callbacks when the nodes(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ * + *
+         *      Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+         * 
+ *
+         *   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
+         *   });
+         * 
+ * @return {tId: string} an object containing info about the transaction + */ + css: function(url, opts) { + return _queue("css", url, opts); + } + }; +}(); + +})(); + + +}, '3.0.0' ); diff --git a/lib/yui/3.0.0/history/history-debug.js b/lib/yui/3.0.0/history/history-debug.js new file mode 100644 index 0000000000..224d4b8c61 --- /dev/null +++ b/lib/yui/3.0.0/history/history-debug.js @@ -0,0 +1,680 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 = '' + fqstate + ''; + + 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 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']}); diff --git a/lib/yui/3.0.0/history/history-min.js b/lib/yui/3.0.0/history/history-min.js new file mode 100644 index 0000000000..8426ae859b --- /dev/null +++ b/lib/yui/3.0.0/history/history-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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=""+W+"";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 diff --git a/lib/yui/3.0.0/history/history.js b/lib/yui/3.0.0/history/history.js new file mode 100644 index 0000000000..224d4b8c61 --- /dev/null +++ b/lib/yui/3.0.0/history/history.js @@ -0,0 +1,680 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 = '' + fqstate + ''; + + 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 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']}); diff --git a/lib/yui/3.0.0/imageloader/imageloader-debug.js b/lib/yui/3.0.0/imageloader/imageloader-debug.js new file mode 100644 index 0000000000..ed52fcc1cd --- /dev/null +++ b/lib/yui/3.0.0/imageloader/imageloader-debug.js @@ -0,0 +1,635 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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, "background:none !important;". + * @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 Y.on. + * @property _triggers + * @private + * @type Array + */ + this._triggers = []; + + /** + * Collection of images (Y.ImgLoadImgObj 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 _foldCheck function and reused later during any subsequent _foldCheck 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 Y.on. + * @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. obj is optional - by default the event is attached to the Y 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 fetch method, with the proper closure, for use with setTimeout. + * @method _getFetchTimeout + * @return {Function} group's fetch method + * @private + */ + _getFetchTimeout: function() { + var self = this; + return function() { self.fetch(); }; + }, + + /** + * Registers an image with the group. + * Arguments are passed through to a Y.ImgLoadImgObj constructor; see that class' attribute documentation for detailed information. "domId" is a required attribute. + * @method registerImage + * @param {Object} * A configuration object literal with attribute name/value pairs (passed through to a Y.ImgLoadImgObj constructor) + * @return {Object} Y.ImgLoadImgObj 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 (foldDistance) 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 "background-image" 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 "src" attribute in the DOM element. + * @attribute srcUrl + * @type String + */ + srcUrl: { + value: null + }, + + /** + * Pixel width of the image. Will be set as a width attribute on the DOM element after the image is fetched. + * Defaults to the natural width of the image (no width 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 height attribute on the DOM element after the image is fetched. + * Defaults to the natural height of the image (no height attribute will be set). + * Usually only used with src images. + * @attribute height + * @type Int + */ + height: { + value: null + }, + + /** + * Whether the image's style.visibility should be set to visible after the image is fetched. + * Used when setting images as visibility:hidden 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 sizingMethod property to be set for the image. + * Only set if isPng value for this image is set to true. + * Defaults to scale. + * @attribute sizingMethod + * @type String + */ + sizingMethod: { + value: 'scale' + }, + + /** + * AlphaImageLoader enabled property to be set for the image. + * Only set if isPng value for this image is set to true. + * Defaults to true. + * @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 Y.get, to avoid repeat calls to access the DOM. + * @property _imgEl + * @private + * @type Object + */ + this._imgEl = null; + + /** + * The vertical position returned from getY, 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 Y.Node) of the DOM element indicated by "domId". + * @method _getImgEl + * @returns {Object} DOM element of the image as a Y.Node 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']}); diff --git a/lib/yui/3.0.0/imageloader/imageloader-min.js b/lib/yui/3.0.0/imageloader/imageloader-min.js new file mode 100644 index 0000000000..adbe211784 --- /dev/null +++ b/lib/yui/3.0.0/imageloader/imageloader-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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;EF){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 diff --git a/lib/yui/3.0.0/imageloader/imageloader.js b/lib/yui/3.0.0/imageloader/imageloader.js new file mode 100644 index 0000000000..c141301027 --- /dev/null +++ b/lib/yui/3.0.0/imageloader/imageloader.js @@ -0,0 +1,623 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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, "background:none !important;". + * @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 Y.on. + * @property _triggers + * @private + * @type Array + */ + this._triggers = []; + + /** + * Collection of images (Y.ImgLoadImgObj 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 _foldCheck function and reused later during any subsequent _foldCheck 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 Y.on. + * @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. obj is optional - by default the event is attached to the Y 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 fetch method, with the proper closure, for use with setTimeout. + * @method _getFetchTimeout + * @return {Function} group's fetch method + * @private + */ + _getFetchTimeout: function() { + var self = this; + return function() { self.fetch(); }; + }, + + /** + * Registers an image with the group. + * Arguments are passed through to a Y.ImgLoadImgObj constructor; see that class' attribute documentation for detailed information. "domId" is a required attribute. + * @method registerImage + * @param {Object} * A configuration object literal with attribute name/value pairs (passed through to a Y.ImgLoadImgObj constructor) + * @return {Object} Y.ImgLoadImgObj 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 (foldDistance) 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 "background-image" 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 "src" attribute in the DOM element. + * @attribute srcUrl + * @type String + */ + srcUrl: { + value: null + }, + + /** + * Pixel width of the image. Will be set as a width attribute on the DOM element after the image is fetched. + * Defaults to the natural width of the image (no width 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 height attribute on the DOM element after the image is fetched. + * Defaults to the natural height of the image (no height attribute will be set). + * Usually only used with src images. + * @attribute height + * @type Int + */ + height: { + value: null + }, + + /** + * Whether the image's style.visibility should be set to visible after the image is fetched. + * Used when setting images as visibility:hidden 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 sizingMethod property to be set for the image. + * Only set if isPng value for this image is set to true. + * Defaults to scale. + * @attribute sizingMethod + * @type String + */ + sizingMethod: { + value: 'scale' + }, + + /** + * AlphaImageLoader enabled property to be set for the image. + * Only set if isPng value for this image is set to true. + * Defaults to true. + * @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 Y.get, to avoid repeat calls to access the DOM. + * @property _imgEl + * @private + * @type Object + */ + this._imgEl = null; + + /** + * The vertical position returned from getY, 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 Y.Node) of the DOM element indicated by "domId". + * @method _getImgEl + * @returns {Object} DOM element of the image as a Y.Node 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']}); diff --git a/lib/yui/3.0.0/io/io-base-debug.js b/lib/yui/3.0.0/io/io-base-debug.js new file mode 100644 index 0000000000..309861e49a --- /dev/null +++ b/lib/yui/3.0.0/io/io-base-debug.js @@ -0,0 +1,743 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/io/io-base-min.js b/lib/yui/3.0.0/io/io-base-min.js new file mode 100644 index 0000000000..c8275963f7 --- /dev/null +++ b/lib/yui/3.0.0/io/io-base-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/io/io-base.js b/lib/yui/3.0.0/io/io-base.js new file mode 100644 index 0000000000..309861e49a --- /dev/null +++ b/lib/yui/3.0.0/io/io-base.js @@ -0,0 +1,743 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/io/io-debug.js b/lib/yui/3.0.0/io/io-debug.js new file mode 100644 index 0000000000..d3a4ef50d0 --- /dev/null +++ b/lib/yui/3.0.0/io/io-debug.js @@ -0,0 +1,1633 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 = '' + + '' + + '' + + '' + + '', + 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(''; + + +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 ';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 diff --git a/lib/yui/3.0.0/node-menunav/node-menunav.js b/lib/yui/3.0.0/node-menunav/node-menunav.js new file mode 100644 index 0000000000..0653b1f728 --- /dev/null +++ b/lib/yui/3.0.0/node-menunav/node-menunav.js @@ -0,0 +1,2165 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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) { + +/** +*

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.

+* +* +*

To use the MenuNav Node Plugin, simply pass a reference to the plugin to a +* Node instance's plug method.

+* +*

+* +* <script type="text/javascript">
+*
+* // Call the "use" method, passing in "node-menunav". This will
+* // load the script and CSS for the MenuNav Node Plugin and all of
+* // the required dependencies.
+*
+* YUI().use("node-menunav", function(Y) {
+*
+* // Use the "contentready" event to initialize the menu when
+* // the subtree of element representing the root menu
+* // (<div id="menu-1">) is ready to be scripted.
+*
+* Y.on("contentready", function () {
+*
+* // The scope of the callback will be a Node instance
+* // representing the root menu (<div id="menu-1">).
+* // Therefore, since "this" represents a Node instance, it
+* // is possible to just call "this.plug" passing in a
+* // reference to the MenuNav Node Plugin.
+*
+* this.plug(Y.Plugin.NodeMenuNav);
+*
+* }, "#menu-1");
+*
+* });
+*
+* </script>
+*
+*

+* +*

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 plug method. +*

+* +*

+* +* <script type="text/javascript">
+*
+* // Call the "use" method, passing in "node-menunav". This will
+* // load the script and CSS for the MenuNav Node Plugin and all of
+* // the required dependencies.
+*
+* YUI().use("node-menunav", function(Y) {
+*
+* // Use the "contentready" event to initialize the menu when
+* // the subtree of element representing the root menu
+* // (<div id="menu-1">) is ready to be scripted.
+*
+* Y.on("contentready", function () {
+*
+* // The scope of the callback will be a Node instance
+* // representing the root menu (<div id="menu-1">).
+* // Therefore, since "this" represents a Node instance, it
+* // is possible to just call "this.plug" passing in a
+* // reference to the MenuNav Node Plugin.
+*
+* this.plug(Y.Plugin.NodeMenuNav, { mouseOutHideDelay: 1000 }); +*

+* }, "#menu-1");
+*
+* });
+*
+* </script>
+*
+*

+* +* @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 plug 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 title +* attribute for the shim used to prevent <select> 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 +* <iframe> shim used to prevent +* <select> 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 +*/ + +// '; + + +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 '; + + 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. + *

+ * This method is invoked after bindUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @method _bindUIStack + * @protected + */ + _bindUIStack: function() { + this.after(ShimChange, this._afterShimChange); + this.after(ZIndexChange, this._afterZIndexChange); + }, + + /** + * Creates/Initializes the DOM to support stackability. + *

+ * This method in invoked after renderUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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. + *

+ * 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. + *

+ * @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']}); diff --git a/lib/yui/3.0.0/widget/widget-stack-min.js b/lib/yui/3.0.0/widget/widget-stack-min.js new file mode 100644 index 0000000000..e9d5f3b276 --- /dev/null +++ b/lib/yui/3.0.0/widget/widget-stack-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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='';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 diff --git a/lib/yui/3.0.0/widget/widget-stack.js b/lib/yui/3.0.0/widget/widget-stack.js new file mode 100644 index 0000000000..f1c8dfbe9f --- /dev/null +++ b/lib/yui/3.0.0/widget/widget-stack.js @@ -0,0 +1,403 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + * Base.build 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 = ''; + + 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. + *

+ * This method is invoked after bindUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @method _bindUIStack + * @protected + */ + _bindUIStack: function() { + this.after(ShimChange, this._afterShimChange); + this.after(ZIndexChange, this._afterZIndexChange); + }, + + /** + * Creates/Initializes the DOM to support stackability. + *

+ * This method in invoked after renderUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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. + *

+ * 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. + *

+ * @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']}); diff --git a/lib/yui/3.0.0/widget/widget-stdmod-debug.js b/lib/yui/3.0.0/widget/widget-stdmod-debug.js new file mode 100644 index 0000000000..4c2c896487 --- /dev/null +++ b/lib/yui/3.0.0/widget/widget-stdmod-debug.js @@ -0,0 +1,755 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 Base.build + * method. + *

+ * 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. + *

+ * @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. + *

+ * Inserts new content before the sections existing content. + *

+ * @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. + *

+ * Inserts new content before the sections existing content. + *

+ * @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. + *

+ * Replaces the sections existing content, with new content. + *

+ * @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 setStdModContent 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 setStdModContent 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 setStdModContent 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. + *
+     *    {
+     *       header : '<div class="yui-widget-hd"></div>',
+     *       body : '<div class="yui-widget-bd"></div>',
+     *       footer : '<div class="yui-widget-ft"></div>'
+     *    }
+     * 
+ * @property WidgetStdMod.TEMPLATES + * @type Object + * @static + */ + StdMod.TEMPLATES = { + header : '
', + body : '
', + footer : '
' + }; + + StdMod.prototype = { + + /** + * Synchronizes the UI to match the Widgets standard module state. + *

+ * This method is invoked after syncUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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. + *

+ * This method is invoked after renderUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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. + *

+ * This method is invoked after bindUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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 where 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 where 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 where 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). + *

+ * 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 where argument. + *

+ * @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. + * + *

NOTE: 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.

+ * + * @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']}); diff --git a/lib/yui/3.0.0/widget/widget-stdmod-min.js b/lib/yui/3.0.0/widget/widget-stdmod-min.js new file mode 100644 index 0000000000..ec92db8996 --- /dev/null +++ b/lib/yui/3.0.0/widget/widget-stdmod-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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:'
',body:'
',footer:'
'};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 ."+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=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 diff --git a/lib/yui/3.0.0/widget/widget-stdmod.js b/lib/yui/3.0.0/widget/widget-stdmod.js new file mode 100644 index 0000000000..4c2c896487 --- /dev/null +++ b/lib/yui/3.0.0/widget/widget-stdmod.js @@ -0,0 +1,755 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 Base.build + * method. + *

+ * 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. + *

+ * @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. + *

+ * Inserts new content before the sections existing content. + *

+ * @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. + *

+ * Inserts new content before the sections existing content. + *

+ * @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. + *

+ * Replaces the sections existing content, with new content. + *

+ * @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 setStdModContent 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 setStdModContent 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 setStdModContent 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. + *
+     *    {
+     *       header : '<div class="yui-widget-hd"></div>',
+     *       body : '<div class="yui-widget-bd"></div>',
+     *       footer : '<div class="yui-widget-ft"></div>'
+     *    }
+     * 
+ * @property WidgetStdMod.TEMPLATES + * @type Object + * @static + */ + StdMod.TEMPLATES = { + header : '
', + body : '
', + footer : '
' + }; + + StdMod.prototype = { + + /** + * Synchronizes the UI to match the Widgets standard module state. + *

+ * This method is invoked after syncUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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. + *

+ * This method is invoked after renderUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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. + *

+ * This method is invoked after bindUI is invoked for the Widget class + * using YUI's aop infrastructure. + *

+ * @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 where 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 where 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 where 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). + *

+ * 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 where argument. + *

+ * @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. + * + *

NOTE: 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.

+ * + * @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']}); diff --git a/lib/yui/3.0.0/widget/widget.js b/lib/yui/3.0.0/widget/widget.js new file mode 100644 index 0000000000..3ccd61e7a7 --- /dev/null +++ b/lib/yui/3.0.0/widget/widget.js @@ -0,0 +1,1329 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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: + *
    + *
  • The render lifecycle method, in addition to the init and destroy + * lifecycle methods provide by Base
  • + *
  • Abstract methods to support consistent MVC structure across + * widgets: renderer, renderUI, bindUI, syncUI
  • + *
  • Support for common widget attributes, such as boundingBox, contentBox, visible, + * disabled, focused, strings
  • + *
+ * + * @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. + *

+ * Defines the static fields which need to be aggregated, + * when this class is used as the main class passed to + * the Base.build method. + *

+ * @property _buildCfg + * @type Object + * @static + * @final + * @private + */ +Widget._buildCfg = { + aggregates : ["HTML_PARSER"] +}; + +/** + * Static property provides a string to identify the class. + *

+ * Currently used to apply class identifiers to the bounding box + * and to classify events fired by the widget. + *

+ * + * @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 Y.config.classNamePrefix attribute used by ClassNameManager and + * Widget.NAME.toLowerCase() (e.g. "yui-widget-xxxxx-yyyyy", based on default values for + * the prefix and widget class name). + *

+ * 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. + *

+ * @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. + *

+ * In the case of nested widgets, the nearest bounding box ancestor is used to + * return the widget instance. + *

+ * @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.: + *
+ *   {
+ *       // 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");
+ *       }
+ *   }
+ * 
+ * + * @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 + * YUI.config.classNamePrefix attribute + the instances NAME property. + * Uses YUI.config.classNameDelimiter attribute to delimit the provided strings. + * e.g. + * + *
+	 *    // 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');
+	 * 
+ *
+ * + * @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). + *

+ * This method should only be invoked once for an initialized + * widget. + *

+ *

+ * It delegates to the widget specific renderer method to do + * the actual work. + *

+ * + * @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. + *

+ * 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. + *

+ */ + 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). + *

+ * Subscribers to the "on" moment of this event, will be notified + * before the widget is rendered. + *

+ *

+ * Subscribers to the "after" momemt of this event, will be notified + * after rendering is complete. + *

+ * + * @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 render 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 HTML_PARSER 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 : "
", + + /** + * Static property defining the markup template for bounding box. + * + * @property BOUNDING_TEMPLATE + * @type String + */ + BOUNDING_TEMPLATE : "
", + + /** + * 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. + *

+ * 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. + *

+ * @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. + *

+ * 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. + *

+ * @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']}); diff --git a/lib/yui/3.0.0/yui-base/yui-base-debug.js b/lib/yui/3.0.0/yui-base/yui-base-debug.js new file mode 100644 index 0000000000..14f8592f11 --- /dev/null +++ b/lib/yui/3.0.0/yui-base/yui-base-debug.js @@ -0,0 +1,2150 @@ +/* +Copyright (c) 2009, 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 -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 + * YUI.namespace("property.package"); + * YUI.namespace("YAHOO.property.package"); + * + * 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: + *
+     * YUI.namespace("really.long.nested.namespace");
+     * 
+ * 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 + *
DEBUG
+ *
Selects the debug versions of the library (e.g., event-debug.js). + * This option will automatically include the Logger widget
+ *
RAW
+ *
Selects the non-minified version of the library (e.g., event.js).
+ * + * You can also define a custom filter, which must be an object literal + * containing a search expression and a replace string: + *
+ *  myFilter: { 
+ *      'searchExp': "-min\\.js", 
+ *      'replaceStr': "-debug.js"
+ *  }
+ * 
+ * + * 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 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 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 -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 + *
+         * 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
+         * 
+ * @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 + *
+         * 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.
+         * 
+ * 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' ); diff --git a/lib/yui/3.0.0/yui-base/yui-base-min.js b/lib/yui/3.0.0/yui-base/yui-base-min.js new file mode 100644 index 0000000000..2610c6ce99 --- /dev/null +++ b/lib/yui/3.0.0/yui-base/yui-base-min.js @@ -0,0 +1,9 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 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-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;P1)){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;GH)?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-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=0){for(E=0;F!==C&&E -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 + * YUI.namespace("property.package"); + * YUI.namespace("YAHOO.property.package"); + * + * 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: + *
+     * YUI.namespace("really.long.nested.namespace");
+     * 
+ * 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 + *
DEBUG
+ *
Selects the debug versions of the library (e.g., event-debug.js). + * This option will automatically include the Logger widget
+ *
RAW
+ *
Selects the non-minified version of the library (e.g., event.js).
+ * + * You can also define a custom filter, which must be an object literal + * containing a search expression and a replace string: + *
+ *  myFilter: { 
+ *      'searchExp': "-min\\.js", 
+ *      'replaceStr': "-debug.js"
+ *  }
+ * 
+ * + * 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 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 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 -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 + *
+         * 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
+         * 
+ * @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 + *
+         * 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.
+         * 
+ * 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' ); diff --git a/lib/yui/3.0.0/yui/get-debug.js b/lib/yui/3.0.0/yui/get-debug.js new file mode 100644 index 0000000000..f42fb45aa4 --- /dev/null +++ b/lib/yui/3.0.0/yui/get-debug.js @@ -0,0 +1,754 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
onSuccess
+ *
+ * callback to execute when the script(s) are finished loading + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onTimeout
+ *
+ * callback to execute when a timeout occurs. + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onEnd
+ *
a function that executes when the transaction finishes, regardless of the exit path
+ *
onFailure
+ *
+ * callback to execute when the script load operation fails + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted successfully
+ *
purge
+ *
A function that, when executed, will remove any nodes + * that were inserted
+ *
+ *
+ *
+ *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
autopurge
+ *
+ * setting to true will let the utilities cleanup routine purge + * the script once loaded + *
+ *
purgethreshold
+ *
+ * The number of transaction before autopurge should be initiated + *
+ *
data
+ *
+ * data that is supplied to the callback when the script(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ * + *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ *
timeout
+ *
Number of milliseconds to wait before aborting and firing the timeout event
+ *
+         *   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
+         *   });
+         * 
+ * @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: + *
+ *
onSuccess
+ *
+ * callback to execute when the css file(s) are finished loading + * The callback receives an object back with the following + * data: + *
win
+ *
the window the link nodes(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ * + *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
data
+ *
+ * data that is supplied to the callbacks when the nodes(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ * + *
+         *      Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+         * 
+ *
+         *   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
+         *   });
+         * 
+ * @return {tId: string} an object containing info about the transaction + */ + css: function(url, opts) { + return _queue("css", url, opts); + } + }; +}(); + +})(); + + +}, '3.0.0' ); diff --git a/lib/yui/3.0.0/yui/get-min.js b/lib/yui/3.0.0/yui/get-min.js new file mode 100644 index 0000000000..cc7e1fd4c4 --- /dev/null +++ b/lib/yui/3.0.0/yui/get-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 + *
onSuccess
+ *
+ * callback to execute when the script(s) are finished loading + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onTimeout
+ *
+ * callback to execute when a timeout occurs. + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onEnd
+ *
a function that executes when the transaction finishes, regardless of the exit path
+ *
onFailure
+ *
+ * callback to execute when the script load operation fails + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted successfully
+ *
purge
+ *
A function that, when executed, will remove any nodes + * that were inserted
+ *
+ *
+ *
+ *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
autopurge
+ *
+ * setting to true will let the utilities cleanup routine purge + * the script once loaded + *
+ *
purgethreshold
+ *
+ * The number of transaction before autopurge should be initiated + *
+ *
data
+ *
+ * data that is supplied to the callback when the script(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ * + *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ *
timeout
+ *
Number of milliseconds to wait before aborting and firing the timeout event
+ *
+         *   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
+         *   });
+         * 
+ * @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: + *
+ *
onSuccess
+ *
+ * callback to execute when the css file(s) are finished loading + * The callback receives an object back with the following + * data: + *
win
+ *
the window the link nodes(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ * + *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
data
+ *
+ * data that is supplied to the callbacks when the nodes(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ * + *
+         *      Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+         * 
+ *
+         *   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
+         *   });
+         * 
+ * @return {tId: string} an object containing info about the transaction + */ + css: function(url, opts) { + return _queue("css", url, opts); + } + }; +}(); + +})(); + + +}, '3.0.0' ); diff --git a/lib/yui/3.0.0/yui/yui-base-debug.js b/lib/yui/3.0.0/yui/yui-base-debug.js new file mode 100644 index 0000000000..41b287201a --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-base-debug.js @@ -0,0 +1,2166 @@ +/* +Copyright (c) 2009, 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 -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 + * YUI.namespace("property.package"); + * YUI.namespace("YAHOO.property.package"); + * + * 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: + *
+     * YUI.namespace("really.long.nested.namespace");
+     * 
+ * 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 + *
DEBUG
+ *
Selects the debug versions of the library (e.g., event-debug.js). + * This option will automatically include the Logger widget
+ *
RAW
+ *
Selects the non-minified version of the library (e.g., event.js).
+ * + * You can also define a custom filter, which must be an object literal + * containing a search expression and a replace string: + *
+ *  myFilter: { 
+ *      'searchExp': "-min\\.js", 
+ *      'replaceStr': "-debug.js"
+ *  }
+ * 
+ * + * 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 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 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 -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 + *
+         * 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
+         * 
+ * @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 + *
+         * 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.
+         * 
+ * 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-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;O1)){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;HI)?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-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=0){for(F=0;G!==D&&F -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 + * YUI.namespace("property.package"); + * YUI.namespace("YAHOO.property.package"); + * + * 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: + *
+     * YUI.namespace("really.long.nested.namespace");
+     * 
+ * 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 + *
DEBUG
+ *
Selects the debug versions of the library (e.g., event-debug.js). + * This option will automatically include the Logger widget
+ *
RAW
+ *
Selects the non-minified version of the library (e.g., event.js).
+ * + * You can also define a custom filter, which must be an object literal + * containing a search expression and a replace string: + *
+ *  myFilter: { 
+ *      'searchExp': "-min\\.js", 
+ *      'replaceStr': "-debug.js"
+ *  }
+ * 
+ * + * 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 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 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 -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 + *
+         * 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
+         * 
+ * @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 + *
+         * 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.
+         * 
+ * 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 -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 + * YUI.namespace("property.package"); + * YUI.namespace("YAHOO.property.package"); + * + * 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: + *
+     * YUI.namespace("really.long.nested.namespace");
+     * 
+ * 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 + *
DEBUG
+ *
Selects the debug versions of the library (e.g., event-debug.js). + * This option will automatically include the Logger widget
+ *
RAW
+ *
Selects the non-minified version of the library (e.g., event.js).
+ * + * You can also define a custom filter, which must be an object literal + * containing a search expression and a replace string: + *
+ *  myFilter: { 
+ *      'searchExp': "-min\\.js", 
+ *      'replaceStr': "-debug.js"
+ *  }
+ * 
+ * + * 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 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 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 -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 + *
+         * 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
+         * 
+ * @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 + *
+         * 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.
+         * 
+ * 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 + *
onSuccess
+ *
+ * callback to execute when the script(s) are finished loading + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onTimeout
+ *
+ * callback to execute when a timeout occurs. + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onEnd
+ *
a function that executes when the transaction finishes, regardless of the exit path
+ *
onFailure
+ *
+ * callback to execute when the script load operation fails + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted successfully
+ *
purge
+ *
A function that, when executed, will remove any nodes + * that were inserted
+ *
+ *
+ *
+ *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
autopurge
+ *
+ * setting to true will let the utilities cleanup routine purge + * the script once loaded + *
+ *
purgethreshold
+ *
+ * The number of transaction before autopurge should be initiated + *
+ *
data
+ *
+ * data that is supplied to the callback when the script(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ * + *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ *
timeout
+ *
Number of milliseconds to wait before aborting and firing the timeout event
+ *
+         *   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
+         *   });
+         * 
+ * @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: + *
+ *
onSuccess
+ *
+ * callback to execute when the css file(s) are finished loading + * The callback receives an object back with the following + * data: + *
win
+ *
the window the link nodes(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ * + *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
data
+ *
+ * data that is supplied to the callbacks when the nodes(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ * + *
+         *      Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+         * 
+ *
+         *   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
+         *   });
+         * 
+ * @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']}); + diff --git a/lib/yui/3.0.0/yui/yui-later-debug.js b/lib/yui/3.0.0/yui/yui-later-debug.js new file mode 100644 index 0000000000..14ad474fd2 --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-later-debug.js @@ -0,0 +1,77 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/yui/yui-later-min.js b/lib/yui/3.0.0/yui/yui-later-min.js new file mode 100644 index 0000000000..243f2ffcda --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-later-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/yui/yui-later.js b/lib/yui/3.0.0/yui/yui-later.js new file mode 100644 index 0000000000..d5b2682566 --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-later.js @@ -0,0 +1,76 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/yui/yui-log-debug.js b/lib/yui/3.0.0/yui/yui-log-debug.js new file mode 100644 index 0000000000..8f6e31c764 --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-log-debug.js @@ -0,0 +1,114 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/yui/yui-log-min.js b/lib/yui/3.0.0/yui/yui-log-min.js new file mode 100644 index 0000000000..6383bfc43d --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-log-min.js @@ -0,0 +1,8 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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 diff --git a/lib/yui/3.0.0/yui/yui-log.js b/lib/yui/3.0.0/yui/yui-log.js new file mode 100644 index 0000000000..8f6e31c764 --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-log.js @@ -0,0 +1,114 @@ +/* +Copyright (c) 2009, Yahoo! Inc. 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']}); diff --git a/lib/yui/3.0.0/yui/yui-min.js b/lib/yui/3.0.0/yui/yui-min.js new file mode 100644 index 0000000000..bd84dc2951 --- /dev/null +++ b/lib/yui/3.0.0/yui/yui-min.js @@ -0,0 +1,10 @@ +/* +Copyright (c) 2009, 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-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;O1)){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;HI)?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-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=0){for(F=0;G!==D&&F -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 + * YUI.namespace("property.package"); + * YUI.namespace("YAHOO.property.package"); + * + * 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: + *
+     * YUI.namespace("really.long.nested.namespace");
+     * 
+ * 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 + *
DEBUG
+ *
Selects the debug versions of the library (e.g., event-debug.js). + * This option will automatically include the Logger widget
+ *
RAW
+ *
Selects the non-minified version of the library (e.g., event.js).
+ * + * You can also define a custom filter, which must be an object literal + * containing a search expression and a replace string: + *
+ *  myFilter: { 
+ *      'searchExp': "-min\\.js", 
+ *      'replaceStr': "-debug.js"
+ *  }
+ * 
+ * + * 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 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 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 -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 + *
+         * 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
+         * 
+ * @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 + *
+         * 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.
+         * 
+ * 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 + *
onSuccess
+ *
+ * callback to execute when the script(s) are finished loading + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onTimeout
+ *
+ * callback to execute when a timeout occurs. + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ *
+ *
onEnd
+ *
a function that executes when the transaction finishes, regardless of the exit path
+ *
onFailure
+ *
+ * callback to execute when the script load operation fails + * The callback receives an object back with the following + * data: + *
+ *
win
+ *
the window the script(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted successfully
+ *
purge
+ *
A function that, when executed, will remove any nodes + * that were inserted
+ *
+ *
+ *
+ *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
autopurge
+ *
+ * setting to true will let the utilities cleanup routine purge + * the script once loaded + *
+ *
purgethreshold
+ *
+ * The number of transaction before autopurge should be initiated + *
+ *
data
+ *
+ * data that is supplied to the callback when the script(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ * + *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ *
timeout
+ *
Number of milliseconds to wait before aborting and firing the timeout event
+ *
+         *   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
+         *   });
+         * 
+ * @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: + *
+ *
onSuccess
+ *
+ * callback to execute when the css file(s) are finished loading + * The callback receives an object back with the following + * data: + *
win
+ *
the window the link nodes(s) were inserted into
+ *
data
+ *
the data object passed in when the request was made
+ *
nodes
+ *
An array containing references to the nodes that were + * inserted
+ *
purge
+ *
A function that, when executed, will remove the nodes + * that were inserted
+ *
+ *
+ * + *
context
+ *
the execution context for the callbacks
+ *
win
+ *
a window other than the one the utility occupies
+ *
data
+ *
+ * data that is supplied to the callbacks when the nodes(s) are + * loaded. + *
+ *
insertBefore
+ *
node or node id that will become the new node's nextSibling
+ *
charset
+ *
Node charset, default utf-8 (deprecated, use the attributes config)
+ *
attributes
+ *
An object literal containing additional attributes to add to the link tags
+ * + *
+         *      Y.Get.css("http://yui.yahooapis.com/2.3.1/build/menu/assets/skins/sam/menu.css");
+         * 
+ *
+         *   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
+         *   });
+         * 
+ * @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']}); + diff --git a/lib/yui/readme_moodle.txt b/lib/yui/readme_moodle.txt index 5e0bbbe6b8..f8d53a7735 100644 --- a/lib/yui/readme_moodle.txt +++ b/lib/yui/readme_moodle.txt @@ -14,4 +14,4 @@ Updated to YUI 2.3.0, 3 August 2007 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)