1/*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2003-2007 Frederico Caldeira Knabben
4 *
5 * == BEGIN LICENSE ==
6 *
7 * Licensed under the terms of any of the following licenses at your
8 * choice:
9 *
10 *  - GNU General Public License Version 2 or later (the "GPL")
11 *    http://www.gnu.org/licenses/gpl.html
12 *
13 *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
14 *    http://www.gnu.org/licenses/lgpl.html
15 *
16 *  - Mozilla Public License Version 1.1 or later (the "MPL")
17 *    http://www.mozilla.org/MPL/MPL-1.1.html
18 *
19 * == END LICENSE ==
20 *
21 * Active selection functions. (Gecko specific implementation)
22 */
23
24// Get the selection type (like document.select.type in IE).
25FCKSelection.GetType = function()
26{
27	// By default set the type to "Text".
28	var type = 'Text' ;
29
30	// Check if the actual selection is a Control (IMG, TABLE, HR, etc...).
31
32	var sel ;
33	try { sel = FCK.EditorWindow.getSelection() ; } catch (e) {}
34
35	if ( sel && sel.rangeCount == 1 )
36	{
37		var range = sel.getRangeAt(0) ;
38		if ( range.startContainer == range.endContainer
39			&& ( range.endOffset - range.startOffset ) == 1
40			&& range.startContainer.nodeType == 1
41			&& FCKListsLib.StyleObjectElements[ range.startContainer.childNodes[ range.startOffset ].nodeName.toLowerCase() ] )
42		{
43			type = 'Control' ;
44		}
45	}
46
47	return type ;
48}
49
50// Retrieves the selected element (if any), just in the case that a single
51// element (object like and image or a table) is selected.
52FCKSelection.GetSelectedElement = function()
53{
54	var selectedElement = null ;
55
56	var selection = !!FCK.EditorWindow && FCK.EditorWindow.getSelection() ;
57
58	if ( selection && selection.anchorNode && selection.anchorNode.nodeType == 1 )
59	{
60		// This one is good for all browsers, expect Safari Mac.
61		selectedElement = selection.anchorNode.childNodes[ selection.anchorOffset ] ;
62
63		// For Safari (Mac only), the anchor node for a control selection is
64		// the control itself, which seams logic. FF and Opera use the parent
65		// as the anchor node, pointing to the control with the offset.
66		// As FF created the selection "standard", Safari would do better by
67		// following their steps.
68		if ( !selectedElement )
69			selectedElement = selection.anchorNode ;
70		else if ( selectedElement.nodeType != 1 )
71			return null ;
72	}
73
74	return selectedElement ;
75}
76
77FCKSelection.GetParentElement = function()
78{
79	if ( this.GetType() == 'Control' )
80		return FCKSelection.GetSelectedElement().parentNode ;
81	else
82	{
83		var oSel = FCK.EditorWindow.getSelection() ;
84		if ( oSel )
85		{
86			// make the common case fast - for collapsed/nearly collapsed selections just return anchor.parent.
87			if ( oSel.anchorNode && oSel.anchorNode == oSel.focusNode )
88				return oSel.anchorNode.parentNode ;
89
90			// looks like we're having a large selection here. To make the behavior same as IE's TextRange.parentElement(),
91			// we need to find the nearest ancestor node which encapsulates both the beginning and the end of the selection.
92			// TODO: A simpler logic can be found.
93			var anchorPath = new FCKElementPath( oSel.anchorNode ) ;
94			var focusPath = new FCKElementPath( oSel.focusNode ) ;
95			var deepPath = null ;
96			var shallowPath = null ;
97			if ( anchorPath.Elements.length > focusPath.Elements.length )
98			{
99				deepPath = anchorPath.Elements ;
100				shallowPath = focusPath.Elements ;
101			}
102			else
103			{
104				deepPath = focusPath.Elements ;
105				shallowPath = anchorPath.Elements ;
106			}
107
108			var deepPathBase = deepPath.length - shallowPath.length ;
109			for( var i = 0 ; i < shallowPath.length ; i++)
110			{
111				if ( deepPath[deepPathBase + i] == shallowPath[i])
112					return shallowPath[i];
113			}
114			return null ;
115		}
116	}
117	return null ;
118}
119
120FCKSelection.GetBoundaryParentElement = function( startBoundary )
121{
122	if ( ! FCK.EditorWindow )
123		return null ;
124	if ( this.GetType() == 'Control' )
125		return FCKSelection.GetSelectedElement().parentNode ;
126	else
127	{
128		var oSel = FCK.EditorWindow.getSelection() ;
129		if ( oSel && oSel.rangeCount > 0 )
130		{
131			var range = oSel.getRangeAt( startBoundary ? 0 : ( oSel.rangeCount - 1 ) ) ;
132
133			var element = startBoundary ? range.startContainer : range.endContainer ;
134
135			return ( element.nodeType == 1 ? element : element.parentNode ) ;
136		}
137	}
138	return null ;
139}
140
141FCKSelection.SelectNode = function( element )
142{
143	var oRange = FCK.EditorDocument.createRange() ;
144	oRange.selectNode( element ) ;
145
146	var oSel = FCK.EditorWindow.getSelection() ;
147	oSel.removeAllRanges() ;
148	oSel.addRange( oRange ) ;
149}
150
151FCKSelection.Collapse = function( toStart )
152{
153	var oSel = FCK.EditorWindow.getSelection() ;
154
155	if ( toStart == null || toStart === true )
156		oSel.collapseToStart() ;
157	else
158		oSel.collapseToEnd() ;
159}
160
161// The "nodeTagName" parameter must be Upper Case.
162FCKSelection.HasAncestorNode = function( nodeTagName )
163{
164	var oContainer = this.GetSelectedElement() ;
165	if ( ! oContainer && FCK.EditorWindow )
166	{
167		try		{ oContainer = FCK.EditorWindow.getSelection().getRangeAt(0).startContainer ; }
168		catch(e){}
169	}
170
171	while ( oContainer )
172	{
173		if ( oContainer.nodeType == 1 && oContainer.tagName == nodeTagName ) return true ;
174		oContainer = oContainer.parentNode ;
175	}
176
177	return false ;
178}
179
180// The "nodeTagName" parameter must be Upper Case.
181FCKSelection.MoveToAncestorNode = function( nodeTagName )
182{
183	var oNode ;
184
185	var oContainer = this.GetSelectedElement() ;
186	if ( ! oContainer )
187		oContainer = FCK.EditorWindow.getSelection().getRangeAt(0).startContainer ;
188
189	while ( oContainer )
190	{
191		if ( oContainer.nodeName == nodeTagName )
192			return oContainer ;
193
194		oContainer = oContainer.parentNode ;
195	}
196	return null ;
197}
198
199FCKSelection.Delete = function()
200{
201	// Gets the actual selection.
202	var oSel = FCK.EditorWindow.getSelection() ;
203
204	// Deletes the actual selection contents.
205	for ( var i = 0 ; i < oSel.rangeCount ; i++ )
206	{
207		oSel.getRangeAt(i).deleteContents() ;
208	}
209
210	return oSel ;
211}
212