8
0

bootstrap-ui-inner.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /**
  2. * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* globals $, window, console:false */
  6. // Basic classes to create an editor.
  7. import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
  8. import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview';
  9. import HtmlDataProcessor from '@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor';
  10. import ElementReplacer from '@ckeditor/ckeditor5-utils/src/elementreplacer';
  11. // Interfaces to extend basic Editor API.
  12. import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin';
  13. import ElementApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin';
  14. // Helper function for adding interfaces to the Editor class.
  15. import mix from '@ckeditor/ckeditor5-utils/src/mix';
  16. // Helper function that binds editor with HTMLForm element.
  17. import attachToForm from '@ckeditor/ckeditor5-core/src/editor/utils/attachtoform';
  18. // Basic features that every editor should enable.
  19. import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
  20. import Enter from '@ckeditor/ckeditor5-enter/src/enter';
  21. import Typing from '@ckeditor/ckeditor5-typing/src/typing';
  22. import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
  23. import UndoEngine from '@ckeditor/ckeditor5-undo/src/undoengine';
  24. // Basic features to associated with the edited content.
  25. import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine';
  26. import ItalicEngine from '@ckeditor/ckeditor5-basic-styles/src/italicengine';
  27. import UnderlineEngine from '@ckeditor/ckeditor5-basic-styles/src/underlineengine';
  28. import HeadingEngine from '@ckeditor/ckeditor5-heading/src/headingengine';
  29. // EasyImage
  30. import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
  31. import { TOKEN_URL } from '@ckeditor/ckeditor5-cloudservices/tests/_utils/cloudservices-config';
  32. // Extending the Editor, which brings base editor API.
  33. export default class BootstrapEditor extends Editor {
  34. constructor( element, config ) {
  35. super( config );
  36. this.element = element;
  37. // Create the ("main") root element of the model tree.
  38. this.model.document.createRoot();
  39. // Use the HTML data processor in this editor.
  40. this.data.processor = new HtmlDataProcessor();
  41. // This editor uses a single editable view in DOM.
  42. this.editable = new InlineEditableUIView( this.locale );
  43. // A helper to easily replace the editor#element with editor.editable#element.
  44. this._elementReplacer = new ElementReplacer();
  45. // When editor#element is a textarea inside a form element
  46. // then content of this textarea will be updated on form submit.
  47. attachToForm( this );
  48. }
  49. destroy() {
  50. // When destroyed, editor sets the output of editor#getData() into editor#element...
  51. this.updateElement();
  52. // ...and restores editor#element.
  53. this._elementReplacer.restore();
  54. return super.destroy();
  55. }
  56. static create( element, config ) {
  57. return new Promise( resolve => {
  58. const editor = new this( element, config );
  59. const editable = editor.editable;
  60. resolve(
  61. editor.initPlugins()
  62. // Render the editable view in DOM first.
  63. .then( () => editable.render() )
  64. // Replace the editor#element with editor.editable#element.
  65. .then( () => editor._elementReplacer.replace( element, editable.element ) )
  66. // Handle the UI of the editor.
  67. .then( () => {
  68. // Create an editing root in the editing layer. It will correspond with the
  69. // document root created in the constructor().
  70. const editingRoot = editor.editing.createRoot( 'div' );
  71. // Bind the basic attributes of the editable in DOM with the editing layer.
  72. editable.bind( 'isReadOnly' ).to( editingRoot );
  73. editable.bind( 'isFocused' ).to( editor.editing.view );
  74. editable.name = editingRoot.rootName;
  75. // Setup the external Bootstrap UI so it works with the editor.
  76. setupButtons( editor );
  77. setupHeadingDropdown( editor );
  78. // Tell the world that the UI of the editor is ready to use.
  79. editor.fire( 'uiReady' );
  80. } )
  81. // Bind the editor editing layer to the editable in DOM.
  82. .then( () => editor.editing.view.attachDomRoot( editable.element ) )
  83. .then( () => editor.loadDataFromElement() )
  84. // Fire the events that announce that the editor is complete and ready to use.
  85. .then( () => {
  86. editor.fire( 'dataReady' );
  87. editor.fire( 'ready' );
  88. } )
  89. .then( () => editor )
  90. );
  91. } );
  92. }
  93. }
  94. // Mixing interfaces, which extends basic editor API.
  95. mix( BootstrapEditor, DataApiMixin );
  96. mix( BootstrapEditor, ElementApiMixin );
  97. // This function activates Bold, Italic, Underline, Undo and Redo buttons in the toolbar.
  98. function setupButtons( editor ) {
  99. [ 'bold', 'italic', 'underline', 'undo', 'redo' ].forEach( commandName => {
  100. // Retrieve the editor command corresponding with the id of the button in DOM.
  101. const command = editor.commands.get( commandName );
  102. // Retrieve the jQuery object corresponding with the button in DOM.
  103. const button = $( `#${ commandName }` );
  104. // Clicking on the buttons should execute the editor command...
  105. button.click( () => editor.execute( commandName ) );
  106. // ...but it should not steal the focus so the editing is uninterrupted.
  107. button.mousedown( evt => evt.preventDefault() );
  108. // Commands can become disabled, e.g. when the editor is read-only.
  109. // Make sure the buttons reflect this state change.
  110. command.on( 'change:isEnabled', onIsEnabledChange );
  111. onIsEnabledChange();
  112. // Bold, Italic and Underline commands have a value that changes
  113. // when the selection starts in an element the command creates.
  114. // The button should indicate that e.g. editing text which is already bold.
  115. if ( !new Set( [ 'undo', 'redo' ] ).has( commandName ) ) {
  116. command.on( 'change:value', onValueChange );
  117. onValueChange();
  118. }
  119. function onValueChange() {
  120. button.toggleClass( 'active', command.value );
  121. }
  122. function onIsEnabledChange() {
  123. button.attr( 'disabled', () => !command.isEnabled );
  124. }
  125. } );
  126. }
  127. // This function activates the headings dropdown in the toolbar.
  128. function setupHeadingDropdown( editor ) {
  129. const menu = $( '.ck-editor .dropdown-menu' );
  130. const button = $( '.ck-editor .dropdown-toggle' );
  131. // Create a dropdown menu entry for each heading configuration option.
  132. editor.config.get( 'heading.options' ).map( option => {
  133. // Retrieve the editor command corresponding with the configuration option.
  134. const command = editor.commands.get( option.modelElement );
  135. // Create the menu item DOM element.
  136. const menuItem = $(
  137. `<a href="#" class="dropdown-item heading-item_${ option.modelElement }">` +
  138. `${ option.title }` +
  139. '</a>' );
  140. // Upon click, the dropdown menua item should execute the command and focus
  141. // the editing view to keep the editing process uninterrupted.
  142. menuItem.click( () => {
  143. editor.execute( option.modelElement );
  144. editor.editing.view.focus();
  145. } );
  146. menu.append( menuItem );
  147. // Make sure the dropdown and its items reflect the state of the
  148. // currently active command.
  149. command.on( 'change:value', onValueChange );
  150. onValueChange();
  151. // Heading commands can become disabled, e.g. when the editor is read-only.
  152. // Make sure the UI reflects this state change.
  153. command.on( 'change:isEnabled', onIsEnabledChange );
  154. onIsEnabledChange();
  155. function onValueChange() {
  156. if ( command.value ) {
  157. button.children( ':first' ).text( option.title );
  158. }
  159. menuItem.toggleClass( 'active', command.value );
  160. }
  161. function onIsEnabledChange() {
  162. button.attr( 'disabled', () => !command.isEnabled );
  163. }
  164. } );
  165. }
  166. // Finally, create the BootstrapEditor instance with a selected set of features.
  167. BootstrapEditor
  168. .create( $( '#editor' ).get( 0 ), {
  169. plugins: [
  170. Clipboard, Enter, Typing, Paragraph, EasyImage,
  171. BoldEngine, ItalicEngine, UnderlineEngine, HeadingEngine, UndoEngine,
  172. ],
  173. cloudServices: {
  174. tokenUrl: TOKEN_URL
  175. }
  176. } )
  177. .then( editor => {
  178. window.editor = editor;
  179. $( '#toggle-readonly' ).on( 'click', () => {
  180. editor.isReadOnly = !editor.isReadOnly;
  181. } );
  182. } )
  183. .catch( err => {
  184. console.error( err.stack );
  185. } );