]>
git.immae.eu Git - perso/Immae/Projets/packagist/ludivine-ckeditor-component.git/blob - sources/plugins/div/dialogs/div.js
2 * Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3 * For licensing, see LICENSE.md or http://ckeditor.com/license
8 // Add to collection with DUP examination.
9 // @param {Object} collection
10 // @param {Object} element
11 // @param {Object} database
12 function addSafely( collection
, element
, database
) {
13 // 1. IE doesn't support customData on text nodes;
14 // 2. Text nodes never get chance to appear twice;
15 if ( !element
.is
|| !element
.getCustomData( 'block_processed' ) ) {
16 element
.is
&& CKEDITOR
.dom
.element
.setMarker( database
, element
, 'block_processed', true );
17 collection
.push( element
);
21 // Dialog reused by both 'creatediv' and 'editdiv' commands.
22 // @param {Object} editor
23 // @param {String} command The command name which indicate what the current command is.
24 function divDialog( editor
, command
) {
25 // Definition of elements at which div operation should stopped.
26 var divLimitDefinition
= ( function() {
28 // Customzie from specialize blockLimit elements
29 var definition
= CKEDITOR
.tools
.extend( {}, CKEDITOR
.dtd
.$blockLimit
);
31 if ( editor
.config
.div_wrapTable
) {
38 // DTD of 'div' element
39 var dtd
= CKEDITOR
.dtd
.div
;
41 // Get the first div limit element on the element's path.
42 // @param {Object} element
43 function getDivContainer( element
) {
44 var container
= editor
.elementPath( element
).blockLimit
;
46 // Never consider read-only (i.e. contenteditable=false) element as
47 // a first div limit (http://dev.ckeditor.com/ticket/11083).
48 if ( container
.isReadOnly() )
49 container
= container
.getParent();
51 // Dont stop at 'td' and 'th' when div should wrap entire table.
52 if ( editor
.config
.div_wrapTable
&& container
.is( [ 'td', 'th' ] ) ) {
53 var parentPath
= editor
.elementPath( container
.getParent() );
54 container
= parentPath
.blockLimit
;
60 // Init all fields' setup/commit function.
61 // @memberof divDialog
62 function setupFields() {
63 this.foreach( function( field
) {
64 // Exclude layout container elements
65 if ( /^(?!vbox|hbox)/.test( field
.type
) ) {
67 // Read the dialog fields values from the specified
68 // element attributes.
69 field
.setup = function( element
) {
70 field
.setValue( element
.getAttribute( field
.id
) || '', 1 );
73 if ( !field
.commit
) {
74 // Set element attributes assigned by the dialog
76 field
.commit = function( element
) {
77 var fieldValue
= this.getValue();
78 // ignore default element attribute values
79 if ( field
.id
== 'dir' && element
.getComputedStyle( 'direction' ) == fieldValue
) {
84 element
.setAttribute( field
.id
, fieldValue
);
86 element
.removeAttribute( field
.id
);
93 // Wrapping 'div' element around appropriate blocks among the selected ranges.
94 // @param {Object} editor
95 function createDiv( editor
) {
96 // new adding containers OR detected pre-existed containers.
98 // node markers store.
100 // All block level elements which contained by the ranges.
101 var containedBlocks
= [],
104 // Get all ranges from the selection.
105 var selection
= editor
.getSelection(),
106 ranges
= selection
.getRanges();
107 var bookmarks
= selection
.createBookmarks();
110 // collect all included elements from dom-iterator
111 for ( i
= 0; i
< ranges
.length
; i
++ ) {
112 iterator
= ranges
[ i
].createIterator();
113 while ( ( block
= iterator
.getNextParagraph() ) ) {
114 // include contents of blockLimit elements.
115 if ( block
.getName() in divLimitDefinition
&& !block
.isReadOnly() ) {
117 childNodes
= block
.getChildren();
118 for ( j
= 0; j
< childNodes
.count(); j
++ )
119 addSafely( containedBlocks
, childNodes
.getItem( j
), database
);
121 while ( !dtd
[ block
.getName() ] && !block
.equals( ranges
[ i
].root
) )
122 block
= block
.getParent();
123 addSafely( containedBlocks
, block
, database
);
128 CKEDITOR
.dom
.element
.clearAllMarkers( database
);
130 var blockGroups
= groupByDivLimit( containedBlocks
);
131 var ancestor
, divElement
;
133 for ( i
= 0; i
< blockGroups
.length
; i
++ ) {
134 // Sometimes we could get empty block group if all elements inside it
135 // don't have parent's nodes (http://dev.ckeditor.com/ticket/13585).
136 if ( !blockGroups
[ i
].length
) {
140 var currentNode
= blockGroups
[ i
][ 0 ];
142 // Calculate the common parent node of all contained elements.
143 ancestor
= currentNode
.getParent();
144 for ( j
= 1; j
< blockGroups
[ i
].length
; j
++ ) {
145 ancestor
= ancestor
.getCommonAncestor( blockGroups
[ i
][ j
] );
148 // If there is no ancestor, mark editable as one (http://dev.ckeditor.com/ticket/13585).
150 ancestor
= editor
.editable();
153 divElement
= new CKEDITOR
.dom
.element( 'div', editor
.document
);
155 // Normalize the blocks in each group to a common parent.
156 for ( j
= 0; j
< blockGroups
[ i
].length
; j
++ ) {
157 currentNode
= blockGroups
[ i
][ j
];
159 // Check if the currentNode has a parent before attempting to operate on it (http://dev.ckeditor.com/ticket/13585).
160 while ( currentNode
.getParent() && !currentNode
.getParent().equals( ancestor
) ) {
161 currentNode
= currentNode
.getParent();
164 // This could introduce some duplicated elements in array.
165 blockGroups
[ i
][ j
] = currentNode
;
168 // Wrapped blocks counting
169 for ( j
= 0; j
< blockGroups
[ i
].length
; j
++ ) {
170 currentNode
= blockGroups
[ i
][ j
];
172 // Avoid DUP elements introduced by grouping.
173 if ( !( currentNode
.getCustomData
&& currentNode
.getCustomData( 'block_processed' ) ) ) {
174 currentNode
.is
&& CKEDITOR
.dom
.element
.setMarker( database
, currentNode
, 'block_processed', true );
176 // Establish new container, wrapping all elements in this group.
178 divElement
.insertBefore( currentNode
);
181 divElement
.append( currentNode
);
185 CKEDITOR
.dom
.element
.clearAllMarkers( database
);
186 containers
.push( divElement
);
189 selection
.selectBookmarks( bookmarks
);
193 // Divide a set of nodes to different groups by their path's blocklimit element.
194 // Note: the specified nodes should be in source order naturally, which mean they are supposed to producea by following class:
195 // * CKEDITOR.dom.range.Iterator
196 // * CKEDITOR.dom.domWalker
197 // @returns {Array[]} the grouped nodes
198 function groupByDivLimit( nodes
) {
203 for ( var i
= 0; i
< nodes
.length
; i
++ ) {
205 var limit
= getDivContainer( block
);
206 if ( !limit
.equals( lastDivLimit
) ) {
207 lastDivLimit
= limit
;
211 // Sometimes we got nodes that are not inside the DOM, which causes error (http://dev.ckeditor.com/ticket/13585).
212 if ( block
.getParent() ) {
213 groups
[ groups
.length
- 1 ].push( block
);
220 // Synchronous field values to other impacted fields is required, e.g. div styles
221 // change should also alter inline-style text.
222 function commitInternally( targetFields
) {
223 var dialog
= this.getDialog(),
224 element
= dialog
._element
&& dialog
._element
.clone() || new CKEDITOR
.dom
.element( 'div', editor
.document
);
226 // Commit this field and broadcast to target fields.
227 this.commit( element
, true );
229 targetFields
= [].concat( targetFields
);
230 var length
= targetFields
.length
,
232 for ( var i
= 0; i
< length
; i
++ ) {
233 field
= dialog
.getContentElement
.apply( dialog
, targetFields
[ i
].split( ':' ) );
234 field
&& field
.setup
&& field
.setup( element
, true );
239 // Registered 'CKEDITOR.style' instances.
242 // Hold a collection of created block container elements.
247 title: editor
.lang
.div
.title
,
252 label: editor
.lang
.common
.generalTab
,
253 title: editor
.lang
.common
.generalTab
,
256 widths: [ '50%', '50%' ],
260 style: 'width: 100%;',
261 label: editor
.lang
.div
.styleSelectLabel
,
263 // Options are loaded dynamically.
265 [ editor
.lang
.common
.notSet
, '' ]
267 onChange: function() {
268 commitInternally
.call( this, [ 'info:elementStyle', 'info:class', 'advanced:dir', 'advanced:style' ] );
270 setup: function( element
) {
271 for ( var name
in styles
)
272 styles
[ name
].checkElementRemovable( element
, true, editor
) && this.setValue( name
, 1 );
274 commit: function( element
) {
276 if ( ( styleName
= this.getValue() ) ) {
277 var style
= styles
[ styleName
];
278 style
.applyToObject( element
, editor
);
281 element
.removeAttribute( 'style' );
288 requiredContent: 'div(cke-xyz)', // Random text like 'xyz' will check if all are allowed.
289 label: editor
.lang
.common
.cssClass
,
296 label: editor
.lang
.common
.advancedTab
,
297 title: editor
.lang
.common
.advancedTab
,
303 widths: [ '50%', '50%' ],
307 requiredContent: 'div[id]',
308 label: editor
.lang
.common
.id
,
314 requiredContent: 'div[lang]',
315 label: editor
.lang
.common
.langCode
,
324 requiredContent: 'div{cke-xyz}', // Random text like 'xyz' will check if all are allowed.
325 style: 'width: 100%;',
326 label: editor
.lang
.common
.cssStyle
,
328 commit: function( element
) {
329 element
.setAttribute( 'style', this.getValue() );
338 requiredContent: 'div[title]',
339 style: 'width: 100%;',
340 label: editor
.lang
.common
.advisoryTitle
,
347 requiredContent: 'div[dir]',
348 style: 'width: 100%;',
349 label: editor
.lang
.common
.langDir
,
352 [ editor
.lang
.common
.notSet
, '' ],
353 [ editor
.lang
.common
.langDirLtr
, 'ltr' ],
354 [ editor
.lang
.common
.langDirRtl
, 'rtl' ]
360 setupFields
.call( this );
362 // Preparing for the 'elementStyle' field.
364 stylesField
= this.getContentElement( 'info', 'elementStyle' );
366 // Reuse the 'stylescombo' plugin's styles definition.
367 editor
.getStylesSet( function( stylesDefinitions
) {
368 var styleName
, style
;
370 if ( stylesDefinitions
) {
371 // Digg only those styles that apply to 'div'.
372 for ( var i
= 0; i
< stylesDefinitions
.length
; i
++ ) {
373 var styleDefinition
= stylesDefinitions
[ i
];
374 if ( styleDefinition
.element
&& styleDefinition
.element
== 'div' ) {
375 styleName
= styleDefinition
.name
;
376 styles
[ styleName
] = style
= new CKEDITOR
.style( styleDefinition
);
378 if ( editor
.filter
.check( style
) ) {
379 // Populate the styles field options with style name.
380 stylesField
.items
.push( [ styleName
, styleName
] );
381 stylesField
.add( styleName
, styleName
);
387 // We should disable the content element
388 // it if no options are available at all.
389 stylesField
[ stylesField
.items
.length
> 1 ? 'enable' : 'disable' ]();
391 // Now setup the field value manually if dialog was opened on element. (http://dev.ckeditor.com/ticket/9689)
392 setTimeout( function() {
393 dialog
._element
&& stylesField
.setup( dialog
._element
);
398 // Whether always create new container regardless of existed
400 if ( command
== 'editdiv' ) {
401 // Try to discover the containers that already existed in
403 // update dialog field values
404 this.setupContent( this._element
= CKEDITOR
.plugins
.div
.getSurroundDiv( editor
) );
408 if ( command
== 'editdiv' )
409 containers
= [ this._element
];
411 containers
= createDiv( editor
, true );
413 // Update elements attributes
414 var size
= containers
.length
;
415 for ( var i
= 0; i
< size
; i
++ ) {
416 this.commitContent( containers
[ i
] );
418 // Remove empty 'style' attribute.
419 !containers
[ i
].getAttribute( 'style' ) && containers
[ i
].removeAttribute( 'style' );
425 // Remove style only when editing existing DIV. (http://dev.ckeditor.com/ticket/6315)
426 if ( command
== 'editdiv' )
427 this._element
.removeCustomData( 'elementStyle' );
428 delete this._element
;
433 CKEDITOR
.dialog
.add( 'creatediv', function( editor
) {
434 return divDialog( editor
, 'creatediv' );
437 CKEDITOR
.dialog
.add( 'editdiv', function( editor
) {
438 return divDialog( editor
, 'editdiv' );
444 * Whether to wrap the entire table instead of individual cells when creating a `<div>` in a table cell.
446 * config.div_wrapTable = true;
448 * @cfg {Boolean} [div_wrapTable=false]
449 * @member CKEDITOR.config