]>
git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js
1 /* global ToolbarConfigurator */
5 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
6 if ( typeof Object
.create
!= 'function' ) {
9 Object
.create = function( o
) {
10 if ( arguments
.length
> 1 ) {
11 throw Error( 'Second argument not supported' );
14 throw Error( 'Cannot set a null [[Prototype]]' );
16 if ( typeof o
!= 'object' ) {
17 throw TypeError( 'Argument must be an object' );
25 // Copy of the divarea plugin (with some enhancements), so we always have some editable mode, regardless of the build's config.
26 CKEDITOR
.plugins
.add( 'toolbarconfiguratorarea', {
27 // Use afterInit to override wysiwygarea's mode. May still fail to override divarea, but divarea is nice.
28 afterInit: function( editor
) {
29 editor
.addMode( 'wysiwyg', function( callback
) {
30 var editingBlock
= CKEDITOR
.dom
.element
.createFromHtml( '<div class="cke_wysiwyg_div cke_reset" hidefocus="true"></div>' );
32 var contentSpace
= editor
.ui
.space( 'contents' );
33 contentSpace
.append( editingBlock
);
35 editingBlock
= editor
.editable( editingBlock
);
37 editingBlock
.detach
= CKEDITOR
.tools
.override( editingBlock
.detach
,
40 org
.apply( this, arguments
);
45 editor
.setData( editor
.getData( 1 ), callback
);
46 editor
.fire( 'contentDom' );
49 // Additions to the divarea.
51 // Speed up data processing.
52 editor
.dataProcessor
.toHtml = function( html
) {
55 editor
.dataProcessor
.toDataFormat = function( html
) {
59 // End of the additions.
63 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
65 Object
.keys
= ( function() {
66 var hasOwnProperty
= Object
.prototype.hasOwnProperty
,
67 hasDontEnumBug
= !( { toString: null } ).propertyIsEnumerable( 'toString' ),
74 'propertyIsEnumerable',
77 dontEnumsLength
= dontEnums
.length
;
79 return function( obj
) {
80 if ( typeof obj
!== 'object' && ( typeof obj
!== 'function' || obj
=== null ) )
81 throw new TypeError( 'Object.keys called on non-object' );
83 var result
= [], prop
, i
;
86 if ( hasOwnProperty
.call( obj
, prop
) )
91 if ( hasDontEnumBug
) {
92 for ( i
= 0; i
< dontEnumsLength
; i
++ ) {
93 if ( hasOwnProperty
.call( obj
, dontEnums
[ i
] ) )
94 result
.push( dontEnums
[ i
] );
105 * @class ToolbarConfigurator.AbstractToolbarModifier
106 * @param {String} editorId An id of modified editor
109 function AbstractToolbarModifier( editorId
, cfg
) {
110 this.cfg
= cfg
|| {};
112 this.editorId
= editorId
;
113 this.fullToolbarEditor
= new ToolbarConfigurator
.FullToolbarEditor();
115 this.mainContainer
= null;
117 this.originalConfig
= null;
118 this.actualConfig
= null;
120 this.waitForReady
= false;
121 this.isEditableVisible
= false;
123 this.toolbarContainer
= null;
124 this.toolbarButtons
= [];
128 ToolbarConfigurator
.AbstractToolbarModifier
= AbstractToolbarModifier
;
131 * @param {String} config
133 AbstractToolbarModifier
.prototype.setConfig = function( config
) {
134 this._onInit( undefined, config
, true );
138 * @param {Function} [callback]
140 AbstractToolbarModifier
.prototype.init = function( callback
) {
143 this.mainContainer
= new CKEDITOR
.dom
.element( 'div' );
145 if ( this.fullToolbarEditor
.editorInstance
!== null ) {
146 throw 'Only one instance of ToolbarModifier is allowed';
149 if ( !this.editorInstance
) {
150 // Do not refresh yet, let's wait for the full toolbar editor (see below).
151 this._createEditor( false );
154 this.editorInstance
.once( 'loaded', function() {
155 that
.fullToolbarEditor
.init( function() {
156 that
._onInit( callback
);
158 if ( typeof that
.onRefresh
== 'function' ) {
161 }, that
.editorInstance
.config
);
164 return this.mainContainer
;
168 * Called editor initialization finished.
170 * @param {Function} callback
171 * @param {String} [actualConfig]
174 AbstractToolbarModifier
.prototype._onInit = function( callback
, actualConfig
) {
175 this.originalConfig
= this.editorInstance
.config
;
177 if ( !actualConfig
) {
178 this.actualConfig
= JSON
.parse( JSON
.stringify( this.originalConfig
) );
180 this.actualConfig
= JSON
.parse( actualConfig
);
183 if ( !this.actualConfig
.toolbarGroups
&& !this.actualConfig
.toolbar
) {
184 this.actualConfig
.toolbarGroups
= getDefaultToolbarGroups( this.editorInstance
);
187 if ( typeof callback
=== 'function' )
188 callback( this.mainContainer
);
190 // Here we are going to keep only `name` and `groups` data from editor `toolbar` property.
191 function getDefaultToolbarGroups( editor
) {
192 var toolbarGroups
= editor
.toolbar
,
195 var max
= toolbarGroups
.length
;
196 for ( var i
= 0; i
< max
; i
++ ) {
197 var group
= toolbarGroups
[ i
];
199 if ( typeof group
== 'string' ) {
200 copy
.push( group
); // separator
204 groups: group
.groups
? group
.groups
.slice() : []
214 * Creates DOM structure of tool.
216 * @returns {CKEDITOR.dom.element}
219 AbstractToolbarModifier
.prototype._createModifier = function() {
220 this.mainContainer
.addClass( 'unselectable' );
222 if ( this.modifyContainer
) {
223 this.modifyContainer
.remove();
226 this.modifyContainer
= new CKEDITOR
.dom
.element( 'div' );
227 this.modifyContainer
.addClass( 'toolbarModifier' );
229 this.mainContainer
.append( this.modifyContainer
);
231 return this.mainContainer
;
235 * Find editable area in CKEditor instance DOM container
237 * @returns {CKEDITOR.dom.element}
239 AbstractToolbarModifier
.prototype.getEditableArea = function() {
240 var selector
= ( '#' + this.editorInstance
.id
+ '_contents' );
242 return this.editorInstance
.container
.findOne( selector
);
246 * Hide editable area in modified editor by sets its height to 0.
250 AbstractToolbarModifier
.prototype._hideEditable = function() {
251 var area
= this.getEditableArea();
253 this.isEditableVisible
= false;
255 this.lastEditableAreaHeight
= area
.getStyle( 'height' );
256 area
.setStyle( 'height', '0' );
260 * Show editable area in modified editor.
264 AbstractToolbarModifier
.prototype._showEditable = function() {
265 this.isEditableVisible
= true;
267 this.getEditableArea().setStyle( 'height', this.lastEditableAreaHeight
|| 'auto' );
271 * Toggle editable area visibility.
275 AbstractToolbarModifier
.prototype._toggleEditable = function() {
276 if ( this.isEditableVisible
)
277 this._hideEditable();
279 this._showEditable();
283 * Usually called when configuration changes.
287 AbstractToolbarModifier
.prototype._refreshEditor = function() {
289 status
= this.editorInstance
.status
;
291 // Wait for ready only once.
292 if ( this.waitForReady
)
296 if ( status
== 'unloaded' || status
== 'loaded' ) {
297 this.waitForReady
= true;
299 this.editorInstance
.once( 'instanceReady', function() {
302 // Ready or destroyed.
308 that
.editorInstance
.destroy();
309 that
._createEditor( true, that
.getActualConfig() );
310 that
.waitForReady
= false;
315 * Creates editor that can be used to present the toolbar configuration.
319 AbstractToolbarModifier
.prototype._createEditor = function( doRefresh
, configOverrides
) {
322 this.editorInstance
= CKEDITOR
.replace( this.editorId
);
324 this.editorInstance
.on( 'configLoaded', function() {
325 var config
= that
.editorInstance
.config
;
327 if ( configOverrides
) {
328 CKEDITOR
.tools
.extend( config
, configOverrides
, true );
331 AbstractToolbarModifier
.extendPluginsConfig( config
);
334 // Prevent creating any other space than the top one.
335 this.editorInstance
.on( 'uiSpace', function( evt
) {
336 if ( evt
.data
.space
!= 'top' ) {
339 }, null, null, -999 );
341 this.editorInstance
.once( 'loaded', function() {
342 var btns
= that
.editorInstance
.ui
.instances
;
344 for ( var i
in btns
) {
346 btns
[ i
].click
= empty
;
347 btns
[ i
].onClick
= empty
;
351 if ( !that
.isEditableVisible
) {
352 that
._hideEditable();
355 if ( that
.currentActive
&& that
.currentActive
.name
) {
356 that
._highlightGroup( that
.currentActive
.name
);
365 if ( doRefresh
&& ( typeof that
.onRefresh
=== 'function' ) ) {
374 * Always returns copy of config.
378 AbstractToolbarModifier
.prototype.getActualConfig = function() {
379 return JSON
.parse( JSON
.stringify( this.actualConfig
) );
383 * Creates toolbar in tool.
387 AbstractToolbarModifier
.prototype._createToolbar = function() {
388 if ( !this.toolbarButtons
.length
) {
392 this.toolbarContainer
= new CKEDITOR
.dom
.element( 'div' );
393 this.toolbarContainer
.addClass( 'toolbar' );
395 var max
= this.toolbarButtons
.length
;
396 for ( var i
= 0; i
< max
; i
+= 1 ) {
397 this._createToolbarBtn( this.toolbarButtons
[ i
] );
402 * Create toolbar button and add it to toolbar container
404 * @param {Object} cfg
405 * @returns {CKEDITOR.dom.element}
408 AbstractToolbarModifier
.prototype._createToolbarBtn = function( cfg
) {
409 var btnText
= ( typeof cfg
.text
=== 'string' ? cfg
.text : cfg
.text
.inactive
),
410 btn
= ToolbarConfigurator
.FullToolbarEditor
.createButton( btnText
, cfg
.cssClass
);
412 this.toolbarContainer
.append( btn
);
413 btn
.data( 'group', cfg
.group
);
414 btn
.addClass( cfg
.position
);
415 btn
.on( 'click', function() {
416 cfg
.clickCallback
.call( this, btn
, cfg
);
424 * @param {Object} config
426 AbstractToolbarModifier
.prototype._fixGroups = function( config
) {
427 var groups
= config
.toolbarGroups
|| [];
429 var max
= groups
.length
;
430 for ( var i
= 0; i
< max
; i
+= 1 ) {
431 var currentGroup
= groups
[ i
];
433 // separator, in config, is in raw format
434 // need to make it more sophisticated to keep unique id
436 if ( currentGroup
== '/' ) {
437 currentGroup
= groups
[ i
] = {};
438 currentGroup
.type
= 'separator';
439 currentGroup
.name
= ( 'separator' + CKEDITOR
.tools
.getNextNumber() );
443 // sometimes subgroups are not set (basic package), so need to
444 // create them artifically
445 currentGroup
.groups
= currentGroup
.groups
|| [];
447 // when there is no subgroup with same name like its parent name
448 // then it have to be added artificially
449 // in order to maintain consistency between user interface and config
450 if ( CKEDITOR
.tools
.indexOf( currentGroup
.groups
, currentGroup
.name
) == -1 ) {
451 this.editorInstance
.ui
.addToolbarGroup( currentGroup
.name
, currentGroup
.groups
[ currentGroup
.groups
.length
- 1 ], currentGroup
.name
);
452 currentGroup
.groups
.push( currentGroup
.name
);
455 this._fixSubgroups( currentGroup
);
460 * Transform subgroup string to object literal
461 * with keys: {String} name and {Number} totalBtns
462 * Please note: this method modify Object provided in first argument
466 * { groups: [ 'nameOne', 'nameTwo' ] }
471 * { groups: [ { name: 'nameOne', totalBtns: 3 }, { name: 'nameTwo', totalBtns: 5 } ] }
474 * @param {Object} group
477 AbstractToolbarModifier
.prototype._fixSubgroups = function( group
) {
478 var subGroups
= group
.groups
;
480 var max
= subGroups
.length
;
481 for ( var i
= 0; i
< max
; i
+= 1 ) {
482 var subgroupName
= subGroups
[ i
];
486 totalBtns: ToolbarConfigurator
.ToolbarModifier
.getTotalSubGroupButtonsNumber( subgroupName
, this.fullToolbarEditor
)
492 * Same as JSON.stringify method but returned string is in one line
494 * @param {Object} json
495 * @param {Object} opts
496 * @param {Boolean} opts.addSpaces
497 * @param {Boolean} opts.noQuotesOnKey
498 * @param {Boolean} opts.singleQuotes
501 AbstractToolbarModifier
.stringifyJSONintoOneLine = function( json
, opts
) {
503 var stringJSON
= JSON
.stringify( json
, null, '' );
505 // IE8 make new line characters
506 stringJSON
= stringJSON
.replace( /\n/g, '' );
508 if ( opts
.addSpaces
) {
509 stringJSON
= stringJSON
.replace( /(\{|:|,|\[|\])/g, function( sentence
) {
510 return sentence
+ ' ';
513 stringJSON
= stringJSON
.replace( /(\])/g, function( sentence
) {
514 return ' ' + sentence
;
518 if ( opts
.noQuotesOnKey
) {
519 stringJSON
= stringJSON
.replace( /"(\w*)":/g, function( sentence
, word
) {
524 if ( opts
.singleQuotes
) {
525 stringJSON
= stringJSON
.replace( /\"/g, '\'' );
532 * Hide toolbar configurator
534 AbstractToolbarModifier
.prototype.hideUI = function() {
536 this.mainContainer
.hide();
537 if ( this.editorInstance
.container
) {
538 this.editorInstance
.container
.hide();
543 * Show toolbar configurator
545 AbstractToolbarModifier
.prototype.showUI = function() {
547 this.mainContainer
.show();
548 if ( this.editorInstance
.container
) {
549 this.editorInstance
.container
.show();
555 * Extends plugins setttings in the specified config with settings useful for
556 * the toolbar configurator.
560 AbstractToolbarModifier
.extendPluginsConfig = function( config
) {
561 var extraPlugins
= config
.extraPlugins
;
563 // Enable the special, lightweight area to replace wysiwygarea.
564 config
.extraPlugins
= ( extraPlugins
? extraPlugins
+ ',' : '' ) + 'toolbarconfiguratorarea';