/** * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or http://ckeditor.com/license */ 'use strict'; /** * Block style type. * * Read more in the {@link CKEDITOR.style} class documentation. * * @readonly * @property {Number} [=1] * @member CKEDITOR */ CKEDITOR.STYLE_BLOCK = 1; /** * Inline style type. * * Read more in the {@link CKEDITOR.style} class documentation. * * @readonly * @property {Number} [=2] * @member CKEDITOR */ CKEDITOR.STYLE_INLINE = 2; /** * Object style type. * * Read more in the {@link CKEDITOR.style} class documentation. * * @readonly * @property {Number} [=3] * @member CKEDITOR */ CKEDITOR.STYLE_OBJECT = 3; ( function() { var blockElements = { address: 1, div: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, p: 1, pre: 1, section: 1, header: 1, footer: 1, nav: 1, article: 1, aside: 1, figure: 1, dialog: 1, hgroup: 1, time: 1, meter: 1, menu: 1, command: 1, keygen: 1, output: 1, progress: 1, details: 1, datagrid: 1, datalist: 1 }, objectElements = { a: 1, blockquote: 1, embed: 1, hr: 1, img: 1, li: 1, object: 1, ol: 1, table: 1, td: 1, tr: 1, th: 1, ul: 1, dl: 1, dt: 1, dd: 1, form: 1, audio: 1, video: 1 }; var semicolonFixRegex = /\s*(?:;\s*|$)/, varRegex = /#\((.+?)\)/g; var notBookmark = CKEDITOR.dom.walker.bookmark( 0, 1 ), nonWhitespaces = CKEDITOR.dom.walker.whitespaces( 1 ); /** * A class representing a style instance for the specific style definition. * In this approach, a style is a set of properties, like attributes and styles, * which can be applied to and removed from a {@link CKEDITOR.dom.selection selection} through * {@link CKEDITOR.editor editor} methods: {@link CKEDITOR.editor#applyStyle} and {@link CKEDITOR.editor#removeStyle}, * respectively. * * Three default style types are available: {@link CKEDITOR#STYLE_BLOCK STYLE_BLOCK}, {@link CKEDITOR#STYLE_INLINE STYLE_INLINE}, * and {@link CKEDITOR#STYLE_OBJECT STYLE_OBJECT}. Based on its type, a style heavily changes its behavior. * You can read more about style types in the [Style Types section of the Styles guide](#!/guide/dev_styles-section-style-types). * * It is possible to define a custom style type by subclassing this class by using the {@link #addCustomHandler} method. * However, because of great complexity of the styles handling job, it is only possible in very specific cases. * * ### Usage * * Basic usage: * * // Define a block style. * var style = new CKEDITOR.style( { element: 'h1' } ); * * // Considering the following selection: * //
Foo
Bar^
* // Executing: * editor.applyStyle( style ); * // Will give: * //Foo
Foo
Bar^
* * style.checkActive( editor.elementPath(), editor ); // -> false * * Object style: * * // Define an object style. * var style = new CKEDITOR.style( { element: 'img', attributes: { 'class': 'foo' } } ); * * // Considering the following selection: * //Foo^
[]Foo
[]Foo
get special treatment. if ( block.is( 'pre' ) ) { newBlock = this._.enterMode == CKEDITOR.ENTER_BR ? null : range.document.createElement( this._.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ); newBlock && block.copyAttributes( newBlock ); replaceBlock( block, newBlock ); } else { removeFromElement.call( this, block ); } } } range.moveToBookmark( bookmark ); } // Replace the original block with new one, with special treatment // forblocks to make sure content format is well preserved, and merging/splitting adjacent // when necessary. (#3188) function replaceBlock( block, newBlock ) { // Block is to be removed, create a temp element to // save contents. var removeBlock = !newBlock; if ( removeBlock ) { newBlock = block.getDocument().createElement( 'div' ); block.copyAttributes( newBlock ); } var newBlockIsPre = newBlock && newBlock.is( 'pre' ), blockIsPre = block.is( 'pre' ), isToPre = newBlockIsPre && !blockIsPre, isFromPre = !newBlockIsPre && blockIsPre; if ( isToPre ) newBlock = toPre( block, newBlock ); else if ( isFromPre ) // Split biginto pieces before start to convert. newBlock = fromPres( removeBlock ? [ block.getHtml() ] : splitIntoPres( block ), newBlock ); else block.moveChildren( newBlock ); newBlock.replace( block ); if ( newBlockIsPre ) { // Merge previousblocks. mergePre( newBlock ); } else if ( removeBlock ) { removeNoAttribsElement( newBlock ); } } // Merge ablock with a previous sibling if available. function mergePre( preBlock ) { var previousBlock; if ( !( ( previousBlock = preBlock.getPrevious( nonWhitespaces ) ) && previousBlock.type == CKEDITOR.NODE_ELEMENT && previousBlock.is( 'pre' ) ) ) return; // Merge the previousblock contents into the current// block. // // Another thing to be careful here is that currentBlock might contain // a '\n' at the beginning, and previousBlock might contain a '\n' // towards the end. These new lines are not normally displayed but they // become visible after merging. var mergedHtml = replace( previousBlock.getHtml(), /\n$/, '' ) + '\n\n' + replace( preBlock.getHtml(), /^\n/, '' ); // Krugle: IE normalizes innerHTML from, breaking whitespaces. if ( CKEDITOR.env.ie ) preBlock.$.outerHTML = '' + mergedHtml + ''; else preBlock.setHtml( mergedHtml ); previousBlock.remove(); } // Split into multipleblocks separated by double line-break. function splitIntoPres( preBlock ) { // Exclude the ones at header OR at tail, // and ignore bookmark content between them. var duoBrRegex = /(\S\s*)\n(?:\s|(]+data-cke-bookmark.*?\/span>))*\n(?!$)/gi, pres = [], splitedHtml = replace( preBlock.getOuterHtml(), duoBrRegex, function( match, charBefore, bookmark ) { return charBefore + '
' + bookmark + ''; } ); splitedHtml.replace( /([\s\S]*?)<\/pre>/gi, function( match, preContent ) { pres.push( preContent ); } ); return pres; } // Wrapper function of String::replace without considering of head/tail bookmarks nodes. function replace( str, regexp, replacement ) { var headBookmark = '', tailBookmark = ''; str = str.replace( /(^]+data-cke-bookmark.*?\/span>)|(]+data-cke-bookmark.*?\/span>$)/gi, function( str, m1, m2 ) { m1 && ( headBookmark = m1 ); m2 && ( tailBookmark = m2 ); return ''; } ); return headBookmark + str.replace( regexp, replacement ) + tailBookmark; } // Converting a list of
into blocks with format well preserved. function fromPres( preHtmls, newBlock ) { var docFrag; if ( preHtmls.length > 1 ) docFrag = new CKEDITOR.dom.documentFragment( newBlock.getDocument() ); for ( var i = 0; i < preHtmls.length; i++ ) { var blockHtml = preHtmls[ i ]; // 1. Trim the first and last line-breaks immediately after and before, // they're not visible. blockHtml = blockHtml.replace( /(\r\n|\r)/g, '\n' ); blockHtml = replace( blockHtml, /^[ \t]*\n/, '' ); blockHtml = replace( blockHtml, /\n$/, '' ); // 2. Convert spaces or tabs at the beginning or at the end to blockHtml = replace( blockHtml, /^[ \t]+|[ \t]+$/g, function( match, offset ) { if ( match.length == 1 ) // one space, preserve it return ' '; else if ( !offset ) // beginning of block return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' '; else // end of block return ' ' + CKEDITOR.tools.repeat( ' ', match.length - 1 ); } ); // 3. Convert \n to
. // 4. Convert contiguous (i.e. non-singular) spaces or tabs to blockHtml = blockHtml.replace( /\n/g, '
' ); blockHtml = blockHtml.replace( /[ \t]{2,}/g, function( match ) { return CKEDITOR.tools.repeat( ' ', match.length - 1 ) + ' '; } ); if ( docFrag ) { var newBlockClone = newBlock.clone(); newBlockClone.setHtml( blockHtml ); docFrag.append( newBlockClone ); } else { newBlock.setHtml( blockHtml ); } } return docFrag || newBlock; } // Converting from a non-PRE block to a PRE block in formatting operations. function toPre( block, newBlock ) { var bogus = block.getBogus(); bogus && bogus.remove(); // First trim the block content. var preHtml = block.getHtml(); // 1. Trim head/tail spaces, they're not visible. preHtml = replace( preHtml, /(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g, '' ); // 2. Delete ANSI whitespaces immediately before and after
because // they are not visible. preHtml = preHtml.replace( /[ \t\r\n]*(
]*>)[ \t\r\n]*/gi, '$1' ); // 3. Compress other ANSI whitespaces since they're only visible as one // single space previously. // 4. Convert to spaces since is no longer needed in. preHtml = preHtml.replace( /([ \t\n\r]+| )/g, ' ' ); // 5. Convert any
to \n. This must not be done earlier because // the \n would then get compressed. preHtml = preHtml.replace( /
]*>/gi, '\n' ); // Krugle: IE normalizes innerHTML to, breaking whitespaces. if ( CKEDITOR.env.ie ) { var temp = block.getDocument().createElement( 'div' ); temp.append( newBlock ); newBlock.$.outerHTML = '' + preHtml + ''; newBlock.copyAttributes( temp.getFirst() ); newBlock = temp.getFirst().remove(); } else { newBlock.setHtml( preHtml ); } return newBlock; } // Removes a style from an element itself, don't care about its subtree. function removeFromElement( element, keepDataAttrs ) { var def = this._.definition, attributes = def.attributes, styles = def.styles, overrides = getOverrides( this )[ element.getName() ], // If the style is only about the element itself, we have to remove the element. removeEmpty = CKEDITOR.tools.isEmpty( attributes ) && CKEDITOR.tools.isEmpty( styles ); // Remove definition attributes/style from the elemnt. for ( var attName in attributes ) { // The 'class' element value must match (#1318). if ( ( attName == 'class' || this._.definition.fullMatch ) && element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) ) continue; // Do not touch data-* attributes (#11011) (#11258). if ( keepDataAttrs && attName.slice( 0, 5 ) == 'data-' ) continue; removeEmpty = element.hasAttribute( attName ); element.removeAttribute( attName ); } for ( var styleName in styles ) { // Full match style insist on having fully equivalence. (#5018) if ( this._.definition.fullMatch && element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) ) continue; removeEmpty = removeEmpty || !!element.getStyle( styleName ); element.removeStyle( styleName ); } // Remove overrides, but don't remove the element if it's a block element removeOverrides( element, overrides, blockElements[ element.getName() ] ); if ( removeEmpty ) { if ( this._.definition.alwaysRemoveElement ) removeNoAttribsElement( element, 1 ); else { if ( !CKEDITOR.dtd.$block[ element.getName() ] || this._.enterMode == CKEDITOR.ENTER_BR && !element.hasAttributes() ) removeNoAttribsElement( element ); else element.renameNode( this._.enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ); } } } // Removes a style from inside an element. Called on applyStyle to make cleanup // before apply. During clean up this function keep data-* attribute in contrast // to removeFromElement. function removeFromInsideElement( element ) { var overrides = getOverrides( this ), innerElements = element.getElementsByTag( this.element ), innerElement; for ( var i = innerElements.count(); --i >= 0; ) { innerElement = innerElements.getItem( i ); // Do not remove elements which are read only (e.g. duplicates inside widgets). if ( !innerElement.isReadOnly() ) removeFromElement.call( this, innerElement, true ); } // Now remove any other element with different name that is // defined to be overriden. for ( var overrideElement in overrides ) { if ( overrideElement != this.element ) { innerElements = element.getElementsByTag( overrideElement ); for ( i = innerElements.count() - 1; i >= 0; i-- ) { innerElement = innerElements.getItem( i ); // Do not remove elements which are read only (e.g. duplicates inside widgets). if ( !innerElement.isReadOnly() ) removeOverrides( innerElement, overrides[ overrideElement ] ); } } } } // Remove overriding styles/attributes from the specific element. // Note: Remove the element if no attributes remain. // @param {Object} element // @param {Object} overrides // @param {Boolean} Don't remove the element function removeOverrides( element, overrides, dontRemove ) { var attributes = overrides && overrides.attributes; if ( attributes ) { for ( var i = 0; i < attributes.length; i++ ) { var attName = attributes[ i ][ 0 ], actualAttrValue; if ( ( actualAttrValue = element.getAttribute( attName ) ) ) { var attValue = attributes[ i ][ 1 ]; // Remove the attribute if: // - The override definition value is null ; // - The override definition valie is a string that // matches the attribute value exactly. // - The override definition value is a regex that // has matches in the attribute value. if ( attValue === null || ( attValue.test && attValue.test( actualAttrValue ) ) || ( typeof attValue == 'string' && actualAttrValue == attValue ) ) element.removeAttribute( attName ); } } } if ( !dontRemove ) removeNoAttribsElement( element ); } // If the element has no more attributes, remove it. function removeNoAttribsElement( element, forceRemove ) { // If no more attributes remained in the element, remove it, // leaving its children. if ( !element.hasAttributes() || forceRemove ) { if ( CKEDITOR.dtd.$block[ element.getName() ] ) { var previous = element.getPrevious( nonWhitespaces ), next = element.getNext( nonWhitespaces ); if ( previous && ( previous.type == CKEDITOR.NODE_TEXT || !previous.isBlockBoundary( { br: 1 } ) ) ) element.append( 'br', 1 ); if ( next && ( next.type == CKEDITOR.NODE_TEXT || !next.isBlockBoundary( { br: 1 } ) ) ) element.append( 'br' ); element.remove( true ); } else { // Removing elements may open points where merging is possible, // so let's cache the first and last nodes for later checking. var firstChild = element.getFirst(); var lastChild = element.getLast(); element.remove( true ); if ( firstChild ) { // Check the cached nodes for merging. firstChild.type == CKEDITOR.NODE_ELEMENT && firstChild.mergeSiblings(); if ( lastChild && !firstChild.equals( lastChild ) && lastChild.type == CKEDITOR.NODE_ELEMENT ) lastChild.mergeSiblings(); } } } } function getElement( style, targetDocument, element ) { var el, elementName = style.element; // The "*" element name will always be a span for this function. if ( elementName == '*' ) elementName = 'span'; // Create the element. el = new CKEDITOR.dom.element( elementName, targetDocument ); // #6226: attributes should be copied before the new ones are applied if ( element ) element.copyAttributes( el ); el = setupElement( el, style ); // Avoid ID duplication. if ( targetDocument.getCustomData( 'doc_processing_style' ) && el.hasAttribute( 'id' ) ) el.removeAttribute( 'id' ); else targetDocument.setCustomData( 'doc_processing_style', 1 ); return el; } function setupElement( el, style ) { var def = style._.definition, attributes = def.attributes, styles = CKEDITOR.style.getStyleText( def ); // Assign all defined attributes. if ( attributes ) { for ( var att in attributes ) el.setAttribute( att, attributes[ att ] ); } // Assign all defined styles. if ( styles ) el.setAttribute( 'style', styles ); return el; } function replaceVariables( list, variablesValues ) { for ( var item in list ) { list[ item ] = list[ item ].replace( varRegex, function( match, varName ) { return variablesValues[ varName ]; } ); } } // Returns an object that can be used for style matching comparison. // Attributes names and values are all lowercased, and the styles get // merged with the style attribute. function getAttributesForComparison( styleDefinition ) { // If we have already computed it, just return it. var attribs = styleDefinition._AC; if ( attribs ) return attribs; attribs = {}; var length = 0; // Loop through all defined attributes. var styleAttribs = styleDefinition.attributes; if ( styleAttribs ) { for ( var styleAtt in styleAttribs ) { length++; attribs[ styleAtt ] = styleAttribs[ styleAtt ]; } } // Includes the style definitions. var styleText = CKEDITOR.style.getStyleText( styleDefinition ); if ( styleText ) { if ( !attribs.style ) length++; attribs.style = styleText; } // Appends the "length" information to the object. attribs._length = length; // Return it, saving it to the next request. return ( styleDefinition._AC = attribs ); } // Get the the collection used to compare the elements and attributes, // defined in this style overrides, with other element. All information in // it is lowercased. // @param {CKEDITOR.style} style function getOverrides( style ) { if ( style._.overrides ) return style._.overrides; var overrides = ( style._.overrides = {} ), definition = style._.definition.overrides; if ( definition ) { // The override description can be a string, object or array. // Internally, well handle arrays only, so transform it if needed. if ( !CKEDITOR.tools.isArray( definition ) ) definition = [ definition ]; // Loop through all override definitions. for ( var i = 0; i < definition.length; i++ ) { var override = definition[ i ], elementName, overrideEl, attrs; // If can be a string with the element name. if ( typeof override == 'string' ) elementName = override.toLowerCase(); // Or an object. else { elementName = override.element ? override.element.toLowerCase() : style.element; attrs = override.attributes; } // We can have more than one override definition for the same // element name, so we attempt to simply append information to // it if it already exists. overrideEl = overrides[ elementName ] || ( overrides[ elementName ] = {} ); if ( attrs ) { // The returning attributes list is an array, because we // could have different override definitions for the same // attribute name. var overrideAttrs = ( overrideEl.attributes = overrideEl.attributes || [] ); for ( var attName in attrs ) { // Each item in the attributes array is also an array, // where [0] is the attribute name and [1] is the // override value. overrideAttrs.push( [ attName.toLowerCase(), attrs[ attName ] ] ); } } } } return overrides; } // Make the comparison of attribute value easier by standardizing it. function normalizeProperty( name, value, isStyle ) { var temp = new CKEDITOR.dom.element( 'span' ); temp[ isStyle ? 'setStyle' : 'setAttribute' ]( name, value ); return temp[ isStyle ? 'getStyle' : 'getAttribute' ]( name ); } // Compare two bunch of styles, with the speciality that value 'inherit' // is treated as a wildcard which will match any value. // @param {Object/String} source // @param {Object/String} target function compareCssText( source, target ) { if ( typeof source == 'string' ) source = CKEDITOR.tools.parseCssText( source ); if ( typeof target == 'string' ) target = CKEDITOR.tools.parseCssText( target, true ); for ( var name in source ) { if ( !( name in target && ( target[ name ] == source[ name ] || source[ name ] == 'inherit' || target[ name ] == 'inherit' ) ) ) return false; } return true; } function applyStyleOnSelection( selection, remove, editor ) { var doc = selection.document, ranges = selection.getRanges(), func = remove ? this.removeFromRange : this.applyToRange, range; var iterator = ranges.createIterator(); while ( ( range = iterator.getNextRange() ) ) func.call( this, range, editor ); selection.selectRanges( ranges ); doc.removeCustomData( 'doc_processing_style' ); } } )(); /** * Generic style command. It applies a specific style when executed. * * var boldStyle = new CKEDITOR.style( { element: 'strong' } ); * // Register the "bold" command, which applies the bold style. * editor.addCommand( 'bold', new CKEDITOR.styleCommand( boldStyle ) ); * * @class * @constructor Creates a styleCommand class instance. * @extends CKEDITOR.commandDefinition * @param {CKEDITOR.style} style The style to be applied when command is executed. * @param {Object} [ext] Additional command definition's properties. */ CKEDITOR.styleCommand = function( style, ext ) { this.style = style; this.allowedContent = style; this.requiredContent = style; CKEDITOR.tools.extend( this, ext, true ); }; /** * @param {CKEDITOR.editor} editor * @todo */ CKEDITOR.styleCommand.prototype.exec = function( editor ) { editor.focus(); if ( this.state == CKEDITOR.TRISTATE_OFF ) editor.applyStyle( this.style ); else if ( this.state == CKEDITOR.TRISTATE_ON ) editor.removeStyle( this.style ); }; /** * Manages styles registration and loading. See also {@link CKEDITOR.config#stylesSet}. * * // The set of styles for the Styles drop-down list. * CKEDITOR.stylesSet.add( 'default', [ * // Block Styles * { name: 'Blue Title', element: 'h3', styles: { 'color': 'Blue' } }, * { name: 'Red Title', element: 'h3', styles: { 'color': 'Red' } }, * * // Inline Styles * { name: 'Marker: Yellow', element: 'span', styles: { 'background-color': 'Yellow' } }, * { name: 'Marker: Green', element: 'span', styles: { 'background-color': 'Lime' } }, * * // Object Styles * { * name: 'Image on Left', * element: 'img', * attributes: { * style: 'padding: 5px; margin-right: 5px', * border: '2', * align: 'left' * } * } * ] ); * * @since 3.2 * @class * @singleton * @extends CKEDITOR.resourceManager */ CKEDITOR.stylesSet = new CKEDITOR.resourceManager( '', 'stylesSet' ); // Backward compatibility (#5025). CKEDITOR.addStylesSet = CKEDITOR.tools.bind( CKEDITOR.stylesSet.add, CKEDITOR.stylesSet ); CKEDITOR.loadStylesSet = function( name, url, callback ) { CKEDITOR.stylesSet.addExternal( name, url, '' ); CKEDITOR.stylesSet.load( name, callback ); }; CKEDITOR.tools.extend( CKEDITOR.editor.prototype, { /** * Registers a function to be called whenever the selection position changes in the * editing area. The current state is passed to the function. The possible * states are {@link CKEDITOR#TRISTATE_ON} and {@link CKEDITOR#TRISTATE_OFF}. * * // Create a style object for the element. * var style = new CKEDITOR.style( { element: 'b' } ); * var editor = CKEDITOR.instances.editor1; * editor.attachStyleStateChange( style, function( state ) { * if ( state == CKEDITOR.TRISTATE_ON ) * alert( 'The current state for the B element is ON' ); * else * alert( 'The current state for the B element is OFF' ); * } ); * * @member CKEDITOR.editor * @param {CKEDITOR.style} style The style to be watched. * @param {Function} callback The function to be called. */ attachStyleStateChange: function( style, callback ) { // Try to get the list of attached callbacks. var styleStateChangeCallbacks = this._.styleStateChangeCallbacks; // If it doesn't exist, it means this is the first call. So, let's create // all the structure to manage the style checks and the callback calls. if ( !styleStateChangeCallbacks ) { // Create the callbacks array. styleStateChangeCallbacks = this._.styleStateChangeCallbacks = []; // Attach to the selectionChange event, so we can check the styles at // that point. this.on( 'selectionChange', function( ev ) { // Loop throw all registered callbacks. for ( var i = 0; i < styleStateChangeCallbacks.length; i++ ) { var callback = styleStateChangeCallbacks[ i ]; // Check the current state for the style defined for that callback. var currentState = callback.style.checkActive( ev.data.path, this ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; // Call the callback function, passing the current state to it. callback.fn.call( this, currentState ); } } ); } // Save the callback info, so it can be checked on the next occurrence of // selectionChange. styleStateChangeCallbacks.push( { style: style, fn: callback } ); }, /** * Applies the style upon the editor's current selection. Shorthand for * {@link CKEDITOR.style#apply}. * * @member CKEDITOR.editor * @param {CKEDITOR.style} style */ applyStyle: function( style ) { style.apply( this ); }, /** * Removes the style from the editor's current selection. Shorthand for * {@link CKEDITOR.style#remove}. * * @member CKEDITOR.editor * @param {CKEDITOR.style} style */ removeStyle: function( style ) { style.remove( this ); }, /** * Gets the current `stylesSet` for this instance. * * editor.getStylesSet( function( stylesDefinitions ) {} ); * * See also {@link CKEDITOR.editor#stylesSet} event. * * @member CKEDITOR.editor * @param {Function} callback The function to be called with the styles data. */ getStylesSet: function( callback ) { if ( !this._.stylesDefinitions ) { var editor = this, // Respect the backwards compatible definition entry configStyleSet = editor.config.stylesCombo_stylesSet || editor.config.stylesSet; // The false value means that none styles should be loaded. if ( configStyleSet === false ) { callback( null ); return; } // #5352 Allow to define the styles directly in the config object if ( configStyleSet instanceof Array ) { editor._.stylesDefinitions = configStyleSet; callback( configStyleSet ); return; } // Default value is 'default'. if ( !configStyleSet ) configStyleSet = 'default'; var partsStylesSet = configStyleSet.split( ':' ), styleSetName = partsStylesSet[ 0 ], externalPath = partsStylesSet[ 1 ]; CKEDITOR.stylesSet.addExternal( styleSetName, externalPath ? partsStylesSet.slice( 1 ).join( ':' ) : CKEDITOR.getUrl( 'styles.js' ), '' ); CKEDITOR.stylesSet.load( styleSetName, function( stylesSet ) { editor._.stylesDefinitions = stylesSet[ styleSetName ]; callback( editor._.stylesDefinitions ); } ); } else { callback( this._.stylesDefinitions ); } } } ); /** * Indicates that fully selected read-only elements will be included when * applying the style (for inline styles only). * * @since 3.5 * @property {Boolean} [includeReadonly=false] * @member CKEDITOR.style */ /** * Indicates that any matches element of this style will be eventually removed * when calling {@link CKEDITOR.editor#removeStyle}. * * @since 4.0 * @property {Boolean} [alwaysRemoveElement=false] * @member CKEDITOR.style */ /** * Disables inline styling on read-only elements. * * @since 3.5 * @cfg {Boolean} [disableReadonlyStyling=false] * @member CKEDITOR.config */ /** * The "styles definition set" to use in the editor. They will be used in the * styles combo and the style selector of the div container. * * The styles may be defined in the page containing the editor, or can be * loaded on demand from an external file. In the second case, if this setting * contains only a name, the `styles.js` file will be loaded from the * CKEditor root folder (what ensures backward compatibility with CKEditor 4.0). * * Otherwise, this setting has the `name:url` syntax, making it * possible to set the URL from which loading the styles file. * Note that the `name` has to be equal to the name used in * {@link CKEDITOR.stylesSet#add} while registering styles set. * * **Note**: Since 4.1 it is possible to set `stylesSet` to `false` * to prevent loading any styles set. * * // Do not load any file. Styles set is empty. * config.stylesSet = false; * * // Load the 'mystyles' styles set from styles.js file. * config.stylesSet = 'mystyles'; * * // Load the 'mystyles' styles set from a relative URL. * config.stylesSet = 'mystyles:/editorstyles/styles.js'; * * // Load from a full URL. * config.stylesSet = 'mystyles:http://www.example.com/editorstyles/styles.js'; * * // Load from a list of definitions. * config.stylesSet = [ * { name: 'Strong Emphasis', element: 'strong' }, * { name: 'Emphasis', element: 'em' }, * ... * ]; * * @since 3.3 * @cfg {String/Array/Boolean} [stylesSet='default'] * @member CKEDITOR.config */