├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── dist ├── css │ └── jquery.qeditor.css ├── index.html └── js │ └── jquery.qeditor.js └── src ├── css └── jquery.qeditor.scss └── js └── jquery.qeditor.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | tmp/**/* 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "rake" 4 | gem "sass" 5 | gem "coffee-script" 6 | 7 | gem "rb-fsevent", "~> 0.9" 8 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | coffee-script (2.2.0) 5 | coffee-script-source 6 | execjs 7 | coffee-script-source (1.6.3) 8 | execjs (1.4.0) 9 | multi_json (~> 1.0) 10 | multi_json (1.7.9) 11 | rake (10.1.0) 12 | rb-fsevent (0.9.3) 13 | sass (3.2.10) 14 | 15 | PLATFORMS 16 | ruby 17 | 18 | DEPENDENCIES 19 | coffee-script 20 | rake 21 | rb-fsevent (~> 0.9) 22 | sass 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jquery.qeditor 2 | ============== 3 | 4 | This is a simple WYSIWYG editor with jQuery. 5 | 6 | [中文介绍请点这里](http://huacnlee.com/blog/jquery-qeditor-introduction/) 7 | 8 | 9 | 10 | ## Featrues 11 | 12 | - Simple UI, Simple Use; 13 | - Use font-awsome as toolbar icon; 14 | - Remove complex html tag, attributes on paste, this can make you web page clean; 15 | - Less code; 16 | - Use `

` not `
` on press `Enter`, This can help you make a neat layout; 17 | - PlaceHolder for WYSIWYG editor; 18 | - FullScreen; 19 | 20 | ## Changelogs 21 | 22 | You can see all of the release notes in here: [Release notes](https://github.com/huacnlee/jquery.qeditor/releases) 23 | 24 | ## Browser support 25 | 26 | - Safari 27 | - Chrome 28 | - Firefox 29 | - IE (No test), maybe 8+ 30 | 31 | ## Demo 32 | 33 | You can try the [Demo app](http://huacnlee.github.io/jquery.qeditor). 34 | 35 | ## Usage 36 | 37 | ```html 38 | 39 | 42 | ``` 43 | 44 | ## How to add custom toolbar icon 45 | 46 | ```js 47 | // Custom a toolbar icon 48 | var toolbar = $("#post_body").parent().find(".qeditor_toolbar"); 49 | var link = $(""); 50 | link.click(function(){ 51 | alert("Put you custom toolbar event in here."); 52 | return false; 53 | }); 54 | toolbar.append(link); 55 | ``` 56 | 57 | 58 | ## Build 59 | 60 | ``` 61 | $ bundle install 62 | $ rake watch # or use "rake build" to release 63 | ``` 64 | 65 | 66 | ## License 67 | 68 | Apache V2 : http://choosealicense.com/licenses/apache 69 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | task :build do 2 | print "Building Sass files..." 3 | `bundle exec sass --cache-location tmp/cache/sass --update src/css:dist/css -t compressed -f` 4 | puts " [Done]" 5 | print "Building CoffeeScript files..." 6 | `bundle exec coffee -c -b -o dist/js src/js` 7 | puts " [Done]" 8 | end 9 | 10 | task :watch do 11 | Process.fork do 12 | # In child 13 | exec("bundle exec sass --scss --watch src/css:dist/css --cache-location tmp/cache/sass") 14 | end 15 | 16 | Process.fork do 17 | exec("bundle exec coffee -w -b -c -o dist/js src/js") 18 | end 19 | Process.waitall 20 | end 21 | -------------------------------------------------------------------------------- /dist/css/jquery.qeditor.css: -------------------------------------------------------------------------------- 1 | .qeditor_border { 2 | background: #FFF; 3 | padding-bottom: 20px; 4 | text-align: center; 5 | position: relative; } 6 | 7 | .qeditor_toolbar { 8 | text-align: left; 9 | background: #FFF; 10 | margin: 0 auto 3px auto; } 11 | .qeditor_toolbar span.vline { 12 | border-left: 1px solid #bbb; 13 | padding-right: 10px; } 14 | .qeditor_toolbar a, .qeditor_toolbar .qe-icon { 15 | color: #666; 16 | text-decoration: none; 17 | font-size: 14px; 18 | margin-right: 8px; } 19 | .qeditor_toolbar a:hover, .qeditor_toolbar .qe-icon:hover { 20 | color: #43be4d; } 21 | .qeditor_toolbar a.qe-state-on { 22 | color: #43be4d; } 23 | .qeditor_toolbar .qe-heading { 24 | position: relative; } 25 | .qeditor_toolbar .qe-heading .qe-menu { 26 | width: 120px; 27 | display: none; 28 | position: absolute; 29 | margin: 0; 30 | padding: 0; 31 | left: -25px; 32 | top: 15px; 33 | border: 1px solid #eee; 34 | border-right: 1px solid #ddd; 35 | border-bottom: 1px solid #ddd; 36 | z-index: 9991; 37 | background: #FFF; 38 | list-style: none; } 39 | .qeditor_toolbar .qe-heading .qe-menu li { 40 | display: inline; 41 | padding: 0; 42 | margin: 0; } 43 | .qeditor_toolbar .qe-heading .qe-menu a { 44 | display: block; 45 | margin: 0; 46 | padding: 2px 10px; } 47 | .qeditor_toolbar .qe-heading .qe-menu a:hover { 48 | background: #f0f0f0; } 49 | .qeditor_toolbar .qe-heading .qe-menu .qe-h1 { 50 | font-size: 17px; 51 | font-weight: bold; } 52 | .qeditor_toolbar .qe-heading .qe-menu .qe-h2 { 53 | font-size: 16px; 54 | font-weight: bold; } 55 | .qeditor_toolbar .qe-heading .qe-menu .qe-h3 { 56 | font-size: 15px; 57 | font-weight: bold; } 58 | .qeditor_toolbar .qe-heading .qe-menu .qe-h4 { 59 | font-size: 14px; 60 | font-weight: bold; } 61 | .qeditor_toolbar .qe-heading .qe-menu .qe-h5 { 62 | font-size: 13px; 63 | font-weight: bold; } 64 | .qeditor_toolbar .qe-heading .qe-menu .qe-h6 { 65 | font-size: 12px; 66 | font-weight: bold; } 67 | .qeditor_toolbar .qe-heading .qe-menu .qe-p { 68 | font-size: 12px; 69 | border-top: 1px solid #eee; } 70 | .qeditor_toolbar .qe-heading.hover { 71 | background: #eee; } 72 | .qeditor_toolbar .qe-heading.hover .qe-menu { 73 | background: #fff; 74 | display: block; } 75 | 76 | .qeditor_preview { 77 | text-align: left; 78 | min-height: 80px; 79 | margin: 0 auto; 80 | overflow-y: scroll; } 81 | 82 | .qeditor_placeholder { 83 | color: #999; } 84 | 85 | .qeditor_fullscreen { 86 | position: fixed; 87 | background: #FFF; 88 | top: 0; 89 | left: 0; 90 | bottom: 0; 91 | right: 0; 92 | z-index: 99990; 93 | padding: 30px 0; 94 | overflow: hidden; } 95 | .qeditor_fullscreen .qeditor_preview { 96 | max-width: 900px; 97 | box-sizing: border-box; 98 | width: 100%; 99 | height: 90%; 100 | margin-top: 20px; 101 | border: 0; 102 | -webkit-box-shadow: none; 103 | -moz-box-shadow: none; 104 | box-shadow: none; 105 | -webkit-transition: none; 106 | -moz-transition: none; 107 | -o-transition: none; 108 | transition: none; 109 | padding: 0; } 110 | .qeditor_fullscreen .qeditor_toolbar { 111 | max-width: 900px; 112 | box-sizing: border-box; 113 | width: 100%; 114 | padding-bottom: 10px; } 115 | .qeditor_fullscreen .qeditor_toolbar a, .qeditor_fullscreen .qeditor_toolbar .qe-icon { 116 | font-size: 18px; 117 | margin-right: 8px; } 118 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jquery.qeditor 6 | 7 | 8 | 9 | 32 | 33 | 34 | 35 | 36 |

37 |

jquery.qeditor

38 |

39 | This is a simple WYSIWYG editor with jQuery. 40 |

41 |

42 | https://github.com/huacnlee/jquery.qeditor 43 |

44 |
45 |

Example

46 |
47 |
48 | 49 |
50 | 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 |
59 |
60 |
61 | 62 | 63 |
64 |
65 |
66 | 94 |
95 | 96 | -------------------------------------------------------------------------------- /dist/js/jquery.qeditor.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.3 2 | /* 3 | jquery.qeditor 4 | ============== 5 | 6 | This is a simple WYSIWYG editor with jQuery. 7 | 8 | ## Author: 9 | 10 | Jason Lee 11 | 12 | ## Requirements: 13 | 14 | [jQuery](http://jquery.com) 15 | (Font-Awesome)[http://fortawesome.github.io/Font-Awesome/] - Toolbar icons 16 | 17 | ## Usage: 18 | 19 | $("textarea").qeditor(); 20 | 21 | and then you need filt the html tags,attributes in you content page. 22 | In Rails application, you can use like this: 23 | 24 | <%= sanitize(@post.body,:tags => %w(strong b i u strike ol ul li address blockquote pre code br div p), :attributes => %w(src)) %> 25 | */ 26 | 27 | var QEDITOR_ALLOW_TAGS_ON_PASTE, QEDITOR_DISABLE_ATTRIBUTES_ON_PASTE, QEDITOR_TOOLBAR_HTML; 28 | 29 | QEDITOR_TOOLBAR_HTML = "
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
"; 30 | 31 | QEDITOR_ALLOW_TAGS_ON_PASTE = "div,p,ul,ol,li,hr,br,b,strong,i,em,img,h2,h3,h4,h5,h6,h7"; 32 | 33 | QEDITOR_DISABLE_ATTRIBUTES_ON_PASTE = ["style", "class", "id", "name", "width", "height"]; 34 | 35 | window.QEditor = { 36 | actions: ['bold', 'italic', 'underline', 'strikethrough', 'insertunorderedlist', 'insertorderedlist', 'blockquote', 'pre'], 37 | action: function(el, a, p) { 38 | var editor; 39 | editor = $(".qeditor_preview", $(el).parent().parent()); 40 | editor.find(".qeditor_placeholder").remove(); 41 | editor.focus(); 42 | if (p === null) { 43 | p = false; 44 | } 45 | if (a === "blockquote" || a === "pre") { 46 | p = a; 47 | a = "formatBlock"; 48 | } 49 | if (a === "createLink") { 50 | p = prompt("Type URL:"); 51 | if (p.trim().length === 0) { 52 | return false; 53 | } 54 | } else if (a === "insertimage") { 55 | p = prompt("Image URL:"); 56 | if (p.trim().length === 0) { 57 | return false; 58 | } 59 | } 60 | if (QEditor.state(a)) { 61 | document.execCommand(a, false, null); 62 | } else { 63 | document.execCommand(a, false, p); 64 | } 65 | QEditor.checkSectionState(editor); 66 | editor.change(); 67 | return false; 68 | }, 69 | state: function(action) { 70 | return document.queryCommandState(action) === true; 71 | }, 72 | prompt: function(title) { 73 | var val; 74 | val = prompt(title); 75 | if (val) { 76 | return val; 77 | } else { 78 | return false; 79 | } 80 | }, 81 | toggleFullScreen: function(el) { 82 | var border; 83 | border = $(el).parent().parent(); 84 | if (border.data("qe-fullscreen") === "1") { 85 | QEditor.exitFullScreen(); 86 | } else { 87 | QEditor.enterFullScreen(border); 88 | } 89 | return false; 90 | }, 91 | enterFullScreen: function(border) { 92 | border.data("qe-fullscreen", "1").addClass("qeditor_fullscreen"); 93 | border.find(".qeditor_preview").focus(); 94 | return border.find(".qe-fullscreen span").attr("class", "fa fa-compress"); 95 | }, 96 | exitFullScreen: function() { 97 | return $(".qeditor_border").removeClass("qeditor_fullscreen").data("qe-fullscreen", "0").find(".qe-fullscreen span").attr("class", "fa fa-arrows-alt"); 98 | }, 99 | getCurrentContainerNode: function() { 100 | var containerNode, node; 101 | if (window.getSelection) { 102 | node = window.getSelection().anchorNode; 103 | containerNode = node.nodeType === 3 ? node.parentNode : node; 104 | } 105 | return containerNode; 106 | }, 107 | checkSectionState: function(editor) { 108 | var a, link, _i, _len, _ref, _results; 109 | _ref = QEditor.actions; 110 | _results = []; 111 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 112 | a = _ref[_i]; 113 | link = editor.parent().find(".qeditor_toolbar a[data-action=" + a + "]"); 114 | if (QEditor.state(a)) { 115 | _results.push(link.addClass("qe-state-on")); 116 | } else { 117 | _results.push(link.removeClass("qe-state-on")); 118 | } 119 | } 120 | return _results; 121 | }, 122 | version: function() { 123 | return "0.2.0"; 124 | } 125 | }; 126 | 127 | (function($) { 128 | return $.fn.qeditor = function(options) { 129 | return this.each(function() { 130 | var currentVal, editor, obj, placeholder, qe_heading, toolbar; 131 | obj = $(this); 132 | obj.addClass("qeditor"); 133 | editor = $('
'); 134 | placeholder = $('
'); 135 | $(document).keyup(function(e) { 136 | if (e.keyCode === 27) { 137 | return QEditor.exitFullScreen(); 138 | } 139 | }); 140 | document.execCommand('defaultParagraphSeparator', false, 'p'); 141 | currentVal = obj.val(); 142 | editor.html(currentVal); 143 | editor.addClass(obj.attr("class")); 144 | obj.after(editor); 145 | placeholder.text(obj.attr("placeholder")); 146 | editor.attr("placeholder", obj.attr("placeholder") || ""); 147 | editor.append(placeholder); 148 | editor.focusin(function() { 149 | QEditor.checkSectionState(editor); 150 | return $(this).find(".qeditor_placeholder").remove(); 151 | }); 152 | editor.blur(function() { 153 | var t; 154 | t = $(this); 155 | QEditor.checkSectionState(editor); 156 | if (t.html().length === 0 || t.html() === "
" || t.html() === "

") { 157 | return $(this).html('
' + $(this).attr("placeholder") + '
'); 158 | } 159 | }); 160 | editor.change(function() { 161 | var pobj, t; 162 | pobj = $(this); 163 | t = pobj.parent().find('.qeditor'); 164 | return t.val(pobj.html()); 165 | }); 166 | editor.on("paste", function() { 167 | var txt; 168 | txt = $(this); 169 | return setTimeout(function() { 170 | var attrName, els, _i, _len; 171 | els = txt.find("*"); 172 | for (_i = 0, _len = QEDITOR_DISABLE_ATTRIBUTES_ON_PASTE.length; _i < _len; _i++) { 173 | attrName = QEDITOR_DISABLE_ATTRIBUTES_ON_PASTE[_i]; 174 | els.removeAttr(attrName); 175 | } 176 | els.find(":not(" + QEDITOR_ALLOW_TAGS_ON_PASTE + ")").contents().unwrap(); 177 | txt.change(); 178 | return true; 179 | }, 100); 180 | }); 181 | editor.keyup(function(e) { 182 | QEditor.checkSectionState(editor); 183 | return $(this).change(); 184 | }); 185 | editor.on("click", function(e) { 186 | QEditor.checkSectionState(editor); 187 | return e.stopPropagation(); 188 | }); 189 | editor.keydown(function(e) { 190 | var node, nodeName; 191 | node = QEditor.getCurrentContainerNode(); 192 | nodeName = ""; 193 | if (node && node.nodeName) { 194 | nodeName = node.nodeName.toLowerCase(); 195 | } 196 | if (e.keyCode === 13 && !(e.shiftKey || e.ctrlKey)) { 197 | if (nodeName === "blockquote" || nodeName === "pre") { 198 | e.stopPropagation(); 199 | document.execCommand('InsertParagraph', false); 200 | document.execCommand("formatBlock", false, "p"); 201 | document.execCommand('outdent', false); 202 | return false; 203 | } 204 | } 205 | }); 206 | obj.hide(); 207 | obj.wrap('
'); 208 | obj.after(editor); 209 | toolbar = $(QEDITOR_TOOLBAR_HTML); 210 | qe_heading = toolbar.find(".qe-heading"); 211 | qe_heading.mouseenter(function() { 212 | $(this).addClass("hover"); 213 | return $(this).find(".qe-menu").show(); 214 | }); 215 | qe_heading.mouseleave(function() { 216 | $(this).removeClass("hover"); 217 | return $(this).find(".qe-menu").hide(); 218 | }); 219 | toolbar.find(".qe-heading .qe-menu a").click(function() { 220 | var link; 221 | link = $(this); 222 | link.parent().parent().hide(); 223 | QEditor.action(this, "formatBlock", link.data("name")); 224 | return false; 225 | }); 226 | toolbar.find("a[data-action]").click(function() { 227 | return QEditor.action(this, $(this).attr("data-action")); 228 | }); 229 | return editor.before(toolbar); 230 | }); 231 | }; 232 | })(jQuery); 233 | -------------------------------------------------------------------------------- /src/css/jquery.qeditor.scss: -------------------------------------------------------------------------------- 1 | .qeditor_border { 2 | background:#FFF; 3 | padding-bottom:20px; 4 | text-align: center; 5 | position: relative; 6 | } 7 | .qeditor_toolbar { 8 | text-align: left; 9 | background:#FFF; 10 | margin: 0 auto 3px auto; 11 | span.vline { border-left:1px solid #bbb; padding-right:10px; } 12 | a,.qe-icon { 13 | color: #666; 14 | text-decoration: none; 15 | font-size:14px; 16 | margin-right:8px; 17 | &:hover { 18 | color: #43be4d; 19 | } 20 | } 21 | a.qe-state-on { 22 | color:#43be4d; 23 | } 24 | 25 | .qe-heading { 26 | position: relative; 27 | .qe-menu { 28 | width: 120px; 29 | display: none; 30 | position: absolute; 31 | margin:0; padding:0; 32 | left:-25px; top:15px; 33 | border:1px solid #eee; 34 | border-right: 1px solid #ddd; 35 | border-bottom: 1px solid #ddd; 36 | z-index:9991; 37 | background:#FFF; 38 | list-style: none; 39 | li { display:inline; padding:0; margin:0; } 40 | a { 41 | display:block; 42 | margin:0; 43 | padding:2px 10px; 44 | &:hover { 45 | background:#f0f0f0; 46 | } 47 | } 48 | .qe-h1 { font-size:17px; font-weight:bold; } 49 | .qe-h2 { font-size:16px; font-weight:bold; } 50 | .qe-h3 { font-size:15px; font-weight:bold; } 51 | .qe-h4 { font-size:14px; font-weight:bold; } 52 | .qe-h5 { font-size:13px; font-weight:bold; } 53 | .qe-h6 { font-size:12px; font-weight:bold; } 54 | .qe-p { font-size:12px; border-top:1px solid #eee; } 55 | } 56 | &.hover { 57 | background:#eee; 58 | .qe-menu { 59 | background:#fff; 60 | display:block; 61 | } 62 | } 63 | } 64 | 65 | } 66 | .qeditor_preview { 67 | text-align: left; 68 | min-height:80px; 69 | margin:0 auto; 70 | overflow-y:scroll; 71 | } 72 | .qeditor_placeholder { color:#999; } 73 | .qeditor_fullscreen { 74 | position: fixed; 75 | background:#FFF; 76 | top:0; left:0; bottom:0; right:0; 77 | z-index: 99990; 78 | padding:30px 0; 79 | overflow:hidden; 80 | 81 | .qeditor_preview { 82 | max-width: 900px; 83 | box-sizing: border-box; 84 | width: 100%; 85 | height: 90%; 86 | margin-top: 20px; 87 | border:0; 88 | -webkit-box-shadow: none; 89 | -moz-box-shadow: none; 90 | box-shadow: none; 91 | -webkit-transition: none; 92 | -moz-transition: none; 93 | -o-transition: none; 94 | transition: none; 95 | padding:0; 96 | } 97 | .qeditor_toolbar { 98 | max-width: 900px; 99 | box-sizing: border-box; 100 | width: 100%; 101 | padding-bottom:10px; 102 | a,.qe-icon { font-size:18px; margin-right:8px; } 103 | } 104 | } -------------------------------------------------------------------------------- /src/js/jquery.qeditor.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | jquery.qeditor 3 | ============== 4 | 5 | This is a simple WYSIWYG editor with jQuery. 6 | 7 | ## Author: 8 | 9 | Jason Lee 10 | 11 | ## Requirements: 12 | 13 | [jQuery](http://jquery.com) 14 | (Font-Awesome)[http://fortawesome.github.io/Font-Awesome/] - Toolbar icons 15 | 16 | ## Usage: 17 | 18 | $("textarea").qeditor(); 19 | 20 | and then you need filt the html tags,attributes in you content page. 21 | In Rails application, you can use like this: 22 | 23 | <%= sanitize(@post.body,:tags => %w(strong b i u strike ol ul li address blockquote pre code br div p), :attributes => %w(src)) %> 24 | ### 25 | 26 | QEDITOR_TOOLBAR_HTML = """ 27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
59 | """ 60 | QEDITOR_ALLOW_TAGS_ON_PASTE = "div,p,ul,ol,li,hr,br,b,strong,i,em,img,h2,h3,h4,h5,h6,h7" 61 | QEDITOR_DISABLE_ATTRIBUTES_ON_PASTE = ["style","class","id","name","width","height"] 62 | 63 | window.QEditor = 64 | actions : ['bold','italic','underline','strikethrough','insertunorderedlist','insertorderedlist','blockquote','pre'] 65 | 66 | action : (el,a,p) -> 67 | editor = $(".qeditor_preview",$(el).parent().parent()) 68 | editor.find(".qeditor_placeholder").remove() 69 | editor.focus() 70 | p = false if p == null 71 | 72 | # pre, blockquote params fix 73 | if a == "blockquote" or a == "pre" 74 | p = a 75 | a = "formatBlock" 76 | 77 | 78 | if a == "createLink" 79 | p = prompt("Type URL:") 80 | return false if p.trim().length == 0 81 | else if a == "insertimage" 82 | p = prompt("Image URL:") 83 | return false if p.trim().length == 0 84 | 85 | if QEditor.state(a) 86 | # remove style 87 | document.execCommand(a,false,null) 88 | else 89 | # apply style 90 | document.execCommand(a, false, p) 91 | QEditor.checkSectionState(editor) 92 | editor.change() 93 | false 94 | 95 | state: (action) -> 96 | document.queryCommandState(action) == true 97 | 98 | prompt : (title) -> 99 | val = prompt(title) 100 | if val 101 | return val 102 | else 103 | return false 104 | 105 | toggleFullScreen : (el) -> 106 | border = $(el).parent().parent() 107 | if border.data("qe-fullscreen") == "1" 108 | QEditor.exitFullScreen() 109 | else 110 | QEditor.enterFullScreen(border) 111 | 112 | false 113 | 114 | enterFullScreen : (border) -> 115 | border.data("qe-fullscreen","1") 116 | .addClass("qeditor_fullscreen") 117 | border.find(".qeditor_preview").focus() 118 | border.find(".qe-fullscreen span").attr("class","fa fa-compress") 119 | 120 | exitFullScreen : () -> 121 | $(".qeditor_border").removeClass("qeditor_fullscreen") 122 | .data("qe-fullscreen","0") 123 | .find(".qe-fullscreen span").attr("class","fa fa-arrows-alt") 124 | 125 | getCurrentContainerNode : () -> 126 | if window.getSelection 127 | node = window.getSelection().anchorNode 128 | containerNode = if node.nodeType == 3 then node.parentNode else node 129 | return containerNode 130 | 131 | checkSectionState : (editor) -> 132 | for a in QEditor.actions 133 | link = editor.parent().find(".qeditor_toolbar a[data-action=#{a}]") 134 | if QEditor.state(a) 135 | link.addClass("qe-state-on") 136 | else 137 | link.removeClass("qe-state-on") 138 | 139 | version : -> 140 | "0.2.0" 141 | 142 | do ($=jQuery)-> 143 | $.fn.qeditor = (options) -> 144 | this.each -> 145 | obj = $(this) 146 | obj.addClass("qeditor") 147 | editor = $('
') 148 | placeholder = $('
') 149 | 150 | $(document).keyup (e) -> 151 | QEditor.exitFullScreen() if e.keyCode == 27 152 | 153 | # use

tag on enter by default 154 | document.execCommand('defaultParagraphSeparator', false, 'p') 155 | 156 | currentVal = obj.val() 157 | # if currentVal.trim().lenth == 0 158 | # TODO: default value need in paragraph 159 | # currentVal = "

" 160 | 161 | editor.html(currentVal) 162 | editor.addClass(obj.attr("class")) 163 | obj.after(editor) 164 | 165 | # add place holder 166 | placeholder.text(obj.attr("placeholder")) 167 | editor.attr("placeholder",obj.attr("placeholder") || "") 168 | editor.append(placeholder) 169 | editor.focusin -> 170 | QEditor.checkSectionState(editor) 171 | $(this).find(".qeditor_placeholder").remove() 172 | editor.blur -> 173 | t = $(this) 174 | QEditor.checkSectionState(editor) 175 | if t.html().length == 0 or t.html() == "
" or t.html() == "

" 176 | $(this).html('
' + $(this).attr("placeholder") + '
' ) 177 | 178 | # put value to origin textare when QEditor has changed value 179 | editor.change -> 180 | pobj = $(this); 181 | t = pobj.parent().find('.qeditor') 182 | t.val(pobj.html()) 183 | 184 | # watch pasite event, to remove unsafe html tag, attributes 185 | editor.on "paste", -> 186 | txt = $(this) 187 | setTimeout -> 188 | els = txt.find("*") 189 | for attrName in QEDITOR_DISABLE_ATTRIBUTES_ON_PASTE 190 | els.removeAttr(attrName) 191 | els.find(":not(#{QEDITOR_ALLOW_TAGS_ON_PASTE})").contents().unwrap() 192 | txt.change() 193 | true 194 | ,100 195 | 196 | # attach change event on editor keyup 197 | editor.keyup (e) -> 198 | QEditor.checkSectionState(editor) 199 | $(this).change() 200 | 201 | editor.on "click", (e) -> 202 | QEditor.checkSectionState(editor) 203 | e.stopPropagation() 204 | 205 | editor.keydown (e) -> 206 | node = QEditor.getCurrentContainerNode() 207 | nodeName = "" 208 | if node and node.nodeName 209 | nodeName = node.nodeName.toLowerCase() 210 | if e.keyCode == 13 && !(e.shiftKey or e.ctrlKey) 211 | if nodeName == "blockquote" or nodeName == "pre" 212 | e.stopPropagation() 213 | document.execCommand('InsertParagraph',false) 214 | document.execCommand("formatBlock",false,"p") 215 | document.execCommand('outdent',false) 216 | return false 217 | 218 | 219 | obj.hide() 220 | obj.wrap('
') 221 | obj.after(editor) 222 | 223 | # render toolbar & binding events 224 | toolbar = $(QEDITOR_TOOLBAR_HTML) 225 | qe_heading = toolbar.find(".qe-heading") 226 | qe_heading.mouseenter -> 227 | $(this).addClass("hover") 228 | $(this).find(".qe-menu").show() 229 | qe_heading.mouseleave -> 230 | $(this).removeClass("hover") 231 | $(this).find(".qe-menu").hide() 232 | toolbar.find(".qe-heading .qe-menu a").click -> 233 | link = $(this) 234 | link.parent().parent().hide() 235 | QEditor.action(this,"formatBlock",link.data("name")) 236 | return false 237 | toolbar.find("a[data-action]").click -> 238 | QEditor.action(this,$(this).attr("data-action")) 239 | editor.before(toolbar) --------------------------------------------------------------------------------