reimporting, phase 2
[mootools.git] / Native / Element.js
blob96f347b4c8feca037387868802318d5d97478b22
1 /*
2 Script: Element.js
3         Contains useful Element prototypes, to be used with the dollar function <$>.
4         
5 Dependencies:
6         <Moo.js>, <Function.js>, <Array.js>, <String.js>
8 Author:
9         Valerio Proietti, <http://mad4milk.net>
10         
11 License:
12         MIT-style license.
13         
14 Credits:
15         - Some functions are inspired by those found in prototype.js <http://prototype.conio.net/> (c) 2005 Sam Stephenson sam [at] conio [dot] net, MIT-style license
19 Class: Element
20         Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>.
23 var Element = new Class({
25         /*
26         Property: initialize
27                 Creates a new element of the type passed in.
28                         
29         Arguments:
30                 el - the tag name for the element you wish to create.
31                         
32         Example:
33                 >var div = new Element('div');
34         */
36         initialize: function(el){
37                 if ($type(el) == 'string') el = document.createElement(el);
38                 return $(el);
39         },
41         inject: function(el, where){
42                 el = $(el) || new Element(el);
43                 switch(where){
44                         case "before": $(el.parentNode).insertBefore(this, el); break;
45                         case "after": {
46                                         if (!el.getNext()) $(el.parentNode).appendChild(this);
47                                         else $(el.parentNode).insertBefore(this, el.getNext());
48                         } break;
49                         case "inside": el.appendChild(this); break;
50                 }
51                 return this;
52         },
53         
54         /*
55         Property: injectBefore
56                 Inserts the Element before the passed element.
57                         
58         Parameteres:
59                 el - a string representing the element to be injected in (myElementId, or div), or an element reference.
60                 If you pass div or another tag, the element will be created.
61                         
62         Example:
63                 >html: 
64                 ><div id="myElement"></div>
65                 ><div id="mySecondElement"></div>
66                 >js:
67                 >$('mySecondElement').injectBefore('myElement');
68                 >resulting html
69                 ><div id="myElement"></div>
70                 ><div id="mySecondElement"></div>
72         */
73         
74         injectBefore: function(el){
75                 return this.inject(el, 'before');
76         },
77         
78         /*  
79         Property: injectAfter
80                 Same as <Element.injectBefore>, but inserts the element after.
81         */
82         
83         injectAfter: function(el){
84                 return this.inject(el, 'after');
85         },
87         /*  
88         Property: injectInside
89                 Same as <Element.injectBefore>, but inserts the element inside.
90         */
91         
92         injectInside: function(el){
93                 return this.inject(el, 'inside');
94         },
96         /*  
97         Property: adopt
98                 Inserts the passed element inside the Element. Works as <Element.injectInside> but in reverse.
99                         
100         Parameteres:
101                 el - a string representing the element to be injected in (myElementId, or div), or an element reference.
102                 If you pass div or another tag, the element will be created.
103         */
104         
105         adopt: function(el){
106                 this.appendChild($(el) || new Element(el));
107                 return this;
108         },
109         
110         /*  
111         Property: remove
112                 Removes the Element from the DOM.
113                         
114         Example:
115                 >$('myElement').remove() //bye bye
116         */
117         
118         remove: function(){
119                 this.parentNode.removeChild(this);
120         },
121         
122         /*  
123         Property: clone
124                 Clones the Element and returns the cloned one.
125                 
126         Returns: 
127                 the cloned element
128                 
129         Example:
130                 >var clone = $('myElement').clone().injectAfter('myElement');
131                 >//clones the Element and append the clone after the Element.
132         */
133         
134         clone: function(contents){
135                 return $(this.cloneNode(contents || true));
136         },
138         /*  
139         Property: replaceWith
140                 Replaces the Element with an element passed.
141                         
142         Parameteres:
143                 el - a string representing the element to be injected in (myElementId, or div), or an element reference.
144                 If you pass div or another tag, the element will be created.
145                 
146         Returns:
147                 the passed in element
148                         
149         Example:
150                 >$('myOldElement').replaceWith($('myNewElement')); //$('myOldElement') is gone, and $('myNewElement') is in its place.
151         */
152         
153         replaceWith: function(el){
154                 var el = $(el) || new Element(el);
155                 this.parentNode.replaceChild(el, this);
156                 return el;
157         },
158         
159         /*  
160         Property: appendText
161                 Appends text node to a DOM element.
163         Arguments:
164                 text - the text to append.
165                 
166         Example:
167                 ><div id="myElement">hey</div>
168                 >$('myElement').appendText(' howdy'); //myElement innerHTML is now "hey howdy"
169         */
171         appendText: function(text){
172                 if (this.getTag() == 'style' && window.ActiveXObject) this.styleSheet.cssText = text;
173                 else this.appendChild(document.createTextNode(text));
174                 return this;
175         },
176         
177         /*
178         Property: hasClass
179                 Tests the Element to see if it has the passed in className.
180                 
181         Returns:
182                 true - the Element has the class
183                 false - it doesn't
184          
185         Arguments:
186                 className - the class name to test.
187          
188         Example:
189                 ><div id="myElement" class="testClass"></div>
190                 >$('myElement').hasClass('testClass'); //returns true
191         */
193         hasClass: function(className){
194                 return !!this.className.test("\\b"+className+"\\b");
195         },
197         /*      
198         Property: addClass
199                 Adds the passed in class to the Element, if the element doesnt already have it.
200                 
201         Arguments:
202                 className - the class name to add
203                 
204         Example: 
205                 ><div id="myElement" class="testClass"></div>
206                 >$('myElement').addClass('newClass'); //<div id="myElement" class="testClass newClass"></div>
207         */
208         
209         addClass: function(className){
210                 if (!this.hasClass(className)) this.className = (this.className+' '+className.trim()).clean();
211                 return this;
212         },
213         
214         /*      
215         Property: removeClass
216                 works like <Element.addClass>, but removes the class from the element.
217         */
219         removeClass: function(className){
220                 if (this.hasClass(className)) this.className = this.className.replace(className.trim(), '').clean();
221                 return this;
222         },
224         /*      
225         Property: toggleClass
226                 Adds or removes the passed in class name to the element, depending on if it's present or not.
227                 
228         Arguments:
229                 className - the class to add or remove
230                 
231         Example:
232                 ><div id="myElement" class="myClass"></div>
233                 >$('myElement').toggleClass('myClass');
234                 ><div id="myElement" class=""></div>
235                 >$('myElement').toggleClass('myClass');
236                 ><div id="myElement" class="myClass"></div>
237         */
238         
239         toggleClass: function(className){
240                 if (this.hasClass(className)) return this.removeClass(className);
241                 else return this.addClass(className);
242         },
243         
244         /*
245         Property: setStyle      
246                 Sets a css property to the Element.
247                 
248                 Arguments:
249                         property - the property to set
250                         value - the value to which to set it
251                 
252                 Example:
253                         >$('myElement').setStyle('width', '300px'); //the width is now 300px
254         */
255         
256         setStyle: function(property, value){
257                 if (property == 'opacity') this.setOpacity(parseFloat(value));
258                 else this.style[property.camelCase()] = value;
259                 return this;
260         },
262         /*
263         Property: setStyles
264                 Applies a collection of styles to the Element.
265                 
266         Arguments:
267                 source - an object or string containing all the styles to apply
268                 
269         Examples:
270                 >$('myElement').setStyles({
271                 >       border: '1px solid #000',
272                 >       width: '300px',
273                 >       height: '400px'
274                 >});
276                 OR
277                 
278                 >$('myElement').setStyle('border: 1px solid #000; width: 300px; height: 400px;');
279         */
280         
281         setStyles: function(source){
282                 if ($type(source) == 'object') {
283                         for (var property in source) this.setStyle(property, source[property]);
284                 } else if ($type(source) == 'string') {
285                         if (window.ActiveXObject) this.cssText = source;
286                         else this.setAttribute('style', source);
287                 }
288                 return this;
289         },
290         
291         /*      
292         Property: setOpacity
293                 Sets the opacity of the Element, and sets also visibility == "hidden" if opacity == 0, and visibility = "visible" if opacity == 1.
294                 
295         Arguments:
296                 opacity - Accepts numbers from 0 to 1.
297                 
298         Example:
299                 >$('myElement').setOpacity(0.5) //make it 50% transparent
300         */
301         
302         setOpacity: function(opacity){
303                 if (opacity == 0){
304                         if(this.style.visibility != "hidden") this.style.visibility = "hidden";
305                 } else {
306                         if(this.style.visibility != "visible") this.style.visibility = "visible";
307                 }
308                 if (window.ActiveXObject) this.style.filter = "alpha(opacity=" + opacity*100 + ")";
309                 this.style.opacity = opacity;
310                 return this;
311         },
312         
313         /*      
314         Property: getStyle
315                 Returns the style of the Element given the property passed in.
316                 
317         Arguments:
318                 property - the css style property you want to retrieve
319                 
320         Example:
321                 >$('myElement').getStyle('width'); //returns "400px"
322                 >//but you can also use
323                 >$('myElement').getStyle('width').toInt(); //returns "400"
324                 
325         Returns:
326                 the style as a string
327         */
328         
329         getStyle: function(property){
330                 var proPerty = property.camelCase();
331                 var style = this.style[proPerty] || false;
332                 if (!style) {
333                         if (document.defaultView) style = document.defaultView.getComputedStyle(this,null).getPropertyValue(property);
334                         else if (this.currentStyle) style = this.currentStyle[proPerty];
335                 }
336                 if (style && ['color', 'backgroundColor', 'borderColor'].test(proPerty) && style.test('rgb')) style = style.rgbToHex();
337                 return style;
338         },
340         /*      
341         Property: addEvent
342                 Attaches an event listener to a DOM element.
343                 
344         Arguments:
345                 action - the event to monitor ('click', 'load', etc)
346                 fn - the function to execute
347                 
348         Example:
349                 >$('myElement').addEvent('click', function(){alert('clicked!')});
350         */
352         addEvent: function(action, fn){
353                 this[action+fn] = fn.bind(this);
354                 if (this.addEventListener) this.addEventListener(action, fn, false);
355                 else this.attachEvent('on'+action, this[action+fn]);
356                 var el = this;
357                 if (this != window) Unload.functions.push(function(){
358                         el.removeEvent(action, fn);
359                         el[action+fn] = null;
360                 });
361                 return this;
362         },
363         
364         /*      
365         Property: removeEvent
366                 Works as Element.addEvent, but instead removes the previously added event listener.
367         */
368         
369         removeEvent: function(action, fn){
370                 if (this.removeEventListener) this.removeEventListener(action, fn, false);
371                 else this.detachEvent('on'+action, this[action+fn]);
372                 return this;
373         },
375         getBrother: function(what){
376                 var el = this[what+'Sibling'];
377                 while ($type(el) == 'textnode') el = el[what+'Sibling'];
378                 return $(el);
379         },
380         
381         /*
382         Property: getPrevious
383                 Returns the previousSibling of the Element, excluding text nodes.
384                 
385         Example:
386                 >$('myElement').getPrevious(); //get the previous DOM element from myElement
387                 
388         Returns:
389                 the sibling element or undefined if none found.
390         */
391         
392         getPrevious: function(){
393                 return this.getBrother('previous');
394         },
395         
396         /*
397         Property: getNext
398                 Works as Element.getPrevious, but tries to find the nextSibling.
399         */
400         
401         getNext: function(){
402                 return this.getBrother('next');
403         },
404         
405         /*
406         Property: getNext
407                 Works as <Element.getPrevious>, but tries to find the firstChild.
408         */
410         getFirst: function(){
411                 var el = this.firstChild;
412                 while ($type(el) == 'textnode') el = el.nextSibling;
413                 return $(el);
414         },
416         /*
417         Property: getLast
418                 Works as <Element.getPrevious>, but tries to find the lastChild.
419         */
421         getLast: function(){
422                 var el = this.lastChild;
423                 while ($type(el) == 'textnode')
424                 el = el.previousSibling;
425                 return $(el);
426         },
428         /*      
429         Property: setProperty
430                 Sets an attribute for the Element.
431                 
432         Arguments:
433                 property - the property to assign the value passed in
434                 value - the value to assign to the property passed in
435                 
436         Example:
437                 >$('myImage').setProperty('src', 'whatever.gif'); //myImage now points to whatever.gif for its source
438         */
440         setProperty: function(property, value){
441                 var el = false;
442                 switch(property){
443                         case 'class': this.className = value; break;
444                         case 'style': this.setStyles(value); break;
445                         case 'name': if (window.ActiveXObject && this.getTag() == 'input'){
446                                 el = $(document.createElement('<input name="'+value+'" />'));
447                                 $A(this.attributes).each(function(attribute){
448                                         if (attribute.name != 'name') el.setProperty(attribute.name, attribute.value);
449                                         
450                                 });
451                                 if (this.parentNode) this.replaceWith(el);
452                         };
453                         default: this.setAttribute(property, value);
454                 }
455                 return el || this;
456         },
457         
458         /*      
459         Property: setProperties
460                 Sets numerous attributes for the Element.
461                 
462         Arguments:
463                 source - an object with key/value pairs.
464                 
465         Example:
466                 >$('myElement').setProperties({
467                 >       src: 'whatever.gif',
468                 >       alt: 'whatever dude'
469                 >});
470                 ><img src="whatever.gif" alt="whatever dude">
471         */
472         
473         setProperties: function(source){
474                 for (var property in source) this.setProperty(property, source[property]);
475                 return this;
476         },
477         
478         /*
479         Property: setHTML
480                 Sets the innerHTML of the Element.
481                 
482         Arguments:
483                 html - the new innerHTML for the element.
484                 
485         Example:
486                 >$('myElement').setHTML(newHTML) //the innerHTML of myElement is now = newHTML
487         */
488         
489         setHTML: function(html){
490                 this.innerHTML = html;
491                 return this;
492         },
493         
494         /*      
495         Property: getProperty
496                 Gets the an attribute of the Element.
497                 
498         Arguments:
499                 property - the attribute to retrieve
500                 
501         Example:
502                 >$('myImage').getProperty('src') // returns whatever.gif
503                 
504         Returns:
505                 the value, or an empty string
506         */
507         
508         getProperty: function(property){
509                 return this.getAttribute(property);
510         },
511         
512         /*
513         Property: getTag
514                 Returns the tagName of the element in lower case.
515                 
516         Example:
517                 >$('myImage').getTag() // returns 'img'
518                 
519         Returns:
520                 The tag name in lower case
521         */
522         
523         getTag: function(){
524                 return this.tagName.toLowerCase();
525         },
526         
527         getOffset: function(what){
528                 what = what.capitalize();
529                 var el = this;
530                 var offset = 0;
531                 do {
532                         offset += el['offset'+what] || 0;
533                         el = el.offsetParent;
534                 } while (el);
535                 return offset;
536         },
538         /*      
539         Property: getTop
540                 Returns the distance from the top of the window to the Element.
541         */
542         
543         getTop: function(){
544                 return this.getOffset('top');
545         },
546         
547         /*      
548         Property: getLeft
549                 Returns the distance from the left of the window to the Element.
550         */
551         
552         getLeft: function(){
553                 return this.getOffset('left');
554         },
555         
556         /*      
557         Property: getValue
558                 Returns the value of the Element, if its tag is textarea, select or input.
559         */
560         
561         getValue: function(){
562                 var value = false;
563                 switch(this.getTag()){
564                         case 'select': value = this.getElementsByTagName('option')[this.selectedIndex].value; break;
565                         case 'input': if ( (this.checked && ['checkbox', 'radio'].test(this.type)) || (['hidden', 'text', 'password'].test(this.type)) ) 
566                                 value = this.value; break;
567                         case 'textarea': value = this.value;
568                 }
569                 return value;
570         }
574 new Object.Native(Element);
576 Element.extend({
577         hasClassName: Element.prototype.hasClass,
578         addClassName: Element.prototype.addClass,
579         removeClassName: Element.prototype.removeClass,
580         toggleClassName: Element.prototype.toggleClass
583 /* Section: Utility Functions  */
586 Function: $Element
587         Applies a method with the passed in args to the passed in element. Useful if you dont want to extend the element
588                 
589         Arguments:
590                 el - the element
591                 method - a string representing the Element Class method to execute on that element
592                 args - an array representing the arguments to pass to that method
593                 
594         Example:
595                 >$Element(el, 'hasClass', className) //true or false
598 function $Element(el, method, args){
599         if ($type(args) != 'array') args = [args];
600         return Element.prototype[method].apply(el, args);
604 Function: $()
605         returns the element passed in with all the Element prototypes applied.
606         
607 Arguments:
608         el - a reference to an actual element or a string representing the id of an element
609                 
610 Example:
611         >$('myElement') // gets a DOM element by id with all the Element prototypes applied.
612         >var div = document.getElementById('myElement');
613         >$(div) //returns an Element also with all the mootools extentions applied.
614                 
615         You'll use this when you aren't sure if a variable is an actual element or an id, as
616         well as just shorthand for document.getElementById().
617                 
618 Returns:
619         a DOM element or false (if no id was found)
620                 
621 Note:
622         you need to call $ on an element only once to get all the prototypes.
623         But its no harm to call it multiple times, as it will detect if it has been already extended.
626 function $(el){
627         if ($type(el) == 'string') el = document.getElementById(el);
628         if ($type(el) == 'element'){
629                 if (!el.extend){
630                         Unload.elements.push(el);
631                         el.extend = Object.extend;
632                         el.extend(Element.prototype);
633                 }
634                 return el;
635         } else return false;
638 window.addEvent = document.addEvent = Element.prototype.addEvent;
639 window.removeEvent = document.removeEvent = Element.prototype.removeEvent;
641 var Unload = {
643         elements: [], functions: [], vars: [],
645         unload: function(){
646                 Unload.functions.each(function(fn){
647                         fn();
648                 });
649                 
650                 window.removeEvent('unload', window.removeFunction);
651                 
652                 Unload.elements.each(function(el){
653                         for(var p in Element.prototype){
654                                 window[p] = null;
655                                 document[p] = null;
656                                 el[p] = null;
657                         }
658                         el.extend = null;
659                 });
660         }
661         
664 window.removeFunction = Unload.unload;
665 window.addEvent('unload', window.removeFunction);