/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ /** * The YAHOO object is the single global object used by YUI Library. It * contains utility function for setting up namespaces, inheritance, and * logging. YAHOO.util, YAHOO.widget, and YAHOO.example are namespaces * created automatically for and used by the library. * @module yahoo * @title YAHOO Global */ /** * YAHOO_config is not included as part of the library. Instead it is an * object that can be defined by the implementer immediately before * including the YUI library. The properties included in this object * will be used to configure global properties needed as soon as the * library begins to load. * @class YAHOO_config * @static */ /** * A reference to a function that will be executed every time a YAHOO module * is loaded. As parameter, this function will receive the version * information for the module. See * YAHOO.env.getVersion for the description of the version data structure. * @property listener * @type Function * @static * @default undefined */ /** * Set to true if the library will be dynamically loaded after window.onload. * Defaults to false * @property injecting * @type boolean * @static * @default undefined */ /** * Instructs the yuiloader component to dynamically load yui components and * their dependencies. See the yuiloader documentation for more information * about dynamic loading * @property load * @static * @default undefined * @see yuiloader */ /** * Forces the use of the supplied locale where applicable in the library * @property locale * @type string * @static * @default undefined */ if (typeof YAHOO == "undefined" || !YAHOO) { /** * The YAHOO global namespace object. If YAHOO is already defined, the * existing YAHOO object will not be overwritten so that defined * namespaces are preserved. * @class YAHOO * @static */ var YAHOO = {}; } /** * Returns the namespace specified and creates it if it doesn't exist *
 * YAHOO.namespace("property.package");
 * YAHOO.namespace("YAHOO.property.package");
 * 
* Either of the above would create YAHOO.property, then * YAHOO.property.package * * Be careful when naming packages. Reserved words may work in some browsers * and not others. For instance, the following will fail in Safari: *
 * YAHOO.namespace("really.long.nested.namespace");
 * 
* This fails because "long" is a future reserved word in ECMAScript * * For implementation code that uses YUI, do not create your components * in the namespaces defined by YUI ( * YAHOO.util, * YAHOO.widget, * YAHOO.lang, * YAHOO.tool, * YAHOO.example, * YAHOO.env) -- create your own namespace (e.g., 'companyname'). * * @method namespace * @static * @param {String*} arguments 1-n namespaces to create * @return {Object} A reference to the last namespace object created */ YAHOO.namespace = function() { var a=arguments, o=null, i, j, d; for (i=0; i *
name:
The name of the module
*
version:
The version in use
*
build:
The build number in use
*
versions:
All versions that were registered
*
builds:
All builds that were registered.
*
mainClass:
An object that was was stamped with the * current version and build. If * mainClass.VERSION != version or mainClass.BUILD != build, * multiple versions of pieces of the library have been * loaded, potentially causing issues.
* * * @method getVersion * @static * @param {String} name the name of the module (event, slider, etc) * @return {Object} The version info */ YAHOO.env.getVersion = function(name) { return YAHOO.env.modules[name] || null; }; /** * 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. YAHOO.env.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 YAHOO.env.ua * @static */ YAHOO.env.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 */ ie: 0, /** * Opera version number or 0. Example: 9.2 * @property opera * @type float */ 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 */ gecko: 0, /** * AppleWebKit version. KHTML browsers that are not WebKit browsers * will evaluate to 1, other browsers 0. Example: 418.9.1 *
         * 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).  
         * 3.x yahoo.com, flickr:422     <-- Safari 3.x hacks the user agent
         *                                   string when hitting yahoo.com and 
         *                                   flickr.com.
         * 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://developer.apple.com/internet/safari/uamatrix.html * @property webkit * @type float */ 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 */ 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 = navigator && navigator.userAgent, loc = window && window.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[^\/]*/); if (m) { o.mobile = m[0]; // Nokia N-series, 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; }(); /* * Initializes the global by creating the default namespaces and applying * any new configuration information that is detected. This is the setup * for env. * @method init * @static * @private */ (function() { YAHOO.namespace("util", "widget", "example"); /*global YAHOO_config*/ if ("undefined" !== typeof YAHOO_config) { var l=YAHOO_config.listener, ls=YAHOO.env.listeners,unique=true, i; if (l) { // if YAHOO is loaded multiple times we need to check to see if // this is a new config object. If it is, add the new component // load listener to the stack for (i=0; i 0) ? L.dump(o[i], d-1) : OBJ); } else { s.push(o[i]); } s.push(COMMA); } if (s.length > 1) { s.pop(); } s.push("]"); // objects {k1 => v1, k2 => v2} } else { s.push("{"); for (i in o) { if (L.hasOwnProperty(o, i)) { 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); } } if (s.length > 1) { s.pop(); } s.push("}"); } return s.join(""); }, /** * Does variable substitution on a string. It scans through the string * looking for expressions enclosed in { } braces. If an expression * is found, it is used a key on the object. If there is a space in * the key, the first word is used for the key and the rest is provided * to an optional function to be used to programatically determine the * value (the extra information might be used for this decision). If * the value for the key in the object, or what is returned from the * function has a string value, number value, or object value, it is * substituted for the bracket expression and it repeats. If this * value is an object, it uses the Object's toString() if this has * been overridden, otherwise it does a shallow dump of the key/value * pairs. * @method substitute * @since 2.3.0 * @param s {String} The string that will be modified. * @param o {Object} An object containing the replacement values * @param f {Function} An optional function that can be used to * process each match. It receives the key, * value, and any extra metadata included with * the key inside of the braces. * @return {String} the substituted string */ substitute: function (s, o, f) { var i, j, k, key, v, meta, saved=[], token, DUMP='dump', SPACE=' ', LBRACE='{', RBRACE='}', dump, objstr; for (;;) { i = s.lastIndexOf(LBRACE); if (i < 0) { break; } j = s.indexOf(RBRACE, i); if (i + 1 >= j) { break; } //Extract key and meta info token = s.substring(i + 1, j); key = token; meta = null; k = key.indexOf(SPACE); if (k > -1) { meta = key.substring(k + 1); key = key.substring(0, k); } // lookup the value v = o[key]; // if a substitution function was provided, execute it if (f) { v = f(key, v, meta); } if (L.isObject(v)) { if (L.isArray(v)) { v = L.dump(v, parseInt(meta, 10)); } else { meta = meta || ""; // look for the keyword 'dump', if found force obj dump dump = meta.indexOf(DUMP); if (dump > -1) { meta = meta.substring(4); } objstr = v.toString(); // use the toString if it is not the Object toString // and the 'dump' meta info was not found if (objstr === OBJECT_TOSTRING || dump > -1) { v = L.dump(v, parseInt(meta, 10)); } else { v = objstr; } } } else if (!L.isString(v) && !L.isNumber(v)) { // This {block} has no replace string. Save it for later. v = "~-" + saved.length + "-~"; saved[saved.length] = token; // break; } s = s.substring(0, i) + v + s.substring(j + 1); } // restore saved {block}s for (i=saved.length-1; i>=0; i=i-1) { s = s.replace(new RegExp("~-" + i + "-~"), "{" + saved[i] + "}", "g"); } return s; }, /** * Returns a string without any leading or trailing whitespace. If * the input is not a string, the input will be returned untouched. * @method trim * @since 2.3.0 * @param s {string} the string to trim * @return {string} the trimmed string */ trim: function(s){ try { return s.replace(/^\s+|\s+$/g, ""); } catch(e) { return s; } }, /** * Returns a new object containing all of the properties of * all the supplied objects. The properties from later objects * will overwrite those in earlier objects. * @method merge * @since 2.3.0 * @param arguments {Object*} the objects to merge * @return the new merged object */ merge: function() { var o={}, a=arguments, l=a.length, i; for (i=0; i * var A = function() {}; * A.prototype.foo = 'foo'; * var a = new A(); * a.foo = 'foo'; * alert(a.hasOwnProperty('foo')); // true * alert(YAHOO.lang.hasOwnProperty(a, 'foo')); // false when using fallback * * @method hasOwnProperty * @param {any} o The object being testing * @param prop {string} the name of the property to test * @return {boolean} the result */ L.hasOwnProperty = (OP.hasOwnProperty) ? function(o, prop) { return o && o.hasOwnProperty(prop); } : function(o, prop) { return !L.isUndefined(o[prop]) && o.constructor.prototype[prop] !== o[prop]; }; // new lang wins OB.augmentObject(L, OB, true); /* * An alias for YAHOO.lang * @class YAHOO.util.Lang */ YAHOO.util.Lang = L; /** * Same as YAHOO.lang.augmentObject, except it only applies prototype * properties. This is an alias for augmentProto. * @see YAHOO.lang.augmentObject * @method augment * @static * @param {Function} r the object to receive the augmentation * @param {Function} s the object that supplies the properties to augment * @param {String*|boolean} arguments zero or more properties methods to * augment the receiver with. If none specified, everything * in the supplier will be used unless it would * overwrite an existing property in the receiver. if true * is specified as the third parameter, all properties will * be applied and will overwrite an existing property in * the receiver */ L.augment = L.augmentProto; /** * An alias for YAHOO.lang.augment * @for YAHOO * @method augment * @static * @param {Function} r the object to receive the augmentation * @param {Function} s the object that supplies the properties to augment * @param {String*} arguments zero or more properties methods to * augment the receiver with. If none specified, everything * in the supplier will be used unless it would * overwrite an existing property in the receiver */ YAHOO.augment = L.augmentProto; /** * An alias for YAHOO.lang.extend * @method extend * @static * @param {Function} subc the object to modify * @param {Function} superc the object to inherit * @param {Object} overrides additional properties/methods to add to the * subclass prototype. These will override the * matching items obtained from the superclass if present. */ YAHOO.extend = L.extend; })(); YAHOO.register("yahoo", YAHOO, {version: "2.8.0r4", build: "2446"}); /* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ /** * The dom module provides helper methods for manipulating Dom elements. * @module dom * */ (function() { // for use with generateId (global to save state if Dom is overwritten) YAHOO.env._id_counter = YAHOO.env._id_counter || 0; // internal shorthand var Y = YAHOO.util, lang = YAHOO.lang, UA = YAHOO.env.ua, trim = YAHOO.lang.trim, propertyCache = {}, // for faster hyphen converts reCache = {}, // cache className regexes RE_TABLE = /^t(?:able|d|h)$/i, // for _calcBorders RE_COLOR = /color$/i, // DOM aliases document = window.document, documentElement = document.documentElement, // string constants OWNER_DOCUMENT = 'ownerDocument', DEFAULT_VIEW = 'defaultView', DOCUMENT_ELEMENT = 'documentElement', COMPAT_MODE = 'compatMode', OFFSET_LEFT = 'offsetLeft', OFFSET_TOP = 'offsetTop', OFFSET_PARENT = 'offsetParent', PARENT_NODE = 'parentNode', NODE_TYPE = 'nodeType', TAG_NAME = 'tagName', SCROLL_LEFT = 'scrollLeft', SCROLL_TOP = 'scrollTop', GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect', GET_COMPUTED_STYLE = 'getComputedStyle', CURRENT_STYLE = 'currentStyle', CSS1_COMPAT = 'CSS1Compat', _BACK_COMPAT = 'BackCompat', _CLASS = 'class', // underscore due to reserved word CLASS_NAME = 'className', EMPTY = '', SPACE = ' ', C_START = '(?:^|\\s)', C_END = '(?= |$)', G = 'g', POSITION = 'position', FIXED = 'fixed', RELATIVE = 'relative', LEFT = 'left', TOP = 'top', MEDIUM = 'medium', BORDER_LEFT_WIDTH = 'borderLeftWidth', BORDER_TOP_WIDTH = 'borderTopWidth', // brower detection isOpera = UA.opera, isSafari = UA.webkit, isGecko = UA.gecko, isIE = UA.ie; /** * Provides helper methods for DOM elements. * @namespace YAHOO.util * @class Dom * @requires yahoo, event */ Y.Dom = { CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8 'for': 'htmlFor', 'class': CLASS_NAME } : { // w3c 'htmlFor': 'for', 'className': _CLASS }, DOT_ATTRIBUTES: {}, /** * Returns an HTMLElement reference. * @method get * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements. * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements. */ get: function(el) { var id, nodes, c, i, len, attr; if (el) { if (el[NODE_TYPE] || el.item) { // Node, or NodeList return el; } if (typeof el === 'string') { // id id = el; el = document.getElementById(el); attr = (el) ? el.attributes : null; if (el && attr && attr.id && attr.id.value === id) { // IE: avoid false match on "name" attribute return el; } else if (el && document.all) { // filter by name el = null; nodes = document.all[id]; for (i = 0, len = nodes.length; i < len; ++i) { if (nodes[i].id === id) { return nodes[i]; } } } return el; } if (YAHOO.util.Element && el instanceof YAHOO.util.Element) { el = el.get('element'); } if ('length' in el) { // array-like c = []; for (i = 0, len = el.length; i < len; ++i) { c[c.length] = Y.Dom.get(el[i]); } return c; } return el; // some other object, just pass it back } return null; }, getComputedStyle: function(el, property) { if (window[GET_COMPUTED_STYLE]) { return el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null)[property]; } else if (el[CURRENT_STYLE]) { return Y.Dom.IE_ComputedStyle.get(el, property); } }, /** * Normalizes currentStyle and ComputedStyle. * @method getStyle * @param {String | HTMLElement |Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. * @param {String} property The style property whose value is returned. * @return {String | Array} The current value of the style property for the element(s). */ getStyle: function(el, property) { return Y.Dom.batch(el, Y.Dom._getStyle, property); }, // branching at load instead of runtime _getStyle: function() { if (window[GET_COMPUTED_STYLE]) { // W3C DOM method return function(el, property) { property = (property === 'float') ? property = 'cssFloat' : Y.Dom._toCamel(property); var value = el.style[property], computed; if (!value) { computed = el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null); if (computed) { // test computed before touching for safari value = computed[property]; } } return value; }; } else if (documentElement[CURRENT_STYLE]) { return function(el, property) { var value; switch(property) { case 'opacity' :// IE opacity uses filter value = 100; try { // will error if no DXImageTransform value = el.filters['DXImageTransform.Microsoft.Alpha'].opacity; } catch(e) { try { // make sure its in the document value = el.filters('alpha').opacity; } catch(err) { } } return value / 100; case 'float': // fix reserved word property = 'styleFloat'; // fall through default: property = Y.Dom._toCamel(property); value = el[CURRENT_STYLE] ? el[CURRENT_STYLE][property] : null; return ( el.style[property] || value ); } }; } }(), /** * Wrapper for setting style properties of HTMLElements. Normalizes "opacity" across modern browsers. * @method setStyle * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. * @param {String} property The style property to be set. * @param {String} val The value to apply to the given property. */ setStyle: function(el, property, val) { Y.Dom.batch(el, Y.Dom._setStyle, { prop: property, val: val }); }, _setStyle: function() { if (isIE) { return function(el, args) { var property = Y.Dom._toCamel(args.prop), val = args.val; if (el) { switch (property) { case 'opacity': if ( lang.isString(el.style.filter) ) { // in case not appended el.style.filter = 'alpha(opacity=' + val * 100 + ')'; if (!el[CURRENT_STYLE] || !el[CURRENT_STYLE].hasLayout) { el.style.zoom = 1; // when no layout or cant tell } } break; case 'float': property = 'styleFloat'; default: el.style[property] = val; } } else { } }; } else { return function(el, args) { var property = Y.Dom._toCamel(args.prop), val = args.val; if (el) { if (property == 'float') { property = 'cssFloat'; } el.style[property] = val; } else { } }; } }(), /** * 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 {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM * reference, or an Array of IDs and/or HTMLElements * @return {Array} The XY position of the element(s) */ getXY: function(el) { return Y.Dom.batch(el, Y.Dom._getXY); }, _canPosition: function(el) { return ( Y.Dom._getStyle(el, 'display') !== 'none' && Y.Dom._inDoc(el) ); }, _getXY: function() { if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) { return function(node) { var scrollLeft, scrollTop, box, doc, off1, off2, mode, bLeft, bTop, floor = Math.floor, // TODO: round? xy = false; if (Y.Dom._canPosition(node)) { box = node[GET_BOUNDING_CLIENT_RECT](); doc = node[OWNER_DOCUMENT]; scrollLeft = Y.Dom.getDocumentScrollLeft(doc); scrollTop = Y.Dom.getDocumentScrollTop(doc); xy = [floor(box[LEFT]), floor(box[TOP])]; if (isIE && UA.ie < 8) { // IE < 8: viewport off by 2 off1 = 2; off2 = 2; mode = doc[COMPAT_MODE]; if (UA.ie === 6) { if (mode !== _BACK_COMPAT) { off1 = 0; off2 = 0; } } if ((mode === _BACK_COMPAT)) { bLeft = _getComputedStyle(doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH); bTop = _getComputedStyle(doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH); 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; } // gecko may return sub-pixel (non-int) values xy[0] = floor(xy[0]); xy[1] = floor(xy[1]); } else { } return xy; }; } else { return function(node) { // ff2, safari: manually calculate by crawling up offsetParents var docScrollLeft, docScrollTop, scrollTop, scrollLeft, bCheck, xy = false, parentNode = node; if (Y.Dom._canPosition(node) ) { xy = [node[OFFSET_LEFT], node[OFFSET_TOP]]; docScrollLeft = Y.Dom.getDocumentScrollLeft(node[OWNER_DOCUMENT]); docScrollTop = Y.Dom.getDocumentScrollTop(node[OWNER_DOCUMENT]); // TODO: refactor with !! or just falsey bCheck = ((isGecko || UA.webkit > 519) ? true : false); // TODO: worth refactoring for TOP/LEFT only? while ((parentNode = parentNode[OFFSET_PARENT])) { xy[0] += parentNode[OFFSET_LEFT]; xy[1] += parentNode[OFFSET_TOP]; 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[PARENT_NODE]) && parentNode[TAG_NAME]) { scrollTop = parentNode[SCROLL_TOP]; scrollLeft = parentNode[SCROLL_LEFT]; //Firefox does something funky with borders when overflow is not visible. if (isGecko && (Y.Dom._getStyle(parentNode, 'overflow') !== 'visible')) { xy = Y.Dom._calcBorders(parentNode, xy); } if (scrollTop || scrollLeft) { xy[0] -= scrollLeft; xy[1] -= scrollTop; } } xy[0] += docScrollLeft; xy[1] += docScrollTop; } else { //Fix FIXED position -- add scrollbars if (isOpera) { xy[0] -= docScrollLeft; xy[1] -= docScrollTop; } else if (isSafari || isGecko) { xy[0] += docScrollLeft; xy[1] += docScrollTop; } } //Round the numbers so we get sane data back xy[0] = Math.floor(xy[0]); xy[1] = Math.floor(xy[1]); } else { } return xy; }; } }(), // NOTE: Executing for loadtime branching /** * Gets the current X position of an element based on page coordinates. The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). * @method getX * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements * @return {Number | Array} The X position of the element(s) */ getX: function(el) { var f = function(el) { return Y.Dom.getXY(el)[0]; }; return Y.Dom.batch(el, f, Y.Dom, true); }, /** * 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 {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements * @return {Number | Array} The Y position of the element(s) */ getY: function(el) { var f = function(el) { return Y.Dom.getXY(el)[1]; }; return Y.Dom.batch(el, f, Y.Dom, true); }, /** * Set the 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 setXY * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements * @param {Array} pos 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(el, pos, noRetry) { Y.Dom.batch(el, Y.Dom._setXY, { pos: pos, noRetry: noRetry }); }, _setXY: function(node, args) { var pos = Y.Dom._getStyle(node, POSITION), setStyle = Y.Dom.setStyle, xy = args.pos, noRetry = args.noRetry, delta = [ // assuming pixels; if not we will have to retry parseInt( Y.Dom.getComputedStyle(node, LEFT), 10 ), parseInt( Y.Dom.getComputedStyle(node, TOP), 10 ) ], currentXY, newXY; if (pos == 'static') { // default to relative pos = RELATIVE; setStyle(node, POSITION, pos); } currentXY = Y.Dom._getXY(node); if (!xy || currentXY === false) { // has to be part of doc to have xy return false; } if ( isNaN(delta[0]) ) {// in case of 'auto' delta[0] = (pos == RELATIVE) ? 0 : node[OFFSET_LEFT]; } if ( isNaN(delta[1]) ) { // in case of 'auto' delta[1] = (pos == RELATIVE) ? 0 : node[OFFSET_TOP]; } if (xy[0] !== null) { // from setX setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px'); } if (xy[1] !== null) { // from setY setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px'); } if (!noRetry) { newXY = Y.Dom._getXY(node); // if retry is true, try one more time if we miss if ( (xy[0] !== null && newXY[0] != xy[0]) || (xy[1] !== null && newXY[1] != xy[1]) ) { Y.Dom._setXY(node, { pos: xy, noRetry: true }); } } }, /** * Set the X position of an html element in page coordinates, regardless of how the element is positioned. * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). * @method setX * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. * @param {Int} x The value to use as the X coordinate for the element(s). */ setX: function(el, x) { Y.Dom.setXY(el, [x, null]); }, /** * Set the Y position of an html element in page coordinates, regardless of how the element is positioned. * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). * @method setY * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. * @param {Int} x To use as the Y coordinate for the element(s). */ setY: function(el, y) { Y.Dom.setXY(el, [null, y]); }, /** * Returns the region position of the given element. * The element must be part of the DOM tree to have a region (display:none or elements not appended return false). * @method getRegion * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements. * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data. */ getRegion: function(el) { var f = function(el) { var region = false; if ( Y.Dom._canPosition(el) ) { region = Y.Region.getRegion(el); } else { } return region; }; return Y.Dom.batch(el, f, Y.Dom, true); }, /** * Returns the width of the client (viewport). * @method getClientWidth * @deprecated Now using getViewportWidth. This interface left intact for back compat. * @return {Int} The width of the viewable area of the page. */ getClientWidth: function() { return Y.Dom.getViewportWidth(); }, /** * Returns the height of the client (viewport). * @method getClientHeight * @deprecated Now using getViewportHeight. This interface left intact for back compat. * @return {Int} The height of the viewable area of the page. */ getClientHeight: function() { return Y.Dom.getViewportHeight(); }, /** * Returns an array of HTMLElements with the given class. * For optimized performance, include a tag and/or root node when possible. * Note: This method operates against a live collection, so modifying the * collection in the callback (removing/appending nodes, etc.) will have * side effects. Instead you should iterate the returned nodes array, * as you would with the native "getElementsByTagName" method. * @method getElementsByClassName * @param {String} className The class name to match against * @param {String} tag (optional) The tag name of the elements being collected * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point. * This element is not included in the className scan. * @param {Function} apply (optional) A function to apply to each element when found * @param {Any} o (optional) An optional arg that is passed to the supplied method * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o" * @return {Array} An array of elements that have the given class name */ getElementsByClassName: function(className, tag, root, apply, o, overrides) { tag = tag || '*'; root = (root) ? Y.Dom.get(root) : null || document; if (!root) { return []; } var nodes = [], elements = root.getElementsByTagName(tag), hasClass = Y.Dom.hasClass; for (var i = 0, len = elements.length; i < len; ++i) { if ( hasClass(elements[i], className) ) { nodes[nodes.length] = elements[i]; } } if (apply) { Y.Dom.batch(nodes, apply, o, overrides); } return nodes; }, /** * Determines whether an HTMLElement has the given className. * @method hasClass * @param {String | HTMLElement | Array} el The element or collection to test * @param {String} className the class name to search for * @return {Boolean | Array} A boolean value or array of boolean values */ hasClass: function(el, className) { return Y.Dom.batch(el, Y.Dom._hasClass, className); }, _hasClass: function(el, className) { var ret = false, current; if (el && className) { current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; if (className.exec) { ret = className.test(current); } else { ret = className && (SPACE + current + SPACE). indexOf(SPACE + className + SPACE) > -1; } } else { } return ret; }, /** * Adds a class name to a given element or collection of elements. * @method addClass * @param {String | HTMLElement | Array} el The element or collection to add the class to * @param {String} className the class name to add to the class attribute * @return {Boolean | Array} A pass/fail boolean or array of booleans */ addClass: function(el, className) { return Y.Dom.batch(el, Y.Dom._addClass, className); }, _addClass: function(el, className) { var ret = false, current; if (el && className) { current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; if ( !Y.Dom._hasClass(el, className) ) { Y.Dom.setAttribute(el, CLASS_NAME, trim(current + SPACE + className)); ret = true; } } else { } return ret; }, /** * Removes a class name from a given element or collection of elements. * @method removeClass * @param {String | HTMLElement | Array} el The element or collection to remove the class from * @param {String} className the class name to remove from the class attribute * @return {Boolean | Array} A pass/fail boolean or array of booleans */ removeClass: function(el, className) { return Y.Dom.batch(el, Y.Dom._removeClass, className); }, _removeClass: function(el, className) { var ret = false, current, newClass, attr; if (el && className) { current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; Y.Dom.setAttribute(el, CLASS_NAME, current.replace(Y.Dom._getClassRegex(className), EMPTY)); newClass = Y.Dom._getAttribute(el, CLASS_NAME); if (current !== newClass) { // else nothing changed Y.Dom.setAttribute(el, CLASS_NAME, trim(newClass)); // trim after comparing to current class ret = true; if (Y.Dom._getAttribute(el, CLASS_NAME) === '') { // remove class attribute if empty attr = (el.hasAttribute && el.hasAttribute(_CLASS)) ? _CLASS : CLASS_NAME; el.removeAttribute(attr); } } } else { } return ret; }, /** * Replace a class with another class for a given element or collection of elements. * If no oldClassName is present, the newClassName is simply added. * @method replaceClass * @param {String | HTMLElement | Array} el The element or collection to remove the class from * @param {String} oldClassName the class name to be replaced * @param {String} newClassName the class name that will be replacing the old class name * @return {Boolean | Array} A pass/fail boolean or array of booleans */ replaceClass: function(el, oldClassName, newClassName) { return Y.Dom.batch(el, Y.Dom._replaceClass, { from: oldClassName, to: newClassName }); }, _replaceClass: function(el, classObj) { var className, from, to, ret = false, current; if (el && classObj) { from = classObj.from; to = classObj.to; if (!to) { ret = false; } else if (!from) { // just add if no "from" ret = Y.Dom._addClass(el, classObj.to); } else if (from !== to) { // else nothing to replace // May need to lead with DBLSPACE? current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY; className = (SPACE + current.replace(Y.Dom._getClassRegex(from), SPACE + to)). split(Y.Dom._getClassRegex(to)); // insert to into what would have been the first occurrence slot className.splice(1, 0, SPACE + to); Y.Dom.setAttribute(el, CLASS_NAME, trim(className.join(EMPTY))); ret = true; } } else { } return ret; }, /** * Returns an ID and applies it to the element "el", if provided. * @method generateId * @param {String | HTMLElement | Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present). * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen"). * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element) */ generateId: function(el, prefix) { prefix = prefix || 'yui-gen'; var f = function(el) { if (el && el.id) { // do not override existing ID return el.id; } var id = prefix + YAHOO.env._id_counter++; if (el) { if (el[OWNER_DOCUMENT] && el[OWNER_DOCUMENT].getElementById(id)) { // in case one already exists // use failed id plus prefix to help ensure uniqueness return Y.Dom.generateId(el, id + prefix); } el.id = id; } return id; }; // batch fails when no element, so just generate and return single ID return Y.Dom.batch(el, f, Y.Dom, true) || f.apply(Y.Dom, arguments); }, /** * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy. * @method isAncestor * @param {String | HTMLElement} haystack The possible ancestor * @param {String | HTMLElement} needle The possible descendent * @return {Boolean} Whether or not the haystack is an ancestor of needle */ isAncestor: function(haystack, needle) { haystack = Y.Dom.get(haystack); needle = Y.Dom.get(needle); var ret = false; if ( (haystack && needle) && (haystack[NODE_TYPE] && needle[NODE_TYPE]) ) { if (haystack.contains && haystack !== needle) { // contains returns true when equal ret = haystack.contains(needle); } else if (haystack.compareDocumentPosition) { // gecko ret = !!(haystack.compareDocumentPosition(needle) & 16); } } else { } return ret; }, /** * Determines whether an HTMLElement is present in the current document. * @method inDocument * @param {String | HTMLElement} el The element to search for * @param {Object} doc An optional document to search, defaults to element's owner document * @return {Boolean} Whether or not the element is present in the current document */ inDocument: function(el, doc) { return Y.Dom._inDoc(Y.Dom.get(el), doc); }, _inDoc: function(el, doc) { var ret = false; if (el && el[TAG_NAME]) { doc = doc || el[OWNER_DOCUMENT]; ret = Y.Dom.isAncestor(doc[DOCUMENT_ELEMENT], el); } else { } return ret; }, /** * Returns an array of HTMLElements that pass the test applied by supplied boolean method. * For optimized performance, include a tag and/or root node when possible. * Note: This method operates against a live collection, so modifying the * collection in the callback (removing/appending nodes, etc.) will have * side effects. Instead you should iterate the returned nodes array, * as you would with the native "getElementsByTagName" method. * @method getElementsBy * @param {Function} method - A boolean method for testing elements which receives the element as its only argument. * @param {String} tag (optional) The tag name of the elements being collected * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point * @param {Function} apply (optional) A function to apply to each element when found * @param {Any} o (optional) An optional arg that is passed to the supplied method * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o" * @return {Array} Array of HTMLElements */ getElementsBy: function(method, tag, root, apply, o, overrides, firstOnly) { tag = tag || '*'; root = (root) ? Y.Dom.get(root) : null || document; if (!root) { return []; } var nodes = [], elements = root.getElementsByTagName(tag); for (var i = 0, len = elements.length; i < len; ++i) { if ( method(elements[i]) ) { if (firstOnly) { nodes = elements[i]; break; } else { nodes[nodes.length] = elements[i]; } } } if (apply) { Y.Dom.batch(nodes, apply, o, overrides); } return nodes; }, /** * Returns the first HTMLElement that passes the test applied by the supplied boolean method. * @method getElementBy * @param {Function} method - A boolean method for testing elements which receives the element as its only argument. * @param {String} tag (optional) The tag name of the elements being collected * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point * @return {HTMLElement} */ getElementBy: function(method, tag, root) { return Y.Dom.getElementsBy(method, tag, root, null, null, null, true); }, /** * Runs the supplied method against each item in the Collection/Array. * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ). * @method batch * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to * @param {Function} method The method to apply to the element(s) * @param {Any} o (optional) An optional arg that is passed to the supplied method * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o" * @return {Any | Array} The return value(s) from the supplied method */ batch: function(el, method, o, overrides) { var collection = [], scope = (overrides) ? o : window; el = (el && (el[TAG_NAME] || el.item)) ? el : Y.Dom.get(el); // skip get() when possible if (el && method) { if (el[TAG_NAME] || el.length === undefined) { // element or not array-like return method.call(scope, el, o); } for (var i = 0; i < el.length; ++i) { collection[collection.length] = method.call(scope, el[i], o); } } else { return false; } return collection; }, /** * Returns the height of the document. * @method getDocumentHeight * @return {Int} The height of the actual document (which includes the body and its margin). */ getDocumentHeight: function() { var scrollHeight = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollHeight : documentElement.scrollHeight, h = Math.max(scrollHeight, Y.Dom.getViewportHeight()); return h; }, /** * Returns the width of the document. * @method getDocumentWidth * @return {Int} The width of the actual document (which includes the body and its margin). */ getDocumentWidth: function() { var scrollWidth = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollWidth : documentElement.scrollWidth, w = Math.max(scrollWidth, Y.Dom.getViewportWidth()); return w; }, /** * Returns the current height of the viewport. * @method getViewportHeight * @return {Int} The height of the viewable area of the page (excludes scrollbars). */ getViewportHeight: function() { var height = self.innerHeight, // Safari, Opera mode = document[COMPAT_MODE]; if ( (mode || isIE) && !isOpera ) { // IE, Gecko height = (mode == CSS1_COMPAT) ? documentElement.clientHeight : // Standards document.body.clientHeight; // Quirks } return height; }, /** * Returns the current width of the viewport. * @method getViewportWidth * @return {Int} The width of the viewable area of the page (excludes scrollbars). */ getViewportWidth: function() { var width = self.innerWidth, // Safari mode = document[COMPAT_MODE]; if (mode || isIE) { // IE, Gecko, Opera width = (mode == CSS1_COMPAT) ? documentElement.clientWidth : // Standards document.body.clientWidth; // Quirks } return width; }, /** * Returns the nearest ancestor that passes the test applied by supplied boolean method. * For performance reasons, IDs are not accepted and argument validation omitted. * @method getAncestorBy * @param {HTMLElement} node The HTMLElement to use as the starting point * @param {Function} method - A boolean method for testing elements which receives the element as its only argument. * @return {Object} HTMLElement or null if not found */ getAncestorBy: function(node, method) { while ( (node = node[PARENT_NODE]) ) { // NOTE: assignment if ( Y.Dom._testElement(node, method) ) { return node; } } return null; }, /** * Returns the nearest ancestor with the given className. * @method getAncestorByClassName * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point * @param {String} className * @return {Object} HTMLElement */ getAncestorByClassName: function(node, className) { node = Y.Dom.get(node); if (!node) { return null; } var method = function(el) { return Y.Dom.hasClass(el, className); }; return Y.Dom.getAncestorBy(node, method); }, /** * Returns the nearest ancestor with the given tagName. * @method getAncestorByTagName * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point * @param {String} tagName * @return {Object} HTMLElement */ getAncestorByTagName: function(node, tagName) { node = Y.Dom.get(node); if (!node) { return null; } var method = function(el) { return el[TAG_NAME] && el[TAG_NAME].toUpperCase() == tagName.toUpperCase(); }; return Y.Dom.getAncestorBy(node, method); }, /** * Returns the previous sibling that is an HTMLElement. * For performance reasons, IDs are not accepted and argument validation omitted. * Returns the nearest HTMLElement sibling if no method provided. * @method getPreviousSiblingBy * @param {HTMLElement} node The HTMLElement to use as the starting point * @param {Function} method A boolean function used to test siblings * that receives the sibling node being tested as its only argument * @return {Object} HTMLElement or null if not found */ getPreviousSiblingBy: function(node, method) { while (node) { node = node.previousSibling; if ( Y.Dom._testElement(node, method) ) { return node; } } return null; }, /** * Returns the previous sibling that is an HTMLElement * @method getPreviousSibling * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point * @return {Object} HTMLElement or null if not found */ getPreviousSibling: function(node) { node = Y.Dom.get(node); if (!node) { return null; } return Y.Dom.getPreviousSiblingBy(node); }, /** * Returns the next HTMLElement sibling that passes the boolean method. * For performance reasons, IDs are not accepted and argument validation omitted. * Returns the nearest HTMLElement sibling if no method provided. * @method getNextSiblingBy * @param {HTMLElement} node The HTMLElement to use as the starting point * @param {Function} method A boolean function used to test siblings * that receives the sibling node being tested as its only argument * @return {Object} HTMLElement or null if not found */ getNextSiblingBy: function(node, method) { while (node) { node = node.nextSibling; if ( Y.Dom._testElement(node, method) ) { return node; } } return null; }, /** * Returns the next sibling that is an HTMLElement * @method getNextSibling * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point * @return {Object} HTMLElement or null if not found */ getNextSibling: function(node) { node = Y.Dom.get(node); if (!node) { return null; } return Y.Dom.getNextSiblingBy(node); }, /** * Returns the first HTMLElement child that passes the test method. * @method getFirstChildBy * @param {HTMLElement} node The HTMLElement to use as the starting point * @param {Function} method A boolean function used to test children * that receives the node being tested as its only argument * @return {Object} HTMLElement or null if not found */ getFirstChildBy: function(node, method) { var child = ( Y.Dom._testElement(node.firstChild, method) ) ? node.firstChild : null; return child || Y.Dom.getNextSiblingBy(node.firstChild, method); }, /** * Returns the first HTMLElement child. * @method getFirstChild * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point * @return {Object} HTMLElement or null if not found */ getFirstChild: function(node, method) { node = Y.Dom.get(node); if (!node) { return null; } return Y.Dom.getFirstChildBy(node); }, /** * Returns the last HTMLElement child that passes the test method. * @method getLastChildBy * @param {HTMLElement} node The HTMLElement to use as the starting point * @param {Function} method A boolean function used to test children * that receives the node being tested as its only argument * @return {Object} HTMLElement or null if not found */ getLastChildBy: function(node, method) { if (!node) { return null; } var child = ( Y.Dom._testElement(node.lastChild, method) ) ? node.lastChild : null; return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method); }, /** * Returns the last HTMLElement child. * @method getLastChild * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point * @return {Object} HTMLElement or null if not found */ getLastChild: function(node) { node = Y.Dom.get(node); return Y.Dom.getLastChildBy(node); }, /** * Returns an array of HTMLElement childNodes that pass the test method. * @method getChildrenBy * @param {HTMLElement} node The HTMLElement to start from * @param {Function} method A boolean function used to test children * that receives the node being tested as its only argument * @return {Array} A static array of HTMLElements */ getChildrenBy: function(node, method) { var child = Y.Dom.getFirstChildBy(node, method), children = child ? [child] : []; Y.Dom.getNextSiblingBy(child, function(node) { if ( !method || method(node) ) { children[children.length] = node; } return false; // fail test to collect all children }); return children; }, /** * Returns an array of HTMLElement childNodes. * @method getChildren * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point * @return {Array} A static array of HTMLElements */ getChildren: function(node) { node = Y.Dom.get(node); if (!node) { } return Y.Dom.getChildrenBy(node); }, /** * Returns the left scroll value of the document * @method getDocumentScrollLeft * @param {HTMLDocument} document (optional) The document to get the scroll value of * @return {Int} The amount that the document is scrolled to the left */ getDocumentScrollLeft: function(doc) { doc = doc || document; return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft); }, /** * Returns the top scroll value of the document * @method getDocumentScrollTop * @param {HTMLDocument} document (optional) The document to get the scroll value of * @return {Int} The amount that the document is scrolled to the top */ getDocumentScrollTop: function(doc) { doc = doc || document; return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop); }, /** * Inserts the new node as the previous sibling of the reference node * @method insertBefore * @param {String | HTMLElement} newNode The node to be inserted * @param {String | HTMLElement} referenceNode The node to insert the new node before * @return {HTMLElement} The node that was inserted (or null if insert fails) */ insertBefore: function(newNode, referenceNode) { newNode = Y.Dom.get(newNode); referenceNode = Y.Dom.get(referenceNode); if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) { return null; } return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode); }, /** * Inserts the new node as the next sibling of the reference node * @method insertAfter * @param {String | HTMLElement} newNode The node to be inserted * @param {String | HTMLElement} referenceNode The node to insert the new node after * @return {HTMLElement} The node that was inserted (or null if insert fails) */ insertAfter: function(newNode, referenceNode) { newNode = Y.Dom.get(newNode); referenceNode = Y.Dom.get(referenceNode); if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) { return null; } if (referenceNode.nextSibling) { return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode.nextSibling); } else { return referenceNode[PARENT_NODE].appendChild(newNode); } }, /** * Creates a Region based on the viewport relative to the document. * @method getClientRegion * @return {Region} A Region object representing the viewport which accounts for document scroll */ getClientRegion: function() { var t = Y.Dom.getDocumentScrollTop(), l = Y.Dom.getDocumentScrollLeft(), r = Y.Dom.getViewportWidth() + l, b = Y.Dom.getViewportHeight() + t; return new Y.Region(t, r, b, l); }, /** * Provides a normalized attribute interface. * @method setAttribute * @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) { Y.Dom.batch(el, Y.Dom._setAttribute, { attr: attr, val: val }); }, _setAttribute: function(el, args) { var attr = Y.Dom._toCamel(args.attr), val = args.val; if (el && el.setAttribute) { if (Y.Dom.DOT_ATTRIBUTES[attr]) { el[attr] = val; } else { attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr; el.setAttribute(attr, val); } } else { } }, /** * Provides a normalized attribute interface. * @method getAttribute * @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) { return Y.Dom.batch(el, Y.Dom._getAttribute, attr); }, _getAttribute: function(el, attr) { var val; attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr; if (el && el.getAttribute) { val = el.getAttribute(attr, 2); } else { } return val; }, _toCamel: function(property) { var c = propertyCache; function tU(x,l) { return l.toUpperCase(); } return c[property] || (c[property] = property.indexOf('-') === -1 ? property : property.replace( /-([a-z])/gi, tU )); }, _getClassRegex: function(className) { var re; if (className !== undefined) { // allow empty string to pass if (className.exec) { // already a RegExp re = className; } else { re = reCache[className]; if (!re) { // escape special chars (".", "[", etc.) className = className.replace(Y.Dom._patterns.CLASS_RE_TOKENS, '\\$1'); re = reCache[className] = new RegExp(C_START + className + C_END, G); } } } return re; }, _patterns: { ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards, CLASS_RE_TOKENS: /([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g }, _testElement: function(node, method) { return node && node[NODE_TYPE] == 1 && ( !method || method(node) ); }, _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 (isGecko) { if (RE_TABLE.test(node[TAG_NAME])) { t = 0; l = 0; } } xy2[0] += l; xy2[1] += t; return xy2; } }; var _getComputedStyle = Y.Dom[GET_COMPUTED_STYLE]; // fix opera computedStyle default color unit (convert to rgb) if (UA.opera) { Y.Dom[GET_COMPUTED_STYLE] = function(node, att) { var val = _getComputedStyle(node, att); if (RE_COLOR.test(att)) { val = Y.Dom.Color.toRGB(val); } return val; }; } // safari converts transparent to rgba(), others use "transparent" if (UA.webkit) { Y.Dom[GET_COMPUTED_STYLE] = function(node, att) { var val = _getComputedStyle(node, att); if (val === 'rgba(0, 0, 0, 0)') { val = 'transparent'; } return val; }; } if (UA.ie && UA.ie >= 8 && document.documentElement.hasAttribute) { // IE 8 standards Y.Dom.DOT_ATTRIBUTES.type = true; // IE 8 errors on input.setAttribute('type') } })(); /** * A region is a representation of an object on a grid. It is defined * by the top, right, bottom, left extents, so is rectangular by default. If * other shapes are required, this class could be extended to support it. * @namespace YAHOO.util * @class Region * @param {Int} t the top extent * @param {Int} r the right extent * @param {Int} b the bottom extent * @param {Int} l the left extent * @constructor */ YAHOO.util.Region = function(t, r, b, l) { /** * The region's top extent * @property top * @type Int */ this.top = t; /** * The region's top extent * @property y * @type Int */ this.y = t; /** * The region's top extent as index, for symmetry with set/getXY * @property 1 * @type Int */ this[1] = t; /** * The region's right extent * @property right * @type int */ this.right = r; /** * The region's bottom extent * @property bottom * @type Int */ this.bottom = b; /** * The region's left extent * @property left * @type Int */ this.left = l; /** * The region's left extent * @property x * @type Int */ this.x = l; /** * The region's left extent as index, for symmetry with set/getXY * @property 0 * @type Int */ this[0] = l; /** * The region's total width * @property width * @type Int */ this.width = this.right - this.left; /** * The region's total height * @property height * @type Int */ this.height = this.bottom - this.top; }; /** * Returns true if this region contains the region passed in * @method contains * @param {Region} region The region to evaluate * @return {Boolean} True if the region is contained with this region, * else false */ YAHOO.util.Region.prototype.contains = function(region) { return ( region.left >= this.left && region.right <= this.right && region.top >= this.top && region.bottom <= this.bottom ); }; /** * Returns the area of the region * @method getArea * @return {Int} the region's area */ YAHOO.util.Region.prototype.getArea = function() { return ( (this.bottom - this.top) * (this.right - this.left) ); }; /** * Returns the region where the passed in region overlaps with this one * @method intersect * @param {Region} region The region that intersects * @return {Region} The overlap region, or null if there is no overlap */ YAHOO.util.Region.prototype.intersect = function(region) { var t = Math.max( this.top, region.top ), r = Math.min( this.right, region.right ), b = Math.min( this.bottom, region.bottom ), l = Math.max( this.left, region.left ); if (b >= t && r >= l) { return new YAHOO.util.Region(t, r, b, l); } else { return null; } }; /** * Returns the region representing the smallest region that can contain both * the passed in region and this region. * @method union * @param {Region} region The region that to create the union with * @return {Region} The union region */ YAHOO.util.Region.prototype.union = function(region) { var t = Math.min( this.top, region.top ), r = Math.max( this.right, region.right ), b = Math.max( this.bottom, region.bottom ), l = Math.min( this.left, region.left ); return new YAHOO.util.Region(t, r, b, l); }; /** * toString * @method toString * @return string the region properties */ YAHOO.util.Region.prototype.toString = function() { return ( "Region {" + "top: " + this.top + ", right: " + this.right + ", bottom: " + this.bottom + ", left: " + this.left + ", height: " + this.height + ", width: " + this.width + "}" ); }; /** * Returns a region that is occupied by the DOM element * @method getRegion * @param {HTMLElement} el The element * @return {Region} The region that the element occupies * @static */ YAHOO.util.Region.getRegion = function(el) { var p = YAHOO.util.Dom.getXY(el), t = p[1], r = p[0] + el.offsetWidth, b = p[1] + el.offsetHeight, l = p[0]; return new YAHOO.util.Region(t, r, b, l); }; ///////////////////////////////////////////////////////////////////////////// /** * A point is a region that is special in that it represents a single point on * the grid. * @namespace YAHOO.util * @class Point * @param {Int} x The X position of the point * @param {Int} y The Y position of the point * @constructor * @extends YAHOO.util.Region */ 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]; } YAHOO.util.Point.superclass.constructor.call(this, y, x, y, x); }; YAHOO.extend(YAHOO.util.Point, YAHOO.util.Region); (function() { /** * Add style management functionality to DOM. * @module dom * @for Dom */ var Y = YAHOO.util, CLIENT_TOP = 'clientTop', CLIENT_LEFT = 'clientLeft', PARENT_NODE = 'parentNode', RIGHT = 'right', HAS_LAYOUT = 'hasLayout', PX = 'px', OPACITY = 'opacity', AUTO = 'auto', BORDER_LEFT_WIDTH = 'borderLeftWidth', BORDER_TOP_WIDTH = 'borderTopWidth', BORDER_RIGHT_WIDTH = 'borderRightWidth', BORDER_BOTTOM_WIDTH = 'borderBottomWidth', VISIBLE = 'visible', TRANSPARENT = 'transparent', HEIGHT = 'height', WIDTH = 'width', STYLE = 'style', CURRENT_STYLE = 'currentStyle', // IE getComputedStyle // TODO: unit-less lineHeight (e.g. 1.22) re_size = /^width|height$/, 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, ComputedStyle = { get: function(el, property) { var value = '', current = el[CURRENT_STYLE][property]; if (property === OPACITY) { value = Y.Dom.getStyle(el, OPACITY); } 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 = Y.Dom.IE.ComputedStyle.getPixel(el, property); } else { value = current; } return value; }, getOffset: function(el, prop) { var current = el[CURRENT_STYLE][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. value = '', actual; if (current == AUTO) { actual = el[offset]; // offsetHeight/Top etc. if (actual === undefined) { // likely "right" or "bottom" value = 0; } value = actual; if (re_size.test(prop)) { // account for box model diff el[STYLE][prop] = actual; if (el[offset] > actual) { // the difference is padding + border (works in Standards & Quirks modes) value = actual - (el[offset] - actual); } el[STYLE][prop] = AUTO; // revert to auto } } else { // convert units to px if (!el[STYLE][pixel] && !el[STYLE][prop]) { // need to map style.width to currentStyle (no currentStyle.pixelWidth) el[STYLE][prop] = current; // no style.pixelWidth if no style.width } value = el[STYLE][pixel]; } return value + PX; }, getBorderWidth: function(el, property) { // clientHeight/Width = paddingBox (e.g. offsetWidth - borderWidth) // clientTop/Left = borderWidth var value = null; if (!el[CURRENT_STYLE][HAS_LAYOUT]) { // TODO: unset layout? el[STYLE].zoom = 1; // need layout to measure client } switch(property) { case BORDER_TOP_WIDTH: value = el[CLIENT_TOP]; break; case BORDER_BOTTOM_WIDTH: value = el.offsetHeight - el.clientHeight - el[CLIENT_TOP]; break; case BORDER_LEFT_WIDTH: value = el[CLIENT_LEFT]; break; case BORDER_RIGHT_WIDTH: value = el.offsetWidth - el.clientWidth - el[CLIENT_LEFT]; break; } return value + PX; }, getPixel: function(node, att) { // use pixelRight to convert to px var val = null, styleRight = node[CURRENT_STYLE][RIGHT], current = node[CURRENT_STYLE][att]; node[STYLE][RIGHT] = current; val = node[STYLE].pixelRight; node[STYLE][RIGHT] = styleRight; // revert return val + PX; }, getMargin: function(node, att) { var val; if (node[CURRENT_STYLE][att] == AUTO) { val = 0 + PX; } else { val = Y.Dom.IE.ComputedStyle.getPixel(node, att); } return val; }, getVisibility: function(node, att) { var current; while ( (current = node[CURRENT_STYLE]) && current[att] == 'inherit') { // NOTE: assignment in test node = node[PARENT_NODE]; } return (current) ? current[att] : VISIBLE; }, getColor: function(node, att) { return Y.Dom.Color.toRGB(node[CURRENT_STYLE][att]) || TRANSPARENT; }, getBorderColor: function(node, att) { var current = node[CURRENT_STYLE], val = current[att] || current.color; return Y.Dom.Color.toRGB(Y.Dom.Color.toHex(val)); } }, //fontSize: getPixelFont, IEComputed = {}; IEComputed.top = IEComputed.right = IEComputed.bottom = IEComputed.left = IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset; IEComputed.color = ComputedStyle.getColor; 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; Y.Dom.IE_COMPUTED = IEComputed; Y.Dom.IE_ComputedStyle = ComputedStyle; })(); (function() { /** * Add style management functionality to DOM. * @module dom * @for Dom */ var TO_STRING = 'toString', PARSE_INT = parseInt, RE = RegExp, Y = YAHOO.util; Y.Dom.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.Dom.Color.re_RGB.test(val)) { val = Y.Dom.Color.toHex(val); } if(Y.Dom.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.Dom.Color.KEYWORDS[val] || val; if (Y.Dom.Color.re_RGB.exec(val)) { var r = (RE.$1.length === 1) ? '0' + RE.$1 : Number(RE.$1), g = (RE.$2.length === 1) ? '0' + RE.$2 : Number(RE.$2), b = (RE.$3.length === 1) ? '0' + RE.$3 : Number(RE.$3); val = [ r[TO_STRING](16), g[TO_STRING](16), b[TO_STRING](16) ].join(''); } if (val.length < 6) { val = val.replace(Y.Dom.Color.re_hex3, '$1$1'); } if (val !== 'transparent' && val.indexOf('#') < 0) { val = '#' + val; } return val.toLowerCase(); } }; }()); YAHOO.register("dom", YAHOO.util.Dom, {version: "2.8.0r4", build: "2446"}); /* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ /** * The CustomEvent class lets you define events for your application * that can be subscribed to by one or more independent component. * * @param {String} type The type of event, which is passed to the callback * when the event fires * @param {Object} context The context the event will fire from. "this" will * refer to this object in the callback. Default value: * the window object. The listener can override this. * @param {boolean} silent pass true to prevent the event from writing to * the debugsystem * @param {int} signature the signature that the custom event subscriber * will receive. YAHOO.util.CustomEvent.LIST or * YAHOO.util.CustomEvent.FLAT. The default is * YAHOO.util.CustomEvent.LIST. * @param fireOnce {boolean} If configured to fire once, the custom event * will only notify subscribers a single time regardless of how many times * the event is fired. In addition, new subscribers will be notified * immediately if the event has already been fired. * @namespace YAHOO.util * @class CustomEvent * @constructor */ YAHOO.util.CustomEvent = function(type, context, silent, signature, fireOnce) { /** * The type of event, returned to subscribers when the event fires * @property type * @type string */ this.type = type; /** * The context the event will fire from by default. Defaults to the window obj. * @property scope * @type object */ this.scope = context || window; /** * By default all custom events are logged in the debug build. Set silent to true * to disable debug output for this event. * @property silent * @type boolean */ this.silent = silent; /** * If configured to fire once, the custom event will only notify subscribers * a single time regardless of how many times the event is fired. In addition, * new subscribers will be notified immediately if the event has already been * fired. * @property fireOnce * @type boolean * @default false */ this.fireOnce = fireOnce; /** * Indicates whether or not this event has ever been fired. * @property fired * @type boolean * @default false */ this.fired = false; /** * For fireOnce events the arguments the event was fired with are stored * so that new subscribers get the proper payload. * @property firedWith * @type Array */ this.firedWith = null; /** * Custom events support two styles of arguments provided to the event * subscribers. *
    *
  • YAHOO.util.CustomEvent.LIST: *
      *
    • param1: event name
    • *
    • param2: array of arguments sent to fire
    • *
    • param3: a custom object supplied by the subscriber
    • *
    *
  • *
  • YAHOO.util.CustomEvent.FLAT *
      *
    • param1: the first argument passed to fire. If you need to * pass multiple parameters, use and array or object literal
    • *
    • param2: a custom object supplied by the subscriber
    • *
    *
  • *
* @property signature * @type int */ this.signature = signature || YAHOO.util.CustomEvent.LIST; /** * The subscribers to this event * @property subscribers * @type Subscriber[] */ this.subscribers = []; if (!this.silent) { } var onsubscribeType = "_YUICEOnSubscribe"; // Only add subscribe events for events that are not generated by // CustomEvent if (type !== onsubscribeType) { /** * Custom events provide a custom event that fires whenever there is * a new subscriber to the event. This provides an opportunity to * handle the case where there is a non-repeating event that has * already fired has a new subscriber. * * @event subscribeEvent * @type YAHOO.util.CustomEvent * @param fn {Function} The function to execute * @param obj An object to be passed along when the event fires. * Defaults to the custom event. * @param override If true, the obj passed in becomes the * execution context of the listener. If an object, that object becomes * the execution context. Defaults to the custom event. */ this.subscribeEvent = new YAHOO.util.CustomEvent(onsubscribeType, this, true); } /** * In order to make it possible to execute the rest of the subscriber * stack when one thows an exception, the subscribers exceptions are * caught. The most recent exception is stored in this property * @property lastError * @type Error */ this.lastError = null; }; /** * Subscriber listener sigature constant. The LIST type returns three * parameters: the event type, the array of args passed to fire, and * the optional custom object * @property YAHOO.util.CustomEvent.LIST * @static * @type int */ YAHOO.util.CustomEvent.LIST = 0; /** * Subscriber listener sigature constant. The FLAT type returns two * parameters: the first argument passed to fire and the optional * custom object * @property YAHOO.util.CustomEvent.FLAT * @static * @type int */ YAHOO.util.CustomEvent.FLAT = 1; YAHOO.util.CustomEvent.prototype = { /** * Subscribes the caller to this event * @method subscribe * @param {Function} fn The function to execute * @param {Object} obj An object to be passed along when the event fires. * overrideContext If true, the obj passed in becomes the execution * context of the listener. If an object, that object becomes the execution context. */ subscribe: function(fn, obj, overrideContext) { if (!fn) { throw new Error("Invalid callback for subscriber to '" + this.type + "'"); } if (this.subscribeEvent) { this.subscribeEvent.fire(fn, obj, overrideContext); } var s = new YAHOO.util.Subscriber(fn, obj, overrideContext); if (this.fireOnce && this.fired) { this.notify(s, this.firedWith); } else { this.subscribers.push(s); } }, /** * Unsubscribes subscribers. * @method unsubscribe * @param {Function} fn The subscribed function to remove, if not supplied * all will be removed * @param {Object} obj 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 {boolean} True if the subscriber was found and detached. */ unsubscribe: function(fn, obj) { if (!fn) { return this.unsubscribeAll(); } var found = false; for (var i=0, len=this.subscribers.length; i *
  • 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() { this.lastError = null; var errors = [], len=this.subscribers.length; var args=[].slice.call(arguments, 0), ret=true, i, rebuild=false; if (this.fireOnce) { if (this.fired) { return true; } else { this.firedWith = args; } } this.fired = true; if (!len && this.silent) { return true; } if (!this.silent) { } // make a copy of the subscribers so that there are // no index problems if one subscriber removes another. var subs = this.subscribers.slice(); for (i=0; i 0) { param = args[0]; } try { ret = s.fn.call(scope, param, s.obj); } catch(e) { this.lastError = e; // errors.push(e); if (throwErrors) { throw e; } } } else { try { ret = s.fn.call(scope, this.type, args, s.obj); } catch(ex) { this.lastError = ex; if (throwErrors) { throw ex; } } } return ret; }, /** * Removes all listeners * @method unsubscribeAll * @return {int} The number of listeners unsubscribed */ unsubscribeAll: function() { var l = this.subscribers.length, i; for (i=l-1; i>-1; i--) { this._delete(i); } this.subscribers=[]; return l; }, /** * @method _delete * @private */ _delete: function(index) { var s = this.subscribers[index]; if (s) { delete s.fn; delete s.obj; } // this.subscribers[index]=null; this.subscribers.splice(index, 1); }, /** * @method toString */ toString: function() { return "CustomEvent: " + "'" + this.type + "', " + "context: " + this.scope; } }; ///////////////////////////////////////////////////////////////////// /** * Stores the subscriber information to be used when the event fires. * @param {Function} fn The function to execute * @param {Object} obj An object to be passed along when the event fires * @param {boolean} overrideContext If true, the obj passed in becomes the execution * context of the listener * @class Subscriber * @constructor */ YAHOO.util.Subscriber = function(fn, obj, overrideContext) { /** * The callback that will be execute when the event fires * @property fn * @type function */ this.fn = fn; /** * An optional custom object that will passed to the callback when * the event fires * @property obj * @type object */ this.obj = YAHOO.lang.isUndefined(obj) ? null : obj; /** * The default execution context for the event listener is defined when the * event is created (usually the object which contains the event). * By setting overrideContext to true, the execution context becomes the custom * object passed in by the subscriber. If overrideContext is an object, that * object becomes the context. * @property overrideContext * @type boolean|object */ this.overrideContext = overrideContext; }; /** * Returns the execution context for this listener. If overrideContext was set to true * the custom obj will be the context. If overrideContext is an object, that is the * context, otherwise the default context will be used. * @method getScope * @param {Object} defaultScope the context to use if this listener does not * override it. */ YAHOO.util.Subscriber.prototype.getScope = function(defaultScope) { if (this.overrideContext) { if (this.overrideContext === true) { return this.obj; } else { return this.overrideContext; } } return defaultScope; }; /** * 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} obj an object to be passed along when the event fires * @return {boolean} true if the supplied arguments match this * subscriber's signature. */ YAHOO.util.Subscriber.prototype.contains = function(fn, obj) { if (obj) { return (this.fn == fn && this.obj == obj); } else { return (this.fn == fn); } }; /** * @method toString */ YAHOO.util.Subscriber.prototype.toString = function() { return "Subscriber { obj: " + this.obj + ", overrideContext: " + (this.overrideContext || "no") + " }"; }; /** * The Event Utility provides utilities for managing DOM Events and tools * for building event systems * * @module event * @title Event Utility * @namespace YAHOO.util * @requires yahoo */ // The first instance of Event will win if it is loaded more than once. // @TODO this needs to be changed so that only the state data that needs to // be preserved is kept, while methods are overwritten/added as needed. // This means that the module pattern can't be used. if (!YAHOO.util.Event) { /** * 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 */ YAHOO.util.Event = function() { /** * True after the onload event has fired * @property loadComplete * @type boolean * @static * @private */ var loadComplete = false, /** * Cache of wrapped listeners * @property listeners * @type array * @static * @private */ listeners = [], /** * User-defined unload function that will be fired before all events * are detached * @property unloadListeners * @type array * @static * @private */ unloadListeners = [], /** * 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 onAvailStack * @static * @private */ onAvailStack = [], /** * Counter for auto id generation * @property counter * @static * @private */ counter = 0, /** * Normalized keycodes for webkit/safari * @property webkitKeymap * @type {int: int} * @private * @static * @final */ webkitKeymap = { 63232: 38, // up 63233: 40, // down 63234: 37, // left 63235: 39, // right 63276: 33, // page up 63277: 34, // page down 25: 9 // SHIFT-TAB (Safari provides a different key code in // this case, even though the shiftKey modifier is set) }, isIE = YAHOO.env.ua.ie, // String constants used by the addFocusListener and removeFocusListener methods FOCUSIN = "focusin", FOCUSOUT = "focusout"; 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 500@amp;40 ms, so it will poll * for 20 seconds or until all outstanding handlers are bound * (whichever comes first). * @property POLL_RETRYS * @type int * @static * @final */ POLL_RETRYS: 500, /** * The poll interval in milliseconds * @property POLL_INTERVAL * @type int * @static * @final */ POLL_INTERVAL: 40, /** * Element to bind, int constant * @property EL * @type int * @static * @final */ EL: 0, /** * Type of event, int constant * @property TYPE * @type int * @static * @final */ TYPE: 1, /** * Function to execute, int constant * @property FN * @type int * @static * @final */ FN: 2, /** * Function wrapped for context correction and cleanup, int constant * @property WFN * @type int * @static * @final */ WFN: 3, /** * Object passed in by the user that will be returned as a * parameter to the callback, int constant. Specific to * unload listeners * @property OBJ * @type int * @static * @final */ UNLOAD_OBJ: 3, /** * Adjusted context, either the element we are registering the event * on or the custom object passed in by the listener, int constant * @property ADJ_SCOPE * @type int * @static * @final */ ADJ_SCOPE: 4, /** * The original obj passed into addListener * @property OBJ * @type int * @static * @final */ OBJ: 5, /** * The original context parameter passed into addListener * @property OVERRIDE * @type int * @static * @final */ OVERRIDE: 6, /** * The original capture parameter passed into addListener * @property CAPTURE * @type int * @static * @final */ CAPTURE: 7, /** * 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, /** * Safari detection * @property isSafari * @private * @static * @deprecated use YAHOO.env.ua.webkit */ isSafari: YAHOO.env.ua.webkit, /** * webkit version * @property webkit * @type string * @private * @static * @deprecated use YAHOO.env.ua.webkit */ webkit: YAHOO.env.ua.webkit, /** * IE detection * @property isIE * @private * @static * @deprecated use YAHOO.env.ua.ie */ isIE: isIE, /** * poll handle * @property _interval * @static * @private */ _interval: null, /** * document readystate poll handle * @property _dri * @static * @private */ _dri: null, /** * Map of special event types * @property _specialTypes * @static * @private */ _specialTypes: { focusin: (isIE ? "focusin" : "focus"), focusout: (isIE ? "focusout" : "blur") }, /** * True when the document is initially usable * @property DOMReady * @type boolean * @static */ DOMReady: false, /** * Errors thrown by subscribers of custom events are caught * and the error message is written to the debug console. If * this property is set to true, it will also re-throw the * error. * @property throwErrors * @type boolean * @default false */ throwErrors: false, /** * @method startInterval * @static * @private */ startInterval: function() { if (!this._interval) { // var self = this; // var callback = function() { self._tryPreloadAttach(); }; // this._interval = setInterval(callback, this.POLL_INTERVAL); this._interval = YAHOO.lang.later(this.POLL_INTERVAL, this, this._tryPreloadAttach, null, true); } }, /** * 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} obj an optional object to be passed back as * a parameter to fn. * @param {boolean|object} overrideContext If set to true, fn will execute * in the context of obj, if set to an object it * will execute in the context of that object * @param checkContent {boolean} check child node readiness (onContentReady) * @static */ onAvailable: function(id, fn, obj, overrideContext, checkContent) { var a = (YAHOO.lang.isString(id)) ? [id] : id; for (var 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} obj an optional object to be passed back as * a parameter to fn. * @param {boolean|object} overrideContext If set to true, fn will execute * in the context of obj. If an object, fn will * exectute in the context of that object * * @static */ onContentReady: function(id, fn, obj, overrideContext) { this.onAvailable(id, fn, obj, overrideContext, true); }, /** * Executes the supplied callback when the DOM is first usable. This * will execute immediately if called after the DOMReady event has * fired. @todo the DOMContentReady event does not fire when the * script is dynamically injected into the page. This means the * DOMReady custom event will never fire in FireFox or Opera when the * library is injected. It _will_ fire in Safari, and the IE * implementation would allow for us to fire it if the defered script * is not available. We want this to behave the same in all browsers. * Is there a way to identify when the script has been injected * instead of included inline? Is there a way to know whether the * window onload event has fired without having had a listener attached * to it when it did so? * *

    The callback is a CustomEvent, so the signature is:

    *

    type <string>, args <array>, customobject <object>

    *

    For DOMReady events, there are no fire argments, so the * signature is:

    *

    "DOMReady", [], obj

    * * * @method onDOMReady * * @param {function} fn what to execute when the element is found. * @param {object} obj an optional object to be passed back as * a parameter to fn. * @param {boolean|object} overrideContext If set to true, fn will execute * in the context of obj, if set to an object it * will execute in the context of that object * * @static */ // onDOMReady: function(fn, obj, overrideContext) { onDOMReady: function() { this.DOMReadyEvent.subscribe.apply(this.DOMReadyEvent, arguments); }, /** * Appends an event handler * * @method _addListener * * @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 {String} sType The type of event to append * @param {Function} fn The method the event invokes * @param {Object} obj An arbitrary object that will be * passed as a parameter to the handler * @param {Boolean|object} overrideContext If true, the obj passed in becomes * the execution context of the listener. If an * object, this object becomes the execution * context. * @param {boolen} capture capture or bubble phase * @return {Boolean} True if the action was successful or defered, * false if one or more of the elements * could not have the listener attached, * or if the operation throws an exception. * @private * @static */ _addListener: function(el, sType, fn, obj, overrideContext, bCapture) { if (!fn || !fn.call) { return false; } // The el argument can be an array of elements or element ids. if ( this._isValidCollection(el)) { var ok = true; for (var i=0,len=el.length; i-1; i--) { ok = ( this.removeListener(el[i], sType, fn) && ok ); } return ok; } if (!fn || !fn.call) { //return false; return this.purgeElement(el, false, sType); } if ("unload" == sType) { for (i=unloadListeners.length-1; i>-1; i--) { li = unloadListeners[i]; if (li && li[0] == el && li[1] == sType && li[2] == fn) { unloadListeners.splice(i, 1); // unloadListeners[i]=null; return true; } } return false; } var cacheItem = null; // The index is a hidden parameter; needed to remove it from // the method signature because it was tempting users to // try and take advantage of it, which is not possible. var index = arguments[3]; if ("undefined" === typeof index) { index = this._getCacheIndex(listeners, el, sType, fn); } if (index >= 0) { cacheItem = listeners[index]; } if (!el || !cacheItem) { return false; } var bCapture = cacheItem[this.CAPTURE] === true ? true : false; try { this._simpleRemove(el, sType, cacheItem[this.WFN], bCapture); } catch(ex) { this.lastError = ex; return false; } // removed the wrapped handler delete listeners[index][this.WFN]; delete listeners[index][this.FN]; listeners.splice(index, 1); // listeners[index]=null; return true; }, /** * Returns the event's target element. Safari sometimes provides * a text node, and this is automatically resolved to the text * node's parent so that it behaves like other browsers. * @method getTarget * @param {Event} ev the event * @param {boolean} resolveTextNode when set to true the target's * parent will be returned if the target is a * text node. @deprecated, the text node is * now resolved automatically * @return {HTMLElement} the event's target * @static */ getTarget: function(ev, resolveTextNode) { var t = ev.target || ev.srcElement; return this.resolveTextNode(t); }, /** * In some cases, some browsers will return a text node inside * the actual element that was targeted. This normalizes the * return value for getTarget and getRelatedTarget. * @method resolveTextNode * @param {HTMLElement} node node to resolve * @return {HTMLElement} the normized node * @static */ resolveTextNode: function(n) { try { if (n && 3 == n.nodeType) { return n.parentNode; } } catch(e) { } return n; }, /** * Returns the event's pageX * @method getPageX * @param {Event} ev the event * @return {int} the event's pageX * @static */ getPageX: function(ev) { var x = ev.pageX; if (!x && 0 !== x) { x = ev.clientX || 0; if ( this.isIE ) { x += this._getScrollLeft(); } } return x; }, /** * Returns the event's pageY * @method getPageY * @param {Event} ev the event * @return {int} the event's pageY * @static */ getPageY: function(ev) { var y = ev.pageY; if (!y && 0 !== y) { y = ev.clientY || 0; if ( this.isIE ) { y += this._getScrollTop(); } } return y; }, /** * Returns the pageX and pageY properties as an indexed array. * @method getXY * @param {Event} ev the event * @return {[x, y]} the pageX and pageY properties of the event * @static */ getXY: function(ev) { return [this.getPageX(ev), this.getPageY(ev)]; }, /** * Returns the event's related target * @method getRelatedTarget * @param {Event} ev the event * @return {HTMLElement} the event's relatedTarget * @static */ getRelatedTarget: function(ev) { var t = ev.relatedTarget; if (!t) { if (ev.type == "mouseout") { t = ev.toElement; } else if (ev.type == "mouseover") { t = ev.fromElement; } } return this.resolveTextNode(t); }, /** * Returns the time of the event. If the time is not included, the * event is modified using the current time. * @method getTime * @param {Event} ev the event * @return {Date} the time of the event * @static */ getTime: function(ev) { if (!ev.time) { var t = new Date().getTime(); try { ev.time = t; } catch(ex) { this.lastError = ex; return t; } } return ev.time; }, /** * Convenience method for stopPropagation + preventDefault * @method stopEvent * @param {Event} ev the event * @static */ stopEvent: function(ev) { this.stopPropagation(ev); this.preventDefault(ev); }, /** * Stops event propagation * @method stopPropagation * @param {Event} ev the event * @static */ stopPropagation: function(ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, /** * Prevents the default behavior of the event * @method preventDefault * @param {Event} ev the event * @static */ preventDefault: function(ev) { if (ev.preventDefault) { ev.preventDefault(); } else { ev.returnValue = false; } }, /** * Finds the event in the window object, the caller's arguments, or * in the arguments of another method in the callstack. This is * executed automatically for events registered through the event * manager, so the implementer should not normally need to execute * this function at all. * @method getEvent * @param {Event} e the event parameter from the handler * @param {HTMLElement} boundEl the element the listener is attached to * @return {Event} the event * @static */ getEvent: function(e, boundEl) { var ev = e || window.event; if (!ev) { var c = this.getEvent.caller; while (c) { ev = c.arguments[0]; if (ev && Event == ev.constructor) { break; } c = c.caller; } } return ev; }, /** * Returns the charcode for an event * @method getCharCode * @param {Event} ev the event * @return {int} the event's charCode * @static */ getCharCode: function(ev) { var code = ev.keyCode || ev.charCode || 0; // webkit key normalization if (YAHOO.env.ua.webkit && (code in webkitKeymap)) { code = webkitKeymap[code]; } return code; }, /** * Locating the saved event handler data by function ref * * @method _getCacheIndex * @static * @private */ _getCacheIndex: function(a, el, sType, fn) { for (var i=0, l=a.length; i 0 && onAvailStack.length > 0); } // onAvailable var notAvail = []; var executeItem = function (el, item) { var context = el; if (item.overrideContext) { if (item.overrideContext === true) { context = item.obj; } else { context = item.overrideContext; } } item.fn.call(context, item.obj); }; var i, len, item, el, ready=[]; // onAvailable onContentReady for (i=0, len=onAvailStack.length; i-1; i--) { item = onAvailStack[i]; if (!item || !item.id) { onAvailStack.splice(i, 1); } } this.startInterval(); } else { if (this._interval) { // clearInterval(this._interval); this._interval.cancel(); this._interval = null; } } this.locked = false; }, /** * Removes all listeners attached to the given element via addListener. * Optionally, the node's children can also be purged. * Optionally, you can specify a specific type of event to remove. * @method purgeElement * @param {HTMLElement} el the element to purge * @param {boolean} recurse recursively purge this element's children * as well. Use with caution. * @param {string} sType optional type of listener to purge. If * left out, all listeners will be removed * @static */ purgeElement: function(el, recurse, sType) { var oEl = (YAHOO.lang.isString(el)) ? this.getEl(el) : el; var elListeners = this.getListeners(oEl, sType), i, len; if (elListeners) { for (i=elListeners.length-1; i>-1; i--) { var l = elListeners[i]; this.removeListener(oEl, l.type, l.fn); } } if (recurse && oEl && oEl.childNodes) { for (i=0,len=oEl.childNodes.length; i-1; j--) { l = listeners[j]; if (l) { EU.removeListener(l[EU.EL], l[EU.TYPE], l[EU.FN], j); } } l=null; } EU._simpleRemove(window, "unload", EU._unload); }, /** * Returns scrollLeft * @method _getScrollLeft * @static * @private */ _getScrollLeft: function() { return this._getScroll()[1]; }, /** * Returns scrollTop * @method _getScrollTop * @static * @private */ _getScrollTop: function() { return this._getScroll()[0]; }, /** * Returns the scrollTop and scrollLeft. Used to calculate the * pageX and pageY in Internet Explorer * @method _getScroll * @static * @private */ _getScroll: function() { var dd = document.documentElement, db = document.body; if (dd && (dd.scrollTop || dd.scrollLeft)) { return [dd.scrollTop, dd.scrollLeft]; } else if (db) { return [db.scrollTop, db.scrollLeft]; } else { return [0, 0]; } }, /** * Used by old versions of CustomEvent, restored for backwards * compatibility * @method regCE * @private * @static * @deprecated still here for backwards compatibility */ regCE: function() {}, /** * Adds a DOM event directly without the caching, cleanup, context adj, etc * * @method _simpleAdd * @param {HTMLElement} el the element to bind the handler to * @param {string} sType the type of event handler * @param {function} fn the callback to invoke * @param {boolen} capture capture or bubble phase * @static * @private */ _simpleAdd: function () { if (window.addEventListener) { return function(el, sType, fn, capture) { el.addEventListener(sType, fn, (capture)); }; } else if (window.attachEvent) { return function(el, sType, fn, capture) { el.attachEvent("on" + sType, fn); }; } else { return function(){}; } }(), /** * Basic remove listener * * @method _simpleRemove * @param {HTMLElement} el the element to bind the handler to * @param {string} sType the type of event handler * @param {function} fn the callback to invoke * @param {boolen} capture capture or bubble phase * @static * @private */ _simpleRemove: function() { if (window.removeEventListener) { return function (el, sType, fn, capture) { el.removeEventListener(sType, fn, (capture)); }; } else if (window.detachEvent) { return function (el, sType, fn) { el.detachEvent("on" + sType, fn); }; } else { return function(){}; } }() }; }(); (function() { var EU = YAHOO.util.Event; /** * YAHOO.util.Event.on is an alias for addListener * @method on * @see addListener * @static */ EU.on = EU.addListener; /** * YAHOO.util.Event.onFocus is an alias for addFocusListener * @method onFocus * @see addFocusListener * @static * @deprecated use YAHOO.util.Event.on and specify "focusin" as the event type. */ EU.onFocus = EU.addFocusListener; /** * YAHOO.util.Event.onBlur is an alias for addBlurListener * @method onBlur * @see addBlurListener * @static * @deprecated use YAHOO.util.Event.on and specify "focusout" as the event type. */ EU.onBlur = EU.addBlurListener; /*! 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 (EU.isIE) { if (self !== self.top) { document.onreadystatechange = function() { if (document.readyState == 'complete') { document.onreadystatechange = null; EU._ready(); } }; } else { // Process onAvailable/onContentReady items when the // DOM is ready. YAHOO.util.Event.onDOMReady( YAHOO.util.Event._tryPreloadAttach, YAHOO.util.Event, true); var n = document.createElement('p'); EU._dri = setInterval(function() { try { // throws an error if doc is not ready n.doScroll('left'); clearInterval(EU._dri); EU._dri = null; EU._ready(); n = null; } catch (ex) { } }, EU.POLL_INTERVAL); } // The document's readyState in Safari currently will // change to loaded/complete before images are loaded. } else if (EU.webkit && EU.webkit < 525) { EU._dri = setInterval(function() { var rs=document.readyState; if ("loaded" == rs || "complete" == rs) { clearInterval(EU._dri); EU._dri = null; EU._ready(); } }, EU.POLL_INTERVAL); // FireFox and Opera: These browsers provide a event for this // moment. The latest WebKit releases now support this event. } else { EU._simpleAdd(document, "DOMContentLoaded", EU._ready); } ///////////////////////////////////////////////////////////// EU._simpleAdd(window, "load", EU._load); EU._simpleAdd(window, "unload", EU._unload); EU._tryPreloadAttach(); })(); } /** * EventProvider is designed to be used with YAHOO.augment to wrap * CustomEvents in an interface that allows events to be subscribed 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 EventProvider */ YAHOO.util.EventProvider = function() { }; YAHOO.util.EventProvider.prototype = { /** * Private storage of custom events * @property __yui_events * @type Object[] * @private */ __yui_events: null, /** * Private storage of custom event subscribers * @property __yui_subscribers * @type Object[] * @private */ __yui_subscribers: null, /** * Subscribe to a CustomEvent by event type * * @method subscribe * @param p_type {string} the type, or name of the event * @param p_fn {function} the function to exectute when the event fires * @param p_obj {Object} An object to be passed along when the event * fires * @param overrideContext {boolean} If true, the obj passed in becomes the * execution scope of the listener */ subscribe: function(p_type, p_fn, p_obj, overrideContext) { this.__yui_events = this.__yui_events || {}; var ce = this.__yui_events[p_type]; if (ce) { ce.subscribe(p_fn, p_obj, overrideContext); } else { this.__yui_subscribers = this.__yui_subscribers || {}; var subs = this.__yui_subscribers; if (!subs[p_type]) { subs[p_type] = []; } subs[p_type].push( { fn: p_fn, obj: p_obj, overrideContext: overrideContext } ); } }, /** * Unsubscribes one or more listeners the from the specified event * @method unsubscribe * @param p_type {string} The type, or name of the event. If the type * is not specified, it will attempt to remove * the listener from all hosted events. * @param p_fn {Function} The subscribed function to unsubscribe, if not * supplied, all subscribers will be removed. * @param p_obj {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 {boolean} true if the subscriber was found and detached. */ unsubscribe: function(p_type, p_fn, p_obj) { this.__yui_events = this.__yui_events || {}; var evts = this.__yui_events; if (p_type) { var ce = evts[p_type]; if (ce) { return ce.unsubscribe(p_fn, p_obj); } } else { var ret = true; for (var i in evts) { if (YAHOO.lang.hasOwnProperty(evts, i)) { ret = ret && evts[i].unsubscribe(p_fn, p_obj); } } return ret; } return false; }, /** * 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 p_type {string} The type, or name of the event */ unsubscribeAll: function(p_type) { return this.unsubscribe(p_type); }, /** * 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 createEvent * * @param p_type {string} the type, or name of the event * @param p_config {object} optional config params. Valid properties are: * *
      *
    • * scope: defines the default execution scope. If not defined * the default scope will be this instance. *
    • *
    • * silent: if true, the custom event will not generate log messages. * This is false by default. *
    • *
    • * fireOnce: if true, the custom event will only notify subscribers * once regardless of the number of times the event is fired. In * addition, new subscribers will be executed immediately if the * event has already fired. * This is false by default. *
    • *
    • * onSubscribeCallback: specifies a callback to execute when the * event has a new subscriber. This will fire immediately for * each queued subscriber if any exist prior to the creation of * the event. *
    • *
    * * @return {CustomEvent} the custom event * */ createEvent: function(p_type, p_config) { this.__yui_events = this.__yui_events || {}; var opts = p_config || {}, events = this.__yui_events, ce; if (events[p_type]) { } else { ce = new YAHOO.util.CustomEvent(p_type, opts.scope || this, opts.silent, YAHOO.util.CustomEvent.FLAT, opts.fireOnce); events[p_type] = ce; if (opts.onSubscribeCallback) { ce.subscribeEvent.subscribe(opts.onSubscribeCallback); } this.__yui_subscribers = this.__yui_subscribers || {}; var qs = this.__yui_subscribers[p_type]; if (qs) { for (var i=0; i *
  • The first argument fire() was executed with
  • *
  • The custom object (if any) that was passed into the subscribe() * method
  • * * @method fireEvent * @param p_type {string} the type, or name of the event * @param arguments {Object*} an arbitrary set of parameters to pass to * the handler. * @return {boolean} the return value from CustomEvent.fire * */ fireEvent: function(p_type) { this.__yui_events = this.__yui_events || {}; var ce = this.__yui_events[p_type]; if (!ce) { return null; } var args = []; for (var i=1; iSee: Element.addListener

    *

    Event fields:
    * <String> type available
    * <HTMLElement> * target the HTMLElement bound to this Element instance
    *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('available', handler);

    * @event available */ /** * Fires when the Element's HTMLElement subtree is rendered. *

    See: Element.addListener

    *

    Event fields:
    * <String> type contentReady
    * <HTMLElement> * target the HTMLElement bound to this Element instance
    *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('contentReady', handler);

    * @event contentReady */ /** * Fires before the Element is appended to another Element. *

    See: Element.addListener

    *

    Event fields:
    * <String> type beforeAppendTo
    * <HTMLElement/Element> * target the HTMLElement/Element being appended to *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('beforeAppendTo', handler);

    * @event beforeAppendTo */ /** * Fires after the Element is appended to another Element. *

    See: Element.addListener

    *

    Event fields:
    * <String> type appendTo
    * <HTMLElement/Element> * target the HTMLElement/Element being appended to *

    Usage:
    * var handler = function(e) {var target = e.target};
    * myTabs.addListener('appendTo', handler);

    * @event appendTo */ YAHOO.augment(Element, AttributeProvider); YAHOO.util.Element = Element; })(); YAHOO.register("element", YAHOO.util.Element, {version: "2.8.0r4", build: "2446"}); /* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ (function () { /** * The Paginator widget provides a set of controls to navigate through paged * data. * * @module paginator * @uses YAHOO.util.EventProvider * @uses YAHOO.util.AttributeProvider */ var Dom = YAHOO.util.Dom, lang = YAHOO.lang, isObject = lang.isObject, isFunction = lang.isFunction, isArray = lang.isArray, isString = lang.isString; /** * Instantiate a Paginator, passing a configuration object to the contructor. * The configuration object should contain the following properties: *
      *
    • rowsPerPage : n (int)
    • *
    • totalRecords : n (int or Paginator.VALUE_UNLIMITED)
    • *
    • containers : id | el | arr (HTMLElement reference, its id, or an array of either)
    • *
    * * @namespace YAHOO.widget * @class Paginator * @constructor * @param config {Object} Object literal to set instance and ui component * configuration. */ function Paginator(config) { var UNLIMITED = Paginator.VALUE_UNLIMITED, attrib, initialPage, records, perPage, startIndex; config = isObject(config) ? config : {}; this.initConfig(); this.initEvents(); // Set the basic config keys first this.set('rowsPerPage',config.rowsPerPage,true); if (Paginator.isNumeric(config.totalRecords)) { this.set('totalRecords',config.totalRecords,true); } this.initUIComponents(); // Update the other config values for (attrib in config) { if (config.hasOwnProperty(attrib)) { this.set(attrib,config[attrib],true); } } // Calculate the initial record offset initialPage = this.get('initialPage'); records = this.get('totalRecords'); perPage = this.get('rowsPerPage'); if (initialPage > 1 && perPage !== UNLIMITED) { startIndex = (initialPage - 1) * perPage; if (records === UNLIMITED || startIndex < records) { this.set('recordOffset',startIndex,true); } } } // Static members lang.augmentObject(Paginator, { /** * Incrementing index used to give instances unique ids. * @static * @property Paginator.id * @type number * @private */ id : 0, /** * Base of id strings used for ui components. * @static * @property Paginator.ID_BASE * @type string * @private */ ID_BASE : 'yui-pg', /** * Used to identify unset, optional configurations, or used explicitly in * the case of totalRecords to indicate unlimited pagination. * @static * @property Paginator.VALUE_UNLIMITED * @type number * @final */ VALUE_UNLIMITED : -1, /** * Default template used by Paginator instances. Update this if you want * all new Paginators to use a different default template. * @static * @property Paginator.TEMPLATE_DEFAULT * @type string */ TEMPLATE_DEFAULT : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink}", /** * Common alternate pagination format, including page links, links for * previous, next, first and last pages as well as a rows-per-page * dropdown. Offered as a convenience. * @static * @property Paginator.TEMPLATE_ROWS_PER_PAGE * @type string */ TEMPLATE_ROWS_PER_PAGE : "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}", /** * Storage object for UI Components * @static * @property Paginator.ui */ ui : {}, /** * Similar to YAHOO.lang.isNumber, but allows numeric strings. This is * is used for attribute validation in conjunction with getters that return * numbers. * * @method Paginator.isNumeric * @param v {Number|String} value to be checked for number or numeric string * @returns {Boolean} true if the input is coercable into a finite number * @static */ isNumeric : function (v) { return isFinite(+v); }, /** * Return a number or null from input * * @method Paginator.toNumber * @param n {Number|String} a number or numeric string * @return Number * @static */ toNumber : function (n) { return isFinite(+n) ? +n : null; } },true); // Instance members and methods Paginator.prototype = { // Instance members /** * Array of nodes in which to render pagination controls. This is set via * the "containers" attribute. * @property _containers * @type Array(HTMLElement) * @private */ _containers : [], /** * Flag used to indicate multiple attributes are being updated via setState * @property _batch * @type boolean * @protected */ _batch : false, /** * Used by setState to indicate when a page change has occurred * @property _pageChanged * @type boolean * @protected */ _pageChanged : false, /** * Temporary state cache used by setState to keep track of the previous * state for eventual pageChange event firing * @property _state * @type Object * @protected */ _state : null, // Instance methods /** * Initialize the Paginator's attributes (see YAHOO.util.Element class * AttributeProvider). * @method initConfig * @private */ initConfig : function () { var UNLIMITED = Paginator.VALUE_UNLIMITED; /** * REQUIRED. Number of records constituting a "page" * @attribute rowsPerPage * @type integer */ this.setAttributeConfig('rowsPerPage', { value : 0, validator : Paginator.isNumeric, setter : Paginator.toNumber }); /** * REQUIRED. Node references or ids of nodes in which to render the * pagination controls. * @attribute containers * @type {string|HTMLElement|Array(string|HTMLElement)} */ this.setAttributeConfig('containers', { value : null, validator : function (val) { if (!isArray(val)) { val = [val]; } for (var i = 0, len = val.length; i < len; ++i) { if (isString(val[i]) || (isObject(val[i]) && val[i].nodeType === 1)) { continue; } return false; } return true; }, method : function (val) { val = Dom.get(val); if (!isArray(val)) { val = [val]; } this._containers = val; } }); /** * Total number of records to paginate through * @attribute totalRecords * @type integer * @default 0 */ this.setAttributeConfig('totalRecords', { value : 0, validator : Paginator.isNumeric, setter : Paginator.toNumber }); /** * Zero based index of the record considered first on the current page. * For page based interactions, don't modify this attribute directly; * use setPage(n). * @attribute recordOffset * @type integer * @default 0 */ this.setAttributeConfig('recordOffset', { value : 0, validator : function (val) { var total = this.get('totalRecords'); if (Paginator.isNumeric(val)) { val = +val; return total === UNLIMITED || total > val || (total === 0 && val === 0); } return false; }, setter : Paginator.toNumber }); /** * Page to display on initial paint * @attribute initialPage * @type integer * @default 1 */ this.setAttributeConfig('initialPage', { value : 1, validator : Paginator.isNumeric, setter : Paginator.toNumber }); /** * Template used to render controls. The string will be used as * innerHTML on all specified container nodes. Bracketed keys * (e.g. {pageLinks}) in the string will be replaced with an instance * of the so named ui component. * @see Paginator.TEMPLATE_DEFAULT * @see Paginator.TEMPLATE_ROWS_PER_PAGE * @attribute template * @type string */ this.setAttributeConfig('template', { value : Paginator.TEMPLATE_DEFAULT, validator : isString }); /** * Class assigned to the element(s) containing pagination controls. * @attribute containerClass * @type string * @default 'yui-pg-container' */ this.setAttributeConfig('containerClass', { value : 'yui-pg-container', validator : isString }); /** * Display pagination controls even when there is only one page. Set * to false to forgo rendering and/or hide the containers when there * is only one page of data. Note if you are using the rowsPerPage * dropdown ui component, visibility will be maintained as long as the * number of records exceeds the smallest page size. * @attribute alwaysVisible * @type boolean * @default true */ this.setAttributeConfig('alwaysVisible', { value : true, validator : lang.isBoolean }); /** * Update the UI immediately upon interaction. If false, changeRequest * subscribers or other external code will need to explicitly set the * new values in the paginator to trigger repaint. * @attribute updateOnChange * @type boolean * @default false * @deprecated use changeRequest listener that calls setState */ this.setAttributeConfig('updateOnChange', { value : false, validator : lang.isBoolean }); // Read only attributes /** * Unique id assigned to this instance * @attribute id * @type integer * @final */ this.setAttributeConfig('id', { value : Paginator.id++, readOnly : true }); /** * Indicator of whether the DOM nodes have been initially created * @attribute rendered * @type boolean * @final */ this.setAttributeConfig('rendered', { value : false, readOnly : true }); }, /** * Initialize registered ui components onto this instance. * @method initUIComponents * @private */ initUIComponents : function () { var ui = Paginator.ui, name,UIComp; for (name in ui) { if (ui.hasOwnProperty(name)) { UIComp = ui[name]; if (isObject(UIComp) && isFunction(UIComp.init)) { UIComp.init(this); } } } }, /** * Initialize this instance's CustomEvents. * @method initEvents * @private */ initEvents : function () { /** * Event fired when the Paginator is initially rendered * @event render */ this.createEvent('render'); /** * Event fired when the Paginator is initially rendered * @event rendered * @deprecated use render event */ this.createEvent('rendered'); // backward compatibility /** * Event fired when a change in pagination values is requested, * either by interacting with the various ui components or via the * setStartIndex(n) etc APIs. * Subscribers will receive the proposed state as the first parameter. * The proposed state object will contain the following keys: *
      *
    • paginator - the Paginator instance
    • *
    • page
    • *
    • totalRecords
    • *
    • recordOffset - index of the first record on the new page
    • *
    • rowsPerPage
    • *
    • records - array containing [start index, end index] for the records on the new page
    • *
    • before - object literal with all these keys for the current state
    • *
    * @event changeRequest */ this.createEvent('changeRequest'); /** * Event fired when attribute changes have resulted in the calculated * current page changing. * @event pageChange */ this.createEvent('pageChange'); /** * Event that fires before the destroy event. * @event beforeDestroy */ this.createEvent('beforeDestroy'); /** * Event used to trigger cleanup of ui components * @event destroy */ this.createEvent('destroy'); this._selfSubscribe(); }, /** * Subscribes to instance attribute change events to automate certain * behaviors. * @method _selfSubscribe * @protected */ _selfSubscribe : function () { // Listen for changes to totalRecords and alwaysVisible this.subscribe('totalRecordsChange',this.updateVisibility,this,true); this.subscribe('alwaysVisibleChange',this.updateVisibility,this,true); // Fire the pageChange event when appropriate this.subscribe('totalRecordsChange',this._handleStateChange,this,true); this.subscribe('recordOffsetChange',this._handleStateChange,this,true); this.subscribe('rowsPerPageChange',this._handleStateChange,this,true); // Update recordOffset when totalRecords is reduced below this.subscribe('totalRecordsChange',this._syncRecordOffset,this,true); }, /** * Sets recordOffset to the starting index of the previous page when * totalRecords is reduced below the current recordOffset. * @method _syncRecordOffset * @param e {Event} totalRecordsChange event * @protected */ _syncRecordOffset : function (e) { var v = e.newValue,rpp,state; if (e.prevValue !== v) { if (v !== Paginator.VALUE_UNLIMITED) { rpp = this.get('rowsPerPage'); if (rpp && this.get('recordOffset') >= v) { state = this.getState({ totalRecords : e.prevValue, recordOffset : this.get('recordOffset') }); this.set('recordOffset', state.before.recordOffset); this._firePageChange(state); } } } }, /** * Fires the pageChange event when the state attributes have changed in * such a way as to locate the current recordOffset on a new page. * @method _handleStateChange * @param e {Event} the attribute change event * @protected */ _handleStateChange : function (e) { if (e.prevValue !== e.newValue) { var change = this._state || {}, state; change[e.type.replace(/Change$/,'')] = e.prevValue; state = this.getState(change); if (state.page !== state.before.page) { if (this._batch) { this._pageChanged = true; } else { this._firePageChange(state); } } } }, /** * Fires a pageChange event in the form of a standard attribute change * event with additional properties prevState and newState. * @method _firePageChange * @param state {Object} the result of getState(oldState) * @protected */ _firePageChange : function (state) { if (isObject(state)) { var current = state.before; delete state.before; this.fireEvent('pageChange',{ type : 'pageChange', prevValue : state.page, newValue : current.page, prevState : state, newState : current }); } }, /** * Render the pagination controls per the format attribute into the * specified container nodes. * @method render * @return the Paginator instance * @chainable */ render : function () { if (this.get('rendered')) { return this; } var template = this.get('template'), state = this.getState(), // ex. yui-pg0-1 (first paginator, second container) id_base = Paginator.ID_BASE + this.get('id') + '-', i, len; // Assemble the containers, keeping them hidden for (i = 0, len = this._containers.length; i < len; ++i) { this._renderTemplate(this._containers[i],template,id_base+i,true); } // Show the containers if appropriate this.updateVisibility(); // Set render attribute manually to support its readOnly contract if (this._containers.length) { this.setAttributeConfig('rendered', { value: true }); this.fireEvent('render', state); // For backward compatibility this.fireEvent('rendered', state); } return this; }, /** * Creates the individual ui components and renders them into a container. * * @method _renderTemplate * @param container {HTMLElement} where to add the ui components * @param template {String} the template to use as a guide for rendering * @param id_base {String} id base for the container's ui components * @param hide {Boolean} leave the container hidden after assembly * @protected */ _renderTemplate : function (container, template, id_base, hide) { var containerClass = this.get('containerClass'), markers, i, len; if (!container) { return; } // Hide the container while its contents are rendered Dom.setStyle(container,'display','none'); Dom.addClass(container, containerClass); // Place the template innerHTML, adding marker spans to the template // html to indicate drop zones for ui components container.innerHTML = template.replace(/\{([a-z0-9_ \-]+)\}/gi, ''); // Replace each marker with the ui component's render() output markers = Dom.getElementsByClassName('yui-pg-ui','span',container); for (i = 0, len = markers.length; i < len; ++i) { this.renderUIComponent(markers[i], id_base); } if (!hide) { // Show the container allowing page reflow Dom.setStyle(container,'display',''); } }, /** * Replaces a marker node with a rendered UI component, determined by the * yui-pg-ui-(UI component class name) in the marker's className. e.g. * yui-pg-ui-PageLinks => new YAHOO.widget.Paginator.ui.PageLinks(this) * * @method renderUIComponent * @param marker {HTMLElement} the marker node to replace * @param id_base {String} string base the component's generated id */ renderUIComponent : function (marker, id_base) { var par = marker.parentNode, name = /yui-pg-ui-(\w+)/.exec(marker.className), UIComp = name && Paginator.ui[name[1]], comp; if (isFunction(UIComp)) { comp = new UIComp(this); if (isFunction(comp.render)) { par.replaceChild(comp.render(id_base),marker); } } }, /** * Removes controls from the page and unhooks events. * @method destroy */ destroy : function () { this.fireEvent('beforeDestroy'); this.fireEvent('destroy'); this.setAttributeConfig('rendered',{value:false}); this.unsubscribeAll(); }, /** * Hides the containers if there is only one page of data and attribute * alwaysVisible is false. Conversely, it displays the containers if either * there is more than one page worth of data or alwaysVisible is turned on. * @method updateVisibility */ updateVisibility : function (e) { var alwaysVisible = this.get('alwaysVisible'), totalRecords,visible,rpp,rppOptions,i,len; if (!e || e.type === 'alwaysVisibleChange' || !alwaysVisible) { totalRecords = this.get('totalRecords'); visible = true; rpp = this.get('rowsPerPage'); rppOptions = this.get('rowsPerPageOptions'); if (isArray(rppOptions)) { for (i = 0, len = rppOptions.length; i < len; ++i) { rpp = Math.min(rpp,rppOptions[i]); } } if (totalRecords !== Paginator.VALUE_UNLIMITED && totalRecords <= rpp) { visible = false; } visible = visible || alwaysVisible; for (i = 0, len = this._containers.length; i < len; ++i) { Dom.setStyle(this._containers[i],'display', visible ? '' : 'none'); } } }, /** * Get the configured container nodes * @method getContainerNodes * @return {Array} array of HTMLElement nodes */ getContainerNodes : function () { return this._containers; }, /** * Get the total number of pages in the data set according to the current * rowsPerPage and totalRecords values. If totalRecords is not set, or * set to YAHOO.widget.Paginator.VALUE_UNLIMITED, returns * YAHOO.widget.Paginator.VALUE_UNLIMITED. * @method getTotalPages * @return {number} */ getTotalPages : function () { var records = this.get('totalRecords'), perPage = this.get('rowsPerPage'); // rowsPerPage not set. Can't calculate if (!perPage) { return null; } if (records === Paginator.VALUE_UNLIMITED) { return Paginator.VALUE_UNLIMITED; } return Math.ceil(records/perPage); }, /** * Does the requested page have any records? * @method hasPage * @param page {number} the page in question * @return {boolean} */ hasPage : function (page) { if (!lang.isNumber(page) || page < 1) { return false; } var totalPages = this.getTotalPages(); return (totalPages === Paginator.VALUE_UNLIMITED || totalPages >= page); }, /** * Get the page number corresponding to the current record offset. * @method getCurrentPage * @return {number} */ getCurrentPage : function () { var perPage = this.get('rowsPerPage'); if (!perPage || !this.get('totalRecords')) { return 0; } return Math.floor(this.get('recordOffset') / perPage) + 1; }, /** * Are there records on the next page? * @method hasNextPage * @return {boolean} */ hasNextPage : function () { var currentPage = this.getCurrentPage(), totalPages = this.getTotalPages(); return currentPage && (totalPages === Paginator.VALUE_UNLIMITED || currentPage < totalPages); }, /** * Get the page number of the next page, or null if the current page is the * last page. * @method getNextPage * @return {number} */ getNextPage : function () { return this.hasNextPage() ? this.getCurrentPage() + 1 : null; }, /** * Is there a page before the current page? * @method hasPreviousPage * @return {boolean} */ hasPreviousPage : function () { return (this.getCurrentPage() > 1); }, /** * Get the page number of the previous page, or null if the current page * is the first page. * @method getPreviousPage * @return {number} */ getPreviousPage : function () { return (this.hasPreviousPage() ? this.getCurrentPage() - 1 : 1); }, /** * Get the start and end record indexes of the specified page. * @method getPageRecords * @param page {number} (optional) The page (current page if not specified) * @return {Array} [start_index, end_index] */ getPageRecords : function (page) { if (!lang.isNumber(page)) { page = this.getCurrentPage(); } var perPage = this.get('rowsPerPage'), records = this.get('totalRecords'), start, end; if (!page || !perPage) { return null; } start = (page - 1) * perPage; if (records !== Paginator.VALUE_UNLIMITED) { if (start >= records) { return null; } end = Math.min(start + perPage, records) - 1; } else { end = start + perPage - 1; } return [start,end]; }, /** * Set the current page to the provided page number if possible. * @method setPage * @param newPage {number} the new page number * @param silent {boolean} whether to forcibly avoid firing the * changeRequest event */ setPage : function (page,silent) { if (this.hasPage(page) && page !== this.getCurrentPage()) { if (this.get('updateOnChange') || silent) { this.set('recordOffset', (page - 1) * this.get('rowsPerPage')); } else { this.fireEvent('changeRequest',this.getState({'page':page})); } } }, /** * Get the number of rows per page. * @method getRowsPerPage * @return {number} the current setting of the rowsPerPage attribute */ getRowsPerPage : function () { return this.get('rowsPerPage'); }, /** * Set the number of rows per page. * @method setRowsPerPage * @param rpp {number} the new number of rows per page * @param silent {boolean} whether to forcibly avoid firing the * changeRequest event */ setRowsPerPage : function (rpp,silent) { if (Paginator.isNumeric(rpp) && +rpp > 0 && +rpp !== this.get('rowsPerPage')) { if (this.get('updateOnChange') || silent) { this.set('rowsPerPage',rpp); } else { this.fireEvent('changeRequest', this.getState({'rowsPerPage':+rpp})); } } }, /** * Get the total number of records. * @method getTotalRecords * @return {number} the current setting of totalRecords attribute */ getTotalRecords : function () { return this.get('totalRecords'); }, /** * Set the total number of records. * @method setTotalRecords * @param total {number} the new total number of records * @param silent {boolean} whether to forcibly avoid firing the changeRequest event */ setTotalRecords : function (total,silent) { if (Paginator.isNumeric(total) && +total >= 0 && +total !== this.get('totalRecords')) { if (this.get('updateOnChange') || silent) { this.set('totalRecords',total); } else { this.fireEvent('changeRequest', this.getState({'totalRecords':+total})); } } }, /** * Get the index of the first record on the current page * @method getStartIndex * @return {number} the index of the first record on the current page */ getStartIndex : function () { return this.get('recordOffset'); }, /** * Move the record offset to a new starting index. This will likely cause * the calculated current page to change. You should probably use setPage. * @method setStartIndex * @param offset {number} the new record offset * @param silent {boolean} whether to forcibly avoid firing the changeRequest event */ setStartIndex : function (offset,silent) { if (Paginator.isNumeric(offset) && +offset >= 0 && +offset !== this.get('recordOffset')) { if (this.get('updateOnChange') || silent) { this.set('recordOffset',offset); } else { this.fireEvent('changeRequest', this.getState({'recordOffset':+offset})); } } }, /** * Get an object literal describing the current state of the paginator. If * an object literal of proposed values is passed, the proposed state will * be returned as an object literal with the following keys: *
      *
    • paginator - instance of the Paginator
    • *
    • page - number
    • *
    • totalRecords - number
    • *
    • recordOffset - number
    • *
    • rowsPerPage - number
    • *
    • records - [ start_index, end_index ]
    • *
    • before - (OPTIONAL) { state object literal for current state }
    • *
    * @method getState * @return {object} * @param changes {object} OPTIONAL object literal with proposed values * Supported change keys include: *
      *
    • rowsPerPage
    • *
    • totalRecords
    • *
    • recordOffset OR
    • *
    • page
    • *
    */ getState : function (changes) { var UNLIMITED = Paginator.VALUE_UNLIMITED, M = Math, max = M.max, ceil = M.ceil, currentState, state, offset; function normalizeOffset(offset,total,rpp) { if (offset <= 0 || total === 0) { return 0; } if (total === UNLIMITED || total > offset) { return offset - (offset % rpp); } return total - (total % rpp || rpp); } currentState = { paginator : this, totalRecords : this.get('totalRecords'), rowsPerPage : this.get('rowsPerPage'), records : this.getPageRecords() }; currentState.recordOffset = normalizeOffset( this.get('recordOffset'), currentState.totalRecords, currentState.rowsPerPage); currentState.page = ceil(currentState.recordOffset / currentState.rowsPerPage) + 1; if (!changes) { return currentState; } state = { paginator : this, before : currentState, rowsPerPage : changes.rowsPerPage || currentState.rowsPerPage, totalRecords : (Paginator.isNumeric(changes.totalRecords) ? max(changes.totalRecords,UNLIMITED) : +currentState.totalRecords) }; if (state.totalRecords === 0) { state.recordOffset = state.page = 0; } else { offset = Paginator.isNumeric(changes.page) ? (changes.page - 1) * state.rowsPerPage : Paginator.isNumeric(changes.recordOffset) ? +changes.recordOffset : currentState.recordOffset; state.recordOffset = normalizeOffset(offset, state.totalRecords, state.rowsPerPage); state.page = ceil(state.recordOffset / state.rowsPerPage) + 1; } state.records = [ state.recordOffset, state.recordOffset + state.rowsPerPage - 1 ]; // limit upper index to totalRecords - 1 if (state.totalRecords !== UNLIMITED && state.recordOffset < state.totalRecords && state.records && state.records[1] > state.totalRecords - 1) { state.records[1] = state.totalRecords - 1; } return state; }, /** * Convenience method to facilitate setting state attributes rowsPerPage, * totalRecords, recordOffset in batch. Also supports calculating * recordOffset from state.page if state.recordOffset is not provided. * Fires only a single pageChange event, if appropriate. * This will not fire a changeRequest event. * @method setState * @param state {Object} Object literal of attribute:value pairs to set */ setState : function (state) { if (isObject(state)) { // get flux state based on current state with before state as well this._state = this.getState({}); // use just the state props from the input obj state = { page : state.page, rowsPerPage : state.rowsPerPage, totalRecords : state.totalRecords, recordOffset : state.recordOffset }; // calculate recordOffset from page if recordOffset not specified. // not using lang.isNumber for support of numeric strings if (state.page && state.recordOffset === undefined) { state.recordOffset = (state.page - 1) * (state.rowsPerPage || this.get('rowsPerPage')); } this._batch = true; this._pageChanged = false; for (var k in state) { if (state.hasOwnProperty(k) && this._configs.hasOwnProperty(k)) { this.set(k,state[k]); } } this._batch = false; if (this._pageChanged) { this._pageChanged = false; this._firePageChange(this.getState(this._state)); } } } }; lang.augmentProto(Paginator, YAHOO.util.AttributeProvider); YAHOO.widget.Paginator = Paginator; })(); (function () { var Paginator = YAHOO.widget.Paginator, l = YAHOO.lang; /** * ui Component to generate the textual report of current pagination status. * E.g. "Now viewing page 1 of 13". * * @namespace YAHOO.widget.Paginator.ui * @class CurrentPageReport * @for YAHOO.widget.Paginator * * @constructor * @param p {Pagintor} Paginator instance to attach to */ Paginator.ui.CurrentPageReport = function (p) { this.paginator = p; p.subscribe('recordOffsetChange', this.update,this,true); p.subscribe('rowsPerPageChange', this.update,this,true); p.subscribe('totalRecordsChange',this.update,this,true); p.subscribe('pageReportTemplateChange', this.update,this,true); p.subscribe('destroy',this.destroy,this,true); //TODO: make this work p.subscribe('pageReportClassChange', this.update,this,true); }; /** * Decorates Paginator instances with new attributes. Called during * Paginator instantiation. * @method init * @param p {Paginator} Paginator instance to decorate * @static */ Paginator.ui.CurrentPageReport.init = function (p) { /** * CSS class assigned to the span containing the info. * @attribute pageReportClass * @default 'yui-pg-current' */ p.setAttributeConfig('pageReportClass', { value : 'yui-pg-current', validator : l.isString }); /** * Used as innerHTML for the span. Place holders in the form of {name} * will be replaced with the so named value from the key:value map * generated by the function held in the pageReportValueGenerator attribute. * @attribute pageReportTemplate * @default '({currentPage} of {totalPages})' * @see pageReportValueGenerator attribute */ p.setAttributeConfig('pageReportTemplate', { value : '({currentPage} of {totalPages})', validator : l.isString }); /** * Function to generate the value map used to populate the * pageReportTemplate. The function is passed the Paginator instance as a * parameter. The default function returns a map with the following keys: *
      *
    • currentPage
    • *
    • totalPages
    • *
    • startIndex
    • *
    • endIndex
    • *
    • startRecord
    • *
    • endRecord
    • *
    • totalRecords
    • *
    * @attribute pageReportValueGenarator */ p.setAttributeConfig('pageReportValueGenerator', { value : function (paginator) { var curPage = paginator.getCurrentPage(), records = paginator.getPageRecords(); return { 'currentPage' : records ? curPage : 0, 'totalPages' : paginator.getTotalPages(), 'startIndex' : records ? records[0] : 0, 'endIndex' : records ? records[1] : 0, 'startRecord' : records ? records[0] + 1 : 0, 'endRecord' : records ? records[1] + 1 : 0, 'totalRecords': paginator.get('totalRecords') }; }, validator : l.isFunction }); }; /** * Replace place holders in a string with the named values found in an * object literal. * @static * @method sprintf * @param template {string} The content string containing place holders * @param values {object} The key:value pairs used to replace the place holders * @return {string} */ Paginator.ui.CurrentPageReport.sprintf = function (template, values) { return template.replace(/\{([\w\s\-]+)\}/g, function (x,key) { return (key in values) ? values[key] : ''; }); }; Paginator.ui.CurrentPageReport.prototype = { /** * Span node containing the formatted info * @property span * @type HTMLElement * @private */ span : null, /** * Generate the span containing info formatted per the pageReportTemplate * attribute. * @method render * @param id_base {string} used to create unique ids for generated nodes * @return {HTMLElement} */ render : function (id_base) { this.span = document.createElement('span'); this.span.id = id_base + '-page-report'; this.span.className = this.paginator.get('pageReportClass'); this.update(); return this.span; }, /** * Regenerate the content of the span if appropriate. Calls * CurrentPageReport.sprintf with the value of the pageReportTemplate * attribute and the value map returned from pageReportValueGenerator * function. * @method update * @param e {CustomEvent} The calling change event */ update : function (e) { if (e && e.prevValue === e.newValue) { return; } this.span.innerHTML = Paginator.ui.CurrentPageReport.sprintf( this.paginator.get('pageReportTemplate'), this.paginator.get('pageReportValueGenerator')(this.paginator)); }, /** * Removes the link/span node and clears event listeners * removal. * @method destroy * @private */ destroy : function () { this.span.parentNode.removeChild(this.span); this.span = null; } }; })(); (function () { var Paginator = YAHOO.widget.Paginator, l = YAHOO.lang; /** * ui Component to generate the page links * * @namespace YAHOO.widget.Paginator.ui * @class PageLinks * @for YAHOO.widget.Paginator * * @constructor * @param p {Pagintor} Paginator instance to attach to */ Paginator.ui.PageLinks = function (p) { this.paginator = p; p.subscribe('recordOffsetChange',this.update,this,true); p.subscribe('rowsPerPageChange',this.update,this,true); p.subscribe('totalRecordsChange',this.update,this,true); p.subscribe('pageLinksChange', this.rebuild,this,true); p.subscribe('pageLinkClassChange', this.rebuild,this,true); p.subscribe('currentPageClassChange', this.rebuild,this,true); p.subscribe('destroy',this.destroy,this,true); //TODO: Make this work p.subscribe('pageLinksContainerClassChange', this.rebuild,this,true); }; /** * Decorates Paginator instances with new attributes. Called during * Paginator instantiation. * @method init * @param p {Paginator} Paginator instance to decorate * @static */ Paginator.ui.PageLinks.init = function (p) { /** * CSS class assigned to each page link/span. * @attribute pageLinkClass * @default 'yui-pg-page' */ p.setAttributeConfig('pageLinkClass', { value : 'yui-pg-page', validator : l.isString }); /** * CSS class assigned to the current page span. * @attribute currentPageClass * @default 'yui-pg-current-page' */ p.setAttributeConfig('currentPageClass', { value : 'yui-pg-current-page', validator : l.isString }); /** * CSS class assigned to the span containing the page links. * @attribute pageLinksContainerClass * @default 'yui-pg-pages' */ p.setAttributeConfig('pageLinksContainerClass', { value : 'yui-pg-pages', validator : l.isString }); /** * Maximum number of page links to display at one time. * @attribute pageLinks * @default 10 */ p.setAttributeConfig('pageLinks', { value : 10, validator : Paginator.isNumeric }); /** * Function used generate the innerHTML for each page link/span. The * function receives as parameters the page number and a reference to the * paginator object. * @attribute pageLabelBuilder * @default function (page, paginator) { return page; } */ p.setAttributeConfig('pageLabelBuilder', { value : function (page, paginator) { return page; }, validator : l.isFunction }); }; /** * Calculates start and end page numbers given a current page, attempting * to keep the current page in the middle * @static * @method calculateRange * @param {int} currentPage The current page * @param {int} totalPages (optional) Maximum number of pages * @param {int} numPages (optional) Preferred number of pages in range * @return {Array} [start_page_number, end_page_number] */ Paginator.ui.PageLinks.calculateRange = function (currentPage,totalPages,numPages) { var UNLIMITED = Paginator.VALUE_UNLIMITED, start, end, delta; // Either has no pages, or unlimited pages. Show none. if (!currentPage || numPages === 0 || totalPages === 0 || (totalPages === UNLIMITED && numPages === UNLIMITED)) { return [0,-1]; } // Limit requested pageLinks if there are fewer totalPages if (totalPages !== UNLIMITED) { numPages = numPages === UNLIMITED ? totalPages : Math.min(numPages,totalPages); } // Determine start and end, trying to keep current in the middle start = Math.max(1,Math.ceil(currentPage - (numPages/2))); if (totalPages === UNLIMITED) { end = start + numPages - 1; } else { end = Math.min(totalPages, start + numPages - 1); } // Adjust the start index when approaching the last page delta = numPages - (end - start + 1); start = Math.max(1, start - delta); return [start,end]; }; Paginator.ui.PageLinks.prototype = { /** * Current page * @property current * @type number * @private */ current : 0, /** * Span node containing the page links * @property container * @type HTMLElement * @private */ container : null, /** * Generate the nodes and return the container node containing page links * appropriate to the current pagination state. * @method render * @param id_base {string} used to create unique ids for generated nodes * @return {HTMLElement} */ render : function (id_base) { var p = this.paginator; // Set up container this.container = document.createElement('span'); this.container.id = id_base + '-pages'; this.container.className = p.get('pageLinksContainerClass'); YAHOO.util.Event.on(this.container,'click',this.onClick,this,true); // Call update, flagging a need to rebuild this.update({newValue : null, rebuild : true}); return this.container; }, /** * Update the links if appropriate * @method update * @param e {CustomEvent} The calling change event */ update : function (e) { if (e && e.prevValue === e.newValue) { return; } var p = this.paginator, currentPage = p.getCurrentPage(); // Replace content if there's been a change if (this.current !== currentPage || !currentPage || e.rebuild) { var labelBuilder = p.get('pageLabelBuilder'), range = Paginator.ui.PageLinks.calculateRange( currentPage, p.getTotalPages(), p.get('pageLinks')), start = range[0], end = range[1], content = '', linkTemplate,i; linkTemplate = '' + labelBuilder(i,p) + ''; } else { content += linkTemplate + i + '">' + labelBuilder(i,p) + ''; } } this.container.innerHTML = content; } }, /** * Force a rebuild of the page links. * @method rebuild * @param e {CustomEvent} The calling change event */ rebuild : function (e) { e.rebuild = true; this.update(e); }, /** * Removes the page links container node and clears event listeners * @method destroy * @private */ destroy : function () { YAHOO.util.Event.purgeElement(this.container,true); this.container.parentNode.removeChild(this.container); this.container = null; }, /** * Listener for the container's onclick event. Looks for qualifying link * clicks, and pulls the page number from the link's page attribute. * Sends link's page attribute to the Paginator's setPage method. * @method onClick * @param e {DOMEvent} The click event */ onClick : function (e) { var t = YAHOO.util.Event.getTarget(e); if (t && YAHOO.util.Dom.hasClass(t, this.paginator.get('pageLinkClass'))) { YAHOO.util.Event.stopEvent(e); this.paginator.setPage(parseInt(t.getAttribute('page'),10)); } } }; })(); (function () { var Paginator = YAHOO.widget.Paginator, l = YAHOO.lang; /** * ui Component to generate the link to jump to the first page. * * @namespace YAHOO.widget.Paginator.ui * @class FirstPageLink * @for YAHOO.widget.Paginator * * @constructor * @param p {Pagintor} Paginator instance to attach to */ Paginator.ui.FirstPageLink = function (p) { this.paginator = p; p.subscribe('recordOffsetChange',this.update,this,true); p.subscribe('rowsPerPageChange',this.update,this,true); p.subscribe('totalRecordsChange',this.update,this,true); p.subscribe('destroy',this.destroy,this,true); // TODO: make this work p.subscribe('firstPageLinkLabelChange',this.update,this,true); p.subscribe('firstPageLinkClassChange',this.update,this,true); }; /** * Decorates Paginator instances with new attributes. Called during * Paginator instantiation. * @method init * @param p {Paginator} Paginator instance to decorate * @static */ Paginator.ui.FirstPageLink.init = function (p) { /** * Used as innerHTML for the first page link/span. * @attribute firstPageLinkLabel * @default '<< first' */ p.setAttributeConfig('firstPageLinkLabel', { value : '<< first', validator : l.isString }); /** * CSS class assigned to the link/span * @attribute firstPageLinkClass * @default 'yui-pg-first' */ p.setAttributeConfig('firstPageLinkClass', { value : 'yui-pg-first', validator : l.isString }); }; // Instance members and methods Paginator.ui.FirstPageLink.prototype = { /** * The currently placed HTMLElement node * @property current * @type HTMLElement * @private */ current : null, /** * Link node * @property link * @type HTMLElement * @private */ link : null, /** * Span node (inactive link) * @property span * @type HTMLElement * @private */ span : null, /** * Generate the nodes and return the appropriate node given the current * pagination state. * @method render * @param id_base {string} used to create unique ids for generated nodes * @return {HTMLElement} */ render : function (id_base) { var p = this.paginator, c = p.get('firstPageLinkClass'), label = p.get('firstPageLinkLabel'); this.link = document.createElement('a'); this.span = document.createElement('span'); this.link.id = id_base + '-first-link'; this.link.href = '#'; this.link.className = c; this.link.innerHTML = label; YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); this.span.id = id_base + '-first-span'; this.span.className = c; this.span.innerHTML = label; this.current = p.getCurrentPage() > 1 ? this.link : this.span; return this.current; }, /** * Swap the link and span nodes if appropriate. * @method update * @param e {CustomEvent} The calling change event */ update : function (e) { if (e && e.prevValue === e.newValue) { return; } var par = this.current ? this.current.parentNode : null; if (this.paginator.getCurrentPage() > 1) { if (par && this.current === this.span) { par.replaceChild(this.link,this.current); this.current = this.link; } } else { if (par && this.current === this.link) { par.replaceChild(this.span,this.current); this.current = this.span; } } }, /** * Removes the link/span node and clears event listeners * removal. * @method destroy * @private */ destroy : function () { YAHOO.util.Event.purgeElement(this.link); this.current.parentNode.removeChild(this.current); this.link = this.span = null; }, /** * Listener for the link's onclick event. Pass new value to setPage method. * @method onClick * @param e {DOMEvent} The click event */ onClick : function (e) { YAHOO.util.Event.stopEvent(e); this.paginator.setPage(1); } }; })(); (function () { var Paginator = YAHOO.widget.Paginator, l = YAHOO.lang; /** * ui Component to generate the link to jump to the last page. * * @namespace YAHOO.widget.Paginator.ui * @class LastPageLink * @for YAHOO.widget.Paginator * * @constructor * @param p {Pagintor} Paginator instance to attach to */ Paginator.ui.LastPageLink = function (p) { this.paginator = p; p.subscribe('recordOffsetChange',this.update,this,true); p.subscribe('rowsPerPageChange',this.update,this,true); p.subscribe('totalRecordsChange',this.update,this,true); p.subscribe('destroy',this.destroy,this,true); // TODO: make this work p.subscribe('lastPageLinkLabelChange',this.update,this,true); p.subscribe('lastPageLinkClassChange', this.update,this,true); }; /** * Decorates Paginator instances with new attributes. Called during * Paginator instantiation. * @method init * @param paginator {Paginator} Paginator instance to decorate * @static */ Paginator.ui.LastPageLink.init = function (p) { /** * Used as innerHTML for the last page link/span. * @attribute lastPageLinkLabel * @default 'last >>' */ p.setAttributeConfig('lastPageLinkLabel', { value : 'last >>', validator : l.isString }); /** * CSS class assigned to the link/span * @attribute lastPageLinkClass * @default 'yui-pg-last' */ p.setAttributeConfig('lastPageLinkClass', { value : 'yui-pg-last', validator : l.isString }); }; Paginator.ui.LastPageLink.prototype = { /** * Currently placed HTMLElement node * @property current * @type HTMLElement * @private */ current : null, /** * Link HTMLElement node * @property link * @type HTMLElement * @private */ link : null, /** * Span node (inactive link) * @property span * @type HTMLElement * @private */ span : null, /** * Empty place holder node for when the last page link is inappropriate to * display in any form (unlimited paging). * @property na * @type HTMLElement * @private */ na : null, /** * Generate the nodes and return the appropriate node given the current * pagination state. * @method render * @param id_base {string} used to create unique ids for generated nodes * @return {HTMLElement} */ render : function (id_base) { var p = this.paginator, c = p.get('lastPageLinkClass'), label = p.get('lastPageLinkLabel'), last = p.getTotalPages(); this.link = document.createElement('a'); this.span = document.createElement('span'); this.na = this.span.cloneNode(false); this.link.id = id_base + '-last-link'; this.link.href = '#'; this.link.className = c; this.link.innerHTML = label; YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); this.span.id = id_base + '-last-span'; this.span.className = c; this.span.innerHTML = label; this.na.id = id_base + '-last-na'; switch (last) { case Paginator.VALUE_UNLIMITED : this.current = this.na; break; case p.getCurrentPage() : this.current = this.span; break; default : this.current = this.link; } return this.current; }, /** * Swap the link, span, and na nodes if appropriate. * @method update * @param e {CustomEvent} The calling change event (ignored) */ update : function (e) { if (e && e.prevValue === e.newValue) { return; } var par = this.current ? this.current.parentNode : null, after = this.link; if (par) { switch (this.paginator.getTotalPages()) { case Paginator.VALUE_UNLIMITED : after = this.na; break; case this.paginator.getCurrentPage() : after = this.span; break; } if (this.current !== after) { par.replaceChild(after,this.current); this.current = after; } } }, /** * Removes the link/span node and clears event listeners * @method destroy * @private */ destroy : function () { YAHOO.util.Event.purgeElement(this.link); this.current.parentNode.removeChild(this.current); this.link = this.span = null; }, /** * Listener for the link's onclick event. Passes to setPage method. * @method onClick * @param e {DOMEvent} The click event */ onClick : function (e) { YAHOO.util.Event.stopEvent(e); this.paginator.setPage(this.paginator.getTotalPages()); } }; })(); (function () { var Paginator = YAHOO.widget.Paginator, l = YAHOO.lang; /** * ui Component to generate the link to jump to the next page. * * @namespace YAHOO.widget.Paginator.ui * @class NextPageLink * @for YAHOO.widget.Paginator * * @constructor * @param p {Pagintor} Paginator instance to attach to */ Paginator.ui.NextPageLink = function (p) { this.paginator = p; p.subscribe('recordOffsetChange', this.update,this,true); p.subscribe('rowsPerPageChange', this.update,this,true); p.subscribe('totalRecordsChange', this.update,this,true); p.subscribe('destroy',this.destroy,this,true); // TODO: make this work p.subscribe('nextPageLinkLabelChange', this.update,this,true); p.subscribe('nextPageLinkClassChange', this.update,this,true); }; /** * Decorates Paginator instances with new attributes. Called during * Paginator instantiation. * @method init * @param p {Paginator} Paginator instance to decorate * @static */ Paginator.ui.NextPageLink.init = function (p) { /** * Used as innerHTML for the next page link/span. * @attribute nextPageLinkLabel * @default 'next >' */ p.setAttributeConfig('nextPageLinkLabel', { value : 'next >', validator : l.isString }); /** * CSS class assigned to the link/span * @attribute nextPageLinkClass * @default 'yui-pg-next' */ p.setAttributeConfig('nextPageLinkClass', { value : 'yui-pg-next', validator : l.isString }); }; Paginator.ui.NextPageLink.prototype = { /** * Currently placed HTMLElement node * @property current * @type HTMLElement * @private */ current : null, /** * Link node * @property link * @type HTMLElement * @private */ link : null, /** * Span node (inactive link) * @property span * @type HTMLElement * @private */ span : null, /** * Generate the nodes and return the appropriate node given the current * pagination state. * @method render * @param id_base {string} used to create unique ids for generated nodes * @return {HTMLElement} */ render : function (id_base) { var p = this.paginator, c = p.get('nextPageLinkClass'), label = p.get('nextPageLinkLabel'), last = p.getTotalPages(); this.link = document.createElement('a'); this.span = document.createElement('span'); this.link.id = id_base + '-next-link'; this.link.href = '#'; this.link.className = c; this.link.innerHTML = label; YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); this.span.id = id_base + '-next-span'; this.span.className = c; this.span.innerHTML = label; this.current = p.getCurrentPage() === last ? this.span : this.link; return this.current; }, /** * Swap the link and span nodes if appropriate. * @method update * @param e {CustomEvent} The calling change event */ update : function (e) { if (e && e.prevValue === e.newValue) { return; } var last = this.paginator.getTotalPages(), par = this.current ? this.current.parentNode : null; if (this.paginator.getCurrentPage() !== last) { if (par && this.current === this.span) { par.replaceChild(this.link,this.current); this.current = this.link; } } else if (this.current === this.link) { if (par) { par.replaceChild(this.span,this.current); this.current = this.span; } } }, /** * Removes the link/span node and clears event listeners * @method destroy * @private */ destroy : function () { YAHOO.util.Event.purgeElement(this.link); this.current.parentNode.removeChild(this.current); this.link = this.span = null; }, /** * Listener for the link's onclick event. Passes to setPage method. * @method onClick * @param e {DOMEvent} The click event */ onClick : function (e) { YAHOO.util.Event.stopEvent(e); this.paginator.setPage(this.paginator.getNextPage()); } }; })(); (function () { var Paginator = YAHOO.widget.Paginator, l = YAHOO.lang; /** * ui Component to generate the link to jump to the previous page. * * @namespace YAHOO.widget.Paginator.ui * @class PreviousPageLink * @for YAHOO.widget.Paginator * * @constructor * @param p {Pagintor} Paginator instance to attach to */ Paginator.ui.PreviousPageLink = function (p) { this.paginator = p; p.subscribe('recordOffsetChange',this.update,this,true); p.subscribe('rowsPerPageChange',this.update,this,true); p.subscribe('totalRecordsChange',this.update,this,true); p.subscribe('destroy',this.destroy,this,true); // TODO: make this work p.subscribe('previousPageLinkLabelChange',this.update,this,true); p.subscribe('previousPageLinkClassChange',this.update,this,true); }; /** * Decorates Paginator instances with new attributes. Called during * Paginator instantiation. * @method init * @param p {Paginator} Paginator instance to decorate * @static */ Paginator.ui.PreviousPageLink.init = function (p) { /** * Used as innerHTML for the previous page link/span. * @attribute previousPageLinkLabel * @default '< prev' */ p.setAttributeConfig('previousPageLinkLabel', { value : '< prev', validator : l.isString }); /** * CSS class assigned to the link/span * @attribute previousPageLinkClass * @default 'yui-pg-previous' */ p.setAttributeConfig('previousPageLinkClass', { value : 'yui-pg-previous', validator : l.isString }); }; Paginator.ui.PreviousPageLink.prototype = { /** * Currently placed HTMLElement node * @property current * @type HTMLElement * @private */ current : null, /** * Link node * @property link * @type HTMLElement * @private */ link : null, /** * Span node (inactive link) * @property span * @type HTMLElement * @private */ span : null, /** * Generate the nodes and return the appropriate node given the current * pagination state. * @method render * @param id_base {string} used to create unique ids for generated nodes * @return {HTMLElement} */ render : function (id_base) { var p = this.paginator, c = p.get('previousPageLinkClass'), label = p.get('previousPageLinkLabel'); this.link = document.createElement('a'); this.span = document.createElement('span'); this.link.id = id_base + '-prev-link'; this.link.href = '#'; this.link.className = c; this.link.innerHTML = label; YAHOO.util.Event.on(this.link,'click',this.onClick,this,true); this.span.id = id_base + '-prev-span'; this.span.className = c; this.span.innerHTML = label; this.current = p.getCurrentPage() > 1 ? this.link : this.span; return this.current; }, /** * Swap the link and span nodes if appropriate. * @method update * @param e {CustomEvent} The calling change event */ update : function (e) { if (e && e.prevValue === e.newValue) { return; } var par = this.current ? this.current.parentNode : null; if (this.paginator.getCurrentPage() > 1) { if (par && this.current === this.span) { par.replaceChild(this.link,this.current); this.current = this.link; } } else { if (par && this.current === this.link) { par.replaceChild(this.span,this.current); this.current = this.span; } } }, /** * Removes the link/span node and clears event listeners * @method destroy * @private */ destroy : function () { YAHOO.util.Event.purgeElement(this.link); this.current.parentNode.removeChild(this.current); this.link = this.span = null; }, /** * Listener for the link's onclick event. Passes to setPage method. * @method onClick * @param e {DOMEvent} The click event */ onClick : function (e) { YAHOO.util.Event.stopEvent(e); this.paginator.setPage(this.paginator.getPreviousPage()); } }; })(); (function () { var Paginator = YAHOO.widget.Paginator, l = YAHOO.lang; /** * ui Component to generate the rows-per-page dropdown * * @namespace YAHOO.widget.Paginator.ui * @class RowsPerPageDropdown * @for YAHOO.widget.Paginator * * @constructor * @param p {Pagintor} Paginator instance to attach to */ Paginator.ui.RowsPerPageDropdown = function (p) { this.paginator = p; p.subscribe('rowsPerPageChange',this.update,this,true); p.subscribe('rowsPerPageOptionsChange',this.rebuild,this,true); p.subscribe('totalRecordsChange',this._handleTotalRecordsChange,this,true); p.subscribe('destroy',this.destroy,this,true); // TODO: make this work p.subscribe('rowsPerPageDropdownClassChange',this.rebuild,this,true); }; /** * Decorates Paginator instances with new attributes. Called during * Paginator instantiation. * @method init * @param p {Paginator} Paginator instance to decorate * @static */ Paginator.ui.RowsPerPageDropdown.init = function (p) { /** * Array of available rows-per-page sizes. Converted into select options. * Array values may be positive integers or object literals in the form
    * { value : NUMBER, text : STRING } * @attribute rowsPerPageOptions * @default [] */ p.setAttributeConfig('rowsPerPageOptions', { value : [], validator : l.isArray }); /** * CSS class assigned to the select node * @attribute rowsPerPageDropdownClass * @default 'yui-pg-rpp-options' */ p.setAttributeConfig('rowsPerPageDropdownClass', { value : 'yui-pg-rpp-options', validator : l.isString }); }; Paginator.ui.RowsPerPageDropdown.prototype = { /** * select node * @property select * @type HTMLElement * @private */ select : null, /** * option node for the optional All value * * @property all * @type HTMLElement * @protected */ all : null, /** * Generate the select and option nodes and returns the select node. * @method render * @param id_base {string} used to create unique ids for generated nodes * @return {HTMLElement} */ render : function (id_base) { this.select = document.createElement('select'); this.select.id = id_base + '-rpp'; this.select.className = this.paginator.get('rowsPerPageDropdownClass'); this.select.title = 'Rows per page'; YAHOO.util.Event.on(this.select,'change',this.onChange,this,true); this.rebuild(); return this.select; }, /** * (Re)generate the select options. * @method rebuild */ rebuild : function (e) { var p = this.paginator, sel = this.select, options = p.get('rowsPerPageOptions'), opt,cfg,val,i,len; this.all = null; for (i = 0, len = options.length; i < len; ++i) { cfg = options[i]; opt = sel.options[i] || sel.appendChild(document.createElement('option')); val = l.isValue(cfg.value) ? cfg.value : cfg; opt.innerHTML = l.isValue(cfg.text) ? cfg.text : cfg; if (l.isString(val) && val.toLowerCase() === 'all') { this.all = opt; opt.value = p.get('totalRecords'); } else{ opt.value = val; } } while (sel.options.length > options.length) { sel.removeChild(sel.firstChild); } this.update(); }, /** * Select the appropriate option if changed. * @method update * @param e {CustomEvent} The calling change event */ update : function (e) { if (e && e.prevValue === e.newValue) { return; } var rpp = this.paginator.get('rowsPerPage')+'', options = this.select.options, i,len; for (i = 0, len = options.length; i < len; ++i) { if (options[i].value === rpp) { options[i].selected = true; break; } } }, /** * Listener for the select's onchange event. Sent to setRowsPerPage method. * @method onChange * @param e {DOMEvent} The change event */ onChange : function (e) { this.paginator.setRowsPerPage( parseInt(this.select.options[this.select.selectedIndex].value,10)); }, /** * Updates the all option value (and Paginator's rowsPerPage attribute if * necessary) in response to a change in the Paginator's totalRecords. * * @method _handleTotalRecordsChange * @param e {Event} attribute change event * @protected */ _handleTotalRecordsChange : function (e) { if (!this.all || (e && e.prevValue === e.newValue)) { return; } this.all.value = e.newValue; if (this.all.selected) { this.paginator.set('rowsPerPage',e.newValue); } }, /** * Removes the select node and clears event listeners * @method destroy * @private */ destroy : function () { YAHOO.util.Event.purgeElement(this.select); this.select.parentNode.removeChild(this.select); this.select = null; } }; })(); YAHOO.register("paginator", YAHOO.widget.Paginator, {version: "2.8.0r4", build: "2446"});