dom-debug.js revision 30c47c5a199c16d0970a8eeda11d4fb56df668d9
0b75a2dd3bd81a69e2d68fe446cacb46be04c1f1Mark Andrews(function(Y) {
c54c1eaf26d5a7fc123c4af3712353156a766df1Mark Andrews * The DOM utility provides a cross-browser abtraction layer
0d993c02babc1e00516272783b310e83bb292d5cMark Andrews * normalizing DOM tasks, and adds extra helper functionality
0d993c02babc1e00516272783b310e83bb292d5cMark Andrews * for other common tasks.
0d993c02babc1e00516272783b310e83bb292d5cMark Andrews * @module dom
4d9f3f00d93fcb8743b1105e8cf82e862be220d1Mark Andrews * @submodule dom-base
86f6b92e35c7bdb5fc1fd1021af75b981863313eMark Andrews * Provides DOM helper methods.
86f6b92e35c7bdb5fc1fd1021af75b981863313eMark Andrews * @class DOM
5af560664daaa984f98cec6925518a3e06c4ab4fMark Andrews COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
8d77066ba0feb1353a7c85f929c365c5103f3976Mark Andrews re_tag = /<([a-z]+)/i;
3e7b37e01ba3efc873486140734fd24788092a30Mark Andrews * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
3e7b37e01ba3efc873486140734fd24788092a30Mark Andrews * @method byId
54532c54130de8f374465bb23d5576fc3257ea96Mark Andrews * @param {String} id the id attribute
54532c54130de8f374465bb23d5576fc3257ea96Mark Andrews * @param {Object} doc optional The document to search. Defaults to current document
020f7361a49c5b1cda91927cf8206c1283fc7496Mark Andrews * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
1c0927d8a091effcf9b2dc5baa533927c113bd5cMark Andrews // TODO: IE Name
70f8c70cdd3ca68edcf9d448eb508abf3697719aMark Andrews // @deprecated
d9e0458a890c49f977fdcf9d995681f546f7c427Mark Andrews // @deprecated
6a3c86ff436452d062912bd91ecd289541869d42Mark Andrews return ret || null;
93da96c1cfd5f3c47169855867dd18db00c8a386Mark Andrews * Returns the text content of the HTMLElement.
93da96c1cfd5f3c47169855867dd18db00c8a386Mark Andrews * @method getText
93da96c1cfd5f3c47169855867dd18db00c8a386Mark Andrews * @param {HTMLElement} element The html element.
7d389c324cc032475f9d219a12ab84bacbd7fbaaMark Andrews * @return {String} The text content of the element (includes text of any descending elements).
cd9bfe5b256a3e84ba090e8fcb5de0d6c50974aeMark Andrews getText: (documentElement.textContent !== undefined) ?
935afdff81843012abeabf8b06e7d3887e7c5e21Mark Andrews } : function(element) {
9b7c023fe6dc88ba1e69ace1f7c3ade40c6475f9Mark Andrews * Sets the text content of the HTMLElement.
935afdff81843012abeabf8b06e7d3887e7c5e21Mark Andrews * @method setText
0a1fa37641b59c56d02f5390917a49e4987f0f75Mark Andrews * @param {HTMLElement} element The html element.
0a1fa37641b59c56d02f5390917a49e4987f0f75Mark Andrews * @param {String} content The content to add.
c61ec97ae0b859914ee26e213fe792f86a157990Mark Andrews setText: (documentElement.textContent !== undefined) ?
5ed4b0d4452967d9b3aaf7a22a2956a6ee67a614Mark Andrews * Finds the previous sibling of the element.
5ed4b0d4452967d9b3aaf7a22a2956a6ee67a614Mark Andrews * @method previous
5ed4b0d4452967d9b3aaf7a22a2956a6ee67a614Mark Andrews * @deprecated Use elementByAxis
3a7c831b697b4604bc90019f2ccc217338117a1cMark Andrews * @param {HTMLElement} element The html element.
c549b3a4d5fedba2ae960df667864e824acb1ef9Mark Andrews * @param {Function} fn optional An optional boolean test to apply.
c549b3a4d5fedba2ae960df667864e824acb1ef9Mark Andrews * The optional function is passed the current DOM node being tested as its only argument.
3a7c831b697b4604bc90019f2ccc217338117a1cMark Andrews * If no function is given, the first sibling is returned.
a483e67c4cdcbfc29ddc62b5a2d0d99b1c542fadMark Andrews * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
a483e67c4cdcbfc29ddc62b5a2d0d99b1c542fadMark Andrews * @return {HTMLElement | null} The matching DOM node or null if none found.
3a7c831b697b4604bc90019f2ccc217338117a1cMark Andrews return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
da091cda77fa951e682119c3df84f60a62bed702Mark Andrews * Finds the next sibling of the element.
da091cda77fa951e682119c3df84f60a62bed702Mark Andrews * @method next
da091cda77fa951e682119c3df84f60a62bed702Mark Andrews * @deprecated Use elementByAxis
3a7c831b697b4604bc90019f2ccc217338117a1cMark Andrews * @param {HTMLElement} element The html element.
d8d489cd8efc45b06a232ac07a636b3d36cc7e8fMark Andrews * @param {Function} fn optional An optional boolean test to apply.
d8d489cd8efc45b06a232ac07a636b3d36cc7e8fMark Andrews * The optional function is passed the current DOM node being tested as its only argument.
3a7c831b697b4604bc90019f2ccc217338117a1cMark Andrews * If no function is given, the first sibling is returned.
bdb1394788a677d0b6e8499ba1ece17a73f476c7Mark Andrews * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
bdb1394788a677d0b6e8499ba1ece17a73f476c7Mark Andrews * @return {HTMLElement | null} The matching DOM node or null if none found.
c8aa2c83113229a59069cbd05c735896f51b886bMark Andrews return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
5b1627d469d07c5bfe7f193e3ddd85d0dd6ad4b0Mark Andrews * Finds the ancestor of the element.
7c23b791f4ae8f0c4c2982a91d13c0ecb15ee798Mark Andrews * @method ancestor
7c23b791f4ae8f0c4c2982a91d13c0ecb15ee798Mark Andrews * @deprecated Use elementByAxis
7c23b791f4ae8f0c4c2982a91d13c0ecb15ee798Mark Andrews * @param {HTMLElement} element The html element.
7c23b791f4ae8f0c4c2982a91d13c0ecb15ee798Mark Andrews * @param {Function} fn optional An optional boolean test to apply.
8989de1059c6292fc43ce507df4991224af2d789Mark Andrews * The optional function is passed the current DOM node being tested as its only argument.
8989de1059c6292fc43ce507df4991224af2d789Mark Andrews * If no function is given, the parentNode is returned.
8989de1059c6292fc43ce507df4991224af2d789Mark Andrews * @param {Boolean} testSelf optional Whether or not to include the element in the scan
acc8b7ac3d16538bf223545bbf71899b9accaebbMark Andrews * @return {HTMLElement | null} The matching DOM node or null if none found.
8e5893c36cdccc706f9632f51e0c8d390d2a8d06Mark Andrews return ret || Y.DOM.elementByAxis(element, PARENT_NODE, fn, null);
9549a96654ead15b264c9159d48eb485e4f9db55Mark Andrews * Searches the element by the given axis for the first matching element.
9549a96654ead15b264c9159d48eb485e4f9db55Mark Andrews * @method elementByAxis
3d822d111c00e70830dc163a5298196a1e7db29fMark Andrews * @param {HTMLElement} element The html element.
3d822d111c00e70830dc163a5298196a1e7db29fMark Andrews * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
88aa68f478c1634f5f10034fb6ea4158efa20ff4Mark Andrews * @param {Function} fn optional An optional boolean test to apply.
88aa68f478c1634f5f10034fb6ea4158efa20ff4Mark Andrews * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
88aa68f478c1634f5f10034fb6ea4158efa20ff4Mark Andrews * The optional function is passed the current HTMLElement being tested as its only argument.
4c83dd7f22b19c75afdd311684f6ba0faa24e8d8Mark Andrews * If no function is given, the first element is returned.
4c83dd7f22b19c75afdd311684f6ba0faa24e8d8Mark Andrews * @return {HTMLElement | null} The matching element or null if none found.
5f4098e478ae913cdc1bb8851599b8f2431050d3Mark Andrews elementByAxis: function(element, axis, fn, all) {
5f4098e478ae913cdc1bb8851599b8f2431050d3Mark Andrews while (element && (element = element[axis])) { // NOTE: assignment
5f4098e478ae913cdc1bb8851599b8f2431050d3Mark Andrews if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
26cf4737b3e84c3a686a5eacebf22ac39e57d4caMark Andrews return null;
34e5a08809dda3276252269ebddd1616e62081a2Mark Andrews * Determines whether or not one HTMLElement is or contains another HTMLElement.
0cd36f1d15caf6622ec3128544d4238ad180a300Mark Andrews * @method contains
0cd36f1d15caf6622ec3128544d4238ad180a300Mark Andrews * @param {HTMLElement} element The containing html element.
0cd36f1d15caf6622ec3128544d4238ad180a300Mark Andrews * @param {HTMLElement} needle The html element that may be contained.
0cd36f1d15caf6622ec3128544d4238ad180a300Mark Andrews * @return {Boolean} Whether or not the element is or contains the needle.
93649589d470624e9e1c34403ad076b3a1a4c5c3Mark Andrews var ret = false;
29f5bb81e2d1d72fc6e44c87404bd4598a34df94Mark Andrews if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
ddc592d128cdde85ada64efbda95981c10c4c03cMark Andrews if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
bd6ad47c3dbc52a54f240432878b6832bd6dd6e2Mark Andrews } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
c7c1bf7dc167ff164193bc04f33a22109e4c0829Mark Andrews if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
93e6ebcd0a0f044ba2add424c265b5e0bb4c8afdMark Andrews * Determines whether or not the HTMLElement is part of the document.
93e6ebcd0a0f044ba2add424c265b5e0bb4c8afdMark Andrews * @method inDoc
93e6ebcd0a0f044ba2add424c265b5e0bb4c8afdMark Andrews * @param {HTMLElement} element The containing html element.
8ac1acc30d0f405222ffa7b2b93131d9d4e18599Mark Andrews * @param {HTMLElement} doc optional The document to check.
8ac1acc30d0f405222ffa7b2b93131d9d4e18599Mark Andrews * @return {Boolean} Whether or not the element is attached to the document.
c99d9017ba00099bfa89e1ed53e63a5cb07d28d5Mark Andrews // there may be multiple elements with the same ID
dd0228908543562781a4c0d8773ae87d4c530633Mark Andrews for (i = 0; node = nodes[i++];) { // check for a match
79a6a33184abff1999ba13b10922ccb34a2758a5Mark Andrews ret = root.querySelectorAll('[id="' + id + '"]');
5d26560e2b93e1aa0334931ec6ccb6045c3581fcMark Andrews if (nodes && nodes.nodeType) { // root.all may return one or many
2b1c71b134eb92f2e297a56f778838e42f41c880Mark Andrews for (i = 0; node = nodes[i++];) { // check for a match
d6fbfd28ea82e425740de903ddc67f7d9e9f82e7Mark Andrews if (node.id === id) { // avoid false positive for node.name
7d3458a972a902740eb142044655aba6c6ffb9acMark Andrews * Creates a new dom node using the provided markup string.
7c441b7f4afdedb6e5a99f113a4f926a005fa950Mark Andrews * @method create
7c441b7f4afdedb6e5a99f113a4f926a005fa950Mark Andrews * @param {String} html The markup used to create the element
7c441b7f4afdedb6e5a99f113a4f926a005fa950Mark Andrews * @param {HTMLDocument} doc An optional document context
f0ffc28f61a68b350fef9257f5f50e1ac866e0abMark Andrews html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
d972fa317829804a692e46a34b6f27a33f861d9dMark Andrews if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
4b171ebd702d72200a4d7609f11c5f79d6b6f964Brian Wellington ret = nodes[0].parentNode.removeChild(nodes[0]);
4b171ebd702d72200a4d7609f11c5f79d6b6f964Brian Wellington } else { // return multiple nodes as a fragment
603d1d1e20fbffc986b3aec93379bb4f6ac37afcMark Andrews if (nodes && (nodes.push || nodes.item) && nodes[0]) {
2dd99c098ca162f985b7ef3c8142a964ad8281aeMark Andrews if (nodes.item) { // convert live list to static array
1fb264ed3aa861a67d7bab9aeb5aea5836e03c14Mark Andrews for (i = 0, len = nodes.length; i < len; i++) {
608c703d1231e0b1f291637ca5361b773afcdbf1Mark Andrews } // else inline with log for minification
608c703d1231e0b1f291637ca5361b773afcdbf1Mark Andrews else { Y.log('unable to convert ' + nodes + ' to fragment', 'warn', 'dom'); }
27151990b2b48f027f7f01972fe8e0dfa1df52d3Mark Andrews CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
fcb2ecdb52a594a5c0d07c2e98e67c14708c16dfMark Andrews * Provides a normalized attribute interface.
fcb2ecdb52a594a5c0d07c2e98e67c14708c16dfMark Andrews * @method setAttibute
3561e645d77448b20b1676680b08c76d559e5335Mark Andrews * @param {String | HTMLElement} el The target element for the attribute.
3561e645d77448b20b1676680b08c76d559e5335Mark Andrews * @param {String} attr The attribute to set.
b9c80c8bddbb88384d7baef297a873b5f8715e49Mark Andrews * @param {String} val The value of the attribute.
b9c80c8bddbb88384d7baef297a873b5f8715e49Mark Andrews setAttribute: function(el, attr, val, ieAttr) {
f38c274c217d0a5b791786877422306a0e477e10Mark Andrews * Provides a normalized attribute interface.
84ef147b1fa0aed15cade55478ed647d15f7b094Mark Andrews * @method getAttibute
84ef147b1fa0aed15cade55478ed647d15f7b094Mark Andrews * @param {String | HTMLElement} el The target element for the attribute.
84ef147b1fa0aed15cade55478ed647d15f7b094Mark Andrews * @param {String} attr The attribute to get.
e53a5a116fc531f730df0adb091278ff8a941dffMark Andrews * @return {String} The current value of the attribute.
986be654feec852eb9da0d15599f18d0035e569bMark Andrews if (ret === null) {
28d8b4118da7abed531ca09136a6d1402837d721Mark Andrews frag = Y.DOM._fragClones[tag] = doc.createElement(tag);
caa8797a00ccb1a02f1690dda5b4aeda9a1db5a7Mark Andrews * Inserts content in a node at the given location
9ae90732df942a7ffcbaa26ba254b55248ce79a5Mark Andrews * @method addHTML
9ae90732df942a7ffcbaa26ba254b55248ce79a5Mark Andrews * @param {HTMLElement} node The node to insert into
9ae90732df942a7ffcbaa26ba254b55248ce79a5Mark Andrews * @param {String} content The content to be inserted
f3222d48cc3d81706d198faa00dea9720eb0768dMark Andrews * @param {String} where Where to insert the content; default is after lastChild
c73c1c33ec9569c8f9ffd205b48f044f9b03795bMark Andrews content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
48b0f5ff87f0a5a138129bcd855fd72908491321Andreas Gustafsson } else { // create from string and cache
fca9cc33ad4299e58e53aa5273d805477267e27aBrian Wellington if (where.nodeType) { // insert regardless of relationship to node
fca9cc33ad4299e58e53aa5273d805477267e27aBrian Wellington // TODO: check if node.contains(where)?
2ca2e1a1ceec59a40f977f01ba8e8f4c0424c484Brian Wellington where.parentNode.insertBefore(newNode, where);
84185d19c7a9ef1ac23cc6236c8773697d4efeb1Brian Wellington case 'replace':
683f10428e292811317df38fa324f242abbf7384Mark Andrews if (newNode) { // allow empty content to clear node
5da1e589c2288dbe87002f771005a78d80a2e258Mark Andrews case 'before':
5da1e589c2288dbe87002f771005a78d80a2e258Mark Andrews case 'after':
852fa3b2e32719d094f3ad6513238841ae1f078bMark Andrews if (node.nextSibling) { // IE errors if refNode is null
852fa3b2e32719d094f3ad6513238841ae1f078bMark Andrews nodeParent.insertBefore(newNode, node.nextSibling);
e9472e9f18f1c4f1279be2b3147be13a2bb731d0Mark Andrews getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
cad61731f8e960d9d99034a2a6eaafe1069c405cMark Andrews // workaround for IE8 JSON stringify bug
cad61731f8e960d9d99034a2a6eaafe1069c405cMark Andrews // which converts empty string values to null
90e303b114e56db5809fdd19805243457fa43cd9Olafur Gudmundsson return (typeof ret === 'string') ? ret : '';
b627356826f7b22e2ef396b80e8394eac76bc109Mark Andrews setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
6c06bc591a830023e5e7a41cc4b37978b98c0c51Mark Andrews * Brute force version of contains.
58d943711575376b05b3b3b303922a8a9d7ce9c1Mark Andrews * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
c36f45e354c0d5b6ab9f821bfe315d0ce9d95a29Mark Andrews * @method _bruteContains
9cec4ca6cac428a46c6d64197c64831dcc02f506Andreas Gustafsson * @param {HTMLElement} element The containing html element.
9cec4ca6cac428a46c6d64197c64831dcc02f506Andreas Gustafsson * @param {HTMLElement} needle The html element that may be contained.
23fb770906bf1fd98210f16ad660078274242963Mark Andrews * @return {Boolean} Whether or not the element is or contains the needle.
8e40433e347bc487cd70f02487fc7ce947a2422aMark Andrews return false;
880723fb130841459d45695b387651cacd6c9bb8Mark Andrews// TODO: move to Lang?
8f44fd4f8d5cefc227ab0fe59cbcbc3979fbc9caAndreas Gustafsson * Memoizes dynamic regular expressions to boost runtime performance.
8f44fd4f8d5cefc227ab0fe59cbcbc3979fbc9caAndreas Gustafsson * @method _getRegExp
8f44fd4f8d5cefc227ab0fe59cbcbc3979fbc9caAndreas Gustafsson * @param {String} str The string to convert to a regular expression.
fefbb64a751f23c9dcf8bb1e62c6ed40a6a04fb2Mark Andrews * @param {String} flags optional An optinal string of flags.
fefbb64a751f23c9dcf8bb1e62c6ed40a6a04fb2Mark Andrews * @return {RegExp} An instance of RegExp
cf300e03de3df3ff422db922520bf07c686c86daMark Andrews Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
242bba8991b030b7764f0bdca3922d75c34ea51eAndreas Gustafsson// TODO: make getDoc/Win true privates?
242bba8991b030b7764f0bdca3922d75c34ea51eAndreas Gustafsson * returns the appropriate document.
242bba8991b030b7764f0bdca3922d75c34ea51eAndreas Gustafsson * @method _getDoc
242bba8991b030b7764f0bdca3922d75c34ea51eAndreas Gustafsson * @param {HTMLElement} element optional Target element.
242bba8991b030b7764f0bdca3922d75c34ea51eAndreas Gustafsson * @return {Object} The document for the given element or the default document.
45fe575607b91147ed753d175a7255198f14f197Andreas Gustafsson return (element[NODE_TYPE] === 9) ? element : // element === document
ef29912666cc6160f7165558bef017ab3849c5e1Mark Andrews element[OWNER_DOCUMENT] || // element === DOM node
ef29912666cc6160f7165558bef017ab3849c5e1Mark Andrews * returns the appropriate window.
0cfa2fb26df42f781eca8c4c856d2d0165055bebMark Andrews * @method _getWin
0cfa2fb26df42f781eca8c4c856d2d0165055bebMark Andrews * @param {HTMLElement} element optional Target element.
0cfa2fb26df42f781eca8c4c856d2d0165055bebMark Andrews * @return {Object} The window for the given element or the default window.
0b81e99ddfb01746e667797dedc291fc550d14d3Brian Wellington return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
3638017bd3cb8e30874f708a125d1541b680b25eBrian Wellington _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
3638017bd3cb8e30874f708a125d1541b680b25eBrian Wellington fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
8bb79fc9b9952172d10f56f31739730e38d4b013Brian Wellington if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
c52806164c335f89e1980af836470b6daffe4f82Andreas Gustafsson tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
c52806164c335f89e1980af836470b6daffe4f82Andreas Gustafsson (!tag || element[TAG_NAME].toUpperCase() === tag) &&
9b2c0d29248ad5f86b47319239a06c783e1b5307Andreas Gustafsson re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
11fe3dcfe2a5fbefd0cfe445872dc4c595506204Andreas Gustafsson // TODO: thead/tfoot with nested tbody
11fe3dcfe2a5fbefd0cfe445872dc4c595506204Andreas Gustafsson // IE adds TBODY when creating TABLE elements (which may share this impl)
d1abb8bb020aacd1ce0da65c2d5d8f7c96ebd52aMark Andrews var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
b4aeceec736cd16d4c4e98f519c8df79b15fbe45Andreas Gustafsson if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
307ba34fa07db768c3a899844f248a2c1d7dcc7fAndreas Gustafsson tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
c0fe9b0d1b01a9a0883977a362ce4128723a56d6Mark Andrews return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
138cc7f283889367b11840ff77a9ea08e17a9daeAndreas Gustafsson // IE: node.value changes the button text, which should be handled via innerHTML
ac1a59e95cfd035f38222e739affd43eafa9eeefMark Andrews attr = node[OWNER_DOCUMENT].createAttribute('value');
7c014c5bf41dc38802e8889c0a9110204eb1a552Andreas Gustafsson return create('<select>' + html + '</select>', doc);
2053e8c26cd69600132632fbee247601ce8c9e8cAndreas Gustafsson return create('<tbody>' + html + '</tbody>', doc);
b20eef7ab022dd984e2e9c12f6a7edf35661d3b0Mark Andrews return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
46ba6046bcb3b534346de13a4ff5c1513e72936bAndreas Gustafsson return (attrs.value && attrs.value.specified) ? node.value : node.text;
62c1fe7b450916acdaf4a3fe65a9b691d5d32f3fBrian Wellington // TODO: implement multipe select
712bf9b0cc4ed34f4bf33b437f8b0e45853b93ceMark Andrews Y.log('multiple select normalization not implemented', 'warn', 'DOM');
7e2f4ceafaae4eac1deddc87f906b29a922fff9dAndreas Gustafsson val = Y.DOM.getValue(options[node.selectedIndex], 'value');
ca033e166ca9f9dc7bf010065a93af668a09fd44Mark Andrews * Determines whether a DOM element has the given className.
ca033e166ca9f9dc7bf010065a93af668a09fd44Mark Andrews * @method hasClass
ca033e166ca9f9dc7bf010065a93af668a09fd44Mark Andrews * @param {HTMLElement} element The DOM element.
f9321a16fb8dce8999a43a6d4008c54845305401Mark Andrews * @param {String} className the class name to search for
c8bedec446212b07511ded85ba04a9a3d5965ba8Mark Andrews * @return {Boolean} Whether or not the element has the given class.
7a104af70fb3071e7949c4e0e585af18ab362db5Mark Andrews var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
2359261a252b339f3cef046cefa10ee1e1d4564dMark Andrews * Adds a class name to a given DOM element.
2359261a252b339f3cef046cefa10ee1e1d4564dMark Andrews * @method addClass
769cd7d5dd677434c3dfa27cbfdd8cb76296fcdcMark Andrews * @param {HTMLElement} element The DOM element.
769cd7d5dd677434c3dfa27cbfdd8cb76296fcdcMark Andrews * @param {String} className the class name to add to the class attribute
6bad645917a026dfa4662dd0a3a78b9efc3f4c36Mark Andrews if (!Y.DOM.hasClass(node, className)) { // skip if already present
11931cc68d6c8139f507a724e7ca9814eed8b552Mark Andrews node.className = Y.Lang.trim([node.className, className].join(' '));
11931cc68d6c8139f507a724e7ca9814eed8b552Mark Andrews * Removes a class name from a given element.
d91d025deffd075db2507d44fab04b79920b3e91Mark Andrews * @method removeClass
d91d025deffd075db2507d44fab04b79920b3e91Mark Andrews * @param {HTMLElement} element The DOM element.
d91d025deffd075db2507d44fab04b79920b3e91Mark Andrews * @param {String} className the class name to remove from the class attribute
c8d9ff1fe871a0e9d5834240c5872a26166cc240Andreas Gustafsson if (className && hasClass(node, className)) {
1d556695ef3c7918ba5061d7d846122d60f5f6c5Mark Andrews node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
ca690f98020f75758bc26f4b5ef1ccf0472a27c3Mark Andrews if ( hasClass(node, className) ) { // in case of multiple adjacent
5f4804c7e47e3cfe7237d27a354d268b0b7ea73bAndreas Gustafsson * Replace a class with another class for a given element.
5cd7e9d4db393c314dd1a761c52d2cb3a4da9b72Andreas Gustafsson * If no oldClassName is present, the newClassName is simply added.
cc8e8b59d6780889739657226a95e23ca1ecadb1Andreas Gustafsson * @method replaceClass
cc8e8b59d6780889739657226a95e23ca1ecadb1Andreas Gustafsson * @param {HTMLElement} element The DOM element
1418d7292da7ebaba1ba389d60192023c0170245Andreas Gustafsson * @param {String} oldClassName the class name to be replaced
cc8e8b59d6780889739657226a95e23ca1ecadb1Andreas Gustafsson * @param {String} newClassName the class name that will be replacing the old class name
65775fe205e8ac935313c42062c75460e0bc1514Andreas Gustafsson replaceClass: function(node, oldC, newC) {
65775fe205e8ac935313c42062c75460e0bc1514Andreas Gustafsson //Y.log('replaceClass replacing ' + oldC + ' with ' + newC, 'info', 'Node');
998358fa900393378c70ad598c2b2e67385089d4Mark Andrews * If the className exists on the node it is removed, if it doesn't exist it is added.
998358fa900393378c70ad598c2b2e67385089d4Mark Andrews * @method toggleClass
2053e8c26cd69600132632fbee247601ce8c9e8cAndreas Gustafsson * @param {HTMLElement} element The DOM element
998358fa900393378c70ad598c2b2e67385089d4Mark Andrews * @param {String} className the class name to be toggled
bc508906db43dda7eab0988348dd0ae3f3023a9bMark Andrews * @param {Boolean} addClass optional boolean to indicate whether class
bc508906db43dda7eab0988348dd0ae3f3023a9bMark Andrews * should be added or removed regardless of current state
b352902413608d0eb310c4bb45412fa45734afbcAndreas Gustafsson toggleClass: function(node, className, force) {
b352902413608d0eb310c4bb45412fa45734afbcAndreas Gustafsson var add = (force !== undefined) ? force :
3d38596530c389610494e6a6ba70d9f5dc9717c5Andreas Gustafsson * Add style management functionality to DOM.
a7cb695600c3c14ac12676f0fb1e179690c5883cMark Andrews * @module dom
a7cb695600c3c14ac12676f0fb1e179690c5883cMark Andrews * @submodule dom-style
4574714ad44ba97f53425fe8d21b7ecb00ac83b9Andreas Gustafsson * Sets a style property for a given element.
4574714ad44ba97f53425fe8d21b7ecb00ac83b9Andreas Gustafsson * @method setStyle
4574714ad44ba97f53425fe8d21b7ecb00ac83b9Andreas Gustafsson * @param {HTMLElement} An HTMLElement to apply the style to.
8d8c145175370d2fd8dbdf425b5ac2a9dc19da96Mark Andrews * @param {String} att The style property to set.
8d8c145175370d2fd8dbdf425b5ac2a9dc19da96Mark Andrews * @param {String|Number} val The value.
9234d92d4e274791eff42cc4ea5766ed7a281b17Mark Andrews if (val === null) {
42f61e5c46d4824918385e7279c1b71d8ada8e8dAndreas Gustafsson CUSTOM_STYLES[att].set(node, val, style);
2ba574f329c14376d26d7c0f22c89d7a978a2625Mark Andrews return; // NOTE: return
2ba574f329c14376d26d7c0f22c89d7a978a2625Mark Andrews } else if (typeof CUSTOM_STYLES[att] === 'string') {
3ad07fa335d40330cd1859da42e67f2457443990Andreas Gustafsson * Returns the current style value for the given property.
3ad07fa335d40330cd1859da42e67f2457443990Andreas Gustafsson * @method getStyle
1094dec52a86e57df53f6167d86de94360a7a382Mark Andrews * @param {HTMLElement} An HTMLElement to get the style from.
1094dec52a86e57df53f6167d86de94360a7a382Mark Andrews * @param {String} att The style property to get.
62a3dbe63e833f2eaf613393399ea4667d8de28dAndreas Gustafsson return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
62a3dbe63e833f2eaf613393399ea4667d8de28dAndreas Gustafsson } else if (typeof CUSTOM_STYLES[att] === 'string') {
e69b9ffb0f8b4d1117a682908c9143ebe3efcd6bAndreas Gustafsson if (val === '') { // TODO: is empty string sufficient?
e69b9ffb0f8b4d1117a682908c9143ebe3efcd6bAndreas Gustafsson val = Y.DOM[GET_COMPUTED_STYLE](node, att);
61d5bfc06be978ea962b1c64309894ac80351771Mark Andrews * Sets multiple style properties.
ada9b8ab20b81716c7ff1f4f3365929b2f7c8ff8Mark Andrews * @method setStyles
ada9b8ab20b81716c7ff1f4f3365929b2f7c8ff8Mark Andrews * @param {HTMLElement} node An HTMLElement to apply the styles to.
ada9b8ab20b81716c7ff1f4f3365929b2f7c8ff8Mark Andrews * @param {Object} hash An object literal of property:value pairs.
024face21cdfbfc7a862a3be061e6780533ef755Andreas Gustafsson * Returns the computed style for the given node.
024face21cdfbfc7a862a3be061e6780533ef755Andreas Gustafsson * @method getComputedStyle
1beaa9e45738ad18cb7cae55aea95a1b16a14f94Andreas Gustafsson * @param {HTMLElement} An HTMLElement to get the style from.
1beaa9e45738ad18cb7cae55aea95a1b16a14f94Andreas Gustafsson * @param {String} att The style property to get.
1beaa9e45738ad18cb7cae55aea95a1b16a14f94Andreas Gustafsson * @return {String} The computed value of the style property.
4fa5d53e750b4e34e19b9648900d489315b185eaAndreas Gustafsson val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
a7e1dcd84ada7e4e4c78f3f281e8a4d99adaf4d1Andreas Gustafsson// normalize reserved word float alternatives ("cssFloat" or "styleFloat")
a7e1dcd84ada7e4e4c78f3f281e8a4d99adaf4d1Andreas Gustafssonif (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
2975d0f819762614526c650b9c2077ef22f81328Andreas Gustafsson} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
2975d0f819762614526c650b9c2077ef22f81328Andreas Gustafsson Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
0bd2ea544e95601e0f0b056acfa079c99d5f6b57Andreas Gustafsson// fix opera computedStyle default color unit (convert to rgb)
5f7516bee5ace9542701f23fc7723a3e3196802aMark Andrews Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
79432444e84d2d104119fe6a3d5cbc04b1375bd4Andreas Gustafsson var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
79432444e84d2d104119fe6a3d5cbc04b1375bd4Andreas Gustafsson val = view[GET_COMPUTED_STYLE](node, '')[att];
f08782f0923d11227983a352c26301cf703383cfMark Andrews// safari converts transparent to rgba(), others use "transparent"
ed2cefaf0ea367ee408cb7f6a54a413814240fa7Andreas Gustafsson Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
ed2cefaf0ea367ee408cb7f6a54a413814240fa7Andreas Gustafsson var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
ed2cefaf0ea367ee408cb7f6a54a413814240fa7Andreas Gustafsson val = view[GET_COMPUTED_STYLE](node, '')[att];
7655e78c366cc0d25e24e2a96ba58e04a96042faAndreas Gustafsson re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
7655e78c366cc0d25e24e2a96ba58e04a96042faAndreas Gustafsson re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
01446841be2b73f9a2ead74056df2d5342414041Andreas Gustafsson val = val.replace(Y.Color.re_hex3, '$1$1');
28cf7340b9c82fc62ca1a1782cb1bd7b0de11aebAndreas Gustafsson if (val !== 'transparent' && val.indexOf('#') < 0) {
5e4c83cfec3f267ea8f22fbb535c61434c94d43cDanny Mayer(function(Y) {
d4196128b31d511c8513edacc70dea7e8d0c053aMark Andrews // TODO: unit-less lineHeight (e.g. 1.22)
4a20a92f4f96cf2b2fd77898c6afec6c45e481b3Andreas Gustafsson 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,
3426f7118c92cab8714a7fddc9e721ff09554447Andreas Gustafsson if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
3426f7118c92cab8714a7fddc9e721ff09554447Andreas Gustafsson value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);
64a5004a66accd190bfd5ddf115667726537be50Andreas Gustafsson } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
64a5004a66accd190bfd5ddf115667726537be50Andreas Gustafsson } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
64a5004a66accd190bfd5ddf115667726537be50Andreas Gustafsson value = Y.DOM.IE.COMPUTED[property](el, property);
64a5004a66accd190bfd5ddf115667726537be50Andreas Gustafsson } else if (re_unit.test(current)) { // convert to pixel
b1ae7a591a4b99a26036e919b87247b65abfcd77Mark Andrews value = ComputedStyle.getPixel(el, property) + PX;
a24d253a3f4e6f4036800744b348fba858d4959eMark Andrews var current = _getStyleObj(el)[prop], // value of "width", "top", etc.
a24d253a3f4e6f4036800744b348fba858d4959eMark Andrews capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
847169dab2d0496df1d66842b2cce67c66bf9680Andreas Gustafsson offset = 'offset' + capped, // "offsetWidth", "offsetTop", etc.
3f543c371fff724d1fb05eb564f732476e946b5bBrian Wellington pixel = 'pixel' + capped, // "pixelWidth", "pixelTop", etc.
5419c0c2d0b77682021084c69f2a5c5e2f9a5525Andreas Gustafsson sizeOffsets = ComputedStyle.sizeOffsets[prop],
847169dab2d0496df1d66842b2cce67c66bf9680Andreas Gustafsson // IE pixelWidth incorrect for percent
847169dab2d0496df1d66842b2cce67c66bf9680Andreas Gustafsson // manually compute by subtracting padding and border from offset size
9aba20edee4e704433a464ae43b070b0775de506Mark Andrews // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
9aba20edee4e704433a464ae43b070b0775de506Mark Andrews // reverting to auto from auto causes position stacking issues (old impl)
ed03e26c44347ec20aff6608de6082e3594d95fbMark Andrews if (current === AUTO || current.indexOf('%') > -1) {
e8d86192fc424f49e43df9cee439ca5c793e6000Mark Andrews value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
e8d86192fc424f49e43df9cee439ca5c793e6000Mark Andrews value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
1299e93989afbe1fee0739811b05fd1641ea14aeAndreas Gustafsson value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
98a5dc52bf668b093cda7901c057f7b54e18a2fcAndreas Gustafsson value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
98a5dc52bf668b093cda7901c057f7b54e18a2fcAndreas Gustafsson } else { // use style.pixelWidth, etc. to convert to pixels
5af0708e7fd78976a33de70f9380785f4086a1f0Andreas Gustafsson // need to map style.width to currentStyle (no currentStyle.pixelWidth)
98a5dc52bf668b093cda7901c057f7b54e18a2fcAndreas Gustafsson if (!el.style[pixel] && !el.style[prop]) {
cad3210bb95057a37aaed20bc8a1542e0534422cAndreas Gustafsson getBorderWidth: function(el, property, omitUnit) {
cad3210bb95057a37aaed20bc8a1542e0534422cAndreas Gustafsson if (current.indexOf(PX) < 0) { // look up keywords
afeded2289de8d193b072da2b44a2d580cc235c1Danny Mayer Y.log('borderWidth computing not implemented', 'warn', 'dom-ie-style');
afeded2289de8d193b072da2b44a2d580cc235c1Danny Mayer return (omitUnit) ? parseFloat(current) : current;
aa9a67adeb48069f5c2e5d8936a8ed5aac7d6ad7Andreas Gustafsson // use pixelRight to convert to px
305b0eda33b16493355db1f1c86313a6f5fbfc3bDanny Mayer while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
c0b6c1a5ab50722793cb99b0d8a1e9e910c146a5Andreas Gustafsson return (current) ? current[att] : VISIBLE;
c0b6c1a5ab50722793cb99b0d8a1e9e910c146a5Andreas Gustafsson if (!current || current === TRANSPARENT) {
c0b6c1a5ab50722793cb99b0d8a1e9e910c146a5Andreas Gustafsson Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
0a532842050020a1b0577c65f91f38bd022daa78Andreas Gustafsson //fontSize: getPixelFont,
23a020bc1312fc35e7c4ea36df846c550cb13634Andreas Gustafsson// use alpha filter for IE opacity
44c141f9471a6bb1fac0cba7880ba768ede2f0c8Brian Wellington if (documentElement.style[OPACITY] === UNDEFINED &&
7250c1a2616761395bdb9ae7cd1ba43f20d3edc4Andreas Gustafsson try { // will error if no DXImageTransform
7250c1a2616761395bdb9ae7cd1ba43f20d3edc4Andreas Gustafsson val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
c38b92000c0f1a95daaad5468777e165b8047de9Mark Andrews try { // make sure its in the document
c38b92000c0f1a95daaad5468777e165b8047de9Mark Andrews Y.log('getStyle: IE opacity filter not found; returning 1', 'warn', 'dom-style');
d81622b537be1971530cfb459acdbbe7d82d883bBrian Wellington if (val === '') { // normalize inline style behavior
a5b9c2b208b51b039c8f4006cddf3d37dd781561Brian Wellington current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
2da0b7dfbd02fab454b8ba60f1fdb7e2a5cbd2dbMark Andrews if (typeof style[FILTER] == 'string') { // in case not appended
2da0b7dfbd02fab454b8ba60f1fdb7e2a5cbd2dbMark Andrews style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
1cb6e8cbe41afade950837319e04da4ccf8649e0Brian Wellington if (!node.currentStyle || !node.currentStyle[HAS_LAYOUT]) {
5419c0c2d0b77682021084c69f2a5c5e2f9a5525Andreas Gustafsson Y.log('document.documentElement.filters error (activeX disabled)', 'warn', 'dom-style');
ecd1addb86319bacc6c0bff2c68373619eebbffcMark Andrews document.createElement('div').style.height = '-1px';
0176adc7c58bb8bd60ec71eeae94dbfbbc4018a8Mark Andrews} catch(e) { // IE throws error on invalid style set; trap common cases
aa0dc8d920a1f79626c3564408db9c5c9a5319a7Andreas Gustafsson Y.log('invalid style value for height: ' + val, 'warn', 'dom-style');
5e88852b94830bf71e37dc700d568cb35e2e6f7eAndreas Gustafsson Y.log('invalid style value for width: ' + val, 'warn', 'dom-style');
35db8a8eda6a889675138eb125d366c8851f68a5Andreas Gustafsson// TODO: top, right, bottom, left
35db8a8eda6a889675138eb125d366c8851f68a5Andreas GustafssonIEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
c6de6524d777c90ae8011af8b10f5cac044081e5Mark AndrewsIEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
bd6504aa9aa16a912412fbe010046aaf4bf23621Brian WellingtonIEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
5e88852b94830bf71e37dc700d568cb35e2e6f7eAndreas Gustafsson IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
bd6504aa9aa16a912412fbe010046aaf4bf23621Brian WellingtonIEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
bd6504aa9aa16a912412fbe010046aaf4bf23621Brian Wellington IEComputed.marginLeft = ComputedStyle.getMargin;
e9596e1fb3dfa560216776acdbfac3cf5ef97157Mark AndrewsIEComputed.visibility = ComputedStyle.getVisibility;
1e289d3cca5cdd01dda650fa6e4c1de1aa8b4196Andreas GustafssonIEComputed.borderColor = IEComputed.borderTopColor =
c54210716ee55b55e22d8dad56fd696a641fc98dBob Halley IEComputed.borderRightColor = IEComputed.borderBottomColor =
c54210716ee55b55e22d8dad56fd696a641fc98dBob Halley IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
3fcf6b956f47405750724bd84e1b2290b61c9186Brian Wellington Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get;
96ed62425310854fd6f6f06bfb7651b3e4c17ee7Andreas Gustafsson * Sets the width of the element to the given size, regardless
96ed62425310854fd6f6f06bfb7651b3e4c17ee7Andreas Gustafsson * of box model, border, padding, etc.
5733d25b06b46067b3751d10436d82aef09cd705Brian Wellington * @method setWidth
82c65f4f62819340ef8198932d3eab8a308a4874Andreas Gustafsson * @param {HTMLElement} element The DOM element.
5733d25b06b46067b3751d10436d82aef09cd705Brian Wellington * @param {String|Int} size The pixel height to size to
eb6e3b04169a766d2b968bcc978191605c2ef24cAndreas Gustafsson * Sets the height of the element to the given size, regardless
eb6e3b04169a766d2b968bcc978191605c2ef24cAndreas Gustafsson * of box model, border, padding, etc.
eb6e3b04169a766d2b968bcc978191605c2ef24cAndreas Gustafsson * @method setHeight
eb6e3b04169a766d2b968bcc978191605c2ef24cAndreas Gustafsson * @param {HTMLElement} element The DOM element.
7d8c3693d0426b56750b14d80c47df5e42fc75e4Andreas Gustafsson * @param {String|Int} size The pixel height to size to
57188b5ff2397c0517e55f622879e69ee547918dAndreas Gustafsson return 'offset' + prop.charAt(0).toUpperCase() + prop.substr(1);
07c336a9a85791dff886b1e28514589a25d9b720Andreas Gustafsson offset = node[Y.DOM._getOffsetProp(node, prop)];
07c336a9a85791dff886b1e28514589a25d9b720Andreas Gustafsson // TODO: handle size less than border/padding (add class?)
9bfa90768ab83ea5a8571c98d3774377da4bdcbeDavid Lawrence(function(Y) {
2d67c2474475acf52c8251dc48bfb7565ee5f2ffDavid Lawrence * Adds position and region management functionality to DOM.
2d67c2474475acf52c8251dc48bfb7565ee5f2ffDavid Lawrence * @module dom
2d67c2474475acf52c8251dc48bfb7565ee5f2ffDavid Lawrence * @submodule dom-screen
ef8d97818f0d30a4e09db97af695f504b311372cMark Andrews GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
63fd201fde27ce408cde1c73a054e401fcfb9e3bDavid Lawrence // TODO: how about thead/tbody/tfoot/tr?
63fd201fde27ce408cde1c73a054e401fcfb9e3bDavid Lawrence // TODO: does caption matter?
edc1c60621b44fbc8131ad1542f657dd129f9a30Andreas Gustafsson * Returns the inner height of the viewport (exludes scrollbar).
edc1c60621b44fbc8131ad1542f657dd129f9a30Andreas Gustafsson * @method winHeight
edc1c60621b44fbc8131ad1542f657dd129f9a30Andreas Gustafsson * @return {Number} The current height of the viewport.
4f4e44c98f315bfadc6dded1b86b465222a83967David Lawrence Y.log('winHeight returning ' + h, 'info', 'dom-screen');
6112718b0dbb01ffbfd3fabc61e30c7e4485b0a7David Lawrence * Returns the inner width of the viewport (exludes scrollbar).
6112718b0dbb01ffbfd3fabc61e30c7e4485b0a7David Lawrence * @method winWidth
6112718b0dbb01ffbfd3fabc61e30c7e4485b0a7David Lawrence * @return {Number} The current width of the viewport.
04260c5c48d234734863f0222e207b6564cd41a8David Lawrence Y.log('winWidth returning ' + w, 'info', 'dom-screen');
6c35e4dd17e6562a6b4d106cbf1d824b9f529356David Lawrence * Document height
504f7802d4c9b43db4820f496c4d00e078effa18David Lawrence * @method docHeight
504f7802d4c9b43db4820f496c4d00e078effa18David Lawrence * @return {Number} The current height of the document.
6af5c66df334c4e275e07b03c9b35e40dbaa4f31Andreas Gustafsson Y.log('docHeight returning ' + h, 'info', 'dom-screen');
3b6bcedffe1d326fd9f6aa3bfb1537af0975fab8Brian Wellington return Math.max(h, Y.DOM._getWinSize(node).height);
841179549b6433e782c164a562eb3422f603533dAndreas Gustafsson * Document width
f808bd34fbd3dd9508e8183e8025635bc330c34aAndreas Gustafsson * @method docWidth
f808bd34fbd3dd9508e8183e8025635bc330c34aAndreas Gustafsson * @return {Number} The current width of the document.
841179549b6433e782c164a562eb3422f603533dAndreas Gustafsson Y.log('docWidth returning ' + w, 'info', 'dom-screen');
841179549b6433e782c164a562eb3422f603533dAndreas Gustafsson return Math.max(w, Y.DOM._getWinSize(node).width);
c2c275f5f4ead0943c76b6463cf7a93095559c64Andreas Gustafsson * Amount page has been scroll horizontally
6c6a6c9f5e2b3c6fd72263eac155e4feddb77316Brian Wellington * @method docScrollX
2445d14b1a95132a473aa30076d0ce1762027e76Mark Andrews * @return {Number} The current amount the screen is scrolled horizontally.
4585aeb2cc84c0e0602da5abf47c31f92ec3b6b2Mark Andrews return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
56877490bc70e4004f6b5e4a16067750ca64be85Andreas Gustafsson * Amount page has been scroll vertically
56877490bc70e4004f6b5e4a16067750ca64be85Andreas Gustafsson * @method docScrollY
711c2be7d9d99ee4415bc4e41ebe4f7f31947b3bAndreas Gustafsson * @return {Number} The current amount the screen is scrolled vertically.
af0be81b2f6ea700dd882d3b18468c7815bd5ef2Andreas Gustafsson return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
ed0e1ae6bc3df39389a24d72cf544b2437bf8340Andreas Gustafsson * Gets the current position of an element based on page coordinates.
ed0e1ae6bc3df39389a24d72cf544b2437bf8340Andreas Gustafsson * Element must be part of the DOM tree to have page coordinates
ed0e1ae6bc3df39389a24d72cf544b2437bf8340Andreas Gustafsson * (display:none or elements not appended return false).
ed0e1ae6bc3df39389a24d72cf544b2437bf8340Andreas Gustafsson * @method getXY
ed0e1ae6bc3df39389a24d72cf544b2437bf8340Andreas Gustafsson * @param element The target element
6cefb60af55912df4411389bccfc38a74e992332Mark Andrews * @return {Array} The XY position of the element
02e81689e3eff98a8d70c98a7cc45c150472316aBrian Wellington if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
02e81689e3eff98a8d70c98a7cc45c150472316aBrian Wellington return function(node) {
aec9f4d0723b0cffcfa9152533fb8f616ec7313bAndreas Gustafsson bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
17dba29ba5db791976e505114baee53a1dde88aaBrian Wellington bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
adcd8c93196ad0a3516252d80597c3c52472ffb2David Lawrence } else { // default to current offsets
adcd8c93196ad0a3516252d80597c3c52472ffb2David Lawrence return function(node) { // manually calculate by crawling up offsetParents
adcd8c93196ad0a3516252d80597c3c52472ffb2David Lawrence //Calculate the Top and Left border sizes (assumes pixels)
adcd8c93196ad0a3516252d80597c3c52472ffb2David Lawrence // TODO: refactor with !! or just falsey
adcd8c93196ad0a3516252d80597c3c52472ffb2David Lawrence bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
5455f30a7532738d750252c00e649890c694ee30Brian Wellington // TODO: worth refactoring for TOP/LEFT only?
e2fd12f3a020ca8c5de168a44fb72e339cdaa3e9Brian Wellington while ((parentNode = parentNode.offsetParent)) {
89d03d4715120fd0c968775bf0724b5a2a647539Mark Andrews // account for any scrolled ancestors
f3ac8ee19231ae3018ec21756f19b1bd639ce7e7Andreas Gustafsson while ((parentNode = parentNode.parentNode)) {
0b135de5a52acec5bb42f96b4e79484d1629fd93Brian Wellington //Firefox does something funky with borders when overflow is not visible.
82e991b8ed4e0ed3b010d191e0cadfd60226c2d9Andreas Gustafsson if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
e7a4f58d55042cbc981a70b5071aaea46b9ebf7fAndreas Gustafsson //Fix FIXED position -- add scrollbars
af1a99a13d73126760b755d63ff7ef8c28ca9070Bob Halley }(),// NOTE: Executing for loadtime branching
82e991b8ed4e0ed3b010d191e0cadfd60226c2d9Andreas Gustafsson parseInt(Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10),
82e991b8ed4e0ed3b010d191e0cadfd60226c2d9Andreas Gustafsson parseInt(Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10)
0e7da7ac26cb234763ff03c3a9bc06e3c22e546fAndreas Gustafsson if ( isNaN(xy[0]) ) { // in case of 'auto'
69d44b2f5ac8e35bdb0b80aeb304f5cb62197892Mark Andrews xy[0] = parseInt(Y.DOM.getStyle(node, LEFT), 10); // try inline
f08f3c6caeb8460cb679a8687f61da61fff69fb0Mark Andrews if ( isNaN(xy[0]) ) { // default to offset value
69d44b2f5ac8e35bdb0b80aeb304f5cb62197892Mark Andrews xy[0] = (pos === RELATIVE) ? 0 : node.offsetLeft || 0;
8fbd23c0aaacdde1348b6457c5db14c433096fd2Andreas Gustafsson if ( isNaN(xy[1]) ) { // in case of 'auto'
5f539d5fc68ca056bd1791e3156b0fe6b28cde16Brian Wellington xy[1] = parseInt(Y.DOM.getStyle(node, TOP), 10); // try inline
c2c275f5f4ead0943c76b6463cf7a93095559c64Andreas Gustafsson if ( isNaN(xy[1]) ) { // default to offset value
5f539d5fc68ca056bd1791e3156b0fe6b28cde16Brian Wellington xy[1] = (pos === RELATIVE) ? 0 : node.offsetTop || 0;
1a286a6613d385b443030a8c932e40ac9e9c301fBob Halley * Gets the current X position of an element based on page coordinates.
1a286a6613d385b443030a8c932e40ac9e9c301fBob Halley * Element must be part of the DOM tree to have page coordinates
1a286a6613d385b443030a8c932e40ac9e9c301fBob Halley * (display:none or elements not appended return false).
1a286a6613d385b443030a8c932e40ac9e9c301fBob Halley * @method getX
3242899a56da9c245956979d5be9c92b2cf0ee24Andreas Gustafsson * @param element The target element
3242899a56da9c245956979d5be9c92b2cf0ee24Andreas Gustafsson * @return {Int} The X position of the element
6f115bdb61672871bd822bdcd09cb1a3aad38aa0David Lawrence * Gets the current Y position of an element based on page coordinates.
6f115bdb61672871bd822bdcd09cb1a3aad38aa0David Lawrence * Element must be part of the DOM tree to have page coordinates
6f115bdb61672871bd822bdcd09cb1a3aad38aa0David Lawrence * (display:none or elements not appended return false).
1ac6cf2f7ae95e4c915cba7038e61930d7c4ba2aAndreas Gustafsson * @method getY
6f115bdb61672871bd822bdcd09cb1a3aad38aa0David Lawrence * @param element The target element
5e194abb5b548524e5c0fd2bb4627ec698b75e2bAndreas Gustafsson * @return {Int} The Y position of the element
09ae77ca30eb17ee32d3f7720ca796a72259cde6Andreas Gustafsson * Set the position of an html element in page coordinates.
09ae77ca30eb17ee32d3f7720ca796a72259cde6Andreas Gustafsson * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
47afc27c28aef95d94e8d1296498ba57a5f00b25Brian Wellington * @method setXY
cedd0ab1e812ec7cf05d57c3e602db41b79f0a2aAndreas Gustafsson * @param element The target element
8c3989000a19f88415d094eb5984f7cf6ba2340cBrian Wellington * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
c2c275f5f4ead0943c76b6463cf7a93095559c64Andreas Gustafsson * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
0c9dd74fecd876563b7f0e4662243ff026b59622Andreas Gustafsson if (pos == 'static') { // default to relative
f8b3c627949bd4bc2f6aafb3dab2f56e3aa9ba06Brian Wellington setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
daad43e5a4e83bd3c055632799ab67e269467db0Brian Wellington setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
3efd6904134ef6c4866a633eabeb55d1c86be7bbBrian Wellington if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
907620b5e0d898da324192cbbe5a5b518f55d175Bob Halley Y.log('setXY setting position to ' + xy, 'info', 'dom-screen');
2c9db9314993504064c1a71f4a059ff9493a75caBrian Wellington Y.log('setXY failed to set ' + node + ' to ' + xy, 'info', 'dom-screen');
9027e1bcf1b245226e3053a75d16c5351d7e60caDavid Lawrence * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
9027e1bcf1b245226e3053a75d16c5351d7e60caDavid Lawrence * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9027e1bcf1b245226e3053a75d16c5351d7e60caDavid Lawrence * @method setX
9027e1bcf1b245226e3053a75d16c5351d7e60caDavid Lawrence * @param element The target element
9027e1bcf1b245226e3053a75d16c5351d7e60caDavid Lawrence * @param {Int} x The X values for new position (coordinates are page-based)
c2c275f5f4ead0943c76b6463cf7a93095559c64Andreas Gustafsson * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
c0968380c4fb0b8196aafb8de225531bd847bb6dBrian Wellington * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
c0968380c4fb0b8196aafb8de225531bd847bb6dBrian Wellington * @method setY
c0968380c4fb0b8196aafb8de225531bd847bb6dBrian Wellington * @param element The target element
1d92d8a2456b23842a649b6104c60a9d6ea25333Brian Wellington * @param {Int} y The Y values for new position (coordinates are page-based)
b52a5b063050f209b0f47379178a1e7ae7404624Andreas Gustafsson var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
34613b2e39478a83076f6a626a4b855cebb19533Andreas Gustafsson l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
b879ed05f4fb8209add6c19a509c984b6c8b3a98Andreas Gustafsson win = doc.defaultView || doc.parentWindow,
279de54fe3a0ac10b64762b18a4569c07b15d742Andreas Gustafsson return { height: root.scrollHeight, width: root.scrollWidth };
55b62439233d930152690b9eba97b06d9dc13d23Mark Andrews(function(Y) {
7c058f1c384ebdba74231111f9358cf08109a5dbBob Halley * Returns an Object literal containing the following about this element: (top, right, bottom, left)
76b3ec5e0c3ae856bc1000270bf3df13580673ebBrian Wellington * @method region
620de5a4b1f23dc9b4ec30d30c0607ff389be0daBob Halley * @param {HTMLElement} element The DOM element.
4e3f8e480f220ef8a87fd28d02f9001b8fc6f423Bob Halley @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
620de5a4b1f23dc9b4ec30d30c0607ff389be0daBob Halley * Find the intersect information for the passes nodes.
620de5a4b1f23dc9b4ec30d30c0607ff389be0daBob Halley * @method intersect
620de5a4b1f23dc9b4ec30d30c0607ff389be0daBob Halley * @param {HTMLElement} element The first element
68b952dc98a9e02b269c0712da120cd773679652Brian Wellington * @param {HTMLElement | Object} element2 The element or region to check the interect with
68b952dc98a9e02b269c0712da120cd773679652Brian Wellington * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
d1cc210d2091916df6f9858fae20a1c760f3b257Andreas Gustafsson @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
d1cc210d2091916df6f9858fae20a1c760f3b257Andreas Gustafsson intersect: function(node, node2, altRegion) {
d1cc210d2091916df6f9858fae20a1c760f3b257Andreas Gustafsson var r = altRegion || DOM.region(node), region = {},
a93cf7e83be621d3d68f51e37121a47a70a6757bMark Andrews return false;
3bb043a8b8b15eece3794ec31ad0ccab103a1c21Brian Wellington area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
14c615e979f674aa61b0ca65c6a252009e521dd8Brian Wellington inRegion: DOM.inRegion(node, node2, false, altRegion)
4d35b6836eb57387a9da6b103331b59cc988b827Mark Andrews * Check if any part of this node is in the passed region
903e9d41ef730f098d38da9588f2824f37b7d73cMark Andrews * @method inRegion
f4b5a0f43481026ea27bd96e3584ca0e92542f0dBob Halley * @param {Object} node2 The node to get the region from or an Object literal of the region
0e7da7ac26cb234763ff03c3a9bc06e3c22e546fAndreas Gustafsson * $param {Boolean} all Should all of the node be inside the region
f4b5a0f43481026ea27bd96e3584ca0e92542f0dBob Halley * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
f4b5a0f43481026ea27bd96e3584ca0e92542f0dBob Halley * @return {Boolean} True if in region, false if not.
f4b5a0f43481026ea27bd96e3584ca0e92542f0dBob Halley inRegion: function(node, node2, all, altRegion) {
a012d6dbfb100390efa7d0d4be64ada0210b09ddBrian Wellington return false;
40c1177517ca5312371da6cc697d813576cfe5a8Andreas Gustafsson if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
22cafd0ece9c8d22a1218f000afdbceda21fe8afBrian Wellington return false;
218c8472e6c8c1a014e412615cc97bb93c0ef9c2Brian Wellington * Check if any part of this element is in the viewport
218c8472e6c8c1a014e412615cc97bb93c0ef9c2Brian Wellington * @method inViewportRegion
f24c135e09214c3843a49fd32ebef2f6a436ba8eBrian Wellington * @param {HTMLElement} element The DOM element.
f24c135e09214c3843a49fd32ebef2f6a436ba8eBrian Wellington * @param {Boolean} all Should all of the node be inside the region
d77312050f1fb1d41b450d4fe6908ea155264d08Brian Wellington * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
f24c135e09214c3843a49fd32ebef2f6a436ba8eBrian Wellington * @return {Boolean} True if in region, false if not.
02940eaf0f732c28c0b39ed114a3803074a80138Andreas Gustafsson inViewportRegion: function(node, all, altRegion) {
02940eaf0f732c28c0b39ed114a3803074a80138Andreas Gustafsson return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
80aba3d49a872ca11d7cf8550c3a993162e7939fMark Andrews _getRegion: function(t, r, b, l) {
0e7da7ac26cb234763ff03c3a9bc06e3c22e546fAndreas Gustafsson region.width = region[RIGHT] - region[LEFT];
91425b5e7204b05165e2c5b244f3dad502f9627dBrian Wellington * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
b18a5b6730dcb062cf7f47c6b3cb909030b58f36Brian Wellington * @method viewportRegion
b18a5b6730dcb062cf7f47c6b3cb909030b58f36Brian Wellington @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
7e361074bc8a2df7a0891a7040eea02ca3a5e286Andreas Gustafsson node = node || Y.config.doc.documentElement;
c17c59662f0969a5e52e8b7529cbde1a7c746095Andreas Gustafsson}, '@VERSION@' ,{requires:['dom-base', 'dom-style']});
ec4f7c6d0f0cfc72bcecdb22bf59890d590218d6Mark Andrews(function(Y) {
651421a5db8a9edf39c76fd8cf859409eb8c373bAndreas Gustafsson * The selector-native module provides support for native querySelector
651421a5db8a9edf39c76fd8cf859409eb8c373bAndreas Gustafsson * @submodule selector-native
651421a5db8a9edf39c76fd8cf859409eb8c373bAndreas Gustafsson * @for Selector
7427490a67b9547242b57c255254f7e146127c48Bob Halley * Provides support for using CSS selectors to query the DOM
aac319b506beb2e9a77ef124ee95b18870194b05Mark Andrews * @class Selector
dfa0badebe5a8260281228d94dbe28e4314a9df6Andreas Gustafsson * @for Selector
dfa0badebe5a8260281228d94dbe28e4314a9df6Andreas GustafssonY.namespace('Selector'); // allow native module to standalone
2d0627005d48b7657fa11792c123466b4f974b61Mark Andrewsvar COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
1ac2c28488fb5de80b3ce9aac3500d215cb61728Brian Wellington _compare: ('sourceIndex' in document.documentElement) ?
a12d9cfa59b5981c52e1aaafedf652d5128f3448Brian Wellington if (a === b) {
702a69f04a89422968ef8fc6fc271fac058e03efBrian Wellington } else if (a > b) {
4b236540be165859390f05911eda86a39ee58eedAndreas Gustafsson } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
c27148868266dd718b6677c794b3e6dca53c3bdcAndreas Gustafsson if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
634784cb66a1c9ddee0c448f71580f024c8fe40bAndreas Gustafsson rangeB = nodeB[OWNER_DOCUMENT].createRange();
ea6709ec8a66e3ffef9c9466613df499567c57f8Brian Wellington compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
14bb9cccae74676e25ae145dc14a3681cc3022b9Mark Andrews * Retrieves a set of nodes based on a given CSS selector.
14bb9cccae74676e25ae145dc14a3681cc3022b9Mark Andrews * @method query
01b8865b1462ba219c90cf6c00f1bf0fdf780d9bBrian Wellington * @param {string} selector The CSS Selector to test the node against.
3b77946b751f39bd4db5a7d1fe48a81e6b1e7a28Bob Halley * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
3b77946b751f39bd4db5a7d1fe48a81e6b1e7a28Bob Halley * @param {Boolean} firstOnly optional Whether or not to return only the first match.
fcc3c131e03cb7e844eaecf74d4f9b7fd38c8398Andreas Gustafsson * @return {Array} An array of nodes that match the given selector.
6d3e8dffb447b9a961360f7f4fd77b0bdb81de76Andreas Gustafsson query: function(selector, root, firstOnly, skipNative) {
7017ede939a5d3e7f2dc113061887a9e81fe8627Brian Wellington useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
b65fc651b8aaab5d0fb9b8f6ef583d699d14a113Mark Andrews fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
c50895694ef939f287aaa7505e0fcf634903bd34Mark Andrews // split group into seperate queries
6d3e8dffb447b9a961360f7f4fd77b0bdb81de76Andreas Gustafsson (!useNative || root.tagName)) { // split native when element scoping is needed
d72269740049af28b091ba81d68a067c88f53547Mark Andrews queries = Selector._splitQueries(selector, root);
a51f77a70bba62f227fb15fe72ecf959893e3f0fMark Andrews if (!firstOnly) { // coerce DOM Collection to Array
ff7f1dc0393cfc8a033be7e94aa56cd57c97d174Andreas Gustafsson if (queries.length > 1) { // remove dupes and sort by doc order
ff7f1dc0393cfc8a033be7e94aa56cd57c97d174Andreas Gustafsson ret = Selector._sort(Selector._deDupe(ret));
9b0c4bf7003db929fe00a345fc96fb97677d29e0Brian Wellington Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
9b0c4bf7003db929fe00a345fc96fb97677d29e0Brian Wellington return (firstOnly) ? (ret[0] || null) : ret;
576f85e5fdb8805307f318db79dfc0d19e390d1aAndreas Gustafsson // allows element scoped queries to begin with combinator
576f85e5fdb8805307f318db79dfc0d19e390d1aAndreas Gustafsson // e.g. query('> p', document.body) === query('body > p')
576f85e5fdb8805307f318db79dfc0d19e390d1aAndreas Gustafsson _splitQueries: function(selector, node) {
32e64787d9bd84a012ddac506f88fbc677b49377Brian Wellington // enforce for element scoping
523dd6a979865b8b1b8f1ecc81e5ce47a168c63fBrian Wellington for (i = 0, len = groups.length; i < len; ++i) {
3f8ad70264645ebd6a2a8bc7e923271eb5bf8416Brian Wellington _nativeQuery: function(selector, root, one) {
9b0e404e5fc71a2bd4fba8a66296477f815af7d5Brian Wellington //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
fff07c1022643da7274d4ba1b086c9c218762dc9Brian Wellington return root['querySelector' + (one ? '' : 'All')](selector);
fff07c1022643da7274d4ba1b086c9c218762dc9Brian Wellington } catch(e) { // fallback to brute if available
fff07c1022643da7274d4ba1b086c9c218762dc9Brian Wellington //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
4817a0628785835abb57d06f2f616b4a6515ac2fAndreas Gustafsson return Y.Selector.query(selector, root, one, true); // redo with skipNative true
d31498a54482c8d5d934875d3fdeaa621c962d6fBrian Wellington Y.log('invalid filter input (nodes: ' + nodes +
d31498a54482c8d5d934875d3fdeaa621c962d6fBrian Wellington ', selector: ' + selector + ')', 'warn', 'Selector');
3b5102fc018a29e52befde5991844843c7b70786Michael Sawyer if (node && node.tagName) { // only test HTMLElements
3b5102fc018a29e52befde5991844843c7b70786Michael Sawyer // we need a root if off-doc
f7e900edbc368275aa7cec7ebec0986e45aeadd7Mark Andrews } else { // only use frag when no parent to query
ec772e873bd7f24418049b5b1b5d7c44ff781356Brian Wellington frag = node[OWNER_DOCUMENT].createDocumentFragment();
75768d5fa2c6c5c441b849ca4efa649a7c2a9e88Bob Halley for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
be171be1799e0ba8cdd35d4f67b772ff086d0d81Andreas Gustafsson * A convenience function to emulate Y.Node's aNode.ancestor(selector).
be171be1799e0ba8cdd35d4f67b772ff086d0d81Andreas Gustafsson * @param {HTMLElement} element An HTMLElement to start the query from.
9f28451bca8377ef6c9ea3b0a49bf342c9fa6800Mark Andrews * @param {String} selector The CSS selector to test the node against.
9f28451bca8377ef6c9ea3b0a49bf342c9fa6800Mark Andrews * @return {HTMLElement} The ancestor node matching the selector, or null.
a110543bb4d2e53caa40f83c2b45786a1efe63efAndreas Gustafsson * @param {Boolean} testSelf optional Whether or not to include the element in the scan
af5dc286ff4b750deec50d1c006aae5fc38019c0Mark Andrews * @method ancestor
ee303f481dfefcd4e4994f8b8b17f2de32aa4d69Brian Wellington ancestor: function (element, selector, testSelf) {
ee303f481dfefcd4e4994f8b8b17f2de32aa4d69Brian Wellington return Y.DOM.ancestor(element, function(n) {
02e38214502c3a946cdfe87e16525747617a1150Brian Wellington * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
02e38214502c3a946cdfe87e16525747617a1150Brian Wellington * @module dom
f8da2d95835c5216570a45e9000f740321503ae3David Lawrence * @submodule selector-css2
f8da2d95835c5216570a45e9000f740321503ae3David Lawrence * @for Selector
fdb2cda3ed366699d70aaf67ee5ae7fcd2ca7561David Lawrence * Provides helper methods for collecting and filtering DOM elements.
32bb863ea960caa650105b60dcd45e3db6840a6fAndreas Gustafsson if (node.children && tag && node.children.tags) {
7d33e8996948523e0180bc7e28a93d534d878dccAndreas Gustafsson } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
d46bf932ed5e1f58a4c424ce1ce7525963354482Brian Wellington //attr: /(\[.*\])/g,
64a84169d7eed05486b10be90afea58f4af146f9Brian Wellington attr: /(\[[^\]]*\])/g,
1599bd6998f54b2b34804d7332f543744368a586Mark Andrews pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
c8fc692fa1445ccfc39b68902546cdfc7ee30d3eBrian Wellington * Mapping of shorthand tokens to corresponding attribute selector
c8fc692fa1445ccfc39b68902546cdfc7ee30d3eBrian Wellington * @property shorthand
c8fc692fa1445ccfc39b68902546cdfc7ee30d3eBrian Wellington * @type object
f19771c55d7e7d5bb38160e710185e6e61749d16Andreas Gustafsson '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
f19771c55d7e7d5bb38160e710185e6e61749d16Andreas Gustafsson '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
f1d427043e94371cdf1f21b3cbd65917adbcff25Andreas Gustafsson * List of operators and corresponding boolean functions.
f1d427043e94371cdf1f21b3cbd65917adbcff25Andreas Gustafsson * These functions are passed the attribute and the current node's value of the attribute.
f1d427043e94371cdf1f21b3cbd65917adbcff25Andreas Gustafsson * @property operators
f1d427043e94371cdf1f21b3cbd65917adbcff25Andreas Gustafsson * @type object
6c29053a20f7614167bafa4388c666644a095349Andreas Gustafsson '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
6c29053a20f7614167bafa4388c666644a095349Andreas Gustafsson //'=': '^{val}$', // equality
6c29053a20f7614167bafa4388c666644a095349Andreas Gustafsson '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
3d509f54ac6bbcc19de5aa6d1ce37e001821dc7bDavid Lawrence '|=': '^{val}-?' // optional hyphen-delimited
a59ed6543bbc13e7c784d6badce7b757c2620e28David Lawrence 'first-child': function(node) {
a59ed6543bbc13e7c784d6badce7b757c2620e28David Lawrence return Y.Selector._children(node[PARENT_NODE])[0] === node;
10e6498d6d7b2cfd8d822788d817fc9a3e0b0c3aDavid Lawrence _bruteQuery: function(selector, root, firstOnly) {
10e6498d6d7b2cfd8d822788d817fc9a3e0b0c3aDavid Lawrence var ret = [],
10e6498d6d7b2cfd8d822788d817fc9a3e0b0c3aDavid Lawrence tokens = Selector._tokenize(selector),
91b191a90cae9b162b8c68a3b4820031e129b37bBrian Wellington token = tokens[tokens.length - 1],
91b191a90cae9b162b8c68a3b4820031e129b37bBrian Wellington rootDoc = Y.DOM._getDoc(root),
253f774e358dba38742a484426a4cadf4f248817Brian Wellington // if we have an initial ID, set to root when in document
2de31518c3da27092120b40fc373cecf600d64e6Brian Wellington if (tokens[0] && rootDoc === root &&
2de31518c3da27092120b40fc373cecf600d64e6Brian Wellington (id = tokens[0].id) &&
4ae3a03bb7dbb050adddc051a5df6f3de057eb27Andreas Gustafsson rootDoc.getElementById(id)) {
4ae3a03bb7dbb050adddc051a5df6f3de057eb27Andreas Gustafsson root = rootDoc.getElementById(id);
2732332fe53d00592109ef69c0075fcc2ad09db9Brian Wellington // prefilter nodes
2732332fe53d00592109ef69c0075fcc2ad09db9Brian Wellington id = token.id;
2732332fe53d00592109ef69c0075fcc2ad09db9Brian Wellington className = token.className;
8fa78d9ad5f5ab6c69d1d52b00b1ffcdf1bd5bebMichael Sawyer tagName = token.tagName || '*';
8fa78d9ad5f5ab6c69d1d52b00b1ffcdf1bd5bebMichael Sawyer // try ID first
e544b507b8019a62c5d2716281f6832519a8791dDavid Lawrence nodes = Y.DOM.allById(id, root);
e544b507b8019a62c5d2716281f6832519a8791dDavid Lawrence // try className
09de21079e902c7356d936ef4f2a31060b36e5f3Brian Wellington } else if (className) {
09de21079e902c7356d936ef4f2a31060b36e5f3Brian Wellington nodes = root.getElementsByClassName(className);
09de21079e902c7356d936ef4f2a31060b36e5f3Brian Wellington } else { // default to tagName
ee4429e13e08f30c366cdc5d10585388b8a9f212Michael Sawyer nodes = root.getElementsByTagName(tagName);
09671f9551077f9eae8c41619b61272cb9821100Andreas Gustafsson if (nodes.length) {
b74896ead5671943135727b50d86d1040d7ffbf3David Lawrence ret = Selector._filterNodes(nodes, tokens, firstOnly);
c9d7e543d0da2996d1cc52d3c5920141df49a4ecBrian Wellington _filterNodes: function(nodes, tokens, firstOnly) {
6dbf9cbe6a39a00de910ef843b9f864abf68bc40Brian Wellington len = tokens.length,
833535ea78ec7a15376b862fd288ffd00f808666Brian Wellington node = nodes[0],
17aac384e029f5dd3314876058c7501f4d84b90bBrian Wellington tmpNode = node,
23f64ea0dcd7f5b7094ae6ade2a002fb7dde1466Brian Wellington getters = Y.Selector.getters,
3c7ce471aa8a1a9c5bc0ca9e41f406bdc9f0b2aeAndreas Gustafsson //FUNCTION = 'function',
f437f6ffae28f88334cf47ce8f948cbf40331ffaAndreas Gustafsson for (i = 0; (tmpNode = node = nodes[i++]);) {
ed8ba54e644957e0ebd51601552193275299ca8dAndreas Gustafsson while (tmpNode && tmpNode.tagName) {
ed8ba54e644957e0ebd51601552193275299ca8dAndreas Gustafsson token = tokens[n];
4d5c668a91c6e5a26653031dd137292bfc03da52Andreas Gustafsson tests = token.tests;
4d5c668a91c6e5a26653031dd137292bfc03da52Andreas Gustafsson j = tests.length;
4d5c668a91c6e5a26653031dd137292bfc03da52Andreas Gustafsson if (j && !pass) {
7789eb1345bef03773a2530dce7f2709cc50aa2aAndreas Gustafsson while ((test = tests[--j])) {
5a6335a8bffdcc15ab4b3bb01d070080f9bc892eMark Andrews operator = test[1];
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson if (getters[test[0]]) {
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson value = getters[test[0]](tmpNode, test[0]);
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson value = tmpNode[test[0]];
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson // use getAttribute for non-standard attributes
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson if (value === undefined && tmpNode.getAttribute) {
9c566a852f31c3a5d0b9d6eaf11463114339c01dAndreas Gustafsson value = tmpNode.getAttribute(test[0]);
f8abaa0fae7f75d9601c10b6a4af8dd907494d45Mark Andrews if ((operator === '=' && value !== test[2]) || // fast path for equality
f8abaa0fae7f75d9601c10b6a4af8dd907494d45Mark Andrews (operator.test && !operator.test(value)) || // regex test
f8abaa0fae7f75d9601c10b6a4af8dd907494d45Mark Andrews (operator.call && !operator(tmpNode, test[0]))) { // function test
1addbb84718fdb7635459ed05f060be086e88f35Andreas Gustafsson // skip non element nodes or non-matching tags
1addbb84718fdb7635459ed05f060be086e88f35Andreas Gustafsson if ((tmpNode = tmpNode[path])) {
1addbb84718fdb7635459ed05f060be086e88f35Andreas Gustafsson while (tmpNode &&
1addbb84718fdb7635459ed05f060be086e88f35Andreas Gustafsson (!tmpNode.tagName ||
1addbb84718fdb7635459ed05f060be086e88f35Andreas Gustafsson (token.tagName && token.tagName !== tmpNode.tagName))
d3be9a9c6ef76a5d7671b0962785ca025b153d2bAndreas Gustafsson tmpNode = tmpNode[path];
e9fce1415f8be4cd38d528950c92c481bd105254Mark Andrews continue testLoop;
90cd33e0baf23574a88a4c967afec8b95a1801deAndreas Gustafsson n--; // move to next token
90cd33e0baf23574a88a4c967afec8b95a1801deAndreas Gustafsson // now that we've passed the test, move up the tree by combinator
90cd33e0baf23574a88a4c967afec8b95a1801deAndreas Gustafsson if (!pass && (combinator = token.combinator)) {
90cd33e0baf23574a88a4c967afec8b95a1801deAndreas Gustafsson path = combinator.axis;
517950ae99fa271b034a5cfec1c9fbb62696f975Mark Andrews tmpNode = tmpNode[path];
517950ae99fa271b034a5cfec1c9fbb62696f975Mark Andrews // skip non element nodes
f9870620b346ed267023dc98ee81adcfef2e16b7Andreas Gustafsson while (tmpNode && !tmpNode.tagName) {
f9870620b346ed267023dc98ee81adcfef2e16b7Andreas Gustafsson tmpNode = tmpNode[path];
19ff7edc1a6388085193f5487e1599f45aa62648Mark Andrews if (combinator.direct) { // one pass only
19ff7edc1a6388085193f5487e1599f45aa62648Mark Andrews path = null;
8a0ff6c15cb20c903f9e16a3d5c2cab603478bc3Mark Andrews } else { // success if we made it this far
8a0ff6c15cb20c903f9e16a3d5c2cab603478bc3Mark Andrews result.push(node);
a6dbd6b6604e27ae3c7190de20dbcaaa6e5a1fd8Andreas Gustafsson if (firstOnly) {
42928d936e79dbda7ea00bbcab6e5d8034a95bf8Andreas Gustafsson return result;
c472ead4a932f93251eddaa41e120c3bfc4f95a4Andreas Gustafsson }// while (tmpNode = node = nodes[++i]);
d7e77a9b59138e8a94d3dfa4e41e1852ad51ac25Andreas Gustafsson node = tmpNode = null;
c472ead4a932f93251eddaa41e120c3bfc4f95a4Andreas Gustafsson return result;
a6dbd6b6604e27ae3c7190de20dbcaaa6e5a1fd8Andreas Gustafsson _getRegExp: function(str, flags) {
383665e42ad838046472e847b16c4e0d3f1aaf76Bob Halley var regexCache = Selector._regexCache;
0e7da7ac26cb234763ff03c3a9bc06e3c22e546fAndreas Gustafsson flags = flags || '';
383665e42ad838046472e847b16c4e0d3f1aaf76Bob Halley if (!regexCache[str + flags]) {
383665e42ad838046472e847b16c4e0d3f1aaf76Bob Halley regexCache[str + flags] = new RegExp(str, flags);
383665e42ad838046472e847b16c4e0d3f1aaf76Bob Halley return regexCache[str + flags];
6b5a6fbe1cc0ceb7e2b516aaada596b79360a5b8Bob Halley combinators: {
5bbed85a33186db4e629e98f45ca702ac6b09127Brian Wellington axis: 'parentNode'
ba6fd2580863759baedd9c47153602b19006a324Andreas Gustafsson axis: 'parentNode',
dd16d9d9e77c2d906ee5ffa3dd9f71cacfbcb081Brian Wellington axis: 'previousSibling',
850d70818503ca1b0f98c9c70b16b51e789fd705Andreas Gustafsson name: ATTRIBUTES,
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence re: /^\[(-?[a-z]+[\w\-]*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence fn: function(match, token) {
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence operators = Y.Selector.operators,
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence // add prefiltering for ID and CLASS
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence if ((match[1] === 'id' && operator === '=') ||
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence document.documentElement.getElementsByClassName &&
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence token.prefilter = match[1];
3c82f274bd880a33fdaf211af4fe8f6b3d6ca556David Lawrence token[match[1]] = match[3];
78d78f05d91205cbde33ca87d24b8d13aa2d8d66Brian Wellington if (operator in operators) {
78d78f05d91205cbde33ca87d24b8d13aa2d8d66Brian Wellington test = operators[operator];
5afc10d6d8278c9ab34b9f6c82ef7bb3bfefd0efAndreas Gustafsson test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
5afc10d6d8278c9ab34b9f6c82ef7bb3bfefd0efAndreas Gustafsson match[2] = test;
3f01dde0bd24561fc3a6c2f7e259a58af4457a86Brian Wellington if (!token.last || token.prefilter !== match[1]) {
3f01dde0bd24561fc3a6c2f7e259a58af4457a86Brian Wellington return match.slice(1);
733e928f714c848aa394c2d12b6239bc7780101bMark Andrews name: TAG_NAME,
0e7da7ac26cb234763ff03c3a9bc06e3c22e546fAndreas Gustafsson re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
c379c1bddb2d84c9219ab6c394b33aa866b9f3bfAndreas Gustafsson fn: function(match, token) {
febaa091847ab004f40500cc475a819f2c73fcddAndreas Gustafsson var tag = match[1].toUpperCase();
febaa091847ab004f40500cc475a819f2c73fcddAndreas Gustafsson token.tagName = tag;
ebfcb6cf66283096ebda1503b6cc042ce86b6bedBrian Wellington if (tag !== '*' && (!token.last || token.prefilter)) {
c379c1bddb2d84c9219ab6c394b33aa866b9f3bfAndreas Gustafsson if (!token.prefilter) {
61b0df9eb522f13aef13cc2704728e799cbc251aMichael Sawyer name: COMBINATOR,
61b0df9eb522f13aef13cc2704728e799cbc251aMichael Sawyer re: /^\s*([>+~]|\s)\s*/,
61b0df9eb522f13aef13cc2704728e799cbc251aMichael Sawyer fn: function(match, token) {
369bb68c2c7709c7fd8b0d6c1d1f8abc6422a7e2Michael Sawyer name: PSEUDOS,
3dcb97b199693012d12e978b8f577a339e434361Andreas Gustafsson re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
a3e41e3c03a32b00fc243fce538a39ddc7237885Andreas Gustafsson } else { // selector token not supported (possibly missing CSS3 module)
4ec1a96d90784f70380bdec66f8a0bd6718a5b71Mark Andrews return false;
b65f2ab14abb4b6ef906d7d02064fba158f07b1eDavid Lawrence Break selector into token units per simple selector.
b65f2ab14abb4b6ef906d7d02064fba158f07b1eDavid Lawrence Combinator is attached to the previous token.
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews selector = Selector._replaceShorthand(Y.Lang.trim(selector));
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews var token = Selector._getToken(), // one token per simple selector (left selector holds combinator)
c03bb27f0675a6e60ceea66b451548e8481bc05cMark Andrews query = selector, // original query for debug report
b65f2ab14abb4b6ef906d7d02064fba158f07b1eDavid Lawrence found = false, // whether or not any matches were found this pass
72fa265baa3d138b43427bcb5c0838740f807045Mark Andrews Search for selector patterns, store, and strip them from the selector string
09671f9551077f9eae8c41619b61272cb9821100Andreas Gustafsson until no patterns match (invalid selector) or we run out of chars.
4b6d5b2312d1482cc406fe58fa3269dd7a915b3fMark Andrews Multiple attributes and pseudos are allowed, in any order.
4b6d5b2312d1482cc406fe58fa3269dd7a915b3fMark Andrews 'form:first-child[type=button]:not(button)[lang|=en]'
b1cde6bf3a8e3a77eb77caf97df0d7ec5c8450dfBrian Wellington for (i = 0; (parser = Selector._parsers[i++]);) {
b1cde6bf3a8e3a77eb77caf97df0d7ec5c8450dfBrian Wellington if ( (match = parser.re.exec(selector)) ) { // note assignment
f8abaa0fae7f75d9601c10b6a4af8dd907494d45Mark Andrews selector = selector.replace(match[0], ''); // strip current match from selector
28b7844ee93231da831ba3c090e1677bb1be5f18Andreas Gustafsson if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
28b7844ee93231da831ba3c090e1677bb1be5f18Andreas Gustafsson match[1] = Selector._attrFilters[match[1]];
28b7844ee93231da831ba3c090e1677bb1be5f18Andreas Gustafsson if (test === false) { // selector not supported
78db9e8f4b686fde6dfa0ec85a68c06cc9d4bf28Brian Wellington } else if (test) {
b20ee662a7c847c9ef7b96ab9e5e34543efe5c0dMark Andrews if (!selector.length || parser.name === COMBINATOR) {
450995b90c8cb66d82c2377d4f9bd9812a132c30Andreas Gustafsson token.combinator = Y.Selector.combinators[match[1]];
190fbe9738bd0c1b9b13732bb8bd56b2b7c71640David Lawrence if (!found || selector.length) { // not fully parsed
c40085afa75a5eae732ec1198384dd5cb24400b6Bob Halley Y.log('query: ' + query + ' contains unsupported token in: ' + selector, 'warn', 'Selector');
c40085afa75a5eae732ec1198384dd5cb24400b6Bob Halley attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
c40085afa75a5eae732ec1198384dd5cb24400b6Bob Halley pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
86c270cbb24117976d6cd3098c3010e067915c24Andreas Gustafsson selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
b38ebe307cb2411535c79afd441870a99cc50eddMark Andrews selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
21a170a0cee1ec2df7be0ce334da309f9a892ea9Andreas Gustafsson selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
bb17aa91c14de959b191a200df61afb6a68f110fBrian Wellington for (i = 0, len = attrs.length; i < len; ++i) {
452d75b18f9d050086964fa39c326cf388517396Mark Andrews selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
6850cdd4497424c9d42ade487edfde9fb9a47de9Brian Wellington for (i = 0, len = pseudos.length; i < len; ++i) {
6850cdd4497424c9d42ade487edfde9fb9a47de9Brian Wellington selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
fb04db11ee6aad3ac3e023ab89b5f9d8a4d5674dMichael SawyerY.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
fb04db11ee6aad3ac3e023ab89b5f9d8a4d5674dMichael Sawyer// IE wants class with native queries
cef18335b7f37bf02bc9e9f2c8750a3a9dcd718aAndreas Gustafssonif (Y.Selector.useNative && document.querySelector) {
fb04db11ee6aad3ac3e023ab89b5f9d8a4d5674dMichael Sawyer Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
fb04db11ee6aad3ac3e023ab89b5f9d8a4d5674dMichael Sawyer}, '@VERSION@' ,{requires:['selector-native']});
e65fe7af00935a0a81d4b0b0ed51c7f6c89f5c3bAndreas GustafssonYUI.add('selector', function(Y){}, '@VERSION@' ,{use:['selector-native', 'selector-css2']});
4bb3a1a63d7943564f30bf9efd312283141439a2Andreas GustafssonYUI.add('dom', function(Y){}, '@VERSION@' ,{use:['dom-base', 'dom-style', 'dom-screen', 'selector']});