JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.6.3
[ckeditor.git] / _source / core / dom / element.js
index 7adb189..c83674a 100644 (file)
@@ -1,5 +1,5 @@
 /*\r
-Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved.\r
 For licensing, see LICENSE.html or http://ckeditor.com/license\r
 */\r
 \r
@@ -580,7 +580,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
                 * in the future.\r
                 * @returns {String} The text value.\r
                 * @example\r
-                * var element = CKEDITOR.dom.element.createFromHtml( '<div>Same <i>text</i>.</div>' );\r
+                * var element = CKEDITOR.dom.element.createFromHtml( '<div>Sample <i>text</i>.</div>' );\r
                 * alert( <b>element.getText()</b> );  // "Sample text."\r
                 */\r
                getText : function()\r
@@ -730,6 +730,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
                        if ( this.isReadOnly()\r
                                        || this.getComputedStyle( 'display' ) == 'none'\r
                                        || this.getComputedStyle( 'visibility' ) == 'hidden'\r
+                                       || this.is( 'a' ) && this.data( 'cke-saved-name' ) && !this.getChildCount()\r
                                        || CKEDITOR.dtd.$nonEditable[ name ] )\r
                        {\r
                                return false;\r
@@ -1261,12 +1262,13 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
                                        if ( CKEDITOR.env.ie || CKEDITOR.env.opera )\r
                                        {\r
                                                var element = this.$,\r
+                                                       elements = element.getElementsByTagName("*"),\r
                                                        e,\r
                                                        i = 0;\r
 \r
                                                element.unselectable = 'on';\r
 \r
-                                               while ( ( e = element.all[ i++ ] ) )\r
+                                               while ( ( e = elements[ i++ ] ) )\r
                                                {\r
                                                        switch ( e.tagName.toLowerCase() )\r
                                                        {\r
@@ -1396,40 +1398,143 @@ CKEDITOR.tools.extend( CKEDITOR.dom.element.prototype,
                        return { x : x, y : y };\r
                },\r
 \r
-               scrollIntoView : function( alignTop )\r
+               /**\r
+                * Make any page element visible inside the browser viewport.\r
+                * @param {Boolean} [alignToTop]\r
+                */\r
+               scrollIntoView : function( alignToTop )\r
                {\r
-                       // Get the element window.\r
-                       var win = this.getWindow(),\r
-                               winHeight = win.getViewPaneSize().height;\r
-\r
-                       // Starts from the offset that will be scrolled with the negative value of\r
-                       // the visible window height.\r
-                       var offset = winHeight * -1;\r
-\r
-                       // Append the view pane's height if align to top.\r
-                       // Append element height if we are aligning to the bottom.\r
-                       if ( alignTop )\r
-                               offset += winHeight;\r
-                       else\r
+                       var parent = this.getParent();\r
+                       if ( !parent ) return;\r
+\r
+                       // Scroll the element into parent container from the inner out.\r
+                       do\r
                        {\r
-                               offset += this.$.offsetHeight || 0;\r
+                               // Check ancestors that overflows.\r
+                               var overflowed =\r
+                                       parent.$.clientWidth && parent.$.clientWidth < parent.$.scrollWidth\r
+                                       || parent.$.clientHeight && parent.$.clientHeight < parent.$.scrollHeight;\r
+\r
+                               if ( overflowed )\r
+                                       this.scrollIntoParent( parent, alignToTop, 1 );\r
+\r
+                               // Walk across the frame.\r
+                               if ( parent.is( 'html' ) )\r
+                               {\r
+                                       var win = parent.getWindow();\r
 \r
-                               // Consider the margin in the scroll, which is ok for our current needs, but\r
-                               // needs investigation if we will be using this function in other places.\r
-                               offset += parseInt( this.getComputedStyle( 'marginBottom' ) || 0, 10 ) || 0;\r
+                                       // Avoid security error.\r
+                                       try\r
+                                       {\r
+                                               var iframe = win.$.frameElement;\r
+                                               iframe && ( parent = new CKEDITOR.dom.element( iframe ) );\r
+                                       }\r
+                                       catch(er){}\r
+                               }\r
                        }\r
+                       while ( ( parent = parent.getParent() ) );\r
+               },\r
 \r
-                       // Append the offsets for the entire element hierarchy.\r
-                       var elementPosition = this.getDocumentPosition();\r
-                       offset += elementPosition.y;\r
+               /**\r
+                * Make any page element visible inside one of the ancestors by scrolling the parent.\r
+                * @param {CKEDITOR.dom.element|CKEDITOR.dom.window} parent The container to scroll into.\r
+                * @param {Boolean} [alignToTop] Align the element's top side with the container's\r
+                * when <code>true</code> is specified; align the bottom with viewport bottom when\r
+                * <code>false</code> is specified. Otherwise scroll on either side with the minimum\r
+                * amount to show the element.\r
+                * @param {Boolean} [hscroll] Whether horizontal overflow should be considered.\r
+                */\r
+               scrollIntoParent : function( parent, alignToTop, hscroll )\r
+               {\r
+                       !parent && ( parent = this.getWindow() );\r
+\r
+                       var doc = parent.getDocument();\r
+                       var isQuirks = doc.$.compatMode == 'BackCompat';\r
 \r
-                       // offset value might be out of range(nagative), fix it(#3692).\r
-                       offset = offset < 0 ? 0 : offset;\r
+                       // On window <html> is scrolled while quirks scrolls <body>.\r
+                       if ( parent instanceof CKEDITOR.dom.window )\r
+                               parent = isQuirks ? doc.getBody() : doc.getDocumentElement();\r
+\r
+                       // Scroll the parent by the specified amount.\r
+                       function scrollBy( x, y )\r
+                       {\r
+                               // Webkit doesn't support "scrollTop/scrollLeft"\r
+                               // on documentElement/body element.\r
+                               if ( /body|html/.test( parent.getName() ) )\r
+                                       parent.getWindow().$.scrollBy( x, y );\r
+                               else\r
+                               {\r
+                                       parent.$[ 'scrollLeft' ] += x;\r
+                                       parent.$[ 'scrollTop' ] += y;\r
+                               }\r
+                       }\r
+\r
+                       // Figure out the element position relative to the specified window.\r
+                       function screenPos( element, refWin )\r
+                       {\r
+                               var pos = { x: 0, y: 0 };\r
+\r
+                               if ( !( element.is( isQuirks ? 'body' : 'html' ) ) )\r
+                               {\r
+                                       var box = element.$.getBoundingClientRect();\r
+                                       pos.x = box.left, pos.y = box.top;\r
+                               }\r
+\r
+                               var win = element.getWindow();\r
+                               if ( !win.equals( refWin ) )\r
+                               {\r
+                                       var outerPos = screenPos( CKEDITOR.dom.element.get( win.$.frameElement ), refWin );\r
+                                       pos.x += outerPos.x, pos.y += outerPos.y;\r
+                               }\r
+\r
+                               return pos;\r
+                       }\r
+\r
+                       // calculated margin size.\r
+                       function margin( element, side )\r
+                       {\r
+                               return parseInt( element.getComputedStyle( 'margin-' + side ) || 0, 10 ) || 0;\r
+                       }\r
+\r
+                       var win = parent.getWindow();\r
+\r
+                       var thisPos = screenPos( this, win ),\r
+                               parentPos = screenPos( parent, win ),\r
+                               eh = this.$.offsetHeight,\r
+                               ew = this.$.offsetWidth,\r
+                               ch = parent.$.clientHeight,\r
+                               cw = parent.$.clientWidth,\r
+                               lt,\r
+                               br;\r
+\r
+                       // Left-top margins.\r
+                       lt =\r
+                       {\r
+                               x : thisPos.x - margin( this, 'left' ) - parentPos.x || 0,\r
+                               y : thisPos.y - margin( this, 'top' ) - parentPos.y|| 0\r
+                       };\r
+\r
+                       // Bottom-right margins.\r
+                       br =\r
+                       {\r
+                               x : thisPos.x + ew + margin( this, 'right' ) - ( ( parentPos.x ) + cw ) || 0,\r
+                               y : thisPos.y + eh + margin( this, 'bottom' ) - ( ( parentPos.y ) + ch ) || 0\r
+                       };\r
+\r
+                       // 1. Do the specified alignment as much as possible;\r
+                       // 2. Otherwise be smart to scroll only the minimum amount;\r
+                       // 3. Never cut at the top;\r
+                       // 4. DO NOT scroll when already visible.\r
+                       if ( lt.y < 0 || br.y > 0 )\r
+                       {\r
+                               scrollBy( 0,\r
+                                                 alignToTop === true ? lt.y :\r
+                                                 alignToTop === false ? br.y :\r
+                                                 lt.y < 0 ? lt.y : br.y );\r
+                       }\r
 \r
-                       // Scroll the window to the desired position, if not already visible(#3795).\r
-                       var currentScroll = win.getScrollPosition().y;\r
-                       if ( offset > currentScroll || offset < currentScroll - winHeight )\r
-                               win.$.scrollTo( 0, offset );\r
+                       if ( hscroll && ( lt.x < 0 || br.x > 0 ) )\r
+                               scrollBy( lt.x < 0 ? lt.x : br.x, 0 );\r
                },\r
 \r
                setState : function( state )\r