P
Peter Michaux
Below is my current code.
I've changed the one character identifiers.
I've added isHostObject and am using it.
I haven't tackled the event part of the library but did add a
placeholder LIB.addDomReadyListener function.
I changed the "getting out of trouble" part to use the body element if
it is available (which it almost certainly will be.)
I'm not going to use the Array.prototype.filter in LIB.filter because
using the native array method means it is not possible to send a
NodeList to the LIB.filter function. The way I have implemented
LIB.filter is more like how the generic functions in ES4 will be.
I'm directly manipulating the style.display property. I think this is
just a choice and neither is "right". I will mention this in the
article.
This is strictly for HTML. (XHTML is out of consideration but a
different version for XHTML would be an interesting appendix.)
Any more suggestions or quibbles, even persistent repeats are welcome
if they seem really important.
Peter
// index.html --------------------------------------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>A Tabbed Pane</title>
<script src="lib.js" type="text/javascript"></script>
<script src="tabbedPane.js" type="text/javascript"></script>
</head>
<body>
<div class="sections">
<div class="section first">
<h2>One</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
</p>
</div>
<div class="section">
<h2>Two</h2>
<p>
Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur.
</p>
</div>
<div class="section">
<h2>Three</h2>
<p>
Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
</div>
</body>
</html>
// tabbedPane.js --------------------------------------------------
// Test what can be tested as soon as possible
// Check that the library loaded
if (typeof LIB == 'object' &&
LIB &&
// Test for library functions I use directly
LIB.getAnElement &&
LIB.getDocumentElement &&
LIB.isHostObject &&
LIB.isHostMethod &&
LIB.filter &&
LIB.forEach &&
LIB.addListener &&
LIB.addDomReadyListener &&
LIB.preventDefault &&
LIB.querySelector &&
LIB.hasClass &&
LIB.removeClass &&
LIB.addClass &&
// Test for host objects and methods I use directly
LIB.isHostObject(this, 'document') &&
LIB.isHostMethod(this.document, 'write') &&
LIB.isHostMethod(this.document, 'createTextNode') &&
LIB.isHostMethod(this.document, 'createElement') &&
(function() {
var el = LIB.getAnElement();
return LIB.isHostObject(el, 'style') &&
typeof el.style.display == 'string'
LIB.isHostMethod(el, 'appendChild') &&
LIB.isHostMethod(el, 'insertBefore') &&
LIB.isHostObject(el, 'firstChild') &&
LIB.isHostObject(el, 'childNodes') &&
typeof el.innerHTML == 'string';
})()) {
(function() {
var doc = this.document;
// configuration
var tabbedPaneCssUrl = 'tabbedPane.css',
tabbedPanesEnabledClassName = 'tabbedPanesEnabled',
tabbedPanesDisabledGroup = 'tabbedPanesDisabled',
currentClassName = 'current',
tabGroupTagName = 'ul',
tabGroupClassName = 'tabs',
tabTagName = 'li',
defaultTabText = 'tab',
paneTagName = 'div',
tabbedPaneGroupClassName = 'sections',
paneGroupTagName = 'div',
tabbedPaneClassName = 'section',
showPaneDisplay = 'block',
hidePaneDisplay = 'none';
var showPane = function(pane) {
pane.style.display = showPaneDisplay;
LIB.addClass(pane, currentClassName);
};
var hidePane = function(pane) {
pane.style.display = hidePaneDisplay;
LIB.removeClass(pane, currentClassName);
};
var makeTabCurrent = function(tab) {
LIB.addClass(tab, currentClassName);
};
var makeTabNotCurrent = function(tab) {
LIB.removeClass(tab, currentClassName);
};
var enliven = function(current, tab, pane) {
LIB.addListener(tab, 'click', function(e) {
LIB.preventDefault(e);
// avoid potential flicker if user clicks the current tab
if (tab == current.tab) {
return;
}
makeTabNotCurrent(current.tab);
hidePane(current.pane);
current.tab = tab;
current.pane = pane;
makeTabCurrent(tab);
showPane(pane);
});
};
var init = function(widgetEl) {
var tabs = doc.createElement(tabGroupTagName),
first = true,
current,
tab,
heading;
LIB.addClass(tabs, tabGroupClassName);
LIB.forEach(
LIB.filter(
widgetEl.childNodes,
function(node) {
return LIB.hasClass(node, tabbedPaneClassName);
}),
function(pane) {
tab = doc.createElement(tabTagName);
if (first) {
current = {tab:tab, paneane};
makeTabCurrent(tab);
showPane(pane);
first = false;
}
else {
hidePane(pane);
}
enliven(current, tab, pane);
heading = LIB.querySelector('h2', pane)[0];
tab.innerHTML = '<a href="#">' +
(heading ?
heading.innerHTML :
defaultTabText) +
'</a>';
tabs.appendChild(tab);
});
widgetEl.insertBefore(tabs, widgetEl.firstChild);
};
// Test that a pane really appears and disappears.
// This test uses a dummy tabbed pane temporarily
// inserted into the page. It is one of
// the largest granularity test possible to determine
// the tabbed pane will work.
//
// Tests that CSS is enabled, the necessary
// CSS is supported and that there are no !important rules
// that will interfere.
var supportsDisplayCss = function() {
var outer = doc.createElement('div'),
middle = doc.createElement(paneGroupTagName),
inner = doc.createElement(paneTagName);
if (LIB.isHostObject(doc, 'body') &&
LIB.isHostMethod(doc.body, 'removeChild') &&
typeof middle.offsetHeight == 'number') {
LIB.addClass(outer, tabbedPanesEnabledClassName);
LIB.addClass(middle, tabbedPaneGroupClassName);
LIB.addClass(inner, tabbedPaneClassName);
inner.innerHTML = '.';
middle.appendChild(inner);
outer.appendChild(middle);
doc.body.appendChild(outer);
showPane(inner);
var height = middle.offsetHeight;
hidePane(inner);
var doesSupport = (height > 0 && middle.offsetHeight == 0);
doc.body.removeChild(outer);
return doesSupport;
}
return false;
};
// We don't know for sure at this point that the tabbed pane
// will work. We have to wait for the DOM is ready to finish
// the tests. We do know we can give the pages some style to use
// during the page load because we can "get out of trouble"
// when window.onload fires. This is
// because the functions used to get out of trouble
// have been feature tested.
doc.write('<link href="'+tabbedPaneCssUrl+'"' +
' rel="stylesheet" type="text/css">');
LIB.addDomReadyListener(function() {
// Cannot test that CSS support works until window.onload.
// This also checks that the stylesheet loaded
if (supportsDisplayCss()) {
LIB.forEach(
LIB.querySelector('.'+tabbedPaneGroupClassName),
init);
LIB.addClass(LIB.getDocumentElement(),
tabbedPanesEnabledClassName);
}
else {
// "get out of trouble"
LIB.addClass(
isHostObject(doc, 'body') ?
doc.body :
LIB.getDocumentElement(),
tabbedPanesDisabledClassName);
}
});
})();
}
// tabbedPane.css ----------------------------------------------
/* this file will only be included if there is a chance that
tabbed panes might work */
/* styles for use until window.onload if
the browser is "tentatively" capable */
..sections .section {
display:none;
}
..sections .first {
display:block;
}
/* if feature tests for tabbed panes fail */
..tabbedPanesDisabled .section {
display:block;
}
/* if feature tests for for tabbed panes pass */
..tabbedPanesEnabled li.current {
background:red;
}
// lib.js ------------------------------------------------------
// test any JavaScript features
// new in NN4+, IE4+, ES3+
// or known to have a bug in some browser
// test all host objects
// TODO don't refer to LIB inside any function in LIB
var LIB = {};
// Some array extras for the app developer.
// These are not used within the library
// to keep library interdependencies low.
// These extras don't use the optional
// thisObject argument of native JavaScript 1.6
// Array.prototype.filter but that can easily
// be added here at the cost of some file size.
LIB.filter = function(a, f) {
var rs = [];
for (var i=0, ilen=a.length; i<ilen; i++) {
if (typeof a != 'undefined' && f(a)) {
rs[rs.length] = a;
}
}
return rs;
};
LIB.forEach = function(a, f) {
for (var i=0, ilen=a.length; i<ilen; i++) {
if (typeof a != 'undefined') {
f(a);
}
}
};
// ---------------------------------------------------
// TODO, feature testing and multiple handlers
LIB.addListener = function(element, eventType, callback) {
element['on'+eventType] = callback;
};
LIB.addDomReadyListener = function(callback) {
LIB.addListener(window, 'load', callback);
};
LIB.preventDefault = function(e) {
if (e.preventDefault) {
e.preventDefault();
return;
}
// can't test for returnValue directly?
if (e.cancelBubble != undefined){
e.returnValue = false;
return;
}
};
// ---------------------------------------------------
(function() {
// No longer needed?
// var isRealObjectProperty = function(o, p) {
// return !!(typeof(o[p]) == 'object' && o[p]);
// };
// LIB.isRealObjectProperty = isRealObjectProperty;
var isHostMethod = function(o, m) {
var t = typeof(o[m]);
return !!(((t=='function' || t=='object') && o[m]) ||
t == 'unknown');
};
LIB.isHostMethod = isHostMethod;
var isHostObject = function(o, m) {
var t = typeof(o[m]);
return !!((t=='function' || t=='object') && o[m]);
};
LIB.isHostObject = isHostObject;
if (!(isHostObject(this, 'document'))) {
return;
}
var doc = this.document;
if (isHostObject(doc, 'documentElement')) {
var getAnElement = function(d) {
return (d || doc).documentElement;
};
LIB.getAnElement = getAnElement;
LIB.getDocumentElement = getAnElement;
}
// Test both interfaces in the DOM spec
if (isHostMethod(doc, 'getElementsByTagName') &&
typeof getAnElement == 'function' &&
isHostMethod(getAnElement(), 'getElementsByTagName')) {
// One possible implementation for developers
// in a situation where it is not a problem that
// IE5 thinks doctype and comments are elements.
LIB.getEBTN = function(tag, root) {
root = root || doc;
var els = root.getElementsByTagName(tag);
if (tag == '*' &&
!els.length &&
isHostMethod(root, 'all')) { // TODO what to do?
els = root.all;
}
return els;
};
}
if (isHostMethod(doc, 'getElementById')) {
// One possible implementation for developers
// not troubled by the name and id attribute
// conflict in IE
LIB.getEBI = function(id, d) {
return (d || doc).getElementById(id);
};
}
if (LIB.getEBTN &&
LIB.getEBI &&
getAnElement &&
(function() {
var el = getAnElement();
return typeof el.nodeType == 'number' &&
typeof el.tagName == 'string' &&
typeof el.className == 'string' &&
typeof el.id == 'string'
})()) {
// One possible selector compiler implementation
// that can handle selectors with a tag name,
// class name and id.
//
// use memoization for efficiency
var cache = {};
var compile = function(s) {
if (cache) {
return cache;
}
var m, // regexp matches
tn, // tagName in s
id, // id in s
cn, // className in s
f; // the function body
m = s.match(/^([^#\.]+)/);
tn = m ? m[1] : null;
m = s.match(/#([^\.]+)/);
id = m ? m[1] : null;
m = s.match(/\.([^#]+)/);
cn = m ? m[1] : null;
f = 'var i,els,el,m,ns=[];';
if (id) {
f += 'if (!d||(d.nodeType==9||(!d.nodeType&&!d.tagName))){'+
'els=((el=LIB.getEBI("'+id+'",d))?[el]:[]);' +
((!cn&&!tn)?'return els;':'') +
'}else{' +
'els=LIB.getEBTN("'+(tn||'*')+'",d);' +
'}';
}
else {
f += 'els=LIB.getEBTN("'+(tn||'*')+'",d);';
}
if (id || cn) {
f += 'i=els.length;' +
'while(i--){' +
'el=els;' +
'if(';
if (id) {
f += 'el.id=="'+id+'"';
}
if ((cn||tn) && id) {
f += '&&';
}
if (tn) {
f += 'el.tagName.toLowerCase()=="' + tn + '"';
}
if (cn && tn) {
f += '&&';
}
if (cn) {
f += '((m=el.className)&&' +
'(" "+m+" ").indexOf(" '+cn+' ")>-1)';
}
f += '){' +
'ns[ns.length]=el;' +
'}' +
'}';
f += 'return ns.reverse()';
}
else {
f += 'return els;';
}
// http://elfz.laacz.lv/beautify/
//console.log('function f(d) {' + f + '}');
f = new Function('d', f);
cache = f;
return f;
}
LIB.querySelector = function(selector, rootEl) {
return (compile(selector))(rootEl);
};
}
})();
// ------------------------------------------
(function() {
if (typeof LIB.getAnElement != 'undefined' &&
typeof LIB.getAnElement().className == 'string') {
// The RegExp support need here
// has been available since NN4 & IE4
var hasClass = function(el, className) {
return (new RegExp(
'(^|\\s+)' + className + '(\\s+|$)')).test(el.className);
};
LIB.hasClass = hasClass;
LIB.addClass = function(el, className) {
if (hasClass(el, className)) {
return;
}
el.className = el.className + ' ' + className;
};
LIB.removeClass = function(el, className) {
el.className = el.className.replace(
new RegExp('(^|\\s+)' + className + '(\\s+|$)', 'g'), ' ');
// in case of multiple adjacent with a single space
if (hasClass(el, className)) {
arguments.callee(el, className);
}
};
}
})();
I've changed the one character identifiers.
I've added isHostObject and am using it.
I haven't tackled the event part of the library but did add a
placeholder LIB.addDomReadyListener function.
I changed the "getting out of trouble" part to use the body element if
it is available (which it almost certainly will be.)
I'm not going to use the Array.prototype.filter in LIB.filter because
using the native array method means it is not possible to send a
NodeList to the LIB.filter function. The way I have implemented
LIB.filter is more like how the generic functions in ES4 will be.
I'm directly manipulating the style.display property. I think this is
just a choice and neither is "right". I will mention this in the
article.
This is strictly for HTML. (XHTML is out of consideration but a
different version for XHTML would be an interesting appendix.)
Any more suggestions or quibbles, even persistent repeats are welcome
if they seem really important.
Peter
// index.html --------------------------------------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>A Tabbed Pane</title>
<script src="lib.js" type="text/javascript"></script>
<script src="tabbedPane.js" type="text/javascript"></script>
</head>
<body>
<div class="sections">
<div class="section first">
<h2>One</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
</p>
</div>
<div class="section">
<h2>Two</h2>
<p>
Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur.
</p>
</div>
<div class="section">
<h2>Three</h2>
<p>
Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum.
</p>
</div>
</div>
</body>
</html>
// tabbedPane.js --------------------------------------------------
// Test what can be tested as soon as possible
// Check that the library loaded
if (typeof LIB == 'object' &&
LIB &&
// Test for library functions I use directly
LIB.getAnElement &&
LIB.getDocumentElement &&
LIB.isHostObject &&
LIB.isHostMethod &&
LIB.filter &&
LIB.forEach &&
LIB.addListener &&
LIB.addDomReadyListener &&
LIB.preventDefault &&
LIB.querySelector &&
LIB.hasClass &&
LIB.removeClass &&
LIB.addClass &&
// Test for host objects and methods I use directly
LIB.isHostObject(this, 'document') &&
LIB.isHostMethod(this.document, 'write') &&
LIB.isHostMethod(this.document, 'createTextNode') &&
LIB.isHostMethod(this.document, 'createElement') &&
(function() {
var el = LIB.getAnElement();
return LIB.isHostObject(el, 'style') &&
typeof el.style.display == 'string'
LIB.isHostMethod(el, 'appendChild') &&
LIB.isHostMethod(el, 'insertBefore') &&
LIB.isHostObject(el, 'firstChild') &&
LIB.isHostObject(el, 'childNodes') &&
typeof el.innerHTML == 'string';
})()) {
(function() {
var doc = this.document;
// configuration
var tabbedPaneCssUrl = 'tabbedPane.css',
tabbedPanesEnabledClassName = 'tabbedPanesEnabled',
tabbedPanesDisabledGroup = 'tabbedPanesDisabled',
currentClassName = 'current',
tabGroupTagName = 'ul',
tabGroupClassName = 'tabs',
tabTagName = 'li',
defaultTabText = 'tab',
paneTagName = 'div',
tabbedPaneGroupClassName = 'sections',
paneGroupTagName = 'div',
tabbedPaneClassName = 'section',
showPaneDisplay = 'block',
hidePaneDisplay = 'none';
var showPane = function(pane) {
pane.style.display = showPaneDisplay;
LIB.addClass(pane, currentClassName);
};
var hidePane = function(pane) {
pane.style.display = hidePaneDisplay;
LIB.removeClass(pane, currentClassName);
};
var makeTabCurrent = function(tab) {
LIB.addClass(tab, currentClassName);
};
var makeTabNotCurrent = function(tab) {
LIB.removeClass(tab, currentClassName);
};
var enliven = function(current, tab, pane) {
LIB.addListener(tab, 'click', function(e) {
LIB.preventDefault(e);
// avoid potential flicker if user clicks the current tab
if (tab == current.tab) {
return;
}
makeTabNotCurrent(current.tab);
hidePane(current.pane);
current.tab = tab;
current.pane = pane;
makeTabCurrent(tab);
showPane(pane);
});
};
var init = function(widgetEl) {
var tabs = doc.createElement(tabGroupTagName),
first = true,
current,
tab,
heading;
LIB.addClass(tabs, tabGroupClassName);
LIB.forEach(
LIB.filter(
widgetEl.childNodes,
function(node) {
return LIB.hasClass(node, tabbedPaneClassName);
}),
function(pane) {
tab = doc.createElement(tabTagName);
if (first) {
current = {tab:tab, paneane};
makeTabCurrent(tab);
showPane(pane);
first = false;
}
else {
hidePane(pane);
}
enliven(current, tab, pane);
heading = LIB.querySelector('h2', pane)[0];
tab.innerHTML = '<a href="#">' +
(heading ?
heading.innerHTML :
defaultTabText) +
'</a>';
tabs.appendChild(tab);
});
widgetEl.insertBefore(tabs, widgetEl.firstChild);
};
// Test that a pane really appears and disappears.
// This test uses a dummy tabbed pane temporarily
// inserted into the page. It is one of
// the largest granularity test possible to determine
// the tabbed pane will work.
//
// Tests that CSS is enabled, the necessary
// CSS is supported and that there are no !important rules
// that will interfere.
var supportsDisplayCss = function() {
var outer = doc.createElement('div'),
middle = doc.createElement(paneGroupTagName),
inner = doc.createElement(paneTagName);
if (LIB.isHostObject(doc, 'body') &&
LIB.isHostMethod(doc.body, 'removeChild') &&
typeof middle.offsetHeight == 'number') {
LIB.addClass(outer, tabbedPanesEnabledClassName);
LIB.addClass(middle, tabbedPaneGroupClassName);
LIB.addClass(inner, tabbedPaneClassName);
inner.innerHTML = '.';
middle.appendChild(inner);
outer.appendChild(middle);
doc.body.appendChild(outer);
showPane(inner);
var height = middle.offsetHeight;
hidePane(inner);
var doesSupport = (height > 0 && middle.offsetHeight == 0);
doc.body.removeChild(outer);
return doesSupport;
}
return false;
};
// We don't know for sure at this point that the tabbed pane
// will work. We have to wait for the DOM is ready to finish
// the tests. We do know we can give the pages some style to use
// during the page load because we can "get out of trouble"
// when window.onload fires. This is
// because the functions used to get out of trouble
// have been feature tested.
doc.write('<link href="'+tabbedPaneCssUrl+'"' +
' rel="stylesheet" type="text/css">');
LIB.addDomReadyListener(function() {
// Cannot test that CSS support works until window.onload.
// This also checks that the stylesheet loaded
if (supportsDisplayCss()) {
LIB.forEach(
LIB.querySelector('.'+tabbedPaneGroupClassName),
init);
LIB.addClass(LIB.getDocumentElement(),
tabbedPanesEnabledClassName);
}
else {
// "get out of trouble"
LIB.addClass(
isHostObject(doc, 'body') ?
doc.body :
LIB.getDocumentElement(),
tabbedPanesDisabledClassName);
}
});
})();
}
// tabbedPane.css ----------------------------------------------
/* this file will only be included if there is a chance that
tabbed panes might work */
/* styles for use until window.onload if
the browser is "tentatively" capable */
..sections .section {
display:none;
}
..sections .first {
display:block;
}
/* if feature tests for tabbed panes fail */
..tabbedPanesDisabled .section {
display:block;
}
/* if feature tests for for tabbed panes pass */
..tabbedPanesEnabled li.current {
background:red;
}
// lib.js ------------------------------------------------------
// test any JavaScript features
// new in NN4+, IE4+, ES3+
// or known to have a bug in some browser
// test all host objects
// TODO don't refer to LIB inside any function in LIB
var LIB = {};
// Some array extras for the app developer.
// These are not used within the library
// to keep library interdependencies low.
// These extras don't use the optional
// thisObject argument of native JavaScript 1.6
// Array.prototype.filter but that can easily
// be added here at the cost of some file size.
LIB.filter = function(a, f) {
var rs = [];
for (var i=0, ilen=a.length; i<ilen; i++) {
if (typeof a != 'undefined' && f(a)) {
rs[rs.length] = a;
}
}
return rs;
};
LIB.forEach = function(a, f) {
for (var i=0, ilen=a.length; i<ilen; i++) {
if (typeof a != 'undefined') {
f(a);
}
}
};
// ---------------------------------------------------
// TODO, feature testing and multiple handlers
LIB.addListener = function(element, eventType, callback) {
element['on'+eventType] = callback;
};
LIB.addDomReadyListener = function(callback) {
LIB.addListener(window, 'load', callback);
};
LIB.preventDefault = function(e) {
if (e.preventDefault) {
e.preventDefault();
return;
}
// can't test for returnValue directly?
if (e.cancelBubble != undefined){
e.returnValue = false;
return;
}
};
// ---------------------------------------------------
(function() {
// No longer needed?
// var isRealObjectProperty = function(o, p) {
// return !!(typeof(o[p]) == 'object' && o[p]);
// };
// LIB.isRealObjectProperty = isRealObjectProperty;
var isHostMethod = function(o, m) {
var t = typeof(o[m]);
return !!(((t=='function' || t=='object') && o[m]) ||
t == 'unknown');
};
LIB.isHostMethod = isHostMethod;
var isHostObject = function(o, m) {
var t = typeof(o[m]);
return !!((t=='function' || t=='object') && o[m]);
};
LIB.isHostObject = isHostObject;
if (!(isHostObject(this, 'document'))) {
return;
}
var doc = this.document;
if (isHostObject(doc, 'documentElement')) {
var getAnElement = function(d) {
return (d || doc).documentElement;
};
LIB.getAnElement = getAnElement;
LIB.getDocumentElement = getAnElement;
}
// Test both interfaces in the DOM spec
if (isHostMethod(doc, 'getElementsByTagName') &&
typeof getAnElement == 'function' &&
isHostMethod(getAnElement(), 'getElementsByTagName')) {
// One possible implementation for developers
// in a situation where it is not a problem that
// IE5 thinks doctype and comments are elements.
LIB.getEBTN = function(tag, root) {
root = root || doc;
var els = root.getElementsByTagName(tag);
if (tag == '*' &&
!els.length &&
isHostMethod(root, 'all')) { // TODO what to do?
els = root.all;
}
return els;
};
}
if (isHostMethod(doc, 'getElementById')) {
// One possible implementation for developers
// not troubled by the name and id attribute
// conflict in IE
LIB.getEBI = function(id, d) {
return (d || doc).getElementById(id);
};
}
if (LIB.getEBTN &&
LIB.getEBI &&
getAnElement &&
(function() {
var el = getAnElement();
return typeof el.nodeType == 'number' &&
typeof el.tagName == 'string' &&
typeof el.className == 'string' &&
typeof el.id == 'string'
})()) {
// One possible selector compiler implementation
// that can handle selectors with a tag name,
// class name and id.
//
// use memoization for efficiency
var cache = {};
var compile = function(s) {
if (cache
return cache
}
var m, // regexp matches
tn, // tagName in s
id, // id in s
cn, // className in s
f; // the function body
m = s.match(/^([^#\.]+)/);
tn = m ? m[1] : null;
m = s.match(/#([^\.]+)/);
id = m ? m[1] : null;
m = s.match(/\.([^#]+)/);
cn = m ? m[1] : null;
f = 'var i,els,el,m,ns=[];';
if (id) {
f += 'if (!d||(d.nodeType==9||(!d.nodeType&&!d.tagName))){'+
'els=((el=LIB.getEBI("'+id+'",d))?[el]:[]);' +
((!cn&&!tn)?'return els;':'') +
'}else{' +
'els=LIB.getEBTN("'+(tn||'*')+'",d);' +
'}';
}
else {
f += 'els=LIB.getEBTN("'+(tn||'*')+'",d);';
}
if (id || cn) {
f += 'i=els.length;' +
'while(i--){' +
'el=els;' +
'if(';
if (id) {
f += 'el.id=="'+id+'"';
}
if ((cn||tn) && id) {
f += '&&';
}
if (tn) {
f += 'el.tagName.toLowerCase()=="' + tn + '"';
}
if (cn && tn) {
f += '&&';
}
if (cn) {
f += '((m=el.className)&&' +
'(" "+m+" ").indexOf(" '+cn+' ")>-1)';
}
f += '){' +
'ns[ns.length]=el;' +
'}' +
'}';
f += 'return ns.reverse()';
}
else {
f += 'return els;';
}
// http://elfz.laacz.lv/beautify/
//console.log('function f(d) {' + f + '}');
f = new Function('d', f);
cache
return f;
}
LIB.querySelector = function(selector, rootEl) {
return (compile(selector))(rootEl);
};
}
})();
// ------------------------------------------
(function() {
if (typeof LIB.getAnElement != 'undefined' &&
typeof LIB.getAnElement().className == 'string') {
// The RegExp support need here
// has been available since NN4 & IE4
var hasClass = function(el, className) {
return (new RegExp(
'(^|\\s+)' + className + '(\\s+|$)')).test(el.className);
};
LIB.hasClass = hasClass;
LIB.addClass = function(el, className) {
if (hasClass(el, className)) {
return;
}
el.className = el.className + ' ' + className;
};
LIB.removeClass = function(el, className) {
el.className = el.className.replace(
new RegExp('(^|\\s+)' + className + '(\\s+|$)', 'g'), ' ');
// in case of multiple adjacent with a single space
if (hasClass(el, className)) {
arguments.callee(el, className);
}
};
}
})();