├── .gitignore ├── Gruntfile.coffee ├── README.md ├── bower.json ├── index-require.html ├── index.html ├── lib └── simditor-marked.js ├── package.json └── src └── simditor-marked.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .gitignore support plugin (hsz.mobi) 2 | .slow 3 | .idea 4 | logs 5 | bower_components 6 | node_modules -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt)-> 2 | grunt.initConfig( 3 | pkg: grunt.file.readJSON('package.json'), 4 | coffee: 5 | compile: 6 | files: 7 | "lib/simditor-marked.js":"src/simditor-marked.coffee" 8 | ) 9 | grunt.loadNpmTasks('grunt-contrib-coffee'); 10 | grunt.registerTask 'default', ['coffee:compile'] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | simditor-marked 2 | ============== 3 | 4 | [Simditor](http://simditor.tower.im/)扩展,将编辑器内markdown的内容格式化 5 | 6 | 另外Simditor全屏编辑插件[simditor-fullscreen](https://github.com/huyinghuan/simditor-fullscreen) 7 | 8 | ### 如何使用 9 | 10 | #### 常规加载 11 | 在Simditor的基础上额外引用[marked.js](https://github.com/chjj/marked/blob/master/lib/marked.js) 和 simditor-marked 的脚本 12 | 13 | ```html 14 | 15 | 16 | ``` 17 | 18 | 配置 19 | 20 | ```javascript 21 | new Simditor({ 22 | textarea: textareaElement, 23 | ..., 24 | toolbar: [..., 'marked'] 25 | }) 26 | ``` 27 | 28 | 点击marked 按钮(类似m的图标)后(或用快捷键Ctrl+m),将格式化编辑器里面markdown的内容。 29 | 如果选中了一部分编辑器的内容,那么只会格式选中的内容,否则格式全部内容。 30 | 31 | #### AMD模式加载 32 | 33 | 引入require.js 34 | ``` 35 | 36 | ``` 37 | 38 | 在require.js的入口 配置 marked 39 | ``` 40 | require.config({ 41 | paths:{ 42 | jquery: 'bower_components/jquery/dist/jquery.min', 43 | simditor: 'bower_components/simditor/lib/simditor', 44 | 'simple-module': 'bower_components/simple-module/lib/module', 45 | 'simple-uploader': 'bower_components/simple-uploader/lib/uploader', 46 | 'simple-hotkeys': 'bower_components/simple-hotkeys/lib/hotkeys', 47 | 'simditor-marked': 'lib/simditor-marked', 48 | marked: 'bower_components/marked/lib/marked' 49 | } 50 | }); 51 | ``` 52 | 53 | 使用simditor 54 | ``` 55 | require( 56 | [ 57 | 'jquery', 58 | 'simditor', 59 | 'simditor-marked' 60 | ],function($, Simditor){ 61 | var editor = new Simditor({ 62 | textarea: $('#editor'), 63 | toolbar: [ 64 | //... 65 | 'marked' 66 | ] 67 | }); 68 | }); 69 | ``` 70 | 71 | 72 | ### 使用bower安装 73 | 74 | ```shell 75 | bower install simditor-marked 76 | ``` 77 | 78 | ### 查看 demo 79 | 80 | clone 本仓库。 81 | 82 | 在仓库目录下运行 83 | ```shell 84 | npm install 85 | bower install 86 | grunt 87 | ``` 88 | 在浏览器打开index.html即可(requirejs 的demo为index-require.html) 89 | 90 | ### History 91 | v0.0.7 92 | 1. 支持2.1.5 93 | (由于官方不在提供统一的图标处理,因此需要额外添加对fontawesome的依赖.具体可以参考demo: index.html) 94 | 95 | v0.0.6 96 | 1.支持mac快捷键 cmd + m 97 | 98 | v0.0.5 99 | 1.支持 ```simditor``` 2.0.6以上版本. 100 | 101 | v0.0.4 102 | 103 | 1. 修复bug #1. 在firefox中无法正确转义代码块 104 | 105 | v0.0.3 106 | 107 | 1. 支持AMD模式加载 108 | 109 | v0.0.2 110 | 111 | 1. 更新icon. 不用锤子用类似m的图标了。 112 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simditor-marked", 3 | "version": "0.0.6", 4 | "homepage": "https://github.com/huyinghuan/simditor-marked", 5 | "authors": [ 6 | "huyinghuan " 7 | ], 8 | "description": "a markdown plugin for Simditor", 9 | "main": "lib/js", 10 | "keywords": [ 11 | "simditor", 12 | "markdown" 13 | ], 14 | "license": "MIT", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "tests" 21 | ], 22 | "dependencies": { 23 | "jquery": "~2.1.0", 24 | "simditor": "~2.1.5", 25 | "marked": "~0.3.2" 26 | }, 27 | "devDependencies": { 28 | "requirejs": "~2.1.15" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /index-require.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 42 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 27 | -------------------------------------------------------------------------------- /lib/simditor-marked.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var __hasProp = {}.hasOwnProperty, 3 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 4 | 5 | (function(factory) { 6 | if ((typeof define === 'function') && define.amd) { 7 | return define(['simditor', 'marked'], factory); 8 | } else { 9 | return factory(window.Simditor, window.marked); 10 | } 11 | })(function(Simditor, _marked) { 12 | var MarkedButton; 13 | MarkedButton = (function(_super) { 14 | __extends(MarkedButton, _super); 15 | 16 | function MarkedButton() { 17 | MarkedButton.__super__.constructor.apply(this, arguments); 18 | this.marked = _marked; 19 | if (!_marked) { 20 | throw new Error('cannot find the plugin marked'); 21 | } 22 | } 23 | 24 | MarkedButton.prototype._init = function() { 25 | if (this.editor.util.os.mac) { 26 | this.title = this.title + ' ( Cmd + m )'; 27 | } else { 28 | this.title = this.title + ' ( Ctrl + m )'; 29 | this.shortcut = 'ctrl+m'; 30 | } 31 | MarkedButton.__super__._init.apply(this, arguments); 32 | return this.setIcon("maxcdn"); 33 | }; 34 | 35 | MarkedButton.prototype.name = 'marked'; 36 | 37 | MarkedButton.prototype.title = 'marked'; 38 | 39 | MarkedButton.prototype.icon = 'maxcdn'; 40 | 41 | MarkedButton.prototype.shortcut = 'cmd+m'; 42 | 43 | MarkedButton.prototype.setIcon = function(icon) { 44 | return this.el.find("span").removeClass().addClass("fa fa-" + icon); 45 | }; 46 | 47 | MarkedButton.prototype.decodeHTML = function(str) { 48 | var div; 49 | div = document.createElement('div'); 50 | div.innerHTML = str; 51 | return div.innerText || div.textContent; 52 | }; 53 | 54 | MarkedButton.prototype.encodeHTML = function(str) { 55 | var div; 56 | div = document.createElement('div'); 57 | div.appendChild(document.createTextNode(str)); 58 | return div.innerHTML; 59 | }; 60 | 61 | MarkedButton.prototype.decodeCodes = function(content) { 62 | var code, codes, div, text, _i, _len; 63 | div = document.createElement('div'); 64 | div.innerHTML = content; 65 | codes = div.querySelectorAll('code'); 66 | for (_i = 0, _len = codes.length; _i < _len; _i++) { 67 | code = codes[_i]; 68 | text = this.decodeHTML(code.innerText || div.textContent); 69 | if (code.innerText) { 70 | code.innerText = text; 71 | } 72 | if (code.textContent) { 73 | code.textContent = text; 74 | } 75 | } 76 | return div.innerHTML; 77 | }; 78 | 79 | MarkedButton.prototype.replaceSelection = function(html, selectInserted) { 80 | var child, div, firstInsertedNode, fragment, lastInsertedNode, range, sel; 81 | if (selectInserted == null) { 82 | selectInserted = true; 83 | } 84 | sel = window.getSelection(); 85 | if (!(sel.getRangeAt && sel.rangeCount)) { 86 | return; 87 | } 88 | range = window.getSelection().getRangeAt(0); 89 | range.deleteContents(); 90 | if (range.createContextualFragment) { 91 | fragment = range.createContextualFragment(html); 92 | } else { 93 | div = document.createElement("div"); 94 | div.innerHTML = html; 95 | fragment = document.createDocumentFragment(); 96 | while ((child = div.firstChild)) { 97 | fragment.appendChild(child); 98 | } 99 | } 100 | firstInsertedNode = fragment.firstChild; 101 | lastInsertedNode = fragment.lastChild; 102 | range.insertNode(fragment); 103 | if (!selectInserted) { 104 | return; 105 | } 106 | if (firstInsertedNode) { 107 | range.setStartBefore(firstInsertedNode); 108 | range.setEndAfter(lastInsertedNode); 109 | } 110 | sel.removeAllRanges(); 111 | return sel.addRange(range); 112 | }; 113 | 114 | MarkedButton.prototype.doReplaceSelction = function(sel) { 115 | var value; 116 | value = this.marked(this.encodeHTML(sel)); 117 | value = this.decodeCodes(value); 118 | return this.replaceSelection(value); 119 | }; 120 | 121 | MarkedButton.prototype.doReplaceAll = function() { 122 | var value; 123 | value = this.editor.getValue(); 124 | value = value.replace(/

/g, '').replace(/<\/p>/g, '\n'); 125 | value = this.marked(value); 126 | value = this.decodeCodes(value); 127 | return this.editor.setValue(value); 128 | }; 129 | 130 | MarkedButton.prototype.command = function() { 131 | var sel; 132 | sel = window.getSelection().toString(); 133 | if (sel.length === 0) { 134 | this.doReplaceAll(); 135 | } else { 136 | this.doReplaceSelction(sel); 137 | } 138 | return this.editor.selection.setRangeAtEndOf('p'); 139 | }; 140 | 141 | return MarkedButton; 142 | 143 | })(Simditor.Button); 144 | return Simditor.Toolbar.addButton(MarkedButton); 145 | }); 146 | 147 | }).call(this); 148 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simditor-marked", 3 | "version": "0.0.6", 4 | "homepage": "https://github.com/huyinghuan/simditor-marked", 5 | "keywords": [ 6 | "simditor", 7 | "markdown" 8 | ], 9 | "license": "MIT", 10 | "devDependencies": { 11 | "grunt": "^0.4.5", 12 | "grunt-contrib-coffee": "^0.11.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/simditor-marked.coffee: -------------------------------------------------------------------------------- 1 | ((factory)-> 2 | if (typeof define is 'function') and define.amd 3 | define ['simditor', 'marked'], factory 4 | else 5 | factory window.Simditor, window.marked 6 | )((Simditor, _marked)-> 7 | class MarkedButton extends Simditor.Button 8 | constructor: -> 9 | super 10 | @marked = _marked 11 | throw new Error('cannot find the plugin marked') if not _marked 12 | 13 | _init: -> 14 | if @editor.util.os.mac 15 | @title = @title + ' ( Cmd + m )' 16 | else 17 | @title = @title + ' ( Ctrl + m )' 18 | @shortcut = 'ctrl+m' 19 | super 20 | @setIcon("maxcdn") 21 | 22 | name: 'marked' 23 | title: 'marked' 24 | icon: 'maxcdn' 25 | shortcut: 'cmd+m' 26 | 27 | setIcon: (icon)-> 28 | @el.find("span").removeClass().addClass("fa fa-#{icon}") 29 | #反转义字符串 30 | decodeHTML: (str)-> 31 | div = document.createElement('div') 32 | div.innerHTML = str 33 | div.innerText or div.textContent 34 | 35 | #转义字符串 36 | encodeHTML: (str)-> 37 | div = document.createElement('div') 38 | div.appendChild document.createTextNode str 39 | div.innerHTML 40 | 41 | #反转义所有code标签里面的字符 42 | decodeCodes: (content)-> 43 | div = document.createElement 'div' 44 | div.innerHTML = content 45 | codes = div.querySelectorAll 'code' 46 | for code in codes 47 | text = @decodeHTML(code.innerText or div.textContent) 48 | code.innerText = text if code.innerText 49 | code.textContent = text if code.textContent 50 | 51 | return div.innerHTML 52 | 53 | 54 | ## 替换选中文字 55 | ## 代码来自 http://stackoverflow.com/questions/5393922/javascript-replace-selection-all-browsers 56 | ## 删除了一部分关于ie8的兼容代码,并改写成了coffee 57 | replaceSelection: (html, selectInserted = true)-> 58 | sel = window.getSelection() 59 | # Test that the Selection object contains at least one Range 60 | return if not (sel.getRangeAt && sel.rangeCount) 61 | range = window.getSelection().getRangeAt(0) 62 | range.deleteContents() 63 | if range.createContextualFragment 64 | fragment = range.createContextualFragment html 65 | else 66 | div = document.createElement "div" 67 | div.innerHTML = html 68 | fragment = document.createDocumentFragment() 69 | fragment.appendChild child while (child = div.firstChild) 70 | 71 | firstInsertedNode = fragment.firstChild 72 | lastInsertedNode = fragment.lastChild 73 | range.insertNode fragment 74 | 75 | return if not selectInserted 76 | if firstInsertedNode 77 | range.setStartBefore firstInsertedNode 78 | range.setEndAfter lastInsertedNode 79 | sel.removeAllRanges() 80 | sel.addRange range 81 | 82 | doReplaceSelction: (sel)-> 83 | value = @marked @encodeHTML sel 84 | value = @decodeCodes value 85 | @replaceSelection value 86 | 87 | doReplaceAll: ()-> 88 | value = @editor.getValue() 89 | value = value.replace(/

/g, '').replace(/<\/p>/g, '\n') 90 | value = @marked value 91 | value = @decodeCodes value 92 | @editor.setValue value 93 | 94 | command: ()-> 95 | sel = window.getSelection().toString() 96 | if sel.length is 0 then @doReplaceAll() else @doReplaceSelction(sel) 97 | @editor.selection.setRangeAtEndOf('p') 98 | 99 | Simditor.Toolbar.addButton(MarkedButton) 100 | ) --------------------------------------------------------------------------------