/* ############################ 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