import rt 3.8.7
[freeside.git] / rt / share / html / NoAuth / RichText / FCKeditor / editor / _source / classes / fckdomrange_ie.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  * Class for working with a selection range, much like the W3C DOM Range, but\r
22  * it is not intended to be an implementation of the W3C interface.\r
23  * (IE Implementation)\r
24  */\r
25 \r
26 FCKDomRange.prototype.MoveToSelection = function()\r
27 {\r
28         this.Release( true ) ;\r
29 \r
30         this._Range = new FCKW3CRange( this.Window.document ) ;\r
31 \r
32         var oSel = this.Window.document.selection ;\r
33 \r
34         if ( oSel.type != 'Control' )\r
35         {\r
36                 var eMarkerStart        = this._GetSelectionMarkerTag( true ) ;\r
37                 var eMarkerEnd          = this._GetSelectionMarkerTag( false ) ;\r
38 \r
39                 if ( !eMarkerStart && !eMarkerEnd )\r
40                 {\r
41                         this._Range.setStart( this.Window.document.body, 0 ) ;\r
42                         this._UpdateElementInfo() ;\r
43                         return ;\r
44                 }\r
45 \r
46                 // Set the start boundary.\r
47                 this._Range.setStart( eMarkerStart.parentNode, FCKDomTools.GetIndexOf( eMarkerStart ) ) ;\r
48                 eMarkerStart.parentNode.removeChild( eMarkerStart ) ;\r
49 \r
50                 // Set the end boundary.\r
51                 this._Range.setEnd( eMarkerEnd.parentNode, FCKDomTools.GetIndexOf( eMarkerEnd ) ) ;\r
52                 eMarkerEnd.parentNode.removeChild( eMarkerEnd ) ;\r
53 \r
54                 this._UpdateElementInfo() ;\r
55         }\r
56         else\r
57         {\r
58                 var oControl = oSel.createRange().item(0) ;\r
59 \r
60                 if ( oControl )\r
61                 {\r
62                         this._Range.setStartBefore( oControl ) ;\r
63                         this._Range.setEndAfter( oControl ) ;\r
64                         this._UpdateElementInfo() ;\r
65                 }\r
66         }\r
67 }\r
68 \r
69 FCKDomRange.prototype.Select = function( forceExpand )\r
70 {\r
71         if ( this._Range )\r
72                 this.SelectBookmark( this.CreateBookmark( true ), forceExpand ) ;\r
73 }\r
74 \r
75 // Not compatible with bookmark created with CreateBookmark2.\r
76 // The bookmark nodes will be deleted from the document.\r
77 FCKDomRange.prototype.SelectBookmark = function( bookmark, forceExpand )\r
78 {\r
79         var bIsCollapsed = this.CheckIsCollapsed() ;\r
80         var bIsStartMakerAlone ;\r
81         var dummySpan ;\r
82 \r
83         // Create marker tags for the start and end boundaries.\r
84         var eStartMarker = this.GetBookmarkNode( bookmark, true ) ;\r
85 \r
86         if ( !eStartMarker )\r
87                 return ;\r
88 \r
89         var eEndMarker ;\r
90         if ( !bIsCollapsed )\r
91                 eEndMarker = this.GetBookmarkNode( bookmark, false ) ;\r
92 \r
93         // Create the main range which will be used for the selection.\r
94         var oIERange = this.Window.document.body.createTextRange() ;\r
95 \r
96         // Position the range at the start boundary.\r
97         oIERange.moveToElementText( eStartMarker ) ;\r
98         oIERange.moveStart( 'character', 1 ) ;\r
99 \r
100         if ( eEndMarker )\r
101         {\r
102                 // Create a tool range for the end.\r
103                 var oIERangeEnd = this.Window.document.body.createTextRange() ;\r
104 \r
105                 // Position the tool range at the end.\r
106                 oIERangeEnd.moveToElementText( eEndMarker ) ;\r
107 \r
108                 // Move the end boundary of the main range to match the tool range.\r
109                 oIERange.setEndPoint( 'EndToEnd', oIERangeEnd ) ;\r
110                 oIERange.moveEnd( 'character', -1 ) ;\r
111         }\r
112         else\r
113         {\r
114                 bIsStartMakerAlone = ( forceExpand || !eStartMarker.previousSibling || eStartMarker.previousSibling.nodeName.toLowerCase() == 'br' ) && !eStartMarker.nextSibing ;\r
115 \r
116                 // Append a temporary <span>&#65279;</span> before the selection.\r
117                 // This is needed to avoid IE destroying selections inside empty\r
118                 // inline elements, like <b></b> (#253).\r
119                 // It is also needed when placing the selection right after an inline\r
120                 // element to avoid the selection moving inside of it.\r
121                 dummySpan = this.Window.document.createElement( 'span' ) ;\r
122                 dummySpan.innerHTML = '&#65279;' ;      // Zero Width No-Break Space (U+FEFF). See #1359.\r
123                 eStartMarker.parentNode.insertBefore( dummySpan, eStartMarker ) ;\r
124 \r
125                 if ( bIsStartMakerAlone )\r
126                 {\r
127                         // To expand empty blocks or line spaces after <br>, we need\r
128                         // instead to have any char, which will be later deleted using the\r
129                         // selection.\r
130                         // \ufeff = Zero Width No-Break Space (U+FEFF). See #1359.\r
131                         eStartMarker.parentNode.insertBefore( this.Window.document.createTextNode( '\ufeff' ), eStartMarker ) ;\r
132                 }\r
133         }\r
134 \r
135         if ( !this._Range )\r
136                 this._Range = this.CreateRange() ;\r
137 \r
138         // Remove the markers (reset the position, because of the changes in the DOM tree).\r
139         this._Range.setStartBefore( eStartMarker ) ;\r
140         eStartMarker.parentNode.removeChild( eStartMarker ) ;\r
141 \r
142         if ( bIsCollapsed )\r
143         {\r
144                 if ( bIsStartMakerAlone )\r
145                 {\r
146                         // Move the selection start to include the temporary &#65279;.\r
147                         oIERange.moveStart( 'character', -1 ) ;\r
148 \r
149                         oIERange.select() ;\r
150 \r
151                         // Remove our temporary stuff.\r
152                         this.Window.document.selection.clear() ;\r
153                 }\r
154                 else\r
155                         oIERange.select() ;\r
156 \r
157                 FCKDomTools.RemoveNode( dummySpan ) ;\r
158         }\r
159         else\r
160         {\r
161                 this._Range.setEndBefore( eEndMarker ) ;\r
162                 eEndMarker.parentNode.removeChild( eEndMarker ) ;\r
163                 oIERange.select() ;\r
164         }\r
165 }\r
166 \r
167 FCKDomRange.prototype._GetSelectionMarkerTag = function( toStart )\r
168 {\r
169         var doc = this.Window.document ;\r
170         var selection = doc.selection ;\r
171 \r
172         // Get a range for the start boundary.\r
173         var oRange ;\r
174 \r
175         // IE may throw an "unspecified error" on some cases (it happened when\r
176         // loading _samples/default.html), so try/catch.\r
177         try\r
178         {\r
179                 oRange = selection.createRange() ;\r
180         }\r
181         catch (e)\r
182         {\r
183                 return null ;\r
184         }\r
185 \r
186         // IE might take the range object to the main window instead of inside the editor iframe window.\r
187         // This is known to happen when the editor window has not been selected before (See #933).\r
188         // We need to avoid that.\r
189         if ( oRange.parentElement().document != doc )\r
190                 return null ;\r
191 \r
192         oRange.collapse( toStart === true ) ;\r
193 \r
194         // Paste a marker element at the collapsed range and get it from the DOM.\r
195         var sMarkerId = 'fck_dom_range_temp_' + (new Date()).valueOf() + '_' + Math.floor(Math.random()*1000) ;\r
196         oRange.pasteHTML( '<span id="' + sMarkerId + '"></span>' ) ;\r
197 \r
198         return doc.getElementById( sMarkerId ) ;\r
199 }\r