diff options
author | Ismaël Bouya <ismael.bouya@normalesup.org> | 2016-01-25 17:45:33 +0100 |
---|---|---|
committer | Ismaël Bouya <ismael.bouya@normalesup.org> | 2016-01-25 18:00:33 +0100 |
commit | 7adcb81e4f83f98c468889aaa5a85558ba88c770 (patch) | |
tree | 0d6ede733777b29060b48df4afaa2c64bfbae276 /sources/samples/toolbarconfigurator/js/toolbarmodifier.js | |
download | connexionswing-ckeditor-component-7adcb81e4f83f98c468889aaa5a85558ba88c770.tar.gz connexionswing-ckeditor-component-7adcb81e4f83f98c468889aaa5a85558ba88c770.tar.zst connexionswing-ckeditor-component-7adcb81e4f83f98c468889aaa5a85558ba88c770.zip |
Initial commit4.5.6
Diffstat (limited to 'sources/samples/toolbarconfigurator/js/toolbarmodifier.js')
-rw-r--r-- | sources/samples/toolbarconfigurator/js/toolbarmodifier.js | 1366 |
1 files changed, 1366 insertions, 0 deletions
diff --git a/sources/samples/toolbarconfigurator/js/toolbarmodifier.js b/sources/samples/toolbarconfigurator/js/toolbarmodifier.js new file mode 100644 index 00000000..bd33d24f --- /dev/null +++ b/sources/samples/toolbarconfigurator/js/toolbarmodifier.js | |||
@@ -0,0 +1,1366 @@ | |||
1 | /* global ToolbarConfigurator, alert */ | ||
2 | |||
3 | 'use strict'; | ||
4 | |||
5 | ( function() { | ||
6 | var AbstractToolbarModifier = ToolbarConfigurator.AbstractToolbarModifier; | ||
7 | |||
8 | /** | ||
9 | * @class ToolbarConfigurator.ToolbarModifier | ||
10 | * @param {String} editorId An id of modified editor | ||
11 | * @param {Object} cfg | ||
12 | * @extends AbstractToolbarModifier | ||
13 | * @constructor | ||
14 | */ | ||
15 | function ToolbarModifier( editorId, cfg ) { | ||
16 | AbstractToolbarModifier.call( this, editorId, cfg ); | ||
17 | |||
18 | this.removedButtons = null; | ||
19 | this.originalConfig = null; | ||
20 | this.actualConfig = null; | ||
21 | this.emptyVisible = false; | ||
22 | |||
23 | // edit, paste, config | ||
24 | this.state = 'edit'; | ||
25 | |||
26 | this.toolbarButtons = [ | ||
27 | { | ||
28 | text: { | ||
29 | active: 'Hide empty toolbar groups', | ||
30 | inactive: 'Show empty toolbar groups' | ||
31 | }, | ||
32 | group: 'edit', | ||
33 | position: 'left', | ||
34 | cssClass: 'button-a-soft', | ||
35 | clickCallback: function( button, buttonDefinition ) { | ||
36 | var className = 'button-a-background'; | ||
37 | |||
38 | button[ button.hasClass( className ) ? 'removeClass' : 'addClass' ]( className ); | ||
39 | |||
40 | this._toggleVisibilityEmptyElements(); | ||
41 | |||
42 | if ( this.emptyVisible ) { | ||
43 | button.setText( buttonDefinition.text.active ); | ||
44 | } else { | ||
45 | button.setText( buttonDefinition.text.inactive ); | ||
46 | } | ||
47 | } | ||
48 | }, | ||
49 | { | ||
50 | text: 'Add row separator', | ||
51 | group: 'edit', | ||
52 | position: 'left', | ||
53 | cssClass: 'button-a-soft', | ||
54 | clickCallback: function() { | ||
55 | this._addSeparator(); | ||
56 | } | ||
57 | }, | ||
58 | /*{ | ||
59 | text: 'Paste config', | ||
60 | group: 'edit', | ||
61 | position: 'left', | ||
62 | clickCallback: function() { | ||
63 | this.state = 'paste'; | ||
64 | |||
65 | this.modifyContainer.addClass( 'hidden' ); | ||
66 | this.configContainer.removeClass( 'hidden' ); | ||
67 | this.configContainer.setHtml( '<textarea></textarea>' ); | ||
68 | this.showToolbarBtnsByGroupName( 'config' ); | ||
69 | } | ||
70 | },*/ | ||
71 | { | ||
72 | text: 'Select config', | ||
73 | group: 'config', | ||
74 | position: 'left', | ||
75 | cssClass: 'button-a-soft', | ||
76 | clickCallback: function() { | ||
77 | this.configContainer.findOne( 'textarea' ).$.select(); | ||
78 | } | ||
79 | }, | ||
80 | { | ||
81 | text: 'Back to configurator', | ||
82 | group: 'config', | ||
83 | position: 'right', | ||
84 | cssClass: 'button-a-background', | ||
85 | clickCallback: function() { | ||
86 | if ( this.state === 'paste' ) { | ||
87 | var cfg = this.configContainer.findOne( 'textarea' ).getValue(); | ||
88 | cfg = ToolbarModifier.evaluateToolbarGroupsConfig( cfg ); | ||
89 | |||
90 | if ( cfg ) { | ||
91 | this.setConfig( cfg ); | ||
92 | } else { | ||
93 | alert( 'Your pasted config is wrong.' ); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | this.state = 'edit'; | ||
98 | this._showConfigurationTool(); | ||
99 | this.showToolbarBtnsByGroupName( this.state ); | ||
100 | } | ||
101 | }, | ||
102 | { | ||
103 | text: 'Get toolbar <span class="highlight">config</span>', | ||
104 | group: 'edit', | ||
105 | position: 'right', | ||
106 | cssClass: 'button-a-background icon-pos-left icon-download', | ||
107 | clickCallback: function() { | ||
108 | this.state = 'config'; | ||
109 | this._showConfig(); | ||
110 | this.showToolbarBtnsByGroupName( this.state ); | ||
111 | } | ||
112 | } | ||
113 | ]; | ||
114 | |||
115 | this.cachedActiveElement = null; | ||
116 | } | ||
117 | |||
118 | // Expose the class. | ||
119 | ToolbarConfigurator.ToolbarModifier = ToolbarModifier; | ||
120 | |||
121 | ToolbarModifier.prototype = Object.create( ToolbarConfigurator.AbstractToolbarModifier.prototype ); | ||
122 | |||
123 | /** | ||
124 | * @returns {Object} | ||
125 | */ | ||
126 | ToolbarModifier.prototype.getActualConfig = function() { | ||
127 | var copy = AbstractToolbarModifier.prototype.getActualConfig.call( this ); | ||
128 | |||
129 | if ( copy.toolbarGroups ) { | ||
130 | |||
131 | var max = copy.toolbarGroups.length; | ||
132 | for ( var i = 0; i < max; i += 1 ) { | ||
133 | var currentGroup = copy.toolbarGroups[ i ]; | ||
134 | |||
135 | copy.toolbarGroups[ i ] = ToolbarModifier.parseGroupToConfigValue( currentGroup ); | ||
136 | } | ||
137 | |||
138 | } | ||
139 | |||
140 | return copy; | ||
141 | }; | ||
142 | |||
143 | /** | ||
144 | * @param {Function} callback | ||
145 | * @param {String} [config] | ||
146 | * @param {Boolean} [forceKeepRemoveButtons=false] | ||
147 | * @private | ||
148 | */ | ||
149 | ToolbarModifier.prototype._onInit = function( callback, config, forceKeepRemoveButtons ) { | ||
150 | forceKeepRemoveButtons = ( forceKeepRemoveButtons === true ); | ||
151 | AbstractToolbarModifier.prototype._onInit.call( this, undefined, config ); | ||
152 | |||
153 | this.removedButtons = []; | ||
154 | |||
155 | if ( forceKeepRemoveButtons ) { | ||
156 | if ( this.actualConfig.removeButtons ) { | ||
157 | this.removedButtons = this.actualConfig.removeButtons.split( ',' ); | ||
158 | } else { | ||
159 | this.removedButtons = []; | ||
160 | } | ||
161 | } else { | ||
162 | if ( !( 'removeButtons' in this.originalConfig ) ) { | ||
163 | this.originalConfig.removeButtons = ''; | ||
164 | this.removedButtons = []; | ||
165 | } else { | ||
166 | this.removedButtons = this.originalConfig.removeButtons ? this.originalConfig.removeButtons.split( ',' ) : []; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | if ( !this.actualConfig.toolbarGroups ) | ||
171 | this.actualConfig.toolbarGroups = this.fullToolbarEditor.getFullToolbarGroupsConfig(); | ||
172 | |||
173 | this._fixGroups( this.actualConfig ); | ||
174 | this._calculateTotalBtns(); | ||
175 | |||
176 | this._createModifier(); | ||
177 | this._refreshMoveBtnsAvalibility(); | ||
178 | this._refreshBtnTabIndexes(); | ||
179 | |||
180 | if ( typeof callback === 'function' ) | ||
181 | callback( this.mainContainer ); | ||
182 | }; | ||
183 | |||
184 | /** | ||
185 | * @private | ||
186 | */ | ||
187 | ToolbarModifier.prototype._showConfigurationTool = function() { | ||
188 | this.configContainer.addClass( 'hidden' ); | ||
189 | this.modifyContainer.removeClass( 'hidden' ); | ||
190 | }; | ||
191 | |||
192 | /** | ||
193 | * Show configuration file in tool | ||
194 | * | ||
195 | * @private | ||
196 | */ | ||
197 | ToolbarModifier.prototype._showConfig = function() { | ||
198 | var that = this, | ||
199 | actualConfig = this.getActualConfig(), | ||
200 | cfg = {}; | ||
201 | if ( actualConfig.toolbarGroups ) { | ||
202 | cfg.toolbarGroups = actualConfig.toolbarGroups; | ||
203 | |||
204 | var groups = prepareGroups( actualConfig.toolbarGroups, this.cfg.trimEmptyGroups ); | ||
205 | |||
206 | cfg.toolbarGroups = '\n\t\t' + groups.join( ',\n\t\t' ); | ||
207 | } | ||
208 | |||
209 | function prepareGroups( toolbarGroups, trimEmptyGroups ) { | ||
210 | var groups = [], | ||
211 | max = toolbarGroups.length; | ||
212 | |||
213 | for ( var i = 0; i < max; i++ ) { | ||
214 | var group = toolbarGroups[ i ]; | ||
215 | |||
216 | if ( group === '/' ) { | ||
217 | groups.push( '\'/\'' ); | ||
218 | continue; | ||
219 | } | ||
220 | |||
221 | if ( trimEmptyGroups ) { | ||
222 | var max2 = group.groups.length; | ||
223 | while ( max2-- ) { | ||
224 | var subgroup = group.groups[ max2 ]; | ||
225 | |||
226 | if ( ToolbarModifier.getTotalSubGroupButtonsNumber( subgroup, that.fullToolbarEditor ) === 0 ) { | ||
227 | group.groups.splice( max2, 1 ); | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | if ( !( trimEmptyGroups && group.groups.length === 0 ) ) { | ||
233 | groups.push( AbstractToolbarModifier.stringifyJSONintoOneLine( group, { | ||
234 | addSpaces: true, | ||
235 | noQuotesOnKey: true, | ||
236 | singleQuotes: true | ||
237 | } ) ); | ||
238 | } | ||
239 | } | ||
240 | |||
241 | return groups; | ||
242 | } | ||
243 | |||
244 | if ( actualConfig.removeButtons ) { | ||
245 | cfg.removeButtons = actualConfig.removeButtons; | ||
246 | } | ||
247 | |||
248 | var content = [ | ||
249 | '<textarea class="configCode" readonly>', | ||
250 | 'CKEDITOR.editorConfig = function( config ) {\n', | ||
251 | ( cfg.toolbarGroups ? '\tconfig.toolbarGroups = [' + cfg.toolbarGroups + '\n\t];' : '' ), | ||
252 | ( cfg.removeButtons ? '\n\n' : '' ), | ||
253 | ( cfg.removeButtons ? '\tconfig.removeButtons = \'' + cfg.removeButtons + '\';' : '' ), | ||
254 | '\n};', | ||
255 | '</textarea>' | ||
256 | ].join( '' ); | ||
257 | |||
258 | |||
259 | |||
260 | this.modifyContainer.addClass( 'hidden' ); | ||
261 | this.configContainer.removeClass( 'hidden' ); | ||
262 | |||
263 | this.configContainer.setHtml( content ); | ||
264 | }; | ||
265 | |||
266 | /** | ||
267 | * Toggle empty groups and subgroups visibility. | ||
268 | * | ||
269 | * @private | ||
270 | */ | ||
271 | ToolbarModifier.prototype._toggleVisibilityEmptyElements = function() { | ||
272 | if ( this.modifyContainer.hasClass( 'empty-visible' ) ) { | ||
273 | this.modifyContainer.removeClass( 'empty-visible' ); | ||
274 | this.emptyVisible = false; | ||
275 | } else { | ||
276 | this.modifyContainer.addClass( 'empty-visible' ); | ||
277 | this.emptyVisible = true; | ||
278 | } | ||
279 | |||
280 | this._refreshMoveBtnsAvalibility(); | ||
281 | }; | ||
282 | |||
283 | /** | ||
284 | * Creates HTML main container of modifier. | ||
285 | * | ||
286 | * @returns {CKEDITOR.dom.element} | ||
287 | * @private | ||
288 | */ | ||
289 | ToolbarModifier.prototype._createModifier = function() { | ||
290 | var that = this; | ||
291 | |||
292 | AbstractToolbarModifier.prototype._createModifier.call( this ); | ||
293 | |||
294 | this.modifyContainer.setHtml( this._toolbarConfigToListString() ); | ||
295 | |||
296 | var groupLi = this.modifyContainer.find( 'li[data-type="group"]' ); | ||
297 | |||
298 | this.modifyContainer.on( 'mouseleave', function() { | ||
299 | this._dehighlightActiveToolGroup(); | ||
300 | }, this ); | ||
301 | |||
302 | var max = groupLi.count(); | ||
303 | for ( var i = 0; i < max; i += 1 ) { | ||
304 | groupLi.getItem( i ).on( 'mouseenter', onGroupHover ); | ||
305 | } | ||
306 | |||
307 | function onGroupHover() { | ||
308 | that._highlightGroup( this.data( 'name' ) ); | ||
309 | } | ||
310 | |||
311 | CKEDITOR.document.on( 'keypress', function( e ) { | ||
312 | var nativeEvent = e.data.$, | ||
313 | keyCode = nativeEvent.keyCode, | ||
314 | spaceOrEnter = ( keyCode === 32 || keyCode === 13 ), | ||
315 | active = new CKEDITOR.dom.element( CKEDITOR.document.$.activeElement ); | ||
316 | |||
317 | var mainContainer = active.getAscendant( function( node ) { | ||
318 | return node.$ === that.mainContainer.$; | ||
319 | } ); | ||
320 | |||
321 | if ( !mainContainer || !spaceOrEnter ) { | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | if ( active.data( 'type' ) === 'button' ) { | ||
326 | active.findOne( 'input' ).$.click(); | ||
327 | } | ||
328 | } ); | ||
329 | |||
330 | this.modifyContainer.on( 'click', function( e ) { | ||
331 | var origEvent = e.data.$, | ||
332 | target = new CKEDITOR.dom.element( ( origEvent.target || origEvent.srcElement ) ), | ||
333 | relativeGroupOrSeparatorLi = ToolbarModifier.getGroupOrSeparatorLiAncestor( target ); | ||
334 | |||
335 | if ( !relativeGroupOrSeparatorLi ) { | ||
336 | return; | ||
337 | } | ||
338 | |||
339 | that.cachedActiveElement = document.activeElement; | ||
340 | |||
341 | // checkbox clicked | ||
342 | if ( target.$ instanceof HTMLInputElement ) | ||
343 | that._handleCheckboxClicked( target ); | ||
344 | |||
345 | // link clicked | ||
346 | else if ( target.$ instanceof HTMLButtonElement ) { | ||
347 | if ( origEvent.preventDefault ) | ||
348 | origEvent.preventDefault(); | ||
349 | else | ||
350 | origEvent.returnValue = false; | ||
351 | |||
352 | var result = that._handleAnchorClicked( target.$ ); | ||
353 | |||
354 | if ( result && result.action == 'remove' ) | ||
355 | return; | ||
356 | |||
357 | } | ||
358 | |||
359 | var elementType = relativeGroupOrSeparatorLi.data( 'type' ), | ||
360 | elementName = relativeGroupOrSeparatorLi.data( 'name' ); | ||
361 | |||
362 | that._setActiveElement( elementType, elementName ); | ||
363 | |||
364 | if ( that.cachedActiveElement ) | ||
365 | that.cachedActiveElement.focus(); | ||
366 | } ); | ||
367 | |||
368 | if ( !this.toolbarContainer ) { | ||
369 | this._createToolbar(); | ||
370 | this.toolbarContainer.insertBefore( this.mainContainer.getChildren().getItem( 0 ) ); | ||
371 | } | ||
372 | |||
373 | this.showToolbarBtnsByGroupName( 'edit' ); | ||
374 | |||
375 | if ( !this.configContainer ) { | ||
376 | this.configContainer = new CKEDITOR.dom.element( 'div' ); | ||
377 | this.configContainer.addClass( 'configContainer' ); | ||
378 | this.configContainer.addClass( 'hidden' ); | ||
379 | |||
380 | this.mainContainer.append( this.configContainer ); | ||
381 | } | ||
382 | |||
383 | return this.mainContainer; | ||
384 | }; | ||
385 | |||
386 | /** | ||
387 | * Show toolbar buttons related to group name provided in argument | ||
388 | * and hide other buttons | ||
389 | * Please note: this method works on toolbar in tool, which is located | ||
390 | * on top of the tool | ||
391 | * | ||
392 | * @param {String} groupName | ||
393 | */ | ||
394 | ToolbarModifier.prototype.showToolbarBtnsByGroupName = function( groupName ) { | ||
395 | if ( !this.toolbarContainer ) { | ||
396 | return; | ||
397 | } | ||
398 | |||
399 | var allButtons = this.toolbarContainer.find( 'button' ); | ||
400 | |||
401 | var max = allButtons.count(); | ||
402 | for ( var i = 0; i < max; i += 1 ) { | ||
403 | var currentBtn = allButtons.getItem( i ); | ||
404 | |||
405 | if ( currentBtn.data( 'group' ) == groupName ) | ||
406 | currentBtn.removeClass( 'hidden' ); | ||
407 | else | ||
408 | currentBtn.addClass( 'hidden' ); | ||
409 | |||
410 | } | ||
411 | }; | ||
412 | |||
413 | /** | ||
414 | * Parse group "model" to configuration value | ||
415 | * | ||
416 | * @param {Object} group | ||
417 | * @returns {Object} | ||
418 | * @private | ||
419 | */ | ||
420 | ToolbarModifier.parseGroupToConfigValue = function( group ) { | ||
421 | if ( group.type == 'separator' ) { | ||
422 | return '/'; | ||
423 | } | ||
424 | |||
425 | var groups = group.groups, | ||
426 | max = groups.length; | ||
427 | |||
428 | delete group.totalBtns; | ||
429 | for ( var i = 0; i < max; i += 1 ) { | ||
430 | groups[ i ] = groups[ i ].name; | ||
431 | } | ||
432 | |||
433 | return group; | ||
434 | }; | ||
435 | |||
436 | /** | ||
437 | * Find closest Li ancestor in DOM tree which is group or separator element | ||
438 | * | ||
439 | * @param {CKEDITOR.dom.element} element | ||
440 | * @returns {CKEDITOR.dom.element} | ||
441 | */ | ||
442 | ToolbarModifier.getGroupOrSeparatorLiAncestor = function( element ) { | ||
443 | if ( element.$ instanceof HTMLLIElement && element.data( 'type' ) == 'group' ) | ||
444 | return element; | ||
445 | else { | ||
446 | return ToolbarModifier.getFirstAncestor( element, function( ancestor ) { | ||
447 | var type = ancestor.data( 'type' ); | ||
448 | |||
449 | return ( type == 'group' || type == 'separator' ); | ||
450 | } ); | ||
451 | } | ||
452 | }; | ||
453 | |||
454 | /** | ||
455 | * Set active element in tool by provided type and name. | ||
456 | * | ||
457 | * @param {String} type | ||
458 | * @param {String} name | ||
459 | */ | ||
460 | ToolbarModifier.prototype._setActiveElement = function( type, name ) { | ||
461 | // clear current active element | ||
462 | if ( this.currentActive ) | ||
463 | this.currentActive.elem.removeClass( 'active' ); | ||
464 | |||
465 | if ( type === null ) { | ||
466 | this._dehighlightActiveToolGroup(); | ||
467 | this.currentActive = null; | ||
468 | return; | ||
469 | } | ||
470 | |||
471 | var liElem = this.mainContainer.findOne( 'ul[data-type=table-body] li[data-type="' + type + '"][data-name="' + name + '"]' ); | ||
472 | |||
473 | liElem.addClass( 'active' ); | ||
474 | |||
475 | // setup model | ||
476 | this.currentActive = { | ||
477 | type: type, | ||
478 | name: name, | ||
479 | elem: liElem | ||
480 | }; | ||
481 | |||
482 | // highlight group in toolbar | ||
483 | if ( type == 'group' ) | ||
484 | this._highlightGroup( name ); | ||
485 | |||
486 | if ( type == 'separator' ) | ||
487 | this._dehighlightActiveToolGroup(); | ||
488 | }; | ||
489 | |||
490 | /** | ||
491 | * @returns {CKEDITOR.dom.element|null} | ||
492 | */ | ||
493 | ToolbarModifier.prototype.getActiveToolGroup = function() { | ||
494 | if ( this.editorInstance.container ) | ||
495 | return this.editorInstance.container.findOne( '.cke_toolgroup.active, .cke_toolbar.active' ); | ||
496 | else | ||
497 | return null; | ||
498 | }; | ||
499 | |||
500 | /** | ||
501 | * @private | ||
502 | */ | ||
503 | ToolbarModifier.prototype._dehighlightActiveToolGroup = function() { | ||
504 | var currentActive = this.getActiveToolGroup(); | ||
505 | |||
506 | if ( currentActive ) | ||
507 | currentActive.removeClass( 'active' ); | ||
508 | |||
509 | // @see ToolbarModifier.prototype._highlightGroup. | ||
510 | if ( this.editorInstance.container ) { | ||
511 | this.editorInstance.container.removeClass( 'some-toolbar-active' ); | ||
512 | } | ||
513 | }; | ||
514 | |||
515 | /** | ||
516 | * Highlight group by its name, and dehighlight current group. | ||
517 | * | ||
518 | * @param {String} name | ||
519 | */ | ||
520 | ToolbarModifier.prototype._highlightGroup = function( name ) { | ||
521 | if ( !this.editorInstance.container ) | ||
522 | return; | ||
523 | |||
524 | var foundBtnName = this.getFirstEnabledButtonInGroup( name ), | ||
525 | foundBtn = this.editorInstance.container.findOne( '.cke_button__' + foundBtnName + ', .cke_combo__' + foundBtnName ); | ||
526 | |||
527 | this._dehighlightActiveToolGroup(); | ||
528 | |||
529 | // Helpful to dim other toolbar groups if one is highlighted. | ||
530 | if ( this.editorInstance.container ) { | ||
531 | this.editorInstance.container.addClass( 'some-toolbar-active' ); | ||
532 | } | ||
533 | |||
534 | if ( foundBtn ) { | ||
535 | var btnToolbar = ToolbarModifier.getFirstAncestor( foundBtn, function( ancestor ) { | ||
536 | return ancestor.hasClass( 'cke_toolbar' ); | ||
537 | } ); | ||
538 | |||
539 | if ( btnToolbar ) | ||
540 | btnToolbar.addClass( 'active' ); | ||
541 | } | ||
542 | }; | ||
543 | |||
544 | /** | ||
545 | * @param {String} groupName | ||
546 | * @return {String|null} | ||
547 | */ | ||
548 | ToolbarModifier.prototype.getFirstEnabledButtonInGroup = function( groupName ) { | ||
549 | var groups = this.actualConfig.toolbarGroups, | ||
550 | groupIndex = this.getGroupIndex( groupName ), | ||
551 | group = groups[ groupIndex ]; | ||
552 | |||
553 | if ( groupIndex === -1 ) { | ||
554 | return null; | ||
555 | } | ||
556 | |||
557 | var max = group.groups ? group.groups.length : 0; | ||
558 | for ( var i = 0; i < max; i += 1 ) { | ||
559 | var currSubgroupName = group.groups[ i ].name, | ||
560 | firstEnabled = this.getFirstEnabledButtonInSubgroup( currSubgroupName ); | ||
561 | |||
562 | if ( firstEnabled ) | ||
563 | return firstEnabled; | ||
564 | } | ||
565 | return null; | ||
566 | }; | ||
567 | |||
568 | /** | ||
569 | * @param {String} subgroupName | ||
570 | * @returns {String|null} | ||
571 | */ | ||
572 | ToolbarModifier.prototype.getFirstEnabledButtonInSubgroup = function( subgroupName ) { | ||
573 | var subgroupBtns = this.fullToolbarEditor.buttonsByGroup[ subgroupName ]; | ||
574 | |||
575 | var max = subgroupBtns ? subgroupBtns.length : 0; | ||
576 | for ( var i = 0; i < max; i += 1 ) { | ||
577 | var currBtnName = subgroupBtns[ i ].name; | ||
578 | if ( !this.isButtonRemoved( currBtnName ) ) | ||
579 | return currBtnName; | ||
580 | } | ||
581 | |||
582 | return null; | ||
583 | }; | ||
584 | |||
585 | /** | ||
586 | * Sets up parameters and call adequate action. | ||
587 | * | ||
588 | * @param {CKEDITOR.dom.element} checkbox | ||
589 | * @private | ||
590 | */ | ||
591 | ToolbarModifier.prototype._handleCheckboxClicked = function( checkbox ) { | ||
592 | var closestLi = checkbox.getAscendant( 'li' ), | ||
593 | elementName = closestLi.data( 'name' ), | ||
594 | aboutToAddToRemoved = !checkbox.$.checked; | ||
595 | |||
596 | if ( aboutToAddToRemoved ) | ||
597 | this._addButtonToRemoved( elementName ); | ||
598 | else | ||
599 | this._removeButtonFromRemoved( elementName ); | ||
600 | }; | ||
601 | |||
602 | /** | ||
603 | * Sets up parameters and call adequate action. | ||
604 | * | ||
605 | * @param {HTMLAnchorElement} anchor | ||
606 | * @private | ||
607 | */ | ||
608 | ToolbarModifier.prototype._handleAnchorClicked = function( anchor ) { | ||
609 | var anchorDOM = new CKEDITOR.dom.element( anchor ), | ||
610 | relativeLi = anchorDOM.getAscendant( 'li' ), | ||
611 | relativeUl = relativeLi.getAscendant( 'ul' ), | ||
612 | elementType = relativeLi.data( 'type' ), | ||
613 | elementName = relativeLi.data( 'name' ), | ||
614 | direction = anchorDOM.data( 'direction' ), | ||
615 | nearestLi = ( direction === 'up' ? relativeLi.getPrevious() : relativeLi.getNext() ), | ||
616 | groupName, | ||
617 | subgroupName, | ||
618 | newIndex; | ||
619 | |||
620 | // nothing to do | ||
621 | if ( anchorDOM.hasClass( 'disabled' ) ) | ||
622 | return null; | ||
623 | |||
624 | // remove separator and nothing else | ||
625 | if ( anchorDOM.hasClass( 'remove' ) ) { | ||
626 | relativeLi.remove(); | ||
627 | this._removeSeparator( relativeLi.data( 'name' ) ); | ||
628 | this._setActiveElement( null ); | ||
629 | return { action: 'remove' }; | ||
630 | } | ||
631 | |||
632 | if ( !anchorDOM.hasClass( 'move' ) || !nearestLi ) | ||
633 | return { action: null }; | ||
634 | |||
635 | // move group or separator | ||
636 | if ( elementType === 'group' || elementType === 'separator' ) { | ||
637 | groupName = elementName; | ||
638 | newIndex = this._moveGroup( direction, groupName ); | ||
639 | } | ||
640 | |||
641 | // move subgroup | ||
642 | if ( elementType === 'subgroup' ) { | ||
643 | subgroupName = elementName; | ||
644 | groupName = relativeLi.getAscendant( 'li' ).data( 'name' ); | ||
645 | newIndex = this._moveSubgroup( direction, groupName, subgroupName ); | ||
646 | } | ||
647 | |||
648 | // Visual effect | ||
649 | if ( direction === 'up' ) | ||
650 | relativeLi.insertBefore( relativeUl.getChild( newIndex ) ); | ||
651 | |||
652 | if ( direction === 'down' ) | ||
653 | relativeLi.insertAfter( relativeUl.getChild( newIndex ) ); | ||
654 | |||
655 | // Should know whether there is next li element after modifications. | ||
656 | var nextLi = relativeLi; | ||
657 | |||
658 | // We are looking for next li element in list (to check whether current one is the last one) | ||
659 | var found; | ||
660 | while ( nextLi = ( direction === 'up' ? nextLi.getPrevious() : nextLi.getNext() ) ) { | ||
661 | if ( !this.emptyVisible && nextLi.hasClass( 'empty' ) ) { | ||
662 | continue; | ||
663 | } | ||
664 | |||
665 | found = nextLi; | ||
666 | break; | ||
667 | } | ||
668 | |||
669 | // If not found, it means that we reached end. | ||
670 | if ( !found ) { | ||
671 | var selector = ( '[data-direction="' + ( direction === 'up' ? 'down' : 'up' ) + '"]' ); | ||
672 | |||
673 | // Shifting direction. | ||
674 | this.cachedActiveElement = anchorDOM.getParent().findOne( selector ); | ||
675 | } | ||
676 | |||
677 | this._refreshMoveBtnsAvalibility(); | ||
678 | this._refreshBtnTabIndexes(); | ||
679 | |||
680 | return { | ||
681 | action: 'move' | ||
682 | }; | ||
683 | }; | ||
684 | |||
685 | /** | ||
686 | * First element can not be moved up, and last element can not be moved down, | ||
687 | * so they are disabled. | ||
688 | */ | ||
689 | ToolbarModifier.prototype._refreshMoveBtnsAvalibility = function() { | ||
690 | var that = this, | ||
691 | disabledBtns = this.mainContainer.find( 'ul[data-type=table-body] li > p > span > button.move.disabled' ); | ||
692 | |||
693 | // enabling all disabled buttons | ||
694 | var max = disabledBtns.count(); | ||
695 | for ( var i = 0; i < max; i += 1 ) { | ||
696 | var currentBtn = disabledBtns.getItem( i ); | ||
697 | currentBtn.removeClass( 'disabled' ); | ||
698 | } | ||
699 | |||
700 | |||
701 | function disableElementsInLists( ulList ) { | ||
702 | var max = ulList.count(); | ||
703 | for ( i = 0; i < max; i += 1 ) { | ||
704 | that._disableElementsInList( ulList.getItem( i ) ); | ||
705 | } | ||
706 | } | ||
707 | |||
708 | // Disable buttons in toolbars. | ||
709 | disableElementsInLists( this.mainContainer.find( 'ul[data-type=table-body]' ) ); | ||
710 | |||
711 | // Disable buttons in toolbar groups. | ||
712 | disableElementsInLists( this.mainContainer.find( 'ul[data-type=table-body] > li > ul' ) ); | ||
713 | }; | ||
714 | |||
715 | /** | ||
716 | * @private | ||
717 | */ | ||
718 | ToolbarModifier.prototype._refreshBtnTabIndexes = function() { | ||
719 | var tabindexed = this.mainContainer.find( '[data-tab="true"]' ); | ||
720 | |||
721 | var max = tabindexed.count(); | ||
722 | for ( var i = 0; i < max; i++ ) { | ||
723 | var item = tabindexed.getItem( i ), | ||
724 | disabled = item.hasClass( 'disabled' ); | ||
725 | |||
726 | item.setAttribute( 'tabindex', disabled ? -1 : i ); | ||
727 | } | ||
728 | }; | ||
729 | |||
730 | /** | ||
731 | * Disable buttons to move elements up and down which should be disabled. | ||
732 | * | ||
733 | * @param {CKEDITOR.dom.element} ul | ||
734 | * @private | ||
735 | */ | ||
736 | ToolbarModifier.prototype._disableElementsInList = function( ul ) { | ||
737 | var liList = ul.getChildren(); | ||
738 | |||
739 | if ( !liList.count() ) | ||
740 | return; | ||
741 | |||
742 | var firstDisabled, lastDisabled; | ||
743 | if ( this.emptyVisible ) { | ||
744 | firstDisabled = ul.getFirst(); | ||
745 | lastDisabled = ul.getLast(); | ||
746 | } else { | ||
747 | firstDisabled = ul.getFirst( isNotEmptyChecker ); | ||
748 | lastDisabled = ul.getLast( isNotEmptyChecker ); | ||
749 | } | ||
750 | |||
751 | function isNotEmptyChecker( element ) { | ||
752 | return !element.hasClass( 'empty' ); | ||
753 | } | ||
754 | |||
755 | if ( firstDisabled ) | ||
756 | var firstDisabledBtn = firstDisabled.findOne( 'p button[data-direction="up"]' ); | ||
757 | |||
758 | if ( lastDisabled ) | ||
759 | var lastDisabledBtn = lastDisabled.findOne( 'p button[data-direction="down"]' ); | ||
760 | |||
761 | if ( firstDisabledBtn ) { | ||
762 | firstDisabledBtn.addClass( 'disabled' ); | ||
763 | firstDisabledBtn.setAttribute( 'tabindex', '-1' ); | ||
764 | } | ||
765 | |||
766 | if ( lastDisabledBtn ) { | ||
767 | lastDisabledBtn.addClass( 'disabled' ); | ||
768 | lastDisabledBtn.setAttribute( 'tabindex', '-1' ); | ||
769 | } | ||
770 | }; | ||
771 | |||
772 | /** | ||
773 | * Gets group index in actual config toolbarGroups | ||
774 | * | ||
775 | * @param {String} name | ||
776 | * @returns {Number} | ||
777 | */ | ||
778 | ToolbarModifier.prototype.getGroupIndex = function( name ) { | ||
779 | var groups = this.actualConfig.toolbarGroups; | ||
780 | |||
781 | var max = groups.length; | ||
782 | for ( var i = 0; i < max; i += 1 ) { | ||
783 | if ( groups[ i ].name === name ) | ||
784 | return i; | ||
785 | } | ||
786 | |||
787 | return -1; | ||
788 | }; | ||
789 | |||
790 | /** | ||
791 | * Handle adding separator. | ||
792 | * | ||
793 | * @private | ||
794 | */ | ||
795 | ToolbarModifier.prototype._addSeparator = function() { | ||
796 | var separatorIndex = this._determineSeparatorToAddIndex(), | ||
797 | separator = ToolbarModifier.createSeparatorLiteral(), | ||
798 | domSeparator = CKEDITOR.dom.element.createFromHtml( ToolbarModifier.getToolbarSeparatorString( separator ) ); | ||
799 | |||
800 | this.actualConfig.toolbarGroups.splice( separatorIndex, 0, separator ); | ||
801 | |||
802 | domSeparator.insertBefore( this.modifyContainer.findOne( 'ul[data-type=table-body]' ).getChild( separatorIndex ) ); | ||
803 | |||
804 | this._setActiveElement( 'separator', separator.name ); | ||
805 | this._refreshMoveBtnsAvalibility(); | ||
806 | this._refreshBtnTabIndexes(); | ||
807 | this._refreshEditor(); | ||
808 | }; | ||
809 | |||
810 | /** | ||
811 | * Handle removing separator. | ||
812 | * | ||
813 | * @param {String} name | ||
814 | */ | ||
815 | ToolbarModifier.prototype._removeSeparator = function( name ) { | ||
816 | var separatorIndex = CKEDITOR.tools.indexOf( this.actualConfig.toolbarGroups, function( group ) { | ||
817 | return group.type == 'separator' && group.name == name; | ||
818 | } ); | ||
819 | |||
820 | this.actualConfig.toolbarGroups.splice( separatorIndex, 1 ); | ||
821 | |||
822 | this._refreshMoveBtnsAvalibility(); | ||
823 | this._refreshBtnTabIndexes(); | ||
824 | this._refreshEditor(); | ||
825 | }; | ||
826 | |||
827 | /** | ||
828 | * Determine index where separator should be added, based on currently selected element. | ||
829 | * | ||
830 | * @returns {Number} | ||
831 | * @private | ||
832 | */ | ||
833 | ToolbarModifier.prototype._determineSeparatorToAddIndex = function() { | ||
834 | if ( !this.currentActive ) | ||
835 | return 0; | ||
836 | |||
837 | var groupLi; | ||
838 | if ( this.currentActive.elem.data( 'type' ) == 'group' || this.currentActive.elem.data( 'type' ) == 'separator' ) | ||
839 | groupLi = this.currentActive.elem; | ||
840 | else | ||
841 | groupLi = this.currentActive.elem.getAscendant( 'li' ); | ||
842 | |||
843 | return groupLi.getIndex(); | ||
844 | }; | ||
845 | |||
846 | /** | ||
847 | * @param {Array} elementsArray | ||
848 | * @param {Number} elementIndex | ||
849 | * @param {String} direction | ||
850 | * @returns {Number} | ||
851 | * @private | ||
852 | */ | ||
853 | ToolbarModifier.prototype._moveElement = function( elementsArray, elementIndex, direction ) { | ||
854 | var nextIndex; | ||
855 | |||
856 | if ( this.emptyVisible ) | ||
857 | nextIndex = ( direction == 'down' ? elementIndex + 1 : elementIndex - 1 ); | ||
858 | else { | ||
859 | // When empty elements are not visible, there is need to skip them. | ||
860 | nextIndex = ToolbarModifier.getFirstElementIndexWith( elementsArray, elementIndex, direction, isEmptyOrSeparatorChecker ); | ||
861 | } | ||
862 | |||
863 | function isEmptyOrSeparatorChecker( element ) { | ||
864 | return element.totalBtns || element.type == 'separator'; | ||
865 | } | ||
866 | |||
867 | var offset = nextIndex - elementIndex; | ||
868 | |||
869 | return ToolbarModifier.moveTo( offset, elementsArray, elementIndex ); | ||
870 | }; | ||
871 | |||
872 | /** | ||
873 | * Moves group located in config level up or down and refresh editor. | ||
874 | * | ||
875 | * @param {String} direction | ||
876 | * @param {String} groupName | ||
877 | * @returns {Number} | ||
878 | */ | ||
879 | ToolbarModifier.prototype._moveGroup = function( direction, groupName ) { | ||
880 | var groupIndex = this.getGroupIndex( groupName ), | ||
881 | groups = this.actualConfig.toolbarGroups, | ||
882 | newIndex = this._moveElement( groups, groupIndex, direction ); | ||
883 | |||
884 | this._refreshMoveBtnsAvalibility(); | ||
885 | this._refreshBtnTabIndexes(); | ||
886 | this._refreshEditor(); | ||
887 | |||
888 | return newIndex; | ||
889 | }; | ||
890 | |||
891 | /** | ||
892 | * Moves subgroup located in config level up or down and refresh editor. | ||
893 | * | ||
894 | * @param {String} direction | ||
895 | * @param {String} groupName | ||
896 | * @param {String} subgroupName | ||
897 | * @private | ||
898 | */ | ||
899 | ToolbarModifier.prototype._moveSubgroup = function( direction, groupName, subgroupName ) { | ||
900 | var groupIndex = this.getGroupIndex( groupName ), | ||
901 | groups = this.actualConfig.toolbarGroups, | ||
902 | group = groups[ groupIndex ], | ||
903 | subgroupIndex = CKEDITOR.tools.indexOf( group.groups, function( subgroup ) { | ||
904 | return subgroup.name == subgroupName; | ||
905 | } ), | ||
906 | newIndex = this._moveElement( group.groups, subgroupIndex, direction ); | ||
907 | |||
908 | this._refreshEditor(); | ||
909 | |||
910 | return newIndex; | ||
911 | }; | ||
912 | |||
913 | /** | ||
914 | * Set `totalBtns` property in `actualConfig.toolbarGroups` elements. | ||
915 | * | ||
916 | * @private | ||
917 | */ | ||
918 | ToolbarModifier.prototype._calculateTotalBtns = function() { | ||
919 | var groups = this.actualConfig.toolbarGroups; | ||
920 | |||
921 | var i = groups.length; | ||
922 | // from the end | ||
923 | while ( i-- ) { | ||
924 | var currentGroup = groups[ i ], | ||
925 | totalBtns = ToolbarModifier.getTotalGroupButtonsNumber( currentGroup, this.fullToolbarEditor ); | ||
926 | |||
927 | if ( currentGroup.type == 'separator' ) { | ||
928 | // nothing to do with separator | ||
929 | continue; | ||
930 | } | ||
931 | |||
932 | currentGroup.totalBtns = totalBtns; | ||
933 | } | ||
934 | }; | ||
935 | |||
936 | /** | ||
937 | * Add button to removeButtons field in config and refresh editor. | ||
938 | * | ||
939 | * @param {String} buttonName | ||
940 | * @private | ||
941 | */ | ||
942 | ToolbarModifier.prototype._addButtonToRemoved = function( buttonName ) { | ||
943 | if ( CKEDITOR.tools.indexOf( this.removedButtons, buttonName ) != -1 ) | ||
944 | throw 'Button already added to removed'; | ||
945 | |||
946 | this.removedButtons.push( buttonName ); | ||
947 | this.actualConfig.removeButtons = this.removedButtons.join( ',' ); | ||
948 | this._refreshEditor(); | ||
949 | }; | ||
950 | |||
951 | /** | ||
952 | * Remove button from removeButtons field in config and refresh editor. | ||
953 | * | ||
954 | * @param {String} buttonName | ||
955 | * @private | ||
956 | */ | ||
957 | ToolbarModifier.prototype._removeButtonFromRemoved = function( buttonName ) { | ||
958 | var foundAtIndex = CKEDITOR.tools.indexOf( this.removedButtons, buttonName ); | ||
959 | |||
960 | if ( foundAtIndex === -1 ) | ||
961 | throw 'Trying to remove button from removed, but not found'; | ||
962 | |||
963 | this.removedButtons.splice( foundAtIndex, 1 ); | ||
964 | this.actualConfig.removeButtons = this.removedButtons.join( ',' ); | ||
965 | this._refreshEditor(); | ||
966 | }; | ||
967 | |||
968 | /** | ||
969 | * Parse group "model" to configuration value | ||
970 | * | ||
971 | * @param {Object} group | ||
972 | * @returns {Object} | ||
973 | * @static | ||
974 | */ | ||
975 | ToolbarModifier.parseGroupToConfigValue = function( group ) { | ||
976 | if ( group.type == 'separator' ) { | ||
977 | return '/'; | ||
978 | } | ||
979 | |||
980 | var groups = group.groups, | ||
981 | max = groups.length; | ||
982 | |||
983 | delete group.totalBtns; | ||
984 | for ( var i = 0; i < max; i += 1 ) { | ||
985 | groups[ i ] = groups[ i ].name; | ||
986 | } | ||
987 | |||
988 | return group; | ||
989 | }; | ||
990 | |||
991 | /** | ||
992 | * Find closest Li ancestor in DOM tree which is group or separator element | ||
993 | * | ||
994 | * @param {CKEDITOR.dom.element} element | ||
995 | * @returns {CKEDITOR.dom.element} | ||
996 | * @static | ||
997 | */ | ||
998 | ToolbarModifier.getGroupOrSeparatorLiAncestor = function( element ) { | ||
999 | if ( element.$ instanceof HTMLLIElement && element.data( 'type' ) == 'group' ) | ||
1000 | return element; | ||
1001 | else { | ||
1002 | return ToolbarModifier.getFirstAncestor( element, function( ancestor ) { | ||
1003 | var type = ancestor.data( 'type' ); | ||
1004 | |||
1005 | return ( type == 'group' || type == 'separator' ); | ||
1006 | } ); | ||
1007 | } | ||
1008 | }; | ||
1009 | |||
1010 | /** | ||
1011 | * Create separator literal with unique id. | ||
1012 | * | ||
1013 | * @public | ||
1014 | * @static | ||
1015 | * @return {Object} | ||
1016 | */ | ||
1017 | ToolbarModifier.createSeparatorLiteral = function() { | ||
1018 | return { | ||
1019 | type: 'separator', | ||
1020 | name: ( 'separator' + CKEDITOR.tools.getNextNumber() ) | ||
1021 | }; | ||
1022 | }; | ||
1023 | |||
1024 | /** | ||
1025 | * Creates HTML unordered list string based on toolbarGroups field in config. | ||
1026 | * | ||
1027 | * @returns {String} | ||
1028 | * @static | ||
1029 | */ | ||
1030 | ToolbarModifier.prototype._toolbarConfigToListString = function() { | ||
1031 | var groups = this.actualConfig.toolbarGroups || [], | ||
1032 | listString = '<ul data-type="table-body">'; | ||
1033 | |||
1034 | var max = groups.length; | ||
1035 | for ( var i = 0; i < max; i += 1 ) { | ||
1036 | var currentGroup = groups[ i ]; | ||
1037 | |||
1038 | if ( currentGroup.type === 'separator' ) | ||
1039 | listString += ToolbarModifier.getToolbarSeparatorString( currentGroup ); | ||
1040 | else | ||
1041 | listString += this._getToolbarGroupString( currentGroup ); | ||
1042 | } | ||
1043 | |||
1044 | listString += '</ul>'; | ||
1045 | |||
1046 | var headerString = ToolbarModifier.getToolbarHeaderString(); | ||
1047 | |||
1048 | return headerString + listString; | ||
1049 | }; | ||
1050 | |||
1051 | /** | ||
1052 | * Created HTML group list element based on group field in config. | ||
1053 | * | ||
1054 | * @param {Object} group | ||
1055 | * @returns {String} | ||
1056 | * @private | ||
1057 | */ | ||
1058 | ToolbarModifier.prototype._getToolbarGroupString = function( group ) { | ||
1059 | var subgroups = group.groups, | ||
1060 | groupString = ''; | ||
1061 | |||
1062 | groupString += [ | ||
1063 | '<li ', | ||
1064 | 'data-type="group" ', | ||
1065 | 'data-name="', group.name, '" ', | ||
1066 | ( group.totalBtns ? '' : 'class="empty"' ), | ||
1067 | '>' | ||
1068 | ].join( '' ); | ||
1069 | groupString += ToolbarModifier.getToolbarElementPreString( group ) + '<ul>'; | ||
1070 | |||
1071 | var max = subgroups.length; | ||
1072 | |||
1073 | for ( var i = 0; i < max; i += 1 ) { | ||
1074 | var currentSubgroup = subgroups[ i ], | ||
1075 | subgroupBtns = this.fullToolbarEditor.buttonsByGroup[ currentSubgroup.name ]; | ||
1076 | |||
1077 | groupString += this._getToolbarSubgroupString( currentSubgroup, subgroupBtns ); | ||
1078 | } | ||
1079 | groupString += '</ul></li>'; | ||
1080 | |||
1081 | return groupString; | ||
1082 | }; | ||
1083 | |||
1084 | /** | ||
1085 | * @param {Object} separator | ||
1086 | * @returns {String} | ||
1087 | * @static | ||
1088 | */ | ||
1089 | ToolbarModifier.getToolbarSeparatorString = function( separator ) { | ||
1090 | return [ | ||
1091 | '<li ', | ||
1092 | 'data-type="', separator.type , '" ', | ||
1093 | 'data-name="', separator.name , '"', | ||
1094 | '>', | ||
1095 | ToolbarModifier.getToolbarElementPreString( 'row separator' ), | ||
1096 | '</li>' | ||
1097 | ].join( '' ); | ||
1098 | }; | ||
1099 | |||
1100 | /** | ||
1101 | * @returns {string} | ||
1102 | */ | ||
1103 | ToolbarModifier.getToolbarHeaderString = function() { | ||
1104 | return '<ul data-type="table-header">' + | ||
1105 | '<li data-type="header">' + | ||
1106 | '<p>Toolbars</p>' + | ||
1107 | '<ul>' + | ||
1108 | '<li>' + | ||
1109 | '<p>Toolbar groups</p>' + | ||
1110 | '<p>Toolbar group items</p>' + | ||
1111 | '</li>' + | ||
1112 | '</ul>' + | ||
1113 | '</li>' + | ||
1114 | '</ul>'; | ||
1115 | }; | ||
1116 | |||
1117 | /** | ||
1118 | * Find and return first ancestor of element provided in first argument | ||
1119 | * which match the criteria checked in function provided in second argument. | ||
1120 | * | ||
1121 | * @param {CKEDITOR.dom.element} element | ||
1122 | * @param {Function} checker | ||
1123 | * @returns {CKEDITOR.dom.element|null} | ||
1124 | */ | ||
1125 | ToolbarModifier.getFirstAncestor = function( element, checker ) { | ||
1126 | var ancestors = element.getParents(), | ||
1127 | i = ancestors.length; | ||
1128 | |||
1129 | while ( i-- ) { | ||
1130 | if ( checker( ancestors[ i ] ) ) | ||
1131 | return ancestors[ i ]; | ||
1132 | } | ||
1133 | |||
1134 | return null; | ||
1135 | }; | ||
1136 | |||
1137 | /** | ||
1138 | * Looking through array elements start from index provided in second argument | ||
1139 | * and go 'up' or 'down' in array | ||
1140 | * last argument is condition checker which should return Boolean value | ||
1141 | * | ||
1142 | * User cases: | ||
1143 | * | ||
1144 | * ToolbarModifier.getFirstElementIndexWith( [3, 4, 8, 1, 4], 2, 'down', function( elem ) { return elem == 4; } ); // 4 | ||
1145 | * ToolbarModifier.getFirstElementIndexWith( [3, 4, 8, 1, 4], 2, 'up', function( elem ) { return elem == 4; } ); // 1 | ||
1146 | * | ||
1147 | * @param {Array} array | ||
1148 | * @param {Number} i | ||
1149 | * @param {String} direction 'up' or 'down' | ||
1150 | * @param {Function} conditionChecker | ||
1151 | * @static | ||
1152 | * @returns {Number} index of found element | ||
1153 | */ | ||
1154 | ToolbarModifier.getFirstElementIndexWith = function( array, i, direction, conditionChecker ) { | ||
1155 | function whileChecker() { | ||
1156 | var result; | ||
1157 | if ( direction === 'up' ) | ||
1158 | result = i--; | ||
1159 | else | ||
1160 | result = ( ++i < array.length ); | ||
1161 | |||
1162 | return result; | ||
1163 | } | ||
1164 | |||
1165 | while ( whileChecker() ) { | ||
1166 | if ( conditionChecker( array[ i ] ) ) | ||
1167 | return i; | ||
1168 | |||
1169 | } | ||
1170 | |||
1171 | return -1; | ||
1172 | }; | ||
1173 | |||
1174 | /** | ||
1175 | * Moves array element at index level up or down. | ||
1176 | * | ||
1177 | * @static | ||
1178 | * @param {String} direction | ||
1179 | * @param {Array} array | ||
1180 | * @param {Number} index | ||
1181 | * @returns {Number} | ||
1182 | */ | ||
1183 | ToolbarModifier.moveTo = function( offset, array, index ) { | ||
1184 | var element, newIndex; | ||
1185 | |||
1186 | if ( index !== -1 ) | ||
1187 | element = array.splice( index, 1 )[ 0 ]; | ||
1188 | |||
1189 | newIndex = index + offset; | ||
1190 | |||
1191 | array.splice( newIndex, 0, element ); | ||
1192 | |||
1193 | return newIndex; | ||
1194 | }; | ||
1195 | |||
1196 | /** | ||
1197 | * @static | ||
1198 | * @param {Object} subgroup | ||
1199 | * @returns {Number} | ||
1200 | */ | ||
1201 | ToolbarModifier.getTotalSubGroupButtonsNumber = function( subgroup, fullToolbarEditor ) { | ||
1202 | var subgroupName = ( typeof subgroup == 'string' ? subgroup : subgroup.name ), | ||
1203 | subgroupBtns = fullToolbarEditor.buttonsByGroup[ subgroupName ]; | ||
1204 | |||
1205 | return ( subgroupBtns ? subgroupBtns.length : 0 ); | ||
1206 | }; | ||
1207 | |||
1208 | /** | ||
1209 | * Returns all buttons number in group which are nested in subgroups also. | ||
1210 | * | ||
1211 | * @param {Object} group | ||
1212 | * @param {ToolbarModifier.FullToolbarEditor} | ||
1213 | * @static | ||
1214 | * @returns {Number} | ||
1215 | */ | ||
1216 | ToolbarModifier.getTotalGroupButtonsNumber = function( group, fullToolbarEditor ) { | ||
1217 | var total = 0, | ||
1218 | subgroups = group.groups; | ||
1219 | |||
1220 | var max = subgroups ? subgroups.length : 0; | ||
1221 | for ( var i = 0; i < max; i += 1 ) | ||
1222 | total += ToolbarModifier.getTotalSubGroupButtonsNumber( subgroups[ i ], fullToolbarEditor ); | ||
1223 | |||
1224 | return total; | ||
1225 | }; | ||
1226 | |||
1227 | /** | ||
1228 | * Creates HTML subgroup list element based on subgroup field in config. | ||
1229 | * | ||
1230 | * @param {Object} subgroup | ||
1231 | * @param {Array} groupBtns | ||
1232 | * @returns {String} | ||
1233 | * @private | ||
1234 | */ | ||
1235 | ToolbarModifier.prototype._getToolbarSubgroupString = function( subgroup, groupBtns ) { | ||
1236 | var subgroupString = ''; | ||
1237 | |||
1238 | subgroupString += [ | ||
1239 | '<li ', | ||
1240 | 'data-type="subgroup" ', | ||
1241 | 'data-name="', subgroup.name, '" ', | ||
1242 | ( subgroup.totalBtns ? '' : 'class="empty" ' ), | ||
1243 | '>' | ||
1244 | ].join( '' ); | ||
1245 | subgroupString += ToolbarModifier.getToolbarElementPreString( subgroup.name ); | ||
1246 | subgroupString += '<ul>'; | ||
1247 | |||
1248 | var max = groupBtns ? groupBtns.length : 0; | ||
1249 | for ( var i = 0; i < max; i += 1 ) | ||
1250 | subgroupString += this.getButtonString( groupBtns[ i ] ); | ||
1251 | |||
1252 | subgroupString += '</ul>'; | ||
1253 | |||
1254 | subgroupString += '</li>'; | ||
1255 | |||
1256 | return subgroupString; | ||
1257 | }; | ||
1258 | |||
1259 | /** | ||
1260 | * @param {String} buttonName | ||
1261 | * @returns {String|null} | ||
1262 | * @private | ||
1263 | */ | ||
1264 | ToolbarModifier.prototype._getConfigButtonName = function( buttonName ) { | ||
1265 | var items = this.fullToolbarEditor.editorInstance.ui.items; | ||
1266 | |||
1267 | var name; | ||
1268 | for ( name in items ) { | ||
1269 | if ( items[ name ].name == buttonName ) | ||
1270 | return name; | ||
1271 | } | ||
1272 | |||
1273 | return null; | ||
1274 | }; | ||
1275 | |||
1276 | /** | ||
1277 | * @param {String} buttonName | ||
1278 | * @returns {Boolean} | ||
1279 | */ | ||
1280 | ToolbarModifier.prototype.isButtonRemoved = function( buttonName ) { | ||
1281 | return CKEDITOR.tools.indexOf( this.removedButtons, this._getConfigButtonName( buttonName ) ) != -1; | ||
1282 | }; | ||
1283 | |||
1284 | /** | ||
1285 | * @param {CKEDITOR.ui.button/CKEDITOR.ui.richCombo} button | ||
1286 | * @returns {String} | ||
1287 | * @public | ||
1288 | */ | ||
1289 | ToolbarModifier.prototype.getButtonString = function( button ) { | ||
1290 | var checked = ( this.isButtonRemoved( button.name ) ? '' : 'checked="checked"' ); | ||
1291 | |||
1292 | return [ | ||
1293 | '<li data-tab="true" data-type="button" data-name="', this._getConfigButtonName( button.name ), '">', | ||
1294 | '<label title="', button.label, '" >', | ||
1295 | '<input ', | ||
1296 | 'tabindex="-1"', | ||
1297 | 'type="checkbox"', | ||
1298 | checked, | ||
1299 | '/>', | ||
1300 | button.$.getOuterHtml(), | ||
1301 | '</label>', | ||
1302 | '</li>' | ||
1303 | ].join( '' ); | ||
1304 | }; | ||
1305 | |||
1306 | /** | ||
1307 | * Creates group header string. | ||
1308 | * | ||
1309 | * @param {Object|String} group | ||
1310 | * @returns {String} | ||
1311 | * @static | ||
1312 | */ | ||
1313 | ToolbarModifier.getToolbarElementPreString = function( group ) { | ||
1314 | var name = ( group.name ? group.name : group ); | ||
1315 | |||
1316 | return [ | ||
1317 | '<p>', | ||
1318 | '<span>', | ||
1319 | '<button title="Move element upward" data-tab="true" data-direction="up" class="move icon-up-big"></button>', | ||
1320 | '<button title="Move element downward" data-tab="true" data-direction="down" class="move icon-down-big"></button>', | ||
1321 | ( name == 'row separator' ? '<button title="Remove element" data-tab="true" class="remove icon-trash"></button>' : '' ), | ||
1322 | name, | ||
1323 | '</span>', | ||
1324 | '</p>' | ||
1325 | ].join( '' ); | ||
1326 | }; | ||
1327 | |||
1328 | /** | ||
1329 | * @static | ||
1330 | * @param {String} cfg | ||
1331 | * @returns {String} | ||
1332 | */ | ||
1333 | ToolbarModifier.evaluateToolbarGroupsConfig = function( cfg ) { | ||
1334 | cfg = ( function( cfg ) { | ||
1335 | var config = {}, result; | ||
1336 | |||
1337 | /*jshint -W002 */ | ||
1338 | try { | ||
1339 | result = eval( '(' + cfg + ')' ); | ||
1340 | } catch ( e ) { | ||
1341 | try { | ||
1342 | result = eval( cfg ); | ||
1343 | } catch ( e ) { | ||
1344 | return null; | ||
1345 | } | ||
1346 | } | ||
1347 | /*jshint +W002 */ | ||
1348 | |||
1349 | if ( config.toolbarGroups && typeof config.toolbarGroups.length === 'number' ) { | ||
1350 | return JSON.stringify( config ); | ||
1351 | } else if ( result && typeof result.length === 'number' ) { | ||
1352 | return JSON.stringify( { toolbarGroups: result } ); | ||
1353 | } else if ( result && result.toolbarGroups ) { | ||
1354 | return JSON.stringify( result ); | ||
1355 | } else { | ||
1356 | return null; | ||
1357 | } | ||
1358 | |||
1359 | }( cfg ) ); | ||
1360 | |||
1361 | return cfg; | ||
1362 | }; | ||
1363 | |||
1364 | return ToolbarModifier; | ||
1365 | } )(); | ||
1366 | |||