您现在的位置是:首页 > cms教程 > 帝国CMS教程帝国CMS教程

帝国cms编辑器ckeditor代码高亮前端显示mac风格

原创2025-09-18帝国CMS教程已有人查阅

导读前年写过一篇关于编辑器代码高亮的文章,很多人一知半解都在问,今天就详细的补充一下细节,到结束的完整过程,我尽量写的全面通俗易懂。已经写过的内容自行查看,只做补充。

前年写过一篇关于编辑器代码高亮的文章,很多人一知半解都在问,今天就详细的补充一下细节,到结束的完整过程,我尽量写的全面通俗易懂。已经写过的内容自行查看,只做补充。先看前面的内容不明白再回来看这篇教程。我们分三步走弄完整。
1,首先前提是你已经在编辑器添加了Code Snippet插件,如果没有?那么请查看这篇文章>>帝国cms编辑器ckeditor安装代码高亮插件
2,已经在编辑器添加了Code Snippet插件那么现在来进行>>帝国cms自带ckeditor编辑器代码高亮的方法codesnippet风格
3,添加行号,和头部圆圈等查看>>帝国cms高亮代码添加行号前端显示Mac OS风格
上次这篇教程很多人卡在了第二步、动手改造找到e/admin/ecmseditor/infoeditor/plugins/codesnippet目录下plugin.js修改很多人函数改错,
还有很多人卡在最后一步明明添加了

template: '<pre class="pure-highlightjs line-numbers ' + codeClass + '"><code class="' + codeClass + '"></code></pre>'

但是页面上只出现<pre class=" language-html">添加的pure-highlightjs line-numbers就是不出来,所以没有风格样式很难看。
问题在于模板中定义的pure-highlightjs和line-numbers类在upcast和downcast过程中没有被正确处理:
问题总结
upcast阶段:当从HTML内容转换为widget数据时,没有保留这些类
downcast阶段:当从widget数据转换为HTML输出时,这些类被完全移除
data更新阶段:当widget数据更新时,没有确保这些类被保留
针对上述问题我直接贴出我优化后的版本方便大家直接用或者参考,同时也做了一些安全措施。
更详细一点路径是:e\你的后台地址\ecmseditor\infoeditor\plugins\codesnippet\plugin.js,到这里这些问题都解决了,更新缓存可能无效,发布一篇文章,或者是之前的内容点修改然后发布然后查看就正常了,效果如代码号下面的。

/**
 * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */

 /**
 * @fileOverview Rich code snippets for CKEditor.
 */

'use strict';

( function() {
	var isBrowserSupported = !CKEDITOR.env.ie || CKEDITOR.env.version > 8;

	CKEDITOR.plugins.add( 'codesnippet', {
		requires: 'widget,dialog',
		lang: 'ar,bg,ca,cs,da,de,de-ch,el,en,en-gb,eo,es,et,eu,fa,fi,fr,fr-ca,gl,he,hr,hu,id,it,ja,km,ko,ku,lt,lv,nb,nl,no,pl,pt,pt-br,ro,ru,sk,sl,sq,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
		icons: 'codesnippet', // %REMOVE_LINE_CORE%
		hidpi: true, // %REMOVE_LINE_CORE%

		beforeInit: function( editor ) {
			editor._.codesnippet = {};

			/**
			 * Sets the custom syntax highlighter. See {@link CKEDITOR.plugins.codesnippet.highlighter}
			 * to learn how to register a custom highlighter.
			 *
			 * **Note**:
			 *
			 * * This method can only be called while initialising plugins (in one of
			 * the three callbacks).
			 * * This method is accessible through the `editor.plugins.codesnippet` namespace only.
			 *
			 * @since 4.4
			 * @member CKEDITOR.plugins.codesnippet
			 * @param {CKEDITOR.plugins.codesnippet.highlighter} highlighter
			 */
			this.setHighlighter = function( highlighter ) {
				editor._.codesnippet.highlighter = highlighter;

				var langs = editor._.codesnippet.langs =
					editor.config.codeSnippet_languages || highlighter.languages;

				// We might escape special regex chars below, but we expect that there
				// should be no crazy values used as lang keys.
				editor._.codesnippet.langsRegex = new RegExp( '(?:^|\\s)language-(' +
					CKEDITOR.tools.objectKeys( langs ).join( '|' ) + ')(?:\\s|$)' );
			};
		},

		onLoad: function() {
			CKEDITOR.dialog.add( 'codeSnippet', this.path + 'dialogs/codesnippet.js' );
		},

		init: function( editor ) {
			editor.ui.addButton && editor.ui.addButton( 'CodeSnippet', {
				label: editor.lang.codesnippet.button,
				command: 'codeSnippet',
				toolbar: 'insert,10'
			} );
		},

		afterInit: function( editor ) {
			var path = this.path;

			registerWidget( editor );

			// At the very end, if no custom highlighter was set so far (by plugin#setHighlighter)
			// we will set default one.
			if ( !editor._.codesnippet.highlighter ) {
				var hljsHighlighter = new CKEDITOR.plugins.codesnippet.highlighter( {
					languages: {
						apache: 'Apache',
						bash: 'Bash',
						coffeescript: 'CoffeeScript',
						cpp: 'C++',
						cs: 'C#',
						css: 'CSS',
						diff: 'Diff',
						html: 'HTML',
						http: 'HTTP',
						ini: 'INI',
						java: 'Java',
						javascript: 'JavaScript',
						json: 'JSON',
						makefile: 'Makefile',
						markdown: 'Markdown',
						nginx: 'Nginx',
						objectivec: 'Objective-C',
						perl: 'Perl',
						php: 'PHP',
						python: 'Python',
						ruby: 'Ruby',
						sql: 'SQL',
						vbscript: 'VBScript',
						xhtml: 'XHTML',
						xml: 'XML'
					},

					init: function( callback ) {
						var that = this;

						if ( isBrowserSupported ) {
							CKEDITOR.scriptLoader.load( path + 'lib/highlight/highlight.pack.js', function() {
								that.hljs = window.hljs;
								callback();
							} );
						}

						// Method is available only if wysiwygarea exists.
						if ( editor.addContentsCss ) {
							editor.addContentsCss( path + 'lib/highlight/styles/' + editor.config.codeSnippet_theme + '.css' );
						}
					},

					highlighter: function( code, language, callback ) {
						var highlighted = this.hljs.highlightAuto( code,
								this.hljs.getLanguage( language ) ? [ language ] : undefined );

						if ( highlighted )
							callback( highlighted.value );
					}
				} );

				this.setHighlighter( hljsHighlighter );
			}
		}
	} );

	/**
	 * Global helpers and classes of the Code Snippet plugin.
	 *
	 * For more information see the [Code Snippet Guide](#!/guide/dev_codesnippet).
	 *
	 * @class
	 * @singleton
	 */
	CKEDITOR.plugins.codesnippet = {
		highlighter: Highlighter
	};

	/**
	 * A Code Snippet highlighter. It can be set as a default highlighter
	 * using {@link CKEDITOR.plugins.codesnippet#setHighlighter}, for example:
	 *
	 *		// Create a new plugin which registers a custom code highlighter
	 *		// based on customEngine in order to replace the one that comes
	 *		// with the Code Snippet plugin.
	 *		CKEDITOR.plugins.add( 'myCustomHighlighter', {
	 *			afterInit: function( editor ) {
	 *				// Create a new instance of the highlighter.
	 *				var myHighlighter = new CKEDITOR.plugins.codesnippet.highlighter( {
	 *					init: function( ready ) {
	 *						// Asynchronous code to load resources and libraries for customEngine.
	 *						customEngine.loadResources( function() {
	 *							// Let the editor know that everything is ready.
	 *							ready();
	 *						} );
	 *					},
	 *					highlighter: function( code, language, callback ) {
	 *						// Let the customEngine highlight the code.
	 *						customEngine.highlight( code, language, function() {
	 *							callback( highlightedCode );
	 *						} );
	 *					}
	 *				} );
	 *
	 *				// Check how it performs.
	 *				myHighlighter.highlight( 'foo()', 'javascript', function( highlightedCode ) {
	 *					console.log( highlightedCode ); // -> <span class="pretty">foo()</span>
	 *				} );
	 *
	 *				// From now on, myHighlighter will be used as a Code Snippet
	 *				// highlighter, overwriting the default engine.
	 *				editor.plugins.codesnippet.setHighlighter( myHighlighter );
	 *			}
	 *		} );
	 *
	 * @since 4.4
	 * @class CKEDITOR.plugins.codesnippet.highlighter
	 * @extends CKEDITOR.plugins.codesnippet
	 * @param {Object} def Highlighter definition. See {@link #highlighter}, {@link #init} and {@link #languages}.
	 */
	function Highlighter( def ) {
		CKEDITOR.tools.extend( this, def );

		/**
		 * A queue of {@link #highlight} jobs to be
		 * done once the highlighter is {@link #ready}.
		 *
		 * @readonly
		 * @property {Array} [=[]]
		 */
		this.queue = [];

		// Async init – execute jobs when ready.
		if ( this.init ) {
			this.init( CKEDITOR.tools.bind( function() {
				// Execute pending jobs.
				var job;

				while ( ( job = this.queue.pop() ) )
					job.call( this );

				this.ready = true;
			}, this ) );
		} else {
			this.ready = true;
		}

		/**
		 * If specified, this function should asynchronously load highlighter-specific
		 * resources and execute `ready` when the highlighter is ready.
		 *
		 * @property {Function} [init]
		 * @param {Function} ready The function to be called once
		 * the highlighter is {@link #ready}.
		 */

		/**
		 * A function which highlights given plain text `code` in a given `language` and, once done,
		 * calls the `callback` function with highlighted markup as an argument.
		 *
		 * @property {Function} [highlighter]
		 * @param {String} code Code to be formatted.
		 * @param {String} lang Language to be used ({@link CKEDITOR.config#codeSnippet_languages}).
		 * @param {Function} callback Function which accepts highlighted String as an argument.
		 */

		/**
		 * Defines languages supported by the highlighter.
		 * They can be restricted with the {@link CKEDITOR.config#codeSnippet_languages} configuration option.
		 *
		 * **Note**: If {@link CKEDITOR.config#codeSnippet_languages} is set, **it will
		 * overwrite** the languages listed in `languages`.
		 *
		 *		languages: {
		 *			coffeescript: 'CoffeeScript',
		 *			cpp: 'C++',
		 *			cs: 'C#',
		 *			css: 'CSS'
		 *		}
		 *
		 * More information on how to change the list of languages is available
		 * in the [Code Snippet documentation](#!/guide/dev_codesnippet-section-changing-languages-list).
		 *
		 * @property {Object} languages
		 */

		/**
		 * A flag which indicates whether the highlighter is ready to do jobs
		 * from the {@link #queue}.
		 *
		 * @readonly
		 * @property {Boolean} ready
		 */
	}

	/**
	 * Executes the {@link #highlighter}. If the highlighter is not ready, it defers the job ({@link #queue})
	 * and executes it when the highlighter is {@link #ready}.
	 *
	 * @param {String} code Code to be formatted.
	 * @param {String} lang Language to be used ({@link CKEDITOR.config#codeSnippet_languages}).
	 * @param {Function} callback Function which accepts highlighted String as an argument.
	 */
	Highlighter.prototype.highlight = function() {
		var arg = arguments;

		// Highlighter is ready – do it now.
		if ( this.ready )
			this.highlighter.apply( this, arg );
		// Queue the job. It will be done once ready.
		else {
			this.queue.push( function() {
				this.highlighter.apply( this, arg );
			} );
		}
	};

// Encapsulates snippet widget registration code.
// @param {CKEDITOR.editor} editor
function registerWidget( editor ) {
	var codeClass = editor.config.codeSnippet_codeClass,
		newLineRegex = /\r?\n/g,
		textarea = new CKEDITOR.dom.element( 'textarea' ),
		lang = editor.lang.codesnippet;

	// 定义允许的额外类名(白名单方式)
	var allowedPreClasses = ['pure-highlightjs', 'line-numbers'];
	
	// 安全地添加ACF规则 - 只允许特定的类
	editor.widgets.add( 'codeSnippet', {
		// 明确指定允许的类,避免使用通配符
		allowedContent: {
			'pre': {
				classes: allowedPreClasses
			},
			'code': {
				classes: 'language-*',
				attributes: '!class' // 只允许class属性
			}
		},
		requiredContent: 'pre',
		styleableElements: 'pre',
		// 模板中使用安全的类名
		template: '<pre class="pure-highlightjs line-numbers ' + codeClass + '"><code class="' + codeClass + '"></code></pre>',
		dialog: 'codeSnippet',
		pathName: lang.pathName,
		mask: true,

		parts: {
			pre: 'pre',
			code: 'code'
		},

		highlight: function() {
			var that = this,
				widgetData = this.data,
				callback = function( formatted ) {
					that.parts.code.setHtml( isBrowserSupported ?
						formatted : formatted.replace( newLineRegex, '<br>' ) );
				};

			callback( CKEDITOR.tools.htmlEncode( widgetData.code ) );

			editor._.codesnippet.highlighter.highlight( widgetData.code, widgetData.lang, function( formatted ) {
				editor.fire( 'lockSnapshot' );
				callback( formatted );
				editor.fire( 'unlockSnapshot' );
			} );
		},

		data: function() {
			var newData = this.data,
				oldData = this.oldData;

			if ( newData.code )
				this.parts.code.setHtml( CKEDITOR.tools.htmlEncode( newData.code ) );

			if ( oldData && newData.lang != oldData.lang )
				this.parts.code.removeClass( 'language-' + oldData.lang );

			if ( newData.lang ) {
				this.parts.code.addClass( 'language-' + newData.lang );
				this.highlight();
			}

			this.oldData = CKEDITOR.tools.copy( newData );
		},

		// 安全的upcast方法
		upcast: function( el, data ) {
			if ( el.name != 'pre' )
				return;

			var childrenArray = getNonEmptyChildren( el ),
				code;

			if ( childrenArray.length != 1 || ( code = childrenArray[ 0 ] ).name != 'code' )
				return;

			if ( code.children.length != 1 || code.children[ 0 ].type != CKEDITOR.NODE_TEXT )
				return;

			// 安全地读取language-*类
			var matchResult = editor._.codesnippet.langsRegex.exec( code.attributes[ 'class' ] );
			if ( matchResult )
				data.lang = matchResult[ 1 ];

			textarea.setHtml( code.getHtml() );
			data.code = textarea.getValue();

			// 安全地处理pre元素的类
			var existingClasses = (el.attributes['class'] || '').split(/\s+/);
			var newClasses = [];
			
			// 只保留白名单中的类和codeClass
			for (var i = 0; i < existingClasses.length; i++) {
				var cls = existingClasses[i];
				if (allowedPreClasses.indexOf(cls) !== -1 || cls === codeClass) {
					newClasses.push(cls);
				}
			}
			
			// 添加必需的类(如果不存在)
			if (newClasses.indexOf('pure-highlightjs') === -1) {
				newClasses.push('pure-highlightjs');
			}
			if (newClasses.indexOf('line-numbers') === -1) {
				newClasses.push('line-numbers');
			}
			if (newClasses.indexOf(codeClass) === -1) {
				newClasses.push(codeClass);
			}
			
			el.attributes['class'] = newClasses.join(' ');
			code.addClass( codeClass );

			return el;
		},

		// 安全的downcast方法
		downcast: function( el ) {
			var code = el.getFirst( 'code' );

			code.children.length = 0;
			code.removeClass( codeClass );
			
			// 清理pre元素的类,只保留允许的类
			var existingClasses = (el.attributes['class'] || '').split(/\s+/);
			var newClasses = [];
			
			for (var i = 0; i < existingClasses.length; i++) {
				var cls = existingClasses[i];
				if (allowedPreClasses.indexOf(cls) !== -1) {
					newClasses.push(cls);
				}
			}
			
			el.attributes['class'] = newClasses.join(' ');
			code.add( new CKEDITOR.htmlParser.text( CKEDITOR.tools.htmlEncode( this.data.code ) ) );

			return el;
		}
	} );

		// Returns an **array** of child elements, with whitespace-only text nodes
		// filtered out.
		// @param {CKEDITOR.htmlParser.element} parentElement
		// @return Array - array of CKEDITOR.htmlParser.node
		var whitespaceOnlyRegex = /^[\s\n\r]*$/;

		function getNonEmptyChildren( parentElement ) {
			var ret = [],
				preChildrenList = parentElement.children,
				curNode;

			// Filter out empty text nodes.
			for ( var i = preChildrenList.length - 1; i >= 0; i-- ) {
				curNode = preChildrenList[ i ];

				if ( curNode.type != CKEDITOR.NODE_TEXT || !curNode.value.match( whitespaceOnlyRegex ) )
					ret.push( curNode );
			}

			return ret;
		}
	}
} )();

/**
 * A CSS class of the `<code>` element used internally for styling
 * (by default [highlight.js](http://highlightjs.org) themes, see
 * {@link CKEDITOR.config#codeSnippet_theme config.codeSnippet_theme}),
 * which means that it is **not present** in the editor output data.
 *
 *		// Changes the class to "myCustomClass".
 *		config.codeSnippet_codeClass = 'myCustomClass';
 *
 * **Note**: The class might need to be changed when you are using a custom
 * highlighter (the default is [highlight.js](http://highlightjs.org)).
 * See {@link CKEDITOR.plugins.codesnippet.highlighter} to read more.
 *
 * Read more in the [documentation](#!/guide/dev_codesnippet)
 * and see the [SDK sample](http://sdk.ckeditor.com/samples/codesnippet.html).
 *
 * @since 4.4
 * @cfg {String} [codeSnippet_codeClass='hljs']
 * @member CKEDITOR.config
 */
CKEDITOR.config.codeSnippet_codeClass = 'hljs';

/**
 * Restricts languages available in the "Code Snippet" dialog window.
 * An empty value is always added to the list.
 *
 * **Note**: If using a custom highlighter library (the default is [highlight.js](http://highlightjs.org)),
 * you may need to refer to external documentation to set `config.codeSnippet_languages` properly.
 *
 * Read more in the [documentation](#!/guide/dev_codesnippet-section-changing-supported-languages)
 * and see the [SDK sample](http://sdk.ckeditor.com/samples/codesnippet.html).
 *
 *		// Restricts languages to JavaScript and PHP.
 *		config.codeSnippet_languages = {
 *			javascript: 'JavaScript',
 *			php: 'PHP'
 *		};
 *
 * @since 4.4
 * @cfg {Object} [codeSnippet_languages=null]
 * @member CKEDITOR.config
 */

/**
 * A theme used to render code snippets. See [available themes](http://highlightjs.org/static/test.html).
 *
 * **Note**: This will only work with the default highlighter
 * ([highlight.js](http://highlightjs.org/static/test.html)).
 *
 * Read more in the [documentation](#!/guide/dev_codesnippet-section-changing-highlighter-theme)
 * and see the [SDK sample](http://sdk.ckeditor.com/samples/codesnippet.html).
 *
 *		// Changes the theme to "pojoaque".
 *		config.codeSnippet_theme = 'pojoaque';
 *
 * @since 4.4
 * @cfg {String} [codeSnippet_theme='default']
 * @member CKEDITOR.config
 */
CKEDITOR.config.codeSnippet_theme = 'default';

本文标签:

很赞哦! ()

相关源码

  • (自适应)大气网络公司工作室个人作品展示网站模板免费下载基于PbootCMS内核开发的响应式网站模板,为网络技术服务类企业打造,具备高度可定制性。通过简洁现代的设计语言展现企业专业形象,后台数据实时同步机制确保多终端内容一致性,查看源码
  • (自适应)黑色摄影作品工作室pbootcms模板网站源码下载为风景摄影、个人工作室打造的高端网站模板,基于PbootCMS开源内核开发,采用HTML5自适应架构,PC与移动端实时数据同步,适配各类拍摄作品展示需求。查看源码
  • (自适应响应式)蓝色环保机械设备网站pbootcms模板HTML5源码下载基于PbootCMS的生态环境技术展示平台,通过内容调整可应用于新能源设备、污水处理、空气净化等环保相关领域。设备参数采用对比表格展示,技术原理支持图文混排;查看源码
  • (自适应)品牌策划网络设计作品公司个人pbootcms网站源码下载本款基于PbootCMS开发的网站模板专为品牌策划、设计公司打造,特别适合展示创意作品、设计案例和企业服务。模板采用现代化设计风格查看源码
  • (自适应响应式)电子数码科技产品介绍带留言网站模板下载为电子产品企业设计的展示系统,集成智能产品对比器、参数规格表和展示模块。支持消费电子、智能设备等多级分类展示,内置产品技术参数数据库。查看源码
  • (自适应响应式)环保水净化处理设备阀门等网站源码下载基于PbootCMS内核开发的响应式模板,为水处理设备、空气净化器等环保企业设计,自适应手机端浏览。通过简洁高效的后台管理系统,助力环保科技企业快速建立专业在线展示平台。查看源码
分享笔记 (共有 篇笔记)
验证码: