]> git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blame - sources/plugins/div/dialogs/div.js
Add audio, color and fonts
[perso/Immae/Projets/packagist/ludivine-ckeditor-component.git] / sources / plugins / div / dialogs / div.js
CommitLineData
eaa92715
IB
1/*\r
2 * Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.\r
3 * For licensing, see LICENSE.md or http://ckeditor.com/license\r
4 */\r
5\r
6( function() {\r
7\r
8 // Add to collection with DUP examination.\r
9 // @param {Object} collection\r
10 // @param {Object} element\r
11 // @param {Object} database\r
12 function addSafely( collection, element, database ) {\r
13 // 1. IE doesn't support customData on text nodes;\r
14 // 2. Text nodes never get chance to appear twice;\r
15 if ( !element.is || !element.getCustomData( 'block_processed' ) ) {\r
16 element.is && CKEDITOR.dom.element.setMarker( database, element, 'block_processed', true );\r
17 collection.push( element );\r
18 }\r
19 }\r
20\r
21 // Dialog reused by both 'creatediv' and 'editdiv' commands.\r
22 // @param {Object} editor\r
23 // @param {String} command The command name which indicate what the current command is.\r
24 function divDialog( editor, command ) {\r
25 // Definition of elements at which div operation should stopped.\r
26 var divLimitDefinition = ( function() {\r
27\r
28 // Customzie from specialize blockLimit elements\r
29 var definition = CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$blockLimit );\r
30\r
31 if ( editor.config.div_wrapTable ) {\r
32 delete definition.td;\r
33 delete definition.th;\r
34 }\r
35 return definition;\r
36 } )();\r
37\r
38 // DTD of 'div' element\r
39 var dtd = CKEDITOR.dtd.div;\r
40\r
41 // Get the first div limit element on the element's path.\r
42 // @param {Object} element\r
43 function getDivContainer( element ) {\r
44 var container = editor.elementPath( element ).blockLimit;\r
45\r
46 // Never consider read-only (i.e. contenteditable=false) element as\r
47 // a first div limit (#11083).\r
48 if ( container.isReadOnly() )\r
49 container = container.getParent();\r
50\r
51 // Dont stop at 'td' and 'th' when div should wrap entire table.\r
52 if ( editor.config.div_wrapTable && container.is( [ 'td', 'th' ] ) ) {\r
53 var parentPath = editor.elementPath( container.getParent() );\r
54 container = parentPath.blockLimit;\r
55 }\r
56\r
57 return container;\r
58 }\r
59\r
60 // Init all fields' setup/commit function.\r
61 // @memberof divDialog\r
62 function setupFields() {\r
63 this.foreach( function( field ) {\r
64 // Exclude layout container elements\r
65 if ( /^(?!vbox|hbox)/.test( field.type ) ) {\r
66 if ( !field.setup ) {\r
67 // Read the dialog fields values from the specified\r
68 // element attributes.\r
69 field.setup = function( element ) {\r
70 field.setValue( element.getAttribute( field.id ) || '', 1 );\r
71 };\r
72 }\r
73 if ( !field.commit ) {\r
74 // Set element attributes assigned by the dialog\r
75 // fields.\r
76 field.commit = function( element ) {\r
77 var fieldValue = this.getValue();\r
78 // ignore default element attribute values\r
79 if ( field.id == 'dir' && element.getComputedStyle( 'direction' ) == fieldValue ) {\r
80 return;\r
81 }\r
82\r
83 if ( fieldValue )\r
84 element.setAttribute( field.id, fieldValue );\r
85 else\r
86 element.removeAttribute( field.id );\r
87 };\r
88 }\r
89 }\r
90 } );\r
91 }\r
92\r
93 // Wrapping 'div' element around appropriate blocks among the selected ranges.\r
94 // @param {Object} editor\r
95 function createDiv( editor ) {\r
96 // new adding containers OR detected pre-existed containers.\r
97 var containers = [];\r
98 // node markers store.\r
99 var database = {};\r
100 // All block level elements which contained by the ranges.\r
101 var containedBlocks = [],\r
102 block;\r
103\r
104 // Get all ranges from the selection.\r
105 var selection = editor.getSelection(),\r
106 ranges = selection.getRanges();\r
107 var bookmarks = selection.createBookmarks();\r
108 var i, iterator;\r
109\r
110 // collect all included elements from dom-iterator\r
111 for ( i = 0; i < ranges.length; i++ ) {\r
112 iterator = ranges[ i ].createIterator();\r
113 while ( ( block = iterator.getNextParagraph() ) ) {\r
114 // include contents of blockLimit elements.\r
115 if ( block.getName() in divLimitDefinition && !block.isReadOnly() ) {\r
116 var j,\r
117 childNodes = block.getChildren();\r
118 for ( j = 0; j < childNodes.count(); j++ )\r
119 addSafely( containedBlocks, childNodes.getItem( j ), database );\r
120 } else {\r
121 while ( !dtd[ block.getName() ] && !block.equals( ranges[ i ].root ) )\r
122 block = block.getParent();\r
123 addSafely( containedBlocks, block, database );\r
124 }\r
125 }\r
126 }\r
127\r
128 CKEDITOR.dom.element.clearAllMarkers( database );\r
129\r
130 var blockGroups = groupByDivLimit( containedBlocks );\r
131 var ancestor, divElement;\r
132\r
133 for ( i = 0; i < blockGroups.length; i++ ) {\r
134 var currentNode = blockGroups[ i ][ 0 ];\r
135\r
136 // Calculate the common parent node of all contained elements.\r
137 ancestor = currentNode.getParent();\r
138 for ( j = 1; j < blockGroups[ i ].length; j++ )\r
139 ancestor = ancestor.getCommonAncestor( blockGroups[ i ][ j ] );\r
140\r
141 divElement = new CKEDITOR.dom.element( 'div', editor.document );\r
142\r
143 // Normalize the blocks in each group to a common parent.\r
144 for ( j = 0; j < blockGroups[ i ].length; j++ ) {\r
145 currentNode = blockGroups[ i ][ j ];\r
146\r
147 while ( !currentNode.getParent().equals( ancestor ) )\r
148 currentNode = currentNode.getParent();\r
149\r
150 // This could introduce some duplicated elements in array.\r
151 blockGroups[ i ][ j ] = currentNode;\r
152 }\r
153\r
154 // Wrapped blocks counting\r
155 for ( j = 0; j < blockGroups[ i ].length; j++ ) {\r
156 currentNode = blockGroups[ i ][ j ];\r
157\r
158 // Avoid DUP elements introduced by grouping.\r
159 if ( !( currentNode.getCustomData && currentNode.getCustomData( 'block_processed' ) ) ) {\r
160 currentNode.is && CKEDITOR.dom.element.setMarker( database, currentNode, 'block_processed', true );\r
161\r
162 // Establish new container, wrapping all elements in this group.\r
163 if ( !j )\r
164 divElement.insertBefore( currentNode );\r
165\r
166 divElement.append( currentNode );\r
167 }\r
168 }\r
169\r
170 CKEDITOR.dom.element.clearAllMarkers( database );\r
171 containers.push( divElement );\r
172 }\r
173\r
174 selection.selectBookmarks( bookmarks );\r
175 return containers;\r
176 }\r
177\r
178 // Divide a set of nodes to different groups by their path's blocklimit element.\r
179 // Note: the specified nodes should be in source order naturally, which mean they are supposed to producea by following class:\r
180 // * CKEDITOR.dom.range.Iterator\r
181 // * CKEDITOR.dom.domWalker\r
182 // @returns {Array[]} the grouped nodes\r
183 function groupByDivLimit( nodes ) {\r
184 var groups = [],\r
185 lastDivLimit = null,\r
186 block;\r
187\r
188 for ( var i = 0; i < nodes.length; i++ ) {\r
189 block = nodes[ i ];\r
190 var limit = getDivContainer( block );\r
191 if ( !limit.equals( lastDivLimit ) ) {\r
192 lastDivLimit = limit;\r
193 groups.push( [] );\r
194 }\r
195 groups[ groups.length - 1 ].push( block );\r
196 }\r
197 return groups;\r
198 }\r
199\r
200 // Synchronous field values to other impacted fields is required, e.g. div styles\r
201 // change should also alter inline-style text.\r
202 function commitInternally( targetFields ) {\r
203 var dialog = this.getDialog(),\r
204 element = dialog._element && dialog._element.clone() || new CKEDITOR.dom.element( 'div', editor.document );\r
205\r
206 // Commit this field and broadcast to target fields.\r
207 this.commit( element, true );\r
208\r
209 targetFields = [].concat( targetFields );\r
210 var length = targetFields.length,\r
211 field;\r
212 for ( var i = 0; i < length; i++ ) {\r
213 field = dialog.getContentElement.apply( dialog, targetFields[ i ].split( ':' ) );\r
214 field && field.setup && field.setup( element, true );\r
215 }\r
216 }\r
217\r
218\r
219 // Registered 'CKEDITOR.style' instances.\r
220 var styles = {};\r
221\r
222 // Hold a collection of created block container elements.\r
223 var containers = [];\r
224\r
225 // @type divDialog\r
226 return {\r
227 title: editor.lang.div.title,\r
228 minWidth: 400,\r
229 minHeight: 165,\r
230 contents: [ {\r
231 id: 'info',\r
232 label: editor.lang.common.generalTab,\r
233 title: editor.lang.common.generalTab,\r
234 elements: [ {\r
235 type: 'hbox',\r
236 widths: [ '50%', '50%' ],\r
237 children: [ {\r
238 id: 'elementStyle',\r
239 type: 'select',\r
240 style: 'width: 100%;',\r
241 label: editor.lang.div.styleSelectLabel,\r
242 'default': '',\r
243 // Options are loaded dynamically.\r
244 items: [\r
245 [ editor.lang.common.notSet, '' ]\r
246 ],\r
247 onChange: function() {\r
248 commitInternally.call( this, [ 'info:elementStyle', 'info:class', 'advanced:dir', 'advanced:style' ] );\r
249 },\r
250 setup: function( element ) {\r
251 for ( var name in styles )\r
252 styles[ name ].checkElementRemovable( element, true, editor ) && this.setValue( name, 1 );\r
253 },\r
254 commit: function( element ) {\r
255 var styleName;\r
256 if ( ( styleName = this.getValue() ) ) {\r
257 var style = styles[ styleName ];\r
258 style.applyToObject( element, editor );\r
259 }\r
260 else {\r
261 element.removeAttribute( 'style' );\r
262 }\r
263 }\r
264 },\r
265 {\r
266 id: 'class',\r
267 type: 'text',\r
268 requiredContent: 'div(cke-xyz)', // Random text like 'xyz' will check if all are allowed.\r
269 label: editor.lang.common.cssClass,\r
270 'default': ''\r
271 } ]\r
272 } ]\r
273 },\r
274 {\r
275 id: 'advanced',\r
276 label: editor.lang.common.advancedTab,\r
277 title: editor.lang.common.advancedTab,\r
278 elements: [ {\r
279 type: 'vbox',\r
280 padding: 1,\r
281 children: [ {\r
282 type: 'hbox',\r
283 widths: [ '50%', '50%' ],\r
284 children: [ {\r
285 type: 'text',\r
286 id: 'id',\r
287 requiredContent: 'div[id]',\r
288 label: editor.lang.common.id,\r
289 'default': ''\r
290 },\r
291 {\r
292 type: 'text',\r
293 id: 'lang',\r
294 requiredContent: 'div[lang]',\r
295 label: editor.lang.common.langCode,\r
296 'default': ''\r
297 } ]\r
298 },\r
299 {\r
300 type: 'hbox',\r
301 children: [ {\r
302 type: 'text',\r
303 id: 'style',\r
304 requiredContent: 'div{cke-xyz}', // Random text like 'xyz' will check if all are allowed.\r
305 style: 'width: 100%;',\r
306 label: editor.lang.common.cssStyle,\r
307 'default': '',\r
308 commit: function( element ) {\r
309 element.setAttribute( 'style', this.getValue() );\r
310 }\r
311 } ]\r
312 },\r
313 {\r
314 type: 'hbox',\r
315 children: [ {\r
316 type: 'text',\r
317 id: 'title',\r
318 requiredContent: 'div[title]',\r
319 style: 'width: 100%;',\r
320 label: editor.lang.common.advisoryTitle,\r
321 'default': ''\r
322 } ]\r
323 },\r
324 {\r
325 type: 'select',\r
326 id: 'dir',\r
327 requiredContent: 'div[dir]',\r
328 style: 'width: 100%;',\r
329 label: editor.lang.common.langDir,\r
330 'default': '',\r
331 items: [\r
332 [ editor.lang.common.notSet, '' ],\r
333 [ editor.lang.common.langDirLtr, 'ltr' ],\r
334 [ editor.lang.common.langDirRtl, 'rtl' ]\r
335 ]\r
336 } ] }\r
337 ]\r
338 } ],\r
339 onLoad: function() {\r
340 setupFields.call( this );\r
341\r
342 // Preparing for the 'elementStyle' field.\r
343 var dialog = this,\r
344 stylesField = this.getContentElement( 'info', 'elementStyle' );\r
345\r
346 // Reuse the 'stylescombo' plugin's styles definition.\r
347 editor.getStylesSet( function( stylesDefinitions ) {\r
348 var styleName, style;\r
349\r
350 if ( stylesDefinitions ) {\r
351 // Digg only those styles that apply to 'div'.\r
352 for ( var i = 0; i < stylesDefinitions.length; i++ ) {\r
353 var styleDefinition = stylesDefinitions[ i ];\r
354 if ( styleDefinition.element && styleDefinition.element == 'div' ) {\r
355 styleName = styleDefinition.name;\r
356 styles[ styleName ] = style = new CKEDITOR.style( styleDefinition );\r
357\r
358 if ( editor.filter.check( style ) ) {\r
359 // Populate the styles field options with style name.\r
360 stylesField.items.push( [ styleName, styleName ] );\r
361 stylesField.add( styleName, styleName );\r
362 }\r
363 }\r
364 }\r
365 }\r
366\r
367 // We should disable the content element\r
368 // it if no options are available at all.\r
369 stylesField[ stylesField.items.length > 1 ? 'enable' : 'disable' ]();\r
370\r
371 // Now setup the field value manually if dialog was opened on element. (#9689)\r
372 setTimeout( function() {\r
373 dialog._element && stylesField.setup( dialog._element );\r
374 }, 0 );\r
375 } );\r
376 },\r
377 onShow: function() {\r
378 // Whether always create new container regardless of existed\r
379 // ones.\r
380 if ( command == 'editdiv' ) {\r
381 // Try to discover the containers that already existed in\r
382 // ranges\r
383 // update dialog field values\r
384 this.setupContent( this._element = CKEDITOR.plugins.div.getSurroundDiv( editor ) );\r
385 }\r
386 },\r
387 onOk: function() {\r
388 if ( command == 'editdiv' )\r
389 containers = [ this._element ];\r
390 else\r
391 containers = createDiv( editor, true );\r
392\r
393 // Update elements attributes\r
394 var size = containers.length;\r
395 for ( var i = 0; i < size; i++ ) {\r
396 this.commitContent( containers[ i ] );\r
397\r
398 // Remove empty 'style' attribute.\r
399 !containers[ i ].getAttribute( 'style' ) && containers[ i ].removeAttribute( 'style' );\r
400 }\r
401\r
402 this.hide();\r
403 },\r
404 onHide: function() {\r
405 // Remove style only when editing existing DIV. (#6315)\r
406 if ( command == 'editdiv' )\r
407 this._element.removeCustomData( 'elementStyle' );\r
408 delete this._element;\r
409 }\r
410 };\r
411 }\r
412\r
413 CKEDITOR.dialog.add( 'creatediv', function( editor ) {\r
414 return divDialog( editor, 'creatediv' );\r
415 } );\r
416\r
417 CKEDITOR.dialog.add( 'editdiv', function( editor ) {\r
418 return divDialog( editor, 'editdiv' );\r
419 } );\r
420\r
421} )();\r
422\r
423/**\r
424 * Whether to wrap the entire table instead of individual cells when creating a `<div>` in a table cell.\r
425 *\r
426 * config.div_wrapTable = true;\r
427 *\r
428 * @cfg {Boolean} [div_wrapTable=false]\r
429 * @member CKEDITOR.config\r
430 */\r