【132】为UEditor1.4.3.3编写代码插件

项目简介

整个项目的代码在 https://gitee.com/zhangchao19890805/csdnBlog.git 中的blog132文件夹中。

写这个代码插件的原因:觉着UEditor默认自带的代码插件太丑了,所以决定自定义一个新的代码插件。

如何运行代码?

如果想要运行代码,需要Tomcat 8.5,把 zhangchao_ueditor1_4-utf8-jsp 文件夹放到 webapps 文件夹中。启动 Tomcat ,并打开浏览器,浏览器中的访问路径是:http://127.0.0.1/zhangchao_ueditor1_4-utf8-jsp

运行效果如图所示:

1.png

在这里插入图片描述

有时候你需要更改项目名称,此时你需要修改两处地方:

  1. index.html 中,开头部分 :
<script type="text/javascript" charset="utf-8">
        window.UEDITOR_HOME_URL = "/zhangchao_ueditor1_4-utf8-jsp/";    
</script>

window.UEDITOR_HOME_URL 修改成你需要的项目路径

  1. jsp/config.json 中修改 imageUrlPrefix 变量,以便图片可以正常上传后显示。代码如下:
"imageUrlPrefix": "/zhangchao_ueditor1_4-utf8-jsp", /* 图片访问路径前缀 */

自定义修改的部分

我集成了 highlight.js 插件。在 themes/iframe.css 中引入了样式文件。代码如下:

/*可以在这里添加你自己的css*/
@import "/zhangchao_ueditor1_4-utf8-jsp/zhangchao/default.min.css";

这个样式文件中既有 highlight.js 的部分,也有我自己添加的样式。

所有代码插件相关的代码都放到了 zhangchao 文件夹里面。按钮栏上最后一个按钮就是插入代码的按钮。这个按钮图片用的是 UEditor 自带的大图片中的一个小图标。

要想使用这个插件,index.html 中有例子:

<div>
    <h1>完整demo</h1>
    <script id="editor" type="text/plain" style="width:1024px;height:500px;"></script>
</div>

<!-- 中间的代码省略 -->

<script type="text/javascript">
    ZhangchaoUEditor("editor")
    // 后面的代码省略
</script>

其中 ZhangchaoUEditor 函数还有第二个可选参数 config 。这个最终会给UE.getEditor 函数作为第二个参数传入。用于更改UEditor 配置。

思路是用 UE.registerUI 注册新按钮。新按钮弹出的窗口中包含 iframe。iframe 中的网页是 code.html。用户不能在编辑器中直接修改代码块。如果想修改代码,要么双击代码块,要么选中代码块后点击插入代码按钮。

下面展示一下关键代码:

ZhangchaoUEditor.js 添加代码插件,并对编辑器已经添加进来的代码块加上事件响应

function ZhangchaoUEditor(scriptId, config){
    var stringUtils = {
        replaceAll: function (str, oldSubstr, newSbustr) {
            var arr = str.split(oldSubstr);
            if (!arr || arr.length == 0) {
                return str;
            }
            var r = arr[0];
            var i = 1;
            for (; i < arr.length; i++) {
    
                r = r + newSbustr + arr[i];
            }
            return r;
        }
    }
    
    function createDialog(editor, uiName){
        var dialog = new UE.ui.Dialog({
            //指定弹出层中页面的路径
            iframeUrl:'/zhangchao_ueditor1_4-utf8-jsp/zhangchao/code.html',
            //需要指定当前的编辑器实例
            editor:editor,
            //指定dialog的名字
            name:uiName,
            //dialog的标题
            title:"插入代码",
            //指定dialog的外围样式
            cssRules:"width:600px;height:300px;",
            //如果给出了buttons就代表dialog有确定和取消
            buttons:[
                {
                    className:'edui-okbutton',
                    label:'确定',
                    onclick:function () {
                        dialog.close(true);
                    }
                },
                {
                    className:'edui-cancelbutton',
                    label:'取消',
                    onclick:function () {
                        dialog.close(false);
                    }
                }
            ]
        });
        return dialog;
    }

    //实例化编辑器
    //建议使用工厂方法getEditor创建和引用编辑器实例,如果在某个闭包下引用该编辑器,直接调用UE.getEditor('editor')就能拿到相关的实例
    UE.registerUI('ZhangchaoUEditor',function(editor,uiName){
        //创建dialog,因为registerUI第一个参数,只有ZhangchaoUEditor,所以uiName必然是ZhangchaoUEditor
        var dialog = createDialog(editor, "ZhangchaoUEditor");
        //参考addCustomizeButton.js
        var btn = new UE.ui.Button({
            name:'dialogbutton' + uiName,
            title:'插入代码',
            //需要添加的额外样式,指定icon图标,这里默认使用一个重复的icon
            cssRules :'background-position: -440px -40px;',
            onclick:function () {
                //渲染dialog
                dialog.render();
                dialog.open();
            }
        });

        return btn;
    }/*index 指定添加到工具栏上的那个位置,默认时追加到最后,editorId 指定这个UI是那个编辑器实例上的,默认是页面上所有的编辑器都会添加这个按钮*/);
    
    var thisConfig = config || {};
    var instance = UE.getEditor(scriptId, thisConfig);
    
    // begin 绑定UEditor 的ready 事件
    instance.addListener('ready', function() {
        var iframeTag = instance.iframe;
        var win = iframeTag.contentWindow;
        var doc = win.document;

        // 所有代码片段去掉选中样式
        function clearAllCodeBorder(){
            var codeTagArr = doc.querySelectorAll("code.hljs");
            var i = 0;
            var length = codeTagArr.length;
            for(i = 0; i < length; i++){
                var node = codeTagArr[i]
                if (node.className && node.className.indexOf("hljs-zhangchao-codeborder") > -1) {
                    var cn = node.className;
                    cn = stringUtils.replaceAll(cn, "hljs-zhangchao-codeborder", "");
                    node.className = cn;
                }
            };
        }

        // begin 在getContent方法执行之前会触发该事件 
        instance.addListener("beforeGetContent", function(){
            clearAllCodeBorder();
        });
        // end   在getContent方法执行之前会触发该事件 

        // 文档内容 iframe 的点击事件。
        win.onclick = function(e){
            clearAllCodeBorder();
            //把被用户点击的code标签选中
            var node = e.target;
            while(node){
                if (node && node.tagName && node.tagName.toLowerCase() == "code" &&
                        node.className.indexOf("hljs") > -1) {
                    if (node.className.indexOf("hljs-zhangchao-codeborder") < 0) {
                        var cn = node.className;
                        // cn = stringUtils.trim(cn)
                        cn = cn.trim();
                        cn += " hljs-zhangchao-codeborder";
                        node.className = cn;
                    }
                    node = null;
                } else {
                    node = node.parentNode;
                }
            }
        }

        // 文档内容 iframe 的双击事件
        win.ondblclick = function(e){
            clearAllCodeBorder();
            //把被用户点击的code标签选中
            var node = e.target;
            while(node){
                if (node && node.tagName && node.tagName.toLowerCase() == "code" &&
                        node.className.indexOf("hljs") > -1) {
                    if (node.className.indexOf("hljs-zhangchao-codeborder") < 0) {
                        var cn = node.className;
                        // cn = stringUtils.trim(cn)
                        cn = cn.trim();
                        cn += " hljs-zhangchao-codeborder";
                        node.className = cn;
                    }

                    //创建dialog
                    var dialog = createDialog(instance, "ZhangchaoUEditor");
                    dialog.render();
                    dialog.open();
                    node = null;
                } else {
                    node = node.parentNode;
                }
            }
        }
        
        // 文档内容 iframe 的失去焦点事件。
        win.onblur = function(){
            clearAllCodeBorder();
        }

        // 禁止拖拽
        doc.ondragstart = function(e){
            e.preventDefault()
            return false;
        }
    });
    // end 绑定UEditor 的ready 事件
}

default.min.css只展示自己添加的代码


/* begin 代码块被点击后显示边框的样式 */
.hljs-zhangchao-codeborder {
    border: 10px solid #eee;
    border-radius: 8px;
    box-shadow: 0px 0px 10px 0px #ff0000;
    box-sizing: border-box;
}
/* end   代码块被点击后显示边框的样式*/

/* begin 去掉文字反选的背景颜色 */
.hljs::-moz-selection {
    /*针对Firefox*/
    background: rgba(0, 0, 0, 0);
    border: 5px solid red;
}

.hljs::selection {
    background: rgba(0, 0, 0, 0);
}

.hljs em::-moz-selection {
    background: rgba(0, 0, 0, 0);
}

.hljs em::selection {
    background: rgba(0, 0, 0, 0);
}

.hljs i::-moz-selection {
    background: rgba(0, 0, 0, 0);
}

.hljs i::selection {
    background: rgba(0, 0, 0, 0);
}

.hljs br::-moz-selection {
    background: rgba(0, 0, 0, 0);
}

.hljs br::selection {
    background: rgba(0, 0, 0, 0);
}

/* end 去掉文字反选的背景颜色 */

code.html 插入代码弹窗的内容:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <link rel="stylesheet" href="default.min.css" type="text/css" />
    <style>
        .zhangchao-code__textarea{
            width: 580px;
            height: 280px;
            background: #000000;
            color: #ffffff;
            display: block;
            margin: 10px auto 0px;
            resize: none;
            font-family: Consolas,Inconsolata,Courier,monospace,PingFang SC,Microsoft YaHei,sans-serif;
            border-radius: 4px;
            padding: 10px;
            box-sizing: border-box;
        }
</style>
    <script src="highlight.min.js"></script>
</head>

<body>
    <!-- <div class="content">  -->
    <textarea spellcheck="false" class="zhangchao-code__textarea" id="zhangchao-code__textarea"></textarea>
    <!-- </div> -->

    <pre style="display:none;"><code id="zhangchao-code__code"></code></pre>

    <!--页面中一定要引入internal.js为了能直接使用当前打开dialog的实例变量-->
    <!--internal.js默认是放到dialogs目录下的-->

    <script type="text/javascript" src="../dialogs/internal.js"></script>
    <script>
        // 为了防止和其他模块的函数重名,特别加入了此对象做命名空间用。
        // NS 表示命名空间 Name Space
        ZhangchaoNS = {}

        /**
         * 检测制定的节点是不是用来展示代码的code标签。
         */
         ZhangchaoNS.isCode = function (node) {
            if (node && node.tagName.toLowerCase() == "code") {
                var className = node.className;
                if (className.indexOf("hljs") > -1) {
                    return true;
                }
            }
            return false;
        }

        /**
         * 检测已有的代码片段有没有被选中
         */
         ZhangchaoNS.isSelected = function () {
            var node = editor.selection.getStart();
            if (ZhangchaoNS.isCode(node)) {
                return true;
            }
            var parent = domUtils.findParentByTagName(node, ["CODE"]);
            if (ZhangchaoNS.isCode(parent)) {
                return true;
            }
            return false;
        }

        /**
         * 如果已经选中代码片段,就返回对应的code标签
         */
         ZhangchaoNS.getCodeTag = function () {
            var node = editor.selection.getStart();
            if (ZhangchaoNS.isCode(node)) {
                return node;
            }
            var parent = domUtils.findParentByTagName(node, ["CODE"]);
            if (ZhangchaoNS.isCode(parent)) {
                return parent;
            }
            return null;
        }


        /**
         * 
         * 处理HTML字符串转义
         * Performs the following substring replacements
         * (to facilitate output to XML/HTML pages):
         * <p>
         * & -> &amp;
         * < -> &lt;
         * > -> &gt;
         * " -> &#034;
         * ' -> &#039;
         */
         ZhangchaoNS.escapeHtml = function (str) {
            // 把原始字符串拆分成单个字符,存储到strArr中。
            var strArr = new Array();
            var i = 0;
            for (i = 0; i < str.length; i++) {
                strArr.push(str.charAt(i));
            }
            // 转义字符。
            for (i = 0; i < strArr.length; i++) {
                var s = strArr[i];
                if (s == "&") {
                    strArr[i] = "&amp;";
                } else if (s == "<") {
                    strArr[i] = "&lt;";
                } else if (s == ">") {
                    strArr[i] = "&gt;";
                } else if (s == "\"") {
                    strArr[i] = "&#034;";
                } else if (s == "'") {
                    strArr[i] = "&#039;";
                }
            }
            // 返回新的字符串
            var newStr = "";
            for (i = 0; i < strArr.length; i++) {
                newStr += strArr[i];
            }
            return newStr;
        }

        /**
         * 
         * 把HTML转义字符还原成原来的字符
         * 
         * &#039;  ->   ' 
         * &#034;  ->   " 
         * &gt;    ->   > 
         * &lt;    ->   <
         * &amp;   ->   & 
         * 
         */
         ZhangchaoNS.reverseEscape = function (str) {
            var newStr = str;
            newStr = ZhangchaoNS.replaceAll(newStr, "&#039;", "'");
            newStr = ZhangchaoNS.replaceAll(newStr, "&#034;", "\"");
            newStr = ZhangchaoNS.replaceAll(newStr, "&gt;", ">");
            newStr = ZhangchaoNS.replaceAll(newStr, "&lt;", "<");
            newStr = ZhangchaoNS.replaceAll(newStr, "&amp;", "&");
            return newStr;
        }


        /**
         * 代码刷新样式
         */
         ZhangchaoNS.brush = function () {
            var textareaTag = document.getElementById("zhangchao-code__textarea");
            var code = document.getElementById("zhangchao-code__code");
            var htmlStr = textareaTag.value;
            htmlStr = ZhangchaoNS.escapeHtml(htmlStr);
            // 空格换成 &nbsp; tab换成4个&nbsp;
            htmlStr = ZhangchaoNS.replaceAll(htmlStr, " ", "&nbsp;");
            htmlStr = ZhangchaoNS.replaceAll(htmlStr, "\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
            code.innerHTML = htmlStr;
            hljs.highlightBlock(code);

        }

        /**
         * 把回车换行符CRLF 转换成 <br/>标签
         **/
         ZhangchaoNS.CRLF2Br = function (str) {
            // 把原始字符串拆分成单个字符,存储到strArr中。
            var strArr = new Array();
            var i = 0;
            for (i = 0; i < str.length; i++) {
                strArr.push(str.charAt(i));
            }
            // \r清除掉, \n 换成<br/>
            for (i = 0; i < strArr.length; i++) {
                var s = strArr[i];
                if (s == "\r") {
                    strArr[i] = "";
                } else if (s == "\n") {
                    strArr[i] = "<br/>";
                }
            }
            // 返回新的字符串
            var newStr = "";
            for (i = 0; i < strArr.length; i++) {
                newStr += strArr[i];
            }
            return newStr;
        }


        /**
         * 字符串全部替换
         */
         ZhangchaoNS.replaceAll = function (str, oldSubstr, newSbustr) {
            var arr = str.split(oldSubstr);
            if (!arr || arr.length == 0) {
                return str;
            }
            var r = arr[0];
            var i = 1;
            for (; i < arr.length; i++) {

                r = r + newSbustr + arr[i];
            }
            return r;
        }


        /**
         * 清理掉HTML标签。并且欢迎转义代码。
         * 顺序和代码高亮反着来。
         * 
         */
         ZhangchaoNS.clearHtml = function (oldHtml) {
            // <br> -> \n
            var str = oldHtml;
            str = ZhangchaoNS.replaceAll(str, "<br>", "\n");
            str = ZhangchaoNS.replaceAll(str, "<br/>", "\n");
            str = ZhangchaoNS.replaceAll(str, "<br />", "\n");
            // 清理掉HTML标签
            var reTag = /<(?:.|\s)*?>/g;
            str = str.replace(reTag, "");
            // &nbsp;  -> 英文空格
            str = ZhangchaoNS.replaceAll(str, "&nbsp;", " ");
            // 还原转义符号
            str = ZhangchaoNS.reverseEscape(str);
            return str;
        }

        // 如果光标在代码段中。替换原来的代码段。
        if (ZhangchaoNS.isSelected()) {
            var codeTag = ZhangchaoNS.getCodeTag();
            var oldHtml = codeTag.innerHTML;
            oldHtml = ZhangchaoNS.clearHtml(oldHtml);
            document.getElementById("zhangchao-code__textarea").value = oldHtml;
        }


        dialog.onok = function () {
            // 当前窗口中的CODE标签
            var currentCodeTag = document.getElementById("zhangchao-code__code");
            // 格式刷子。让代码高亮。
            ZhangchaoNS.brush();
            var htmlStr = ZhangchaoNS.CRLF2Br(currentCodeTag.innerHTML);
            // span标签换成 i 标签
            htmlStr = ZhangchaoNS.replaceAll(htmlStr, "<span class=", "<i style=\"font-style:normal;\" class=")
            htmlStr = ZhangchaoNS.replaceAll(htmlStr, "</span>", "</i>");
            if (ZhangchaoNS.isSelected()) {
                var codeTag = ZhangchaoNS.getCodeTag();
                codeTag.innerHTML = htmlStr;
            } else {
                htmlStr = "<p></p><code class=\"" + currentCodeTag.className + "\" contenteditable=\"false\" contentEditable=\"false\">" +
                        htmlStr + "</code><p></p>";
                editor.execCommand('inserthtml', htmlStr);
            }

            // begin 让代码块不可编辑
            var iframeTag = editor.iframe;
            var win = iframeTag.contentWindow;
            var doc = win.document;
            var codeTagArr = doc.querySelectorAll("code.hljs");
            var i = 0;
            var length = codeTagArr.length;
            for(i = 0; i < length; i++){
                var codeTag = codeTagArr[i];
                codeTag.contentEditable = false;
            }
            // end   让代码块不可编辑

        };
        dialog.oncancel = function () {
            // editor.execCommand('inserthtml', '<span>html code cancel</span>');
        };
    </script>
</body>

</html>
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值