]>
Commit | Line | Data |
---|---|---|
3332bebe IB |
1 | /* global CodeMirror, ToolbarConfigurator */ |
2 | ||
3 | 'use strict'; | |
4 | ||
5 | ( function() { | |
6 | var AbstractToolbarModifier = ToolbarConfigurator.AbstractToolbarModifier, | |
7 | FullToolbarEditor = ToolbarConfigurator.FullToolbarEditor; | |
8 | ||
9 | /** | |
10 | * @class ToolbarConfigurator.ToolbarTextModifier | |
11 | * @param {String} editorId An id of modified editor | |
12 | * @extends AbstractToolbarModifier | |
13 | * @constructor | |
14 | */ | |
15 | function ToolbarTextModifier( editorId ) { | |
16 | AbstractToolbarModifier.call( this, editorId ); | |
17 | ||
18 | this.codeContainer = null; | |
19 | this.hintContainer = null; | |
20 | } | |
21 | ||
22 | // Expose the class. | |
23 | ToolbarConfigurator.ToolbarTextModifier = ToolbarTextModifier; | |
24 | ||
25 | ToolbarTextModifier.prototype = Object.create( AbstractToolbarModifier.prototype ); | |
26 | ||
27 | /** | |
28 | * @param {Function} callback | |
29 | * @param {String} [config] | |
30 | * @private | |
31 | */ | |
32 | ToolbarTextModifier.prototype._onInit = function( callback, config ) { | |
33 | AbstractToolbarModifier.prototype._onInit.call( this, undefined, config ); | |
34 | ||
35 | this._createModifier( config ? this.actualConfig : undefined ); | |
36 | ||
37 | if ( typeof callback === 'function' ) | |
38 | callback( this.mainContainer ); | |
39 | }; | |
40 | ||
41 | /** | |
42 | * Creates HTML main container of modifier. | |
43 | * | |
44 | * @param {String} cfg | |
45 | * @returns {CKEDITOR.dom.element} | |
46 | * @private | |
47 | */ | |
48 | ToolbarTextModifier.prototype._createModifier = function( cfg ) { | |
49 | var that = this; | |
50 | ||
51 | this._createToolbar(); | |
52 | ||
53 | if ( this.toolbarContainer ) { | |
54 | this.mainContainer.append( this.toolbarContainer ); | |
55 | } | |
56 | ||
57 | AbstractToolbarModifier.prototype._createModifier.call( this ); | |
58 | ||
59 | this._setupActualConfig( cfg ); | |
60 | ||
61 | var toolbarCfg = this.actualConfig.toolbar, | |
62 | cfgValue; | |
63 | ||
64 | if ( CKEDITOR.tools.isArray( toolbarCfg ) ) { | |
65 | var stringifiedToolbar = '[\n\t\t' + FullToolbarEditor.map( toolbarCfg, function( json ) { | |
66 | return AbstractToolbarModifier.stringifyJSONintoOneLine( json, { | |
67 | addSpaces: true, | |
68 | noQuotesOnKey: true, | |
69 | singleQuotes: true | |
70 | } ); | |
71 | } ).join( ',\n\t\t' ) + '\n\t]'; | |
72 | ||
73 | cfgValue = '\tconfig.toolbar = ' + stringifiedToolbar + ';'; | |
74 | } else { | |
75 | cfgValue = 'config.toolbar = [];'; | |
76 | } | |
77 | ||
78 | cfgValue = [ | |
79 | 'CKEDITOR.editorConfig = function( config ) {\n', | |
80 | cfgValue, | |
81 | '\n};' | |
82 | ].join( '' ); | |
83 | ||
84 | function hint( cm ) { | |
85 | var data = setupData( cm ); | |
86 | ||
87 | if ( data.charsBetween === null ) { | |
88 | return; | |
89 | } | |
90 | ||
91 | var unused = that.getUnusedButtonsArray( that.actualConfig.toolbar, true, data.charsBetween ), | |
92 | to = cm.getCursor(), | |
93 | from = CodeMirror.Pos( to.line, ( to.ch - ( data.charsBetween.length ) ) ), | |
94 | token = cm.getTokenAt( to ), | |
95 | prevToken = cm.getTokenAt( { line: to.line, ch: token.start } ); | |
96 | ||
97 | // determine that we are at beginning of group, | |
98 | // so first key is "name" | |
99 | if ( prevToken.string === '{' ) | |
100 | unused = [ 'name' ]; | |
101 | ||
102 | // preventing close with special character and move cursor forward | |
103 | // when no autocomplete | |
104 | if ( unused.length === 0 ) | |
105 | return; | |
106 | ||
107 | return new HintData( from, to, unused ); | |
108 | } | |
109 | ||
110 | function HintData( from, to, list ) { | |
111 | this.from = from; | |
112 | this.to = to; | |
113 | this.list = list; | |
114 | this._handlers = []; | |
115 | } | |
116 | ||
117 | function setupData( cm, character ) { | |
118 | var result = {}; | |
119 | ||
120 | result.cur = cm.getCursor(); | |
121 | result.tok = cm.getTokenAt( result.cur ); | |
122 | ||
123 | result[ 'char' ] = character || result.tok.string.charAt( result.tok.string.length - 1 ); | |
124 | ||
125 | // Getting string between begin of line and cursor. | |
126 | var curLineTillCur = cm.getRange( CodeMirror.Pos( result.cur.line, 0 ), result.cur ); | |
127 | ||
128 | // Reverse string. | |
129 | var currLineTillCurReversed = curLineTillCur.split( '' ).reverse().join( '' ); | |
130 | ||
131 | // Removing proper string definitions : | |
132 | // FROM: | |
133 | // R' ,'odeR' ,'odnU' [ :smeti{ | |
134 | // ^^^^^^ ^^^^^^ | |
135 | // TO: | |
136 | // R' , [ :smeti{ | |
137 | currLineTillCurReversed = currLineTillCurReversed.replace( /(['|"]\w*['|"])/g, '' ); | |
138 | ||
139 | // Matching letters till ' or " character and end string char. | |
140 | // R' , [ :smeti{ | |
141 | // ^ | |
142 | result.charsBetween = currLineTillCurReversed.match( /(^\w*)(['|"])/ ); | |
143 | ||
144 | if ( result.charsBetween ) { | |
145 | result.endChar = result.charsBetween[ 2 ]; | |
146 | ||
147 | // And reverse string (bring to original state). | |
148 | result.charsBetween = result.charsBetween[ 1 ].split( '' ).reverse().join( '' ); | |
149 | } | |
150 | ||
151 | return result; | |
152 | } | |
153 | ||
154 | function complete( cm ) { | |
155 | setTimeout( function() { | |
156 | if ( !cm.state.completionActive ) { | |
157 | CodeMirror.showHint( cm, hint, { | |
158 | hintsClass: 'toolbar-modifier', | |
159 | completeSingle: false | |
160 | } ); | |
161 | } | |
162 | }, 100 ); | |
163 | ||
164 | return CodeMirror.Pass; | |
165 | } | |
166 | ||
167 | var codeMirrorWrapper = new CKEDITOR.dom.element( 'div' ); | |
168 | codeMirrorWrapper.addClass( 'codemirror-wrapper' ); | |
169 | this.modifyContainer.append( codeMirrorWrapper ); | |
170 | this.codeContainer = CodeMirror( codeMirrorWrapper.$, { | |
171 | mode: { name: 'javascript', json: true }, | |
172 | // For some reason (most likely CM's bug) gutter breaks CM's height. | |
173 | // Refreshing CM does not help. | |
174 | lineNumbers: false, | |
175 | lineWrapping: true, | |
176 | // Trick to make CM autogrow. http://codemirror.net/demo/resize.html | |
177 | viewportMargin: Infinity, | |
178 | value: cfgValue, | |
179 | smartIndent: false, | |
180 | indentWithTabs: true, | |
181 | indentUnit: 4, | |
182 | tabSize: 4, | |
183 | theme: 'neo', | |
184 | extraKeys: { | |
185 | 'Left': complete, | |
186 | 'Right': complete, | |
187 | "'''": complete, | |
188 | "'\"'": complete, | |
189 | Backspace: complete, | |
190 | Delete: complete, | |
191 | 'Shift-Tab': 'indentLess' | |
192 | } | |
193 | } ); | |
194 | ||
195 | this.codeContainer.on( 'endCompletion', function( cm, completionData ) { | |
196 | var data = setupData( cm ); | |
197 | ||
198 | // preventing close with special character and move cursor forward | |
199 | // when no autocomplete | |
200 | if ( completionData === undefined ) | |
201 | return; | |
202 | ||
203 | cm.replaceSelection( data.endChar ); | |
204 | } ); | |
205 | ||
206 | this.codeContainer.on( 'change', function() { | |
207 | var value = that.codeContainer.getValue(); | |
208 | ||
209 | value = that._evaluateValue( value ); | |
210 | ||
211 | if ( value !== null ) { | |
212 | that.actualConfig.toolbar = ( value.toolbar ? value.toolbar : that.actualConfig.toolbar ); | |
213 | ||
214 | that._fillHintByUnusedElements(); | |
215 | that._refreshEditor(); | |
216 | ||
217 | that.mainContainer.removeClass( 'invalid' ); | |
218 | } else { | |
219 | that.mainContainer.addClass( 'invalid' ); | |
220 | } | |
221 | } ); | |
222 | ||
223 | this.hintContainer = new CKEDITOR.dom.element( 'div' ); | |
224 | this.hintContainer.addClass( 'toolbarModifier-hints' ); | |
225 | ||
226 | this._fillHintByUnusedElements(); | |
227 | this.hintContainer.insertBefore( codeMirrorWrapper ); | |
228 | }; | |
229 | ||
230 | /** | |
231 | * Create DOM string and set to hint container, | |
232 | * show proper information when no unused element left. | |
233 | * | |
234 | * @private | |
235 | */ | |
236 | ToolbarTextModifier.prototype._fillHintByUnusedElements = function() { | |
237 | var unused = this.getUnusedButtonsArray( this.actualConfig.toolbar, true ); | |
238 | unused = this.groupButtonNamesByGroup( unused ); | |
239 | ||
240 | var unusedElements = FullToolbarEditor.map( unused, function( elem ) { | |
241 | var buttonsList = FullToolbarEditor.map( elem.buttons, function( buttonName ) { | |
242 | return '<code>' + buttonName + '</code> '; | |
243 | } ).join( '' ); | |
244 | ||
245 | return [ | |
246 | '<dt>', | |
247 | '<code>', elem.name, '</code>', | |
248 | '</dt>', | |
249 | '<dd>', | |
250 | buttonsList, | |
251 | '</dd>' | |
252 | ].join( '' ); | |
253 | } ).join( ' ' ); | |
254 | ||
255 | var listHeader = [ | |
256 | '<dt class="list-header">Toolbar group</dt>', | |
257 | '<dd class="list-header">Unused items</dd>' | |
258 | ].join( '' ); | |
259 | ||
260 | var header = '<h3>Unused toolbar items</h3>'; | |
261 | ||
262 | if ( !unused.length ) { | |
263 | listHeader = '<p>All items are in use.</p>'; | |
264 | } | |
265 | ||
266 | this.codeContainer.refresh(); | |
267 | ||
268 | this.hintContainer.setHtml( header + '<dl>' + listHeader + unusedElements + '</dl>' ); | |
269 | }; | |
270 | ||
271 | /** | |
272 | * @param {String} buttonName | |
273 | * @returns {String} | |
274 | */ | |
275 | ToolbarTextModifier.prototype.getToolbarGroupByButtonName = function( buttonName ) { | |
276 | var buttonNames = this.fullToolbarEditor.buttonNamesByGroup; | |
277 | ||
278 | for ( var groupName in buttonNames ) { | |
279 | var buttons = buttonNames[ groupName ]; | |
280 | ||
281 | var i = buttons.length; | |
282 | while ( i-- ) { | |
283 | if ( buttonName === buttons[ i ] ) { | |
284 | return groupName; | |
285 | } | |
286 | } | |
287 | ||
288 | } | |
289 | ||
290 | return null; | |
291 | }; | |
292 | ||
293 | /** | |
294 | * Filter all available toolbar elements by array of elements provided in first argument. | |
295 | * Returns elements which are not used. | |
296 | * | |
297 | * @param {Object} toolbar | |
298 | * @param {Boolean} [sorted=false] | |
299 | * @param {String} prefix | |
300 | * @returns {Array} | |
301 | */ | |
302 | ToolbarTextModifier.prototype.getUnusedButtonsArray = function( toolbar, sorted, prefix ) { | |
303 | sorted = ( sorted === true ? true : false ); | |
304 | var providedElements = ToolbarTextModifier.mapToolbarCfgToElementsList( toolbar ), | |
305 | allElements = Object.keys( this.fullToolbarEditor.editorInstance.ui.items ); | |
306 | ||
307 | // get rid of "-" elements | |
308 | allElements = FullToolbarEditor.filter( allElements, function( elem ) { | |
309 | var isSeparator = ( elem === '-' ), | |
310 | matchPrefix = ( prefix === undefined || elem.toLowerCase().indexOf( prefix.toLowerCase() ) === 0 ); | |
311 | ||
312 | return !isSeparator && matchPrefix; | |
313 | } ); | |
314 | ||
315 | var elementsNotUsed = FullToolbarEditor.filter( allElements, function( elem ) { | |
316 | return CKEDITOR.tools.indexOf( providedElements, elem ) == -1; | |
317 | } ); | |
318 | ||
319 | if ( sorted ) | |
320 | elementsNotUsed.sort(); | |
321 | ||
322 | return elementsNotUsed; | |
323 | }; | |
324 | ||
325 | /** | |
326 | * | |
327 | * @param {Array} buttons | |
328 | * @returns {Array} | |
329 | */ | |
330 | ToolbarTextModifier.prototype.groupButtonNamesByGroup = function( buttons ) { | |
331 | var result = [], | |
332 | groupedBtns = JSON.parse( JSON.stringify( this.fullToolbarEditor.buttonNamesByGroup ) ); | |
333 | ||
334 | for ( var groupName in groupedBtns ) { | |
335 | var currGroup = groupedBtns[ groupName ]; | |
336 | currGroup = FullToolbarEditor.filter( currGroup, function( btnName ) { | |
337 | return CKEDITOR.tools.indexOf( buttons, btnName ) !== -1; | |
338 | } ); | |
339 | ||
340 | if ( currGroup.length ) { | |
341 | result.push( { | |
342 | name: groupName, | |
343 | buttons: currGroup | |
344 | } ); | |
345 | } | |
346 | ||
347 | } | |
348 | ||
349 | return result; | |
350 | }; | |
351 | ||
352 | /** | |
353 | * Map toolbar config value to flat items list. | |
354 | * | |
355 | * input: | |
356 | * [ | |
357 | * { name: "basicstyles", items: ["Bold", "Italic"] }, | |
358 | * { name: "advancedstyles", items: ["Bold", "Outdent", "Indent"] } | |
359 | * ] | |
360 | * | |
361 | * output: | |
362 | * ["Bold", "Italic", "Outdent", "Indent"] | |
363 | * | |
364 | * @param {Object} toolbar | |
365 | * @returns {Array} | |
366 | */ | |
367 | ToolbarTextModifier.mapToolbarCfgToElementsList = function( toolbar ) { | |
368 | var elements = []; | |
369 | ||
370 | var max = toolbar.length; | |
371 | for ( var i = 0; i < max; i += 1 ) { | |
372 | if ( !toolbar[ i ] || typeof toolbar[ i ] === 'string' ) | |
373 | continue; | |
374 | ||
375 | elements = elements.concat( FullToolbarEditor.filter( toolbar[ i ].items, checker ) ); | |
376 | } | |
377 | ||
378 | function checker( elem ) { | |
379 | return elem !== '-'; | |
380 | } | |
381 | ||
382 | return elements; | |
383 | }; | |
384 | ||
385 | /** | |
386 | * @param {String} cfg | |
387 | * @private | |
388 | */ | |
389 | ToolbarTextModifier.prototype._setupActualConfig = function( cfg ) { | |
390 | cfg = cfg || this.editorInstance.config; | |
391 | ||
392 | // if toolbar already exists in config, there is nothing to do | |
393 | if ( CKEDITOR.tools.isArray( cfg.toolbar ) ) | |
394 | return; | |
395 | ||
396 | // if toolbar group not present, we need to pick them from full toolbar instance | |
397 | if ( !cfg.toolbarGroups ) | |
398 | cfg.toolbarGroups = this.fullToolbarEditor.getFullToolbarGroupsConfig( true ); | |
399 | ||
400 | this._fixGroups( cfg ); | |
401 | ||
402 | cfg.toolbar = this._mapToolbarGroupsToToolbar( cfg.toolbarGroups, this.actualConfig.removeButtons ); | |
403 | ||
404 | this.actualConfig.toolbar = cfg.toolbar; | |
405 | this.actualConfig.removeButtons = ''; | |
406 | }; | |
407 | ||
408 | /** | |
409 | * **Please note:** This method modify element provided in first argument. | |
410 | * | |
411 | * @param {Array} toolbarGroups | |
412 | * @returns {Array} | |
413 | * @private | |
414 | */ | |
415 | ToolbarTextModifier.prototype._mapToolbarGroupsToToolbar = function( toolbarGroups, removedBtns ) { | |
416 | removedBtns = removedBtns || this.editorInstance.config.removedBtns; | |
417 | removedBtns = typeof removedBtns == 'string' ? removedBtns.split( ',' ) : []; | |
418 | ||
419 | // from the end, because array indexes may change | |
420 | var i = toolbarGroups.length; | |
421 | while ( i-- ) { | |
422 | var mappedSubgroup = this._mapToolbarSubgroup( toolbarGroups[ i ], removedBtns ); | |
423 | ||
424 | if ( toolbarGroups[ i ].type === 'separator' ) { | |
425 | toolbarGroups[ i ] = '/'; | |
426 | continue; | |
427 | } | |
428 | ||
429 | // don't want empty groups | |
430 | if ( CKEDITOR.tools.isArray( mappedSubgroup ) && mappedSubgroup.length === 0 ) { | |
431 | toolbarGroups.splice( i, 1 ); | |
432 | continue; | |
433 | } | |
434 | ||
435 | if ( typeof mappedSubgroup == 'string' ) | |
436 | toolbarGroups[ i ] = mappedSubgroup; | |
437 | else { | |
438 | toolbarGroups[ i ] = { | |
439 | name: toolbarGroups[ i ].name, | |
440 | items: mappedSubgroup | |
441 | }; | |
442 | } | |
443 | } | |
444 | ||
445 | return toolbarGroups; | |
446 | }; | |
447 | ||
448 | /** | |
449 | * | |
450 | * @param {String|Object} group | |
451 | * @param {Array} removedBtns | |
452 | * @returns {Array} | |
453 | * @private | |
454 | */ | |
455 | ToolbarTextModifier.prototype._mapToolbarSubgroup = function( group, removedBtns ) { | |
456 | var totalBtns = 0; | |
457 | if ( typeof group == 'string' ) | |
458 | return group; | |
459 | ||
460 | var max = group.groups ? group.groups.length : 0, | |
461 | result = []; | |
462 | for ( var i = 0; i < max; i += 1 ) { | |
463 | var currSubgroup = group.groups[ i ]; | |
464 | ||
465 | var buttons = this.fullToolbarEditor.buttonsByGroup[ typeof currSubgroup === 'string' ? currSubgroup : currSubgroup.name ] || []; | |
466 | buttons = this._mapButtonsToButtonsNames( buttons, removedBtns ); | |
467 | var currTotalBtns = buttons.length; | |
468 | totalBtns += currTotalBtns; | |
469 | result = result.concat( buttons ); | |
470 | ||
471 | if ( currTotalBtns ) | |
472 | result.push( '-' ); | |
473 | } | |
474 | ||
475 | if ( result[ result.length - 1 ] == '-' ) | |
476 | result.pop(); | |
477 | ||
478 | return result; | |
479 | }; | |
480 | ||
481 | /** | |
482 | * | |
483 | * @param {Array} buttons | |
484 | * @param {Array} removedBtns | |
485 | * @returns {Array} | |
486 | * @private | |
487 | */ | |
488 | ToolbarTextModifier.prototype._mapButtonsToButtonsNames = function( buttons, removedBtns ) { | |
489 | var i = buttons.length; | |
490 | while ( i-- ) { | |
491 | var currBtn = buttons[ i ], | |
492 | camelCasedName; | |
493 | ||
494 | if ( typeof currBtn === 'string' ) { | |
495 | camelCasedName = currBtn; | |
496 | } else { | |
497 | camelCasedName = this.fullToolbarEditor.getCamelCasedButtonName( currBtn.name ); | |
498 | } | |
499 | ||
500 | if ( CKEDITOR.tools.indexOf( removedBtns, camelCasedName ) !== -1 ) { | |
501 | buttons.splice( i, 1 ); | |
502 | continue; | |
503 | } | |
504 | ||
505 | buttons[ i ] = camelCasedName; | |
506 | } | |
507 | ||
508 | return buttons; | |
509 | }; | |
510 | ||
511 | /** | |
512 | * @param {String} val | |
513 | * @returns {Object} | |
514 | * @private | |
515 | */ | |
516 | ToolbarTextModifier.prototype._evaluateValue = function( val ) { | |
517 | var parsed; | |
518 | ||
519 | try { | |
520 | var config = {}; | |
521 | ( function() { | |
522 | var CKEDITOR = Function( 'var CKEDITOR = {}; ' + val + '; return CKEDITOR;' )(); | |
523 | ||
524 | CKEDITOR.editorConfig( config ); | |
525 | parsed = config; | |
526 | } )(); | |
527 | ||
528 | // CKEditor does not handle empty arrays in configuration files | |
529 | // on IE8 | |
530 | var i = parsed.toolbar.length; | |
531 | while ( i-- ) | |
532 | if ( !parsed.toolbar[ i ] ) parsed.toolbar.splice( i, 1 ); | |
533 | ||
534 | } catch ( e ) { | |
535 | parsed = null; | |
536 | } | |
537 | ||
538 | return parsed; | |
539 | }; | |
540 | ||
541 | /** | |
542 | * @param {Array} toolbar | |
543 | * @returns {{toolbarGroups: Array, removeButtons: string}} | |
544 | */ | |
545 | ToolbarTextModifier.prototype.mapToolbarToToolbarGroups = function( toolbar ) { | |
546 | var usedGroups = {}, | |
547 | removeButtons = [], | |
548 | toolbarGroups = []; | |
549 | ||
550 | var max = toolbar.length; | |
551 | for ( var i = 0; i < max; i++ ) { | |
552 | if ( toolbar[ i ] === '/' ) { | |
553 | toolbarGroups.push( '/' ); | |
554 | continue; | |
555 | } | |
556 | ||
557 | var items = toolbar[ i ].items; | |
558 | ||
559 | var toolbarGroup = {}; | |
560 | toolbarGroup.name = toolbar[ i ].name; | |
561 | toolbarGroup.groups = []; | |
562 | ||
563 | var max2 = items.length; | |
564 | for ( var j = 0; j < max2; j++ ) { | |
565 | var item = items[ j ]; | |
566 | ||
567 | if ( item === '-' ) { | |
568 | continue; | |
569 | } | |
570 | ||
571 | var groupName = this.getToolbarGroupByButtonName( item ); | |
572 | ||
573 | var groupIndex = toolbarGroup.groups.indexOf( groupName ); | |
574 | if ( groupIndex === -1 ) { | |
575 | toolbarGroup.groups.push( groupName ); | |
576 | } | |
577 | ||
578 | usedGroups[ groupName ] = usedGroups[ groupName ] || {}; | |
579 | ||
580 | var buttons = ( usedGroups[ groupName ].buttons = usedGroups[ groupName ].buttons || {} ); | |
581 | ||
582 | buttons[ item ] = buttons[ item ] || { used: 0, origin: toolbarGroup.name }; | |
583 | buttons[ item ].used++; | |
584 | } | |
585 | ||
586 | toolbarGroups.push( toolbarGroup ); | |
587 | } | |
588 | ||
589 | // Handling removed buttons | |
590 | removeButtons = prepareRemovedButtons( usedGroups, this.fullToolbarEditor.buttonNamesByGroup ); | |
591 | ||
592 | function prepareRemovedButtons( usedGroups, buttonNames ) { | |
593 | var removed = []; | |
594 | ||
595 | for ( var groupName in usedGroups ) { | |
596 | var group = usedGroups[ groupName ]; | |
597 | var allButtonsInGroup = buttonNames[ groupName ].slice(); | |
598 | ||
599 | removed = removed.concat( removeStuffFromArray( allButtonsInGroup, Object.keys( group.buttons ) ) ); | |
600 | } | |
601 | ||
602 | return removed; | |
603 | } | |
604 | ||
605 | function removeStuffFromArray( array, stuff ) { | |
606 | array = array.slice(); | |
607 | var i = stuff.length; | |
608 | ||
609 | while ( i-- ) { | |
610 | var atIndex = array.indexOf( stuff[ i ] ); | |
611 | if ( atIndex !== -1 ) { | |
612 | array.splice( atIndex, 1 ); | |
613 | } | |
614 | } | |
615 | ||
616 | return array; | |
617 | } | |
618 | ||
619 | return { toolbarGroups: toolbarGroups, removeButtons: removeButtons.join( ',' ) }; | |
620 | }; | |
621 | ||
622 | return ToolbarTextModifier; | |
623 | } )(); |