import torrus 1.0.9
[freeside.git] / rt / share / html / NoAuth / RichText / FCKeditor / editor / _source / commandclasses / fckindentcommands.js
1 /*\r
2  * FCKeditor - The text editor for Internet - http://www.fckeditor.net\r
3  * Copyright (C) 2003-2009 Frederico Caldeira Knabben\r
4  *\r
5  * == BEGIN LICENSE ==\r
6  *\r
7  * Licensed under the terms of any of the following licenses at your\r
8  * choice:\r
9  *\r
10  *  - GNU General Public License Version 2 or later (the "GPL")\r
11  *    http://www.gnu.org/licenses/gpl.html\r
12  *\r
13  *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")\r
14  *    http://www.gnu.org/licenses/lgpl.html\r
15  *\r
16  *  - Mozilla Public License Version 1.1 or later (the "MPL")\r
17  *    http://www.mozilla.org/MPL/MPL-1.1.html\r
18  *\r
19  * == END LICENSE ==\r
20  *\r
21  * FCKIndentCommand Class: controls block indentation.\r
22  */\r
23 \r
24 var FCKIndentCommand = function( name, offset )\r
25 {\r
26         this.Name = name ;\r
27         this.Offset = offset ;\r
28         this.IndentCSSProperty = FCKConfig.ContentLangDirection.IEquals( 'ltr' ) ? 'marginLeft' : 'marginRight' ;\r
29 }\r
30 \r
31 FCKIndentCommand._InitIndentModeParameters = function()\r
32 {\r
33         if ( FCKConfig.IndentClasses && FCKConfig.IndentClasses.length > 0 )\r
34         {\r
35                 this._UseIndentClasses = true ;\r
36                 this._IndentClassMap = {} ;\r
37                 for ( var i = 0 ; i < FCKConfig.IndentClasses.length ;i++ )\r
38                         this._IndentClassMap[FCKConfig.IndentClasses[i]] = i + 1 ;\r
39                 this._ClassNameRegex = new RegExp( '(?:^|\\s+)(' + FCKConfig.IndentClasses.join( '|' ) + ')(?=$|\\s)' ) ;\r
40         }\r
41         else\r
42                 this._UseIndentClasses = false ;\r
43 }\r
44 \r
45 \r
46 FCKIndentCommand.prototype =\r
47 {\r
48         Execute : function()\r
49         {\r
50                 // Save an undo snapshot before doing anything.\r
51                 FCKUndo.SaveUndoStep() ;\r
52 \r
53                 var range = new FCKDomRange( FCK.EditorWindow ) ;\r
54                 range.MoveToSelection() ;\r
55                 var bookmark = range.CreateBookmark() ;\r
56 \r
57                 // Two cases to handle here: either we're in a list, or not.\r
58                 // If we're in a list, then the indent/outdent operations would be done on the list nodes.\r
59                 // Otherwise, apply the operation on the nearest block nodes.\r
60                 var nearestListBlock = FCKDomTools.GetCommonParentNode( range.StartNode || range.StartContainer ,\r
61                                 range.EndNode || range.EndContainer,\r
62                                 ['ul', 'ol'] ) ;\r
63                 if ( nearestListBlock )\r
64                         this._IndentList( range, nearestListBlock ) ;\r
65                 else\r
66                         this._IndentBlock( range ) ;\r
67 \r
68                 range.MoveToBookmark( bookmark ) ;\r
69                 range.Select() ;\r
70 \r
71                 FCK.Focus() ;\r
72                 FCK.Events.FireEvent( 'OnSelectionChange' ) ;\r
73         },\r
74 \r
75         GetState : function()\r
76         {\r
77                 // Disabled if not WYSIWYG.\r
78                 if ( FCK.EditMode != FCK_EDITMODE_WYSIWYG || ! FCK.EditorWindow )\r
79                         return FCK_TRISTATE_DISABLED ;\r
80 \r
81                 // Initialize parameters if not already initialzed.\r
82                 if ( FCKIndentCommand._UseIndentClasses == undefined )\r
83                         FCKIndentCommand._InitIndentModeParameters() ;\r
84 \r
85                 // If we're not in a list, and the starting block's indentation is zero, and the current\r
86                 // command is the outdent command, then we should return FCK_TRISTATE_DISABLED.\r
87                 var startContainer = FCKSelection.GetBoundaryParentElement( true ) ;\r
88                 var endContainer = FCKSelection.GetBoundaryParentElement( false ) ;\r
89                 var listNode = FCKDomTools.GetCommonParentNode( startContainer, endContainer, ['ul','ol'] ) ;\r
90 \r
91                 if ( listNode )\r
92                 {\r
93                         if ( this.Name.IEquals( 'outdent' ) )\r
94                                 return FCK_TRISTATE_OFF ;\r
95                         var firstItem = FCKTools.GetElementAscensor( startContainer, 'li' ) ;\r
96                         if ( !firstItem || !firstItem.previousSibling )\r
97                                 return FCK_TRISTATE_DISABLED ;\r
98                         return FCK_TRISTATE_OFF ;\r
99                 }\r
100                 if ( ! FCKIndentCommand._UseIndentClasses && this.Name.IEquals( 'indent' ) )\r
101                         return FCK_TRISTATE_OFF;\r
102 \r
103                 var path = new FCKElementPath( startContainer ) ;\r
104                 var firstBlock = path.Block || path.BlockLimit ;\r
105                 if ( !firstBlock )\r
106                         return FCK_TRISTATE_DISABLED ;\r
107 \r
108                 if ( FCKIndentCommand._UseIndentClasses )\r
109                 {\r
110                         var indentClass = firstBlock.className.match( FCKIndentCommand._ClassNameRegex ) ;\r
111                         var indentStep = 0 ;\r
112                         if ( indentClass != null )\r
113                         {\r
114                                 indentClass = indentClass[1] ;\r
115                                 indentStep = FCKIndentCommand._IndentClassMap[indentClass] ;\r
116                         }\r
117                         if ( ( this.Name == 'outdent' && indentStep == 0 ) ||\r
118                                         ( this.Name == 'indent' && indentStep == FCKConfig.IndentClasses.length ) )\r
119                                 return FCK_TRISTATE_DISABLED ;\r
120                         return FCK_TRISTATE_OFF ;\r
121                 }\r
122                 else\r
123                 {\r
124                         var indent = parseInt( firstBlock.style[this.IndentCSSProperty], 10 ) ;\r
125                         if ( isNaN( indent ) )\r
126                                 indent = 0 ;\r
127                         if ( indent <= 0 )\r
128                                 return FCK_TRISTATE_DISABLED ;\r
129                         return FCK_TRISTATE_OFF ;\r
130                 }\r
131         },\r
132 \r
133         _IndentBlock : function( range )\r
134         {\r
135                 var iterator = new FCKDomRangeIterator( range ) ;\r
136                 iterator.EnforceRealBlocks = true ;\r
137 \r
138                 range.Expand( 'block_contents' ) ;\r
139                 var commonParents = FCKDomTools.GetCommonParents( range.StartContainer, range.EndContainer ) ;\r
140                 var nearestParent = commonParents[commonParents.length - 1] ;\r
141                 var block ;\r
142 \r
143                 while ( ( block = iterator.GetNextParagraph() ) )\r
144                 {\r
145                         // We don't want to indent subtrees recursively, so only perform the indent operation\r
146                         // if the block itself is the nearestParent, or the block's parent is the nearestParent.\r
147                         if ( ! ( block == nearestParent || block.parentNode == nearestParent ) )\r
148                                 continue ;\r
149 \r
150                         if ( FCKIndentCommand._UseIndentClasses )\r
151                         {\r
152                                 // Transform current class name to indent step index.\r
153                                 var indentClass = block.className.match( FCKIndentCommand._ClassNameRegex ) ;\r
154                                 var indentStep = 0 ;\r
155                                 if ( indentClass != null )\r
156                                 {\r
157                                         indentClass = indentClass[1] ;\r
158                                         indentStep = FCKIndentCommand._IndentClassMap[indentClass] ;\r
159                                 }\r
160 \r
161                                 // Operate on indent step index, transform indent step index back to class name.\r
162                                 if ( this.Name.IEquals( 'outdent' ) )\r
163                                         indentStep-- ;\r
164                                 else if ( this.Name.IEquals( 'indent' ) )\r
165                                         indentStep++ ;\r
166                                 indentStep = Math.min( indentStep, FCKConfig.IndentClasses.length ) ;\r
167                                 indentStep = Math.max( indentStep, 0 ) ;\r
168                                 var className = block.className.replace( FCKIndentCommand._ClassNameRegex, '' ) ;\r
169                                 if ( indentStep < 1 )\r
170                                         block.className = className ;\r
171                                 else\r
172                                         block.className = ( className.length > 0 ? className + ' ' : '' ) +\r
173                                                 FCKConfig.IndentClasses[indentStep - 1] ;\r
174                         }\r
175                         else\r
176                         {\r
177                                 // Offset distance is assumed to be in pixels for now.\r
178                                 var currentOffset = parseInt( block.style[this.IndentCSSProperty], 10 ) ;\r
179                                 if ( isNaN( currentOffset ) )\r
180                                         currentOffset = 0 ;\r
181                                 currentOffset += this.Offset ;\r
182                                 currentOffset = Math.max( currentOffset, 0 ) ;\r
183                                 currentOffset = Math.ceil( currentOffset / this.Offset ) * this.Offset ;\r
184                                 block.style[this.IndentCSSProperty] = currentOffset ? currentOffset + FCKConfig.IndentUnit : '' ;\r
185                                 if ( block.getAttribute( 'style' ) == '' )\r
186                                         block.removeAttribute( 'style' ) ;\r
187                         }\r
188                 }\r
189         },\r
190 \r
191         _IndentList : function( range, listNode )\r
192         {\r
193                 // Our starting and ending points of the range might be inside some blocks under a list item...\r
194                 // So before playing with the iterator, we need to expand the block to include the list items.\r
195                 var startContainer = range.StartContainer ;\r
196                 var endContainer = range.EndContainer ;\r
197                 while ( startContainer && startContainer.parentNode != listNode )\r
198                         startContainer = startContainer.parentNode ;\r
199                 while ( endContainer && endContainer.parentNode != listNode )\r
200                         endContainer = endContainer.parentNode ;\r
201 \r
202                 if ( ! startContainer || ! endContainer )\r
203                         return ;\r
204 \r
205                 // Now we can iterate over the individual items on the same tree depth.\r
206                 var block = startContainer ;\r
207                 var itemsToMove = [] ;\r
208                 var stopFlag = false ;\r
209                 while ( stopFlag == false )\r
210                 {\r
211                         if ( block == endContainer )\r
212                                 stopFlag = true ;\r
213                         itemsToMove.push( block ) ;\r
214                         block = block.nextSibling ;\r
215                 }\r
216                 if ( itemsToMove.length < 1 )\r
217                         return ;\r
218 \r
219                 // Do indent or outdent operations on the array model of the list, not the list's DOM tree itself.\r
220                 // The array model demands that it knows as much as possible about the surrounding lists, we need\r
221                 // to feed it the further ancestor node that is still a list.\r
222                 var listParents = FCKDomTools.GetParents( listNode ) ;\r
223                 for ( var i = 0 ; i < listParents.length ; i++ )\r
224                 {\r
225                         if ( listParents[i].nodeName.IEquals( ['ul', 'ol'] ) )\r
226                         {\r
227                                 listNode = listParents[i] ;\r
228                                 break ;\r
229                         }\r
230                 }\r
231                 var indentOffset = this.Name.IEquals( 'indent' ) ? 1 : -1 ;\r
232                 var startItem = itemsToMove[0] ;\r
233                 var lastItem = itemsToMove[ itemsToMove.length - 1 ] ;\r
234                 var markerObj = {} ;\r
235 \r
236                 // Convert the list DOM tree into a one dimensional array.\r
237                 var listArray = FCKDomTools.ListToArray( listNode, markerObj ) ;\r
238 \r
239                 // Apply indenting or outdenting on the array.\r
240                 var baseIndent = listArray[lastItem._FCK_ListArray_Index].indent ;\r
241                 for ( var i = startItem._FCK_ListArray_Index ; i <= lastItem._FCK_ListArray_Index ; i++ )\r
242                         listArray[i].indent += indentOffset ;\r
243                 for ( var i = lastItem._FCK_ListArray_Index + 1 ; i < listArray.length && listArray[i].indent > baseIndent ; i++ )\r
244                         listArray[i].indent += indentOffset ;\r
245 \r
246                 /* For debug use only\r
247                 var PrintArray = function( listArray, doc )\r
248                 {\r
249                         var s = [] ;\r
250                         for ( var i = 0 ; i < listArray.length ; i++ )\r
251                         {\r
252                                 for ( var j in listArray[i] )\r
253                                 {\r
254                                         if ( j != 'contents' )\r
255                                                 s.push( j + ":" + listArray[i][j] + "; " ) ;\r
256                                         else\r
257                                         {\r
258                                                 var docFrag = doc.createDocumentFragment() ;\r
259                                                 var tmpNode = doc.createElement( 'span' ) ;\r
260                                                 for ( var k = 0 ; k < listArray[i][j].length ; k++ )\r
261                                                         docFrag.appendChild( listArray[i][j][k].cloneNode( true ) ) ;\r
262                                                 tmpNode.appendChild( docFrag ) ;\r
263                                                 s.push( j + ":" + tmpNode.innerHTML + "; ") ;\r
264                                         }\r
265                                 }\r
266                                 s.push( '\n' ) ;\r
267                         }\r
268                         alert( s.join('') ) ;\r
269                 }\r
270                 PrintArray( listArray, FCK.EditorDocument ) ;\r
271                 */\r
272 \r
273                 // Convert the array back to a DOM forest (yes we might have a few subtrees now).\r
274                 // And replace the old list with the new forest.\r
275                 var newList = FCKDomTools.ArrayToList( listArray ) ;\r
276                 if ( newList )\r
277                         listNode.parentNode.replaceChild( newList.listNode, listNode ) ;\r
278 \r
279                 // Clean up the markers.\r
280                 FCKDomTools.ClearAllMarkers( markerObj ) ;\r
281         }\r
282 } ;\r