diff options
Diffstat (limited to 'sources/plugins/indentblock')
-rw-r--r-- | sources/plugins/indentblock/plugin.js | 312 |
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 | */ | ||