plugin.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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. ( function() {
  6. var template = '<a id="{id}"' +
  7. ' class="cke_button cke_button__{name} cke_button_{state} {cls}"' +
  8. ( CKEDITOR.env.gecko && !CKEDITOR.env.hc ? '' : ' href="javascript:void(\'{titleJs}\')"' ) +
  9. ' title="{title}"' +
  10. ' tabindex="-1"' +
  11. ' hidefocus="true"' +
  12. ' role="button"' +
  13. ' aria-labelledby="{id}_label"' +
  14. ' aria-haspopup="{hasArrow}"' +
  15. ' aria-disabled="{ariaDisabled}"';
  16. // Some browsers don't cancel key events in the keydown but in the
  17. // keypress.
  18. // TODO: Check if really needed.
  19. if ( CKEDITOR.env.gecko && CKEDITOR.env.mac )
  20. template += ' onkeypress="return false;"';
  21. // With Firefox, we need to force the button to redraw, otherwise it
  22. // will remain in the focus state.
  23. if ( CKEDITOR.env.gecko )
  24. template += ' onblur="this.style.cssText = this.style.cssText;"';
  25. template += ' onkeydown="return CKEDITOR.tools.callFunction({keydownFn},event);"' +
  26. ' onfocus="return CKEDITOR.tools.callFunction({focusFn},event);" ' +
  27. ( CKEDITOR.env.ie ? 'onclick="return false;" onmouseup' : 'onclick' ) + // #188
  28. '="CKEDITOR.tools.callFunction({clickFn},this);return false;">' +
  29. '<span class="cke_button_icon cke_button__{iconName}_icon" style="{style}"';
  30. template += '>&nbsp;</span>' +
  31. '<span id="{id}_label" class="cke_button_label cke_button__{name}_label" aria-hidden="false">{label}</span>' +
  32. '{arrowHtml}' +
  33. '</a>';
  34. var templateArrow = '<span class="cke_button_arrow">' +
  35. // BLACK DOWN-POINTING TRIANGLE
  36. ( CKEDITOR.env.hc ? '&#9660;' : '' ) +
  37. '</span>';
  38. var btnArrowTpl = CKEDITOR.addTemplate( 'buttonArrow', templateArrow ),
  39. btnTpl = CKEDITOR.addTemplate( 'button', template );
  40. CKEDITOR.plugins.add( 'button', {
  41. lang: 'af,ar,bg,ca,cs,da,de,el,en,en-gb,eo,es,fa,fi,fr,gl,he,hu,it,ja,km,ko,ku,lt,nb,nl,pl,pt,pt-br,ro,ru,sk,sl,sq,sv,tr,tt,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
  42. beforeInit: function( editor ) {
  43. editor.ui.addHandler( CKEDITOR.UI_BUTTON, CKEDITOR.ui.button.handler );
  44. }
  45. } );
  46. /**
  47. * Button UI element.
  48. *
  49. * @readonly
  50. * @property {String} [='button']
  51. * @member CKEDITOR
  52. */
  53. CKEDITOR.UI_BUTTON = 'button';
  54. /**
  55. * Represents a button UI element. This class should not be called directly. To
  56. * create new buttons use {@link CKEDITOR.ui#addButton} instead.
  57. *
  58. * @class
  59. * @constructor Creates a button class instance.
  60. * @param {Object} definition The button definition.
  61. */
  62. CKEDITOR.ui.button = function( definition ) {
  63. CKEDITOR.tools.extend( this, definition,
  64. // Set defaults.
  65. {
  66. title: definition.label,
  67. click: definition.click ||
  68. function( editor ) {
  69. editor.execCommand( definition.command );
  70. }
  71. } );
  72. this._ = {};
  73. };
  74. /**
  75. * Represents the button handler object.
  76. *
  77. * @class
  78. * @singleton
  79. * @extends CKEDITOR.ui.handlerDefinition
  80. */
  81. CKEDITOR.ui.button.handler = {
  82. /**
  83. * Transforms a button definition in a {@link CKEDITOR.ui.button} instance.
  84. *
  85. * @member CKEDITOR.ui.button.handler
  86. * @param {Object} definition
  87. * @returns {CKEDITOR.ui.button}
  88. */
  89. create: function( definition ) {
  90. return new CKEDITOR.ui.button( definition );
  91. }
  92. };
  93. /** @class CKEDITOR.ui.button */
  94. CKEDITOR.ui.button.prototype = {
  95. /**
  96. * Renders the button.
  97. *
  98. * @param {CKEDITOR.editor} editor The editor instance which this button is
  99. * to be used by.
  100. * @param {Array} output The output array to which the HTML code related to
  101. * this button should be appended.
  102. */
  103. render: function( editor, output ) {
  104. function updateState() {
  105. // "this" is a CKEDITOR.ui.button instance.
  106. var mode = editor.mode;
  107. if ( mode ) {
  108. // Restore saved button state.
  109. var state = this.modes[ mode ] ? modeStates[ mode ] !== undefined ? modeStates[ mode ] : CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED;
  110. state = editor.readOnly && !this.readOnly ? CKEDITOR.TRISTATE_DISABLED : state;
  111. this.setState( state );
  112. // Let plugin to disable button.
  113. if ( this.refresh )
  114. this.refresh();
  115. }
  116. }
  117. var env = CKEDITOR.env,
  118. id = this._.id = CKEDITOR.tools.getNextId(),
  119. stateName = '',
  120. command = this.command,
  121. // Get the command name.
  122. clickFn;
  123. this._.editor = editor;
  124. var instance = {
  125. id: id,
  126. button: this,
  127. editor: editor,
  128. focus: function() {
  129. var element = CKEDITOR.document.getById( id );
  130. element.focus();
  131. },
  132. execute: function() {
  133. this.button.click( editor );
  134. },
  135. attach: function( editor ) {
  136. this.button.attach( editor );
  137. }
  138. };
  139. var keydownFn = CKEDITOR.tools.addFunction( function( ev ) {
  140. if ( instance.onkey ) {
  141. ev = new CKEDITOR.dom.event( ev );
  142. return ( instance.onkey( instance, ev.getKeystroke() ) !== false );
  143. }
  144. } );
  145. var focusFn = CKEDITOR.tools.addFunction( function( ev ) {
  146. var retVal;
  147. if ( instance.onfocus )
  148. retVal = ( instance.onfocus( instance, new CKEDITOR.dom.event( ev ) ) !== false );
  149. return retVal;
  150. } );
  151. var selLocked = 0;
  152. instance.clickFn = clickFn = CKEDITOR.tools.addFunction( function() {
  153. // Restore locked selection in Opera.
  154. if ( selLocked ) {
  155. editor.unlockSelection( 1 );
  156. selLocked = 0;
  157. }
  158. instance.execute();
  159. // Fixed iOS focus issue when your press disabled button (#12381).
  160. if ( env.iOS ) {
  161. editor.focus();
  162. }
  163. } );
  164. // Indicate a mode sensitive button.
  165. if ( this.modes ) {
  166. var modeStates = {};
  167. editor.on( 'beforeModeUnload', function() {
  168. if ( editor.mode && this._.state != CKEDITOR.TRISTATE_DISABLED )
  169. modeStates[ editor.mode ] = this._.state;
  170. }, this );
  171. // Update status when activeFilter, mode or readOnly changes.
  172. editor.on( 'activeFilterChange', updateState, this );
  173. editor.on( 'mode', updateState, this );
  174. // If this button is sensitive to readOnly state, update it accordingly.
  175. !this.readOnly && editor.on( 'readOnly', updateState, this );
  176. } else if ( command ) {
  177. // Get the command instance.
  178. command = editor.getCommand( command );
  179. if ( command ) {
  180. command.on( 'state', function() {
  181. this.setState( command.state );
  182. }, this );
  183. stateName += ( command.state == CKEDITOR.TRISTATE_ON ? 'on' : command.state == CKEDITOR.TRISTATE_DISABLED ? 'disabled' : 'off' );
  184. }
  185. }
  186. // For button that has text-direction awareness on selection path.
  187. if ( this.directional ) {
  188. editor.on( 'contentDirChanged', function( evt ) {
  189. var el = CKEDITOR.document.getById( this._.id ),
  190. icon = el.getFirst();
  191. var pathDir = evt.data;
  192. // Make a minor direction change to become style-able for the skin icon.
  193. if ( pathDir != editor.lang.dir )
  194. el.addClass( 'cke_' + pathDir );
  195. else
  196. el.removeClass( 'cke_ltr' ).removeClass( 'cke_rtl' );
  197. // Inline style update for the plugin icon.
  198. icon.setAttribute( 'style', CKEDITOR.skin.getIconStyle( iconName, pathDir == 'rtl', this.icon, this.iconOffset ) );
  199. }, this );
  200. }
  201. if ( !command )
  202. stateName += 'off';
  203. var name = this.name || this.command,
  204. iconName = name;
  205. // Check if we're pointing to an icon defined by another command. (#9555)
  206. if ( this.icon && !( /\./ ).test( this.icon ) ) {
  207. iconName = this.icon;
  208. this.icon = null;
  209. }
  210. var params = {
  211. id: id,
  212. name: name,
  213. iconName: iconName,
  214. label: this.label,
  215. cls: this.className || '',
  216. state: stateName,
  217. ariaDisabled: stateName == 'disabled' ? 'true' : 'false',
  218. title: this.title,
  219. titleJs: env.gecko && !env.hc ? '' : ( this.title || '' ).replace( "'", '' ),
  220. hasArrow: this.hasArrow ? 'true' : 'false',
  221. keydownFn: keydownFn,
  222. focusFn: focusFn,
  223. clickFn: clickFn,
  224. style: CKEDITOR.skin.getIconStyle( iconName, ( editor.lang.dir == 'rtl' ), this.icon, this.iconOffset ),
  225. arrowHtml: this.hasArrow ? btnArrowTpl.output() : ''
  226. };
  227. btnTpl.output( params, output );
  228. if ( this.onRender )
  229. this.onRender();
  230. return instance;
  231. },
  232. /**
  233. * Sets the button state.
  234. *
  235. * @param {Number} state Indicates the button state. One of {@link CKEDITOR#TRISTATE_ON},
  236. * {@link CKEDITOR#TRISTATE_OFF}, or {@link CKEDITOR#TRISTATE_DISABLED}.
  237. */
  238. setState: function( state ) {
  239. if ( this._.state == state )
  240. return false;
  241. this._.state = state;
  242. var element = CKEDITOR.document.getById( this._.id );
  243. if ( element ) {
  244. element.setState( state, 'cke_button' );
  245. state == CKEDITOR.TRISTATE_DISABLED ?
  246. element.setAttribute( 'aria-disabled', true ) :
  247. element.removeAttribute( 'aria-disabled' );
  248. if ( !this.hasArrow ) {
  249. // Note: aria-pressed attribute should not be added to menuButton instances. (#11331)
  250. state == CKEDITOR.TRISTATE_ON ?
  251. element.setAttribute( 'aria-pressed', true ) :
  252. element.removeAttribute( 'aria-pressed' );
  253. } else {
  254. var newLabel = state == CKEDITOR.TRISTATE_ON ?
  255. this._.editor.lang.button.selectedLabel.replace( /%1/g, this.label ) : this.label;
  256. CKEDITOR.document.getById( this._.id + '_label' ).setText( newLabel );
  257. }
  258. return true;
  259. } else {
  260. return false;
  261. }
  262. },
  263. /**
  264. * Gets the button state.
  265. *
  266. * @returns {Number} The button state. One of {@link CKEDITOR#TRISTATE_ON},
  267. * {@link CKEDITOR#TRISTATE_OFF}, or {@link CKEDITOR#TRISTATE_DISABLED}.
  268. */
  269. getState: function() {
  270. return this._.state;
  271. },
  272. /**
  273. * Returns this button's {@link CKEDITOR.feature} instance.
  274. *
  275. * It may be this button instance if it has at least one of
  276. * `allowedContent` and `requiredContent` properties. Otherwise,
  277. * if a command is bound to this button by the `command` property, then
  278. * that command will be returned.
  279. *
  280. * This method implements the {@link CKEDITOR.feature#toFeature} interface method.
  281. *
  282. * @since 4.1
  283. * @param {CKEDITOR.editor} Editor instance.
  284. * @returns {CKEDITOR.feature} The feature.
  285. */
  286. toFeature: function( editor ) {
  287. if ( this._.feature )
  288. return this._.feature;
  289. var feature = this;
  290. // If button isn't a feature, return command if is bound.
  291. if ( !this.allowedContent && !this.requiredContent && this.command )
  292. feature = editor.getCommand( this.command ) || feature;
  293. return this._.feature = feature;
  294. }
  295. };
  296. /**
  297. * Adds a button definition to the UI elements list.
  298. *
  299. * editorInstance.ui.addButton( 'MyBold', {
  300. * label: 'My Bold',
  301. * command: 'bold',
  302. * toolbar: 'basicstyles,1'
  303. * } );
  304. *
  305. * @member CKEDITOR.ui
  306. * @param {String} name The button name.
  307. * @param {Object} definition The button definition.
  308. * @param {String} definition.label The textual part of the button (if visible) and its tooltip.
  309. * @param {String} definition.command The command to be executed once the button is activated.
  310. * @param {String} definition.toolbar The {@link CKEDITOR.config#toolbarGroups toolbar group} into which
  311. * the button will be added. An optional index value (separated by a comma) determines the button position within the group.
  312. */
  313. CKEDITOR.ui.prototype.addButton = function( name, definition ) {
  314. this.add( name, CKEDITOR.UI_BUTTON, definition );
  315. };
  316. } )();