+++ /dev/null
-/*\r
- * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
- * Copyright (C) 2003-2009 Frederico Caldeira Knabben\r
- *\r
- * == BEGIN LICENSE ==\r
- *\r
- * Licensed under the terms of any of the following licenses at your\r
- * choice:\r
- *\r
- * - GNU General Public License Version 2 or later (the "GPL")\r
- * http://www.gnu.org/licenses/gpl.html\r
- *\r
- * - GNU Lesser General Public License Version 2.1 or later (the "LGPL")\r
- * http://www.gnu.org/licenses/lgpl.html\r
- *\r
- * - Mozilla Public License Version 1.1 or later (the "MPL")\r
- * http://www.mozilla.org/MPL/MPL-1.1.html\r
- *\r
- * == END LICENSE ==\r
- *\r
- * Utility functions to work with the DOM.\r
- */\r
-\r
-var FCKDomTools =\r
-{\r
- /**\r
- * Move all child nodes from one node to another.\r
- */\r
- MoveChildren : function( source, target, toTargetStart )\r
- {\r
- if ( source == target )\r
- return ;\r
-\r
- var eChild ;\r
-\r
- if ( toTargetStart )\r
- {\r
- while ( (eChild = source.lastChild) )\r
- target.insertBefore( source.removeChild( eChild ), target.firstChild ) ;\r
- }\r
- else\r
- {\r
- while ( (eChild = source.firstChild) )\r
- target.appendChild( source.removeChild( eChild ) ) ;\r
- }\r
- },\r
-\r
- MoveNode : function( source, target, toTargetStart )\r
- {\r
- if ( toTargetStart )\r
- target.insertBefore( FCKDomTools.RemoveNode( source ), target.firstChild ) ;\r
- else\r
- target.appendChild( FCKDomTools.RemoveNode( source ) ) ;\r
- },\r
-\r
- // Remove blank spaces from the beginning and the end of the contents of a node.\r
- TrimNode : function( node )\r
- {\r
- this.LTrimNode( node ) ;\r
- this.RTrimNode( node ) ;\r
- },\r
-\r
- LTrimNode : function( node )\r
- {\r
- var eChildNode ;\r
-\r
- while ( (eChildNode = node.firstChild) )\r
- {\r
- if ( eChildNode.nodeType == 3 )\r
- {\r
- var sTrimmed = eChildNode.nodeValue.LTrim() ;\r
- var iOriginalLength = eChildNode.nodeValue.length ;\r
-\r
- if ( sTrimmed.length == 0 )\r
- {\r
- node.removeChild( eChildNode ) ;\r
- continue ;\r
- }\r
- else if ( sTrimmed.length < iOriginalLength )\r
- {\r
- eChildNode.splitText( iOriginalLength - sTrimmed.length ) ;\r
- node.removeChild( node.firstChild ) ;\r
- }\r
- }\r
- break ;\r
- }\r
- },\r
-\r
- RTrimNode : function( node )\r
- {\r
- var eChildNode ;\r
-\r
- while ( (eChildNode = node.lastChild) )\r
- {\r
- if ( eChildNode.nodeType == 3 )\r
- {\r
- var sTrimmed = eChildNode.nodeValue.RTrim() ;\r
- var iOriginalLength = eChildNode.nodeValue.length ;\r
-\r
- if ( sTrimmed.length == 0 )\r
- {\r
- // If the trimmed text node is empty, just remove it.\r
-\r
- // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#81).\r
- eChildNode.parentNode.removeChild( eChildNode ) ;\r
- continue ;\r
- }\r
- else if ( sTrimmed.length < iOriginalLength )\r
- {\r
- // If the trimmed text length is less than the original\r
- // length, strip all spaces from the end by splitting\r
- // the text and removing the resulting useless node.\r
-\r
- eChildNode.splitText( sTrimmed.length ) ;\r
- // Use "node.lastChild.parentNode" instead of "node" to avoid IE bug (#81).\r
- node.lastChild.parentNode.removeChild( node.lastChild ) ;\r
- }\r
- }\r
- break ;\r
- }\r
-\r
- if ( !FCKBrowserInfo.IsIE && !FCKBrowserInfo.IsOpera )\r
- {\r
- eChildNode = node.lastChild ;\r
-\r
- if ( eChildNode && eChildNode.nodeType == 1 && eChildNode.nodeName.toLowerCase() == 'br' )\r
- {\r
- // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324).\r
- eChildNode.parentNode.removeChild( eChildNode ) ;\r
- }\r
- }\r
- },\r
-\r
- RemoveNode : function( node, excludeChildren )\r
- {\r
- if ( excludeChildren )\r
- {\r
- // Move all children before the node.\r
- var eChild ;\r
- while ( (eChild = node.firstChild) )\r
- node.parentNode.insertBefore( node.removeChild( eChild ), node ) ;\r
- }\r
-\r
- return node.parentNode.removeChild( node ) ;\r
- },\r
-\r
- GetFirstChild : function( node, childNames )\r
- {\r
- // If childNames is a string, transform it in a Array.\r
- if ( typeof ( childNames ) == 'string' )\r
- childNames = [ childNames ] ;\r
-\r
- var eChild = node.firstChild ;\r
- while( eChild )\r
- {\r
- if ( eChild.nodeType == 1 && eChild.tagName.Equals.apply( eChild.tagName, childNames ) )\r
- return eChild ;\r
-\r
- eChild = eChild.nextSibling ;\r
- }\r
-\r
- return null ;\r
- },\r
-\r
- GetLastChild : function( node, childNames )\r
- {\r
- // If childNames is a string, transform it in a Array.\r
- if ( typeof ( childNames ) == 'string' )\r
- childNames = [ childNames ] ;\r
-\r
- var eChild = node.lastChild ;\r
- while( eChild )\r
- {\r
- if ( eChild.nodeType == 1 && ( !childNames || eChild.tagName.Equals( childNames ) ) )\r
- return eChild ;\r
-\r
- eChild = eChild.previousSibling ;\r
- }\r
-\r
- return null ;\r
- },\r
-\r
- /*\r
- * Gets the previous element (nodeType=1) in the source order. Returns\r
- * "null" If no element is found.\r
- * @param {Object} currentNode The node to start searching from.\r
- * @param {Boolean} ignoreSpaceTextOnly Sets how text nodes will be\r
- * handled. If set to "true", only white spaces text nodes\r
- * will be ignored, while non white space text nodes will stop\r
- * the search, returning null. If "false" or omitted, all\r
- * text nodes are ignored.\r
- * @param {string[]} stopSearchElements An array of element names that\r
- * will cause the search to stop when found, returning null.\r
- * May be omitted (or null).\r
- * @param {string[]} ignoreElements An array of element names that\r
- * must be ignored during the search.\r
- */\r
- GetPreviousSourceElement : function( currentNode, ignoreSpaceTextOnly, stopSearchElements, ignoreElements )\r
- {\r
- if ( !currentNode )\r
- return null ;\r
-\r
- if ( stopSearchElements && currentNode.nodeType == 1 && currentNode.nodeName.IEquals( stopSearchElements ) )\r
- return null ;\r
-\r
- if ( currentNode.previousSibling )\r
- currentNode = currentNode.previousSibling ;\r
- else\r
- return this.GetPreviousSourceElement( currentNode.parentNode, ignoreSpaceTextOnly, stopSearchElements, ignoreElements ) ;\r
-\r
- while ( currentNode )\r
- {\r
- if ( currentNode.nodeType == 1 )\r
- {\r
- if ( stopSearchElements && currentNode.nodeName.IEquals( stopSearchElements ) )\r
- break ;\r
-\r
- if ( !ignoreElements || !currentNode.nodeName.IEquals( ignoreElements ) )\r
- return currentNode ;\r
- }\r
- else if ( ignoreSpaceTextOnly && currentNode.nodeType == 3 && currentNode.nodeValue.RTrim().length > 0 )\r
- break ;\r
-\r
- if ( currentNode.lastChild )\r
- currentNode = currentNode.lastChild ;\r
- else\r
- return this.GetPreviousSourceElement( currentNode, ignoreSpaceTextOnly, stopSearchElements, ignoreElements ) ;\r
- }\r
-\r
- return null ;\r
- },\r
-\r
- /*\r
- * Gets the next element (nodeType=1) in the source order. Returns\r
- * "null" If no element is found.\r
- * @param {Object} currentNode The node to start searching from.\r
- * @param {Boolean} ignoreSpaceTextOnly Sets how text nodes will be\r
- * handled. If set to "true", only white spaces text nodes\r
- * will be ignored, while non white space text nodes will stop\r
- * the search, returning null. If "false" or omitted, all\r
- * text nodes are ignored.\r
- * @param {string[]} stopSearchElements An array of element names that\r
- * will cause the search to stop when found, returning null.\r
- * May be omitted (or null).\r
- * @param {string[]} ignoreElements An array of element names that\r
- * must be ignored during the search.\r
- */\r
- GetNextSourceElement : function( currentNode, ignoreSpaceTextOnly, stopSearchElements, ignoreElements, startFromSibling )\r
- {\r
- while( ( currentNode = this.GetNextSourceNode( currentNode, startFromSibling ) ) ) // Only one "=".\r
- {\r
- if ( currentNode.nodeType == 1 )\r
- {\r
- if ( stopSearchElements && currentNode.nodeName.IEquals( stopSearchElements ) )\r
- break ;\r
-\r
- if ( ignoreElements && currentNode.nodeName.IEquals( ignoreElements ) )\r
- return this.GetNextSourceElement( currentNode, ignoreSpaceTextOnly, stopSearchElements, ignoreElements ) ;\r
-\r
- return currentNode ;\r
- }\r
- else if ( ignoreSpaceTextOnly && currentNode.nodeType == 3 && currentNode.nodeValue.RTrim().length > 0 )\r
- break ;\r
- }\r
-\r
- return null ;\r
- },\r
-\r
- /*\r
- * Get the next DOM node available in source order.\r
- */\r
- GetNextSourceNode : function( currentNode, startFromSibling, nodeType, stopSearchNode )\r
- {\r
- if ( !currentNode )\r
- return null ;\r
-\r
- var node ;\r
-\r
- if ( !startFromSibling && currentNode.firstChild )\r
- node = currentNode.firstChild ;\r
- else\r
- {\r
- if ( stopSearchNode && currentNode == stopSearchNode )\r
- return null ;\r
-\r
- node = currentNode.nextSibling ;\r
-\r
- if ( !node && ( !stopSearchNode || stopSearchNode != currentNode.parentNode ) )\r
- return this.GetNextSourceNode( currentNode.parentNode, true, nodeType, stopSearchNode ) ;\r
- }\r
-\r
- if ( nodeType && node && node.nodeType != nodeType )\r
- return this.GetNextSourceNode( node, false, nodeType, stopSearchNode ) ;\r
-\r
- return node ;\r
- },\r
-\r
- /*\r
- * Get the next DOM node available in source order.\r
- */\r
- GetPreviousSourceNode : function( currentNode, startFromSibling, nodeType, stopSearchNode )\r
- {\r
- if ( !currentNode )\r
- return null ;\r
-\r
- var node ;\r
-\r
- if ( !startFromSibling && currentNode.lastChild )\r
- node = currentNode.lastChild ;\r
- else\r
- {\r
- if ( stopSearchNode && currentNode == stopSearchNode )\r
- return null ;\r
-\r
- node = currentNode.previousSibling ;\r
-\r
- if ( !node && ( !stopSearchNode || stopSearchNode != currentNode.parentNode ) )\r
- return this.GetPreviousSourceNode( currentNode.parentNode, true, nodeType, stopSearchNode ) ;\r
- }\r
-\r
- if ( nodeType && node && node.nodeType != nodeType )\r
- return this.GetPreviousSourceNode( node, false, nodeType, stopSearchNode ) ;\r
-\r
- return node ;\r
- },\r
-\r
- // Inserts a element after a existing one.\r
- InsertAfterNode : function( existingNode, newNode )\r
- {\r
- return existingNode.parentNode.insertBefore( newNode, existingNode.nextSibling ) ;\r
- },\r
-\r
- GetParents : function( node )\r
- {\r
- var parents = new Array() ;\r
-\r
- while ( node )\r
- {\r
- parents.unshift( node ) ;\r
- node = node.parentNode ;\r
- }\r
-\r
- return parents ;\r
- },\r
-\r
- GetCommonParents : function( node1, node2 )\r
- {\r
- var p1 = this.GetParents( node1 ) ;\r
- var p2 = this.GetParents( node2 ) ;\r
- var retval = [] ;\r
- for ( var i = 0 ; i < p1.length ; i++ )\r
- {\r
- if ( p1[i] == p2[i] )\r
- retval.push( p1[i] ) ;\r
- }\r
- return retval ;\r
- },\r
-\r
- GetCommonParentNode : function( node1, node2, tagList )\r
- {\r
- var tagMap = {} ;\r
- if ( ! tagList.pop )\r
- tagList = [ tagList ] ;\r
- while ( tagList.length > 0 )\r
- tagMap[tagList.pop().toLowerCase()] = 1 ;\r
-\r
- var commonParents = this.GetCommonParents( node1, node2 ) ;\r
- var currentParent = null ;\r
- while ( ( currentParent = commonParents.pop() ) )\r
- {\r
- if ( tagMap[currentParent.nodeName.toLowerCase()] )\r
- return currentParent ;\r
- }\r
- return null ;\r
- },\r
-\r
- GetIndexOf : function( node )\r
- {\r
- var currentNode = node.parentNode ? node.parentNode.firstChild : null ;\r
- var currentIndex = -1 ;\r
-\r
- while ( currentNode )\r
- {\r
- currentIndex++ ;\r
-\r
- if ( currentNode == node )\r
- return currentIndex ;\r
-\r
- currentNode = currentNode.nextSibling ;\r
- }\r
-\r
- return -1 ;\r
- },\r
-\r
- PaddingNode : null,\r
-\r
- EnforcePaddingNode : function( doc, tagName )\r
- {\r
- // In IE it can happen when the page is reloaded that doc or doc.body is null, so exit here\r
- try\r
- {\r
- if ( !doc || !doc.body )\r
- return ;\r
- }\r
- catch (e)\r
- {\r
- return ;\r
- }\r
-\r
- this.CheckAndRemovePaddingNode( doc, tagName, true ) ;\r
- try\r
- {\r
- if ( doc.body.lastChild && ( doc.body.lastChild.nodeType != 1\r
- || doc.body.lastChild.tagName.toLowerCase() == tagName.toLowerCase() ) )\r
- return ;\r
- }\r
- catch (e)\r
- {\r
- return ;\r
- }\r
-\r
- var node = doc.createElement( tagName ) ;\r
- if ( FCKBrowserInfo.IsGecko && FCKListsLib.NonEmptyBlockElements[ tagName ] )\r
- FCKTools.AppendBogusBr( node ) ;\r
- this.PaddingNode = node ;\r
- if ( doc.body.childNodes.length == 1\r
- && doc.body.firstChild.nodeType == 1\r
- && doc.body.firstChild.tagName.toLowerCase() == 'br'\r
- && ( doc.body.firstChild.getAttribute( '_moz_dirty' ) != null\r
- || doc.body.firstChild.getAttribute( 'type' ) == '_moz' ) )\r
- doc.body.replaceChild( node, doc.body.firstChild ) ;\r
- else\r
- doc.body.appendChild( node ) ;\r
- },\r
-\r
- CheckAndRemovePaddingNode : function( doc, tagName, dontRemove )\r
- {\r
- var paddingNode = this.PaddingNode ;\r
- if ( ! paddingNode )\r
- return ;\r
-\r
- // If the padding node is changed, remove its status as a padding node.\r
- try\r
- {\r
- if ( paddingNode.parentNode != doc.body\r
- || paddingNode.tagName.toLowerCase() != tagName\r
- || ( paddingNode.childNodes.length > 1 )\r
- || ( paddingNode.firstChild && paddingNode.firstChild.nodeValue != '\xa0'\r
- && String(paddingNode.firstChild.tagName).toLowerCase() != 'br' ) )\r
- {\r
- this.PaddingNode = null ;\r
- return ;\r
- }\r
- }\r
- catch (e)\r
- {\r
- this.PaddingNode = null ;\r
- return ;\r
- }\r
-\r
- // Now we're sure the padding node exists, and it is unchanged, and it\r
- // isn't the only node in doc.body, remove it.\r
- if ( !dontRemove )\r
- {\r
- if ( paddingNode.parentNode.childNodes.length > 1 )\r
- paddingNode.parentNode.removeChild( paddingNode ) ;\r
- this.PaddingNode = null ;\r
- }\r
- },\r
-\r
- HasAttribute : function( element, attributeName )\r
- {\r
- if ( element.hasAttribute )\r
- return element.hasAttribute( attributeName ) ;\r
- else\r
- {\r
- var att = element.attributes[ attributeName ] ;\r
- return ( att != undefined && att.specified ) ;\r
- }\r
- },\r
-\r
- /**\r
- * Checks if an element has "specified" attributes.\r
- */\r
- HasAttributes : function( element )\r
- {\r
- var attributes = element.attributes ;\r
-\r
- for ( var i = 0 ; i < attributes.length ; i++ )\r
- {\r
- if ( FCKBrowserInfo.IsIE && attributes[i].nodeName == 'class' )\r
- {\r
- // IE has a strange bug. If calling removeAttribute('className'),\r
- // the attributes collection will still contain the "class"\r
- // attribute, which will be marked as "specified", even if the\r
- // outerHTML of the element is not displaying the class attribute.\r
- // Note : I was not able to reproduce it outside the editor,\r
- // but I've faced it while working on the TC of #1391.\r
- if ( element.className.length > 0 )\r
- return true ;\r
- }\r
- else if ( attributes[i].specified )\r
- return true ;\r
- }\r
-\r
- return false ;\r
- },\r
-\r
- /**\r
- * Remove an attribute from an element.\r
- */\r
- RemoveAttribute : function( element, attributeName )\r
- {\r
- if ( FCKBrowserInfo.IsIE && attributeName.toLowerCase() == 'class' )\r
- attributeName = 'className' ;\r
-\r
- return element.removeAttribute( attributeName, 0 ) ;\r
- },\r
-\r
- /**\r
- * Removes an array of attributes from an element\r
- */\r
- RemoveAttributes : function (element, aAttributes )\r
- {\r
- for ( var i = 0 ; i < aAttributes.length ; i++ )\r
- this.RemoveAttribute( element, aAttributes[i] );\r
- },\r
-\r
- GetAttributeValue : function( element, att )\r
- {\r
- var attName = att ;\r
-\r
- if ( typeof att == 'string' )\r
- att = element.attributes[ att ] ;\r
- else\r
- attName = att.nodeName ;\r
-\r
- if ( att && att.specified )\r
- {\r
- // IE returns "null" for the nodeValue of a "style" attribute.\r
- if ( attName == 'style' )\r
- return element.style.cssText ;\r
- // There are two cases when the nodeValue must be used:\r
- // - for the "class" attribute (all browsers).\r
- // - for events attributes (IE only).\r
- else if ( attName == 'class' || attName.indexOf('on') == 0 )\r
- return att.nodeValue ;\r
- else\r
- {\r
- // Use getAttribute to get its value exactly as it is\r
- // defined.\r
- return element.getAttribute( attName, 2 ) ;\r
- }\r
- }\r
- return null ;\r
- },\r
-\r
- /**\r
- * Checks whether one element contains the other.\r
- */\r
- Contains : function( mainElement, otherElement )\r
- {\r
- // IE supports contains, but only for element nodes.\r
- if ( mainElement.contains && otherElement.nodeType == 1 )\r
- return mainElement.contains( otherElement ) ;\r
-\r
- while ( ( otherElement = otherElement.parentNode ) ) // Only one "="\r
- {\r
- if ( otherElement == mainElement )\r
- return true ;\r
- }\r
- return false ;\r
- },\r
-\r
- /**\r
- * Breaks a parent element in the position of one of its contained elements.\r
- * For example, in the following case:\r
- * <b>This <i>is some<span /> sample</i> test text</b>\r
- * If element = <span />, we have these results:\r
- * <b>This <i>is some</i><span /><i> sample</i> test text</b> (If parent = <i>)\r
- * <b>This <i>is some</i></b><span /><b><i> sample</i> test text</b> (If parent = <b>)\r
- */\r
- BreakParent : function( element, parent, reusableRange )\r
- {\r
- var range = reusableRange || new FCKDomRange( FCKTools.GetElementWindow( element ) ) ;\r
-\r
- // We'll be extracting part of this element, so let's use our\r
- // range to get the correct piece.\r
- range.SetStart( element, 4 ) ;\r
- range.SetEnd( parent, 4 ) ;\r
-\r
- // Extract it.\r
- var docFrag = range.ExtractContents() ;\r
-\r
- // Move the element outside the broken element.\r
- range.InsertNode( element.parentNode.removeChild( element ) ) ;\r
-\r
- // Re-insert the extracted piece after the element.\r
- docFrag.InsertAfterNode( element ) ;\r
-\r
- range.Release( !!reusableRange ) ;\r
- },\r
-\r
- /**\r
- * Retrieves a uniquely identifiable tree address of a DOM tree node.\r
- * The tree address returns is an array of integers, with each integer\r
- * indicating a child index from a DOM tree node, starting from\r
- * document.documentElement.\r
- *\r
- * For example, assuming <body> is the second child from <html> (<head>\r
- * being the first), and we'd like to address the third child under the\r
- * fourth child of body, the tree address returned would be:\r
- * [1, 3, 2]\r
- *\r
- * The tree address cannot be used for finding back the DOM tree node once\r
- * the DOM tree structure has been modified.\r
- */\r
- GetNodeAddress : function( node, normalized )\r
- {\r
- var retval = [] ;\r
- while ( node && node != FCKTools.GetElementDocument( node ).documentElement )\r
- {\r
- var parentNode = node.parentNode ;\r
- var currentIndex = -1 ;\r
- for( var i = 0 ; i < parentNode.childNodes.length ; i++ )\r
- {\r
- var candidate = parentNode.childNodes[i] ;\r
- if ( normalized === true &&\r
- candidate.nodeType == 3 &&\r
- candidate.previousSibling &&\r
- candidate.previousSibling.nodeType == 3 )\r
- continue;\r
- currentIndex++ ;\r
- if ( parentNode.childNodes[i] == node )\r
- break ;\r
- }\r
- retval.unshift( currentIndex ) ;\r
- node = node.parentNode ;\r
- }\r
- return retval ;\r
- },\r
-\r
- /**\r
- * The reverse transformation of FCKDomTools.GetNodeAddress(). This\r
- * function returns the DOM node pointed to by its index address.\r
- */\r
- GetNodeFromAddress : function( doc, addr, normalized )\r
- {\r
- var cursor = doc.documentElement ;\r
- for ( var i = 0 ; i < addr.length ; i++ )\r
- {\r
- var target = addr[i] ;\r
- if ( ! normalized )\r
- {\r
- cursor = cursor.childNodes[target] ;\r
- continue ;\r
- }\r
-\r
- var currentIndex = -1 ;\r
- for (var j = 0 ; j < cursor.childNodes.length ; j++ )\r
- {\r
- var candidate = cursor.childNodes[j] ;\r
- if ( normalized === true &&\r
- candidate.nodeType == 3 &&\r
- candidate.previousSibling &&\r
- candidate.previousSibling.nodeType == 3 )\r
- continue ;\r
- currentIndex++ ;\r
- if ( currentIndex == target )\r
- {\r
- cursor = candidate ;\r
- break ;\r
- }\r
- }\r
- }\r
- return cursor ;\r
- },\r
-\r
- CloneElement : function( element )\r
- {\r
- element = element.cloneNode( false ) ;\r
-\r
- // The "id" attribute should never be cloned to avoid duplication.\r
- element.removeAttribute( 'id', false ) ;\r
-\r
- return element ;\r
- },\r
-\r
- ClearElementJSProperty : function( element, attrName )\r
- {\r
- if ( FCKBrowserInfo.IsIE )\r
- element.removeAttribute( attrName ) ;\r
- else\r
- delete element[attrName] ;\r
- },\r
-\r
- SetElementMarker : function ( markerObj, element, attrName, value)\r
- {\r
- var id = String( parseInt( Math.random() * 0xffffffff, 10 ) ) ;\r
- element._FCKMarkerId = id ;\r
- element[attrName] = value ;\r
- if ( ! markerObj[id] )\r
- markerObj[id] = { 'element' : element, 'markers' : {} } ;\r
- markerObj[id]['markers'][attrName] = value ;\r
- },\r
-\r
- ClearElementMarkers : function( markerObj, element, clearMarkerObj )\r
- {\r
- var id = element._FCKMarkerId ;\r
- if ( ! id )\r
- return ;\r
- this.ClearElementJSProperty( element, '_FCKMarkerId' ) ;\r
- for ( var j in markerObj[id]['markers'] )\r
- this.ClearElementJSProperty( element, j ) ;\r
- if ( clearMarkerObj )\r
- delete markerObj[id] ;\r
- },\r
-\r
- ClearAllMarkers : function( markerObj )\r
- {\r
- for ( var i in markerObj )\r
- this.ClearElementMarkers( markerObj, markerObj[i]['element'], true ) ;\r
- },\r
-\r
- /**\r
- * Convert a DOM list tree into a data structure that is easier to\r
- * manipulate. This operation should be non-intrusive in the sense that it\r
- * does not change the DOM tree, with the exception that it may add some\r
- * markers to the list item nodes when markerObj is specified.\r
- */\r
- ListToArray : function( listNode, markerObj, baseArray, baseIndentLevel, grandparentNode )\r
- {\r
- if ( ! listNode.nodeName.IEquals( ['ul', 'ol'] ) )\r
- return [] ;\r
-\r
- if ( ! baseIndentLevel )\r
- baseIndentLevel = 0 ;\r
- if ( ! baseArray )\r
- baseArray = [] ;\r
- // Iterate over all list items to get their contents and look for inner lists.\r
- for ( var i = 0 ; i < listNode.childNodes.length ; i++ )\r
- {\r
- var listItem = listNode.childNodes[i] ;\r
- if ( ! listItem.nodeName.IEquals( 'li' ) )\r
- continue ;\r
- var itemObj = { 'parent' : listNode, 'indent' : baseIndentLevel, 'contents' : [] } ;\r
- if ( ! grandparentNode )\r
- {\r
- itemObj.grandparent = listNode.parentNode ;\r
- if ( itemObj.grandparent && itemObj.grandparent.nodeName.IEquals( 'li' ) )\r
- itemObj.grandparent = itemObj.grandparent.parentNode ;\r
- }\r
- else\r
- itemObj.grandparent = grandparentNode ;\r
- if ( markerObj )\r
- this.SetElementMarker( markerObj, listItem, '_FCK_ListArray_Index', baseArray.length ) ;\r
- baseArray.push( itemObj ) ;\r
- for ( var j = 0 ; j < listItem.childNodes.length ; j++ )\r
- {\r
- var child = listItem.childNodes[j] ;\r
- if ( child.nodeName.IEquals( ['ul', 'ol'] ) )\r
- // Note the recursion here, it pushes inner list items with\r
- // +1 indentation in the correct order.\r
- this.ListToArray( child, markerObj, baseArray, baseIndentLevel + 1, itemObj.grandparent ) ;\r
- else\r
- itemObj.contents.push( child ) ;\r
- }\r
- }\r
- return baseArray ;\r
- },\r
-\r
- // Convert our internal representation of a list back to a DOM forest.\r
- ArrayToList : function( listArray, markerObj, baseIndex )\r
- {\r
- if ( baseIndex == undefined )\r
- baseIndex = 0 ;\r
- if ( ! listArray || listArray.length < baseIndex + 1 )\r
- return null ;\r
- var doc = FCKTools.GetElementDocument( listArray[baseIndex].parent ) ;\r
- var retval = doc.createDocumentFragment() ;\r
- var rootNode = null ;\r
- var currentIndex = baseIndex ;\r
- var indentLevel = Math.max( listArray[baseIndex].indent, 0 ) ;\r
- var currentListItem = null ;\r
- while ( true )\r
- {\r
- var item = listArray[currentIndex] ;\r
- if ( item.indent == indentLevel )\r
- {\r
- if ( ! rootNode || listArray[currentIndex].parent.nodeName != rootNode.nodeName )\r
- {\r
- rootNode = listArray[currentIndex].parent.cloneNode( false ) ;\r
- retval.appendChild( rootNode ) ;\r
- }\r
- currentListItem = doc.createElement( 'li' ) ;\r
- rootNode.appendChild( currentListItem ) ;\r
- for ( var i = 0 ; i < item.contents.length ; i++ )\r
- currentListItem.appendChild( item.contents[i].cloneNode( true ) ) ;\r
- currentIndex++ ;\r
- }\r
- else if ( item.indent == Math.max( indentLevel, 0 ) + 1 )\r
- {\r
- var listData = this.ArrayToList( listArray, null, currentIndex ) ;\r
- currentListItem.appendChild( listData.listNode ) ;\r
- currentIndex = listData.nextIndex ;\r
- }\r
- else if ( item.indent == -1 && baseIndex == 0 && item.grandparent )\r
- {\r
- var currentListItem ;\r
- if ( item.grandparent.nodeName.IEquals( ['ul', 'ol'] ) )\r
- currentListItem = doc.createElement( 'li' ) ;\r
- else\r
- {\r
- if ( FCKConfig.EnterMode.IEquals( ['div', 'p'] ) && ! item.grandparent.nodeName.IEquals( 'td' ) )\r
- currentListItem = doc.createElement( FCKConfig.EnterMode ) ;\r
- else\r
- currentListItem = doc.createDocumentFragment() ;\r
- }\r
- for ( var i = 0 ; i < item.contents.length ; i++ )\r
- currentListItem.appendChild( item.contents[i].cloneNode( true ) ) ;\r
- if ( currentListItem.nodeType == 11 )\r
- {\r
- if ( currentListItem.lastChild &&\r
- currentListItem.lastChild.getAttribute &&\r
- currentListItem.lastChild.getAttribute( 'type' ) == '_moz' )\r
- currentListItem.removeChild( currentListItem.lastChild );\r
- currentListItem.appendChild( doc.createElement( 'br' ) ) ;\r
- }\r
- if ( currentListItem.nodeName.IEquals( FCKConfig.EnterMode ) && currentListItem.firstChild )\r
- {\r
- this.TrimNode( currentListItem ) ;\r
- if ( FCKListsLib.BlockBoundaries[currentListItem.firstChild.nodeName.toLowerCase()] )\r
- {\r
- var tmp = doc.createDocumentFragment() ;\r
- while ( currentListItem.firstChild )\r
- tmp.appendChild( currentListItem.removeChild( currentListItem.firstChild ) ) ;\r
- currentListItem = tmp ;\r
- }\r
- }\r
- if ( FCKBrowserInfo.IsGeckoLike && currentListItem.nodeName.IEquals( ['div', 'p'] ) )\r
- FCKTools.AppendBogusBr( currentListItem ) ;\r
- retval.appendChild( currentListItem ) ;\r
- rootNode = null ;\r
- currentIndex++ ;\r
- }\r
- else\r
- return null ;\r
-\r
- if ( listArray.length <= currentIndex || Math.max( listArray[currentIndex].indent, 0 ) < indentLevel )\r
- {\r
- break ;\r
- }\r
- }\r
-\r
- // Clear marker attributes for the new list tree made of cloned nodes, if any.\r
- if ( markerObj )\r
- {\r
- var currentNode = retval.firstChild ;\r
- while ( currentNode )\r
- {\r
- if ( currentNode.nodeType == 1 )\r
- this.ClearElementMarkers( markerObj, currentNode ) ;\r
- currentNode = this.GetNextSourceNode( currentNode ) ;\r
- }\r
- }\r
-\r
- return { 'listNode' : retval, 'nextIndex' : currentIndex } ;\r
- },\r
-\r
- /**\r
- * Get the next sibling node for a node. If "includeEmpties" is false,\r
- * only element or non empty text nodes are returned.\r
- */\r
- GetNextSibling : function( node, includeEmpties )\r
- {\r
- node = node.nextSibling ;\r
-\r
- while ( node && !includeEmpties && node.nodeType != 1 && ( node.nodeType != 3 || node.nodeValue.length == 0 ) )\r
- node = node.nextSibling ;\r
-\r
- return node ;\r
- },\r
-\r
- /**\r
- * Get the previous sibling node for a node. If "includeEmpties" is false,\r
- * only element or non empty text nodes are returned.\r
- */\r
- GetPreviousSibling : function( node, includeEmpties )\r
- {\r
- node = node.previousSibling ;\r
-\r
- while ( node && !includeEmpties && node.nodeType != 1 && ( node.nodeType != 3 || node.nodeValue.length == 0 ) )\r
- node = node.previousSibling ;\r
-\r
- return node ;\r
- },\r
-\r
- /**\r
- * Checks if an element has no "useful" content inside of it\r
- * node tree. No "useful" content means empty text node or a signle empty\r
- * inline node.\r
- * elementCheckCallback may point to a function that returns a boolean\r
- * indicating that a child element must be considered in the element check.\r
- */\r
- CheckIsEmptyElement : function( element, elementCheckCallback )\r
- {\r
- var child = element.firstChild ;\r
- var elementChild ;\r
-\r
- while ( child )\r
- {\r
- if ( child.nodeType == 1 )\r
- {\r
- if ( elementChild || !FCKListsLib.InlineNonEmptyElements[ child.nodeName.toLowerCase() ] )\r
- return false ;\r
-\r
- if ( !elementCheckCallback || elementCheckCallback( child ) === true )\r
- elementChild = child ;\r
- }\r
- else if ( child.nodeType == 3 && child.nodeValue.length > 0 )\r
- return false ;\r
-\r
- child = child.nextSibling ;\r
- }\r
-\r
- return elementChild ? this.CheckIsEmptyElement( elementChild, elementCheckCallback ) : true ;\r
- },\r
-\r
- SetElementStyles : function( element, styleDict )\r
- {\r
- var style = element.style ;\r
- for ( var styleName in styleDict )\r
- style[ styleName ] = styleDict[ styleName ] ;\r
- },\r
-\r
- SetOpacity : function( element, opacity )\r
- {\r
- if ( FCKBrowserInfo.IsIE )\r
- {\r
- opacity = Math.round( opacity * 100 ) ;\r
- element.style.filter = ( opacity > 100 ? '' : 'progid:DXImageTransform.Microsoft.Alpha(opacity=' + opacity + ')' ) ;\r
- }\r
- else\r
- element.style.opacity = opacity ;\r
- },\r
-\r
- GetCurrentElementStyle : function( element, propertyName )\r
- {\r
- if ( FCKBrowserInfo.IsIE )\r
- return element.currentStyle[ propertyName ] ;\r
- else\r
- return element.ownerDocument.defaultView.getComputedStyle( element, '' ).getPropertyValue( propertyName ) ;\r
- },\r
-\r
- GetPositionedAncestor : function( element )\r
- {\r
- var currentElement = element ;\r
-\r
- while ( currentElement != FCKTools.GetElementDocument( currentElement ).documentElement )\r
- {\r
- if ( this.GetCurrentElementStyle( currentElement, 'position' ) != 'static' )\r
- return currentElement ;\r
-\r
- if ( currentElement == FCKTools.GetElementDocument( currentElement ).documentElement\r
- && currentWindow != w )\r
- currentElement = currentWindow.frameElement ;\r
- else\r
- currentElement = currentElement.parentNode ;\r
- }\r
-\r
- return null ;\r
- },\r
-\r
- /**\r
- * Current implementation for ScrollIntoView (due to #1462 and #2279). We\r
- * don't have a complete implementation here, just the things that fit our\r
- * needs.\r
- */\r
- ScrollIntoView : function( element, alignTop )\r
- {\r
- // Get the element window.\r
- var window = FCKTools.GetElementWindow( element ) ;\r
- var windowHeight = FCKTools.GetViewPaneSize( window ).Height ;\r
-\r
- // Starts the offset that will be scrolled with the negative value of\r
- // the visible window height.\r
- var offset = windowHeight * -1 ;\r
-\r
- // Appends the height it we are about to align the bottoms.\r
- if ( alignTop === false )\r
- {\r
- offset += element.offsetHeight || 0 ;\r
-\r
- // Consider the margin in the scroll, which is ok for our current\r
- // needs, but needs investigation if we will be using this function\r
- // in other places.\r
- offset += parseInt( this.GetCurrentElementStyle( element, 'marginBottom' ) || 0, 10 ) || 0 ;\r
- }\r
-\r
- // Appends the offsets for the entire element hierarchy.\r
- var elementPosition = FCKTools.GetDocumentPosition( window, element ) ;\r
- offset += elementPosition.y ;\r
-\r
- // Scroll the window to the desired position, if not already visible.\r
- var currentScroll = FCKTools.GetScrollPosition( window ).Y ;\r
- if ( offset > 0 && ( offset > currentScroll || offset < currentScroll - windowHeight ) )\r
- window.scrollTo( 0, offset ) ;\r
- },\r
-\r
- /**\r
- * Check if the element can be edited inside the browser.\r
- */\r
- CheckIsEditable : function( element )\r
- {\r
- // Get the element name.\r
- var nodeName = element.nodeName.toLowerCase() ;\r
-\r
- // Get the element DTD (defaults to span for unknown elements).\r
- var childDTD = FCK.DTD[ nodeName ] || FCK.DTD.span ;\r
-\r
- // In the DTD # == text node.\r
- return ( childDTD['#'] && !FCKListsLib.NonEditableElements[ nodeName ] ) ;\r
- },\r
-\r
- GetSelectedDivContainers : function()\r
- {\r
- var currentBlocks = [] ;\r
- var range = new FCKDomRange( FCK.EditorWindow ) ;\r
- range.MoveToSelection() ;\r
-\r
- var startNode = range.GetTouchedStartNode() ;\r
- var endNode = range.GetTouchedEndNode() ;\r
- var currentNode = startNode ;\r
-\r
- if ( startNode == endNode )\r
- {\r
- while ( endNode.nodeType == 1 && endNode.lastChild )\r
- endNode = endNode.lastChild ;\r
- endNode = FCKDomTools.GetNextSourceNode( endNode ) ;\r
- }\r
-\r
- while ( currentNode && currentNode != endNode )\r
- {\r
- if ( currentNode.nodeType != 3 || !/^[ \t\n]*$/.test( currentNode.nodeValue ) )\r
- {\r
- var path = new FCKElementPath( currentNode ) ;\r
- var blockLimit = path.BlockLimit ;\r
- if ( blockLimit && blockLimit.nodeName.IEquals( 'div' ) && currentBlocks.IndexOf( blockLimit ) == -1 )\r
- currentBlocks.push( blockLimit ) ;\r
- }\r
-\r
- currentNode = FCKDomTools.GetNextSourceNode( currentNode ) ;\r
- }\r
-\r
- return currentBlocks ;\r
- }\r
-} ;\r