3 One of the most important items in MooTools. Contains the dollar function, the dollars function, and an handful of cross-browser,
4 time-saver methods to let you easily work with HTML Elements.
10 var Element = new Native({
14 legacy: window.Element,
16 initialize: function(tag, props){
17 var konstructor = Element.Constructors.get(tag);
18 if (konstructor) return konstructor(props);
19 if (typeof tag == 'string') return document.newElement(tag, props);
20 return $(tag).set(props);
23 afterImplement: function(key, value){
24 Element.Prototype[key] = value;
25 if (Array[key]) return;
26 Elements.implement(key, function(){
27 var items = [], elements = true;
28 for (var i = 0, j = this.length; i < j; i++){
29 var returns = this[i][key].apply(this[i], arguments);
31 if (elements) elements = ($type(returns) == 'element');
33 return (elements) ? new Elements(items) : items;
39 Element.Prototype = {$family: {name: 'element'}};
41 Element.Constructors = new Hash;
43 var IFrame = new Native({
49 initialize: function(){
50 var params = Array.link(arguments, {properties: Object.type, iframe: $defined});
51 var props = params.properties || {};
52 var iframe = $(params.iframe) || false;
53 var onload = props.onload || $empty;
55 props.id = props.name = $pick(props.id, props.name, iframe.id, iframe.name, 'IFrame_' + $time());
56 iframe = new Element(iframe || 'iframe', props);
57 var onFrameLoad = function(){
58 var host = $try(function(){
59 return iframe.contentWindow.location.host;
61 if (host && host == window.location.host){
62 var win = new Window(iframe.contentWindow);
63 new Document(iframe.contentWindow.document);
64 $extend(win.Element.prototype, Element.Prototype);
66 onload.call(iframe.contentWindow, iframe.contentWindow.document);
68 (window.frames[props.id]) ? onFrameLoad() : iframe.addListener('load', onFrameLoad);
74 var Elements = new Native({
76 initialize: function(elements, options){
77 options = $extend({ddup: true, cash: true}, options);
78 elements = elements || [];
79 if (options.ddup || options.cash){
80 var uniques = {}, returned = [];
81 for (var i = 0, l = elements.length; i < l; i++){
82 var el = $.element(elements[i], !options.cash);
84 if (uniques[el.uid]) continue;
85 uniques[el.uid] = true;
91 return (options.cash) ? $extend(elements, this) : elements;
98 filter: function(filter, bind){
99 if (!filter) return this;
100 return new Elements(Array.filter(this, (typeof filter == 'string') ? function(item){
101 return item.match(filter);
109 newElement: function(tag, props){
110 if (Browser.Engine.trident && props){
111 ['name', 'type', 'checked'].each(function(attribute){
112 if (!props[attribute]) return;
113 tag += ' ' + attribute + '="' + props[attribute] + '"';
114 if (attribute != 'checked') delete props[attribute];
116 tag = '<' + tag + '>';
118 return $.element(this.createElement(tag)).set(props);
121 newTextNode: function(text){
122 return this.createTextNode(text);
125 getDocument: function(){
129 getWindow: function(){
137 $: function(el, nocash){
138 if (el && el.$family && el.uid) return el;
139 var type = $type(el);
140 return ($[type]) ? $[type](el, nocash, this.document) : null;
143 $$: function(selector){
144 if (arguments.length == 1 && typeof selector == 'string') return this.document.getElements(selector);
146 var args = Array.flatten(arguments);
147 for (var i = 0, l = args.length; i < l; i++){
149 switch ($type(item)){
150 case 'element': elements.push(item); break;
151 case 'string': elements.extend(this.document.getElements(item, true));
154 return new Elements(elements);
157 getDocument: function(){
158 return this.document;
161 getWindow: function(){
167 $.string = function(id, nocash, doc){
168 id = doc.getElementById(id);
169 return (id) ? $.element(id, nocash) : null;
172 $.element = function(el, nocash){
174 if (!nocash && !el.$family && !(/^object|embed$/i).test(el.tagName)){
175 var proto = Element.Prototype;
176 for (var p in proto) el[p] = proto[p];
181 $.object = function(obj, nocash, doc){
182 if (obj.toElement) return $.element(obj.toElement(doc), nocash);
186 $.textnode = $.whitespace = $.window = $.document = $arguments(0);
188 Native.implement([Element, Document], {
190 getElement: function(selector, nocash){
191 return $(this.getElements(selector, true)[0] || null, nocash);
194 getElements: function(tags, nocash){
195 tags = tags.split(',');
197 var ddup = (tags.length > 1);
198 tags.each(function(tag){
199 var partial = this.getElementsByTagName(tag.trim());
200 (ddup) ? elements.extend(partial) : elements = partial;
202 return new Elements(elements, {ddup: ddup, cash: !nocash});
209 var collected = {}, storage = {};
210 var props = {input: 'checked', option: 'selected', textarea: (Browser.Engine.webkit && Browser.Engine.version < 420) ? 'innerHTML' : 'value'};
212 var get = function(uid){
213 return (storage[uid] || (storage[uid] = {}));
216 var clean = function(item, retain){
219 if (Browser.Engine.trident){
220 if (item.clearAttributes){
221 var clone = retain && item.cloneNode(false);
222 item.clearAttributes();
223 if (clone) item.mergeAttributes(clone);
224 } else if (item.removeEvents){
227 if ((/object/i).test(item.tagName)){
229 if (typeof item[p] == 'function') item[p] = $empty;
231 Element.dispose(item);
235 collected[uid] = storage[uid] = null;
238 var purge = function(){
239 Hash.each(collected, clean);
240 if (Browser.Engine.trident) $A(document.getElementsByTagName('object')).each(clean);
241 if (window.CollectGarbage) CollectGarbage();
242 collected = storage = null;
245 var walk = function(element, walk, start, match, all, nocash){
246 var el = element[start || walk];
249 if (el.nodeType == 1 && (!match || Element.match(el, match))){
250 if (!all) return $(el, nocash);
255 return (all) ? new Elements(elements, {ddup: false, cash: !nocash}) : null;
260 'class': 'className',
262 'text': (Browser.Engine.trident || (Browser.Engine.webkit && Browser.Engine.version < 420)) ? 'innerText' : 'textContent'
264 var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'];
265 var camels = ['value', 'type', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap'];
267 bools = bools.associate(bools);
269 Hash.extend(attributes, bools);
270 Hash.extend(attributes, camels.associate(camels.map(String.toLowerCase)));
274 before: function(context, element){
275 if (element.parentNode) element.parentNode.insertBefore(context, element);
278 after: function(context, element){
279 if (!element.parentNode) return;
280 var next = element.nextSibling;
281 (next) ? element.parentNode.insertBefore(context, next) : element.parentNode.appendChild(context);
284 bottom: function(context, element){
285 element.appendChild(context);
288 top: function(context, element){
289 var first = element.firstChild;
290 (first) ? element.insertBefore(context, first) : element.appendChild(context);
295 inserters.inside = inserters.bottom;
297 Hash.each(inserters, function(inserter, where){
299 where = where.capitalize();
301 Element.implement('inject' + where, function(el){
302 inserter(this, $(el, true));
306 Element.implement('grab' + where, function(el){
307 inserter($(el, true), this);
315 set: function(prop, value){
316 switch ($type(prop)){
318 for (var p in prop) this.set(p, prop[p]);
321 var property = Element.Properties.get(prop);
322 (property && property.set) ? property.set.apply(this, Array.slice(arguments, 1)) : this.setProperty(prop, value);
328 var property = Element.Properties.get(prop);
329 return (property && property.get) ? property.get.apply(this, Array.slice(arguments, 1)) : this.getProperty(prop);
332 erase: function(prop){
333 var property = Element.Properties.get(prop);
334 (property && property.erase) ? property.erase.apply(this) : this.removeProperty(prop);
338 setProperty: function(attribute, value){
339 var key = attributes[attribute];
340 if (value == undefined) return this.removeProperty(attribute);
341 if (key && bools[attribute]) value = !!value;
342 (key) ? this[key] = value : this.setAttribute(attribute, '' + value);
346 setProperties: function(attributes){
347 for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]);
351 getProperty: function(attribute){
352 var key = attributes[attribute];
353 var value = (key) ? this[key] : this.getAttribute(attribute, 2);
354 return (bools[attribute]) ? !!value : (key) ? value : value || null;
357 getProperties: function(){
358 var args = $A(arguments);
359 return args.map(this.getProperty, this).associate(args);
362 removeProperty: function(attribute){
363 var key = attributes[attribute];
364 (key) ? this[key] = (key && bools[attribute]) ? false : '' : this.removeAttribute(attribute);
368 removeProperties: function(){
369 Array.each(arguments, this.removeProperty, this);
373 hasClass: function(className){
374 return this.className.contains(className, ' ');
377 addClass: function(className){
378 if (!this.hasClass(className)) this.className = (this.className + ' ' + className).clean();
382 removeClass: function(className){
383 this.className = this.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1');
387 toggleClass: function(className){
388 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
392 Array.flatten(arguments).each(function(element){
393 element = $(element, true);
394 if (element) this.appendChild(element);
399 appendText: function(text, where){
400 return this.grab(this.getDocument().newTextNode(text), where);
403 grab: function(el, where){
404 inserters[where || 'bottom']($(el, true), this);
408 inject: function(el, where){
409 inserters[where || 'bottom'](this, $(el, true));
413 replaces: function(el){
415 el.parentNode.replaceChild(this, el);
419 wraps: function(el, where){
421 return this.replaces(el).grab(el, where);
424 getPrevious: function(match, nocash){
425 return walk(this, 'previousSibling', null, match, false, nocash);
428 getAllPrevious: function(match, nocash){
429 return walk(this, 'previousSibling', null, match, true, nocash);
432 getNext: function(match, nocash){
433 return walk(this, 'nextSibling', null, match, false, nocash);
436 getAllNext: function(match, nocash){
437 return walk(this, 'nextSibling', null, match, true, nocash);
440 getFirst: function(match, nocash){
441 return walk(this, 'nextSibling', 'firstChild', match, false, nocash);
444 getLast: function(match, nocash){
445 return walk(this, 'previousSibling', 'lastChild', match, false, nocash);
448 getParent: function(match, nocash){
449 return walk(this, 'parentNode', null, match, false, nocash);
452 getParents: function(match, nocash){
453 return walk(this, 'parentNode', null, match, true, nocash);
456 getSiblings: function(match, nocash) {
457 return this.getParent().getChildren(match, nocash).erase(this);
460 getChildren: function(match, nocash){
461 return walk(this, 'nextSibling', 'firstChild', match, true, nocash);
464 getWindow: function(){
465 return this.ownerDocument.window;
468 getDocument: function(){
469 return this.ownerDocument;
472 getElementById: function(id, nocash){
473 var el = this.ownerDocument.getElementById(id);
474 if (!el) return null;
475 for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
476 if (!parent) return null;
478 return $.element(el, nocash);
481 getSelected: function(){
482 return new Elements($A(this.options).filter(function(option){
483 return option.selected;
487 getComputedStyle: function(property){
488 if (this.currentStyle) return this.currentStyle[property.camelCase()];
489 var computed = this.getDocument().defaultView.getComputedStyle(this, null);
490 return (computed) ? computed.getPropertyValue([property.hyphenate()]) : null;
493 toQueryString: function(){
494 var queryString = [];
495 this.getElements('input, select, textarea', true).each(function(el){
496 if (!el.name || el.disabled) return;
497 var value = (el.tagName.toLowerCase() == 'select') ? Element.getSelected(el).map(function(opt){
499 }) : ((el.type == 'radio' || el.type == 'checkbox') && !el.checked) ? null : el.value;
500 $splat(value).each(function(val){
501 if (typeof val != 'undefined') queryString.push(el.name + '=' + encodeURIComponent(val));
504 return queryString.join('&');
507 clone: function(contents, keepid){
508 contents = contents !== false;
509 var clone = this.cloneNode(contents);
510 var clean = function(node, element){
511 if (!keepid) node.removeAttribute('id');
512 if (Browser.Engine.trident){
513 node.clearAttributes();
514 node.mergeAttributes(element);
515 node.removeAttribute('uid');
517 var no = node.options, eo = element.options;
518 for (var j = no.length; j--;) no[j].selected = eo[j].selected;
521 var prop = props[element.tagName.toLowerCase()];
522 if (prop && element[prop]) node[prop] = element[prop];
526 var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*');
527 for (var i = ce.length; i--;) clean(ce[i], te[i]);
536 Element.dispose(this);
542 $A(this.childNodes).each(function(node){
543 Element.destroy(node);
549 return (this.parentNode) ? this.parentNode.removeChild(this) : this;
552 hasChild: function(el){
554 if (!el) return false;
555 if (Browser.Engine.webkit && Browser.Engine.version < 420) return $A(this.getElementsByTagName(el.tagName)).contains(el);
556 return (this.contains) ? (this != el && this.contains(el)) : !!(this.compareDocumentPosition(el) & 16);
559 match: function(tag){
560 return (!tag || (tag == this) || (Element.get(this, 'tag') == tag));
565 Native.implement([Element, Window, Document], {
567 addListener: function(type, fn){
568 if (type == 'unload'){
569 var old = fn, self = this;
571 self.removeListener('unload', fn);
575 collected[this.uid] = this;
577 if (this.addEventListener) this.addEventListener(type, fn, false);
578 else this.attachEvent('on' + type, fn);
582 removeListener: function(type, fn){
583 if (this.removeEventListener) this.removeEventListener(type, fn, false);
584 else this.detachEvent('on' + type, fn);
588 retrieve: function(property, dflt){
589 var storage = get(this.uid), prop = storage[property];
590 if (dflt != undefined && prop == undefined) prop = storage[property] = dflt;
594 store: function(property, value){
595 var storage = get(this.uid);
596 storage[property] = value;
600 eliminate: function(property){
601 var storage = get(this.uid);
602 delete storage[property];
608 window.addListener('unload', purge);
612 Element.Properties = new Hash;
614 Element.Properties.style = {
616 set: function(style){
617 this.style.cssText = style;
621 return this.style.cssText;
625 this.style.cssText = '';
630 Element.Properties.tag = {
633 return this.tagName.toLowerCase();
638 Element.Properties.html = (function(){
639 var wrapper = document.createElement('div');
642 table: [1, '<table>', '</table>'],
643 select: [1, '<select>', '</select>'],
644 tbody: [2, '<table><tbody>', '</tbody></table>'],
645 tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
647 translations.thead = translations.tfoot = translations.tbody;
651 var html = Array.flatten(arguments).join('');
652 var wrap = Browser.Engine.trident && translations[this.get('tag')];
655 first.innerHTML = wrap[1] + html + wrap[2];
656 for (var i = wrap[0]; i--;) first = first.firstChild;
657 this.empty().adopt(first.childNodes);
659 this.innerHTML = html;
664 html.erase = html.set;
669 if (Browser.Engine.webkit && Browser.Engine.version < 420) Element.Properties.text = {
671 if (this.innerText) return this.innerText;
672 var temp = this.ownerDocument.newElement('div', {html: this.innerHTML}).inject(this.ownerDocument.body);
673 var text = temp.innerText;