123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- /**
- * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or http://ckeditor.com/license
- */
- /**
- * @fileOverview Handles the indentation of block elements.
- */
- ( function() {
- 'use strict';
- var $listItem = CKEDITOR.dtd.$listItem,
- $list = CKEDITOR.dtd.$list,
- TRISTATE_DISABLED = CKEDITOR.TRISTATE_DISABLED,
- TRISTATE_OFF = CKEDITOR.TRISTATE_OFF;
- CKEDITOR.plugins.add( 'indentblock', {
- requires: 'indent',
- init: function( editor ) {
- var globalHelpers = CKEDITOR.plugins.indent,
- classes = editor.config.indentClasses;
- // Register commands.
- globalHelpers.registerCommands( editor, {
- indentblock: new commandDefinition( editor, 'indentblock', true ),
- outdentblock: new commandDefinition( editor, 'outdentblock' )
- } );
- function commandDefinition() {
- globalHelpers.specificDefinition.apply( this, arguments );
- this.allowedContent = {
- 'div h1 h2 h3 h4 h5 h6 ol p pre ul': {
- // Do not add elements, but only text-align style if element is validated by other rule.
- propertiesOnly: true,
- styles: !classes ? 'margin-left,margin-right' : null,
- classes: classes || null
- }
- };
- if ( this.enterBr )
- this.allowedContent.div = true;
- this.requiredContent = ( this.enterBr ? 'div' : 'p' ) +
- ( classes ? '(' + classes.join( ',' ) + ')' : '{margin-left}' );
- this.jobs = {
- '20': {
- refresh: function( editor, path ) {
- var firstBlock = path.block || path.blockLimit;
- // Switch context from somewhere inside list item to list item,
- // if not found just assign self (doing nothing).
- if ( !firstBlock.is( $listItem ) ) {
- var ascendant = firstBlock.getAscendant( $listItem );
- firstBlock = ( ascendant && path.contains( ascendant ) ) || firstBlock;
- }
- // Switch context from list item to list
- // because indentblock can indent entire list
- // but not a single list element.
- if ( firstBlock.is( $listItem ) )
- firstBlock = firstBlock.getParent();
- // [-] Context in the path or ENTER_BR
- //
- // Don't try to indent if the element is out of
- // this plugin's scope. This assertion is omitted
- // if ENTER_BR is in use since there may be no block
- // in the path.
- if ( !this.enterBr && !this.getContext( path ) )
- return TRISTATE_DISABLED;
- else if ( classes ) {
- // [+] Context in the path or ENTER_BR
- // [+] IndentClasses
- //
- // If there are indentation classes, check if reached
- // the highest level of indentation. If so, disable
- // the command.
- if ( indentClassLeft.call( this, firstBlock, classes ) )
- return TRISTATE_OFF;
- else
- return TRISTATE_DISABLED;
- } else {
- // [+] Context in the path or ENTER_BR
- // [-] IndentClasses
- // [+] Indenting
- //
- // No indent-level limitations due to indent classes.
- // Indent-like command can always be executed.
- if ( this.isIndent )
- return TRISTATE_OFF;
- // [+] Context in the path or ENTER_BR
- // [-] IndentClasses
- // [-] Indenting
- // [-] Block in the path
- //
- // No block in path. There's no element to apply indentation
- // so disable the command.
- else if ( !firstBlock )
- return TRISTATE_DISABLED;
- // [+] Context in the path or ENTER_BR
- // [-] IndentClasses
- // [-] Indenting
- // [+] Block in path.
- //
- // Not using indentClasses but there is firstBlock.
- // We can calculate current indentation level and
- // try to increase/decrease it.
- else {
- return CKEDITOR[
- ( getIndent( firstBlock ) || 0 ) <= 0 ? 'TRISTATE_DISABLED' : 'TRISTATE_OFF'
- ];
- }
- }
- },
- exec: function( editor ) {
- var selection = editor.getSelection(),
- range = selection && selection.getRanges()[ 0 ],
- nearestListBlock;
- // If there's some list in the path, then it will be
- // a full-list indent by increasing or decreasing margin property.
- if ( ( nearestListBlock = editor.elementPath().contains( $list ) ) )
- indentElement.call( this, nearestListBlock, classes );
- // If no list in the path, use iterator to indent all the possible
- // paragraphs in the range, creating them if necessary.
- else {
- var iterator = range.createIterator(),
- enterMode = editor.config.enterMode,
- block;
- iterator.enforceRealBlocks = true;
- iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
- while ( ( block = iterator.getNextParagraph( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) ) {
- if ( !block.isReadOnly() )
- indentElement.call( this, block, classes );
- }
- }
- return true;
- }
- }
- };
- }
- CKEDITOR.tools.extend( commandDefinition.prototype, globalHelpers.specificDefinition.prototype, {
- // Elements that, if in an elementpath, will be handled by this
- // command. They restrict the scope of the plugin.
- 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 },
- // A regex built on config#indentClasses to detect whether an
- // element has some indentClass or not.
- classNameRegex: classes ? new RegExp( '(?:^|\\s+)(' + classes.join( '|' ) + ')(?=$|\\s)' ) : null
- } );
- }
- } );
- // Generic indentation procedure for indentation of any element
- // either with margin property or config#indentClass.
- function indentElement( element, classes, dir ) {
- if ( element.getCustomData( 'indent_processed' ) )
- return;
- var editor = this.editor,
- isIndent = this.isIndent;
- if ( classes ) {
- // Transform current class f to indent step index.
- var indentClass = element.$.className.match( this.classNameRegex ),
- indentStep = 0;
- if ( indentClass ) {
- indentClass = indentClass[ 1 ];
- indentStep = CKEDITOR.tools.indexOf( classes, indentClass ) + 1;
- }
- // Operate on indent step index, transform indent step index
- // back to class name.
- if ( ( indentStep += isIndent ? 1 : -1 ) < 0 )
- return;
- indentStep = Math.min( indentStep, classes.length );
- indentStep = Math.max( indentStep, 0 );
- element.$.className = CKEDITOR.tools.ltrim( element.$.className.replace( this.classNameRegex, '' ) );
- if ( indentStep > 0 )
- element.addClass( classes[ indentStep - 1 ] );
- } else {
- var indentCssProperty = getIndentCss( element, dir ),
- currentOffset = parseInt( element.getStyle( indentCssProperty ), 10 ),
- indentOffset = editor.config.indentOffset || 40;
- if ( isNaN( currentOffset ) )
- currentOffset = 0;
- currentOffset += ( isIndent ? 1 : -1 ) * indentOffset;
- if ( currentOffset < 0 )
- return;
- currentOffset = Math.max( currentOffset, 0 );
- currentOffset = Math.ceil( currentOffset / indentOffset ) * indentOffset;
- element.setStyle(
- indentCssProperty,
- currentOffset ? currentOffset + ( editor.config.indentUnit || 'px' ) : ''
- );
- if ( element.getAttribute( 'style' ) === '' )
- element.removeAttribute( 'style' );
- }
- CKEDITOR.dom.element.setMarker( this.database, element, 'indent_processed', 1 );
- return;
- }
- // Method that checks if current indentation level for an element
- // reached the limit determined by config#indentClasses.
- function indentClassLeft( node, classes ) {
- var indentClass = node.$.className.match( this.classNameRegex ),
- isIndent = this.isIndent;
- // If node has one of the indentClasses:
- // * If it holds the topmost indentClass, then
- // no more classes have left.
- // * If it holds any other indentClass, it can use the next one
- // or the previous one.
- // * Outdent is always possible. We can remove indentClass.
- if ( indentClass )
- return isIndent ? indentClass[ 1 ] != classes.slice( -1 ) : true;
- // If node has no class which belongs to indentClasses,
- // then it is at 0-level. It can be indented but not outdented.
- else
- return isIndent;
- }
- // Determines indent CSS property for an element according to
- // what is the direction of such element. It can be either `margin-left`
- // or `margin-right`.
- function getIndentCss( element, dir ) {
- return ( dir || element.getComputedStyle( 'direction' ) ) == 'ltr' ? 'margin-left' : 'margin-right';
- }
- // Return the numerical indent value of margin-left|right of an element,
- // considering element's direction. If element has no margin specified,
- // NaN is returned.
- function getIndent( element ) {
- return parseInt( element.getStyle( getIndentCss( element ) ), 10 );
- }
- } )();
- /**
- * A list of classes to use for indenting the contents. If set to `null`, no classes will be used
- * and instead the {@link #indentUnit} and {@link #indentOffset} properties will be used.
- *
- * // Use the 'Indent1', 'Indent2', 'Indent3' classes.
- * config.indentClasses = ['Indent1', 'Indent2', 'Indent3'];
- *
- * @cfg {Array} [indentClasses=null]
- * @member CKEDITOR.config
- */
- /**
- * The size in {@link CKEDITOR.config#indentUnit indentation units} of each indentation step.
- *
- * config.indentOffset = 4;
- *
- * @cfg {Number} [indentOffset=40]
- * @member CKEDITOR.config
- */
- /**
- * The unit used for {@link CKEDITOR.config#indentOffset indentation offset}.
- *
- * config.indentUnit = 'em';
- *
- * @cfg {String} [indentUnit='px']
- * @member CKEDITOR.config
- */
|