dom.js revision 7f301ced05415668ede239d3d16fe4a4199754e6
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTrippYUI.add('dom-base', function(Y) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp(function(Y) {
a75ebc38c1de401b679953a9b87bd323f0f48d02Tripp/**
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * The DOM utility provides a cross-browser abtraction layer
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * normalizing DOM tasks, and adds extra helper functionality
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * for other common tasks.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @module dom
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @submodule dom-base
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp *
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
422668e1d4513bb870b8b576fd9d828c8872f074Tripp/**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Provides DOM helper methods.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @class DOM
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp *
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Trippvar NODE_TYPE = 'nodeType',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp OWNER_DOCUMENT = 'ownerDocument',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp DOCUMENT_ELEMENT = 'documentElement',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp DEFAULT_VIEW = 'defaultView',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp PARENT_WINDOW = 'parentWindow',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp TAG_NAME = 'tagName',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp PARENT_NODE = 'parentNode',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp FIRST_CHILD = 'firstChild',
4beb671b9f23325489cc74a7950f2b1f1420b5f3Tripp LAST_CHILD = 'lastChild',
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp PREVIOUS_SIBLING = 'previousSibling',
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp NEXT_SIBLING = 'nextSibling',
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp CONTAINS = 'contains',
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp
422668e1d4513bb870b8b576fd9d828c8872f074Tripp re_tag = /<([a-z]+)/i;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTrippY.DOM = {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp /**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method byId
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {String} id the id attribute
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {Object} doc optional The document to search. Defaults to current document
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp byId: function(id, doc) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp doc = doc || Y.config.doc;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp // TODO: IE Name
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp return doc.getElementById(id);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Returns the text content of the HTMLElement.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method getText
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} element The html element.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @return {String} The text content of the element (includes text of any descending elements).
7174b256e7956a8efaff0352e7cac98b942f804fTripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp getText: (document.documentElement.textContent !== undefined) ?
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp function(element) {
7174b256e7956a8efaff0352e7cac98b942f804fTripp var ret = '';
7174b256e7956a8efaff0352e7cac98b942f804fTripp if (element) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp ret = element.textContent;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp return ret || '';
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp } : function(element) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp var ret = '';
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (element) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp ret = element.innerText;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp return ret || '';
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp },
422668e1d4513bb870b8b576fd9d828c8872f074Tripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Returns the text content of the HTMLElement.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method getText
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} element The html element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @return {String} The text content of the element (includes text of any descending elements).
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp */
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp setText: (document.documentElement.textContent !== undefined) ?
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp function(element, content) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (element) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp element.textContent = content;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp } : function(element, content) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (element) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp element.innerText = content;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp },
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp// TODO: pull out sugar (rely on _childBy, byAxis, etc)?
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Finds the firstChild of the given HTMLElement.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method firstChild
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {HTMLElement} element The html element.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {Function} fn optional An optional boolean test to apply.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * The optional function is passed the current HTMLElement being tested as its only argument.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * If no function is given, the first found is returned.
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp * @return {HTMLElement | null} The first matching child html element.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp */
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp firstChild: function(element, fn) {
422668e1d4513bb870b8b576fd9d828c8872f074Tripp return Y.DOM._childBy(element, null, fn);
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp },
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp firstChildByTag: function(element, tag, fn) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return Y.DOM._childBy(element, tag, fn);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Finds the lastChild of the given HTMLElement.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method lastChild
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp * @param {HTMLElement} element The html element.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {String} tag The tag to search for.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {Function} fn optional An optional boolean test to apply.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * The optional function is passed the current HTMLElement being tested as its only argument.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * If no function is given, the first found is returned.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * @return {HTMLElement | null} The first matching child html element.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp */
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp lastChild: function(element, fn) {
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp return Y.DOM._childBy(element, null, fn, true);
422668e1d4513bb870b8b576fd9d828c8872f074Tripp },
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp lastChildByTag: function(element, tag, fn) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp return Y.DOM._childBy(element, tag, fn, true);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp /*
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * Finds all HTMLElement childNodes matching the given tag.
8209f3939e32e0e5bde64192267fdaf9db6f4fbcTripp * @method childrenByTag
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * @param {HTMLElement} element The html element.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * @param {String} tag The tag to search for.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * @param {Function} fn optional An optional boolean test to apply.
422668e1d4513bb870b8b576fd9d828c8872f074Tripp * The optional function is passed the current HTMLElement being tested as its only argument.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * If no function is given, all children with the given tag are collected.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @return {Array} The collection of child elements.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * TODO: deprecate? Webkit children.tags() returns grandchildren
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp _childrenByTag: function() {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if (document[DOCUMENT_ELEMENT].children) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return function(element, tag, fn, toArray) { // TODO: keep toArray option?
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp var elements = [],
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp wrapFn = fn;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if (element) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if (tag && !Y.UA.webkit) { // children.tags() broken in safari
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp elements = element.children.tags(tag);
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp } else {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp elements = element.children;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (tag) {
f4bca85db4b211c6b3afe1f319ae46cebe62992dTripp wrapFn = function(el) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return el[TAG_NAME].toUpperCase() === tag && (!fn || fn(el));
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp };
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
f4bca85db4b211c6b3afe1f319ae46cebe62992dTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp elements = Y.DOM.filterElementsBy(elements, wrapFn);
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp return elements;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp };
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp } else {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp return function(element, tag, fn) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp var elements = [],
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp wrapFn = fn;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (element) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp elements = element.childNodes;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if (tag) { // wrap fn and add tag test TODO: allow tag in filterElementsBy?
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp wrapFn = function(el) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return el[TAG_NAME].toUpperCase() === tag && (!fn || fn(el));
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp };
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp elements = Y.DOM.filterElementsBy(elements, wrapFn);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return elements;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp };
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }(),
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Finds all HTMLElement childNodes.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method children
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} element The html element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Function} fn optional An optional boolean test to apply.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * The optional function is passed the current HTMLElement being tested as its only argument.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * If no function is given, all children are collected.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @return {Array} The collection of child elements.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp children: function(element, fn) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return Y.DOM._childrenByTag(element, null, fn);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Finds the previous sibling of the element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method previous
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} element The html element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Function} fn optional An optional boolean test to apply.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * The optional function is passed the current DOM node being tested as its only argument.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * If no function is given, the first sibling is returned.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @return {HTMLElement | null} The matching DOM node or null if none found.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp previous: function(element, fn, all) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Finds the next sibling of the element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method next
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} element The html element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Function} fn optional An optional boolean test to apply.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * The optional function is passed the current DOM node being tested as its only argument.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * If no function is given, the first sibling is returned.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @return {HTMLElement | null} The matching DOM node or null if none found.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp next: function(element, fn, all) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Finds the ancestor of the element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method ancestor
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} element The html element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Function} fn optional An optional boolean test to apply.
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp * The optional function is passed the current DOM node being tested as its only argument.
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp * If no function is given, the parentNode is returned.
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp * @return {HTMLElement | null} The matching DOM node or null if none found.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp // TODO: optional stopAt node?
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp ancestor: function(element, fn, all) {
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp },
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Searches the element by the given axis for the first matching element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method elementByAxis
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} element The html element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Function} fn optional An optional boolean test to apply.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * The optional function is passed the current HTMLElement being tested as its only argument.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * If no function is given, the first element is returned.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @return {HTMLElement | null} The matching element or null if none found.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp elementByAxis: function(element, axis, fn, all) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp while (element && (element = element[axis])) { // NOTE: assignment
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return element;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return null;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp /**
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp * Finds all elements with the given tag.
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp * @method byTag
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp * @param {String} tag The tag being search for.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {HTMLElement} root optional An optional root element to start from.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Function} fn optional An optional boolean test to apply.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * The optional function is passed the current HTMLElement being tested as its only argument.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * If no function is given, all elements with the given tag are returned.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @return {Array} The collection of matching elements.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp */
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp byTag: function(tag, root, fn) {
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp root = root || Y.config.doc;
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp var elements = root.getElementsByTagName(tag),
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp retNodes = [],
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp i, len;
422668e1d4513bb870b8b576fd9d828c8872f074Tripp
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp for (i = 0, len = elements.length; i < len; ++i) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if ( !fn || fn(elements[i]) ) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp retNodes[retNodes.length] = elements[i];
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp return retNodes;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Finds the first element with the given tag.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method firstByTag
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {String} tag The tag being search for.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {HTMLElement} root optional An optional root element to start from.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @param {Function} fn optional An optional boolean test to apply.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * The optional function is passed the current HTMLElement being tested as its only argument.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * If no function is given, the first match is returned.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @return {HTMLElement} The matching element.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
c8497af565e3869417da55c16f0afc9fafb7d79aTripp firstByTag: function(tag, root, fn) {
c8497af565e3869417da55c16f0afc9fafb7d79aTripp root = root || Y.config.doc;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp var elements = root.getElementsByTagName(tag),
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp ret = null,
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp i, len;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp for (i = 0, len = elements.length; i < len; ++i) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if ( !fn || fn(elements[i]) ) {
f4bca85db4b211c6b3afe1f319ae46cebe62992dTripp ret = elements[i];
f4bca85db4b211c6b3afe1f319ae46cebe62992dTripp break;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
c8497af565e3869417da55c16f0afc9fafb7d79aTripp return ret;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp },
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp /**
c8497af565e3869417da55c16f0afc9fafb7d79aTripp * Filters a collection of HTMLElements by the given attributes.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method filterElementsBy
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {Array} elements The collection of HTMLElements to filter.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {Function} fn A boolean test to apply.
c8497af565e3869417da55c16f0afc9fafb7d79aTripp * The function is passed the current HTMLElement being tested as its only argument.
c8497af565e3869417da55c16f0afc9fafb7d79aTripp * If no function is given, all HTMLElements are kept.
f4bca85db4b211c6b3afe1f319ae46cebe62992dTripp * @return {Array} The filtered collection of HTMLElements.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp */
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp filterElementsBy: function(elements, fn, firstOnly) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp var ret = (firstOnly) ? null : [],
c8497af565e3869417da55c16f0afc9fafb7d79aTripp i, len;
c8497af565e3869417da55c16f0afc9fafb7d79aTripp for (i = 0, len = elements.length; i < len; ++i) {
f4bca85db4b211c6b3afe1f319ae46cebe62992dTripp if (elements[i][TAG_NAME] && (!fn || fn(elements[i]))) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (firstOnly) {
0b4a5b1827b124fb34746b7c748a082a51e32502Tripp ret = elements[i];
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp break;
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp } else {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp ret[ret.length] = elements[i];
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
c8497af565e3869417da55c16f0afc9fafb7d79aTripp }
c8497af565e3869417da55c16f0afc9fafb7d79aTripp
c8497af565e3869417da55c16f0afc9fafb7d79aTripp return ret;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp },
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
c8497af565e3869417da55c16f0afc9fafb7d79aTripp /**
c8497af565e3869417da55c16f0afc9fafb7d79aTripp * Determines whether or not one HTMLElement is or contains another HTMLElement.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method contains
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {HTMLElement} element The containing html element.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {HTMLElement} needle The html element that may be contained.
c8497af565e3869417da55c16f0afc9fafb7d79aTripp * @return {Boolean} Whether or not the element is or contains the needle.
c8497af565e3869417da55c16f0afc9fafb7d79aTripp */
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp contains: function(element, needle) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp var ret = false;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
c8497af565e3869417da55c16f0afc9fafb7d79aTripp if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp ret = false;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp } else if (element[CONTAINS]) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp ret = element[CONTAINS](needle);
c8497af565e3869417da55c16f0afc9fafb7d79aTripp } else {
c8497af565e3869417da55c16f0afc9fafb7d79aTripp ret = Y.DOM._bruteContains(element, needle);
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
c8497af565e3869417da55c16f0afc9fafb7d79aTripp ret = true;
c8497af565e3869417da55c16f0afc9fafb7d79aTripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp return ret;
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp },
6aa3a8d9176704373c2e24b0530b508f643fe6a0Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Determines whether or not the HTMLElement is part of the document.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method inDoc
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {HTMLElement} element The containing html element.
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {HTMLElement} doc optional The document to check.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * @return {Boolean} Whether or not the element is attached to the document.
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp */
422668e1d4513bb870b8b576fd9d828c8872f074Tripp inDoc: function(element, doc) {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp doc = doc || element[OWNER_DOCUMENT];
4beb671b9f23325489cc74a7950f2b1f1420b5f3Tripp var id = element.id;
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (!id) { // TODO: remove when done?
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp id = element.id = Y.guid();
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return !! (doc.getElementById(id));
4beb671b9f23325489cc74a7950f2b1f1420b5f3Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp /**
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * Inserts the new node as the previous sibling of the reference node
4beb671b9f23325489cc74a7950f2b1f1420b5f3Tripp * @method insertBefore
4beb671b9f23325489cc74a7950f2b1f1420b5f3Tripp * @param {String | HTMLElement} newNode The node to be inserted
4beb671b9f23325489cc74a7950f2b1f1420b5f3Tripp * @param {String | HTMLElement} referenceNode The node to insert the new node before
4beb671b9f23325489cc74a7950f2b1f1420b5f3Tripp * @return {HTMLElement} The node that was inserted (or null if insert fails)
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp */
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp insertBefore: function(newNode, referenceNode) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp var ret = null,
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp parent;
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if (newNode && referenceNode && (parent = referenceNode.parentNode)) { // NOTE: assignment
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if (typeof newNode === 'string') {
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp newNode = Y.DOM.create(newNode);
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp }
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp ret = parent.insertBefore(newNode, referenceNode);
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp } else {
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp }
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp return ret;
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp },
422668e1d4513bb870b8b576fd9d828c8872f074Tripp
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Inserts the new node as the next sibling of the reference node
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @method insertAfter
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {String | HTMLElement} newNode The node to be inserted
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {String | HTMLElement} referenceNode The node to insert the new node after
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp * @return {HTMLElement} The node that was inserted (or null if insert fails)
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp */
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp insertAfter: function(newNode, referenceNode) {
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
422668e1d4513bb870b8b576fd9d828c8872f074Tripp return null;
8648721e29bb657dd5c5ff20f03e86fe50628ce6Tripp }
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp if (typeof newNode === 'string') {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp newNode = Y.DOM.create(newNode);
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp }
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp if (referenceNode[NEXT_SIBLING]) {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode[NEXT_SIBLING]);
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp } else {
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp return referenceNode[PARENT_NODE].appendChild(newNode);
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp }
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp },
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp
f99edfa53c6ad6f0caab146a750c4404fd898dc3Tripp /**
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * Creates a new dom node using the provided markup string.
a5057260e5538ddf2faca20fa81271eeff2bf892Tripp * @method create
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {String} html The markup used to create the element
e393eced613f9b4a5fb6bdd461d0e0bf5064d5ecTripp * @param {HTMLDocument} doc An optional document context
*/
create: function(html, doc) {
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 = doc.createDocumentFragment();
while (nodes.length) {
ret.appendChild(nodes[0]);
}
}
Y.DOM._cloneCache[html] = ret.cloneNode(true);
return ret;
},
CUSTOM_ATTRIBUTES: (!document.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) {
if (el && el.setAttribute) {
attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
el.setAttribute(attr, val);
}
},
/**
* 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) {
var ret = '';
if (el && el.getAttribute) {
attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
ret = el.getAttribute(attr, 2);
if (ret === null) {
ret = ''; // per DOM spec
}
}
return ret;
},
srcIndex: (document.documentElement.sourceIndex) ?
function(node) {
return (node && node.sourceIndex) ? node.sourceIndex : null;
} :
function(node) {
return (node && node[OWNER_DOCUMENT]) ?
[].indexOf.call(node[OWNER_DOCUMENT].
getElementsByTagName('*'), node) : null;
},
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: {},
addHTML: function(node, content, where, execScripts) {
content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
var scripts,
newNode = Y.DOM._cloneCache[content];
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':
node.parentNode.insertBefore(newNode, node);
break;
case 'after':
if (node.nextSibling) { // IE errors if refNode is null
node.parentNode.insertBefore(newNode, node.nextSibling);
} else {
node.parentNode.appendChild(newNode);
}
break;
default:
node.appendChild(newNode);
}
}
} else {
node.appendChild(newNode);
}
if (execScripts) {
if (newNode.tagName.toUpperCase() === 'SCRIPT' && !Y.UA.gecko) {
scripts = [newNode]; // execute the new script
} else {
scripts = newNode.getElementsByTagName('script');
}
Y.DOM._execScripts(scripts);
} else if (content.nodeType || content.indexOf('<script') > -1) { // prevent any scripts from being injected
Y.DOM._stripScripts(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;
}
}
},
_stripScripts: function(node) {
var scripts = node.getElementsByTagName('script'),
i, script;
for (i = 0, script; script = scripts[i++];) {
script.parentNode.removeChild(script);
}
},
_execScripts: function(scripts, startIndex) {
var newScript,
i, script;
startIndex = startIndex || 0;
for (i = startIndex, script; script = scripts[i++];) {
newScript = script.ownerDocument.createElement('script');
script.parentNode.replaceChild(newScript, script);
if (script.text) {
newScript.text = script.text;
} else if (script.src) {
newScript.src = script.src;
// "pause" while loading to ensure exec order
// FF reports typeof onload as "undefined", so try IE first
if (typeof newScript.onreadystatechange !== 'undefined') {
newScript.onreadystatechange = function() {
if (/loaded|complete/.test(script.readyState)) {
event.srcElement.onreadystatechange = null;
// timer to help ensure exec order
setTimeout(function() {
Y.DOM._execScripts(scripts, i++);
}, 0);
}
};
} else {
newScript.onload = function(e) {
e.target.onload = null;
Y.DOM._execScripts(scripts, i++);
};
}
return; // NOTE: early return to chain async loading
}
}
},
/**
* 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;
},
// TODO: document this
_childBy: function(element, tag, fn, rev) {
var ret = null,
root, axis;
if (element) {
if (rev) {
root = element[LAST_CHILD];
axis = PREVIOUS_SIBLING;
} else {
root = element[FIRST_CHILD];
axis = NEXT_SIBLING;
}
if (Y.DOM._testElement(root, tag, fn)) { // is the matching element
ret = root;
} else { // need to scan nextSibling axis of firstChild to find matching element
ret = Y.DOM.elementByAxis(root, axis, fn);
}
}
return ret;
},
_batch: function(nodes, fn, arg1, arg2, arg3, etc) {
fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
var result,
ret = [];
if (fn && nodes) {
Y.each(nodes, function(node) {
if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
ret[ret.length] = result;
}
});
}
return ret.length ? ret : nodes;
},
_testElement: function(element, tag, fn) {
tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
return (element && element[TAG_NAME] &&
(!tag || element[TAG_NAME].toUpperCase() === tag) &&
(!fn || fn(element)));
},
creators: {},
_IESimpleCreate: function(html, doc) {
doc = doc || Y.config.doc;
return doc.createElement(html);
}
};
(function(Y) {
var creators = Y.DOM.creators,
create = Y.DOM.create,
re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
TABLE_OPEN = '<table>',
TABLE_CLOSE = '</table>';
if (Y.UA.gecko || Y.UA.ie) { // require custom creation code for certain element types
Y.mix(creators, {
option: function(html, doc) {
return create('<select>' + html + '</select>', doc);
},
tr: function(html, doc) {
return create('<tbody>' + html + '</tbody>', doc);
},
td: function(html, doc) {
return create('<tr>' + html + '</tr>', doc);
},
tbody: function(html, doc) {
return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
},
legend: 'fieldset'
});
creators.col = creators.tbody; // IE wraps in colgroup
}
if (Y.UA.ie) {
Y.mix(creators, {
// TODO: thead/tfoot with nested tbody
// IE adds TBODY when creating TABLE elements (which may share this impl)
tbody: function(html, doc) {
var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
tb = frag.children.tags('tbody')[0];
if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
}
return frag;
},
script: function(html, doc) {
var frag = doc.createElement('div');
frag.innerHTML = '-' + html;
frag.removeChild(frag[FIRST_CHILD]);
return frag;
}
}, true);
Y.mix(Y.DOM.VALUE_GETTERS, {
button: function(node) {
return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
}
});
Y.mix(Y.DOM.VALUE_SETTERS, {
// IE: node.value changes the button text, which should be handled via innerHTML
button: function(node, val) {
var attr = node.attributes.value;
if (!attr) {
attr = node[OWNER_DOCUMENT].createAttribute('value');
node.setAttributeNode(attr);
}
attr.value = val;
}
});
}
if (Y.UA.gecko || Y.UA.ie) {
Y.mix(creators, {
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);
/**
* 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
* @for DOM
*/
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 && Y.DOM.hasClass(node, className)) {
node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
className + '(?:\\s+|$)'), ' '));
if ( Y.DOM.hasClass(node, className) ) { // in case of multiple adjacent
Y.DOM.removeClass(node, className);
}
}
},
/**
* Replace a class with another class for a given element.
* If no oldClassName is present, the newClassName is simply added.
* @method replaceClass
* @param {HTMLElement} element The DOM element.
* @param {String} oldClassName the class name to be replaced
* @param {String} newClassName the class name that will be replacing the old class name
*/
replaceClass: function(node, oldC, newC) {
Y.DOM.addClass(node, newC);
Y.DOM.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 (Y.DOM.hasClass(node, className)) {
Y.DOM.removeClass(node, className);
} else {
Y.DOM.addClass(node, className);
}
}
});
}, '@VERSION@' ,{requires:['event'], skinnable:false});
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) {
/**
* Add style management functionality to DOM.
* @module dom
* @submodule dom-style
* @for DOM
*/
var TO_STRING = 'toString',
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)) {
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.Color.re_hex3, '$1$1');
}
if (val !== 'transparent' && val.indexOf('#') < 0) {
val = '#' + val;
}
return val.toLowerCase();
}
};
})(Y);
(function(Y) {
/**
* Add style management functionality to DOM.
* @module dom
* @submodule dom-style
* @for DOM
*/
var CLIENT_TOP = 'clientTop',
CLIENT_LEFT = 'clientLeft',
HAS_LAYOUT = 'hasLayout',
PX = 'px',
FILTER = 'filter',
FILTERS = 'filters',
OPACITY = 'opacity',
AUTO = 'auto',
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_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,
_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) {
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;
},
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.
actual,
value = '';
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 (current.indexOf('%') > -1) { // IE pixelWidth incorrect for percent; manually compute
current = el.clientWidth - // offsetWidth - borderWidth
ComputedStyle.getPixel(el, 'paddingRight') -
ComputedStyle.getPixel(el, 'paddingLeft');
}
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.currentStyle || !el.currentStyle[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,
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
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
}
}
}
};
}
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) {
if (parseInt(val, 10) >= 0) {
style.height = val;
} else {
}
}
};
Y.DOM.CUSTOM_STYLES.width = {
set: function(node, val, style) {
if (parseInt(val, 10) >= 0) {
style.width = val;
} else {
}
}
};
}
// TODO: top, right, bottom, left
IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
IEComputed.color = IEComputed.backgroundColor = 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;
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);
}, '@VERSION@' ,{skinnable:false, 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
*/
winHeight: function(node) {
var h = Y.DOM._getWinSize(node).height;
return h;
},
/**
* Returns the inner width of the viewport (exludes scrollbar).
* @method winWidth
*/
winWidth: function(node) {
var w = Y.DOM._getWinSize(node).width;
return w;
},
/**
* Document height
* @method docHeight
*/
docHeight: function(node) {
var h = Y.DOM._getDocSize(node).height;
return Math.max(h, Y.DOM._getWinSize(node).height);
},
/**
* Document width
* @method docWidth
*/
docWidth: function(node) {
var w = Y.DOM._getDocSize(node).width;
return Math.max(w, Y.DOM._getWinSize(node).width);
},
/**
* Amount page has been scroll vertically
* @method docScrollX
*/
docScrollX: function(node) {
var doc = Y.DOM._getDoc(node);
return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
},
/**
* Amount page has been scroll horizontally
* @method docScrollY
*/
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) {
/**
* Adds position and region management functionality to DOM.
* @module dom
* @submodule dom-screen
* @for DOM
*/
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);
}, '@VERSION@' ,{requires:['dom-base', 'dom-style'], skinnable:false});
YUI.add('selector', function(Y) {
(function(Y) {
/**
* The selector-native module provides support for native querySelector
* @module selector-native
*/
/**
* Provides a wrapper for native querySelectorAll
* @for Selector
*/
Y.namespace('Selector'); // allow native module to standalone
var NativeSelector = {
_reLead: /^\s*([>+~]|:self)/,
_reUnSupported: /!./,
_foundCache: [],
_supportsNative: function() {
// whitelist and feature detection to manage
// future implementations manually
return ( (Y.UA.ie >= 8 || Y.UA.webkit > 525) &&
document.querySelectorAll);
},
_toArray: function(nodes) { // TODO: move to Y.Array
var ret = nodes,
i, len;
if (!nodes.slice) {
try {
ret = Array.prototype.slice.call(nodes);
} catch(e) { // IE: requires manual copy
ret = [];
for (i = 0, len = nodes.length; i < len; ++i) {
ret[i] = nodes[i];
}
}
}
return ret;
},
_clearFoundCache: function() {
var foundCache = NativeSelector._foundCache,
i, len;
for (i = 0, len = foundCache.length; i < len; ++i) {
try { // IE no like delete
delete foundCache[i]._found;
} catch(e) {
foundCache[i].removeAttribute('_found');
}
}
foundCache = [];
},
_sort: function(nodes) {
if (nodes) {
nodes = NativeSelector._toArray(nodes);
if (nodes.sort) {
nodes.sort(function(a, b) {
return Y.DOM.srcIndex(a) - Y.DOM.srcIndex(b);
});
}
}
return nodes;
},
_deDupe: function(nodes) {
var ret = [],
cache = NativeSelector._foundCache,
i, node;
for (i = 0, node; node = nodes[i++];) {
if (!node._found) {
ret[ret.length] = cache[cache.length] = node;
node._found = true;
}
}
NativeSelector._clearFoundCache();
return ret;
},
// allows element scoped queries to begin with combinator
// e.g. query('> p', document.body) === query('body > p')
_prepQuery: function(root, selector) {
var groups = selector.split(','),
queries = [],
isDocRoot = (root && root.nodeType === 9),
i, len;
if (root) {
if (!isDocRoot) {
root.id = root.id || Y.guid();
// break into separate queries for element scoping
for (i = 0, len = groups.length; i < len; ++i) {
selector = '#' + root.id + ' ' + groups[i]; // prepend with root ID
queries.push({root: root.ownerDocument, selector: selector});
}
} else {
queries.push({root: root, selector: selector});
}
}
return queries;
},
_query: function(selector, root, firstOnly) {
if (NativeSelector._reUnSupported.test(selector)) {
return Y.Selector._brute.query(selector, root, firstOnly);
}
var ret = firstOnly ? null : [],
queryName = firstOnly ? 'querySelector' : 'querySelectorAll',
result,
queries,
i, query;
root = root || Y.config.doc;
if (selector) {
queries = NativeSelector._prepQuery(root, selector);
ret = [];
for (i = 0, query; query = queries[i++];) {
try {
result = query.root[queryName](query.selector);
if (queryName === 'querySelectorAll') { // convert NodeList to Array
result = NativeSelector._toArray(result);
}
ret = ret.concat(result);
} catch(e) {
}
}
if (queries.length > 1) { // remove dupes and sort by doc order
ret = NativeSelector._sort(NativeSelector._deDupe(ret));
}
ret = (!firstOnly) ? ret : ret[0] || null;
}
return ret;
},
_filter: function(nodes, selector) {
var ret = [],
i, node;
if (nodes && selector) {
for (i = 0, node; (node = nodes[i++]);) {
if (Y.Selector._test(node, selector)) {
ret[ret.length] = node;
}
}
} else {
}
return ret;
},
_test: function(node, selector) {
var ret = false,
groups = selector.split(','),
id,
item,
i, group;
if (node && node.tagName) { // only test HTMLElements
node.id = node.id || Y.guid();
for (i = 0, group; group = groups[i++];) {
group += '#' + node.id; // add ID for uniqueness
item = Y.Selector.query(group, null, true);
ret = (item === node);
if (ret) {
break;
}
}
}
return ret;
}
};
if (Y.UA.ie && Y.UA.ie <= 8) {
NativeSelector._reUnSupported = /:(?:nth|not|root|only|checked|first|last|empty)/;
}
Y.mix(Y.Selector, NativeSelector, true);
// allow standalone selector-native module
if (NativeSelector._supportsNative()) {
Y.Selector.query = NativeSelector._query;
//Y.Selector.filter = NativeSelector._filter;
//Y.Selector.test = NativeSelector._test;
}
Y.Selector.test = NativeSelector._test;
Y.Selector.filter = NativeSelector._filter;
})(Y);
/**
* The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
* @module selector
* @title Selector Utility
* @requires yahoo, dom
*/
/**
* Provides helper methods for collecting and filtering DOM elements.
* @class Selector
* @static
*/
var PARENT_NODE = 'parentNode',
TAG_NAME = 'tagName',
ATTRIBUTES = 'attributes',
COMBINATOR = 'combinator',
PSEUDOS = 'pseudos',
PREVIOUS = 'previous',
PREVIOUS_SIBLING = 'previousSibling',
LENGTH = 'length',
_childCache = [], // cache to cleanup expando node.children
Selector = Y.Selector,
SelectorCSS2 = {
SORT_RESULTS: true,
_children: function(node) {
var ret = node.children,
i, n;
if (!ret && node[TAG_NAME]) { // only HTMLElements have children
ret = [];
for (i = 0, n; n = node.childNodes[i++];) {
if (n.tagName) {
ret[ret.length] = n;
}
}
_childCache[_childCache.length] = node;
node.children = ret;
}
return ret || [];
},
_regexCache: {},
_re: {
attr: /(\[.*\])/g,
urls: /^(?:href|src)/
},
/**
* 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, m) { return Y.DOM.getAttribute(node, m[0]) !== ''; }, // 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;
}
},
_brute: {
/**
* 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) {
var ret = [];
if (selector) {
ret = Selector._query(selector, root, firstOnly);
}
Selector._cleanup();
return (firstOnly) ? (ret[0] || null) : ret;
}
},
// TODO: make extensible? events?
_cleanup: function() {
for (var i = 0, node; node = _childCache[i++];) {
delete node.children;
}
_childCache = [];
},
_query: function(selector, root, firstOnly, deDupe) {
var ret = [],
groups = selector.split(','), // TODO: handle comma in attribute/pseudo
nodes = [],
tokens,
token,
i, len;
if (groups.length > 1) {
for (i = 0, len = groups.length; i < len; ++i) {
ret = ret.concat(arguments.callee(groups[i],
root, firstOnly, true));
}
ret = Selector.SORT_RESULTS ? Selector._sort(ret) : ret;
Selector._clearFoundCache();
} else {
root = root || Y.config.doc;
if (root.nodeType !== 9) { // enforce element scope
if (!root.id) {
root.id = Y.guid();
}
// fast-path ID when possible
if (root.ownerDocument.getElementById(root.id)) {
selector = '#' + root.id + ' ' + selector;
root = root.ownerDocument;
}
}
tokens = Selector._tokenize(selector, root);
token = tokens.pop();
if (token) {
if (deDupe) {
token.deDupe = true; // TODO: better approach?
}
if (tokens[0] && tokens[0].id && root.nodeType === 9 && root.getElementById(tokens[0].id)) {
root = root.getElementById(tokens[0].id);
}
// TODO: no prefilter for off-dom id
if (root && !nodes.length && token.prefilter) {
nodes = token.prefilter(root, token);
}
if (nodes.length) {
if (firstOnly) {
Y.Array.some(nodes, Selector._testToken, token);
} else {
Y.Array.each(nodes, Selector._testToken, token);
}
}
ret = token.result;
}
}
return ret;
},
_testToken: function(node, index, nodes, token) {
var token = token || this,
tag = token.tag,
previous = token[PREVIOUS],
result = token.result,
i = 0,
nextTest = previous && previous[COMBINATOR] ?
Selector.combinators[previous[COMBINATOR]] :
null,
test,
attr;
if (//node[TAG_NAME] && // tagName limits to HTMLElements
(tag === '*' || tag === node[TAG_NAME]) &&
!(token.last && node._found) ) {
while ((attr = token.tests[i])) {
i++;
test = attr.test;
if (test.test) {
if (!test.test(Y.DOM.getAttribute(node, attr.name))) {
return false;
}
} else if (!test(node, attr.match)) {
return false;
}
}
if (nextTest && !nextTest(node, token)) {
return false;
}
if (token.root && token.root.nodeType !== 9 && !Y.DOM.contains(token.root, node)) {
return false;
}
result[result.length] = node;
if (token.deDupe && token.last) {
node._found = true;
Selector._foundCache.push(node);
}
return true;
}
return false;
},
_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: {
' ': function(node, token) {
var test = Selector._testToken,
previous = token[PREVIOUS];
while ( (node = node[PARENT_NODE]) ) {
if (test(node, null, null, previous)) {
return true;
}
}
return false;
},
'>': function(node, token) {
return Selector._testToken(node[PARENT_NODE], null, null, token[PREVIOUS]);
},
'+': function(node, token) {
var sib = node[PREVIOUS_SIBLING];
while (sib && sib.nodeType !== 1) {
sib = sib[PREVIOUS_SIBLING];
}
if (sib && Y.Selector._testToken(sib, null, null, token[PREVIOUS])) {
return true;
}
return false;
}
},
_parsers: [
{
name: TAG_NAME,
re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
fn: function(token, match) {
token.tag = match[1].toUpperCase();
token.prefilter = function(root) {
return root.getElementsByTagName(token.tag);
};
return true;
}
},
{
name: ATTRIBUTES,
re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
fn: function(token, match) {
var val = match[3],
operator = !(match[2] && val) ? '' : match[2],
test = Selector.operators[operator];
// might be a function
if (typeof test === 'string') {
test = Selector._getRegExp(test.replace('{val}', val));
}
if (match[1] === 'id' && val) { // store ID for fast-path match
token.id = val;
token.prefilter = function(root) {
var doc = root.nodeType === 9 ? root : root.ownerDocument,
node = doc.getElementById(val);
return node ? [node] : [];
};
} else if (document.documentElement.getElementsByClassName &&
match[1].indexOf('class') === 0) {
if (!token.prefilter) {
token.prefilter = function(root) {
return root.getElementsByClassName(val);
};
test = true; // skip class test
}
}
return test;
}
},
{
name: COMBINATOR,
re: /^\s*([>+~]|\s)\s*/,
fn: function(token, match) {
token[COMBINATOR] = match[1];
return !! Selector.combinators[token[COMBINATOR]];
}
},
{
name: PSEUDOS,
re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
fn: function(token, match) {
return Selector[PSEUDOS][match[1]];
}
}
],
_getToken: function(token) {
return {
previous: token,
combinator: ' ',
tag: '*',
prefilter: function(root) {
return root.getElementsByTagName('*');
},
tests: [],
result: []
};
},
/**
Break selector into token units per simple selector.
Combinator is attached to the previous token.
*/
_tokenize: function(selector, root) {
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
test,
match, // the regex match
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; parser = Selector._parsers[i++];) {
if ( (match = parser.re.exec(selector)) ) { // note assignment
test = parser.fn(token, match);
if (test) {
if (test !== true) { // auto-pass
token.tests.push({
name: match[1],
test: test,
match: match.slice(1)
});
}
found = true;
selector = selector.replace(match[0], ''); // strip current match from selector
if (!selector.length || parser.name === COMBINATOR) {
token.root = root;
tokens.push(token);
token = Selector._getToken(token);
}
} else {
found = false;
break outer;
}
}
}
} while (found && selector.length);
if (!found || selector.length) { // not fully parsed
tokens = [];
} else if (tokens.length) {
tokens[tokens.length - 1].last = true;
}
return tokens;
},
_replaceShorthand: function(selector) {
var shorthand = Selector.shorthand,
attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
re, i, len;
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]);
}
}
return selector;
}
};
Y.mix(Y.Selector, SelectorCSS2, true);
// only override native when not supported
if (!Y.Selector._supportsNative()) {
Y.Selector.query = Selector._brute.query;
}
}, '@VERSION@' ,{requires:['dom-base'], skinnable:false});
YUI.add('dom', function(Y){}, '@VERSION@' ,{skinnable:false, use:['dom-base', 'dom-style', 'dom-screen', 'selector']});