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