文章目录

前言

代码显示全部都是白色,看起来比较费时间,而且也没有行数显示,于是查到了使用Google的Code-pretty这个插件进行上色。这样上色的好处是,原来的代码不需要加入一些颜色的代码,只需要在显示的时候JS加上去就好了。而且之前看到很多博客都使用Code-pretty来上色。

一、code-prettify

下载code-prettify

github:code-prettify | 百度网盘(密码roay2018年6月22日版本

最简单的使用demo

<!DOCTYPE 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.jspackage.json,于是百度了一下,查到gulpjs插件,是前端开发中对代码进行自动化构建的工具,能对JS/coffee/sass/less/html/image/css等文件进行测试、检查、合并、压缩、格式化,简化重复的工作。只要我搭建一样的环境,就能够修改wangEditor的源码了。

1、安装node.js

gulp是基于node.js开发的,所以需要装node.js,进入node.js下载界面,下载最新的稳定版本(2018年3月19日最新稳定版本8.10.0LTS),安装到任意位置。

【查看node.js版本】

node -v

【查看npm版本】

npm -v

2、安装cnpm

npm(node package manager)是nodejs的包管理器,由于源都在国外,因此淘宝开发了一个淘宝NPM镜像,每10分钟和官网同步一次。cnpm的使用方法和npm完全一样,只需要把npm替换成cnpm执行即可,win+R,输入cmd进入命令行,执行安装命令:

npm install cnpm -g --registry=https://registry.npm.taobao.org

【查看cnpm版本】

cnpm -v

注意,安装完后要关闭cmd再重新打开cmd,否则直接使用cnpm会出错。

3、安装全局gulp

【安装gulp】

cnpm install gulp -g

【查看gulp版本】

gulp -v

4、package.json文件

这一步wangEditor作者已经写好,不需要进行。

5、把gulp部署到wangEditor项目里面

除了安装全局的,还需要在wangEditor项目里面装一个专门的,注意要先进入项目目录

//进入项目目录
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。

6、gulpfile.js文件

这一步wangEditor作者也写好了,不需要进行。

7、运行gulp

打开phpstorm(我是用phpstorm开发的),注意这里有个坑,如果发现phpstorm一直在进行目录扫描,电脑非常卡,原因和解决参看《phpstorm一直扫描目录导致电脑卡》,再打开wangEditor\gulpfile.js,在文件上右键,会发现多了一个Show Gulp Tasks

点击后,就会弹出来相关的东西,右键可以运行一个默认程序。

这个程序就是更新程序,如果没有任何错误,会提示这些东西:

我们修改任意src里面的js、less文件,再运行这里的gulpfile.js就会执行更新,.min,js、.min,css就会更新。但其实,只需要修改后ctrl+s保存,这个gulpfile.js就会自动执行更新,很方便。

修改wangEditor源代码(错误尝试)

目标是把:

<pre>
    <code>
          xxxxx
    </code>
</pre>

修改为:

<pre class="prettyprint lang-xxxx linenums">
     xxxxx
</pre>

wangEditor的src文件夹目录结构是这样的:

刚打开,就报一堆书写错误,提示“xxx definenation are not supported by current JavaScrpit version”,解决参看《phpstorm报JS书写错误》

1、修改src/js/menus/code/index.js

这个js写了菜单code的所有代码,先对这个进行修改。代码思路很清晰:

(a)如果选中了文字,用code进行包裹,效果类似:

我是被code包裹的文字

(b)否则,如果光标在<pre><code>里面,则说明要更新,调用更新的函数

(c)否则,调用创建新的<pre><code>区域函数

跟着这个思路修改就行。

(1)onclick里,修改编辑内容的入口参数,增加codeclass

注意到源代码:

if (this._active) {
            // 选中状态,将编辑内容
            this._createPanel($startElem.html())
        } else {
            // 未选中状态,将创建内容
            this._createPanel()
        }

看到用了html()这个方法,估计这个$startElem就是指的<code><pre>的dom,于是类似地去写,把第3行改为:

this._createPanel($startElem.attr('class').split(' ')[1],$startElem.html())

获取<pre class="prettyprint lang-xxxx linenums">中的lang-xxx(代码类别)。

完整代码:

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()
        }
    },


(2)_createPanel里,增加select的内容

这里有个技巧,如果option里面,有selected=selected属性,就可以让它成为select的默认选中值。

_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
    },

(3)修改_insertCode、_updateCode、_tryChangeActive

// 插入代码
    _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')
        }
    }

2、修改text/index.js

text控制了编辑器的操作,比如光标变化、按下键盘按键,因此需要对这里也进行修改,因为我们改变了标签。

将所有的:

if (selectionNodeName !== 'CODE' || parentNodeName !== 'PRE')

改为:

if(selectionNodeName !== 'PRE')

3、修改src/less/panel.less

在.w-e-panel-tab-content的.w-e-button-container:after后面增加select的CSS

.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;
                    }
                }

            }

4、修改src/less/text.less

修改pre标签的CSS即可,这里由于我没有使用wangEditor自带的CSS,因此没有做。

修改wangEditor源代码(正确尝试)

其实呢,Code-prettify是支持<pre><code>标签的,但是class需要写在code里面:

<pre>
     <code class="prettyprint lang-xxxx linenums">
     ....

这种标签竟然是HTML5的,还是要多看readme文件呀,《Code-Prettify的Readme文件翻译》

因此wangEditor只需要改src/js/menus/code/index.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')
        }
    }

最终效果:

三、修改已有的文章

已经写了很多文章了,都没有加入class标签,因此需要修改数据库:

这里为了匹配<pre><code></code><pre>标签,费了很大的劲,有很多坑,参考《PHP匹配HTML标签》

参考网页

1、《用谷歌代码高亮插件code-prettify打造属于你的漂亮博文》

2、《windows下Gulp入门详细教程》

3、《npm install --save/--save-dev的区别》

4、《关于php中正则匹配包括换行符在内的任意字符的问题总结》

5、《正则表达式-问号的四种用法》


转载请注明出处http://www.bewindoweb.com/172.html | 三颗豆子
分享许可方式知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议
重大发现:转载注明原文网址的同学刚买了彩票就中奖,刚写完代码就跑通,刚转身就遇到了真爱。
你可能还会喜欢
具体问题具体杠