/*\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
case 'style':\r
// IE does not return inline styles via getAttribute(). See #2947.\r
return this.$.style.cssText;\r
+\r
+ case 'contenteditable':\r
+ case 'contentEditable':\r
+ return this.$.attributes.getNamedItem( 'contentEditable' ).specified ?\r
+ this.$.getAttribute( 'contentEditable' ) : null;\r
}\r
\r
return standard.call( this, name );\r
* 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
return false;\r
},\r
\r
- isEditable : function()\r
+ /**\r
+ * Decide whether one element is able to receive cursor.\r
+ * @param {Boolean} [textCursor=true] Only consider element that could receive text child.\r
+ */\r
+ isEditable : function( textCursor )\r
{\r
- if ( this.isReadOnly() )\r
- return false;\r
-\r
- // Get the element name.\r
var name = this.getName();\r
\r
- // Get the element DTD (defaults to span for unknown elements).\r
- var dtd = !CKEDITOR.dtd.$nonEditable[ name ]\r
- && ( CKEDITOR.dtd[ name ] || CKEDITOR.dtd.span );\r
+ 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
+ || CKEDITOR.dtd.$empty[ name ] )\r
+ {\r
+ return false;\r
+ }\r
\r
- // In the DTD # == text node.\r
- return ( dtd && dtd['#'] );\r
+ if ( textCursor !== false )\r
+ {\r
+ // Get the element DTD (defaults to span for unknown elements).\r
+ var dtd = CKEDITOR.dtd[ name ] || CKEDITOR.dtd.span;\r
+ // In the DTD # == text node.\r
+ return ( dtd && dtd[ '#'] );\r
+ }\r
+\r
+ return true;\r
},\r
\r
isIdentical : function( otherElement )\r
*/\r
isVisible : function()\r
{\r
- var isVisible = !!this.$.offsetHeight && this.getComputedStyle( 'visibility' ) != 'hidden',\r
+ var isVisible = ( this.$.offsetHeight || this.$.offsetWidth ) && this.getComputedStyle( 'visibility' ) != 'hidden',\r
elementWindow,\r
elementWindowFrame;\r
\r
}\r
}\r
\r
- return isVisible;\r
+ return !!isVisible;\r
},\r
\r
/**\r
this.$.tabIndex = value;\r
else if ( name == 'checked' )\r
this.$.checked = value;\r
+ else if ( name == 'contenteditable' )\r
+ standard.call( this, 'contentEditable', value );\r
else\r
standard.apply( this, arguments );\r
return this;\r
name = 'className';\r
else if ( name == 'tabindex' )\r
name = 'tabIndex';\r
+ else if ( name == 'contenteditable' )\r
+ name = 'contentEditable';\r
standard.call( this, name );\r
};\r
}\r
*/\r
removeStyle : function( name )\r
{\r
- this.setStyle( name, '' );\r
- if ( this.$.style.removeAttribute )\r
- this.$.style.removeAttribute( CKEDITOR.tools.cssStyleToDomStyle( name ) );\r
+ // Removes the specified property from the current style object.\r
+ var $ = this.$.style;\r
+ $.removeProperty ? $.removeProperty( name ) : $.removeAttribute( CKEDITOR.tools.cssStyleToDomStyle( name ) );\r
\r
if ( !this.$.style.cssText )\r
this.removeAttribute( 'style' );\r
*/\r
setOpacity : function( opacity )\r
{\r
- if ( CKEDITOR.env.ie )\r
+ if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )\r
{\r
opacity = Math.round( opacity * 100 );\r
this.setStyle( 'filter', opacity >= 100 ? '' : 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + opacity + ')' );\r
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
getDocumentPosition : function( refDocument )\r
{\r
var x = 0, y = 0,\r
- body = this.getDocument().getBody(),\r
- quirks = this.getDocument().$.compatMode == 'BackCompat';\r
-\r
- var doc = this.getDocument();\r
+ doc = this.getDocument(),\r
+ body = doc.getBody(),\r
+ quirks = doc.$.compatMode == 'BackCompat';\r
\r
if ( document.documentElement[ "getBoundingClientRect" ] )\r
{\r
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
+ // 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
+ // 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
+ /**\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
+ // 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
- offset += this.$.offsetHeight || 0;\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
- // 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
+ 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
- // Append the offsets for the entire element hierarchy.\r
- var elementPosition = this.getDocumentPosition();\r
- offset += elementPosition.y;\r
+ var win = parent.getWindow();\r
\r
- // offset value might be out of range(nagative), fix it(#3692).\r
- offset = offset < 0 ? 0 : offset;\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