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 CKEDITOR.dialog.add( 'specialchar', function( editor ) 7 { 8 /** 9 * Simulate "this" of a dialog for non-dialog events. 10 * @type {CKEDITOR.dialog} 11 */ 12 var dialog, 13 lang = editor.lang.specialChar; 14 15 var onChoice = function( evt ) 16 { 17 var target, value; 18 if ( evt.data ) 19 target = evt.data.getTarget(); 20 else 21 target = new CKEDITOR.dom.element( evt ); 22 23 if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) ) 24 { 25 target.removeClass( "cke_light_background" ); 26 dialog.hide(); 27 28 // We must use "insertText" here to keep text styled. 29 var span = editor.document.createElement( 'span' ); 30 span.setHtml( value ); 31 editor.insertText( span.getText() ); 32 } 33 }; 34 35 var onClick = CKEDITOR.tools.addFunction( onChoice ); 36 37 var focusedNode; 38 39 var onFocus = function( evt, target ) 40 { 41 var value; 42 target = target || evt.data.getTarget(); 43 44 if ( target.getName() == 'span' ) 45 target = target.getParent(); 46 47 if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) ) 48 { 49 // Trigger blur manually if there is focused node. 50 if ( focusedNode ) 51 onBlur( null, focusedNode ); 52 53 var htmlPreview = dialog.getContentElement( 'info', 'htmlPreview' ).getElement(); 54 55 dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( value ); 56 htmlPreview.setHtml( CKEDITOR.tools.htmlEncode( value ) ); 57 target.getParent().addClass( "cke_light_background" ); 58 59 // Memorize focused node. 60 focusedNode = target; 61 } 62 }; 63 64 var onBlur = function( evt, target ) 65 { 66 target = target || evt.data.getTarget(); 67 68 if ( target.getName() == 'span' ) 69 target = target.getParent(); 70 71 if ( target.getName() == 'a' ) 72 { 73 dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( ' ' ); 74 dialog.getContentElement( 'info', 'htmlPreview' ).getElement().setHtml( ' ' ); 75 target.getParent().removeClass( "cke_light_background" ); 76 77 focusedNode = undefined; 78 } 79 }; 80 81 var onKeydown = CKEDITOR.tools.addFunction( function( ev ) 82 { 83 ev = new CKEDITOR.dom.event( ev ); 84 85 // Get an Anchor element. 86 var element = ev.getTarget(); 87 var relative, nodeToMove; 88 var keystroke = ev.getKeystroke(), 89 rtl = editor.lang.dir == 'rtl'; 90 91 switch ( keystroke ) 92 { 93 // UP-ARROW 94 case 38 : 95 // relative is TR 96 if ( ( relative = element.getParent().getParent().getPrevious() ) ) 97 { 98 nodeToMove = relative.getChild( [element.getParent().getIndex(), 0] ); 99 nodeToMove.focus(); 100 onBlur( null, element ); 101 onFocus( null, nodeToMove ); 102 } 103 ev.preventDefault(); 104 break; 105 // DOWN-ARROW 106 case 40 : 107 // relative is TR 108 if ( ( relative = element.getParent().getParent().getNext() ) ) 109 { 110 nodeToMove = relative.getChild( [ element.getParent().getIndex(), 0 ] ); 111 if ( nodeToMove && nodeToMove.type == 1 ) 112 { 113 nodeToMove.focus(); 114 onBlur( null, element ); 115 onFocus( null, nodeToMove ); 116 } 117 } 118 ev.preventDefault(); 119 break; 120 // SPACE 121 // ENTER is already handled as onClick 122 case 32 : 123 onChoice( { data: ev } ); 124 ev.preventDefault(); 125 break; 126 127 // RIGHT-ARROW 128 case rtl ? 37 : 39 : 129 // relative is TD 130 if ( ( relative = element.getParent().getNext() ) ) 131 { 132 nodeToMove = relative.getChild( 0 ); 133 if ( nodeToMove.type == 1 ) 134 { 135 nodeToMove.focus(); 136 onBlur( null, element ); 137 onFocus( null, nodeToMove ); 138 ev.preventDefault( true ); 139 } 140 else 141 onBlur( null, element ); 142 } 143 // relative is TR 144 else if ( ( relative = element.getParent().getParent().getNext() ) ) 145 { 146 nodeToMove = relative.getChild( [ 0, 0 ] ); 147 if ( nodeToMove && nodeToMove.type == 1 ) 148 { 149 nodeToMove.focus(); 150 onBlur( null, element ); 151 onFocus( null, nodeToMove ); 152 ev.preventDefault( true ); 153 } 154 else 155 onBlur( null, element ); 156 } 157 break; 158 159 // LEFT-ARROW 160 case rtl ? 39 : 37 : 161 // relative is TD 162 if ( ( relative = element.getParent().getPrevious() ) ) 163 { 164 nodeToMove = relative.getChild( 0 ); 165 nodeToMove.focus(); 166 onBlur( null, element ); 167 onFocus( null, nodeToMove ); 168 ev.preventDefault( true ); 169 } 170 // relative is TR 171 else if ( ( relative = element.getParent().getParent().getPrevious() ) ) 172 { 173 nodeToMove = relative.getLast().getChild( 0 ); 174 nodeToMove.focus(); 175 onBlur( null, element ); 176 onFocus( null, nodeToMove ); 177 ev.preventDefault( true ); 178 } 179 else 180 onBlur( null, element ); 181 break; 182 default : 183 // Do not stop not handled events. 184 return; 185 } 186 }); 187 188 return { 189 title : lang.title, 190 minWidth : 430, 191 minHeight : 280, 192 buttons : [ CKEDITOR.dialog.cancelButton ], 193 charColumns : 17, 194 onLoad : function() 195 { 196 var columns = this.definition.charColumns, 197 extraChars = editor.config.extraSpecialChars, 198 chars = editor.config.specialChars; 199 200 var charsTableLabel = CKEDITOR.tools.getNextId() + '_specialchar_table_label'; 201 var html = [ '<table role="listbox" aria-labelledby="' + charsTableLabel + '"' + 202 ' style="width: 320px; height: 100%; border-collapse: separate;"' + 203 ' align="center" cellspacing="2" cellpadding="2" border="0">' ]; 204 205 var i = 0, 206 size = chars.length, 207 character, 208 charDesc; 209 210 while ( i < size ) 211 { 212 html.push( '<tr role="presentation">' ) ; 213 214 for ( var j = 0 ; j < columns ; j++, i++ ) 215 { 216 if ( ( character = chars[ i ] ) ) 217 { 218 charDesc = ''; 219 220 if ( character instanceof Array ) 221 { 222 charDesc = character[ 1 ]; 223 character = character[ 0 ]; 224 } 225 else 226 { 227 var _tmpName = character.replace( '&', '' ).replace( ';', '' ).replace( '#', '' ); 228 229 // Use character in case description unavailable. 230 charDesc = lang[ _tmpName ] || character; 231 } 232 233 var charLabelId = 'cke_specialchar_label_' + i + '_' + CKEDITOR.tools.getNextNumber(); 234 235 html.push( 236 '<td class="cke_dark_background" style="cursor: default" role="presentation">' + 237 '<a href="javascript: void(0);" role="option"' + 238 ' aria-posinset="' + ( i +1 ) + '"', 239 ' aria-setsize="' + size + '"', 240 ' aria-labelledby="' + charLabelId + '"', 241 ' style="cursor: inherit; display: block; height: 1.25em; margin-top: 0.25em; text-align: center;" title="', CKEDITOR.tools.htmlEncode( charDesc ), '"' + 242 ' onkeydown="CKEDITOR.tools.callFunction( ' + onKeydown + ', event, this )"' + 243 ' onclick="CKEDITOR.tools.callFunction(' + onClick + ', this); return false;"' + 244 ' tabindex="-1">' + 245 '<span style="margin: 0 auto;cursor: inherit">' + 246 character + 247 '</span>' + 248 '<span class="cke_voice_label" id="' + charLabelId + '">' + 249 charDesc + 250 '</span></a>'); 251 } 252 else 253 html.push( '<td class="cke_dark_background"> ' ); 254 255 html.push( '</td>' ); 256 } 257 html.push( '</tr>' ); 258 } 259 260 html.push( '</tbody></table>', '<span id="' + charsTableLabel + '" class="cke_voice_label">' + lang.options +'</span>' ); 261 262 this.getContentElement( 'info', 'charContainer' ).getElement().setHtml( html.join( '' ) ); 263 }, 264 contents : [ 265 { 266 id : 'info', 267 label : editor.lang.common.generalTab, 268 title : editor.lang.common.generalTab, 269 padding : 0, 270 align : 'top', 271 elements : [ 272 { 273 type : 'hbox', 274 align : 'top', 275 widths : [ '320px', '90px' ], 276 children : 277 [ 278 { 279 type : 'html', 280 id : 'charContainer', 281 html : '', 282 onMouseover : onFocus, 283 onMouseout : onBlur, 284 focus : function() 285 { 286 var firstChar = this.getElement().getElementsByTag( 'a' ).getItem( 0 ); 287 setTimeout( function() 288 { 289 firstChar.focus(); 290 onFocus( null, firstChar ); 291 }, 0 ); 292 }, 293 onShow : function() 294 { 295 var firstChar = this.getElement().getChild( [ 0, 0, 0, 0, 0 ] ); 296 setTimeout( function() 297 { 298 firstChar.focus(); 299 onFocus( null, firstChar ); 300 }, 0 ); 301 }, 302 onLoad : function( event ) 303 { 304 dialog = event.sender; 305 } 306 }, 307 { 308 type : 'hbox', 309 align : 'top', 310 widths : [ '100%' ], 311 children : 312 [ 313 { 314 type : 'vbox', 315 align : 'top', 316 children : 317 [ 318 { 319 type : 'html', 320 html : '<div></div>' 321 }, 322 { 323 type : 'html', 324 id : 'charPreview', 325 className : 'cke_dark_background', 326 style : 'border:1px solid #eeeeee;font-size:28px;height:40px;width:70px;padding-top:9px;font-family:\'Microsoft Sans Serif\',Arial,Helvetica,Verdana;text-align:center;', 327 html : '<div> </div>' 328 }, 329 { 330 type : 'html', 331 id : 'htmlPreview', 332 className : 'cke_dark_background', 333 style : 'border:1px solid #eeeeee;font-size:14px;height:20px;width:70px;padding-top:2px;font-family:\'Microsoft Sans Serif\',Arial,Helvetica,Verdana;text-align:center;', 334 html : '<div> </div>' 335 } 336 ] 337 } 338 ] 339 } 340 ] 341 } 342 ] 343 } 344 ] 345 }; 346 } ); 347