前言
由于想在自己的页面里嵌入markdown编辑器,搜了不少现成的,感觉都不是很满意:
- EpicEditor:太丑,代码不高亮,没有工具栏
- Bootstrap-Markdown:不能多行代码,没有实时预览
- PageDown:不能多行代码,使用不方便
- zepto(没记错的话):太重,是一个框架
所以萌生了自己写一个的想法
文档和Demo,快点我
提供的功能
- 工具栏
- 多行代码与高亮
- 快捷键
- 两种模式——实时效果和按键预览
- 两种调用方法--手动添加类名和异步加载
- Tab缩进
- 没有全屏编辑和自动保存
依赖的第三方工具
- highlight.js
- marked.js
- jquery
- bootstrap(基本主题需要,并非必要)
代码
javascript
/** * * Created by suemi on 14-12-5. */ var SuMarkdown=function(option){ var load=function(mark){ var get=function(dst){ var result={}; $(dst).each(function(i,e){ result.select=( // Mozilla, Webkit ('selectionStart' in e && function() { var l = e.selectionEnd - e.selectionStart; return { start: e.selectionStart, end: e.selectionEnd, length: l, text: e.value.substr(e.selectionStart, l) }; }) || // Internet Explorer (document.selection && function() { e.focus(); var r = document.selection.createRange(); if (r === null) { return { start: 0, end: e.value.length, length: 0 }; } var re = e.createTextRange(); var rc = re.duplicate(); re.moveToBookmark(r.getBookmark()); rc.setEndPoint('EndToStart', re); return { start: rc.text.length, end: rc.text.length + r.text.length, length: r.text.length, text: r.text }; }) || // Not supported function() { return null; } )(); if(result.select) { result.container=$(this); return false; } else return true; }); return result; }; var replace= function (target,text,option){ var tmp; $(target).each(function(i,e){ tmp=( // Mozilla, Webkit ('selectionStart' in e && function() { var start = e.selectionStart; e.value = e.value.substr(0, e.selectionStart) + text + e.value.substr(e.selectionEnd, e.value.length); if (option === true || option === undefined) { e.selectionStart = start; e.selectionEnd = start + text.length; } else { e.selectionStart = e.selectionEnd = start + text.length; } return $(e); }) || // Internet Explorer (document.selection && function() { e.focus(); document.selection.createRange().text = text; return $(e); }) || // Not supported function() { e.value += text; return $(e); } )(); if(tmp) return false; else return true; }); return tmp; }; var methods={ bold:function(){ var reg=/^\*{2}[^\0]*\*{2}$/m; var target=get($('textarea',mark)); var dst; if(!reg.test(target.select.text)) dst=replace(target.container,'**'+target.select.text+'**'); else dst=replace(target.container,target.select.text.split('**')[1]); return dst; }, italic:function(){ var reg=/^_[^\0]*_$/m; var target=get($('textarea',mark)); var dst; if(!reg.test(target.select.text)) dst=replace(target.container,'_'+target.select.text+'_'); else dst=replace(target.container,target.select.text.split('_')[1]); return dst; }, head:function(){ var reg=/^#{1,6}[^\0]*/m; var target=get($('textarea',mark)); var dst; if(!reg.test(target.select.text)) dst=replace(target.container,'###'+target.select.text); else dst=replace(target.container,target.select.text.split(/#{1,6}/)[1]); return dst; }, link:function(){ var reg=/^\[[^\0]*\]\([^\0]*\)$/m; var target=get($('textarea',mark)); var dst; if(!reg.test(target.select.text)) { var url=prompt('Enter your URL :'); if(!url) return target.container; if(target.select.text=='') target.select.text="Enter your link description here:"; dst = replace(target.container, '[ ' + target.select.text + ' ](' + url + ')'); } else dst=replace(target.container,target.select.text.split('[')[1].split(']')[0]); return dst; }, img:function(){ var reg=/^!\[[^\0]*\]\([^\0]*\)$/m; var target=get($('textarea',mark)); var dst; if(!reg.test(target.select.text)) { var url=prompt('Enter your Image URL :'); if(!url) return target.container; if(target.select.text=='') target.select.text="Enter your image description here:"; dst = replace(target.container, '![ ' + target.select.text + ' ](' + url + ')'); } else dst=replace(target.container,target.select.text.split('[')[1].split(']')[0]); return dst; }, list:function(){ var target=get($('textarea',mark)); if(target.select.text=='') target.select.text='list text here'; var dst=replace(target.container,'- '+target.select.text); return dst; }, orderlist:function(){ var target=get($('textarea',mark)); if(target.select.text=='') target.select.text='list text here'; var dst=replace(target.container,'1. '+target.select.text); return dst; }, code:function(){ var reg=/^`[^\0]*`$/m; var target=get($('textarea',mark)); var dst; if(target.select.text=='') target.select.text='list text here'; if(!reg.test(target.select.text)) dst=replace(target.container,'`'+target.select.text+'`'); else dst=replace(target.container,target.select.text.split('`')[1]); return dst; }, block:function(){ var target=get($('textarea',mark)); if(target.select.text=='') target.select.text='quote here'; var dst=replace(target.container,'> '+target.select.text); return dst; }, tab:function(){ var target=get($('textarea',mark)); var dst=replace(target.container,' ' +target.select.text.split('\n').join('\n '),false); return dst; }, preview:function(event){ var blank=$('.suPreview',mark); var state=blank.css('display'); var target=get($('textarea',mark)); if(state=='none'){ blank.html(marked(target.container.val())); $('pre code',blank).each(function(i,block){ hljs.highlightBlock(block); }); $('.suEditor',mark).css('display','none'); blank.css('display','block'); } else{ blank.css('display','none'); $('.suEditor',mark).css('display','block'); target.container.focus(); } } }; //set the hotkey $('textarea',mark).attr('data-state','0').on('keyup',function(event){ if(event.keyCode==17) $(this).attr('data-state','0'); }); $('textarea:input',mark).on('keydown',function(event){ var dst,tmp; switch(event.keyCode){ case 9: event.preventDefault(); dst=methods.tab(); break; case 17: $(this).attr('data-state','1'); break; case 66: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.bold(); event.preventDefault(); } break; case 73: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.italic(); event.preventDefault(); } break; case 71: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.img(); event.preventDefault(); } break; case 72: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.head(); event.preventDefault(); } break; case 75: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.code(); event.preventDefault(); } break; case 76: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.link(); event.preventDefault(); } break; case 79: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.orderlist(); event.preventDefault(); } break; case 81: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.block(); event.preventDefault(); } break; case 85: tmp=$(this).attr('data-state'); if(tmp=='1') { methods.list(); event.preventDefault(); } break; default : break; } }); $('.su-toolbar > *',mark).each(function(i,element){ var str=$(this).attr('class'); str=str.split(/\s+/g); for(var i in str){ if(str[i].substring(0,8)=='su-tool-'){ var method=str[i].substring(8); if(method=='preview') break; $(this).on('click',function(event){ event.preventDefault(); var dst=methods[method](); if(option.preview) $('.suPreview',mark).html(marked(dst.val())); dst.focus(); }); } } return true; }); if(!option.clean){ $('.suPreview',mark).css({ "width":"50%", "float":"left", 'overflow': 'auto', 'padding': '0 20px', }); $('.suEditor',mark).css({ "width":"50%", "float":"left", "display":"block" }); $('textarea',mark).css({ "tab-size":"4", "padding":"20px", "resize":"none", "overflow":"auto" }); $('textarea:focus',mark).css('background','#fff'); } if(option.preview){ $('.su-tool-preview',mark).attr('disabled','true'); $('.suEditor textarea',mark).on('keyup',function(){ $('.suPreview',mark).html(marked($(this).val())); $('pre code',mark).each(function(i,block){ hljs.highlightBlock(block); }); }); } else{ $('.suPreview',mark).css({ 'width':"100%", 'display':'none', "border-left":"solid 1px", "border-color": $('textarea',mark).css('border-color') }); $('.suEditor',mark).css({width:"100%"}); $('.su-tool-preview',mark).on('click',function(){ methods.preview(); }); } if(option.css) mark.css(option.css); if(option.textCss) $('.textarea',mark).css(option.textCss); if(option.previewCss) $('.suPreview',mark).css(option.previewCss); if(option.textHeight){ $('textarea',mark).css('height',option.textHeight); $('.suPreview',mark).css('height',option.textHeight); } }; if(!option.target) option.target=$('.suMarkdown'); if(!option.insert){ var mark=option.target; console.log($('textarea',mark).css('height')); load(option.target); } else{ $.get(option.baseUrl,function(data){ mark=$("<div></div>"); mark.html(data); load(mark); $(option.target).append(mark); },'html'); } };
使用时
javascript
$(function(){ var option={ target:'.suMarkdown', preview:true, }; SuMarkdown(option); });
为了便于理解,附上基本模板的代码
html
<style> .su-toolbar{ width:100%; height:45px; display: block; background: #f5f5f5; padding: 5px; border: solid 1px; border-color: #cccccc; } .su-toolbar .tool-block{ cursor: pointer; display: block; width:35px; margin:0 1%; height:35px; float:left; padding: 5px; } .su-toolbar .tool-block *{ left:20%; top:20%; } .su-toolbar .tool-block:hover{ background: #00ffff; } .su-toolbar button{ margin-top:5px; } .suEditor{ width:50%; float:left; display: block; } .suEditor textarea{ width:100%; height: 400px; background: #f5f5f5; tab-size: 4; border:solid 1px; border-top: none; border-color: #cccccc; padding: 20px; resize: none; } .suEditor textarea:focus{ background: #fff; border-color:#cccccc ; outline: none; } .suPreview{ width:50%; left:50%; float:left; background: #f5f5f5; height: 400px; display: block; overflow: auto; padding: 0 20px; border-right: solid 1px; border-bottom: solid 1px; border-color: #cccccc; } </style> <div class="su-toolbar"> <div class="tool-block su-tool-bold" title="加粗(Ctrl+B)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-bold"></span> </div> <div class="tool-block su-tool-italic" title="斜体(Ctrl+I)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-italic"></span> </div> <div class="tool-block su-tool-head" title="标题(Ctrl+H)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-header"></span> </div> <div class="tool-block su-tool-link" title="链接(Ctrl+L)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-link"></span> </div> <div class="tool-block su-tool-img" title="图片(Ctrl+G)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-picture"></span> </div> <div class="tool-block su-tool-list" title="无序列表(Ctrl+U)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-list"></span> </div> <div class="tool-block su-tool-orderlist" title="有序列表(Ctrl+O)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-th-list"></span> </div> <div class="tool-block su-tool-code" title="单行代码(Ctrl+K)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-asterisk"></span> </div> <div class="tool-block su-tool-block" title="区块(Ctrl+Q)" data-placement="top" data-toggle="tooltip"> <span class="glyphicon glyphicon-comment"></span> </div> <button class="btn btn-sm btn-primary pull-right su-tool-preview"> <span class="glyphicon glyphicon-search"></span> Preview </button> </div> <div class="suEditor"> <textarea ></textarea> </div> <div class="suPreview"> </div>