specialchar.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. /**
  2. * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or http://ckeditor.com/license
  4. */
  5. CKEDITOR.dialog.add( 'specialchar', function( editor ) {
  6. // Simulate "this" of a dialog for non-dialog events.
  7. // @type {CKEDITOR.dialog}
  8. var dialog,
  9. lang = editor.lang.specialchar;
  10. var onChoice = function( evt ) {
  11. var target, value;
  12. if ( evt.data )
  13. target = evt.data.getTarget();
  14. else
  15. target = new CKEDITOR.dom.element( evt );
  16. if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) ) {
  17. target.removeClass( 'cke_light_background' );
  18. dialog.hide();
  19. // We must use "insertText" here to keep text styled.
  20. var span = editor.document.createElement( 'span' );
  21. span.setHtml( value );
  22. editor.insertText( span.getText() );
  23. }
  24. };
  25. var onClick = CKEDITOR.tools.addFunction( onChoice );
  26. var focusedNode;
  27. var onFocus = function( evt, target ) {
  28. var value;
  29. target = target || evt.data.getTarget();
  30. if ( target.getName() == 'span' )
  31. target = target.getParent();
  32. if ( target.getName() == 'a' && ( value = target.getChild( 0 ).getHtml() ) ) {
  33. // Trigger blur manually if there is focused node.
  34. if ( focusedNode )
  35. onBlur( null, focusedNode );
  36. var htmlPreview = dialog.getContentElement( 'info', 'htmlPreview' ).getElement();
  37. dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( value );
  38. htmlPreview.setHtml( CKEDITOR.tools.htmlEncode( value ) );
  39. target.getParent().addClass( 'cke_light_background' );
  40. // Memorize focused node.
  41. focusedNode = target;
  42. }
  43. };
  44. var onBlur = function( evt, target ) {
  45. target = target || evt.data.getTarget();
  46. if ( target.getName() == 'span' )
  47. target = target.getParent();
  48. if ( target.getName() == 'a' ) {
  49. dialog.getContentElement( 'info', 'charPreview' ).getElement().setHtml( ' ' );
  50. dialog.getContentElement( 'info', 'htmlPreview' ).getElement().setHtml( ' ' );
  51. target.getParent().removeClass( 'cke_light_background' );
  52. focusedNode = undefined;
  53. }
  54. };
  55. var onKeydown = CKEDITOR.tools.addFunction( function( ev ) {
  56. ev = new CKEDITOR.dom.event( ev );
  57. // Get an Anchor element.
  58. var element = ev.getTarget();
  59. var relative, nodeToMove;
  60. var keystroke = ev.getKeystroke(),
  61. rtl = editor.lang.dir == 'rtl';
  62. switch ( keystroke ) {
  63. // UP-ARROW
  64. case 38:
  65. // relative is TR
  66. if ( ( relative = element.getParent().getParent().getPrevious() ) ) {
  67. nodeToMove = relative.getChild( [ element.getParent().getIndex(), 0 ] );
  68. nodeToMove.focus();
  69. onBlur( null, element );
  70. onFocus( null, nodeToMove );
  71. }
  72. ev.preventDefault();
  73. break;
  74. // DOWN-ARROW
  75. case 40:
  76. // relative is TR
  77. if ( ( relative = element.getParent().getParent().getNext() ) ) {
  78. nodeToMove = relative.getChild( [ element.getParent().getIndex(), 0 ] );
  79. if ( nodeToMove && nodeToMove.type == 1 ) {
  80. nodeToMove.focus();
  81. onBlur( null, element );
  82. onFocus( null, nodeToMove );
  83. }
  84. }
  85. ev.preventDefault();
  86. break;
  87. // SPACE
  88. // ENTER is already handled as onClick
  89. case 32:
  90. onChoice( { data: ev } );
  91. ev.preventDefault();
  92. break;
  93. // RIGHT-ARROW
  94. case rtl ? 37 : 39:
  95. // relative is TD
  96. if ( ( relative = element.getParent().getNext() ) ) {
  97. nodeToMove = relative.getChild( 0 );
  98. if ( nodeToMove.type == 1 ) {
  99. nodeToMove.focus();
  100. onBlur( null, element );
  101. onFocus( null, nodeToMove );
  102. ev.preventDefault( true );
  103. } else {
  104. onBlur( null, element );
  105. }
  106. }
  107. // relative is TR
  108. else if ( ( relative = element.getParent().getParent().getNext() ) ) {
  109. nodeToMove = relative.getChild( [ 0, 0 ] );
  110. if ( nodeToMove && nodeToMove.type == 1 ) {
  111. nodeToMove.focus();
  112. onBlur( null, element );
  113. onFocus( null, nodeToMove );
  114. ev.preventDefault( true );
  115. } else {
  116. onBlur( null, element );
  117. }
  118. }
  119. break;
  120. // LEFT-ARROW
  121. case rtl ? 39 : 37:
  122. // relative is TD
  123. if ( ( relative = element.getParent().getPrevious() ) ) {
  124. nodeToMove = relative.getChild( 0 );
  125. nodeToMove.focus();
  126. onBlur( null, element );
  127. onFocus( null, nodeToMove );
  128. ev.preventDefault( true );
  129. }
  130. // relative is TR
  131. else if ( ( relative = element.getParent().getParent().getPrevious() ) ) {
  132. nodeToMove = relative.getLast().getChild( 0 );
  133. nodeToMove.focus();
  134. onBlur( null, element );
  135. onFocus( null, nodeToMove );
  136. ev.preventDefault( true );
  137. } else {
  138. onBlur( null, element );
  139. }
  140. break;
  141. default:
  142. // Do not stop not handled events.
  143. return;
  144. }
  145. } );
  146. return {
  147. title: lang.title,
  148. minWidth: 430,
  149. minHeight: 280,
  150. buttons: [ CKEDITOR.dialog.cancelButton ],
  151. charColumns: 17,
  152. onLoad: function() {
  153. var columns = this.definition.charColumns,
  154. chars = editor.config.specialChars;
  155. var charsTableLabel = CKEDITOR.tools.getNextId() + '_specialchar_table_label';
  156. var html = [ '<table role="listbox" aria-labelledby="' + charsTableLabel + '"' +
  157. ' style="width: 320px; height: 100%; border-collapse: separate;"' +
  158. ' align="center" cellspacing="2" cellpadding="2" border="0">' ];
  159. var i = 0,
  160. size = chars.length,
  161. character, charDesc;
  162. while ( i < size ) {
  163. html.push( '<tr role="presentation">' );
  164. for ( var j = 0; j < columns; j++, i++ ) {
  165. if ( ( character = chars[ i ] ) ) {
  166. charDesc = '';
  167. if ( character instanceof Array ) {
  168. charDesc = character[ 1 ];
  169. character = character[ 0 ];
  170. } else {
  171. var _tmpName = character.replace( '&', '' ).replace( ';', '' ).replace( '#', '' );
  172. // Use character in case description unavailable.
  173. charDesc = lang[ _tmpName ] || character;
  174. }
  175. var charLabelId = 'cke_specialchar_label_' + i + '_' + CKEDITOR.tools.getNextNumber();
  176. html.push( '<td class="cke_dark_background" style="cursor: default" role="presentation">' +
  177. '<a href="javascript: void(0);" role="option"' +
  178. ' aria-posinset="' + ( i + 1 ) + '"', ' aria-setsize="' + size + '"', ' aria-labelledby="' + charLabelId + '"', ' class="cke_specialchar" title="', CKEDITOR.tools.htmlEncode( charDesc ), '"' +
  179. ' onkeydown="CKEDITOR.tools.callFunction( ' + onKeydown + ', event, this )"' +
  180. ' onclick="CKEDITOR.tools.callFunction(' + onClick + ', this); return false;"' +
  181. ' tabindex="-1">' +
  182. '<span style="margin: 0 auto;cursor: inherit">' +
  183. character +
  184. '</span>' +
  185. '<span class="cke_voice_label" id="' + charLabelId + '">' +
  186. charDesc +
  187. '</span></a>' );
  188. } else {
  189. html.push( '<td class="cke_dark_background">&nbsp;' );
  190. }
  191. html.push( '</td>' );
  192. }
  193. html.push( '</tr>' );
  194. }
  195. html.push( '</tbody></table>', '<span id="' + charsTableLabel + '" class="cke_voice_label">' + lang.options + '</span>' );
  196. this.getContentElement( 'info', 'charContainer' ).getElement().setHtml( html.join( '' ) );
  197. },
  198. contents: [ {
  199. id: 'info',
  200. label: editor.lang.common.generalTab,
  201. title: editor.lang.common.generalTab,
  202. padding: 0,
  203. align: 'top',
  204. elements: [ {
  205. type: 'hbox',
  206. align: 'top',
  207. widths: [ '320px', '90px' ],
  208. children: [ {
  209. type: 'html',
  210. id: 'charContainer',
  211. html: '',
  212. onMouseover: onFocus,
  213. onMouseout: onBlur,
  214. focus: function() {
  215. var firstChar = this.getElement().getElementsByTag( 'a' ).getItem( 0 );
  216. setTimeout( function() {
  217. firstChar.focus();
  218. onFocus( null, firstChar );
  219. }, 0 );
  220. },
  221. onShow: function() {
  222. var firstChar = this.getElement().getChild( [ 0, 0, 0, 0, 0 ] );
  223. setTimeout( function() {
  224. firstChar.focus();
  225. onFocus( null, firstChar );
  226. }, 0 );
  227. },
  228. onLoad: function( event ) {
  229. dialog = event.sender;
  230. }
  231. },
  232. {
  233. type: 'hbox',
  234. align: 'top',
  235. widths: [ '100%' ],
  236. children: [ {
  237. type: 'vbox',
  238. align: 'top',
  239. children: [
  240. {
  241. type: 'html',
  242. html: '<div></div>'
  243. },
  244. {
  245. type: 'html',
  246. id: 'charPreview',
  247. className: 'cke_dark_background',
  248. 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;',
  249. html: '<div>&nbsp;</div>'
  250. },
  251. {
  252. type: 'html',
  253. id: 'htmlPreview',
  254. className: 'cke_dark_background',
  255. 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;',
  256. html: '<div>&nbsp;</div>'
  257. }
  258. ]
  259. } ]
  260. } ]
  261. } ]
  262. } ]
  263. };
  264. } );