diff -r 000000000000 -r 40c8f766c9b8 src/cm/media/js/lib/yui/yui3.0.0/build/dom/dom.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui3.0.0/build/dom/dom.js Mon Nov 23 15:14:29 2009 +0100 @@ -0,0 +1,2466 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 3.0.0 +build: 1549 +*/ +YUI.add('dom-base', function(Y) { + +(function(Y) { +/** + * The DOM utility provides a cross-browser abtraction layer + * normalizing DOM tasks, and adds extra helper functionality + * for other common tasks. + * @module dom + * @submodule dom-base + * + */ + +/** + * Provides DOM helper methods. + * @class DOM + * + */ +var NODE_TYPE = 'nodeType', + OWNER_DOCUMENT = 'ownerDocument', + DEFAULT_VIEW = 'defaultView', + PARENT_WINDOW = 'parentWindow', + TAG_NAME = 'tagName', + PARENT_NODE = 'parentNode', + FIRST_CHILD = 'firstChild', + PREVIOUS_SIBLING = 'previousSibling', + NEXT_SIBLING = 'nextSibling', + CONTAINS = 'contains', + COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', + + documentElement = document.documentElement, + + re_tag = /<([a-z]+)/i; + +Y.DOM = { + /** + * Returns the HTMLElement with the given ID (Wrapper for document.getElementById). + * @method byId + * @param {String} id the id attribute + * @param {Object} doc optional The document to search. Defaults to current document + * @return {HTMLElement | null} The HTMLElement with the id, or null if none found. + */ + byId: function(id, doc) { + doc = doc || Y.config.doc; + // TODO: IE Name + return doc.getElementById(id); + }, + + // @deprecated + children: function(node, tag) { + var ret = []; + if (node) { + tag = tag || '*'; + ret = Y.Selector.query('> ' + tag, node); + } + return ret; + }, + + // @deprecated + firstByTag: function(tag, root) { + var ret; + root = root || Y.config.doc; + + if (tag && root.getElementsByTagName) { + ret = root.getElementsByTagName(tag)[0]; + } + + return ret || null; + }, + + /** + * Returns the text content of the HTMLElement. + * @method getText + * @param {HTMLElement} element The html element. + * @return {String} The text content of the element (includes text of any descending elements). + */ + getText: (documentElement.textContent !== undefined) ? + function(element) { + var ret = ''; + if (element) { + ret = element.textContent; + } + return ret || ''; + } : function(element) { + var ret = ''; + if (element) { + ret = element.innerText; + } + return ret || ''; + }, + + /** + * Sets the text content of the HTMLElement. + * @method setText + * @param {HTMLElement} element The html element. + * @param {String} content The content to add. + */ + setText: (documentElement.textContent !== undefined) ? + function(element, content) { + if (element) { + element.textContent = content; + } + } : function(element, content) { + if (element) { + element.innerText = content; + } + }, + + /* + * Finds the previous sibling of the element. + * @method previous + * @deprecated Use elementByAxis + * @param {HTMLElement} element The html element. + * @param {Function} fn optional An optional boolean test to apply. + * The optional function is passed the current DOM node being tested as its only argument. + * If no function is given, the first sibling is returned. + * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes. + * @return {HTMLElement | null} The matching DOM node or null if none found. + */ + previous: function(element, fn, all) { + return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all); + }, + + /* + * Finds the next sibling of the element. + * @method next + * @deprecated Use elementByAxis + * @param {HTMLElement} element The html element. + * @param {Function} fn optional An optional boolean test to apply. + * The optional function is passed the current DOM node being tested as its only argument. + * If no function is given, the first sibling is returned. + * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes. + * @return {HTMLElement | null} The matching DOM node or null if none found. + */ + next: function(element, fn, all) { + return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all); + }, + + /* + * Finds the ancestor of the element. + * @method ancestor + * @deprecated Use elementByAxis + * @param {HTMLElement} element The html element. + * @param {Function} fn optional An optional boolean test to apply. + * The optional function is passed the current DOM node being tested as its only argument. + * If no function is given, the parentNode is returned. + * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes. + * @return {HTMLElement | null} The matching DOM node or null if none found. + */ + // TODO: optional stopAt node? + ancestor: function(element, fn, all) { + return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all); + }, + + /** + * Searches the element by the given axis for the first matching element. + * @method elementByAxis + * @param {HTMLElement} element The html element. + * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling). + * @param {Function} fn optional An optional boolean test to apply. + * @param {Boolean} all optional Whether all node types should be returned, or just element nodes. + * The optional function is passed the current HTMLElement being tested as its only argument. + * If no function is given, the first element is returned. + * @return {HTMLElement | null} The matching element or null if none found. + */ + elementByAxis: function(element, axis, fn, all) { + while (element && (element = element[axis])) { // NOTE: assignment + if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) { + return element; + } + } + return null; + }, + + /** + * Determines whether or not one HTMLElement is or contains another HTMLElement. + * @method contains + * @param {HTMLElement} element The containing html element. + * @param {HTMLElement} needle The html element that may be contained. + * @return {Boolean} Whether or not the element is or contains the needle. + */ + contains: function(element, needle) { + var ret = false; + + if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) { + ret = false; + } else if (element[CONTAINS]) { + if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE + ret = element[CONTAINS](needle); + } else { + ret = Y.DOM._bruteContains(element, needle); + } + } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko + if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { + ret = true; + } + } + + return ret; + }, + + /** + * Determines whether or not the HTMLElement is part of the document. + * @method inDoc + * @param {HTMLElement} element The containing html element. + * @param {HTMLElement} doc optional The document to check. + * @return {Boolean} Whether or not the element is attached to the document. + */ + inDoc: function(element, doc) { + doc = doc || element[OWNER_DOCUMENT]; + var id = element.id; + if (!id) { // TODO: remove when done? + id = element.id = Y.guid(); + } + + return !! (doc.getElementById(id)); + }, + + /** + * Creates a new dom node using the provided markup string. + * @method create + * @param {String} html The markup used to create the element + * @param {HTMLDocument} doc An optional document context + */ + create: function(html, doc) { + if (typeof html === 'string') { + html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML + } + + if (!doc && Y.DOM._cloneCache[html]) { + return Y.DOM._cloneCache[html].cloneNode(true); // NOTE: return + } + + doc = doc || Y.config.doc; + var m = re_tag.exec(html), + create = Y.DOM._create, + custom = Y.DOM.creators, + ret = null, + tag, nodes; + + if (m && custom[m[1]]) { + if (typeof custom[m[1]] === 'function') { + create = custom[m[1]]; + } else { + tag = custom[m[1]]; + } + } + + nodes = create(html, doc, tag).childNodes; + + if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment" + ret = nodes[0].parentNode.removeChild(nodes[0]); + } else { // return multiple nodes as a fragment + ret = Y.DOM._nl2frag(nodes, doc); + } + + if (ret) { + Y.DOM._cloneCache[html] = ret.cloneNode(true); + } + return ret; + }, + + _nl2frag: function(nodes, doc) { + var ret = null, + i, len; + + if (nodes && (nodes.push || nodes.item) && nodes[0]) { + doc = doc || nodes[0].ownerDocument; + ret = doc.createDocumentFragment(); + + if (nodes.item) { // convert live list to static array + nodes = Y.Array(nodes, 0, true); + } + + for (i = 0, len = nodes.length; i < len; i++) { + ret.appendChild(nodes[i]); + } + } // else inline with log for minification + return ret; + }, + + + CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8 + 'for': 'htmlFor', + 'class': 'className' + } : { // w3c + 'htmlFor': 'for', + 'className': 'class' + }, + + /** + * Provides a normalized attribute interface. + * @method setAttibute + * @param {String | HTMLElement} el The target element for the attribute. + * @param {String} attr The attribute to set. + * @param {String} val The value of the attribute. + */ + setAttribute: function(el, attr, val, ieAttr) { + if (el && el.setAttribute) { + attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr; + el.setAttribute(attr, val, ieAttr); + } + }, + + + /** + * Provides a normalized attribute interface. + * @method getAttibute + * @param {String | HTMLElement} el The target element for the attribute. + * @param {String} attr The attribute to get. + * @return {String} The current value of the attribute. + */ + getAttribute: function(el, attr, ieAttr) { + ieAttr = (ieAttr !== undefined) ? ieAttr : 2; + var ret = ''; + if (el && el.getAttribute) { + attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr; + ret = el.getAttribute(attr, ieAttr); + + if (ret === null) { + ret = ''; // per DOM spec + } + } + return ret; + }, + + isWindow: function(obj) { + return obj.alert && obj.document; + }, + + _fragClones: { + div: document.createElement('div') + }, + + _create: function(html, doc, tag) { + tag = tag || 'div'; + + var frag = Y.DOM._fragClones[tag]; + if (frag) { + frag = frag.cloneNode(false); + } else { + frag = Y.DOM._fragClones[tag] = doc.createElement(tag); + } + frag.innerHTML = html; + return frag; + }, + + _removeChildNodes: function(node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } + }, + + _cloneCache: {}, + + /** + * Inserts content in a node at the given location + * @method addHTML + * @param {HTMLElement} node The node to insert into + * @param {String} content The content to be inserted + * @param {String} where Where to insert the content; default is after lastChild + */ + addHTML: function(node, content, where) { + if (typeof content === 'string') { + content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML + } + + var newNode = Y.DOM._cloneCache[content], + nodeParent = node.parentNode; + + if (newNode) { + newNode = newNode.cloneNode(true); + } else { + if (content.nodeType) { // domNode + newNode = content; + } else { // create from string and cache + newNode = Y.DOM.create(content); + } + } + + if (where) { + if (where.nodeType) { // insert regardless of relationship to node + // TODO: check if node.contains(where)? + where.parentNode.insertBefore(newNode, where); + } else { + switch (where) { + case 'replace': + while (node.firstChild) { + node.removeChild(node.firstChild); + } + node.appendChild(newNode); + break; + case 'before': + nodeParent.insertBefore(newNode, node); + break; + case 'after': + if (node.nextSibling) { // IE errors if refNode is null + nodeParent.insertBefore(newNode, node.nextSibling); + } else { + nodeParent.appendChild(newNode); + } + break; + default: + node.appendChild(newNode); + } + } + } else { + node.appendChild(newNode); + } + + return newNode; + }, + + VALUE_SETTERS: {}, + + VALUE_GETTERS: {}, + + getValue: function(node) { + var ret = '', // TODO: return null? + getter; + + if (node && node[TAG_NAME]) { + getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()]; + + if (getter) { + ret = getter(node); + } else { + ret = node.value; + } + } + + return (typeof ret === 'string') ? ret : ''; + }, + + setValue: function(node, val) { + var setter; + + if (node && node[TAG_NAME]) { + setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()]; + + if (setter) { + setter(node, val); + } else { + node.value = val; + } + } + }, + + /** + * Brute force version of contains. + * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc). + * @method _bruteContains + * @private + * @param {HTMLElement} element The containing html element. + * @param {HTMLElement} needle The html element that may be contained. + * @return {Boolean} Whether or not the element is or contains the needle. + */ + _bruteContains: function(element, needle) { + while (needle) { + if (element === needle) { + return true; + } + needle = needle.parentNode; + } + return false; + }, + +// TODO: move to Lang? + /** + * Memoizes dynamic regular expressions to boost runtime performance. + * @method _getRegExp + * @private + * @param {String} str The string to convert to a regular expression. + * @param {String} flags optional An optinal string of flags. + * @return {RegExp} An instance of RegExp + */ + _getRegExp: function(str, flags) { + flags = flags || ''; + Y.DOM._regexCache = Y.DOM._regexCache || {}; + if (!Y.DOM._regexCache[str + flags]) { + Y.DOM._regexCache[str + flags] = new RegExp(str, flags); + } + return Y.DOM._regexCache[str + flags]; + }, + +// TODO: make getDoc/Win true privates? + /** + * returns the appropriate document. + * @method _getDoc + * @private + * @param {HTMLElement} element optional Target element. + * @return {Object} The document for the given element or the default document. + */ + _getDoc: function(element) { + element = element || {}; + + return (element[NODE_TYPE] === 9) ? element : // element === document + element[OWNER_DOCUMENT] || // element === DOM node + element.document || // element === window + Y.config.doc; // default + }, + + /** + * returns the appropriate window. + * @method _getWin + * @private + * @param {HTMLElement} element optional Target element. + * @return {Object} The window for the given element or the default window. + */ + _getWin: function(element) { + var doc = Y.DOM._getDoc(element); + return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win; + }, + + _batch: function(nodes, fn, arg1, arg2, arg3, etc) { + fn = (typeof name === 'string') ? Y.DOM[fn] : fn; + var result, + ret = []; + + if (fn && nodes) { + Y.each(nodes, function(node) { + if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) { + ret[ret.length] = result; + } + }); + } + + return ret.length ? ret : nodes; + }, + + _testElement: function(element, tag, fn) { + tag = (tag && tag !== '*') ? tag.toUpperCase() : null; + return (element && element[TAG_NAME] && + (!tag || element[TAG_NAME].toUpperCase() === tag) && + (!fn || fn(element))); + }, + + creators: {}, + + _IESimpleCreate: function(html, doc) { + doc = doc || Y.config.doc; + return doc.createElement(html); + } +}; + + +(function(Y) { + var creators = Y.DOM.creators, + create = Y.DOM.create, + re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s* 1 && tb && !re_tbody.test(html)) { + tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody + } + return frag; + }, + + script: function(html, doc) { + var frag = doc.createElement('div'); + + frag.innerHTML = '-' + html; + frag.removeChild(frag[FIRST_CHILD]); + return frag; + } + + }, true); + + Y.mix(Y.DOM.VALUE_GETTERS, { + button: function(node) { + return (node.attributes && node.attributes.value) ? node.attributes.value.value : ''; + } + }); + + Y.mix(Y.DOM.VALUE_SETTERS, { + // IE: node.value changes the button text, which should be handled via innerHTML + button: function(node, val) { + var attr = node.attributes.value; + if (!attr) { + attr = node[OWNER_DOCUMENT].createAttribute('value'); + node.setAttributeNode(attr); + } + + attr.value = val; + } + }); + } + + if (Y.UA.gecko || Y.UA.ie) { + Y.mix(creators, { + option: function(html, doc) { + return create('', doc); + }, + + tr: function(html, doc) { + return create('' + html + '', doc); + }, + + td: function(html, doc) { + return create('' + html + '', doc); + }, + + tbody: function(html, doc) { + return create(TABLE_OPEN + html + TABLE_CLOSE, doc); + } + }); + + Y.mix(creators, { + legend: 'fieldset', + th: creators.td, + thead: creators.tbody, + tfoot: creators.tbody, + caption: creators.tbody, + colgroup: creators.tbody, + col: creators.tbody, + optgroup: creators.option + }); + } + + Y.mix(Y.DOM.VALUE_GETTERS, { + option: function(node) { + var attrs = node.attributes; + return (attrs.value && attrs.value.specified) ? node.value : node.text; + }, + + select: function(node) { + var val = node.value, + options = node.options; + + if (options && val === '') { + if (node.multiple) { + } else { + val = Y.DOM.getValue(options[node.selectedIndex], 'value'); + } + } + + return val; + } + }); +})(Y); + +})(Y); +var addClass, hasClass, removeClass; + +Y.mix(Y.DOM, { + /** + * Determines whether a DOM element has the given className. + * @method hasClass + * @param {HTMLElement} element The DOM element. + * @param {String} className the class name to search for + * @return {Boolean} Whether or not the element has the given class. + */ + hasClass: function(node, className) { + var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)'); + return re.test(node.className); + }, + + /** + * Adds a class name to a given DOM element. + * @method addClass + * @param {HTMLElement} element The DOM element. + * @param {String} className the class name to add to the class attribute + */ + addClass: function(node, className) { + if (!Y.DOM.hasClass(node, className)) { // skip if already present + node.className = Y.Lang.trim([node.className, className].join(' ')); + } + }, + + /** + * Removes a class name from a given element. + * @method removeClass + * @param {HTMLElement} element The DOM element. + * @param {String} className the class name to remove from the class attribute + */ + removeClass: function(node, className) { + if (className && hasClass(node, className)) { + node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' + + className + '(?:\\s+|$)'), ' ')); + + if ( hasClass(node, className) ) { // in case of multiple adjacent + removeClass(node, className); + } + } + }, + + /** + * Replace a class with another class for a given element. + * If no oldClassName is present, the newClassName is simply added. + * @method replaceClass + * @param {HTMLElement} element The DOM element. + * @param {String} oldClassName the class name to be replaced + * @param {String} newClassName the class name that will be replacing the old class name + */ + replaceClass: function(node, oldC, newC) { + addClass(node, newC); + removeClass(node, oldC); + }, + + /** + * If the className exists on the node it is removed, if it doesn't exist it is added. + * @method toggleClass + * @param {HTMLElement} element The DOM element. + * @param {String} className the class name to be toggled + */ + toggleClass: function(node, className) { + if (hasClass(node, className)) { + removeClass(node, className); + } else { + addClass(node, className); + } + } +}); + +hasClass = Y.DOM.hasClass; +removeClass = Y.DOM.removeClass; +addClass = Y.DOM.addClass; + + + +}, '3.0.0' ,{requires:['oop']}); +YUI.add('dom-style', function(Y) { + +(function(Y) { +/** + * Add style management functionality to DOM. + * @module dom + * @submodule dom-style + * @for DOM + */ + +var DOCUMENT_ELEMENT = 'documentElement', + DEFAULT_VIEW = 'defaultView', + OWNER_DOCUMENT = 'ownerDocument', + STYLE = 'style', + FLOAT = 'float', + CSS_FLOAT = 'cssFloat', + STYLE_FLOAT = 'styleFloat', + TRANSPARENT = 'transparent', + GET_COMPUTED_STYLE = 'getComputedStyle', + + DOCUMENT = Y.config.doc, + UNDEFINED = undefined, + + re_color = /color$/i; + + +Y.mix(Y.DOM, { + CUSTOM_STYLES: { + }, + + + /** + * Sets a style property for a given element. + * @method setStyle + * @param {HTMLElement} An HTMLElement to apply the style to. + * @param {String} att The style property to set. + * @param {String|Number} val The value. + */ + setStyle: function(node, att, val, style) { + style = style || node.style; + var CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES; + + if (style) { + if (val === null) { + val = ''; // normalize for unsetting + } + if (att in CUSTOM_STYLES) { + if (CUSTOM_STYLES[att].set) { + CUSTOM_STYLES[att].set(node, val, style); + return; // NOTE: return + } else if (typeof CUSTOM_STYLES[att] === 'string') { + att = CUSTOM_STYLES[att]; + } + } + style[att] = val; + } + }, + + /** + * Returns the current style value for the given property. + * @method getStyle + * @param {HTMLElement} An HTMLElement to get the style from. + * @param {String} att The style property to get. + */ + getStyle: function(node, att) { + var style = node[STYLE], + CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES, + val = ''; + + if (style) { + if (att in CUSTOM_STYLES) { + if (CUSTOM_STYLES[att].get) { + return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return + } else if (typeof CUSTOM_STYLES[att] === 'string') { + att = CUSTOM_STYLES[att]; + } + } + val = style[att]; + if (val === '') { // TODO: is empty string sufficient? + val = Y.DOM[GET_COMPUTED_STYLE](node, att); + } + } + + return val; + }, + + /** + * Sets multiple style properties. + * @method setStyles + * @param {HTMLElement} node An HTMLElement to apply the styles to. + * @param {Object} hash An object literal of property:value pairs. + */ + setStyles: function(node, hash) { + var style = node.style; + Y.each(hash, function(v, n) { + Y.DOM.setStyle(node, n, v, style); + }, Y.DOM); + }, + + /** + * Returns the computed style for the given node. + * @method getComputedStyle + * @param {HTMLElement} An HTMLElement to get the style from. + * @param {String} att The style property to get. + * @return {String} The computed value of the style property. + */ + getComputedStyle: function(node, att) { + var val = '', + doc = node[OWNER_DOCUMENT]; + + if (node[STYLE]) { + val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att]; + } + return val; + } +}); + +// normalize reserved word float alternatives ("cssFloat" or "styleFloat") +if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) { + Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT; +} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) { + Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT; +} + +// fix opera computedStyle default color unit (convert to rgb) +if (Y.UA.opera) { + Y.DOM[GET_COMPUTED_STYLE] = function(node, att) { + var view = node[OWNER_DOCUMENT][DEFAULT_VIEW], + val = view[GET_COMPUTED_STYLE](node, '')[att]; + + if (re_color.test(att)) { + val = Y.Color.toRGB(val); + } + + return val; + }; + +} + +// safari converts transparent to rgba(), others use "transparent" +if (Y.UA.webkit) { + Y.DOM[GET_COMPUTED_STYLE] = function(node, att) { + var view = node[OWNER_DOCUMENT][DEFAULT_VIEW], + val = view[GET_COMPUTED_STYLE](node, '')[att]; + + if (val === 'rgba(0, 0, 0, 0)') { + val = TRANSPARENT; + } + + return val; + }; + +} +})(Y); +(function(Y) { +var PARSE_INT = parseInt, + RE = RegExp; + +Y.Color = { + KEYWORDS: { + black: '000', + silver: 'c0c0c0', + gray: '808080', + white: 'fff', + maroon: '800000', + red: 'f00', + purple: '800080', + fuchsia: 'f0f', + green: '008000', + lime: '0f0', + olive: '808000', + yellow: 'ff0', + navy: '000080', + blue: '00f', + teal: '008080', + aqua: '0ff' + }, + + re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i, + re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i, + re_hex3: /([0-9A-F])/gi, + + toRGB: function(val) { + if (!Y.Color.re_RGB.test(val)) { + val = Y.Color.toHex(val); + } + + if(Y.Color.re_hex.exec(val)) { + val = 'rgb(' + [ + PARSE_INT(RE.$1, 16), + PARSE_INT(RE.$2, 16), + PARSE_INT(RE.$3, 16) + ].join(', ') + ')'; + } + return val; + }, + + toHex: function(val) { + val = Y.Color.KEYWORDS[val] || val; + if (Y.Color.re_RGB.exec(val)) { + val = [ + Number(RE.$1).toString(16), + Number(RE.$2).toString(16), + Number(RE.$3).toString(16) + ]; + + for (var i = 0; i < val.length; i++) { + if (val[i].length < 2) { + val[i] = val[i].replace(Y.Color.re_hex3, '$1$1'); + } + } + + val = '#' + val.join(''); + } + + if (val.length < 6) { + val = val.replace(Y.Color.re_hex3, '$1$1'); + } + + if (val !== 'transparent' && val.indexOf('#') < 0) { + val = '#' + val; + } + + return val.toLowerCase(); + } +}; +})(Y); + +(function(Y) { +var HAS_LAYOUT = 'hasLayout', + PX = 'px', + FILTER = 'filter', + FILTERS = 'filters', + OPACITY = 'opacity', + AUTO = 'auto', + + BORDER_WIDTH = 'borderWidth', + BORDER_TOP_WIDTH = 'borderTopWidth', + BORDER_RIGHT_WIDTH = 'borderRightWidth', + BORDER_BOTTOM_WIDTH = 'borderBottomWidth', + BORDER_LEFT_WIDTH = 'borderLeftWidth', + WIDTH = 'width', + HEIGHT = 'height', + TRANSPARENT = 'transparent', + VISIBLE = 'visible', + GET_COMPUTED_STYLE = 'getComputedStyle', + UNDEFINED = undefined, + documentElement = document.documentElement, + + // TODO: unit-less lineHeight (e.g. 1.22) + re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i, + + _getStyleObj = function(node) { + return node.currentStyle || node.style; + }, + + ComputedStyle = { + CUSTOM_STYLES: {}, + + get: function(el, property) { + var value = '', + current; + + if (el) { + current = _getStyleObj(el)[property]; + + if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) { + value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el); + } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert + value = current; + } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function + value = Y.DOM.IE.COMPUTED[property](el, property); + } else if (re_unit.test(current)) { // convert to pixel + value = ComputedStyle.getPixel(el, property) + PX; + } else { + value = current; + } + } + + return value; + }, + + sizeOffsets: { + width: ['Left', 'Right'], + height: ['Top', 'Bottom'], + top: ['Top'], + bottom: ['Bottom'] + }, + + getOffset: function(el, prop) { + var current = _getStyleObj(el)[prop], // value of "width", "top", etc. + capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc. + offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc. + pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc. + sizeOffsets = ComputedStyle.sizeOffsets[prop], + value = ''; + + // IE pixelWidth incorrect for percent + // manually compute by subtracting padding and border from offset size + // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used + // reverting to auto from auto causes position stacking issues (old impl) + if (current === AUTO || current.indexOf('%') > -1) { + value = el['offset' + capped]; + + if (sizeOffsets[0]) { + value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]); + value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1); + } + + if (sizeOffsets[1]) { + value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]); + value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1); + } + + } else { // use style.pixelWidth, etc. to convert to pixels + // need to map style.width to currentStyle (no currentStyle.pixelWidth) + if (!el.style[pixel] && !el.style[prop]) { + el.style[prop] = current; + } + value = el.style[pixel]; + + } + return value + PX; + }, + + borderMap: { + thin: '2px', + medium: '4px', + thick: '6px' + }, + + getBorderWidth: function(el, property, omitUnit) { + var unit = omitUnit ? '' : PX, + current = el.currentStyle[property]; + + if (current.indexOf(PX) < 0) { // look up keywords + if (ComputedStyle.borderMap[current]) { + current = ComputedStyle.borderMap[current]; + } else { + } + } + return (omitUnit) ? parseFloat(current) : current; + }, + + getPixel: function(node, att) { + // use pixelRight to convert to px + var val = null, + style = _getStyleObj(node), + styleRight = style.right, + current = style[att]; + + node.style.right = current; + val = node.style.pixelRight; + node.style.right = styleRight; // revert + + return val; + }, + + getMargin: function(node, att) { + var val, + style = _getStyleObj(node); + + if (style[att] == AUTO) { + val = 0; + } else { + val = ComputedStyle.getPixel(node, att); + } + return val + PX; + }, + + getVisibility: function(node, att) { + var current; + while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test + node = node.parentNode; + } + return (current) ? current[att] : VISIBLE; + }, + + getColor: function(node, att) { + var current = _getStyleObj(node)[att]; + + if (!current || current === TRANSPARENT) { + Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) { + current = _getStyleObj(parent)[att]; + if (current && current !== TRANSPARENT) { + node = parent; + return true; + } + }); + } + + return Y.Color.toRGB(current); + }, + + getBorderColor: function(node, att) { + var current = _getStyleObj(node), + val = current[att] || current.color; + return Y.Color.toRGB(Y.Color.toHex(val)); + } + }, + + //fontSize: getPixelFont, + IEComputed = {}; + +// use alpha filter for IE opacity +try { + if (documentElement.style[OPACITY] === UNDEFINED && + documentElement[FILTERS]) { + Y.DOM.CUSTOM_STYLES[OPACITY] = { + get: function(node) { + var val = 100; + try { // will error if no DXImageTransform + val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY]; + + } catch(e) { + try { // make sure its in the document + val = node[FILTERS]('alpha')[OPACITY]; + } catch(err) { + } + } + return val / 100; + }, + + set: function(node, val, style) { + var current, + styleObj; + + if (val === '') { // normalize inline style behavior + styleObj = _getStyleObj(node); + current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity + val = current; + } + + if (typeof style[FILTER] == 'string') { // in case not appended + style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')'; + + if (!node.currentStyle || !node.currentStyle[HAS_LAYOUT]) { + style.zoom = 1; // needs layout + } + } + } + }; + } +} catch(e) { +} + +try { + document.createElement('div').style.height = '-1px'; +} catch(e) { // IE throws error on invalid style set; trap common cases + Y.DOM.CUSTOM_STYLES.height = { + set: function(node, val, style) { + var floatVal = parseFloat(val); + if (isNaN(floatVal) || floatVal >= 0) { + style.height = val; + } else { + } + } + }; + + Y.DOM.CUSTOM_STYLES.width = { + set: function(node, val, style) { + var floatVal = parseFloat(val); + if (isNaN(floatVal) || floatVal >= 0) { + style.width = val; + } else { + } + } + }; +} + +// TODO: top, right, bottom, left +IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset; + +IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor; + +IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] = + IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] = + ComputedStyle.getBorderWidth; + +IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom = + IEComputed.marginLeft = ComputedStyle.getMargin; + +IEComputed.visibility = ComputedStyle.getVisibility; +IEComputed.borderColor = IEComputed.borderTopColor = + IEComputed.borderRightColor = IEComputed.borderBottomColor = + IEComputed.borderLeftColor = ComputedStyle.getBorderColor; + +if (!Y.config.win[GET_COMPUTED_STYLE]) { + Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get; +} + +Y.namespace('DOM.IE'); +Y.DOM.IE.COMPUTED = IEComputed; +Y.DOM.IE.ComputedStyle = ComputedStyle; + +})(Y); + + +}, '3.0.0' ,{requires:['dom-base']}); +YUI.add('dom-screen', function(Y) { + +(function(Y) { + +/** + * Adds position and region management functionality to DOM. + * @module dom + * @submodule dom-screen + * @for DOM + */ + +var DOCUMENT_ELEMENT = 'documentElement', + COMPAT_MODE = 'compatMode', + POSITION = 'position', + FIXED = 'fixed', + RELATIVE = 'relative', + LEFT = 'left', + TOP = 'top', + _BACK_COMPAT = 'BackCompat', + MEDIUM = 'medium', + BORDER_LEFT_WIDTH = 'borderLeftWidth', + BORDER_TOP_WIDTH = 'borderTopWidth', + GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect', + GET_COMPUTED_STYLE = 'getComputedStyle', + + // TODO: how about thead/tbody/tfoot/tr? + // TODO: does caption matter? + RE_TABLE = /^t(?:able|d|h)$/i; + +Y.mix(Y.DOM, { + /** + * Returns the inner height of the viewport (exludes scrollbar). + * @method winHeight + * @return {Number} The current height of the viewport. + */ + winHeight: function(node) { + var h = Y.DOM._getWinSize(node).height; + return h; + }, + + /** + * Returns the inner width of the viewport (exludes scrollbar). + * @method winWidth + * @return {Number} The current width of the viewport. + */ + winWidth: function(node) { + var w = Y.DOM._getWinSize(node).width; + return w; + }, + + /** + * Document height + * @method docHeight + * @return {Number} The current height of the document. + */ + docHeight: function(node) { + var h = Y.DOM._getDocSize(node).height; + return Math.max(h, Y.DOM._getWinSize(node).height); + }, + + /** + * Document width + * @method docWidth + * @return {Number} The current width of the document. + */ + docWidth: function(node) { + var w = Y.DOM._getDocSize(node).width; + return Math.max(w, Y.DOM._getWinSize(node).width); + }, + + /** + * Amount page has been scroll horizontally + * @method docScrollX + * @return {Number} The current amount the screen is scrolled horizontally. + */ + docScrollX: function(node) { + var doc = Y.DOM._getDoc(node); + return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft); + }, + + /** + * Amount page has been scroll vertically + * @method docScrollY + * @return {Number} The current amount the screen is scrolled vertically. + */ + docScrollY: function(node) { + var doc = Y.DOM._getDoc(node); + return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop); + }, + + /** + * Gets the current position of an element based on page coordinates. + * Element must be part of the DOM tree to have page coordinates + * (display:none or elements not appended return false). + * @method getXY + * @param element The target element + * @return {Array} The XY position of the element + + TODO: test inDocument/display? + */ + getXY: function() { + if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) { + return function(node) { + var xy = null, + scrollLeft, + scrollTop, + box, + off1, off2, + bLeft, bTop, + mode, + doc; + + if (node) { + if (Y.DOM.inDoc(node)) { + scrollLeft = Y.DOM.docScrollX(node); + scrollTop = Y.DOM.docScrollY(node); + box = node[GET_BOUNDING_CLIENT_RECT](); + doc = Y.DOM._getDoc(node); + xy = [box.left, box.top]; + + if (Y.UA.ie) { + off1 = 2; + off2 = 2; + mode = doc[COMPAT_MODE]; + bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH); + bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH); + + if (Y.UA.ie === 6) { + if (mode !== _BACK_COMPAT) { + off1 = 0; + off2 = 0; + } + } + + if ((mode == _BACK_COMPAT)) { + if (bLeft !== MEDIUM) { + off1 = parseInt(bLeft, 10); + } + if (bTop !== MEDIUM) { + off2 = parseInt(bTop, 10); + } + } + + xy[0] -= off1; + xy[1] -= off2; + + } + + if ((scrollTop || scrollLeft)) { + xy[0] += scrollLeft; + xy[1] += scrollTop; + } + } else { // default to current offsets + xy = Y.DOM._getOffset(node); + } + } + return xy; + }; + } else { + return function(node) { // manually calculate by crawling up offsetParents + //Calculate the Top and Left border sizes (assumes pixels) + var xy = null, + parentNode, + bCheck, + scrollTop, + scrollLeft; + + if (node) { + if (Y.DOM.inDoc(node)) { + xy = [node.offsetLeft, node.offsetTop]; + parentNode = node; + // TODO: refactor with !! or just falsey + bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false); + + // TODO: worth refactoring for TOP/LEFT only? + while ((parentNode = parentNode.offsetParent)) { + xy[0] += parentNode.offsetLeft; + xy[1] += parentNode.offsetTop; + if (bCheck) { + xy = Y.DOM._calcBorders(parentNode, xy); + } + } + + // account for any scrolled ancestors + if (Y.DOM.getStyle(node, POSITION) != FIXED) { + parentNode = node; + + while ((parentNode = parentNode.parentNode)) { + scrollTop = parentNode.scrollTop; + scrollLeft = parentNode.scrollLeft; + + //Firefox does something funky with borders when overflow is not visible. + if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) { + xy = Y.DOM._calcBorders(parentNode, xy); + } + + + if (scrollTop || scrollLeft) { + xy[0] -= scrollLeft; + xy[1] -= scrollTop; + } + } + xy[0] += Y.DOM.docScrollX(node); + xy[1] += Y.DOM.docScrollY(node); + + } else { + //Fix FIXED position -- add scrollbars + xy[0] += Y.DOM.docScrollX(node); + xy[1] += Y.DOM.docScrollY(node); + } + } else { + xy = Y.DOM._getOffset(node); + } + } + + return xy; + }; + } + }(),// NOTE: Executing for loadtime branching + + _getOffset: function(node) { + var pos, + xy = null; + + if (node) { + pos = Y.DOM.getStyle(node, POSITION); + xy = [ + parseInt(Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10), + parseInt(Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10) + ]; + + if ( isNaN(xy[0]) ) { // in case of 'auto' + xy[0] = parseInt(Y.DOM.getStyle(node, LEFT), 10); // try inline + if ( isNaN(xy[0]) ) { // default to offset value + xy[0] = (pos === RELATIVE) ? 0 : node.offsetLeft || 0; + } + } + + if ( isNaN(xy[1]) ) { // in case of 'auto' + xy[1] = parseInt(Y.DOM.getStyle(node, TOP), 10); // try inline + if ( isNaN(xy[1]) ) { // default to offset value + xy[1] = (pos === RELATIVE) ? 0 : node.offsetTop || 0; + } + } + } + + return xy; + + }, + + /** + * Gets the current X position of an element based on page coordinates. + * Element must be part of the DOM tree to have page coordinates + * (display:none or elements not appended return false). + * @method getX + * @param element The target element + * @return {Int} The X position of the element + */ + + getX: function(node) { + return Y.DOM.getXY(node)[0]; + }, + + /** + * Gets the current Y position of an element based on page coordinates. + * Element must be part of the DOM tree to have page coordinates + * (display:none or elements not appended return false). + * @method getY + * @param element The target element + * @return {Int} The Y position of the element + */ + + getY: function(node) { + return Y.DOM.getXY(node)[1]; + }, + + /** + * Set the position of an html element in page coordinates. + * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). + * @method setXY + * @param element The target element + * @param {Array} xy Contains X & Y values for new position (coordinates are page-based) + * @param {Boolean} noRetry By default we try and set the position a second time if the first fails + */ + setXY: function(node, xy, noRetry) { + var setStyle = Y.DOM.setStyle, + pos, + delta, + newXY, + currentXY; + + if (node && xy) { + pos = Y.DOM.getStyle(node, POSITION); + + delta = Y.DOM._getOffset(node); + + if (pos == 'static') { // default to relative + pos = RELATIVE; + setStyle(node, POSITION, pos); + } + + currentXY = Y.DOM.getXY(node); + + if (xy[0] !== null) { + setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px'); + } + + if (xy[1] !== null) { + setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px'); + } + + if (!noRetry) { + newXY = Y.DOM.getXY(node); + if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) { + Y.DOM.setXY(node, xy, true); + } + } + + } else { + } + }, + + /** + * Set the X position of an html element in page coordinates, regardless of how the element is positioned. + * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). + * @method setX + * @param element The target element + * @param {Int} x The X values for new position (coordinates are page-based) + */ + setX: function(node, x) { + return Y.DOM.setXY(node, [x, null]); + }, + + /** + * Set the Y position of an html element in page coordinates, regardless of how the element is positioned. + * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false). + * @method setY + * @param element The target element + * @param {Int} y The Y values for new position (coordinates are page-based) + */ + setY: function(node, y) { + return Y.DOM.setXY(node, [null, y]); + }, + + _calcBorders: function(node, xy2) { + var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0, + l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0; + if (Y.UA.gecko) { + if (RE_TABLE.test(node.tagName)) { + t = 0; + l = 0; + } + } + xy2[0] += l; + xy2[1] += t; + return xy2; + }, + + _getWinSize: function(node) { + var doc = Y.DOM._getDoc(), + win = doc.defaultView || doc.parentWindow, + mode = doc[COMPAT_MODE], + h = win.innerHeight, + w = win.innerWidth, + root = doc[DOCUMENT_ELEMENT]; + + if ( mode && !Y.UA.opera ) { // IE, Gecko + if (mode != 'CSS1Compat') { // Quirks + root = doc.body; + } + h = root.clientHeight; + w = root.clientWidth; + } + return { height: h, width: w }; + }, + + _getDocSize: function(node) { + var doc = Y.DOM._getDoc(), + root = doc[DOCUMENT_ELEMENT]; + + if (doc[COMPAT_MODE] != 'CSS1Compat') { + root = doc.body; + } + + return { height: root.scrollHeight, width: root.scrollWidth }; + } +}); +})(Y); +(function(Y) { +var TOP = 'top', + RIGHT = 'right', + BOTTOM = 'bottom', + LEFT = 'left', + + getOffsets = function(r1, r2) { + var t = Math.max(r1[TOP], r2[TOP]), + r = Math.min(r1[RIGHT], r2[RIGHT]), + b = Math.min(r1[BOTTOM], r2[BOTTOM]), + l = Math.max(r1[LEFT], r2[LEFT]), + ret = {}; + + ret[TOP] = t; + ret[RIGHT] = r; + ret[BOTTOM] = b; + ret[LEFT] = l; + return ret; + }, + + DOM = Y.DOM; + +Y.mix(DOM, { + /** + * Returns an Object literal containing the following about this element: (top, right, bottom, left) + * @method region + * @param {HTMLElement} element The DOM element. + @return {Object} Object literal containing the following about this element: (top, right, bottom, left) + */ + region: function(node) { + var xy = DOM.getXY(node), + ret = false; + + if (node && xy) { + ret = DOM._getRegion( + xy[1], // top + xy[0] + node.offsetWidth, // right + xy[1] + node.offsetHeight, // bottom + xy[0] // left + ); + } + + return ret; + }, + + /** + * Find the intersect information for the passes nodes. + * @method intersect + * @param {HTMLElement} element The first element + * @param {HTMLElement | Object} element2 The element or region to check the interect with + * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop) + @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion) + */ + intersect: function(node, node2, altRegion) { + var r = altRegion || DOM.region(node), region = {}, + n = node2, + off; + + if (n.tagName) { + region = DOM.region(n); + } else if (Y.Lang.isObject(node2)) { + region = node2; + } else { + return false; + } + + off = getOffsets(region, r); + return { + top: off[TOP], + right: off[RIGHT], + bottom: off[BOTTOM], + left: off[LEFT], + area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])), + yoff: ((off[BOTTOM] - off[TOP])), + xoff: (off[RIGHT] - off[LEFT]), + inRegion: DOM.inRegion(node, node2, false, altRegion) + }; + + }, + /** + * Check if any part of this node is in the passed region + * @method inRegion + * @param {Object} node2 The node to get the region from or an Object literal of the region + * $param {Boolean} all Should all of the node be inside the region + * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop) + * @return {Boolean} True if in region, false if not. + */ + inRegion: function(node, node2, all, altRegion) { + var region = {}, + r = altRegion || DOM.region(node), + n = node2, + off; + + if (n.tagName) { + region = DOM.region(n); + } else if (Y.Lang.isObject(node2)) { + region = node2; + } else { + return false; + } + + if (all) { + return ( + r[LEFT] >= region[LEFT] && + r[RIGHT] <= region[RIGHT] && + r[TOP] >= region[TOP] && + r[BOTTOM] <= region[BOTTOM] ); + } else { + off = getOffsets(region, r); + if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) { + return true; + } else { + return false; + } + + } + }, + + /** + * Check if any part of this element is in the viewport + * @method inViewportRegion + * @param {HTMLElement} element The DOM element. + * @param {Boolean} all Should all of the node be inside the region + * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop) + * @return {Boolean} True if in region, false if not. + */ + inViewportRegion: function(node, all, altRegion) { + return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion); + + }, + + _getRegion: function(t, r, b, l) { + var region = {}; + + region[TOP] = region[1] = t; + region[LEFT] = region[0] = l; + region[BOTTOM] = b; + region[RIGHT] = r; + region.width = region[RIGHT] - region[LEFT]; + region.height = region[BOTTOM] - region[TOP]; + + return region; + }, + + /** + * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left) + * @method viewportRegion + @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left) + */ + viewportRegion: function(node) { + node = node || Y.config.doc.documentElement; + var ret = false, + scrollX, + scrollY; + + if (node) { + scrollX = DOM.docScrollX(node); + scrollY = DOM.docScrollY(node); + + ret = DOM._getRegion(scrollY, // top + DOM.winWidth(node) + scrollX, // right + scrollY + DOM.winHeight(node), // bottom + scrollX); // left + } + + return ret; + } +}); +})(Y); + + +}, '3.0.0' ,{requires:['dom-base', 'dom-style']}); +YUI.add('selector-native', function(Y) { + +(function(Y) { +/** + * The selector-native module provides support for native querySelector + * @module dom + * @submodule selector-native + * @for Selector + */ + +/** + * Provides support for using CSS selectors to query the DOM + * @class Selector + * @static + * @for Selector + */ + +Y.namespace('Selector'); // allow native module to standalone + +var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition', + OWNER_DOCUMENT = 'ownerDocument', + TMP_PREFIX = 'yui-tmp-', + g_counter = 0; + +var Selector = { + _foundCache: [], + + useNative: true, + + _compare: ('sourceIndex' in document.documentElement) ? + function(nodeA, nodeB) { + var a = nodeA.sourceIndex, + b = nodeB.sourceIndex; + + if (a === b) { + return 0; + } else if (a > b) { + return 1; + } + + return -1; + + } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ? + function(nodeA, nodeB) { + if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) { + return -1; + } else { + return 1; + } + } : + function(nodeA, nodeB) { + var rangeA, rangeB, compare; + if (nodeA && nodeB) { + rangeA = nodeA[OWNER_DOCUMENT].createRange(); + rangeA.setStart(nodeA, 0); + rangeB = nodeB[OWNER_DOCUMENT].createRange(); + rangeB.setStart(nodeB, 0); + compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END + } + + return compare; + + }), + + _sort: function(nodes) { + if (nodes) { + nodes = Y.Array(nodes, 0, true); + if (nodes.sort) { + nodes.sort(Selector._compare); + } + } + + return nodes; + }, + + _deDupe: function(nodes) { + var ret = [], + i, node; + + for (i = 0; (node = nodes[i++]);) { + if (!node._found) { + ret[ret.length] = node; + node._found = true; + } + } + + for (i = 0; (node = ret[i++]);) { + node._found = null; + node.removeAttribute('_found'); + } + + return ret; + }, + + /** + * Retrieves a set of nodes based on a given CSS selector. + * @method query + * + * @param {string} selector The CSS Selector to test the node against. + * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc + * @param {Boolean} firstOnly optional Whether or not to return only the first match. + * @return {Array} An array of nodes that match the given selector. + * @static + */ + query: function(selector, root, firstOnly, skipNative) { + root = root || Y.config.doc; + var ret = [], + useNative = (Y.Selector.useNative && document.querySelector && !skipNative), + queries = [[selector, root]], + query, + result, + i, + fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery; + + if (selector && fn) { + // split group into seperate queries + if (!skipNative && // already done if skipping + (!useNative || root.tagName)) { // split native when element scoping is needed + queries = Selector._splitQueries(selector, root); + } + + for (i = 0; (query = queries[i++]);) { + result = fn(query[0], query[1], firstOnly); + if (!firstOnly) { // coerce DOM Collection to Array + result = Y.Array(result, 0, true); + } + if (result) { + ret = ret.concat(result); + } + } + + if (queries.length > 1) { // remove dupes and sort by doc order + ret = Selector._sort(Selector._deDupe(ret)); + } + } + + return (firstOnly) ? (ret[0] || null) : ret; + + }, + + // allows element scoped queries to begin with combinator + // e.g. query('> p', document.body) === query('body > p') + _splitQueries: function(selector, node) { + var groups = selector.split(','), + queries = [], + prefix = '', + i, len; + + if (node) { + // enforce for element scoping + if (node.tagName) { + node.id = node.id || Y.guid(); + prefix = '#' + node.id + ' '; + } + + for (i = 0, len = groups.length; i < len; ++i) { + selector = prefix + groups[i]; + queries.push([selector, node]); + } + } + + return queries; + }, + + _nativeQuery: function(selector, root, one) { + try { + return root['querySelector' + (one ? '' : 'All')](selector); + } catch(e) { // fallback to brute if available + return Y.Selector.query(selector, root, one, true); // redo with skipNative true + } + }, + + filter: function(nodes, selector) { + var ret = [], + i, node; + + if (nodes && selector) { + for (i = 0; (node = nodes[i++]);) { + if (Y.Selector.test(node, selector)) { + ret[ret.length] = node; + } + } + } else { + } + + return ret; + }, + + test: function(node, selector, root) { + var ret = false, + groups = selector.split(','), + item, + i, group; + + if (node && node.tagName) { // only test HTMLElements + root = root || node.ownerDocument; + + if (!node.id) { + node.id = TMP_PREFIX + g_counter++; + } + for (i = 0; (group = groups[i++]);) { // TODO: off-dom test + group += '#' + node.id; // add ID for uniqueness + item = Y.Selector.query(group, root, true); + ret = (item === node); + if (ret) { + break; + } + } + } + + return ret; + } +}; + +Y.mix(Y.Selector, Selector, true); + +})(Y); + + +}, '3.0.0' ,{requires:['dom-base']}); +YUI.add('selector-css2', function(Y) { + +/** + * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements. + * @module dom + * @submodule selector-css2 + * @for Selector + */ + +/** + * Provides helper methods for collecting and filtering DOM elements. + */ + +var PARENT_NODE = 'parentNode', + TAG_NAME = 'tagName', + ATTRIBUTES = 'attributes', + COMBINATOR = 'combinator', + PSEUDOS = 'pseudos', + + Selector = Y.Selector, + + SelectorCSS2 = { + SORT_RESULTS: true, + _children: function(node, tag) { + var ret = node.children, + i, + children = [], + childNodes, + child; + + if (node.children && tag && node.children.tags) { + children = node.children.tags(tag); + } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children + childNodes = ret || node.childNodes; + ret = []; + for (i = 0; (child = childNodes[i++]);) { + if (child.tagName) { + if (!tag || tag === child.tagName) { + ret.push(child); + } + } + } + } + + return ret || []; + }, + + _regexCache: {}, + + _re: { + attr: /(\[.*\])/g, + pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i + }, + + /** + * Mapping of shorthand tokens to corresponding attribute selector + * @property shorthand + * @type object + */ + shorthand: { + '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]', + '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]' + }, + + /** + * List of operators and corresponding boolean functions. + * These functions are passed the attribute and the current node's value of the attribute. + * @property operators + * @type object + */ + operators: { + '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute + //'': '.+', + //'=': '^{val}$', // equality + '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited + '|=': '^{val}-?' // optional hyphen-delimited + }, + + pseudos: { + 'first-child': function(node) { + return Y.Selector._children(node[PARENT_NODE])[0] === node; + } + }, + + _bruteQuery: function(selector, root, firstOnly) { + var ret = [], + nodes = [], + tokens = Selector._tokenize(selector), + token = tokens[tokens.length - 1], + rootDoc = Y.DOM._getDoc(root), + id, + className, + tagName; + + + // if we have an initial ID, set to root when in document + if (tokens[0] && rootDoc === root && + (id = tokens[0].id) && + rootDoc.getElementById(id)) { + root = rootDoc.getElementById(id); + } + + if (token) { + // prefilter nodes + id = token.id; + className = token.className; + tagName = token.tagName || '*'; + + // try ID first + if (id) { + if (rootDoc.getElementById(id)) { // if in document + nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId? + } + // try className if supported + } else if (className) { + nodes = root.getElementsByClassName(className); + } else if (tagName) { // default to tagName + nodes = root.getElementsByTagName(tagName || '*'); + } + + if (nodes.length) { + ret = Selector._filterNodes(nodes, tokens, firstOnly); + } + } + + return ret; + }, + + _filterNodes: function(nodes, tokens, firstOnly) { + var i = 0, + j, + len = tokens.length, + n = len - 1, + result = [], + node = nodes[0], + tmpNode = node, + getters = Y.Selector.getters, + operator, + combinator, + token, + path, + pass, + //FUNCTION = 'function', + value, + tests, + test; + + //do { + for (i = 0; (tmpNode = node = nodes[i++]);) { + n = len - 1; + path = null; + + testLoop: + while (tmpNode && tmpNode.tagName) { + token = tokens[n]; + tests = token.tests; + j = tests.length; + if (j && !pass) { + while ((test = tests[--j])) { + operator = test[1]; + if (getters[test[0]]) { + value = getters[test[0]](tmpNode, test[0]); + } else { + value = tmpNode[test[0]]; + // use getAttribute for non-standard attributes + if (value === undefined && tmpNode.getAttribute) { + value = tmpNode.getAttribute(test[0]); + } + } + + if ((operator === '=' && value !== test[2]) || // fast path for equality + (operator.test && !operator.test(value)) || // regex test + (operator.call && !operator(tmpNode, test[0]))) { // function test + + // skip non element nodes or non-matching tags + if ((tmpNode = tmpNode[path])) { + while (tmpNode && + (!tmpNode.tagName || + (token.tagName && token.tagName !== tmpNode.tagName)) + ) { + tmpNode = tmpNode[path]; + } + } + continue testLoop; + } + } + } + + n--; // move to next token + // now that we've passed the test, move up the tree by combinator + if (!pass && (combinator = token.combinator)) { + path = combinator.axis; + tmpNode = tmpNode[path]; + + // skip non element nodes + while (tmpNode && !tmpNode.tagName) { + tmpNode = tmpNode[path]; + } + + if (combinator.direct) { // one pass only + path = null; + } + + } else { // success if we made it this far + result.push(node); + if (firstOnly) { + return result; + } + break; + } + } + }// while (tmpNode = node = nodes[++i]); + node = tmpNode = null; + return result; + }, + + _getRegExp: function(str, flags) { + var regexCache = Selector._regexCache; + flags = flags || ''; + if (!regexCache[str + flags]) { + regexCache[str + flags] = new RegExp(str, flags); + } + return regexCache[str + flags]; + }, + + combinators: { + ' ': { + axis: 'parentNode' + }, + + '>': { + axis: 'parentNode', + direct: true + }, + + + '+': { + axis: 'previousSibling', + direct: true + } + }, + + _parsers: [ + { + name: ATTRIBUTES, + re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i, + fn: function(match, token) { + var operator = match[2] || '', + operators = Y.Selector.operators, + test; + + // add prefiltering for ID and CLASS + if ((match[1] === 'id' && operator === '=') || + (match[1] === 'className' && + document.getElementsByClassName && + (operator === '~=' || operator === '='))) { + token.prefilter = match[1]; + token[match[1]] = match[3]; + } + + // add tests + if (operator in operators) { + test = operators[operator]; + if (typeof test === 'string') { + test = Y.Selector._getRegExp(test.replace('{val}', match[3])); + } + match[2] = test; + } + if (!token.last || token.prefilter !== match[1]) { + return match.slice(1); + } + } + + }, + { + name: TAG_NAME, + re: /^((?:-?[_a-z]+[\w-]*)|\*)/i, + fn: function(match, token) { + var tag = match[1].toUpperCase(); + token.tagName = tag; + + if (tag !== '*' && (!token.last || token.prefilter)) { + return [TAG_NAME, '=', tag]; + } + if (!token.prefilter) { + token.prefilter = 'tagName'; + } + } + }, + { + name: COMBINATOR, + re: /^\s*([>+~]|\s)\s*/, + fn: function(match, token) { + } + }, + { + name: PSEUDOS, + re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i, + fn: function(match, token) { + var test = Selector[PSEUDOS][match[1]]; + if (test) { // reorder match array + return [match[2], test]; + } else { // selector token not supported (possibly missing CSS3 module) + return false; + } + } + } + ], + + _getToken: function(token) { + return { + tagName: null, + id: null, + className: null, + attributes: {}, + combinator: null, + tests: [] + }; + }, + + /** + Break selector into token units per simple selector. + Combinator is attached to the previous token. + */ + _tokenize: function(selector) { + selector = selector || ''; + selector = Selector._replaceShorthand(Y.Lang.trim(selector)); + var token = Selector._getToken(), // one token per simple selector (left selector holds combinator) + query = selector, // original query for debug report + tokens = [], // array of tokens + found = false, // whether or not any matches were found this pass + match, // the regex match + test, + i, parser; + + /* + Search for selector patterns, store, and strip them from the selector string + until no patterns match (invalid selector) or we run out of chars. + + Multiple attributes and pseudos are allowed, in any order. + for example: + 'form:first-child[type=button]:not(button)[lang|=en]' + */ + outer: + do { + found = false; // reset after full pass + for (i = 0; (parser = Selector._parsers[i++]);) { + if ( (match = parser.re.exec(selector)) ) { // note assignment + if (parser !== COMBINATOR ) { + token.selector = selector; + } + selector = selector.replace(match[0], ''); // strip current match from selector + if (!selector.length) { + token.last = true; + } + + if (Selector._attrFilters[match[1]]) { // convert class to className, etc. + match[1] = Selector._attrFilters[match[1]]; + } + + test = parser.fn(match, token); + if (test === false) { // selector not supported + found = false; + break outer; + } else if (test) { + token.tests.push(test); + } + + if (!selector.length || parser.name === COMBINATOR) { + tokens.push(token); + token = Selector._getToken(token); + if (parser.name === COMBINATOR) { + token.combinator = Y.Selector.combinators[match[1]]; + } + } + found = true; + } + } + } while (found && selector.length); + + if (!found || selector.length) { // not fully parsed + tokens = []; + } + return tokens; + }, + + _replaceShorthand: function(selector) { + var shorthand = Selector.shorthand, + attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#" + pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#" + re, i, len; + + if (pseudos) { + selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!'); + } + + if (attrs) { + selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!'); + } + + for (re in shorthand) { + if (shorthand.hasOwnProperty(re)) { + selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]); + } + } + + if (attrs) { + for (i = 0, len = attrs.length; i < len; ++i) { + selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]); + } + } + if (pseudos) { + for (i = 0, len = pseudos.length; i < len; ++i) { + selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]); + } + } + return selector; + }, + + _attrFilters: { + 'class': 'className', + 'for': 'htmlFor' + }, + + getters: { + href: function(node, attr) { + return Y.DOM.getAttribute(node, attr); + } + } + }; + +Y.mix(Y.Selector, SelectorCSS2, true); +Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href; + +// IE wants class with native queries +if (Y.Selector.useNative && document.querySelector) { + Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]'; +} + + + +}, '3.0.0' ,{requires:['selector-native']}); + + +YUI.add('selector', function(Y){}, '3.0.0' ,{use:['selector-native', 'selector-css2']}); + + + +YUI.add('dom', function(Y){}, '3.0.0' ,{use:['dom-base', 'dom-style', 'dom-screen', 'selector']}); +