aboutsummaryrefslogtreecommitdiff
path: root/sources/plugins/indentblock
diff options
context:
space:
mode:
Diffstat (limited to 'sources/plugins/indentblock')
-rw-r--r--sources/plugins/indentblock/plugin.js312
1 files changed, 312 insertions, 0 deletions
diff --git a/sources/plugins/indentblock/plugin.js b/sources/plugins/indentblock/plugin.js
new file mode 100644
index 0000000..b39fd73
--- /dev/null
+++ b/sources/plugins/indentblock/plugin.js
@@ -0,0 +1,312 @@
1/**
2 * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
4 */
5
6/**
7 * @fileOverview Handles the indentation of block elements.
8 */
9
10( function() {
11 'use strict';
12
13 var $listItem = CKEDITOR.dtd.$listItem,
14 $list = CKEDITOR.dtd.$list,
15 TRISTATE_DISABLED = CKEDITOR.TRISTATE_DISABLED,
16 TRISTATE_OFF = CKEDITOR.TRISTATE_OFF;
17
18 CKEDITOR.plugins.add( 'indentblock', {
19 requires: 'indent',
20 init: function( editor ) {
21 var globalHelpers = CKEDITOR.plugins.indent,
22 classes = editor.config.indentClasses;
23
24 // Register commands.
25 globalHelpers.registerCommands( editor, {
26 indentblock: new commandDefinition( editor, 'indentblock', true ),
27 outdentblock: new commandDefinition( editor, 'outdentblock' )
28 } );
29
30 function commandDefinition() {
31 globalHelpers.specificDefinition.apply( this, arguments );
32
33 this.allowedContent = {
34 'div h1 h2 h3 h4 h5 h6 ol p pre ul': {
35 // Do not add elements, but only text-align style if element is validated by other rule.
36 propertiesOnly: true,
37 styles: !classes ? 'margin-left,margin-right' : null,
38 classes: classes || null
39 }
40 };
41
42 this.contentTransformations = [
43 [ 'div: splitMarginShorthand' ],
44 [ 'h1: splitMarginShorthand' ],
45 [ 'h2: splitMarginShorthand' ],
46 [ 'h3: splitMarginShorthand' ],
47 [ 'h4: splitMarginShorthand' ],
48 [ 'h5: splitMarginShorthand' ],
49 [ 'h6: splitMarginShorthand' ],
50 [ 'ol: splitMarginShorthand' ],
51 [ 'p: splitMarginShorthand' ],
52 [ 'pre: splitMarginShorthand' ],
53 [ 'ul: splitMarginShorthand' ]
54 ];
55
56 if ( this.enterBr )
57 this.allowedContent.div = true;
58
59 this.requiredContent = ( this.enterBr ? 'div' : 'p' ) +
60 ( classes ? '(' + classes.join( ',' ) + ')' : '{margin-left}' );
61
62 this.jobs = {
63 '20': {
64 refresh: function( editor, path ) {
65 var firstBlock = path.block || path.blockLimit;
66
67 // Switch context from somewhere inside list item to list item,
68 // if not found just assign self (doing nothing).
69 if ( !firstBlock.is( $listItem ) ) {
70 var ascendant = firstBlock.getAscendant( $listItem );
71
72 firstBlock = ( ascendant && path.contains( ascendant ) ) || firstBlock;
73 }
74
75 // Switch context from list item to list
76 // because indentblock can indent entire list
77 // but not a single list element.
78
79 if ( firstBlock.is( $listItem ) )
80 firstBlock = firstBlock.getParent();
81
82 // [-] Context in the path or ENTER_BR
83 //
84 // Don't try to indent if the element is out of
85 // this plugin's scope. This assertion is omitted
86 // if ENTER_BR is in use since there may be no block
87 // in the path.
88
89 if ( !this.enterBr && !this.getContext( path ) )
90 return TRISTATE_DISABLED;
91
92 else if ( classes ) {
93
94 // [+] Context in the path or ENTER_BR
95 // [+] IndentClasses
96 //
97 // If there are indentation classes, check if reached
98 // the highest level of indentation. If so, disable
99 // the command.
100
101 if ( indentClassLeft.call( this, firstBlock, classes ) )
102 return TRISTATE_OFF;
103 else
104 return TRISTATE_DISABLED;
105 } else {
106
107 // [+] Context in the path or ENTER_BR
108 // [-] IndentClasses
109 // [+] Indenting
110 //
111 // No indent-level limitations due to indent classes.
112 // Indent-like command can always be executed.
113
114 if ( this.isIndent )
115 return TRISTATE_OFF;
116
117 // [+] Context in the path or ENTER_BR
118 // [-] IndentClasses
119 // [-] Indenting
120 // [-] Block in the path
121 //
122 // No block in path. There's no element to apply indentation
123 // so disable the command.
124
125 else if ( !firstBlock )
126 return TRISTATE_DISABLED;
127
128 // [+] Context in the path or ENTER_BR
129 // [-] IndentClasses
130 // [-] Indenting
131 // [+] Block in path.
132 //
133 // Not using indentClasses but there is firstBlock.
134 // We can calculate current indentation level and
135 // try to increase/decrease it.
136
137 else {
138 return CKEDITOR[
139 ( getIndent( firstBlock ) || 0 ) <= 0 ? 'TRISTATE_DISABLED' : 'TRISTATE_OFF'
140 ];
141 }
142 }
143 },
144
145 exec: function( editor ) {
146 var selection = editor.getSelection(),
147 range = selection && selection.getRanges()[ 0 ],
148 nearestListBlock;
149
150 // If there's some list in the path, then it will be
151 // a full-list indent by increasing or decreasing margin property.
152 if ( ( nearestListBlock = editor.elementPath().contains( $list ) ) )
153 indentElement.call( this, nearestListBlock, classes );
154
155 // If no list in the path, use iterator to indent all the possible
156 // paragraphs in the range, creating them if necessary.
157 else {
158 var iterator = range.createIterator(),
159 enterMode = editor.config.enterMode,
160 block;
161
162 iterator.enforceRealBlocks = true;
163 iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
164
165 while ( ( block = iterator.getNextParagraph( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) ) {
166 if ( !block.isReadOnly() )
167 indentElement.call( this, block, classes );
168 }
169 }
170
171 return true;
172 }
173 }
174 };
175 }
176
177 CKEDITOR.tools.extend( commandDefinition.prototype, globalHelpers.specificDefinition.prototype, {
178 // Elements that, if in an elementpath, will be handled by this
179 // command. They restrict the scope of the plugin.
180 context: { div: 1, dl: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, ul: 1, ol: 1, p: 1, pre: 1, table: 1 },
181
182 // A regex built on config#indentClasses to detect whether an
183 // element has some indentClass or not.
184 classNameRegex: classes ? new RegExp( '(?:^|\\s+)(' + classes.join( '|' ) + ')(?=$|\\s)' ) : null
185 } );
186 }
187 } );
188
189 // Generic indentation procedure for indentation of any element
190 // either with margin property or config#indentClass.
191 function indentElement( element, classes, dir ) {
192 if ( element.getCustomData( 'indent_processed' ) )
193 return;
194
195 var editor = this.editor,
196 isIndent = this.isIndent;
197
198 if ( classes ) {
199 // Transform current class f to indent step index.
200 var indentClass = element.$.className.match( this.classNameRegex ),
201 indentStep = 0;
202
203 if ( indentClass ) {
204 indentClass = indentClass[ 1 ];
205 indentStep = CKEDITOR.tools.indexOf( classes, indentClass ) + 1;
206 }
207
208 // Operate on indent step index, transform indent step index
209 // back to class name.
210 if ( ( indentStep += isIndent ? 1 : -1 ) < 0 )
211 return;
212
213 indentStep = Math.min( indentStep, classes.length );
214 indentStep = Math.max( indentStep, 0 );
215 element.$.className = CKEDITOR.tools.ltrim( element.$.className.replace( this.classNameRegex, '' ) );
216
217 if ( indentStep > 0 )
218 element.addClass( classes[ indentStep - 1 ] );
219 } else {
220 var indentCssProperty = getIndentCss( element, dir ),
221 currentOffset = parseInt( element.getStyle( indentCssProperty ), 10 ),
222 indentOffset = editor.config.indentOffset || 40;
223
224 if ( isNaN( currentOffset ) )
225 currentOffset = 0;
226
227 currentOffset += ( isIndent ? 1 : -1 ) * indentOffset;
228
229 if ( currentOffset < 0 )
230 return;
231
232 currentOffset = Math.max( currentOffset, 0 );
233 currentOffset = Math.ceil( currentOffset / indentOffset ) * indentOffset;
234
235 element.setStyle(
236 indentCssProperty,
237 currentOffset ? currentOffset + ( editor.config.indentUnit || 'px' ) : ''
238 );
239
240 if ( element.getAttribute( 'style' ) === '' )
241 element.removeAttribute( 'style' );
242 }
243
244 CKEDITOR.dom.element.setMarker( this.database, element, 'indent_processed', 1 );
245
246 return;
247 }
248
249 // Method that checks if current indentation level for an element
250 // reached the limit determined by config#indentClasses.
251 function indentClassLeft( node, classes ) {
252 var indentClass = node.$.className.match( this.classNameRegex ),
253 isIndent = this.isIndent;
254
255 // If node has one of the indentClasses:
256 // * If it holds the topmost indentClass, then
257 // no more classes have left.
258 // * If it holds any other indentClass, it can use the next one
259 // or the previous one.
260 // * Outdent is always possible. We can remove indentClass.
261 if ( indentClass )
262 return isIndent ? indentClass[ 1 ] != classes.slice( -1 ) : true;
263
264 // If node has no class which belongs to indentClasses,
265 // then it is at 0-level. It can be indented but not outdented.
266 else
267 return isIndent;
268 }
269
270 // Determines indent CSS property for an element according to
271 // what is the direction of such element. It can be either `margin-left`
272 // or `margin-right`.
273 function getIndentCss( element, dir ) {
274 return ( dir || element.getComputedStyle( 'direction' ) ) == 'ltr' ? 'margin-left' : 'margin-right';
275 }
276
277 // Return the numerical indent value of margin-left|right of an element,
278 // considering element's direction. If element has no margin specified,
279 // NaN is returned.
280 function getIndent( element ) {
281 return parseInt( element.getStyle( getIndentCss( element ) ), 10 );
282 }
283} )();
284
285/**
286 * A list of classes to use for indenting the contents. If set to `null`, no classes will be used
287 * and instead the {@link #indentUnit} and {@link #indentOffset} properties will be used.
288 *
289 * // Use the 'Indent1', 'Indent2', 'Indent3' classes.
290 * config.indentClasses = ['Indent1', 'Indent2', 'Indent3'];
291 *
292 * @cfg {Array} [indentClasses=null]
293 * @member CKEDITOR.config
294 */
295
296/**
297 * The size in {@link CKEDITOR.config#indentUnit indentation units} of each indentation step.
298 *
299 * config.indentOffset = 4;
300 *
301 * @cfg {Number} [indentOffset=40]
302 * @member CKEDITOR.config
303 */
304
305/**
306 * The unit used for {@link CKEDITOR.config#indentOffset indentation offset}.
307 *
308 * config.indentUnit = 'em';
309 *
310 * @cfg {String} [indentUnit='px']
311 * @member CKEDITOR.config
312 */