1 /* 2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 (function() 7 { 8 CKEDITOR.htmlParser.filter = CKEDITOR.tools.createClass( 9 { 10 $ : function( rules ) 11 { 12 this._ = 13 { 14 elementNames : [], 15 attributeNames : [], 16 elements : { $length : 0 }, 17 attributes : { $length : 0 } 18 }; 19 20 if ( rules ) 21 this.addRules( rules, 10 ); 22 }, 23 24 proto : 25 { 26 addRules : function( rules, priority ) 27 { 28 if ( typeof priority != 'number' ) 29 priority = 10; 30 31 // Add the elementNames. 32 addItemsToList( this._.elementNames, rules.elementNames, priority ); 33 34 // Add the attributeNames. 35 addItemsToList( this._.attributeNames, rules.attributeNames, priority ); 36 37 // Add the elements. 38 addNamedItems( this._.elements, rules.elements, priority ); 39 40 // Add the attributes. 41 addNamedItems( this._.attributes, rules.attributes, priority ); 42 43 // Add the text. 44 this._.text = transformNamedItem( this._.text, rules.text, priority ) || this._.text; 45 46 // Add the comment. 47 this._.comment = transformNamedItem( this._.comment, rules.comment, priority ) || this._.comment; 48 49 // Add root fragment. 50 this._.root = transformNamedItem( this._.root, rules.root, priority ) || this._.root; 51 }, 52 53 onElementName : function( name ) 54 { 55 return filterName( name, this._.elementNames ); 56 }, 57 58 onAttributeName : function( name ) 59 { 60 return filterName( name, this._.attributeNames ); 61 }, 62 63 onText : function( text ) 64 { 65 var textFilter = this._.text; 66 return textFilter ? textFilter.filter( text ) : text; 67 }, 68 69 onComment : function( commentText, comment ) 70 { 71 var textFilter = this._.comment; 72 return textFilter ? textFilter.filter( commentText, comment ) : commentText; 73 }, 74 75 onFragment : function( element ) 76 { 77 var rootFilter = this._.root; 78 return rootFilter ? rootFilter.filter( element ) : element; 79 }, 80 81 onElement : function( element ) 82 { 83 // We must apply filters set to the specific element name as 84 // well as those set to the generic $ name. So, add both to an 85 // array and process them in a small loop. 86 var filters = [ this._.elements[ '^' ], this._.elements[ element.name ], this._.elements.$ ], 87 filter, ret; 88 89 for ( var i = 0 ; i < 3 ; i++ ) 90 { 91 filter = filters[ i ]; 92 if ( filter ) 93 { 94 ret = filter.filter( element, this ); 95 96 if ( ret === false ) 97 return null; 98 99 if ( ret && ret != element ) 100 return this.onNode( ret ); 101 102 // The non-root element has been dismissed by one of the filters. 103 if ( element.parent && !element.name ) 104 break; 105 } 106 } 107 108 return element; 109 }, 110 111 onNode : function( node ) 112 { 113 var type = node.type; 114 115 return type == CKEDITOR.NODE_ELEMENT ? this.onElement( node ) : 116 type == CKEDITOR.NODE_TEXT ? new CKEDITOR.htmlParser.text( this.onText( node.value ) ) : 117 type == CKEDITOR.NODE_COMMENT ? new CKEDITOR.htmlParser.comment( this.onComment( node.value ) ): 118 null; 119 }, 120 121 onAttribute : function( element, name, value ) 122 { 123 var filter = this._.attributes[ name ]; 124 125 if ( filter ) 126 { 127 var ret = filter.filter( value, element, this ); 128 129 if ( ret === false ) 130 return false; 131 132 if ( typeof ret != 'undefined' ) 133 return ret; 134 } 135 136 return value; 137 } 138 } 139 }); 140 141 function filterName( name, filters ) 142 { 143 for ( var i = 0 ; name && i < filters.length ; i++ ) 144 { 145 var filter = filters[ i ]; 146 name = name.replace( filter[ 0 ], filter[ 1 ] ); 147 } 148 return name; 149 } 150 151 function addItemsToList( list, items, priority ) 152 { 153 if ( typeof items == 'function' ) 154 items = [ items ]; 155 156 var i, j, 157 listLength = list.length, 158 itemsLength = items && items.length; 159 160 if ( itemsLength ) 161 { 162 // Find the index to insert the items at. 163 for ( i = 0 ; i < listLength && list[ i ].pri < priority ; i++ ) 164 { /*jsl:pass*/ } 165 166 // Add all new items to the list at the specific index. 167 for ( j = itemsLength - 1 ; j >= 0 ; j-- ) 168 { 169 var item = items[ j ]; 170 if ( item ) 171 { 172 item.pri = priority; 173 list.splice( i, 0, item ); 174 } 175 } 176 } 177 } 178 179 function addNamedItems( hashTable, items, priority ) 180 { 181 if ( items ) 182 { 183 for ( var name in items ) 184 { 185 var current = hashTable[ name ]; 186 187 hashTable[ name ] = 188 transformNamedItem( 189 current, 190 items[ name ], 191 priority ); 192 193 if ( !current ) 194 hashTable.$length++; 195 } 196 } 197 } 198 199 function transformNamedItem( current, item, priority ) 200 { 201 if ( item ) 202 { 203 item.pri = priority; 204 205 if ( current ) 206 { 207 // If the current item is not an Array, transform it. 208 if ( !current.splice ) 209 { 210 if ( current.pri > priority ) 211 current = [ item, current ]; 212 else 213 current = [ current, item ]; 214 215 current.filter = callItems; 216 } 217 else 218 addItemsToList( current, item, priority ); 219 220 return current; 221 } 222 else 223 { 224 item.filter = item; 225 return item; 226 } 227 } 228 } 229 230 // Invoke filters sequentially on the array, break the iteration 231 // when it doesn't make sense to continue anymore. 232 function callItems( currentEntry ) 233 { 234 var isNode = currentEntry.type 235 || currentEntry instanceof CKEDITOR.htmlParser.fragment; 236 237 for ( var i = 0 ; i < this.length ; i++ ) 238 { 239 // Backup the node info before filtering. 240 if ( isNode ) 241 { 242 var orgType = currentEntry.type, 243 orgName = currentEntry.name; 244 } 245 246 var item = this[ i ], 247 ret = item.apply( window, arguments ); 248 249 if ( ret === false ) 250 return ret; 251 252 // We're filtering node (element/fragment). 253 if ( isNode ) 254 { 255 // No further filtering if it's not anymore 256 // fitable for the subsequent filters. 257 if ( ret && ( ret.name != orgName 258 || ret.type != orgType ) ) 259 { 260 return ret; 261 } 262 } 263 // Filtering value (nodeName/textValue/attrValue). 264 else 265 { 266 // No further filtering if it's not 267 // any more values. 268 if ( typeof ret != 'string' ) 269 return ret; 270 } 271 272 ret != undefined && ( currentEntry = ret ); 273 } 274 275 return currentEntry; 276 } 277 })(); 278 279 // "entities" plugin 280 /* 281 { 282 text : function( text ) 283 { 284 // TODO : Process entities. 285 return text.toUpperCase(); 286 } 287 }; 288 */ 289