source: extensions/FCKEditor/editor/_source/internals/fckstyles.js @ 3295

Last change on this file since 3295 was 3295, checked in by patdenice, 15 years ago

New extension added:
FCK Editor (2.0.a)

File size: 10.2 KB
Line 
1/*
2 * FCKeditor - The text editor for Internet - http://www.fckeditor.net
3 * Copyright (C) 2003-2009 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 * Handles styles in a give document.
22 */
23
24var FCKStyles = FCK.Styles =
25{
26        _Callbacks : {},
27        _ObjectStyles : {},
28
29        ApplyStyle : function( style )
30        {
31                if ( typeof style == 'string' )
32                        style = this.GetStyles()[ style ] ;
33
34                if ( style )
35                {
36                        if ( style.GetType() == FCK_STYLE_OBJECT )
37                                style.ApplyToObject( FCKSelection.GetSelectedElement() ) ;
38                        else
39                                style.ApplyToSelection( FCK.EditorWindow ) ;
40
41                        FCK.Events.FireEvent( 'OnSelectionChange' ) ;
42                }
43        },
44
45        RemoveStyle : function( style )
46        {
47                if ( typeof style == 'string' )
48                        style = this.GetStyles()[ style ] ;
49
50                if ( style )
51                {
52                        style.RemoveFromSelection( FCK.EditorWindow ) ;
53                        FCK.Events.FireEvent( 'OnSelectionChange' ) ;
54                }
55        },
56
57        /**
58         * Defines a callback function to be called when the current state of a
59         * specific style changes.
60         */
61        AttachStyleStateChange : function( styleName, callback, callbackOwner )
62        {
63                var callbacks = this._Callbacks[ styleName ] ;
64
65                if ( !callbacks )
66                        callbacks = this._Callbacks[ styleName ] = [] ;
67
68                callbacks.push( [ callback, callbackOwner ] ) ;
69        },
70
71        CheckSelectionChanges : function()
72        {
73                var startElement = FCKSelection.GetBoundaryParentElement( true ) ;
74
75                if ( !startElement )
76                        return ;
77
78                // Walks the start node parents path, checking all styles that are being listened.
79                var path = new FCKElementPath( startElement ) ;
80                var styles = this.GetStyles() ;
81
82                for ( var styleName in styles )
83                {
84                        var callbacks = this._Callbacks[ styleName ] ;
85
86                        if ( callbacks )
87                        {
88                                var style = styles[ styleName ] ;
89                                var state = style.CheckActive( path ) ;
90
91                                if ( state != ( style._LastState || null ) )
92                                {
93                                        style._LastState = state ;
94
95                                        for ( var i = 0 ; i < callbacks.length ; i++ )
96                                        {
97                                                var callback = callbacks[i][0] ;
98                                                var callbackOwner = callbacks[i][1] ;
99
100                                                callback.call( callbackOwner || window, styleName, state ) ;
101                                        }
102                                }
103                        }
104                }
105        },
106
107        CheckStyleInSelection : function( styleName )
108        {
109                return false ;
110        },
111
112        _GetRemoveFormatTagsRegex : function ()
113        {
114                var regex = new RegExp( '^(?:' + FCKConfig.RemoveFormatTags.replace( /,/g,'|' ) + ')$', 'i' ) ;
115
116                return (this._GetRemoveFormatTagsRegex = function()
117                {
118                        return regex ;
119                })
120                && regex  ;
121        },
122
123        /**
124         * Remove all styles from the current selection.
125         * TODO:
126         *  - This is almost a duplication of FCKStyle.RemoveFromRange. We should
127         *    try to merge things.
128         */
129        RemoveAll : function()
130        {
131                var range = new FCKDomRange( FCK.EditorWindow ) ;
132                range.MoveToSelection() ;
133
134                if ( range.CheckIsCollapsed() )
135                        return ;
136
137                        // Expand the range, if inside inline element boundaries.
138                range.Expand( 'inline_elements' ) ;
139
140                // Get the bookmark nodes.
141                // Bookmark the range so we can re-select it after processing.
142                var bookmark = range.CreateBookmark( true ) ;
143
144                // The style will be applied within the bookmark boundaries.
145                var startNode   = range.GetBookmarkNode( bookmark, true ) ;
146                var endNode             = range.GetBookmarkNode( bookmark, false ) ;
147
148                range.Release( true ) ;
149
150                var tagsRegex = this._GetRemoveFormatTagsRegex() ;
151
152                // We need to check the selection boundaries (bookmark spans) to break
153                // the code in a way that we can properly remove partially selected nodes.
154                // For example, removing a <b> style from
155                //              <b>This is [some text</b> to show <b>the] problem</b>
156                // ... where [ and ] represent the selection, must result:
157                //              <b>This is </b>[some text to show the]<b> problem</b>
158                // The strategy is simple, we just break the partial nodes before the
159                // removal logic, having something that could be represented this way:
160                //              <b>This is </b>[<b>some text</b> to show <b>the</b>]<b> problem</b>
161
162                // Let's start checking the start boundary.
163                var path = new FCKElementPath( startNode ) ;
164                var pathElements = path.Elements ;
165                var pathElement ;
166
167                for ( var i = 1 ; i < pathElements.length ; i++ )
168                {
169                        pathElement = pathElements[i] ;
170
171                        if ( pathElement == path.Block || pathElement == path.BlockLimit )
172                                break ;
173
174                        // If this element can be removed (even partially).
175                        if ( tagsRegex.test( pathElement.nodeName ) )
176                                FCKDomTools.BreakParent( startNode, pathElement, range ) ;
177                }
178
179                // Now the end boundary.
180                path = new FCKElementPath( endNode ) ;
181                pathElements = path.Elements ;
182
183                for ( var i = 1 ; i < pathElements.length ; i++ )
184                {
185                        pathElement = pathElements[i] ;
186
187                        if ( pathElement == path.Block || pathElement == path.BlockLimit )
188                                break ;
189
190                        elementName = pathElement.nodeName.toLowerCase() ;
191
192                        // If this element can be removed (even partially).
193                        if ( tagsRegex.test( pathElement.nodeName ) )
194                                FCKDomTools.BreakParent( endNode, pathElement, range ) ;
195                }
196
197                // Navigate through all nodes between the bookmarks.
198                var currentNode = FCKDomTools.GetNextSourceNode( startNode, true, 1 ) ;
199
200                while ( currentNode )
201                {
202                        // If we have reached the end of the selection, stop looping.
203                        if ( currentNode == endNode )
204                                break ;
205
206                        // Cache the next node to be processed. Do it now, because
207                        // currentNode may be removed.
208                        var nextNode = FCKDomTools.GetNextSourceNode( currentNode, false, 1 ) ;
209
210                        // Remove elements nodes that match with this style rules.
211                        if ( tagsRegex.test( currentNode.nodeName ) )
212                                FCKDomTools.RemoveNode( currentNode, true ) ;
213                        else
214                                FCKDomTools.RemoveAttributes( currentNode, FCKConfig.RemoveAttributesArray );
215
216                        currentNode = nextNode ;
217                }
218
219                range.SelectBookmark( bookmark ) ;
220
221                FCK.Events.FireEvent( 'OnSelectionChange' ) ;
222        },
223
224        GetStyle : function( styleName )
225        {
226                return this.GetStyles()[ styleName ] ;
227        },
228
229        GetStyles : function()
230        {
231                var styles = this._GetStyles ;
232                if ( !styles )
233                {
234                        styles = this._GetStyles = FCKTools.Merge(
235                                this._LoadStylesCore(),
236                                this._LoadStylesCustom(),
237                                this._LoadStylesXml() ) ;
238                }
239                return styles ;
240        },
241
242        CheckHasObjectStyle : function( elementName )
243        {
244                return !!this._ObjectStyles[ elementName ] ;
245        },
246
247        _LoadStylesCore : function()
248        {
249                var styles = {};
250                var styleDefs = FCKConfig.CoreStyles ;
251
252                for ( var styleName in styleDefs )
253                {
254                        // Core styles are prefixed with _FCK_.
255                        var style = styles[ '_FCK_' + styleName ] = new FCKStyle( styleDefs[ styleName ] ) ;
256                        style.IsCore = true ;
257                }
258                return styles ;
259        },
260
261        _LoadStylesCustom : function()
262        {
263                var styles = {};
264                var styleDefs = FCKConfig.CustomStyles ;
265
266                if ( styleDefs )
267                {
268                        for ( var styleName in styleDefs )
269                        {
270                                var style = styles[ styleName ] = new FCKStyle( styleDefs[ styleName ] ) ;
271                                style.Name = styleName ;
272                        }
273                }
274
275                return styles ;
276        },
277
278        _LoadStylesXml : function()
279        {
280                var styles = {};
281
282                var stylesXmlPath = FCKConfig.StylesXmlPath ;
283
284                if ( !stylesXmlPath || stylesXmlPath.length == 0 )
285                        return styles ;
286
287                // Load the XML file into a FCKXml object.
288                var xml = new FCKXml() ;
289                xml.LoadUrl( stylesXmlPath ) ;
290
291                var stylesXmlObj = FCKXml.TransformToObject( xml.SelectSingleNode( 'Styles' ) ) ;
292
293                // Get the "Style" nodes defined in the XML file.
294                var styleNodes = stylesXmlObj.$Style ;
295
296                // Check that it did contain some valid nodes
297                if ( !styleNodes )
298                        return styles ;
299
300                // Add each style to our "Styles" collection.
301                for ( var i = 0 ; i < styleNodes.length ; i++ )
302                {
303                        var styleNode = styleNodes[i] ;
304
305                        var element = ( styleNode.element || '' ).toLowerCase() ;
306
307                        if ( element.length == 0 )
308                                throw( 'The element name is required. Error loading "' + stylesXmlPath + '"' ) ;
309
310                        var styleDef = {
311                                Element : element,
312                                Attributes : {},
313                                Styles : {},
314                                Overrides : []
315                        } ;
316
317                        // Get the attributes defined for the style (if any).
318                        var attNodes = styleNode.$Attribute || [] ;
319
320                        // Add the attributes to the style definition object.
321                        for ( var j = 0 ; j < attNodes.length ; j++ )
322                        {
323                                styleDef.Attributes[ attNodes[j].name ] = attNodes[j].value ;
324                        }
325
326                        // Get the styles defined for the style (if any).
327                        var cssStyleNodes = styleNode.$Style || [] ;
328
329                        // Add the attributes to the style definition object.
330                        for ( j = 0 ; j < cssStyleNodes.length ; j++ )
331                        {
332                                styleDef.Styles[ cssStyleNodes[j].name ] = cssStyleNodes[j].value ;
333                        }
334
335                        // Load override definitions.
336                        var cssStyleOverrideNodes = styleNode.$Override ;
337                        if ( cssStyleOverrideNodes )
338                        {
339                                for ( j = 0 ; j < cssStyleOverrideNodes.length ; j++ )
340                                {
341                                        var overrideNode = cssStyleOverrideNodes[j] ;
342                                        var overrideDef =
343                                        {
344                                                Element : overrideNode.element
345                                        } ;
346
347                                        var overrideAttNode = overrideNode.$Attribute ;
348                                        if ( overrideAttNode )
349                                        {
350                                                overrideDef.Attributes = {} ;
351                                                for ( var k = 0 ; k < overrideAttNode.length ; k++ )
352                                                {
353                                                        var overrideAttValue = overrideAttNode[k].value || null ;
354                                                        if ( overrideAttValue )
355                                                        {
356                                                                // Check if the override attribute value is a regular expression.
357                                                                var regexMatch = overrideAttValue && FCKRegexLib.RegExp.exec( overrideAttValue ) ;
358                                                                if ( regexMatch )
359                                                                        overrideAttValue = new RegExp( regexMatch[1], regexMatch[2] || '' ) ;
360                                                        }
361                                                        overrideDef.Attributes[ overrideAttNode[k].name ] = overrideAttValue ;
362                                                }
363                                        }
364
365                                        styleDef.Overrides.push( overrideDef ) ;
366                                }
367                        }
368
369                        var style = new FCKStyle( styleDef ) ;
370                        style.Name = styleNode.name || element ;
371
372                        if ( style.GetType() == FCK_STYLE_OBJECT )
373                                this._ObjectStyles[ element ] = true ;
374
375                        // Add the style to the "Styles" collection using it's name as the key.
376                        styles[ style.Name ] = style ;
377                }
378
379                return styles ;
380        }
381} ;
Note: See TracBrowser for help on using the repository browser.