/EQT_V2/EQT/EQTWebApp/fckeditor/editor/_source/internals/fckstyles.js
JavaScript | 381 lines | 240 code | 74 blank | 67 comment | 63 complexity | fe3d7c522d3a6fb160035c581b25cdb5 MD5 | raw file
Possible License(s): LGPL-2.1
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} ;