/*
############################
Current Differences between WSDOM/Element and jslib/Element
Please maintain these when updating this file:
- WSDOM's Create references WSDOM.Events and looks for that as the property name
- WSDOM Create's children parameter is looped through, while jslib/Element only calls addChild
############################
Element.3.js
A library to handle DOM Elements
Documentation at http://dev2.wallst.com/playground/docs/
and http://wiki.wsod.local/wsodwiki/index.php/Element.3.js_:Dev
Most functions take an element handle or an element's id as a string as the first parameter
"set" functions can also take an array of elements or ids.
Element.get - returns a handle on an element
Element.create - extension of document.createElement. 2nd parameter is properties, 3rd parameter is children.
Element.addChild - appends an element to an element
Element.remove - remove an element from the DOM
Element.setDisplay - sets display: property of element(s)
Element.setVisibility - sets visibility: property of element(s)
Element.remove - removes element(s) from DOM
Element.setHTML - set innerHTML value of element(s). Optional 3rd parameter to append content
Element.parseSelector - returns list of elements that match CSS selector string
Element.forEach - accepts a CSS selector or array of elements and iterates on each element
Element.setStyle - an inline way to set multiple style properties of an element - example: Element.setStyle(el,"border:2px solid red;width:400px;background:#888;");
Element.removeChildNodes - remove all nodes within an element
Element.cloneNode - extension of el.cloneNode that strips all DOM events
Element.insertBefore - keeps the Element syntax in your code
Element.insertAfter - because the DOM only offers insertBefore
Element.nextElement - returns next sibling of indicated tagname
Element.previousElement - returns previous sibling of indicated tagname
Element.getParent - returns the first parent element of a certain tag name
Element.getXY - returns an {x:,y:} object of an element's position relative to the html body
Element.setXY - set an element(s) position
Element.getSize - returns an {width:,height:} object of an element's total size
Element.getSizeXY - returns an {x:,y:} object of an element's total size
Element.setSize - set the size of an element(s)
Element.setWidth - set the width of an element(s)
Element.setHeight - set the height of an element(s)
Element.getBorderSize - returns the current border size
Element.addClass - adds a class attribute
Element.removeClass - removes a class attribute
Element.toggleClass - reverses a class attribute
Element.switchClass - takes a 3rd parameter boolean to turn on/off a class attribute
Element.hasClass - returns if an element is part of a class
Element.getStyle
//////////////////////////
Element Extras (separate file Element.3extras.js)
Element.isInsideOf
Element.isInsideOfNS
Element.isInsideOfEW
Element.debug
Element.setOpacity
Element.setDisabled
Element.setAttribute - same functionality as el.setAttribute but accepts an array of elements
Element.setProperty - sets a JavaScript property to a single or array of elements
Element.getParentBySelector - like getParent but with CSS Selector support
Element.isOnScreen - returns if an element is rendered on the screen. Traverses up the DOM and checks .display and .visibility
*/
/**
* @class Element contains functions for working with the DOM in a cross-browser way.
* See also: http://wiki.wsod.local/wsodwiki/index.php/Element.3.js_:Dev
* @static
*/
var Element_class = function() {
}
// -------------------------------------------------------
// DOM Helpers
// -------------------------------------------------------
/**
* Returns a handle on an element with the specified ID
* @param {String|| Number} el
* @return {element} Native DOM element if found
*/
Element_class.prototype.get = function(el) {
if (typeof el == "string" || typeof el == "number") el = document.getElementById(el);
return el;
};
/**
* Element.create can be used in a variety of ways to create a new HTML element and (optionally) insert it into the DOM
* @example var el = WSDOM.Element.create("div",{className:"primary",style:"color:red;font-weight:bold;"},"Default Text",document.body)
*
* @param {String} tag Element tag name such as 'div'
* @param {Object} [attributes] Element attributes object literal, such as {id:'container3',className:'container'} Note that 'class' is a reserved word,
* be sure to use 'className' to set CSS classes
* @param {Object || String} [children] Element or String text node to add as children
* @param {Object} [parent] Element to append newly created node to (optional)
* @param {Object} [ElementObjectInstance]
* @return {element} Newly created native DOM element
*/
Element_class.prototype.create = function (tag, attributes, children, parent, ElementObjectInstance) {
var element = document.createElement(tag);
// mapping for IE specific attributes
var attributeMap = {
"for":["htmlFor"],
"colspan":["colSpan"],
"usemap":["useMap"]
}
for (var i in attributes)
{
if (i == "className" || i == "class")
{
element.className = attributes[i];
}
else if (document.all && attributeMap[i])
{
for (var j = 0; j also needs an ID to work correctly
if (tag.match(/^map$/i) && attributes && attributes.name && !attributes.id) {
element.setAttribute("id", attributes.name);
}
if (arguments.length > 2 && children != undefined && children !== "") {
this.addChild(element, children);
};
if (parent) {
this.addChild(parent,element);
}
return (ElementObjectInstance) ? new ElementObject(element) : element;
};
/**
* Append an element to another element
* @param {element} el Parent
* @param {element || string} child Element or text to append
*/
Element_class.prototype.addChild = function (el, child) {
el = this.get(el);
if (!this.isArray(child)) {
child = [child]
}
for (var i=0; ihttp://wiki.wsod.local/wsodwiki/index.php/Element.parseSelector:Dev
* for much more info.
* @example Element.parseSelector('tr.highlight', myTable); // returns all TRs belonging to class highlight within myTable
Element.parseSelector('input[type="checkbox"]', myForm); // returns all check boxes
Element.parseSelector('div[position=..'absolute'] ', myDiv); // returns all divs with absolute positioning
* @param {string} selector The selector string to search for
* @param {element} [node] The parent element to search under. It is important to try and specify as small a subset of the DOM as possible by
* setting this parent element, as running parseSelector on the entire DOM can get quite slow
* @param {string || number} [specific] This will allow you to return a specific element from the matched list,
* valid options are "first", "last", or any number for the nth match
*/
Element_class.prototype.parseSelector = function() {
var context = this;
var SEPERATOR = /\s*,\s*/;
function parseSelector(selector, node, num) {
node = node || document.documentElement;
node = this.get(node);
var argSelectors = selector.split(SEPERATOR);
var result = [];
for(var i = 0; i < argSelectors.length; i++) {
// if the selector starts with #, we can use getElementById to improve performance
// only do this if we're searching from the root (selectors like '.myDiv #myEl' don't make much sense,
// but should still work as expected).
if(node == document.documentElement) {
var matches = argSelectors[i].match(/^\s*#([^\s.:@[~+>]*)\s+(.*)$/); // check for simple #id
if(matches) {
node = document.getElementById(matches[1]) || [];
argSelectors[i] = matches[2];
}
}
var stream = toStream(argSelectors[i]), prevToken;
if (this.isArray(node)) {
var nodes = node;
// remove * and " " put in by toStream if it starts with a non alphanumeric
if (!argSelectors[i].match(/^[A-Za-z1-9]/)) {
while (stream[0] && stream[0].match(/[* ]/)) { stream.shift(); }
}
}
else {
var nodes = [node]
}
for(var j = 0;j < stream.length;) {
var token = stream[j++];
var args = '';
if (token == "[") {
args = stream[j];
while(stream[j++] != ']' && j < stream.length) { args += stream[j] };
args = args.slice(0, -1);
var filter = "";
}
else {
var filter = stream[j++];
}
if(stream[j] == '(') {
while(stream[j++] != ')' && j < stream.length) args += stream[j];
args = args.slice(0, -1);
}
prevToken = token;
nodes = select(nodes, token, filter, args);
}
result = result.concat(nodes);
}
// simple selection of an individual node
if (num != undefined) {
if (result.length) {
var REMatch;
if (num == "first") {
return result[0]
}
else if (num == "last") {
return result[result.length-1]
}
else if (REMatch = String(num).match(/nth\((.*)\)/) || num == "even") { // even is the same as nth(2)
if (num == "even") num = 2;
else num = REMatch[1];
var result2 = [];
for (var i=0,len=result.length; i= num) {
return result[num]
}
else {
return null; // num passed, but no matches or unsupported type
}
}
else {
return null;
}
}
return result;
}
var WHITESPACE = /\s*([\s>+~(),]|^|$)\s*/g;
var IMPLIED_ALL = /([\s>+~,]|[^(]\+|^)([#.:@])/g;
var STANDARD_SELECT = /^[^\s>+~]/;
var STREAM = /[\s#.:>+~[\]()@!]|[^\s#.:>+~[\]()@!]+/g;
function toStream(selector) {
var stream = selector
.replace(WHITESPACE, '$1')
.replace(IMPLIED_ALL, '$1*$2');
if(STANDARD_SELECT.test(stream)) {
stream = ' ' + stream;
}
return stream.match(STREAM) || [];
}
function select(nodes, token, filter, args) {
return (selectors[token]) ? selectors[token](nodes, filter, args) : [];
}
var util = {
toArray: function(enumerable) {
var a = [];
for(var i = 0; i < enumerable.length; i++) a.push(enumerable[i]);
return a;
},
push: function(arr,val) {
arr.push(val)
//for(var i = 1; i < arguments.length; i++) arr[arr.length] = arguments[i];
return arr.length;
}
};
var dom = {
isTag: function(node, tag) {
return (tag == '*') || (
tag.toLowerCase() == node.nodeName.toLowerCase().replace(':html', '')
);
},
previousSiblingElement: function(node) {
do node = node.previousSibling; while(node && node.nodeType != 1);
return node;
},
nextSiblingElement: function(node) {
do node = node.nextSibling; while(node && node.nodeType != 1);
return node;
},
hasClass: function(name, node) {
return (node.className || '').match('(^|\\s)'+name+'(\\s|$)');
},
getByTag: function(tag, node) {
/* IE5.x does not support document.getElementsByTagName("*")
therefore we're falling back to element.all */
if(tag == '*') {
var nodes = node.getElementsByTagName(tag);
if(nodes.length == 0 && node.all != null) return node.all
return nodes;
}
return node.getElementsByTagName(tag);
}
};
var selectors = {
'#': function(nodes, filter) {
for(var i = 0; i < nodes.length; i++) {
if(nodes[i].getAttribute('id') == filter) return [nodes[i]];
}
return [];
},
' ': function(nodes, filter) {
var result = [];
for(var i = 0; i < nodes.length; i++) {
result = result.concat(util.toArray(dom.getByTag(filter, nodes[i])));
}
return result;
},
'>': function(nodes, filter) {
var result = [];
for(var i = 0, node; i < nodes.length; i++) {
node = nodes[i];
for(var j = 0, child; j < node.childNodes.length; j++) {
child = node.childNodes[j];
if(child.nodeType == 1 && dom.isTag(child, filter)) {
result.push(child);
}
}
}
return result;
},
'.': function(nodes, filter) {
var result = [];
for(var i = 0, node; i < nodes.length; i++) {
node = nodes[i];
if(dom.hasClass([filter], node)) result.push(node);
}
return result;
},
'!': function(nodes, filter) {
var result = [];
for(var i = 0, node; i < nodes.length; i++) {
node = nodes[i];
if(!dom.hasClass([filter], node)) result.push(node);
}
return result;
},
':': function(nodes, filter, args) {
return (pseudoClasses[filter]) ? pseudoClasses[filter](nodes, args) : [];
},
'+': function(nodes, filter) {
var result = [];
for(var i = 0, node; i < nodes.length; i++) {
node = nodes[i];
var sibling = parseSelector.dom.nextSiblingElement(node);
if(sibling && parseSelector.dom.isTag(sibling, filter)) {
result.push(sibling);
}
}
return result;
},
'~': function(nodes, filter) {
var result = [];
for(var i = 0, node; i < nodes.length; i++) {
node = nodes[i];
var sibling = parseSelector.dom.previousSiblingElement(node);
if(parseSelector.dom.isTag(sibling, filter)) result.push(sibling);
}
return result;
},
'[': function(nodes, filter, args) { // CSS Attribute
args = args.replace(/'/g,'"'); // allow single quotes
var attributeProps = [];
if (!/[<>=]/.test(args)) { // has operator test
attributeProps = ["",args,"",""];
}
else {
var params = args.match(/([^!^$*\/.<>=]*)(!\*=|\*=|\$=|!\$=|\^=|\/=|!\/=|<=|>=|<|>|!=|=)(\.*)(i?)(["'`])([^\5]*)(\5)/);
// (attName) (operator) (type) (caseinsenstive)[quote](value)[quote]
if (params) { attributeProps = params; }
}
//alert(attributeProps )
attributeProps = {name:attributeProps[1],operator:attributeProps[2],isStyle:attributeProps[3]=="..",isProperty:attributeProps[3]==".",casei:attributeProps[4]?true:false,value:attributeProps[6],isValue:(attributeProps[5]=="`")};
// Leave this line for debuging purposes
//alert(args + "\n att: " + attributeProps.name + "\n operator: " + attributeProps.operator + "\n val: " + attributeProps.value + "\n isStyle: " + attributeProps.isStyle + "\n isProperty: " + attributeProps.isProperty + "\n isValue: " + attributeProps.isValue + "\n casei: " + attributeProps.casei)
if (attributeProps.casei) { attributeProps.value = attributeProps.value.toLowerCase(); }
var result = [];
for(var i = 0, node, att, val, el; i < nodes.length; i++) {
node = el = nodes[i];
if (attributeProps.isStyle) {
att = context.getStyle(node,attributeProps.name);
if (!att) continue;
}
else if (attributeProps.isProperty) {
if (node[attributeProps.name] != undefined) {
var att = node[attributeProps.name];
}
else {
continue;
}
}
else {
att = node.getAttribute(attributeProps.name);
if (!att) continue;
}
if (/[*^$\/]/.test(attributeProps.operator)) { // so .match complies
att = String(att)
}
if (attributeProps.casei) { att = att.toLowerCase(); }
val = attributeProps.value;
//alert("Tag: " + el.tagName + ", ID: " + el.id + " , Att: " + att + ", Operator: " + attributeProps.operator + ", Val: " + val);
if (attributeProps.isValue) { val = eval(val.replace(/`/g,"'")); }
if (!attributeProps.operator) {
result.push(nodes[i])
}
else {
switch(attributeProps.operator){
case '=': if (att == val) result.push(node); break;
case '!=': if (att != val) result.push(node); break;
case '*=': if (att.match(val)) result.push(node); break;
case '!*=': if (!att.match(val)) result.push(node); break;
case '^=': if (att.match('^'+val)) result.push(node); break;
case '!^=': if (!att.match('^'+val)) result.push(node); break;
case '$=': if (att.match(val+'$')) result.push(node); break;
case '!$=': if (!att.match(val+'$')) result.push(node); break;
case '/=': if (att.match(val)) result.push(node); break;
case '!/=': if (!att.match(val)) result.push(node); break;
case '>=': if (att >= val) result.push(node); break;
case '>': if (att > val) result.push(node); break;
case '<=': if (att <= val) result.push(node); break;
case '<': if (att < val) result.push(node); break;
}
}
}
return result;
}
};
parseSelector.selectors = selectors;
var pseudoClasses = { };
parseSelector.pseudoClasses = pseudoClasses;
parseSelector.util = util;
parseSelector.dom = dom;
return parseSelector.apply(this,arguments);
};
/*---------------------------------
is -
Is an element a selector?
----------------------------------*/
Element_class.prototype.is = function( domNode, selector, parent, propagate ) {
parent = parent || domNode.parentNode;
var queriedEls = this.parseSelector(selector, parent);
var i
,l = queriedEls.length;
while(domNode && domNode !== parent) {
for(i = 0; i < l; i++) {
if(domNode === queriedEls[i]) {
return true;
}
}
if(propagate) {
domNode = domNode.parentNode;
continue;
}
break;
}
return false;
};
Element_class.prototype.forEach = function(el, callback, context) {
var returnEls = [];
if (typeof el == "string") {
el = this.parseSelector(el);
}
else if (!this.isArray(el)) {
el = [el]
}
var success;
for (var i=0,elLen = el.length; i 1) {
nv[0] = nv[0].replace(/\-(.)/g, function() {
return arguments[1].toUpperCase();
}).replace(/\s/g, "");
pairs.push({n:nv[0],v:nv[1].replace(/^\s*|\s*$/g, "")});
}
}
if (!this.isArray(el)) {
el = [el]
}
var attributeMap = {
"float":["cssFloat","styleFloat"]
}
for (var i=0; i