root/trunk/dom.js

Revision 261, 20.9 kB (checked in by janine, 21 months ago)

New functions for finding the X and Y positions of an element.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2DOM Library - Copyright 2005 Six Apart
3$Id$
4
5Copyright (c) 2005, Six Apart, Ltd.
6All rights reserved.
7
8Redistribution and use in source and binary forms, with or without
9modification, are permitted provided that the following conditions are
10met:
11
12    * Redistributions of source code must retain the above copyright
13notice, this list of conditions and the following disclaimer.
14
15    * Redistributions in binary form must reproduce the above
16copyright notice, this list of conditions and the following disclaimer
17in the documentation and/or other materials provided with the
18distribution.
19
20    * Neither the name of "Six Apart" nor the names of its
21contributors may be used to endorse or promote products derived from
22this software without specific prior written permission.
23
24THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36*/
37
38
39/* Node class */
40
41if( !defined( window.Node ) )
42    Node = {};
43
44try {
45    Node.extend( {
46        ELEMENT_NODE: 1,
47        ATTRIBUTE_NODE: 2,
48        TEXT_NODE: 3,
49        CDATA_SECTION_NODE: 4, 
50        COMMENT_NODE: 8,   
51        DOCUMENT_NODE: 9,
52        DOCUMENT_FRAGMENT_NODE: 11
53    } );
54} catch( e ) {}
55
56
57/* DOM class */
58
59if( !defined( window.DOM ) )
60    DOM = {};
61
62
63DOM.extend( {
64    getElement: function( e ) {
65        return (typeof e == "string" || typeof e == "number") ? document.getElementById( e ) : e;
66    },
67
68
69    addEventListener: function( e, eventName, func, useCapture ) {
70        try {
71            if( e.addEventListener )
72                e.addEventListener( eventName, func, useCapture );
73            else if( e.attachEvent )
74                e.attachEvent( "on" + eventName, func );
75            else
76                e[ "on" + eventName ] = func;
77        } catch( e ) {}
78    },
79
80
81    removeEventListener: function( e, eventName, func, useCapture ) {
82        try {
83            if( e.removeEventListener )
84                e.removeEventListener( eventName, func, useCapture );
85            else if( e.detachEvent )
86                e.detachEvent( "on" + eventName, func );
87            else
88                e[ "on" + eventName ] = undefined;
89        } catch( e ) {}
90    },
91   
92   
93    focus: function( e ) {
94        try {
95            e = DOM.getElement( e );
96            e.focus();
97        } catch( e ) {}
98    },
99
100
101    blur: function( e ) {
102        try {
103            e = DOM.getElement( e );
104            e.blur();
105        } catch( e ) {}
106    },
107   
108
109    /* style */
110   
111    getComputedStyle: function( e ) {
112        if( e.currentStyle )
113            return e.currentStyle;
114        var style = {};
115        var owner = DOM.getOwnerDocument( e );
116        if( owner && owner.defaultView && owner.defaultView.getComputedStyle ) {           
117            try {
118                style = owner.defaultView.getComputedStyle( e, null );
119            } catch( e ) {}
120        }
121        return style;
122    },
123
124
125    getStyle: function( e, p ) {
126        var s = DOM.getComputedStyle( e );
127        return s[ p ];
128    },
129
130
131    // given a window (or defaulting to current window), returns
132    // object with .x and .y of client's usable area
133    getClientDimensions: function( w ) {
134        if( !w )
135            w = window;
136
137        var d = {};
138
139        // most browsers
140        if( w.innerHeight ) {
141            d.x = w.innerWidth;
142            d.y = w.innerHeight;
143            return d;
144        }
145
146        // IE6, strict
147        var de = w.document.documentElement;
148        if( de && de.clientHeight ) {
149            d.x = de.clientWidth;
150            d.y = de.clientHeight;
151            return d;
152        }
153
154        // IE, misc
155        if( document.body ) {
156            d.x = document.body.clientWidth;
157            d.y = document.body.clientHeight;
158            return d;
159        }
160       
161        return undefined;
162    },
163
164
165    getDimensions: function( e ) {
166        if( !e )
167            return undefined;
168
169        var style = DOM.getComputedStyle( e );
170
171        return {
172            offsetLeft: e.offsetLeft,
173            offsetTop: e.offsetTop,
174            offsetWidth: e.offsetWidth,
175            offsetHeight: e.offsetHeight,
176            clientWidth: e.clientWidth,
177            clientHeight: e.clientHeight,
178           
179            offsetRight: e.offsetLeft + e.offsetWidth,
180            offsetBottom: e.offsetTop + e.offsetHeight,
181            clientLeft: finiteInt( style.borderLeftWidth ) + finiteInt( style.paddingLeft ),
182            clientTop: finiteInt( style.borderTopWidth ) + finiteInt( style.paddingTop ),
183            clientRight: e.clientLeft + e.clientWidth,
184            clientBottom: e.clientTop + e.clientHeight
185        };
186    },
187
188
189    getAbsoluteDimensions: function( e ) {
190        var d = DOM.getDimensions( e );
191        if( !d )
192            return d;
193        d.absoluteLeft = d.offsetLeft;
194        d.absoluteTop = d.offsetTop;
195        d.absoluteRight = d.offsetRight;
196        d.absoluteBottom = d.offsetBottom;
197        var bork = 0;
198        while( e ) {
199            try { // IE 6 sometimes gives an unwarranted error ("htmlfile: Unspecified error").
200                e = e.offsetParent;
201            } catch ( err ) {
202                log( "In DOM.getAbsoluteDimensions: " + err.message ); 
203                if ( ++bork > 25 )
204                    return null;
205            }
206            if( !e )
207                return d;
208            d.absoluteLeft += e.offsetLeft;
209            d.absoluteTop += e.offsetTop;
210            d.absoluteRight += e.offsetLeft;
211            d.absoluteBottom += e.offsetTop;
212        }
213        return d;
214    },
215   
216   
217    getIframeAbsoluteDimensions: function( e ) {
218        var d = DOM.getAbsoluteDimensions( e );
219        if( !d )
220            return d;
221        var iframe = DOM.getOwnerIframe( e );
222        if( !defined( iframe ) )
223            return d;
224       
225        var d2 = DOM.getIframeAbsoluteDimensions( iframe );
226        var scroll = DOM.getWindowScroll( iframe.contentWindow );
227        var left = d2.absoluteLeft - scroll.left;
228        var top = d2.absoluteTop - scroll.top;
229       
230        d.absoluteLeft += left;
231        d.absoluteTop += top;
232        d.absoluteRight += left;
233        d.absoluteBottom += top;
234       
235        return d;
236    },
237   
238   
239    setLeft: function( e, v ) { e.style.left = finiteInt( v ) + "px"; },
240    setTop: function( e, v ) { e.style.top = finiteInt( v ) + "px"; },
241    setRight: function( e, v ) { e.style.right = finiteInt( v ) + "px"; },
242    setBottom: function( e, v ) { e.style.bottom = finiteInt( v ) + "px"; },
243    setWidth: function( e, v ) { e.style.width = max( 0, finiteInt( v ) ) + "px"; },
244    setHeight: function( e, v ) { e.style.height = max( 0, finiteInt( v ) ) + "px"; },
245    setZIndex: function( e, v ) { e.style.zIndex = finiteInt( v ); },
246
247
248    getWindowScroll: function( w ) {
249        var s = {
250            left: 0,
251            top: 0
252        };
253
254        if (!w) w = window;
255        var d = w.document;
256        var de = d.documentElement;
257
258        // most browsers
259        if ( defined( w.pageXOffset ) ) {
260            s.left = w.pageXOffset;
261            s.top = w.pageYOffset;
262        }
263
264        // ie
265        else if( de && defined( de.scrollLeft ) ) {
266            s.left = de.scrollLeft;
267            s.top = de.scrollTop;
268        }
269
270        // safari
271        else if( defined( w.scrollX ) ) {
272            s.left = w.scrollX;
273            s.top = w.scrollY;
274        }
275
276        // opera
277        else if( d.body && defined( d.body.scrollLeft ) ) {
278            s.left = d.body.scrollLeft;
279            s.top = d.body.scrollTop;
280        }
281
282
283        return s;
284    },
285
286
287    getAbsoluteCursorPosition: function( event ) {
288        event = event || window.event;
289        var s = DOM.getWindowScroll( window );
290        return {
291            x: s.left + event.clientX,
292            y: s.top + event.clientY
293        };
294    },
295   
296   
297    invisibleStyle: {
298        display: "block",
299        position: "absolute",
300        left: 0,
301        top: 0,
302        width: 0,
303        height: 0,
304        margin: 0,
305        border: 0,
306        padding: 0,
307        fontSize: "0.1px",
308        lineHeight: 0,
309        opacity: 0,
310        MozOpacity: 0,
311        filter: "alpha(opacity=0)"
312    },
313   
314   
315    makeInvisible: function( e ) {
316        for( var p in this.invisibleStyle ) {
317            if( this.invisibleStyle.hasOwnProperty( p ) )
318                e.style[ p ] = this.invisibleStyle[ p ];
319        }
320    },
321
322
323    /* text and selection related methods */
324
325    mergeTextNodes: function( n ) {
326        var c = 0;
327        while( n ) {
328            if( n.nodeType == Node.TEXT_NODE && n.nextSibling && n.nextSibling.nodeType == Node.TEXT_NODE ) {
329                n.nodeValue += n.nextSibling.nodeValue;
330                n.parentNode.removeChild( n.nextSibling );
331                c++;
332            } else {
333                if( n.firstChild )
334                    c += DOM.mergeTextNodes( n.firstChild );
335                n = n.nextSibling;
336            }
337        }
338        return c;
339    },
340   
341   
342    selectElement: function( e ) { 
343        var d = e.ownerDocument; 
344       
345        // internet explorer 
346        if( d.body.createControlRange ) { 
347            var r = d.body.createControlRange(); 
348            r.addElement( e ); 
349            r.select(); 
350        } 
351    }, 
352   
353   
354    /* dom methods */
355   
356    isImmutable: function( n ) {
357        try {
358            if( n.getAttribute( "contenteditable" ) == "false" )
359                return true;
360        } catch( e ) {}
361        return false;
362    },
363   
364   
365    getImmutable: function( n ) {
366        var immutable = null;
367        while( n ) {
368            if( DOM.isImmutable( n ) )
369                immutable = n;
370            n = n.parentNode;
371        }
372        return immutable;
373    },
374
375
376    getOwnerDocument: function( n ) {
377        if( !n )
378            return document;
379        if( n.ownerDocument )
380            return n.ownerDocument;
381        if( n.getElementById )
382            return n;
383        return document;
384    },
385
386
387    getOwnerWindow: function( n ) {
388        if( !n )
389            return window;
390        if( n.parentWindow )
391            return n.parentWindow;
392        var doc = DOM.getOwnerDocument( n );
393        if( doc && doc.defaultView )
394            return doc.defaultView;
395        return window;
396    },
397   
398   
399    getOwnerIframe: function( n ) {
400        if( !n )
401            return undefined;
402        var nw = DOM.getOwnerWindow( n );
403        var nd = DOM.getOwnerDocument( n );
404        var pw = nw.parent || nw.parentWindow;
405        if( !pw )
406            return undefined;
407        var parentDocument = pw.document;
408        var es = parentDocument.getElementsByTagName( "iframe" );
409        for( var i = 0; i < es.length; i++ ) {
410            var e = es[ i ];
411            try {
412                var d = e.contentDocument || e.contentWindow.document;
413                if( d === nd )
414                    return e;
415            }catch(err) {};
416        }
417        return undefined;
418    },
419
420
421    filterElementsByClassName: function( es, className ) {
422        var filtered = [];
423        for( var i = 0; i < es.length; i++ ) {
424            var e = es[ i ];
425            if( DOM.hasClassName( e, className ) )
426                filtered[ filtered.length ] = e;
427        }
428        return filtered;
429    },
430   
431   
432    filterElementsByAttribute: function( es, attr ) {
433        if( !es )
434            return [];
435        if( !defined( attr ) || attr == null || attr == "" )
436            return es;
437        var filtered = [];
438        for( var i = 0; i < es.length; i++ ) {
439            var element = es[ i ];
440            if( !element )
441                continue;
442            if( element.getAttribute && ( element.getAttribute( attr ) ) )
443                filtered[ filtered.length ] = element;
444        }
445        return filtered;
446    },
447
448
449    filterElementsByTagName: function( es, tagName ) {
450        if( tagName == "*" )
451            return es;
452        var filtered = [];
453        tagName = tagName.toLowerCase();
454        for( var i = 0; i < es.length; i++ ) {
455            var e = es[ i ];
456            if( e.tagName && e.tagName.toLowerCase() == tagName )
457                filtered[ filtered.length ] = e;
458        }
459        return filtered;
460    },
461
462
463    getElementsByTagAndAttribute: function( root, tagName, attr ) {
464        if( !root )
465            root = document;
466        var es = root.getElementsByTagName( tagName );
467        return DOM.filterElementsByAttribute( es, attr );
468    },
469   
470   
471    getElementsByAttribute: function( root, attr ) {
472        return DOM.getElementsByTagAndAttribute( root, "*", attr );
473    },
474
475
476    getElementsByAttributeAndValue: function( root, attr, value ) {
477        var es = DOM.getElementsByTagAndAttribute( root, "*", attr );
478        var filtered = [];
479        for ( var i = 0; i < es.length; i++ )
480            if ( es[ i ].getAttribute( attr ) == value )
481                filtered.push( es[ i ] );
482        return filtered;
483    },
484   
485
486    getElementsByTagAndClassName: function( root, tagName, className ) {
487        if( !root )
488            root = document;
489        var elements = root.getElementsByTagName( tagName );
490        return DOM.filterElementsByClassName( elements, className );
491    },
492
493
494    getElementsByClassName: function( root, className ) {
495        return DOM.getElementsByTagAndClassName( root, "*", className );
496    },
497
498
499    getAncestors: function( n, includeSelf ) {
500        if( !n )
501            return [];
502        var as = includeSelf ? [ n ] : [];
503        n = n.parentNode;
504        while( n ) {
505            as.push( n );
506            n = n.parentNode;
507        }
508        return as;
509    },
510   
511   
512    getAncestorsByTagName: function( n, tagName, includeSelf ) {
513        var es = DOM.getAncestors( n, includeSelf );
514        return DOM.filterElementsByTagName( es, tagName );
515    },
516   
517   
518    getFirstAncestorByTagName: function( n, tagName, includeSelf ) {
519        return DOM.getAncestorsByTagName( n, tagName, includeSelf )[ 0 ];
520    },
521
522
523    getAncestorsByClassName: function( n, className, includeSelf ) {
524        var es = DOM.getAncestors( n, includeSelf );
525        return DOM.filterElementsByClassName( es, className );
526    },
527
528
529    getFirstAncestorByClassName: function( n, className, includeSelf ) {
530        return DOM.getAncestorsByClassName( n, className, includeSelf )[ 0 ];
531    },
532
533
534    getAncestorsByTagAndClassName: function( n, tagName, className, includeSelf ) {
535        var es = DOM.getAncestorsByTagName( n, tagName, includeSelf );
536        return DOM.filterElementsByClassName( es, className );
537    },
538
539
540    getFirstAncestorByTagAndClassName: function( n, tagName, className, includeSelf ) {
541        return DOM.getAncestorsByTagAndClassName( n, tagName, className, includeSelf )[ 0 ];
542    },
543
544
545    getPreviousElement: function( n ) {
546        n = n.previousSibling;
547        while( n ) {
548            if( n.nodeType == Node.ELEMENT_NODE )
549                return n;
550            n = n.previousSibling;
551        }
552        return null;
553    },
554
555
556    getNextElement: function( n ) {
557        n = n.nextSibling;
558        while( n ) {
559            if( n.nodeType == Node.ELEMENT_NODE )
560                return n;
561            n = n.nextSibling;
562        }
563        return null;
564    },
565
566
567    isInlineNode: function( n ) {
568        // text nodes are inline
569        if( n.nodeType == Node.TEXT_NODE )
570            return n;
571
572        // document nodes are non-inline
573        if( n.nodeType == Node.DOCUMENT_NODE )
574            return false;
575
576        // all nonelement nodes are inline
577        if( n.nodeType != Node.ELEMENT_NODE )
578            return n;
579
580        // br elements are not inline
581        if( n.tagName && n.tagName.toLowerCase() == "br" )
582            return false;
583
584        // examine the style property of the inline n
585        var display = DOM.getStyle( n, "display" ); 
586        if( display && display.indexOf( "inline" ) >= 0 ) 
587            return n;
588    },
589   
590   
591    isTextNode: function( n ) {
592        if( n.nodeType == Node.TEXT_NODE )
593            return n;
594    },
595   
596   
597    isInlineTextNode: function( n ) {
598        if( n.nodeType == Node.TEXT_NODE )
599            return n;
600        if( !DOM.isInlineNode( n ) )
601            return null;
602    },
603
604
605    /* this and the following classname functions honor w3c case-sensitive classnames */
606
607    getClassNames: function( e ) {
608        if( !e || !e.className )
609            return [];
610        return e.className.split( /\s+/g );
611    },
612
613
614    hasClassName: function( e, className ) {
615        if( !e || !e.className )
616            return false;
617        var cs = DOM.getClassNames( e );
618        for( var i = 0; i < cs.length; i++ ) {
619            if( cs[ i ] == className )
620                return true;
621        }
622        return false;
623    },
624
625
626    addClassName: function( e, className ) {
627        if( !e || !className )
628            return false;
629        var cs = DOM.getClassNames( e );
630        for( var i = 0; i < cs.length; i++ ) {
631            if( cs[ i ] == className )
632                return true;
633        }
634        cs.push( className );
635        e.className = cs.join( " " );
636        return false;
637    },
638
639
640    removeClassName: function( e, className ) {
641        var r = false;
642        if( !e || !e.className || !className )
643            return r;
644        var cs = (e.className && e.className.length)
645            ? e.className.split( /\s+/g )
646            : [];
647        var ncs = [];
648        for( var i = 0; i < cs.length; i++ ) {
649            if( cs[ i ] == className ) {
650                r = true;
651                continue;
652            }
653            ncs.push( cs[ i ] );
654        }
655        if( r )
656            e.className = ncs.join( " " );
657        return r;
658    },
659   
660   
661    /* tree manipulation methods */
662   
663    replaceWithChildNodes: function( n ) {
664        var firstChild = n.firstChild;
665        var parentNode = n.parentNode;
666        while( n.firstChild )
667            parentNode.insertBefore( n.removeChild( n.firstChild ), n );
668        parentNode.removeChild( n );
669        return firstChild;
670    },
671   
672   
673    /* factory methods */
674   
675    createInvisibleInput: function( d ) {
676        if( !d )
677            d = window.document;
678        var e = document.createElement( "input" );
679        e.setAttribute( "autocomplete", "off" );
680        e.autocomplete = "off";
681        DOM.makeInvisible( e );
682        return e;
683    },
684
685
686    getMouseEventAttribute: function( event, a ) {
687        if( !a )
688            return;
689        var es = DOM.getAncestors( event.target, true );
690        for( var i = 0; i < es.length; i++ ) {
691            try {
692                var e = es[ i ]
693                var v = e.getAttribute ? e.getAttribute( a ) : null;
694                if( v ) {
695                    event.attributeElement = e;
696                    event.attribute = v;
697                    return v;
698                }
699            } catch( e ) {}
700        }
701    },
702   
703
704    setElementAttribute: function( e, a, v ) {
705        /* safari workaround
706         * safari's setAttribute assumes you want to use a namespace
707         * when you have a colon in your attribute
708         */
709        if ( navigator.userAgent.toLowerCase().match(/webkit/) ) {
710            var at = e.attributes;
711            for ( var i = 0; i < at.length; i++ )
712                if ( at[ i ].name == a )
713                    return at[ i ].nodeValue = v;
714        } else
715            e.setAttribute( a, v );
716    },
717
718
719    swapAttributes: function( e, tg, at ) {
720        var ar = e.getAttribute( tg );
721        if( !ar )
722            return false;
723       
724        /* clone the node with all children */
725        if ( e.tagName.toLowerCase() == 'script' ) {
726            /* only clone and replace script tags */
727            var cl = e.cloneNode( true );
728            if ( !cl )
729                return false;
730
731            DOM.setElementAttribute( cl, at, ar );
732            cl.removeAttribute( tg );
733       
734            /* replace new, old */
735            return e.parentNode.replaceChild( cl, e );
736        } else {
737            DOM.setElementAttribute( e, at, ar );
738            e.removeAttribute( tg );
739        }
740    },
741
742
743    findPosX: function( e ) {
744        var curleft = 0;
745
746        if (e.offsetParent) {
747            while (1) {
748                curleft += e.offsetLeft;
749                if (!e.offsetParent) {
750                    break;
751                }
752                e = e.offsetParent;
753            }
754        } else if (e.x) {
755            curleft += e.x;
756        }
757
758        return curleft;
759    },
760
761
762    findPosY: function( e ) {
763        var curtop = 0;
764
765        if (e.offsetParent) {
766            while (1) {
767                curtop += e.offsetTop;
768                if (!e.offsetParent) {
769                    break;
770                }
771                e = e.offsetParent;
772            }
773        } else if (e.y) {
774            curtop += e.y;
775        }
776
777        return curtop;
778    }
779   
780   
781} );
782
783
784$ = DOM.getElement;
Note: See TracBrowser for help on using the browser.