dom-debug.js revision 14dc4f889a7b6c39f10aa490ea3c4c69de5e99bb
472cb5998fc636f7d3cb6e3780dba7986a75e912Luke SmithYUI.add('dom-base', function(Y) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore(function(Y) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore/**
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * The DOM utility provides a cross-browser abtraction layer
0a9c6f9f30a66e52ec4ea4ed93504580b3a5669aAdam Moore * normalizing DOM tasks, and adds extra helper functionality
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * for other common tasks.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @module dom
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @submodule dom-base
0a9c6f9f30a66e52ec4ea4ed93504580b3a5669aAdam Moore * @for DOM
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore *
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore */
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore/**
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * Provides DOM helper methods.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @class DOM
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore *
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore */
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moorevar NODE_TYPE = 'nodeType',
472cb5998fc636f7d3cb6e3780dba7986a75e912Luke Smith OWNER_DOCUMENT = 'ownerDocument',
472cb5998fc636f7d3cb6e3780dba7986a75e912Luke Smith DOCUMENT_ELEMENT = 'documentElement',
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith DEFAULT_VIEW = 'defaultView',
45e6f13936658ddeb74bff8dde445a67419a1995Luke Smith PARENT_WINDOW = 'parentWindow',
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore TAG_NAME = 'tagName',
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore PARENT_NODE = 'parentNode',
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore FIRST_CHILD = 'firstChild',
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore PREVIOUS_SIBLING = 'previousSibling',
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore NEXT_SIBLING = 'nextSibling',
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore CONTAINS = 'contains',
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore EMPTY_STRING = '',
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore documentElement = Y.config.doc.documentElement,
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore re_tag = /<([a-z]+)/i,
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore createFromDIV = function(html, tag) {
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith var div = Y.config.doc.createElement('div'),
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith ret = true;
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith div.innerHTML = html;
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith if (!div.firstChild || div.firstChild.tagName !== tag) {
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith ret = false;
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith }
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith return ret;
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith },
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke SmithY_DOM = {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore /**
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @method byId
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {String} id the id attribute
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {Object} doc optional The document to search. Defaults to current document
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore */
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore byId: function(id, doc) {
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore // handle dupe IDs and IE name collision
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore return Y_DOM.allById(id, doc)[0] || null;
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore },
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore /**
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * Returns the text content of the HTMLElement.
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @method getText
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @param {HTMLElement} element The html element.
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @return {String} The text content of the element (includes text of any descending elements).
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore */
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore getText: (documentElement.textContent !== undefined) ?
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore function(element) {
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore var ret = '';
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore if (element) {
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore ret = element.textContent;
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore }
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore return ret || '';
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore } : function(element) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore var ret = '';
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (element) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore ret = element.innerText || element.nodeValue; // might be a textNode
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore }
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore return ret || '';
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore /**
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * Sets the text content of the HTMLElement.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @method setText
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {HTMLElement} element The html element.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {String} content The content to add.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore */
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore setText: (documentElement.textContent !== undefined) ?
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore function(element, content) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (element) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore element.textContent = content;
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore }
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore } : function(element, content) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore if ('innerText' in element) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore element.innerText = content;
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore } else if ('nodeValue' in element) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore element.nodeValue = content;
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore }
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
057b95e2faede12bf00a82837632ae67e03ad0fcAdam Moore },
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore /*
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * Finds the ancestor of the element.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @method ancestor
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {HTMLElement} element The html element.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {Function} fn optional An optional boolean test to apply.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * The optional function is passed the current DOM node being tested as its only argument.
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * If no function is given, the parentNode is returned.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {Boolean} testSelf optional Whether or not to include the element in the scan
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @return {HTMLElement | null} The matching DOM node or null if none found.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore */
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ancestor: function(element, fn, testSelf) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore var ret = null;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (testSelf) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = (!fn || fn(element)) ? element : null;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore return ret || Y_DOM.elementByAxis(element, PARENT_NODE, fn, null);
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore },
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore /*
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * Finds the ancestors of the element.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @method ancestors
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {HTMLElement} element The html element.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {Function} fn optional An optional boolean test to apply.
057b95e2faede12bf00a82837632ae67e03ad0fcAdam Moore * The optional function is passed the current DOM node being tested as its only argument.
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * If no function is given, all ancestors are returned.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {Boolean} testSelf optional Whether or not to include the element in the scan
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @return {Array} An array containing all matching DOM nodes.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore */
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore ancestors: function(element, fn, testSelf) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore var ancestor = Y_DOM.ancestor.apply(Y_DOM, arguments),
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = (ancestor) ? [ancestor] : [];
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore while ((ancestor = Y_DOM.ancestor(ancestor, fn))) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (ancestor) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret.unshift(ancestor);
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore return ret;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore /**
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * Searches the element by the given axis for the first matching element.
057b95e2faede12bf00a82837632ae67e03ad0fcAdam Moore * @method elementByAxis
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @param {HTMLElement} element The html element.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {Function} fn optional An optional boolean test to apply.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * The optional function is passed the current HTMLElement being tested as its only argument.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * If no function is given, the first element is returned.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @return {HTMLElement | null} The matching element or null if none found.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore */
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore elementByAxis: function(element, axis, fn, all) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore while (element && (element = element[axis])) { // NOTE: assignment
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore return element;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore return null;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore /**
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * Determines whether or not one HTMLElement is or contains another HTMLElement.
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @method contains
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {HTMLElement} element The containing html element.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @param {HTMLElement} needle The html element that may be contained.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore * @return {Boolean} Whether or not the element is or contains the needle.
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore */
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore contains: function(element, needle) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore var ret = false;
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = false;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore } else if (element[CONTAINS]) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore ret = element[CONTAINS](needle);
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore } else {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = Y_DOM._bruteContains(element, needle);
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = true;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore return ret;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore /**
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * Determines whether or not the HTMLElement is part of the document.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @method inDoc
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {HTMLElement} element The containing html element.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {HTMLElement} doc optional The document to check.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @return {Boolean} Whether or not the element is attached to the document.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore */
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore inDoc: function(element, doc) {
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore var ret = false,
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore rootNode;
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore if (element && element.nodeType) {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore (doc) || (doc = element[OWNER_DOCUMENT]);
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore rootNode = doc[DOCUMENT_ELEMENT];
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore // contains only works with HTML_ELEMENT
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (rootNode && rootNode.contains && element.tagName) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = rootNode.contains(element);
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore } else {
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore ret = Y_DOM.contains(rootNode, element);
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore return ret;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore allById: function(id, root) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore root = root || Y.config.doc;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore var nodes = [],
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = [],
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore i,
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore node;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (root.querySelectorAll) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = root.querySelectorAll('[id="' + id + '"]');
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore } else if (root.all) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore nodes = root.all(id);
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (nodes && nodes.nodeType) { // root.all may return one or many
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore nodes = [nodes];
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (nodes && nodes.length) {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore for (i = 0; node = nodes[i++];) { // check for a match
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (node.attributes && node.attributes.id
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore && node.attributes.id.value === id) { // avoid false positive for node.name & form.id
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore ret.push(node);
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore } else {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore ret = [Y_DOM._getDoc(root).getElementById(id)];
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore return ret;
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore },
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore /**
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore * Creates a new dom node using the provided markup string.
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore * @method create
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore * @param {String} html The markup used to create the element
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore * @param {HTMLDocument} doc An optional document context
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore * @return {HTMLElement|DocumentFragment} returns a single HTMLElement
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore * when creating one node, and a documentFragment when creating
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore * multiple nodes.
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore */
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore create: function(html, doc) {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (typeof html === 'string') {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
111837fe5c14f516f4a15878f9bbaf7bb0091364Adam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore doc = doc || Y.config.doc;
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore var m = re_tag.exec(html),
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore create = Y_DOM._create,
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore custom = Y_DOM.creators,
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore ret = null,
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore tag, nodes;
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (html != undefined) { // not undefined or null
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (m && custom[m[1]]) {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (typeof custom[m[1]] === 'function') {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore create = custom[m[1]];
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore } else {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore tag = custom[m[1]];
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore nodes = create(html, doc, tag).childNodes;
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore ret = nodes[0].parentNode.removeChild(nodes[0]);
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected)
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore if (nodes.length === 2) {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore ret = nodes[0].nextSibling;
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore } else {
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore nodes[0].parentNode.removeChild(nodes[0]);
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore ret = Y_DOM._nl2frag(nodes, doc);
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore } else { // return multiple nodes as a fragment
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore ret = Y_DOM._nl2frag(nodes, doc);
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore }
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore
d1f171a81a8b50c0f694f3dd1ea7ccc08e86cf55Adam Moore return ret;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore _nl2frag: function(nodes, doc) {
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore var ret = null,
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore i, len;
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore if (nodes && (nodes.push || nodes.item) && nodes[0]) {
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore doc = doc || nodes[0].ownerDocument;
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore ret = doc.createDocumentFragment();
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (nodes.item) { // convert live list to static array
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore nodes = Y.Array(nodes, 0, true);
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore }
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore for (i = 0, len = nodes.length; i < len; i++) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret.appendChild(nodes[i]);
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore } // else inline with log for minification
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore else { Y.log('unable to convert ' + nodes + ' to fragment', 'warn', 'dom'); }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore return ret;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore 'for': 'htmlFor',
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore 'class': 'className'
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore } : { // w3c
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore 'htmlFor': 'for',
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore 'className': 'class'
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore },
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore /**
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * Provides a normalized attribute interface.
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @method setAttibute
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * @param {String | HTMLElement} el The target element for the attribute.
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore * @param {String} attr The attribute to set.
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore * @param {String} val The value of the attribute.
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore */
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore setAttribute: function(el, attr, val, ieAttr) {
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore if (el && attr && el.setAttribute) {
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore el.setAttribute(attr, val, ieAttr);
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore else { Y.log('bad input to setAttribute', 'warn', 'dom'); }
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore },
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore
6a3faa9e0e4639febffbd7018ce47b861626d0baAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore /**
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * Provides a normalized attribute interface.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @method getAttibute
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {String | HTMLElement} el The target element for the attribute.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {String} attr The attribute to get.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @return {String} The current value of the attribute.
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore */
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore getAttribute: function(el, attr, ieAttr) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore var ret = '';
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (el && attr && el.getAttribute) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = el.getAttribute(attr, ieAttr);
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (ret === null) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = ''; // per DOM spec
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore }
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore else { Y.log('bad input to getAttribute', 'warn', 'dom'); }
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore return ret;
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore },
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore isWindow: function(obj) {
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore return !!(obj && obj.alert && obj.document);
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore },
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore _fragClones: {},
df39d9c816a02c79aa6a3436784de5bba0ef7075Adam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore _create: function(html, doc, tag) {
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore tag = tag || 'div';
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore var frag = Y_DOM._fragClones[tag];
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore if (frag) {
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore frag = frag.cloneNode(false);
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore } else {
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore frag = Y_DOM._fragClones[tag] = doc.createElement(tag);
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore }
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore frag.innerHTML = html;
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore return frag;
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore },
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore _removeChildNodes: function(node) {
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore while (node.firstChild) {
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore node.removeChild(node.firstChild);
03f9aefec605c500b64625110a955e65b900b100Adam Moore }
03f9aefec605c500b64625110a955e65b900b100Adam Moore },
03f9aefec605c500b64625110a955e65b900b100Adam Moore
03f9aefec605c500b64625110a955e65b900b100Adam Moore /**
03f9aefec605c500b64625110a955e65b900b100Adam Moore * Inserts content in a node at the given location
03f9aefec605c500b64625110a955e65b900b100Adam Moore * @method addHTML
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore * @param {HTMLElement} node The node to insert into
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore * @param {String | HTMLElement | Array | HTMLCollection} content The content to be inserted
03f9aefec605c500b64625110a955e65b900b100Adam Moore * @param {String | HTMLElement} where Where to insert the content
03f9aefec605c500b64625110a955e65b900b100Adam Moore * If no "where" is given, content is appended to the node
03f9aefec605c500b64625110a955e65b900b100Adam Moore * Possible values for "where"
03f9aefec605c500b64625110a955e65b900b100Adam Moore * <dl>
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * <dt>HTMLElement</dt>
03f9aefec605c500b64625110a955e65b900b100Adam Moore * <dd>The element to insert before</dd>
03f9aefec605c500b64625110a955e65b900b100Adam Moore * <dt>"replace"</dt>
03f9aefec605c500b64625110a955e65b900b100Adam Moore * <dd>Replaces the existing HTML</dd>
03f9aefec605c500b64625110a955e65b900b100Adam Moore * <dt>"before"</dt>
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore * <dd>Inserts before the existing HTML</dd>
03f9aefec605c500b64625110a955e65b900b100Adam Moore * <dt>"before"</dt>
03f9aefec605c500b64625110a955e65b900b100Adam Moore * <dd>Inserts content before the node</dd>
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore * <dt>"after"</dt>
f89b4dd628000da1b003539c3c181e6b9880de00Adam Moore * <dd>Inserts content after the node</dd>
03f9aefec605c500b64625110a955e65b900b100Adam Moore * </dl>
2f03ba9e07559709925bfe6cb1b40c83aa810672Adam Moore */
f89b4dd628000da1b003539c3c181e6b9880de00Adam Moore addHTML: function(node, content, where) {
f89b4dd628000da1b003539c3c181e6b9880de00Adam Moore var nodeParent = node.parentNode,
f89b4dd628000da1b003539c3c181e6b9880de00Adam Moore i = 0,
f89b4dd628000da1b003539c3c181e6b9880de00Adam Moore item,
03f9aefec605c500b64625110a955e65b900b100Adam Moore ret = content,
f89b4dd628000da1b003539c3c181e6b9880de00Adam Moore newNode;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (content != undefined) { // not null or undefined (maybe 0)
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore if (content.nodeType) { // DOM node, just add it
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore newNode = content;
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore } else if (typeof content == 'string' || typeof content == 'number') {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore ret = newNode = Y_DOM.create(content);
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore } else if (content[0] && content[0].nodeType) { // array or collection
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore newNode = Y.config.doc.createDocumentFragment();
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore while ((item = content[i++])) {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore newNode.appendChild(item); // append to fragment for insertion
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore if (where) {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore if (where.nodeType) { // insert regardless of relationship to node
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore where.parentNode.insertBefore(newNode, where);
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore } else {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore switch (where) {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore case 'replace':
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore while (node.firstChild) {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore node.removeChild(node.firstChild);
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (newNode) { // allow empty content to clear node
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore node.appendChild(newNode);
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore break;
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore case 'before':
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore nodeParent.insertBefore(newNode, node);
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore break;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore case 'after':
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore if (node.nextSibling) { // IE errors if refNode is null
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore nodeParent.insertBefore(newNode, node.nextSibling);
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore } else {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore nodeParent.appendChild(newNode);
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore break;
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore default:
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore node.appendChild(newNode);
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore } else if (newNode) {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore node.appendChild(newNode);
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore }
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore return ret;
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore },
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore VALUE_SETTERS: {},
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore VALUE_GETTERS: {},
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore getValue: function(node) {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore var ret = '', // TODO: return null?
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore getter;
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore if (node && node[TAG_NAME]) {
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore getter = Y_DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (getter) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = getter(node);
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore } else {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore ret = node.value;
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore }
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore // workaround for IE8 JSON stringify bug
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore // which converts empty string values to null
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore if (ret === EMPTY_STRING) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore ret = EMPTY_STRING; // for real
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore }
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore return (typeof ret === 'string') ? ret : '';
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore },
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore setValue: function(node, val) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore var setter;
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore if (node && node[TAG_NAME]) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore setter = Y_DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore if (setter) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore setter(node, val);
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore } else {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore node.value = val;
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore }
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore }
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore },
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore siblings: function(node, fn) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore var nodes = [],
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore sibling = node;
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore while ((sibling = sibling[PREVIOUS_SIBLING])) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore if (sibling[TAG_NAME] && (!fn || fn(sibling))) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore nodes.unshift(sibling);
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore }
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore }
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore sibling = node;
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore while ((sibling = sibling[NEXT_SIBLING])) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore if (sibling[TAG_NAME] && (!fn || fn(sibling))) {
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore nodes.push(sibling);
5cbcc8e7f5c3e4ad283e5cb76520840300f81a0aAdam Moore }
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore }
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore return nodes;
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore },
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore /**
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore * Brute force version of contains.
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore * @method _bruteContains
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore * @private
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore * @param {HTMLElement} element The containing html element.
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore * @param {HTMLElement} needle The html element that may be contained.
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore * @return {Boolean} Whether or not the element is or contains the needle.
f6baa527839e75655081768c365a749c59edc80dAdam Moore */
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore _bruteContains: function(element, needle) {
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore while (needle) {
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore if (element === needle) {
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore return true;
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore }
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore needle = needle.parentNode;
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore }
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore return false;
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore },
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore// TODO: move to Lang?
38ede344a04d04daedd560a485bd38f50e4c0a71Adam Moore /**
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * Memoizes dynamic regular expressions to boost runtime performance.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @method _getRegExp
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @private
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @param {String} str The string to convert to a regular expression.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @param {String} flags optional An optinal string of flags.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @return {RegExp} An instance of RegExp
f841387cb4997959ecf710977b259b86f959ba48Luke Smith */
f841387cb4997959ecf710977b259b86f959ba48Luke Smith _getRegExp: function(str, flags) {
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith flags = flags || '';
f328401955235416edeee0c5ac3ae9fb6dee940aLuke Smith Y_DOM._regexCache = Y_DOM._regexCache || {};
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith if (!Y_DOM._regexCache[str + flags]) {
f0a82c6a29c12f3a85bdc740203affbbe75ce665Luke Smith Y_DOM._regexCache[str + flags] = new RegExp(str, flags);
f841387cb4997959ecf710977b259b86f959ba48Luke Smith }
f841387cb4997959ecf710977b259b86f959ba48Luke Smith return Y_DOM._regexCache[str + flags];
f841387cb4997959ecf710977b259b86f959ba48Luke Smith },
f841387cb4997959ecf710977b259b86f959ba48Luke Smith
f841387cb4997959ecf710977b259b86f959ba48Luke Smith// TODO: make getDoc/Win true privates?
f841387cb4997959ecf710977b259b86f959ba48Luke Smith /**
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * returns the appropriate document.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @method _getDoc
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @private
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @param {HTMLElement} element optional Target element.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @return {Object} The document for the given element or the default document.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith */
f841387cb4997959ecf710977b259b86f959ba48Luke Smith _getDoc: function(element) {
f841387cb4997959ecf710977b259b86f959ba48Luke Smith var doc = Y.config.doc;
f841387cb4997959ecf710977b259b86f959ba48Luke Smith if (element) {
f841387cb4997959ecf710977b259b86f959ba48Luke Smith doc = (element[NODE_TYPE] === 9) ? element : // element === document
f841387cb4997959ecf710977b259b86f959ba48Luke Smith element[OWNER_DOCUMENT] || // element === DOM node
f841387cb4997959ecf710977b259b86f959ba48Luke Smith element.document || // element === window
f841387cb4997959ecf710977b259b86f959ba48Luke Smith Y.config.doc; // default
f841387cb4997959ecf710977b259b86f959ba48Luke Smith }
f841387cb4997959ecf710977b259b86f959ba48Luke Smith
f841387cb4997959ecf710977b259b86f959ba48Luke Smith return doc;
f841387cb4997959ecf710977b259b86f959ba48Luke Smith },
f841387cb4997959ecf710977b259b86f959ba48Luke Smith
f841387cb4997959ecf710977b259b86f959ba48Luke Smith /**
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * returns the appropriate window.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @method _getWin
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @private
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @param {HTMLElement} element optional Target element.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith * @return {Object} The window for the given element or the default window.
f841387cb4997959ecf710977b259b86f959ba48Luke Smith */
f841387cb4997959ecf710977b259b86f959ba48Luke Smith _getWin: function(element) {
f841387cb4997959ecf710977b259b86f959ba48Luke Smith var doc = Y_DOM._getDoc(element);
f841387cb4997959ecf710977b259b86f959ba48Luke Smith return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
f841387cb4997959ecf710977b259b86f959ba48Luke Smith },
f841387cb4997959ecf710977b259b86f959ba48Luke Smith
f841387cb4997959ecf710977b259b86f959ba48Luke Smith _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
f841387cb4997959ecf710977b259b86f959ba48Luke Smith fn = (typeof fn === 'string') ? Y_DOM[fn] : fn;
f841387cb4997959ecf710977b259b86f959ba48Luke Smith var result,
f841387cb4997959ecf710977b259b86f959ba48Luke Smith args = Array.prototype.slice.call(arguments, 2),
f841387cb4997959ecf710977b259b86f959ba48Luke Smith i = 0,
f841387cb4997959ecf710977b259b86f959ba48Luke Smith node,
f841387cb4997959ecf710977b259b86f959ba48Luke Smith ret;
f841387cb4997959ecf710977b259b86f959ba48Luke Smith
f841387cb4997959ecf710977b259b86f959ba48Luke Smith if (fn && nodes) {
f841387cb4997959ecf710977b259b86f959ba48Luke Smith while ((node = nodes[i++])) {
f841387cb4997959ecf710977b259b86f959ba48Luke Smith result = result = fn.call(Y_DOM, node, arg1, arg2, arg3, etc);
f841387cb4997959ecf710977b259b86f959ba48Luke Smith if (typeof result !== 'undefined') {
f841387cb4997959ecf710977b259b86f959ba48Luke Smith (ret) || (ret = []);
f841387cb4997959ecf710977b259b86f959ba48Luke Smith ret.push(result);
f841387cb4997959ecf710977b259b86f959ba48Luke Smith }
f841387cb4997959ecf710977b259b86f959ba48Luke Smith }
f841387cb4997959ecf710977b259b86f959ba48Luke Smith }
f841387cb4997959ecf710977b259b86f959ba48Luke Smith
f841387cb4997959ecf710977b259b86f959ba48Luke Smith return (typeof ret !== 'undefined') ? ret : nodes;
bc8660dcb747bedc141ca9061de83f6f32f8018fAdam Moore },
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore wrap: function(node, html) {
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore var parent = Y.DOM.create(html),
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore nodes = parent.getElementsByTagName('*');
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (nodes.length) {
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore parent = nodes[nodes.length - 1];
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore }
772f655fcf57e58b97fa46b6a8d3fc772b83f743Adam Moore
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore if (node.parentNode) {
3641f0baf10c9737e4ac6aac1566bfeaca00eeffAdam Moore node.parentNode.replaceChild(parent, node);
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore }
e69255aa5a65f8406ba2fabaf69fe4e1d05daf69Adam Moore parent.appendChild(node);
},
unwrap: function(node) {
var parent = node.parentNode,
lastChild = parent.lastChild,
node = parent.firstChild,
next = node,
grandparent;
if (parent) {
grandparent = parent.parentNode;
if (grandparent) {
while (node !== lastChild) {
next = node.nextSibling;
grandparent.insertBefore(node, parent);
node = next;
}
grandparent.replaceChild(lastChild, parent);
} else {
parent.removeChild(node);
}
}
},
creators: {}
};
(function(Y) {
var creators = Y_DOM.creators,
create = Y_DOM.create,
re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
TABLE_OPEN = '<table>',
TABLE_CLOSE = '</table>';
if (Y.UA.ie) {
Y.mix(creators, {
// TODO: thead/tfoot with nested tbody
// IE adds TBODY when creating TABLE elements (which may share this impl)
tbody: function(html, doc) {
var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
tb = frag.children.tags('tbody')[0];
if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
}
return frag;
},
script: function(html, doc) {
var frag = doc.createElement('div');
frag.innerHTML = '-' + html;
frag.removeChild(frag[FIRST_CHILD]);
return frag;
}
}, true);
Y.mix(Y_DOM.VALUE_GETTERS, {
button: function(node) {
return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
}
});
Y.mix(Y_DOM.VALUE_SETTERS, {
// IE: node.value changes the button text, which should be handled via innerHTML
button: function(node, val) {
var attr = node.attributes.value;
if (!attr) {
attr = node[OWNER_DOCUMENT].createAttribute('value');
node.setAttributeNode(attr);
}
attr.value = val;
},
select: function(node, val) {
for (var i = 0, options = node.getElementsByTagName('option'), option;
option = options[i++];) {
if (Y_DOM.getValue(option) === val) {
Y_DOM.setAttribute(option, 'selected', true);
break;
}
}
}
});
Y_DOM.creators.col = Y_DOM.creators.link = Y_DOM.creators.style = Y_DOM.creators.script;
}
if (!createFromDIV('<tr/>', 'TR')) {
Y.mix(creators, {
option: function(html, doc) {
return create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc);
},
tr: function(html, doc) {
return create('<tbody>' + html + '</tbody>', doc);
},
td: function(html, doc) {
return create('<tr>' + html + '</tr>', doc);
},
col: function(html, doc) {
return create('<colgroup>' + html + '</colgroup>', doc);
},
tbody: 'table'
});
Y.mix(creators, {
legend: 'fieldset',
th: creators.td,
thead: creators.tbody,
tfoot: creators.tbody,
caption: creators.tbody,
colgroup: 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 && options.length && val === '') {
// TODO: implement multipe select
if (node.multiple) {
Y.log('multiple select normalization not implemented', 'warn', 'DOM');
} else {
val = Y_DOM.getValue(options[node.selectedIndex]);
}
}
return val;
}
});
})(Y);
Y.DOM = Y_DOM;
})(Y);
var addClass, hasClass, removeClass;
Y.mix(Y.DOM, {
/**
* Determines whether a DOM element has the given className.
* @method hasClass
* @for DOM
* @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
* @for DOM
* @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
* @for DOM
* @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
* @for DOM
* @param {HTMLElement} element The DOM element
* @param {String} oldClassName the class name to be replaced
* @param {String} newClassName the class name that will be replacing the old class name
*/
replaceClass: function(node, oldC, newC) {
//Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node');
removeClass(node, oldC); // remove first in case oldC === newC
addClass(node, newC);
},
/**
* If the className exists on the node it is removed, if it doesn't exist it is added.
* @method toggleClass
* @for DOM
* @param {HTMLElement} element The DOM element
* @param {String} className the class name to be toggled
* @param {Boolean} addClass optional boolean to indicate whether class
* should be added or removed regardless of current state
*/
toggleClass: function(node, className, force) {
var add = (force !== undefined) ? force :
!(hasClass(node, className));
if (add) {
addClass(node, className);
} else {
removeClass(node, className);
}
}
});
hasClass = Y.DOM.hasClass;
removeClass = Y.DOM.removeClass;
addClass = Y.DOM.addClass;
Y.mix(Y.DOM, {
/**
* Sets the width of the element to the given size, regardless
* of box model, border, padding, etc.
* @method setWidth
* @param {HTMLElement} element The DOM element.
* @param {String|Int} size The pixel height to size to
*/
setWidth: function(node, size) {
Y.DOM._setSize(node, 'width', size);
},
/**
* Sets the height of the element to the given size, regardless
* of box model, border, padding, etc.
* @method setHeight
* @param {HTMLElement} element The DOM element.
* @param {String|Int} size The pixel height to size to
*/
setHeight: function(node, size) {
Y.DOM._setSize(node, 'height', size);
},
_setSize: function(node, prop, val) {
val = (val > 0) ? val : 0;
var size = 0;
node.style[prop] = val + 'px';
size = (prop === 'height') ? node.offsetHeight : node.offsetWidth;
if (size > val) {
val = val - (size - val);
if (val < 0) {
val = 0;
}
node.style[prop] = val + 'px';
}
}
});
}, '@VERSION@' ,{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',
GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
DOCUMENT = Y.config.doc,
UNDEFINED = undefined,
Y_DOM = Y.DOM,
TRANSFORM = 'transform',
VENDOR_TRANSFORM = [
'WebkitTransform',
'MozTransform',
'OTransform'
],
re_color = /color$/i,
re_unit = /width|height|top|left|right|bottom|margin|padding/i;
Y.Array.each(VENDOR_TRANSFORM, function(val) {
if (val in DOCUMENT[DOCUMENT_ELEMENT].style) {
TRANSFORM = val;
}
});
Y.mix(Y_DOM, {
DEFAULT_UNIT: 'px',
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,
current;
if (style) {
if (val === null || val === '') { // normalize unsetting
val = '';
} else if (!isNaN(new Number(val)) && re_unit.test(att)) { // number values may need a unit
val += Y_DOM.DEFAULT_UNIT;
}
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, style) {
style = style || node.style;
var 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] && doc[DEFAULT_VIEW] && doc[DEFAULT_VIEW][GET_COMPUTED_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.DOM._getAttrOffset = function(node, attr) {
var val = Y.DOM[GET_COMPUTED_STYLE](node, attr),
offsetParent = node.offsetParent,
position,
parentOffset,
offset;
if (val === 'auto') {
position = Y.DOM.getStyle(node, 'position');
if (position === 'static' || position === 'relative') {
val = 0;
} else if (offsetParent && offsetParent[GET_BOUNDING_CLIENT_RECT]) {
parentOffset = offsetParent[GET_BOUNDING_CLIENT_RECT]()[attr];
offset = node[GET_BOUNDING_CLIENT_RECT]()[attr];
if (attr === 'left' || attr === 'top') {
val = offset - parentOffset;
} else {
val = parentOffset - node[GET_BOUNDING_CLIENT_RECT]()[attr];
}
}
}
return val;
};
Y.DOM._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;
};
Y_DOM.CUSTOM_STYLES.transform = {
set: function(node, val, style) {
style[TRANSFORM] = val;
},
get: function(node, style) {
return Y_DOM[GET_COMPUTED_STYLE](node, TRANSFORM);
}
};
})(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] = '0' + val[i];
}
}
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.toUpperCase();
}
};
})(Y);
}, '@VERSION@' ,{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',
Y_DOM = Y.DOM,
// TODO: how about thead/tbody/tfoot/tr?
// TODO: does caption matter?
RE_TABLE = /^t(?:able|d|h)$/i,
SCROLL_NODE;
if (Y.UA.ie) {
if (Y.config.doc[COMPAT_MODE] !== 'quirks') {
SCROLL_NODE = DOCUMENT_ELEMENT;
} else {
SCROLL_NODE = 'body';
}
}
Y.mix(Y_DOM, {
/**
* Returns the inner height of the viewport (exludes scrollbar).
* @method winHeight
* @return {Number} The current height of the viewport.
*/
winHeight: function(node) {
var h = Y_DOM._getWinSize(node).height;
Y.log('winHeight returning ' + h, 'info', 'dom-screen');
return h;
},
/**
* Returns the inner width of the viewport (exludes scrollbar).
* @method winWidth
* @return {Number} The current width of the viewport.
*/
winWidth: function(node) {
var w = Y_DOM._getWinSize(node).width;
Y.log('winWidth returning ' + w, 'info', 'dom-screen');
return w;
},
/**
* Document height
* @method docHeight
* @return {Number} The current height of the document.
*/
docHeight: function(node) {
var h = Y_DOM._getDocSize(node).height;
Y.log('docHeight returning ' + h, 'info', 'dom-screen');
return Math.max(h, Y_DOM._getWinSize(node).height);
},
/**
* Document width
* @method docWidth
* @return {Number} The current width of the document.
*/
docWidth: function(node) {
var w = Y_DOM._getDocSize(node).width;
Y.log('docWidth returning ' + w, 'info', 'dom-screen');
return Math.max(w, Y_DOM._getWinSize(node).width);
},
/**
* Amount page has been scroll horizontally
* @method docScrollX
* @return {Number} The current amount the screen is scrolled horizontally.
*/
docScrollX: function(node, doc) {
doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
var dv = doc.defaultView,
pageOffset = (dv) ? dv.pageXOffset : 0;
return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft, pageOffset);
},
/**
* Amount page has been scroll vertically
* @method docScrollY
* @return {Number} The current amount the screen is scrolled vertically.
*/
docScrollY: function(node, doc) {
doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc; // perf optimization
var dv = doc.defaultView,
pageOffset = (dv) ? dv.pageYOffset : 0;
return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop, pageOffset);
},
/**
* 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 (Y.config.doc[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
return function(node) {
var xy = null,
scrollLeft,
scrollTop,
box,
off1, off2,
bLeft, bTop,
mode,
doc,
inDoc,
rootNode;
if (node && node.tagName) {
doc = node.ownerDocument;
rootNode = doc[DOCUMENT_ELEMENT];
// inline inDoc check for perf
if (rootNode.contains) {
inDoc = rootNode.contains(node);
} else {
inDoc = Y.DOM.contains(rootNode, node);
}
if (inDoc) {
scrollLeft = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollLeft : Y_DOM.docScrollX(node, doc);
scrollTop = (SCROLL_NODE) ? doc[SCROLL_NODE].scrollTop : Y_DOM.docScrollY(node, doc);
box = node[GET_BOUNDING_CLIENT_RECT]();
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)) {
if (!Y.UA.ios) {
xy[0] += scrollLeft;
xy[1] += scrollTop;
}
}
} else {
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,
doc,
parentNode,
bCheck,
scrollTop,
scrollLeft;
if (node) {
if (Y_DOM.inDoc(node)) {
xy = [node.offsetLeft, node.offsetTop];
doc = node.ownerDocument;
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, doc);
xy[1] += Y_DOM.docScrollY(node, doc);
} else {
//Fix FIXED position -- add scrollbars
xy[0] += Y_DOM.docScrollX(node, doc);
xy[1] += Y_DOM.docScrollY(node, doc);
}
} else {
xy = Y_DOM._getOffset(node);
}
}
return xy;
};
}
}(),// NOTE: Executing for loadtime branching
/**
* Gets the current X position of an element based on page coordinates.
* Element must be part of the DOM tree to have page coordinates
* (display:none or elements not appended return false).
* @method getX
* @param element The target element
* @return {Int} The X position of the element
*/
getX: function(node) {
return Y_DOM.getXY(node)[0];
},
/**
* Gets the current Y position of an element based on page coordinates.
* Element must be part of the DOM tree to have page coordinates
* (display:none or elements not appended return false).
* @method getY
* @param element The target element
* @return {Int} The Y position of the element
*/
getY: function(node) {
return Y_DOM.getXY(node)[1];
},
/**
* Set the position of an html element in page coordinates.
* The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
* @method setXY
* @param element The target element
* @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
* @param {Boolean} noRetry By default we try and set the position a second time if the first fails
*/
setXY: function(node, xy, noRetry) {
var setStyle = Y_DOM.setStyle,
pos,
delta,
newXY,
currentXY;
if (node && xy) {
pos = Y_DOM.getStyle(node, POSITION);
delta = Y_DOM._getOffset(node);
if (pos == 'static') { // default to relative
pos = RELATIVE;
setStyle(node, POSITION, pos);
}
currentXY = Y_DOM.getXY(node);
if (xy[0] !== null) {
setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
}
if (xy[1] !== null) {
setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
}
if (!noRetry) {
newXY = Y_DOM.getXY(node);
if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
Y_DOM.setXY(node, xy, true);
}
}
Y.log('setXY setting position to ' + xy, 'info', 'dom-screen');
} else {
Y.log('setXY failed to set ' + node + ' to ' + xy, 'info', 'dom-screen');
}
},
/**
* Set the X position of an html element in page coordinates, regardless of how the element is positioned.
* The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
* @method setX
* @param element The target element
* @param {Int} x The X values for new position (coordinates are page-based)
*/
setX: function(node, x) {
return Y_DOM.setXY(node, [x, null]);
},
/**
* Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
* The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
* @method setY
* @param element The target element
* @param {Int} y The Y values for new position (coordinates are page-based)
*/
setY: function(node, y) {
return Y_DOM.setXY(node, [null, y]);
},
/**
* @method swapXY
* @description Swap the xy position with another node
* @param {Node} node The node to swap with
* @param {Node} otherNode The other node to swap with
* @return {Node}
*/
swapXY: function(node, otherNode) {
var xy = Y_DOM.getXY(node);
Y_DOM.setXY(node, Y_DOM.getXY(otherNode));
Y_DOM.setXY(otherNode, xy);
},
_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, doc) {
doc = doc || (node) ? Y_DOM._getDoc(node) : Y.config.doc;
var 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 = (node) ? Y_DOM._getDoc(node) : Y.config.doc,
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)
* @for DOM
* @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
* @for DOM
* @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
* @for DOM
* @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
* @for DOM
* @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
* @for DOM
* @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', 'event-base']});
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';
var Selector = {
_foundCache: [],
useNative: true,
_compare: ('sourceIndex' in Y.config.doc.documentElement) ?
function(nodeA, nodeB) {
var a = nodeA.sourceIndex,
b = nodeB.sourceIndex;
if (a === b) {
return 0;
} else if (a > b) {
return 1;
}
return -1;
} : (Y.config.doc.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 && Y.config.doc.querySelector && !skipNative),
queries = [[selector, root]],
query,
result,
i,
fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
if (selector && fn) {
// split group into seperate queries
if (!skipNative && // already done if skipping
(!useNative || root.tagName)) { // split native when element scoping is needed
queries = Selector._splitQueries(selector, root);
}
for (i = 0; (query = queries[i++]);) {
result = fn(query[0], query[1], firstOnly);
if (!firstOnly) { // coerce DOM Collection to Array
result = Y.Array(result, 0, true);
}
if (result) {
ret = ret.concat(result);
}
}
if (queries.length > 1) { // remove dupes and sort by doc order
ret = Selector._sort(Selector._deDupe(ret));
}
}
Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
return (firstOnly) ? (ret[0] || null) : ret;
},
// allows element scoped queries to begin with combinator
// e.g. query('> p', document.body) === query('body > p')
_splitQueries: function(selector, node) {
var groups = selector.split(','),
queries = [],
prefix = '',
i, len;
if (node) {
// enforce for element scoping
if (node.tagName) {
node.id = node.id || Y.guid();
prefix = '[id="' + 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) {
if (Y.UA.webkit && selector.indexOf(':checked') > -1 &&
(Y.Selector.pseudos && Y.Selector.pseudos.checked)) { // webkit (chrome, safari) fails to find "selected"
return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
}
try {
//Y.log('trying native query with: ' + selector, 'info', 'selector-native');
return root['querySelector' + (one ? '' : 'All')](selector);
} catch(e) { // fallback to brute if available
//Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
return Y.Selector.query(selector, root, one, true); // redo with skipNative true
}
},
filter: function(nodes, selector) {
var ret = [],
i, node;
if (nodes && selector) {
for (i = 0; (node = nodes[i++]);) {
if (Y.Selector.test(node, selector)) {
ret[ret.length] = node;
}
}
} else {
Y.log('invalid filter input (nodes: ' + nodes +
', selector: ' + selector + ')', 'warn', 'Selector');
}
return ret;
},
test: function(node, selector, root) {
var ret = false,
groups = selector.split(','),
useFrag = false,
parent,
item,
items,
frag,
i, j, group;
if (node && node.tagName) { // only test HTMLElements
// we need a root if off-doc
if (!root && !Y.DOM.inDoc(node)) {
parent = node.parentNode;
if (parent) {
root = parent;
} else { // only use frag when no parent to query
frag = node[OWNER_DOCUMENT].createDocumentFragment();
frag.appendChild(node);
root = frag;
useFrag = true;
}
}
root = root || node[OWNER_DOCUMENT];
if (!node.id) {
node.id = Y.guid();
}
for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
group += '[id="' + node.id + '"]';
items = Y.Selector.query(group, root);
for (j = 0; item = items[j++];) {
if (item === node) {
ret = true;
break;
}
}
if (ret) {
break;
}
}
if (useFrag) { // cleanup
frag.removeChild(node);
}
}
return ret;
},
/**
* A convenience function to emulate Y.Node's aNode.ancestor(selector).
* @param {HTMLElement} element An HTMLElement to start the query from.
* @param {String} selector The CSS selector to test the node against.
* @return {HTMLElement} The ancestor node matching the selector, or null.
* @param {Boolean} testSelf optional Whether or not to include the element in the scan
* @static
* @method ancestor
*/
ancestor: function (element, selector, testSelf) {
return Y.DOM.ancestor(element, function(n) {
return Y.Selector.test(n, selector);
}, testSelf);
}
};
Y.mix(Y.Selector, Selector, true);
})(Y);
}, '@VERSION@' ,{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 = {
_reRegExpTokens: /([\^\$\?\[\]\*\+\-\.\(\)\|\\])/, // TODO: move?
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 || [];
},
_re: {
//attr: /(\[.*\])/g,
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),
child,
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 || '*';
if (root.getElementsByTagName) { // non-IE lacks DOM api on doc frags
// try ID first, unless no root.all && root not in document
// (root.all works off document, but not getElementById)
// TODO: move to allById?
if (id && (root.all || (root.nodeType === 9 || Y.DOM.inDoc(root)))) {
nodes = Y.DOM.allById(id, root);
// try className
} else if (className) {
nodes = root.getElementsByClassName(className);
} else { // default to tagName
nodes = root.getElementsByTagName(tagName);
}
} else { // brute getElementsByTagName('*')
child = root.firstChild;
while (child) {
if (child.tagName) { // only collect HTMLElements
nodes.push(child);
}
child = child.nextSilbing || child.firstChild;
}
}
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
(typeof operator !== 'string' && // protect against String.test monkey-patch (Moo)
operator.test && !operator.test(value)) || // regex test
(!operator.test && // protect against RegExp as function (webkit)
typeof operator === 'function' && !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;
},
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' &&
Y.config.doc.documentElement.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') {
match[3] = match[3].replace(Y.Selector._reRegExpTokens, '\\$1');
test = Y.DOM._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.name !== COMBINATOR ) {
token.selector = selector;
}
selector = selector.replace(match[0], ''); // strip current match from selector
if (!selector.length) {
token.last = true;
}
if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
match[1] = Selector._attrFilters[match[1]];
}
test = parser.fn(match, token);
if (test === false) { // selector not supported
found = false;
break outer;
} else if (test) {
token.tests.push(test);
}
if (!selector.length || parser.name === COMBINATOR) {
tokens.push(token);
token = Selector._getToken(token);
if (parser.name === COMBINATOR) {
token.combinator = Y.Selector.combinators[match[1]];
}
}
found = true;
}
}
} while (found && selector.length);
if (!found || selector.length) { // not fully parsed
Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
tokens = [];
}
return tokens;
},
_replaceShorthand: function(selector) {
var shorthand = Selector.shorthand,
attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
re, i, len;
if (pseudos) {
selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
}
if (attrs) {
selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
}
for (re in shorthand) {
if (shorthand.hasOwnProperty(re)) {
selector = selector.replace(Y.DOM._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 && Y.config.doc.querySelector) {
Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
}
}, '@VERSION@' ,{requires:['selector-native']});
YUI.add('selector', function(Y){}, '@VERSION@' ,{use:['selector-native', 'selector-css2']});
YUI.add('dom', function(Y){}, '@VERSION@' ,{use:['dom-base', 'dom-style', 'dom-screen', 'selector']});