真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

解決自媒體一鍵多平臺(tái)發(fā)布,從零開發(fā)Markdown編輯器(一)

前言

在這個(gè)人人都是自媒體的時(shí)代,為了擴(kuò)大個(gè)人影響力同時(shí)預(yù)防文章被盜版至其他平臺(tái),多平臺(tái)發(fā)布文章就成了創(chuàng)作者們的一大痛點(diǎn),為了解決這一痛點(diǎn)就需要將文章的編輯到發(fā)布無(wú)縫集成。
現(xiàn)在要實(shí)現(xiàn)這一功能,開發(fā)一個(gè)完全可控的Markdown編輯器就是第一步。
本文源碼已上傳Github:Github hxsfx MarkdownEditor

創(chuàng)新互聯(lián)主營(yíng)溆浦網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,重慶APP開發(fā),溆浦h5成都微信小程序搭建,溆浦網(wǎng)站營(yíng)銷推廣歡迎溆浦等地區(qū)企業(yè)咨詢

界面草圖

技術(shù)選型

考慮到編輯器解析渲染放在前端更合適,采用了HTML+JS+CSS實(shí)現(xiàn)Markdown編輯器模塊。

功能演示及代碼分享

各位小伙伴可以訪問在線演示地址:https://md.hxsfx.com/

1、標(biāo)題語(yǔ)法

  • 功能演示
  • 代碼分享
var h4_start = "#### ";
var h3_start = "### ";
var h2_start = "## ";
var h1_start = "# ";
if (textContent.startsWith(h4_start)) {
    html = textContent.substring(h4_start.length, textContent.length);
    tagName = "h4";
}//四級(jí)標(biāo)題
else if (textContent.startsWith(h3_start)) {
    html = textContent.substring(h3_start.length, textContent.length);
    tagName = "h3";
}//三級(jí)標(biāo)題
else if (textContent.startsWith(h2_start)) {
    html = textContent.substring(h2_start.length, textContent.length);
    tagName = "h2";
}//二級(jí)標(biāo)題
else if (textContent.startsWith(h1_start)) {
    html = textContent.substring(h1_start.length, textContent.length);
    tagName = "h1";
}//一級(jí)標(biāo)題

2、強(qiáng)調(diào)語(yǔ)法

  • 功能演示
  • 代碼分享
//提取強(qiáng)調(diào)語(yǔ)法
function ExtractEmphasisGrammar(html) {
    //粗斜體
    var html = html.replace(/\*\*\*.*?\*\*\*/g, function (strongAndem_val) {
        var _strongAndem_val = strongAndem_val.substring(3, strongAndem_val.length - 3)
        return CreatePreviewSectionHTML("strong,em", _strongAndem_val);
    });
    //粗體
    var html = html.replace(/\*\*.*?\*\*/g, function (strong_val) {
        var _strong_val = strong_val.substring(2, strong_val.length - 2);
        return CreatePreviewSectionHTML("strong", _strong_val);
    });
    //斜體
    var html = html.replace(/\*.*?\*/g, function (em_val) {
        var _em_val = em_val.substring(1, em_val.length - 1);
        return CreatePreviewSectionHTML("em", _em_val);
    });
    return html;
}
//根據(jù)標(biāo)簽和內(nèi)部?jī)?nèi)容生成預(yù)覽區(qū)域內(nèi)行塊html
function CreatePreviewSectionHTML(tagName, innerHTML) {
    var html = innerHTML;
    if (tagName == "code") {
        html = html.replace(/(\s)/g, " ");//.replace("/ /g"," ");
    }//將空格替換為轉(zhuǎn)義字符防止多個(gè)空格在html顯示為一個(gè)

    if (tagName == "" || tagName == null || tagName == undefined) {
    } else if (tagName == "hr") {
        html = "
"; } else { var start_tagName = ""; var end_tagName = ""; var tagNameSplit = tagName.split(","); for (var i = 0; i < tagNameSplit.length; i++) { start_tagName += "<" + tagNameSplit[i] + ">"; end_tagName = end_tagName + ""; } html = start_tagName + html + end_tagName; } return html; }

3、引用語(yǔ)法

  • 功能演示
  • 代碼分享
var blockquote_start = ">";
if (textContent.startsWith(blockquote_start)) {
    isBlockquote = true;
    html = textContent.substring(blockquote_start.length, textContent.length);
    tagName = "blockquote";
}//引用

4、列表語(yǔ)法

  • 功能演示

  • 代碼分享

var olli_pattern = /^ {0,3}[1-9]*\. /; //有序列表正則表達(dá)式
var ulli_pattern = /^[ ]{0,3}(\* |- |\+ )/; //有序列表正則表達(dá)式

if (olli_pattern.test(textContent)) {
    //有序列表項(xiàng)
    if (textContent.startsWith(" ")) {
        isOL2 = true;
    } else {
        isOL = true;
    }
    html = textContent.replace(olli_pattern, "");
    tagName = "ol";
}
else if (ulli_pattern.test(textContent)) {
    //無(wú)序列表項(xiàng)
    if (textContent.startsWith(" ")) {
        isUL2 = true;
    } else {
        isUL = true;
    }
    html = textContent.replace(ulli_pattern, "");
    tagName = "ul";
}
//提取列表語(yǔ)法
function ExtractList(analysisResult, prevAnalysisResult) {
    var isExtractTable = true;
    if (prevAnalysisResult == null || prevAnalysisResult.ListInfo == null) {
        isExtractTable = CreateListInfo(analysisResult, isExtractTable);
    }//沒有上一行 或者 上一行不是列表
    else {
        var liHTML = analysisResult.AnalysisHTML;
        if ((prevAnalysisResult.IsOL && analysisResult.IsOL) ||
            (prevAnalysisResult.IsUL && analysisResult.IsUL)) {
            //接著上一行繼續(xù)
            analysisResult.ListInfo = prevAnalysisResult.ListInfo;
            analysisResult.ListInfo.LiInfoArray.push({ LiHtml: liHTML, LiInfoArray: [] });
            analysisResult.ListInfo.IsMergePrevHTML = true;
        }//同為一級(jí)標(biāo)題且標(biāo)簽相同
        else if ((prevAnalysisResult.IsOL && analysisResult.IsUL) ||
            (prevAnalysisResult.IsUL && analysisResult.IsOL)) {
            isExtractTable = CreateListInfo(analysisResult, isExtractTable);
        }
        else if ((prevAnalysisResult.IsOL && (analysisResult.IsOL2 || analysisResult.IsUL2)) ||
            (prevAnalysisResult.IsUL && (analysisResult.IsOL2 || analysisResult.IsUL2))) {
            var currentFisrtLevelLiInfoArray = prevAnalysisResult.ListInfo.LiInfoArray.slice(-1)[0];
            var secondLevelLiInfoArray = currentFisrtLevelLiInfoArray.LiInfoArray;
            var isFindPeer = false;
            for (var i = 0; i < secondLevelLiInfoArray.length; i++) {
                var _secondLevelLiInfo = secondLevelLiInfoArray[i];
                if (_secondLevelLiInfo.TagName == analysisResult.TagName) {
                    isFindPeer = true;
                    _secondLevelLiInfo.LiHtmlList.push(liHTML);
                }
            }
            if (!isFindPeer) {
                secondLevelLiInfoArray.push({ TagName: analysisResult.TagName, LiHtmlList: [liHTML] });
            }
            analysisResult.ListInfo = prevAnalysisResult.ListInfo;
            analysisResult.ListInfo.IsMergePrevHTML = true;
        }
        else if ((prevAnalysisResult.IsOL2 && (analysisResult.IsOL || analysisResult.IsUL)) ||
            (prevAnalysisResult.IsUL2 && (analysisResult.IsOL || analysisResult.IsUL))) {
            if (prevAnalysisResult.ListInfo.TagName == analysisResult.TagName) {
                prevAnalysisResult.ListInfo.LiInfoArray.push({ LiHtml: liHTML, LiInfoArray: [] });
                analysisResult.ListInfo = prevAnalysisResult.ListInfo;
                analysisResult.ListInfo.IsMergePrevHTML = true;
            }//此二級(jí)有序項(xiàng)對(duì)應(yīng)一級(jí)項(xiàng)的列表項(xiàng)跟當(dāng)前一致且為一級(jí)
            else {
                isExtractTable = CreateListInfo(analysisResult, isExtractTable);
            }//當(dāng)前一級(jí)與上一個(gè)一級(jí)標(biāo)簽不同無(wú)法合并,再生成一個(gè)新的
        }
        else if ((prevAnalysisResult.IsOL2 && analysisResult.IsOL2) ||
            (prevAnalysisResult.IsUL2 && analysisResult.IsUL2)) {
            var currentFisrtLevelLiInfoArray = prevAnalysisResult.ListInfo.LiInfoArray.slice(-1)[0];
            var secondLevelLiInfoArray = currentFisrtLevelLiInfoArray.LiInfoArray.slice(-1)[0];
            secondLevelLiInfoArray.LiHtmlList.push(liHTML);
            analysisResult.ListInfo = prevAnalysisResult.ListInfo;
            analysisResult.ListInfo.IsMergePrevHTML = true;
        }//同為二級(jí)同標(biāo)簽,直接追加
        else if ((prevAnalysisResult.IsOL2 && analysisResult.IsUL2) ||
            (prevAnalysisResult.IsUL2 && analysisResult.IsOL2)) {
            var currentFisrtLevelLiInfoArray = prevAnalysisResult.ListInfo.LiInfoArray.slice(-1)[0];
            //這兒是要新添加不同的二級(jí)標(biāo)簽跟上面不一樣所以不要屬性
            currentFisrtLevelLiInfoArray.LiInfoArray.push({ TagName: analysisResult.TagName, LiHtmlList: [liHTML] });
            analysisResult.ListInfo = prevAnalysisResult.ListInfo;
            analysisResult.ListInfo.IsMergePrevHTML = true;
        }//雖然同時(shí)二級(jí),但標(biāo)簽不同,需生成新的二級(jí)
    }
    return isExtractTable;
}
function CreateListInfo(analysisResult, isExtractTable) {
    if (analysisResult.IsUL || analysisResult.IsOL) {
        analysisResult.ListInfo = {
            IsMergePrevHTML: false,
            TagName: analysisResult.TagName,
            LiInfoArray: [
                {
                    LiHtml: analysisResult.AnalysisHTML,
                    LiInfoArray: []
                    //LiInfoArray: [{
                    // TagName: "",
                    // LiHtmlList: []
                    //}],
                },
            ]
        };
    } //識(shí)別為一級(jí)無(wú)序列表項(xiàng) 或者 識(shí)別為一級(jí)有序列表項(xiàng)
    else {
        isExtractTable = false;
    } //第一行識(shí)別為二級(jí)列表項(xiàng),做普通處理
    return isExtractTable;
}

5、代碼塊語(yǔ)法

  • 功能演示
  • 代碼分享
var isCodeBlock = false;
if (analysisResult.IsCodeBlock) {
    isCodeBlock = analysisResult.IsCodeBlock;
    //當(dāng)前是代碼塊結(jié)束或開始
    if (prevAnalysisResult == null) {
        //代碼塊開始
        analysisResult.TextContent = "";
        previewHTMLArray.push(CreatePreviewSectionHTML("pre", analysisResult.TextContent));
    }
    else {
        if (prevAnalysisResult.IsCodeBlock) {
            //結(jié)束
            analysisResult.IsCodeBlock = false;
            analysisResult.TextContent = prevAnalysisResult.TextContent;
            previewHTMLArray[previewHTMLArray.length - 1] = CreatePreviewSectionHTML("pre", analysisResult.TextContent);
        }
        else {
            //開始
            analysisResult.TextContent = "";
            previewHTMLArray.push(CreatePreviewSectionHTML("pre", analysisResult.TextContent));
        }
    }
}

if (prevAnalysisResult.IsCodeBlock) {
    //當(dāng)前在代碼塊內(nèi)
    isCodeBlock = true;
    analysisResult.IsCodeBlock = true;
    var _textContent = "";
    if (analysisResult.TextContent != "" && analysisResult.TextContent != null) {
        _textContent = CreatePreviewSectionHTML("code", analysisResult.TextContent);
    }
    analysisResult.TextContent = prevAnalysisResult.TextContent + _textContent;
    previewHTMLArray[previewHTMLArray.length - 1] = CreatePreviewSectionHTML("pre", analysisResult.TextContent);
}

6、分隔線語(yǔ)法

  • 功能演示
  • 代碼分享
var separator_pattern = /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/;//分隔線正則表達(dá)式
if (separator_pattern.test(textContent)) {
    tagName = "hr";
}//分隔線

7、鏈接語(yǔ)法

  • 功能演示
  • 代碼分享
var def_pattern = /^ {0,3}\[(label)\]: *\n? *]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/;
if (def_pattern.test(textContent)) {
    html = "textContent";
    tagName = "a";
}//鏈接
//提取鏈接和圖片語(yǔ)法
function ExtractLink(html) {
    var link_pattern = /!?\[(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?\]\(\s*(?:<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)(?:\s+(?:"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/g;
    if (link_pattern.test(html)) {
        var link_pattern2 = /^!?\[((?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;
        html = html.replace(link_pattern, function (val) {
            var getVals = link_pattern2.exec(val);
            var text = getVals[1];
            var href = getVals[2];
            href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
            href = href.replace(//, "***");
            href = href.replace(/<\/strong><\/em>/, "***");
            href = href.replace(//, "**");
            href = href.replace(/<\/strong>/, "**");
            href = href.replace(//, "*");
            href = href.replace(/<\/em>/, "*");
            var title = getVals[3];
            title = getVals[3] ? getVals[3].slice(1, -1) : '';
            title = title.replace(//, "***");
            title = title.replace(/<\/strong><\/em>/, "***");
            title = title.replace(//, "**");
            title = title.replace(/<\/strong>/, "**");
            title = title.replace(//, "*");
            title = title.replace(/<\/em>/, "*");
            if (getVals[0].startsWith("!")) {
                return "\""";
            } else {
                var text = ExtractLink(text);
                return "" + text + "";
            }
        });
    }
    return html;
}

8、圖片語(yǔ)法

  • 功能演示
  • 代碼分享
    參考第7點(diǎn)的鏈接語(yǔ)法

9、表格語(yǔ)法

  • 功能演示
  • 代碼分享
var table_tag_pattern = /^[ ]{0,3}((\|[ ]*?(?:[:]{0,1}- *){3,}[:]{0,1}[ ]*)+\|){1,}[ | ]{0,}$/;//表格出現(xiàn)的標(biāo)識(shí)
if (table_tag_pattern.test(textContent)) {
    html = textContent;
}//表格
//提取表格語(yǔ)法
function ExtractTable(analysisResult, prevAnalysisResult) {
    var isExtractTable = true;
    if (prevAnalysisResult == null) {
        isExtractTable = false;
    }//但因?yàn)槭堑谝恍?,所以不能轉(zhuǎn)為表格
    else {
        //把當(dāng)前內(nèi)容根據(jù)|分隔進(jìn)行拆分
        var currentSplitArray = analysisResult.AnalysisHTML.split('|');
        if (analysisResult.IsTable && prevAnalysisResult.TableInfo == null) {
            if (/^[ ]{0,3}\|/.test(prevAnalysisResult.AnalysisHTML)) {
                var headerTHTextArray = prevAnalysisResult.AnalysisHTML.split('|');
                analysisResult.TableInfo = {};
                analysisResult.TableInfo.TextAlignArray = new Array();
                var columnCount = currentSplitArray.length - 2;
                if (columnCount > headerTHTextArray.length - 2) {
                    columnCount = headerTHTextArray.length - 2;
                }
                var thHTML = "";
                for (var i = 0; i < columnCount; i++) {
                    var _tagText = currentSplitArray[i + 1].trim();
                    var textAlign = "";
                    if (_tagText.startsWith("-") && _tagText.endsWith("-")) {
                        textAlign = ""
                    } else if (_tagText.startsWith(":") && _tagText.endsWith(":")) {
                        textAlign = "center";
                    } else if (_tagText.startsWith(":")) {
                        textAlign = "left";
                    } else if (_tagText.endsWith(":")) {
                        textAlign = "right";
                    }
                    var textAlignStyle = ""
                    if (textAlign != "") {
                        textAlignStyle = "style=\"text-align:" + textAlign + "\";";
                    }
                    analysisResult.TableInfo.TextAlignArray.push(textAlignStyle);
                    thHTML += "" + headerTHTextArray[i + 1] + ""
                }
                analysisResult.TableInfo.THeadHTML = "" + thHTML + "";
                analysisResult.TableInfo.TBodyHTMLArray = new Array();
            }//檢查上一行是不是符合做表頭內(nèi)容文本的格式條件
            else {
                isExtractTable = false;
            }//此行雖然是表格標(biāo)識(shí)出現(xiàn),但因?yàn)樯弦恍懈袷讲粚?duì),不滿足生成表格的條件
        }//當(dāng)表頭還未生成的時(shí)候先生成表頭
        else if (prevAnalysisResult.TableInfo != null) {
            analysisResult.TableInfo = prevAnalysisResult.TableInfo;
            var tdHtml = "";
            for (var i = 0; i < analysisResult.TableInfo.TextAlignArray.length; i++) {
                var text = currentSplitArray[i + 1];
                if (text === undefined) {
                    text = "";
                }
                tdHtml += "" + text + ""
            }
            analysisResult.TableInfo.TBodyHTMLArray.push("" + tdHtml + "");
        }//當(dāng)表頭生成后生成表體
        else {
            isExtractTable = false;
        }
    }
    if (isExtractTable == false) {
        analysisResult.TableInfo = null;
    }
    return isExtractTable;
}

10、其他功能

  • 功能演示
  • 代碼分享
//通過使用localStorage實(shí)現(xiàn)本地緩存
//緩存輸入至localStorage
function LocalStorageInputMD(mdInputHTML){
    localStorage.setItem('ls_mdInput',mdInputHTML);
}
//實(shí)現(xiàn)輸入內(nèi)容導(dǎo)出
function exportRaw(name, data) {
    var urlObject = window.URL || window.webkitURL || window;
    var export_blob = new Blob([data]);
    var save_link = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
    save_link.href = urlObject.createObjectURL(export_blob);
    save_link.download = name;
    var ev = document.createEvent("MouseEvents");
    ev.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    save_link.dispatchEvent(ev);
}
//通過將輸入緩存至EditorElementRecordHistoryArray中,實(shí)現(xiàn)撤銷和重做功能
//點(diǎn)擊撤銷按鈕
UndoButtonElement.onclick = function () {
    //document.execCommand("Undo");
    if (EditorElementRecordHistoryArray === undefined ||
        EditorElementRecordHistoryArray == null) {
        EditorElementRecordHistoryArray = new Array();
    }
    else {
        if (EditorElementRecordHistoryArray.length >= 2) {
            if (EditorElementRecordHistoryArray.length <= 2) {
                UndoButtonElement.className += " disable";
            }//當(dāng)記錄元素小于等于1個(gè),就可以撤銷了
            if (EditorElementRecordHistoryArray_undo === undefined ||
                EditorElementRecordHistoryArray_undo == null) {
                EditorElementRecordHistoryArray_undo = new Array();
            }//先判斷重做隊(duì)列是否為null
            RedoButtonElement.className = RedoButtonElement.className.replace("disable", "");//將重做按鈕點(diǎn)亮
            EditorElementRecordHistoryArray_undo.push(EditorElementRecordHistoryArray.pop());//將保存的歷史記錄最后一條取出來(lái)(這是當(dāng)前條,取出來(lái)要放到重做隊(duì)列)
            editDivElement.innerHTML = EditorElementRecordHistoryArray.pop();//將當(dāng)前條的前一條取出來(lái)
            Preview();//渲染
        }
    }
}
//點(diǎn)擊重做按鈕
RedoButtonElement.onclick = function () {
    //document.execCommand("Redo");
    if (EditorElementRecordHistoryArray_undo !== undefined &&
        EditorElementRecordHistoryArray_undo != null &&
        EditorElementRecordHistoryArray_undo.length > 0) {
        editDivElement.innerHTML = EditorElementRecordHistoryArray_undo.pop();
        Preview();//渲染
    }
    if (EditorElementRecordHistoryArray_undo === undefined ||
        EditorElementRecordHistoryArray_undo == null ||
        EditorElementRecordHistoryArray_undo.length <= 0) {
        if (RedoButtonElement.className.indexOf("disable") < 0) {
            RedoButtonElement.className += " disable";
        }
    }
}
//只要有輸入動(dòng)作就清空重做記錄
function ClearUndo() {
    EditorElementRecordHistoryArray_undo = new Array();
    if (RedoButtonElement.className.indexOf("disable") < 0) {
        RedoButtonElement.className += " disable";
    }
}

寫在最后

本次開發(fā)代碼質(zhì)量只能說(shuō)有手就行哈哈。接下來(lái)除了完成新功能的添加,也會(huì)預(yù)留一部分的時(shí)間來(lái)重構(gòu)代碼。如果各位小伙伴有什么建議的可以通過評(píng)論或者私信的方式告訴我,讓我們一起學(xué)習(xí)吧。

預(yù)告一下

后期將對(duì)接各平臺(tái)發(fā)布功能(初步預(yù)計(jì)5個(gè)平臺(tái),包括博客園、知乎、今日頭條、CSDN、簡(jiǎn)書),預(yù)期1個(gè)月左右完成一個(gè)平臺(tái)對(duì)接,爭(zhēng)取春節(jié)前完成5個(gè)平臺(tái)的一鍵發(fā)布功能。


本文標(biāo)題:解決自媒體一鍵多平臺(tái)發(fā)布,從零開發(fā)Markdown編輯器(一)
分享路徑:http://weahome.cn/article/dsojgeg.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部