123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /*
- * Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or http://ckeditor.com/license
- */
- ( function() {
- // Add to collection with DUP examination.
- // @param {Object} collection
- // @param {Object} element
- // @param {Object} database
- function addSafely( collection, element, database ) {
- // 1. IE doesn't support customData on text nodes;
- // 2. Text nodes never get chance to appear twice;
- if ( !element.is || !element.getCustomData( 'block_processed' ) ) {
- element.is && CKEDITOR.dom.element.setMarker( database, element, 'block_processed', true );
- collection.push( element );
- }
- }
- // Dialog reused by both 'creatediv' and 'editdiv' commands.
- // @param {Object} editor
- // @param {String} command The command name which indicate what the current command is.
- function divDialog( editor, command ) {
- // Definition of elements at which div operation should stopped.
- var divLimitDefinition = ( function() {
- // Customzie from specialize blockLimit elements
- var definition = CKEDITOR.tools.extend( {}, CKEDITOR.dtd.$blockLimit );
- if ( editor.config.div_wrapTable ) {
- delete definition.td;
- delete definition.th;
- }
- return definition;
- } )();
- // DTD of 'div' element
- var dtd = CKEDITOR.dtd.div;
- // Get the first div limit element on the element's path.
- // @param {Object} element
- function getDivContainer( element ) {
- var container = editor.elementPath( element ).blockLimit;
- // Never consider read-only (i.e. contenteditable=false) element as
- // a first div limit (#11083).
- if ( container.isReadOnly() )
- container = container.getParent();
- // Dont stop at 'td' and 'th' when div should wrap entire table.
- if ( editor.config.div_wrapTable && container.is( [ 'td', 'th' ] ) ) {
- var parentPath = editor.elementPath( container.getParent() );
- container = parentPath.blockLimit;
- }
- return container;
- }
- // Init all fields' setup/commit function.
- // @memberof divDialog
- function setupFields() {
- this.foreach( function( field ) {
- // Exclude layout container elements
- if ( /^(?!vbox|hbox)/.test( field.type ) ) {
- if ( !field.setup ) {
- // Read the dialog fields values from the specified
- // element attributes.
- field.setup = function( element ) {
- field.setValue( element.getAttribute( field.id ) || '', 1 );
- };
- }
- if ( !field.commit ) {
- // Set element attributes assigned by the dialog
- // fields.
- field.commit = function( element ) {
- var fieldValue = this.getValue();
- // ignore default element attribute values
- if ( field.id == 'dir' && element.getComputedStyle( 'direction' ) == fieldValue ) {
- return;
- }
- if ( fieldValue )
- element.setAttribute( field.id, fieldValue );
- else
- element.removeAttribute( field.id );
- };
- }
- }
- } );
- }
- // Wrapping 'div' element around appropriate blocks among the selected ranges.
- // @param {Object} editor
- function createDiv( editor ) {
- // new adding containers OR detected pre-existed containers.
- var containers = [];
- // node markers store.
- var database = {};
- // All block level elements which contained by the ranges.
- var containedBlocks = [],
- block;
- // Get all ranges from the selection.
- var selection = editor.getSelection(),
- ranges = selection.getRanges();
- var bookmarks = selection.createBookmarks();
- var i, iterator;
- // collect all included elements from dom-iterator
- for ( i = 0; i < ranges.length; i++ ) {
- iterator = ranges[ i ].createIterator();
- while ( ( block = iterator.getNextParagraph() ) ) {
- // include contents of blockLimit elements.
- if ( block.getName() in divLimitDefinition && !block.isReadOnly() ) {
- var j,
- childNodes = block.getChildren();
- for ( j = 0; j < childNodes.count(); j++ )
- addSafely( containedBlocks, childNodes.getItem( j ), database );
- } else {
- while ( !dtd[ block.getName() ] && !block.equals( ranges[ i ].root ) )
- block = block.getParent();
- addSafely( containedBlocks, block, database );
- }
- }
- }
- CKEDITOR.dom.element.clearAllMarkers( database );
- var blockGroups = groupByDivLimit( containedBlocks );
- var ancestor, divElement;
- for ( i = 0; i < blockGroups.length; i++ ) {
- var currentNode = blockGroups[ i ][ 0 ];
- // Calculate the common parent node of all contained elements.
- ancestor = currentNode.getParent();
- for ( j = 1; j < blockGroups[ i ].length; j++ )
- ancestor = ancestor.getCommonAncestor( blockGroups[ i ][ j ] );
- divElement = new CKEDITOR.dom.element( 'div', editor.document );
- // Normalize the blocks in each group to a common parent.
- for ( j = 0; j < blockGroups[ i ].length; j++ ) {
- currentNode = blockGroups[ i ][ j ];
- while ( !currentNode.getParent().equals( ancestor ) )
- currentNode = currentNode.getParent();
- // This could introduce some duplicated elements in array.
- blockGroups[ i ][ j ] = currentNode;
- }
- // Wrapped blocks counting
- for ( j = 0; j < blockGroups[ i ].length; j++ ) {
- currentNode = blockGroups[ i ][ j ];
- // Avoid DUP elements introduced by grouping.
- if ( !( currentNode.getCustomData && currentNode.getCustomData( 'block_processed' ) ) ) {
- currentNode.is && CKEDITOR.dom.element.setMarker( database, currentNode, 'block_processed', true );
- // Establish new container, wrapping all elements in this group.
- if ( !j )
- divElement.insertBefore( currentNode );
- divElement.append( currentNode );
- }
- }
- CKEDITOR.dom.element.clearAllMarkers( database );
- containers.push( divElement );
- }
- selection.selectBookmarks( bookmarks );
- return containers;
- }
- // Divide a set of nodes to different groups by their path's blocklimit element.
- // Note: the specified nodes should be in source order naturally, which mean they are supposed to producea by following class:
- // * CKEDITOR.dom.range.Iterator
- // * CKEDITOR.dom.domWalker
- // @returns {Array[]} the grouped nodes
- function groupByDivLimit( nodes ) {
- var groups = [],
- lastDivLimit = null,
- block;
- for ( var i = 0; i < nodes.length; i++ ) {
- block = nodes[ i ];
- var limit = getDivContainer( block );
- if ( !limit.equals( lastDivLimit ) ) {
- lastDivLimit = limit;
- groups.push( [] );
- }
- groups[ groups.length - 1 ].push( block );
- }
- return groups;
- }
- // Synchronous field values to other impacted fields is required, e.g. div styles
- // change should also alter inline-style text.
- function commitInternally( targetFields ) {
- var dialog = this.getDialog(),
- element = dialog._element && dialog._element.clone() || new CKEDITOR.dom.element( 'div', editor.document );
- // Commit this field and broadcast to target fields.
- this.commit( element, true );
- targetFields = [].concat( targetFields );
- var length = targetFields.length,
- field;
- for ( var i = 0; i < length; i++ ) {
- field = dialog.getContentElement.apply( dialog, targetFields[ i ].split( ':' ) );
- field && field.setup && field.setup( element, true );
- }
- }
- // Registered 'CKEDITOR.style' instances.
- var styles = {};
- // Hold a collection of created block container elements.
- var containers = [];
- // @type divDialog
- return {
- title: editor.lang.div.title,
- minWidth: 400,
- minHeight: 165,
- contents: [ {
- id: 'info',
- label: editor.lang.common.generalTab,
- title: editor.lang.common.generalTab,
- elements: [ {
- type: 'hbox',
- widths: [ '50%', '50%' ],
- children: [ {
- id: 'elementStyle',
- type: 'select',
- style: 'width: 100%;',
- label: editor.lang.div.styleSelectLabel,
- 'default': '',
- // Options are loaded dynamically.
- items: [
- [ editor.lang.common.notSet, '' ]
- ],
- onChange: function() {
- commitInternally.call( this, [ 'info:elementStyle', 'info:class', 'advanced:dir', 'advanced:style' ] );
- },
- setup: function( element ) {
- for ( var name in styles )
- styles[ name ].checkElementRemovable( element, true, editor ) && this.setValue( name, 1 );
- },
- commit: function( element ) {
- var styleName;
- if ( ( styleName = this.getValue() ) ) {
- var style = styles[ styleName ];
- style.applyToObject( element, editor );
- }
- else {
- element.removeAttribute( 'style' );
- }
- }
- },
- {
- id: 'class',
- type: 'text',
- requiredContent: 'div(cke-xyz)', // Random text like 'xyz' will check if all are allowed.
- label: editor.lang.common.cssClass,
- 'default': ''
- } ]
- } ]
- },
- {
- id: 'advanced',
- label: editor.lang.common.advancedTab,
- title: editor.lang.common.advancedTab,
- elements: [ {
- type: 'vbox',
- padding: 1,
- children: [ {
- type: 'hbox',
- widths: [ '50%', '50%' ],
- children: [ {
- type: 'text',
- id: 'id',
- requiredContent: 'div[id]',
- label: editor.lang.common.id,
- 'default': ''
- },
- {
- type: 'text',
- id: 'lang',
- requiredContent: 'div[lang]',
- label: editor.lang.common.langCode,
- 'default': ''
- } ]
- },
- {
- type: 'hbox',
- children: [ {
- type: 'text',
- id: 'style',
- requiredContent: 'div{cke-xyz}', // Random text like 'xyz' will check if all are allowed.
- style: 'width: 100%;',
- label: editor.lang.common.cssStyle,
- 'default': '',
- commit: function( element ) {
- element.setAttribute( 'style', this.getValue() );
- }
- } ]
- },
- {
- type: 'hbox',
- children: [ {
- type: 'text',
- id: 'title',
- requiredContent: 'div[title]',
- style: 'width: 100%;',
- label: editor.lang.common.advisoryTitle,
- 'default': ''
- } ]
- },
- {
- type: 'select',
- id: 'dir',
- requiredContent: 'div[dir]',
- style: 'width: 100%;',
- label: editor.lang.common.langDir,
- 'default': '',
- items: [
- [ editor.lang.common.notSet, '' ],
- [ editor.lang.common.langDirLtr, 'ltr' ],
- [ editor.lang.common.langDirRtl, 'rtl' ]
- ]
- } ] }
- ]
- } ],
- onLoad: function() {
- setupFields.call( this );
- // Preparing for the 'elementStyle' field.
- var dialog = this,
- stylesField = this.getContentElement( 'info', 'elementStyle' );
- // Reuse the 'stylescombo' plugin's styles definition.
- editor.getStylesSet( function( stylesDefinitions ) {
- var styleName, style;
- if ( stylesDefinitions ) {
- // Digg only those styles that apply to 'div'.
- for ( var i = 0; i < stylesDefinitions.length; i++ ) {
- var styleDefinition = stylesDefinitions[ i ];
- if ( styleDefinition.element && styleDefinition.element == 'div' ) {
- styleName = styleDefinition.name;
- styles[ styleName ] = style = new CKEDITOR.style( styleDefinition );
- if ( editor.filter.check( style ) ) {
- // Populate the styles field options with style name.
- stylesField.items.push( [ styleName, styleName ] );
- stylesField.add( styleName, styleName );
- }
- }
- }
- }
- // We should disable the content element
- // it if no options are available at all.
- stylesField[ stylesField.items.length > 1 ? 'enable' : 'disable' ]();
- // Now setup the field value manually if dialog was opened on element. (#9689)
- setTimeout( function() {
- dialog._element && stylesField.setup( dialog._element );
- }, 0 );
- } );
- },
- onShow: function() {
- // Whether always create new container regardless of existed
- // ones.
- if ( command == 'editdiv' ) {
- // Try to discover the containers that already existed in
- // ranges
- // update dialog field values
- this.setupContent( this._element = CKEDITOR.plugins.div.getSurroundDiv( editor ) );
- }
- },
- onOk: function() {
- if ( command == 'editdiv' )
- containers = [ this._element ];
- else
- containers = createDiv( editor, true );
- // Update elements attributes
- var size = containers.length;
- for ( var i = 0; i < size; i++ ) {
- this.commitContent( containers[ i ] );
- // Remove empty 'style' attribute.
- !containers[ i ].getAttribute( 'style' ) && containers[ i ].removeAttribute( 'style' );
- }
- this.hide();
- },
- onHide: function() {
- // Remove style only when editing existing DIV. (#6315)
- if ( command == 'editdiv' )
- this._element.removeCustomData( 'elementStyle' );
- delete this._element;
- }
- };
- }
- CKEDITOR.dialog.add( 'creatediv', function( editor ) {
- return divDialog( editor, 'creatediv' );
- } );
- CKEDITOR.dialog.add( 'editdiv', function( editor ) {
- return divDialog( editor, 'editdiv' );
- } );
- } )();
- /**
- * Whether to wrap the whole table instead of individual cells when created `<div>` in table cell.
- *
- * config.div_wrapTable = true;
- *
- * @cfg {Boolean} [div_wrapTable=false]
- * @member CKEDITOR.config
- */
|