代码显示全部都是白色,看起来比较费时间,而且也没有行数显示,于是查到了使用Google的Code-pretty这个插件进行上色。这样上色的好处是,原来的代码不需要加入一些颜色的代码,只需要在显示的时候JS加上去就好了。而且之前看到很多博客都使用Code-pretty来上色。
code-prettify
下载google/code-prettify
github:code-prettify
最简单的使用demo
html
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<link href="prettify.css" rel="stylesheet">
<script type="text/javascript" src="prettify.js"></script>
</head>
<body>
<pre>
<code class="prettyprint lang-js linenums">
var a = 0;
alert(a);
</code>
</pre>
<script type="text/javascript">
prettyPrint();
</script>
</body>
</html>引用CSS、JS,然后写一个prettyPrint()脚本。
修改wangEditor
问题变成了,如何让后台添加pre、code标签的时候,自动加入class标签,标记code的类型,方便上色。最开始,我以为code-prettify不支持pre里面含有code标签,而wangEditor的代码编辑器就是<pre><code>的格式,需要修改wangEditor源码。
注意到wangEditor的文件下面有src文件夹,因此可以尝试去修改src,然而我引用的是.min.js,所以新问题是如何在修改src后自动生成.min,js文件。
搭建windows下的gulp环境
注意到有一个不认识的gulpfile.js和package.json,于是百度了一下,查到gulpjs插件,是前端开发中对代码进行自动化构建的工具,能对JS/coffee/sass/less/html/image/css等文件进行测试、检查、合并、压缩、格式化,简化重复的工作。只要我搭建一样的环境,就能够修改wangEditor的源码了。
- 安装node.js

gulp是基于node.js开发的,所以需要装node.js,进入node.js下载界面,下载最新的稳定版本(2018年3月19日最新稳定版本8.10.0LTS),安装到任意位置。
shell
# 【查看node.js版本】
node -vshell
# 【查看npm版本】
npm -v- 安装cnpm
npm(node package manager)是nodejs的包管理器,由于源都在国外,因此淘宝开发了一个淘宝NPM镜像,每10分钟和官网同步一次。cnpm的使用方法和npm完全一样,只需要把npm替换成cnpm执行即可,win+R,输入cmd进入命令行,执行安装命令:
shell
npm install cnpm -g --registry=https://registry.npm.taobao.orgshell
# 【查看cnpm版本】
cnpm -v注意,安装完后要关闭cmd再重新打开cmd,否则直接使用cnpm会出错。
- 安装全局gulp
shell
# 【安装gulp】
cnpm install gulp -g
# 【查看gulp版本】
gulp -v- package.json文件
这一步wangEditor作者已经写好,不需要进行。
- 把gulp部署到wangEditor项目里面
除了安装全局的,还需要在wangEditor项目里面装一个专门的,注意要先进入项目目录。
shell
# 进入项目目录
G:
cd G:\test\plugin\wangEditor
# 安装依赖包
cnpm install --save-dev
# 安装gulp-less
cnpm install gulp-less --save-dev
# 安装gulp
cnpm install gulp --save-dev--save-dev的意思是只安装开发需要的包。完成后会在wangEditor目录下生成很深很深的node_modules文件夹。其中,less是一种CSS的生成器,wangEditor作者采用了这种生成器来写CSS。
- gulpfile.js文件
这一步wangEditor作者也写好了,不需要进行。
- 运行gulp
打开phpstorm(我是用phpstorm开发的),再打开wangEditor\gulpfile.js,在文件上右键,会发现多了一个Show Gulp Tasks。
点击后,就会弹出来相关的东西,右键可以运行一个默认程序。
这个程序就是更新程序,如果没有任何错误,会提示这些东西:
我们修改任意src里面的js、less文件,再运行这里的gulpfile.js就会执行更新,.min,js、.min,css就会更新。但其实,只需要修改后ctrl+s保存,这个gulpfile.js就会自动执行更新,很方便。
修改wangEditor源代码(错误尝试)
目标是把:
html
<pre>
<code>
xxxxx
</code>
</pre>修改为:
html
<pre class="prettyprint lang-xxxx linenums">
xxxxx
</pre>wangEditor的src文件夹目录结构是这样的: 
修改src/js/menus/code/index.js
这个js写了菜单code的所有代码,先对这个进行修改。代码思路很清晰:
text
* 如果选中了文字,用code进行包裹,效果类似:`我是被code包裹的文字`
* 否则,如果光标在<pre><code>里面,则说明要更新,调用更新的函数
* 否则,调用创建新的<pre><code>区域函数跟着这个思路修改就行。
- onclick里,修改编辑内容的入口参数,增加codeclass
注意到源代码:
js
if (this._active) {
// 选中状态,将编辑内容
this._createPanel($startElem.html())
} else {
// 未选中状态,将创建内容
this._createPanel()
}看到用了html()这个方法,估计这个$startElem就是指的<code><pre>的dom,于是类似地去写,把第3行改为:
js
this._createPanel($startElem.attr('class').split(' ')[1],$startElem.html())获取<pre class="prettyprint lang-xxxx linenums">中的lang-xxx(代码类别)。
完整代码:
js
onClick: function (e) {
const editor = this.editor
const $startElem = editor.selection.getSelectionStartElem()
const $endElem = editor.selection.getSelectionEndElem()
const isSeleEmpty = editor.selection.isSelectionEmpty()
const selectionText = editor.selection.getSelectionText()
let $code
if (!$startElem.equal($endElem)) {
// 跨元素选择,不做处理
editor.selection.restoreSelection()
return
}
if (!isSeleEmpty) {
// 选取不是空,用 <code> 包裹即可
$code = $(`<code>${selectionText}</code>`)
editor.cmd.do('insertElem', $code)
editor.selection.createRangeByElem($code, false)
editor.selection.restoreSelection()
return
}
// 选取是空,且没有夸元素选择,则插入 <pre><code></code></prev>
if (this._active) {
// 选中状态,将编辑内容
this._createPanel($startElem.attr('class').split(' ')[1],$startElem.html())
} else {
// 未选中状态,将创建内容
this._createPanel()
}
},- _createPanel里,增加select的内容
这里有个技巧,如果option里面,有selected=selected属性,就可以让它成为select的默认选中值。
js
_createPanel: function (codeclass,value) {
// value - 要编辑的内容
codeclass = codeclass || 'lang-c'
value = value || ''
const type = !value ? 'new' : 'edit'
const textId = getRandom('texxt')
const btnId = getRandom('btn')
const selectId = getRandom('select')
const sval = new Array('c','java','python','bash','a','b','d')
const stxt = new Array('C语言','Java','Python','Bash命令行','按键精灵','易语言','其他')
var sh = ''
for (var i=0;i<sval.length;i++){
if (codeclass === 'lang-'+sval[i]) {
sh += `<option value=lang-${sval[i]} selected=selected>${stxt[i]}</option>`
}else{
sh += `<option value=lang-${sval[i]}>${stxt[i]}</option>`
}
}
const panel = new Panel(this, {
width: 500,
// 一个 Panel 包含多个 tab
tabs: [
{
// 标题
title: '插入代码',
// 模板
tpl: `<div>
<div class="w-e-select-container">
<select id="${selectId}" value="${codeclass}">
${sh}
</select>
</div>
<textarea id="${textId}" style="height:145px;;">${value}</textarea>
<div class="w-e-button-container">
<button id="${btnId}" class="right">插入</button>
</div>
<div>`,
// 事件绑定
events: [
// 插入代码
{
selector: '#' + btnId,
type: 'click',
fn: () => {
const $text = $('#' + textId)
let text = $text.val() || $text.html()
text = replaceHtmlSymbol(text)
const $codeclass = $('#' + selectId)
let codeclass = $codeclass.val()
codeclass = replaceHtmlSymbol(codeclass)
if (type === 'new') {
// 新插入
this._insertCode(codeclass,text)
} else {
// 编辑更新
this._updateCode(codeclass,text)
}
// 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
return true
}
}
]
} // first tab end
] // tabs end
}) // new Panel end
// 显示 panel
panel.show()
// 记录属性
this.panel = panel
},- 修改_insertCode、_updateCode、_tryChangeActive
js
// 插入代码
_insertCode: function (codeclass,value) {
const editor = this.editor
editor.cmd.do('insertHTML', `<pre class="prettyprint ${codeclass} linenums">${value}</pre><p><br></p>`)
},
// 更新代码
_updateCode: function (codeclass,value) {
const editor = this.editor
const $selectionELem = editor.selection.getSelectionContainerElem()
if (!$selectionELem) {
return
}
$selectionELem.html(value)
$selectionELem.attr('class','prettyprint '+codeclass+ ' linenums')
editor.selection.restoreSelection()
//BWB:特别地恢复标签:
},
// 试图改变 active 状态
tryChangeActive: function (e) {
const editor = this.editor
const $elem = this.$elem
const $selectionELem = editor.selection.getSelectionContainerElem()
if (!$selectionELem) {
return
}
//const $parentElem = $selectionELem.parent()
//if ($selectionELem.getNodeName() === 'CODE' && $parentElem.getNodeName() === 'PRE') {
if ($selectionELem.getNodeName() === 'PRE') {
this._active = true
$elem.addClass('w-e-active')
} else {
this._active = false
$elem.removeClass('w-e-active')
}
}修改text/index.js
text控制了编辑器的操作,比如光标变化、按下键盘按键,因此需要对这里也进行修改,因为我们改变了标签。
将所有的:
js
if (selectionNodeName !== 'CODE' || parentNodeName !== 'PRE')改为:
js
if(selectionNodeName !== 'PRE')修改src/less/panel.less
在.w-e-panel-tab-content的.w-e-button-container:after后面增加select的CSS
less
.w-e-select-container{
width:120px;
display:block;
height:30px;
margin-bottom:10px;
select{
background: #f2f2f2;
border: none;
padding-left: 10px;
width: 100%;
height: 30px;
color:#242424;
display:block;
font-size:14px;
option{
background:#fff;
border: none;
padding:0px 2px;
}
}
}修改src/less/text.less
修改pre标签的CSS即可,这里由于我没有使用wangEditor自带的CSS,因此没有做。
修改wangEditor源代码(正确尝试)
其实呢,Code-prettify是支持<pre><code>标签的,但是class需要写在code里面:
html
<pre>
<code class="prettyprint lang-xxxx linenums">
....这种标签竟然是HTML5的,还是要多看readme文件呀。因此wangEditor只需要改src/js/menus/code/index.js即可,注意这里和前面会有一点不同:
js
onClick: function (e) {
const editor = this.editor
const $startElem = editor.selection.getSelectionStartElem()
const $endElem = editor.selection.getSelectionEndElem()
const isSeleEmpty = editor.selection.isSelectionEmpty()
const selectionText = editor.selection.getSelectionText()
let $code
if (!$startElem.equal($endElem)) {
// 跨元素选择,不做处理
editor.selection.restoreSelection()
return
}
if (!isSeleEmpty) {
// 选取不是空,用 <code> 包裹即可
$code = $(`<code>${selectionText}</code>`)
editor.cmd.do('insertElem', $code)
editor.selection.createRangeByElem($code, false)
editor.selection.restoreSelection()
return
}
// 选取是空,且没有夸元素选择,则插入 <pre><code></code></prev>
if (this._active) {
// 选中状态,将编辑内容
this._createPanel($startElem.attr('class').split(' ')[1],$startElem.html())
} else {
// 未选中状态,将创建内容
this._createPanel()
}
},
_createPanel: function (codeclass,value) {
// value - 要编辑的内容
codeclass = codeclass || 'lang-c'
value = value || ''
const type = !value ? 'new' : 'edit'
const textId = getRandom('texxt')
const btnId = getRandom('btn')
const selectId = getRandom('select')
const sval = new Array('c','java','PHP','python','matlab','html','css','js','bash','ajjl','eyy','txt')
const stxt = new Array('C/C++','Java','PHP','Python','Matlab','HTML','CSS','JavaScript','Bash命令行','按键精灵','易语言','普通文本')
var sh = ''
for (var i=0;i<sval.length;i++){
if (codeclass === 'lang-'+sval[i]) {
sh += `<option value=lang-${sval[i]} selected=selected>${stxt[i]}</option>`
}else{
sh += `<option value=lang-${sval[i]}>${stxt[i]}</option>`
}
}
const panel = new Panel(this, {
width: 500,
// 一个 Panel 包含多个 tab
tabs: [
{
// 标题
title: '插入代码',
// 模板
tpl: `<div>
<div class="w-e-select-container">
<select id="${selectId}" value="${codeclass}">
${sh}
</select>
</div>
<textarea id="${textId}" style="height:145px;;">${value}</textarea>
<div class="w-e-button-container">
<button id="${btnId}" class="right">插入</button>
</div>
<div>`,
// 事件绑定
events: [
// 插入代码
{
selector: '#' + btnId,
type: 'click',
fn: () => {
const $text = $('#' + textId)
let text = $text.val() || $text.html()
text = replaceHtmlSymbol(text)
const $codeclass = $('#' + selectId)
let codeclass = $codeclass.val()
codeclass = replaceHtmlSymbol(codeclass)
if (type === 'new') {
// 新插入
this._insertCode(codeclass,text)
} else {
// 编辑更新
this._updateCode(codeclass,text)
}
// 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
return true
}
}
]
} // first tab end
] // tabs end
}) // new Panel end
// 显示 panel
panel.show()
// 记录属性
this.panel = panel
},
// 插入代码
_insertCode: function (codeclass,value) {
const editor = this.editor
editor.cmd.do('insertHTML', `<pre><code class="prettyprint ${codeclass} linenums">${value}</code></pre><p><br></p>`)
},
// 更新代码
_updateCode: function (codeclass,value) {
const editor = this.editor
const $selectionELem = editor.selection.getSelectionContainerElem()
if (!$selectionELem) {
return
}
$selectionELem.html(value)
$selectionELem.attr('class','prettyprint '+codeclass+' linenums')
editor.selection.restoreSelection()
},
// 试图改变 active 状态
tryChangeActive: function (e) {
const editor = this.editor
const $elem = this.$elem
const $selectionELem = editor.selection.getSelectionContainerElem()
if (!$selectionELem) {
return
}
const $parentElem = $selectionELem.parent()
if ($selectionELem.getNodeName() === 'CODE' && $parentElem.getNodeName() === 'PRE') {
this._active = true
$elem.addClass('w-e-active')
} else {
this._active = false
$elem.removeClass('w-e-active')
}
}最终效果: 



粤公网安备44030602007943号