jquery轻量级富文本编辑器Trumbowyg
html:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="renderer" content="webkit"> <title></title> <link rel="stylesheet" href="assets/trumbowyg.css"> ...... <script type="text/javascript" src="js/jquery.js"></script> ...... <script type="text/javascript" src="assets/trumbowyg.js"></script> ...... <script type="text/javascript" src="assets/trumbowyg.upload.js"></script> </head> <body> <div id="odiv" style="display:none;position:absolute;z-index:100;"> <img src="assets/pic/sx.png" title="缩小" border="0" alt="缩小" onclick="sub(-1);"/> <img src="assets/pic/fd.png" title="放大" border="0" alt="放大" onclick="sub(1)"/> <img src="assets/pic/cz.png" title="重置" border="0" alt="重置" onclick="sub(0)"/> <img src="assets/pic/sc.png" title="删除" border="0" alt="删除" onclick="del();odiv.style.display='none';"/> </div> <div onmousedown="show_element(event)" style="clear:both" id="customized-buttonpane" class="editor"></div> </body> </head>
trumbowyg.css
.trumbowyg-box,.editor{clear:both;display:block;position:relative;border:1px solid #DDD;width:100%;min-height:300px;margin:17px auto}.trumbowyg-box .editor{margin:0 auto;background:#FEFEFE}.trumbowyg-box.trumbowyg-fullscreen{background:#FEFEFE}.trumbowyg-editor,.trumbowyg-textarea{position:relative;padding:1% 2%;min-height:300px;width:96%;border-style:none;resize:none;outline:none}.trumbowyg-box-blur .trumbowyg-editor *{color:transparent !important;text-shadow:0 0 7px #333}.trumbowyg-box-blur .trumbowyg-editor img{filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=20);opacity:0.2}.trumbowyg-textarea{position:relative;display:block;overflow:auto;border:none;white-space:normal}.trumbowyg-button-pane{position:relative;width:100%;background:#ecf0f1;border-bottom:1px solid #d7e0e2;margin:0;padding:0;list-style-type:none;line-height:10px}.trumbowyg-button-pane li{display:inline-block;text-align:center;overflow:hidden}.trumbowyg-button-pane li.trumbowyg-separator{width:1px;background:#d7e0e2;margin:0 5px;height:35px}.trumbowyg-button-pane.trumbowyg-disable li:not(.trumbowyg-not-disable) button{opacity:.2;cursor:default}.trumbowyg-button-pane.trumbowyg-disable li.trumbowyg-separator{background:#e3e9eb}.trumbowyg-button-pane:not(.trumbowyg-disable) li button:hover,.trumbowyg-button-pane:not(.trumbowyg-disable) li button:focus,.trumbowyg-button-pane li button.trumbowyg-active,.trumbowyg-button-pane li.trumbowyg-not-disable button:hover,.trumbowyg-button-pane li.trumbowyg-not-disable button:focus{outline:none}.trumbowyg-button-pane li .trumbowyg-open-dropdown:after{display:block;content:" ";position:absolute;top:25px;right:3px;height:0;width:0;border:3px solid transparent;border-top-color:#555}.trumbowyg-button-pane .trumbowyg-buttons-right{float:right;width:auto}.trumbowyg-button-pane .trumbowyg-buttons-right button{float:left}.trumbowyg-dropdown{width:200px;border:1px solid #ecf0f1;padding:5px 0;border-top:none;background:#FFF;margin-left:-1px;-moz-box-shadow:rgba(0,0,0,0.1) 0 2px 3px;-webkit-box-shadow:rgba(0,0,0,0.1) 0 2px 3px;box-shadow:rgba(0,0,0,0.1) 0 2px 3px}.trumbowyg-dropdown button{display:block;width:100%;height:35px;line-height:35px;text-decoration:none;background:#FFF;padding:0 14px;color:#333;border:none;cursor:pointer;text-align:left;font-size:15px;-moz-transition:all 0.15s;-o-transition:all 0.15s;-webkit-transition:all 0.15s;transition:all 0.15s}.trumbowyg-dropdown button:hover,.trumbowyg-dropdown button:focus{background:#ecf0f1}.trumbowyg-modal{position:absolute;top:0;left:50%;margin-left:-260px;width:520px;height:290px;overflow:hidden}.trumbowyg-modal-box{position:absolute;top:0;left:50%;margin-left:-250px;width:400px;padding: 10px 10px 35px 30px;z-index:1;background-color:#FFF;text-align:center;-moz-box-shadow:rgba(0,0,0,0.2) 0 2px 3px;-webkit-box-shadow:rgba(0,0,0,0.2) 0 2px 3px;box-shadow:rgba(0,0,0,0.2) 0 2px 3px}.trumbowyg-modal-box .trumbowyg-modal-title{font-size:24px;font-weight:bold;margin:0 0 20px;padding:15px 0 13px;display:block;border-bottom:1px solid #EEE;color:#333;background:#fbfcfc}.trumbowyg-modal-box .trumbowyg-progress{width:80%;background:#F00;height:3px;position:absolute;top:58px}.trumbowyg-modal-box .trumbowyg-progress .trumbowyg-progress-bar{background:#2BC06A;height:100%;-moz-transition:width 0.15s linear;-o-transition:width 0.15s linear;-webkit-transition:width 0.15s linear;transition:width 0.15s linear}.trumbowyg-modal-box input,input[type=text]{width:100%;}.trumbowyg-modal-box label{display:block;position:relative;margin:15px 12px;height:27px;line-height:27px;overflow:hidden}.trumbowyg-modal-box label .trumbowyg-input-infos{display:block;text-align:left;height:25px;line-height:25px;-moz-transition:all 0.15s;-o-transition:all 0.15s;-webkit-transition:all 0.15s;transition:all 0.15s}.trumbowyg-modal-box label .trumbowyg-input-infos span{display:block;color:#859fa5;background-color:#fbfcfc;border:1px solid #DEDEDE;padding:0 2%;width:19.5%}.trumbowyg-modal-box label .trumbowyg-input-infos span.trumbowyg-msg-error{color:#e74c3c}.trumbowyg-modal-box label.trumbowyg-input-error input,.trumbowyg-modal-box label.trumbowyg-input-error textarea{border:1px solid #e74c3c}.trumbowyg-modal-box label.trumbowyg-input-error .trumbowyg-input-infos{margin-top:-27px}.trumbowyg-modal-box label input{position:absolute;top:0;right:20px;height:25px;line-height:25px;border:1px solid #DEDEDE;background:transparent;width:72%;padding:0 2%;margin:0 0 0 23%;-moz-transition:all 0.15s;-o-transition:all 0.15s;-webkit-transition:all 0.15s;transition:all 0.15s}.trumbowyg-modal-box label input:hover,.trumbowyg-modal-box label input:focus{outline:none;border:1px solid #95a5a6}.trumbowyg-modal-box label input:focus{background:rgba(230,230,255,0.1)}.trumbowyg-modal-box .error{margin-top:25px;display:block;color:red}.trumbowyg-modal-box .trumbowyg-modal-button{position:absolute;bottom:10px;right:0;text-decoration:none;color:#FFF;display:block;width:70px; height:35px;line-height:33px;margin:0 10px;background-color:#333;border:none;border-top:none;cursor:pointer;font-family:"Trebuchet MS", Helvetica, Verdana, sans-serif;-moz-transition:all 0.15s;-o-transition:all 0.15s;-webkit-transition:all 0.15s;transition:all 0.15s;margin-right: 100px;}.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-submit{right:110px;background:#2bc06a}.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-submit:hover,.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-submit:focus{background:#40d47e;outline:none}.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-submit:active{background:#25a25a}.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-reset{background:#b33528}.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-reset:hover,.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-reset:focus{background:#d14233;outline:none}.trumbowyg-modal-box .trumbowyg-modal-button.trumbowyg-modal-reset:active{background:#962d22}.trumbowyg-overlay{position:absolute;background-color:rgba(255,255,255,0.5);width:100%;left:0;display:none}.trumbowyg-fullscreen.trumbowyg-box,.trumbowyg-fullscreen .editor{border:none}.trumbowyg-fullscreen .trumbowyg-overlay{height:100% !important}.trumbowyg-editor object,.trumbowyg-editor embed,.trumbowyg-editor video,.trumbowyg-editor img{max-width:100%}.trumbowyg-editor video,.trumbowyg-editor img{cursor:move}.trumbowyg-editor.trumbowyg-reset-css{background:#FEFEFE !important;font-family:"Trebuchet MS", Helvetica, Verdana, sans-serif !important;font-size:14px !important;line-height:1.45em !important;white-space:normal !important;color:#333}.trumbowyg-editor.trumbowyg-reset-css a{color:#15c !important;text-decoration:underline !important}.trumbowyg-editor.trumbowyg-reset-css div,.trumbowyg-editor.trumbowyg-reset-css p,.trumbowyg-editor.trumbowyg-reset-css ul,.trumbowyg-editor.trumbowyg-reset-css ol,.trumbowyg-editor.trumbowyg-reset-css blockquote{box-shadow:none !important;background:none !important;margin:0 !important;margin-bottom:15px !important;line-height:1.4em !important;font-family:"Trebuchet MS", Helvetica, Verdana, sans-serif !important;font-size:14px !important;border:none}.trumbowyg-editor.trumbowyg-reset-css iframe,.trumbowyg-editor.trumbowyg-reset-css object,.trumbowyg-editor.trumbowyg-reset-css hr{margin-bottom:15px !important}.trumbowyg-editor.trumbowyg-reset-css blockquote{margin-left:32px !important;font-style:italic !important;color:#555}.trumbowyg-editor.trumbowyg-reset-css ul,.trumbowyg-editor.trumbowyg-reset-css ol{padding-left:20px !important}.trumbowyg-editor.trumbowyg-reset-css ul ul,.trumbowyg-editor.trumbowyg-reset-css ol ol,.trumbowyg-editor.trumbowyg-reset-css ul ol,.trumbowyg-editor.trumbowyg-reset-css ol ul{border:none;margin:2px !important;padding:0 !important;padding-left:24px !important}.trumbowyg-editor.trumbowyg-reset-css hr{display:block;height:1px;border:none;border-top:1px solid #CCC}.trumbowyg-editor.trumbowyg-reset-css h1,.trumbowyg-editor.trumbowyg-reset-css h2,.trumbowyg-editor.trumbowyg-reset-css h3,.trumbowyg-editor.trumbowyg-reset-css h4{color:#111;background:none;margin:0 !important;padding:0 !important;font-weight:bold}.trumbowyg-editor.trumbowyg-reset-css h1{font-size:32px !important;line-height:38px !important;margin-bottom:20px !important}.trumbowyg-editor.trumbowyg-reset-css h2{font-size:26px !important;line-height:34px !important;margin-bottom:15px !important}.trumbowyg-editor.trumbowyg-reset-css h3{font-size:22px !important;line-height:28px !important;margin-bottom:7px !important}.trumbowyg-editor.trumbowyg-reset-css h4{font-size:16px !important;line-height:22px !important;margin-bottom:7px !important}.trumbowyg-button-pane li button{display:block;position:relative;text-indent:-9999px;width:35px;height:35px;overflow:hidden;background:transparent url('icons-sa75ce98b2b.png') no-repeat;border:none;cursor:pointer;-moz-transition:all 0.15s;-o-transition:all 0.15s;-webkit-transition:all 0.15s;transition:all 0.15s;transition:background-color .15s, background-image .15s, opacity .15s}.trumbowyg-button-pane li button.trumbowyg-viewHTML-button{background-position:5px -545px}.trumbowyg-button-pane li button.trumbowyg-formatting-button{background-position:5px -120px}.trumbowyg-button-pane li button.trumbowyg-bold-button,.trumbowyg-button-pane li button.trumbowyg-strong-button{background-position:5px -45px}.trumbowyg-button-pane li button.trumbowyg-italic-button,.trumbowyg-button-pane li button.trumbowyg-em-button{background-position:5px -270px}.trumbowyg-button-pane li button.trumbowyg-underline-button{background-position:5px -470px}.trumbowyg-button-pane li button.trumbowyg-strikethrough-button,.trumbowyg-button-pane li button.trumbowyg-del-button{background-position:5px -445px}.trumbowyg-button-pane li button.trumbowyg-link-button{background-position:5px -345px}.trumbowyg-button-pane li button.trumbowyg-insertImage-button{background-position:5px -245px}.trumbowyg-button-pane li button.trumbowyg-justifyLeft-button{background-position:5px -320px}.trumbowyg-button-pane li button.trumbowyg-justifyCenter-button{background-position:5px -70px}.trumbowyg-button-pane li button.trumbowyg-justifyRight-button{background-position:5px -395px}.trumbowyg-button-pane li button.trumbowyg-justifyFull-button{background-position:5px -295px}.trumbowyg-button-pane li button.trumbowyg-unorderedList-button{background-position:5px -495px}.trumbowyg-button-pane li button.trumbowyg-orderedList-button{background-position:5px -370px}.trumbowyg-button-pane li button.trumbowyg-horizontalRule-button{background-position:5px -220px}.trumbowyg-button-pane li button.trumbowyg-fullscreen-button{background-position:5px -170px}.trumbowyg-button-pane li button.trumbowyg-close-button{background-position:5px -95px}.trumbowyg-fullscreen .trumbowyg-button-pane li button.trumbowyg-fullscreen-button{background-position:5px -145px}.trumbowyg-button-pane li:first-child button{margin-left:6px}.trumbowyg-button-pane li:last-child button{margin-right:6px}.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-bold-button,.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-strong-button{background-position:5px -195px}.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-underline-button{background-position:5px -420px}.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-strikethrough-button,.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-del-button{background-position:5px -20px}@media only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 4 / 3), only screen and (min-device-pixel-ratio: 1.3), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx){.trumbowyg-button-pane li button{background-size:72%;background-image:url('../images/icons-2x-s9034954e6d.png')}.trumbowyg-button-pane li button.trumbowyg-viewHTML-button{background-position:5px -545px}.trumbowyg-button-pane li button.trumbowyg-formatting-button{background-position:5px -120px}.trumbowyg-button-pane li button.trumbowyg-bold-button,.trumbowyg-button-pane li button.trumbowyg-strong-button{background-position:5px -45px}.trumbowyg-button-pane li button.trumbowyg-italic-button,.trumbowyg-button-pane li button.trumbowyg-em-button{background-position:5px -270px}.trumbowyg-button-pane li button.trumbowyg-underline-button{background-position:5px -470px}.trumbowyg-button-pane li button.trumbowyg-strikethrough-button,.trumbowyg-button-pane li button.trumbowyg-del-button{background-position:5px -445px}.trumbowyg-button-pane li button.trumbowyg-link-button{background-position:5px -345px}.trumbowyg-button-pane li button.trumbowyg-insertImage-button{background-position:5px -245px}.trumbowyg-button-pane li button.trumbowyg-justifyLeft-button{background-position:5px -320px}.trumbowyg-button-pane li button.trumbowyg-justifyCenter-button{background-position:5px -70px}.trumbowyg-button-pane li button.trumbowyg-justifyRight-button{background-position:5px -395px}.trumbowyg-button-pane li button.trumbowyg-justifyFull-button{background-position:5px -295px}.trumbowyg-button-pane li button.trumbowyg-unorderedList-button{background-position:5px -495px}.trumbowyg-button-pane li button.trumbowyg-orderedList-button{background-position:5px -370px}.trumbowyg-button-pane li button.trumbowyg-horizontalRule-button{background-position:5px -220px}.trumbowyg-button-pane li button.trumbowyg-fullscreen-button{background-position:5px -170px}.trumbowyg-button-pane li button.trumbowyg-close-button{background-position:5px -95px}.trumbowyg-fullscreen .trumbowyg-button-pane li a.trumbowyg-fullscreen-button{background-position:5px -145px}.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-bold-button,.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-strong-button{background-position:5px -195px}.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-underline-button{background-position:5px -420px}.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-strikethrough-button,.trumbowyg-fr .trumbowyg-button-pane li button.trumbowyg-del-button{background-position:5px -20px}} html, body { margin: 0; padding: 0; } header { text-align: center; } #main { max-width: 960px; margin: 0 auto; }
trumbowyg.js
/* =========================================================== * trumbowyg.js v1.0 * Core code of Trumbowyg plugin * http://alex-d.github.com/Trumbowyg * =========================================================== * Author : Alexandre Demode (Alex-D) * Twitter : @AlexandreDemode * Website : alex-d.fr */ $.trumbowyg = { langs: { en: { viewHTML: "View HTML", formatting: "Formatting", p: "Paragraph", blockquote: "Quote", code: "Code", header: "Header", bold: "Bold", italic: "Italic", strikethrough: "Stroke", underline: "Underline", strong: "Strong", em: "Emphasis", del: "Deleted", unorderedList: "Unordered list", orderedList: "Ordered list", insertImage: "Insert Image", insertVideo: "Insert Video", link: "Link", createLink: "Insert link", unlink: "Remove link", justifyLeft: "Align Left", justifyCenter: "Align Center", justifyRight: "Align Right", justifyFull: "Align Justify", horizontalRule: "Insert horizontal rule", fullscreen: "fullscreen", close: "Close", submit: "Confirm", reset: "Cancel", invalidUrl: "Invalid URL", required: "Required", description: "Description", title: "Title", text: "Text" } }, // User default options opts: {}, btnsGrps: { design: ['bold', 'italic', 'underline', 'strikethrough'], semantic: ['strong', 'em', 'del'], justify: ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], lists: ['unorderedList', 'orderedList'] } }; (function($){ $.fn.trumbowyg = function(opts, params){ if($.isObject(opts) || opts == null){ return this.each(function(){ if(!$(this).data('trumbowyg')) $(this).data('trumbowyg', new Trumbowyg(this, opts)); }); } else if(this.length == 1){ try { var t = $(this).data('trumbowyg'); switch(opts){ // Modal box case 'openModal': return t.openModal(params.title, params.content); case 'closeModal': return t.closeModal(); case 'openModalInsert': return t.openModalInsert(params.title, params.fields, params.callback); // Selection case 'saveSelection': return t.saveSelection(); case 'getSelection': return t.selection; case 'getSelectedText': return t.selection+''; case 'restoreSelection': return t.restoreSelection(); // Destroy case 'destroy': return t.destroy(); // Empty case 'empty': return t.empty(); // Public options case 'lang': return t.lang; case 'duration': return t.o.duration; // HTML case 'html': return t.html(params); } } catch(e){} } return false; }; var Trumbowyg = function(editorElem, opts){ // jQuery object of the editor this.$e = $(editorElem); this.$creator = $(editorElem); // Extend with options opts = $.extend(true, {}, opts, $.trumbowyg.opts); // Localization management if(typeof opts.lang === 'undefined' || typeof $.trumbowyg.langs[opts.lang] === 'undefined') this.lang = $.trumbowyg.langs['en']; else this.lang = $.extend(true, {}, $.trumbowyg.langs['en'], $.trumbowyg.langs[opts.lang]); // Defaults Options this.o = $.extend(true, { lang: 'en', dir: 'ltr', duration: 200, // Duration of modal box animations mobile: false, tablet: true, closable: false, fullscreenable: true, fixedBtnPane: false, fixedFullWidth: false, semantic: false, resetCss: false, autogrow: false, prefix: 'trumbowyg-', convertLink: true, btns: ['viewHTML', '|', 'formatting', '|', $.trumbowyg.btnsGrps.design, '|', 'link', '|', 'insertImage', '|', $.trumbowyg.btnsGrps.justify, '|', $.trumbowyg.btnsGrps.lists, '|', 'horizontalRule'], btnsAdd: [], /** * When the button is associated to a empty object * func and title attributs are defined from the button key value * * For example * foo: {} * is equivalent to : * foo: { * func: 'foo', * title: this.lang.foo * } */ btnsDef: { viewHTML: { func: 'toggle' }, p: { func: 'formatBlock' }, blockquote: { func: 'formatBlock' }, h1: { func: 'formatBlock', title: this.lang.header + ' 1' }, h2: { func: 'formatBlock', title: this.lang.header + ' 2' }, h3: { func: 'formatBlock', title: this.lang.header + ' 3' }, h4: { func: 'formatBlock', title: this.lang.header + ' 4' }, bold: {}, italic: {}, underline: {}, strikethrough: {}, strong: { func: 'bold' }, em: { func: 'italic' }, del: { func: 'strikethrough' }, createLink: {}, unlink: {}, insertImage: {}, justifyLeft: {}, justifyCenter: {}, justifyRight: {}, justifyFull: {}, unorderedList: { func: 'insertUnorderedList' }, orderedList: { func: 'insertOrderedList' }, horizontalRule: { func: 'insertHorizontalRule' }, // Dropdowns formatting: { dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4'] }, link: { dropdown: ['createLink', 'unlink'] } } }, opts); if(this.o.semantic && !opts.btns) this.o.btns = [ 'viewHTML', '|', 'formatting', '|', $.trumbowyg.btnsGrps.semantic, '|', 'link', '|', 'insertImage', '|', $.trumbowyg.btnsGrps.justify, '|', $.trumbowyg.btnsGrps.lists, '|', 'horizontalRule' ]; else if(opts && opts.btns) this.o.btns = opts.btns; this.init(); } Trumbowyg.prototype = { init: function(){ this.height = this.$e.css('height'); if(this.isEnabled()){ this.buildEditor(true); return; } this.buildEditor(); this.buildBtnPane(); this.fixedBtnPaneEvents(); this.buildOverlay(); }, buildEditor: function(disable){ if(disable === true){ if(!this.$e.is('textarea')){ var textarea = this.buildTextarea().val(this.$e.val()); this.$e.hide().after(textarea); } return; } this.$box = $('<div/>', { 'class': this.o.prefix + 'box ' + this.o.prefix + this.o.lang + ' trumbowyg' }); this.isTextarea = true; if(this.$e.is('textarea')) this.$editor = $('<div/>'); else { this.$editor = this.$e; this.$e = this.buildTextarea().val(this.$e.val()); this.isTextarea = false; } this.$e.hide() .addClass(this.o.prefix + 'textarea'); var html = ''; if(this.isTextarea){ html = this.$e.val(); this.$box.insertAfter(this.$e) .append(this.$editor) .append(this.$e); } else { html = this.$editor.html(); this.$box.insertAfter(this.$editor) .append(this.$e) .append(this.$editor); this.syncCode(); } this.$editor.addClass(this.o.prefix + 'editor') .attr('contenteditable', true) .attr('dir', this.o.dir) .html(html); if(this.o.resetCss) this.$editor.addClass(this.o.prefix + 'reset-css'); if(!this.o.autogrow){ $.each([this.$editor, this.$e], $.proxy(function(i, $el){ $el.css({ overflow: 'auto' }); }, this)); } if(this.o.semantic){ this.$editor.html( this.$editor.html() .replace("<br>", "</p><p>") .replace("&nsbp;", "") ); this.semanticCode(); } var that = this; this.$editor .on('dblclick', 'img', function(){ var $img = $(this); that.openModalInsert(that.lang.insertImage, { url: { label: 'URL', value: $img.attr('src'), required: true }, alt: { label: 'description', value: $img.attr('alt') } }, function(values){ $img.attr('src', values['url']); $img.attr('alt', values['alt']); }); return false; }) .on('keyup', function(e){ that.semanticCode(false, e.which === 13); }) .on('blur', function(){ that.syncCode(); }); }, // Build the Textarea which contain HTML generated code buildTextarea: function(){ return $('<textarea/>', { 'name': this.$e.attr('id'), 'height': this.height }); }, // Build button pane, use o.btns and o.btnsAdd options buildBtnPane: function(){ var t = this; if(t.o.btns === false) return; var pfx = t.o.prefix; t.$btnPane = $('<ul/>', { 'class': pfx + 'button-pane' }); $.each(t.o.btns.concat(t.o.btnsAdd), $.proxy(function(i, btn){ // Managment of group of buttons try { var b = btn.split('btnGrp-'); if(b[1] != undefined) btn = $.trumbowyg.btnsGrps[b[1]]; } catch(e){} if(!$.isArray(btn)) btn = [btn]; $.each(btn, $.proxy(function(i, btn){ try { // Prevent buildBtn error var $li = $('<li/>'); if(btn === '|') // It's a separator $li.addClass(pfx + 'separator'); else if(t.isSupportedBtn(btn)){ // It's a supported button if(btn === 'viewHTML') $li.addClass(pfx + 'not-disable'); $li.append(t.buildBtn(btn)); } t.$btnPane.append($li); } catch(e){} }, t)); }, t)); // Build right li for fullscreen and close buttons var $liRight = $('<li/>', { 'class': pfx + 'not-disable ' + pfx + 'buttons-right' }); // Add the fullscreen button if(t.o.fullscreenable) $liRight .append(t.buildRightBtn('fullscreen') .on('click', $.proxy(function(e){ var cssClass = pfx + 'fullscreen'; t.$box.toggleClass(cssClass); if(t.$box.hasClass(cssClass)){ $('body').css('overflow', 'hidden'); t.$box.css({ position: 'fixed', top: 0, left: 0, width: '100%', margin: 0, padding: 0, zIndex: 99999 }); $([t.$editor, t.$e]).each(function(){ $(this).css({ overflow: 'auto' }); }); t.$btnPane.css('width', '100%'); $(".trumbowyg-fullscreen .editor").css("height",$(window).height()-65); } else { $('body').css('overflow', 'auto'); t.$box.removeAttr('style'); if(!t.o.autogrow){ h = t.height; $([t.$editor, t.$e]).each(function(i, $el){ $el.css('height', h); }); } } $(window).trigger('scroll'); }, t))); // Build and add close button if(t.o.closable) $liRight .append(t.buildRightBtn('close') .on('click', $.proxy(function(e){ var cssClass = pfx + 'fullscreen'; if(t.$box.hasClass(cssClass)) $('body').css('overflow', 'auto'); t.destroy(); }, t))); // Add right li only if isn't empty if($liRight.not(':empty')) t.$btnPane.append($liRight); t.$box.prepend(t.$btnPane); }, // Build a button and his action buildBtn: function(name){ var pfx = this.o.prefix, btnDef = this.o.btnsDef[name], t = this, textDef = this.lang[name] || name.charAt(0).toUpperCase() + name.slice(1); var $btn = $('<button/>', { 'type': 'button', 'class': pfx + name +'-button' + (btnDef.ico ? ' '+ pfx + btnDef.ico +'-button' : ''), 'text': btnDef.text || btnDef.title || textDef, 'title': btnDef.title || btnDef.text || textDef, 'mousedown': function(e){ if(!btnDef.dropdown || t.$box.find('.'+name+'-'+pfx + 'dropdown').is(':hidden')) $('body').trigger('mousedown'); if(t.$btnPane.hasClass(pfx + 'disable') && !$(this).parent().hasClass(pfx + 'not-disable')) return false; t.execCommand((btnDef.dropdown ? 'dropdown' : false) || btnDef.func || name, btnDef.param || name); e.stopPropagation(); e.preventDefault(); return false; } }); if(btnDef.dropdown){ $btn.addClass(pfx + 'open-dropdown'); var cssClass = pfx + 'dropdown'; var dropdown = $('<div/>', { 'class': name + '-' + cssClass + ' ' + cssClass + ' ' + pfx + 'fixed-top' }); for(var i = 0, c = btnDef.dropdown.length; i < c; i++){ if(t.o.btnsDef[btnDef.dropdown[i]] && t.isSupportedBtn(btnDef.dropdown[i])) dropdown.append(t.buildSubBtn(btnDef.dropdown[i])); } this.$box.append(dropdown.hide()); } return $btn; }, // Build a button for dropdown menu buildSubBtn: function(name){ var btnDef = this.o.btnsDef[name]; return $('<button/>', { 'type': 'button', 'text': btnDef.text || btnDef.title || this.lang[name] || name, 'mousedown': $.proxy(function(e){ $('body').trigger('mousedown'); this.execCommand(btnDef.func || name, btnDef.param || name); e.stopPropagation(); e.preventDefault(); return false; }, this) }); }, // Build a button for right li buildRightBtn: function(name){ return $('<button/>', { 'type': 'button', 'class': this.o.prefix + name+'-button', 'title': this.lang[name], 'text': this.lang[name] }); }, // Check if button is supported isSupportedBtn: function(btn){ return typeof this.o.btnsDef[btn].isSupported !== 'function' || this.o.btnsDef[btn].isSupported() }, // Build overlay for modal box buildOverlay: function(){ return this.$overlay = $('<div/>', { 'class': this.o.prefix + 'overlay' }).css({ top: this.$btnPane.outerHeight(), height: (parseInt(this.$editor.outerHeight()) + 1) + 'px' }).appendTo(this.$box); }, showOverlay: function(){ $(window).trigger('scroll'); this.$overlay.fadeIn(this.o.duration); this.$box.addClass(this.o.prefix + 'box-blur'); }, hideOverlay: function(){ this.$overlay.fadeOut(this.o.duration/4); this.$box.removeClass(this.o.prefix + 'box-blur'); }, // Management of fixed button pane fixedBtnPaneEvents: function(){ if(!this.o.fixedBtnPane) return; this.isFixed = false; $(window).on('scroll', $.proxy(function(e){ if(!this.$box) return; this.syncCode(); var wScroll = $(window).scrollTop(), offset = this.$box.offset().top + 1, toFixed = (wScroll - offset > 0) && ((wScroll - offset - parseInt(this.height)) < 0); if(toFixed){ if(!this.isFixed){ this.isFixed = true; this.$btnPane.css({ position: 'fixed', top: 0, left: (this.o.fixedFullWidth) ? '0' : 'auto', width: (this.o.fixedFullWidth) ? '100%' : ((parseInt(this.$box.css('width'))-1) + 'px'), zIndex: 7 }); this.$editor.css({ marginTop: this.$btnPane.css('height') }); this.$e.css({ marginTop: this.$btnPane.css('height') }); } $('.' + this.o.prefix + 'fixed-top', this.$box).css({ position: this.o.fixedFullWidth ? 'fixed' : 'absolute', top: this.o.fixedFullWidth ? this.$btnPane.outerHeight() : parseInt(this.$btnPane.outerHeight()) + (wScroll - offset) + 'px', zIndex: 15 }); } else if(this.isFixed) { this.isFixed = false; this.$btnPane.css({ position: 'relative' }); this.$editor.css({ marginTop: 0 }); this.$e.css({ marginTop: 0 }); $('.' + this.o.prefix + 'fixed-top', this.$box).css({ position: 'absolute', top: this.$btnPane.outerHeight() }); } }, this)); }, // Destroy the editor destroy: function(){ var html = this.html(); if(this.isTextarea) this.$box.after(this.$e.css({height: this.height}) .val(html) .removeClass(this.o.prefix + 'textarea') .show()); else this.$box.after(this.$editor.css({height: this.height}) .removeClass(this.o.prefix + 'editor') .attr('contenteditable', false) .html(html) .show()); this.$box.remove(); this.$creator.removeData('trumbowyg'); }, // Empty the editor empty: function(){ this.$e.val(''); this.syncCode(true); }, // Function call when click on viewHTML button toggle: function(){ this.semanticCode(false, true); this.$editor.toggle(); this.$e.toggle(); this.$btnPane.toggleClass(this.o.prefix + 'disable'); this.$btnPane.find('.'+this.o.prefix + 'viewHTML-button').toggleClass(this.o.prefix + 'active'); }, // Open dropdown when click on a button which open that dropdown: function(name){ var pfx = this.o.prefix; var $dropdown = this.$box.find('.'+name+'-'+pfx + 'dropdown'), $btn = this.$btnPane.find('.'+pfx+name+'-button'); if($dropdown.is(':hidden')){ $btn.addClass(this.o.prefix + 'active'); $dropdown.css({ position: 'absolute', top: this.$btnPane.outerHeight(), left: (this.o.fixedFullWidth && this.isFixed) ? $btn.offset().left+'px' : ($btn.offset().left - this.$btnPane.offset().left)+'px' }).show(); $(window).trigger('scroll'); $('body').on('mousedown', $.proxy(function(e){ $('.' + pfx + 'dropdown').hide(); $('.' + pfx + 'active').removeClass(pfx + 'active'); $('body').off('mousedown'); }, this)); } else { $('body').trigger('mousedown'); } }, // HTML Code management html: function(html){ if(html){ this.$e.val(html); this.syncCode(true); return tbw; } else return this.$e.val(); }, syncCode: function(force){ if(!force && this.$editor.is(':visible')) this.$e.val(this.$editor.html()); else this.$editor.html(this.$e.val()); if(this.o.autogrow){ this.height = this.$editor.css('height'); this.$e.css({ height: this.height }); } }, // Analyse and update to semantic code // @param force : force to sync code from textarea // @param full : wrap text nodes in <p> semanticCode: function(force, full){ this.syncCode(force); if(this.o.semantic){ this.semanticTag('b', 'strong'); this.semanticTag('i', 'em'); this.semanticTag('strike', 'del'); if(full){ // Wrap text nodes in p this.$editor.contents() .filter(function(){ // Only non-empty text nodes return this.nodeType === 3 && $.trim(this.nodeValue).length > 0; }).wrap('<p></p>').end() // Remove all br .filter("br").remove(); this.saveSelection(); this.semanticTag('div', 'p'); this.restoreSelection(); } this.$e.val(this.$editor.html()); } }, semanticTag: function(oldTag, newTag){ $(oldTag, this.$editor).each(function(){ $(this).replaceWith(function(){ return '<'+newTag+'>' + $(this).html() + '</'+newTag+'>'; }); }); }, // Function call when user click on « Insert Link » createLink: function(){ var that = this; this.saveSelection(); this.openModalInsert(this.lang.createLink, { url: { label: 'URL', value: 'http://', required: true }, title: { label: this.lang.title, value: this.selection }, text: { label: this.lang.text, value: this.selection } }, function(values){ that.execCommand('createLink', values['url']); var link = $(['a[href="', values['url'], '"]:not([title])'].join(''), that.$box); if($.trim(values['text']).length !== 0) link.text(values['text']); if($.trim(values['title']).length !== 0) link.attr('title', values['title']); return true; }); }, insertImage: function(){ //图片上传 var that = this; this.saveSelection(); this.openModalInsert(this.lang.insertImage, { url: { label: 'URL', value: '', required: true }, alt: { label: 'description', value: this.selection } }, function(values){ that.execCommand('insertImage', values['url']); $(['img[src="', values['url'], '"]:not([alt])'].join(''), that.$box).attr('alt', values['alt']); return true; }); }, /* * Call method of trumbowyg if exist * else try to call anonymous function * and finaly native execCommand */ execCommand: function(cmd, param){ if(cmd != 'dropdown') this.$editor.focus(); try { this[cmd](param); } catch(e){ try { cmd(param, this); } catch(e){ this.$editor.focus(); if(cmd == 'insertHorizontalRule') param = null; document.execCommand(cmd, false, param); } } this.syncCode(); }, formatBlock: function(param){ if($.browser.msie) param = '<' + param + '>'; document.execCommand('formatBlock', false, param); }, // Open a modal box openModal: function(title, content){ var pfx = this.o.prefix; // No open a modal box when exist other modal box if($('.' + pfx + 'modal-box', this.$box).size() > 0) return false; this.saveSelection(); this.showOverlay(); // Disable all btnPane btns this.$btnPane.addClass(pfx + 'disable'); $('.' + pfx + 'not-disable', this.$btnPane) .not('.' + pfx + 'buttons-right') .removeClass(pfx + 'not-disable') .addClass(pfx + 'not-disable-old'); // Build out of ModalBox, it's the mask for animations var $modal = $('<div/>', { 'class': pfx + 'modal ' + pfx + 'fixed-top' }).css({ top: (parseInt(this.$btnPane.css('height')) + 1) + 'px' }).appendTo(this.$box); // Click on overflay close modal by cancelling them this.$overlay.one('click', function(e){ e.preventDefault(); $modal.trigger(pfx + 'cancel'); }); $e = this.$editor; // Build the form $form = $('<form/>', { action: 'javascript:void(null);', html: content }) .on('submit', function(e){ e.preventDefault(); $modal.trigger(pfx + 'confirm'); }) .on('reset', function(e){ e.preventDefault(); $modal.trigger(pfx + 'cancel'); }); // Build ModalBox and animate to show them var $modalBox = $('<div/>', { 'class': pfx + 'modal-box', html: $form }) .css({ top: '-' + parseInt(this.$btnPane.outerHeight()) + 'px', opacity: 0 }) .appendTo($modal) .animate({ top: 0, opacity: 1 }, this.o.duration / 2); // Append title $('<span/>', { text: title, 'class': pfx + 'modal-title' }).prependTo($modalBox); // Focus in modal box $modalBox.find('input:first').focus(); // Append Confirm and Cancel buttons this.buildModalBtn('submit', $modalBox); this.buildModalBtn('reset', $modalBox); $('body').trigger('scroll'); return $modal; }, buildModalBtn: function(name, modal){ return $('<button/>', { 'class': this.o.prefix + 'modal-button ' + this.o.prefix + 'modal-' + name, 'type': name, 'text': this.lang[name] || name }).appendTo(modal.find('form')); }, // close current modal box closeModal: function(){ var pfx = this.o.prefix; this.$btnPane.removeClass(pfx + 'disable'); this.$overlay.off(); $('.' + this.o.prefix + 'not-disable-old', this.$btnPane) .removeClass(pfx + 'not-disable-old') .addClass(pfx + 'not-disable'); var that = this, $modalBox = $('.' + pfx + 'modal-box', this.$box); $modalBox.animate({ top: '-' + $modalBox.css('height') }, this.o.duration/2, function(){ $(this).parent().remove(); that.hideOverlay(); }); }, // Preformated build and management modal openModalInsert: function(title, fields, cmd){ var html = '', pfx = this.o.prefix; for(f in fields){ var fd = fields[f]; label = (fd.label == undefined) ? (this.lang[f] ? this.lang[f] : f.charAt(0).toUpperCase() + f.slice(1)) : (this.lang[fd.label] ? this.lang[fd.label] : fd.label) ; if(fd.name == undefined) fd.name = f; if(!fd.pattern && f == 'url'){ fd.pattern = /^(http|https):\/\/([\w~#!:.?+=&%@!\-\/]+)$/; fd.patternError = this.lang.invalidUrl; } html += '<label><input type="'+(fd.type || 'text')+'" name="'+fd.name+'" value="'+(fd.value || '')+'"><span class="'+pfx+'input-infos"><span>'+label+'</span></span></label>'; } var modBox = this.openModal(title, html), that = this; modBox .on(pfx + 'confirm', function(){ var $form = $(this).find('form'), valid = true, values = {}; for(f in fields) { var $field = $('input[name="'+f+'"]', $form); values[f] = $field.val(); // Validate value if(fields[f].required && (values[f] == null || values[f] == undefined || $.trim(values[f]) == "")) { valid = false; that.addErrorOnModalField($field, that.lang.required); } else if(fields[f].pattern && !fields[f].pattern.test(values[f])) { valid = false; that.addErrorOnModalField($field, fields[f].patternError); } } if(valid) { that.restoreSelection(); if(cmd(values, fields)) { that.syncCode(); that.closeModal(); modBox.off(pfx + 'confirm'); } } }) .one(pfx + 'cancel', function(){ modBox.off(pfx + 'confirm'); that.closeModal(); that.restoreSelection(); }); return modBox; }, addErrorOnModalField: function($field, err){ var $label = $field.parent(), pfx = this.o.prefix; $label.addClass(pfx + 'input-error'); $field.on('change keyup', function(){ $label.removeClass(pfx + 'input-error'); }); $label.find('input+span').append('<span class="'+ pfx +'msg-error">'+ err +'</span>'); }, // Selection management saveSelection: function(){ this.selection = null; if(window.getSelection){ var sel = window.getSelection(); if(sel.getRangeAt && sel.rangeCount) this.selection = sel.getRangeAt(0); } else if(document.selection && document.selection.createRange){ this.selection = document.selection.createRange(); } }, restoreSelection: function(){ range = this.selection; if(range){ if(window.getSelection){ var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } else if(document.selection && range.select){ range.select(); } } }, // Return true if must enable Trumbowyg on this mobile device isEnabled: function(){ var mobile = "iPhone|iPod|Android|BlackBerry|Windows\sPhone|ZuneWP7"; var exprTablet = new RegExp("(iPad|webOS)"); var exprMobile = new RegExp("("+mobile+")"); return (this.o.tablet === true && exprTablet.test(navigator.userAgent)) || (this.o.mobile === true && exprMobile.test(navigator.userAgent)); } }; /* isObject */ var toString = Object.prototype.toString, hasOwnProp = Object.prototype.hasOwnProperty; $.isObject = function(obj) { if(toString.call(obj) !== "[object Object]") return false; var key; for(key in obj){} return !key || hasOwnProp.call(obj, key); }; $.isString = function(str){ return typeof(str) === 'string' }; })(jQuery); function del() { var element=document.getElementById("demoImage"); if(element) { element.parentNode.removeChild(element); } } function show_element(e) { if(!e) { var e = window.event; } //获取事件点击元素 var targ = e.target; //获取元素名称 var tname = targ.tagName; var oDiv=document.getElementById('odiv'); if(tname=="IMG") { var scrollTop=Math.max(document.documentElement.scrollTop,document.body.scrollTop); oDiv.style.left=e.clientX+5+'px'; oDiv.style.top=e.clientY+5+scrollTop+'px'; oDiv.style.display="inline"; targ.width=targ.width; var s=document.getElementById('demoImage'); if(s!=null) { s.id=""; } targ.id='demoImage' }else{ var wait = setInterval(function(){ if (oDiv != 0 && oDiv != null && oDiv != undefined) { clearInterval(wait); oDiv.style.display="none"; } },50); } } function tim(m) { var s=document.getElementById('demoImage'); s.width=s.width+m; s.width=s.width; } function sub(m) { var s=document.getElementById('demoImage'); if(s!=null) { if(m!=0) { var t=s.width/10; while(t>0) { if(m==1) setTimeout('tim(1)','50'); else setTimeout('tim(-1);','50'); t=t-1; } } else s.width=-1; s.width=s.width; } } /* =========================================================== * zh_cn.js * Simplified Chinese translation for Trumbowyg * http://alex-d.github.com/Trumbowyg * =========================================================== * Author : Liu Kai (akai) * Twitter : @akai404 * Github : https://github.com/akai */ $.trumbowyg.langs.zh_cn = { viewHTML: "源代码", formatting: "格式", p: "段落", blockquote: "引用", code: "代码", header: "标题", bold: "加粗", italic: "斜体", strikethrough: "删除线", underline: "下划线", strong: "加粗", em: "斜体", del: "删除线", unorderedList: "无序列表", orderedList: "有序列表", image: "图片", insertImage: "网络图片", upload: "本地上传", uploadError: "上传失败", insertVideo: "插入视频", link: "超链接", createLink: "插入链接", unlink: "取消链接", align: "对齐方式", justifyLeft: "居左对齐", justifyCenter: "居中对齐", justifyRight: "居右对齐", justifyFull: "两端对齐", horizontalRule: "插入分隔线", fullscreen: "全屏", close: "关闭", submit: "确定", reset: "取消", invalidUrl: "无效的 URL", required: "必需的", description: "描述", title: "标题", text: "文字" } $(function(){ $.trumbowyg.btnsGrps.test = ['bold','italic','underline','link']; $('#customized-buttonpane').trumbowyg({ lang: 'zh_cn', fixedBtnPane: true, btnsDef: { align: { dropdown: ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], ico: 'justifyLeft' }, image: { dropdown: ['insertImage', 'upload'], ico: 'insertImage' } }, btns: ['viewHTML', '|', 'formatting', '|', 'btnGrp-test', '|', 'align', '|', 'btnGrp-lists', '|', 'image'] }); $('.editor').on('dblclick', function(e){ $(this).trumbowyg({ lang: 'zh_cn', closable: false, fixedBtnPane: true }); }); });
trumbowyg.upload.js
/* =========================================================== * trumbowyg.upload.js v1.0 * Upload plugin for Trumbowyg * http://alex-d.github.com/Trumbowyg * =========================================================== * Author : Alexandre Demode (Alex-D) * Twitter : @AlexandreDemode * Website : alex-d.fr */ (function($){ addXhrProgressEvent(); $.extend(true, $.trumbowyg, { upload: { serverPath: urlcore + '/file/upload/batch' }, opts: { btnsDef: { upload: { func: function(params, tbw){ var file, pfx = tbw.o.prefix; var $modal = tbw.openModalInsert( // Title tbw.lang['upload'], // Fields { file: { type: 'file', required: true }, alt: { label: 'description' } }, // Callback function(values, fields){ var data = new FormData(); data.append('files', file); if($('.' + pfx +'progress', $modal).length == 0) $('.' + pfx + 'modal-title', $modal) .after( $('<div/>', { 'class': pfx +'progress' }) .append( $('<div/>', { 'class': pfx +'progress-bar' }) ) ); $.ajax({ url: $.trumbowyg.upload.serverPath, type: 'POST', data: data, cache: false, dataType: 'json', processData: false, contentType: false, progressUpload: function(e){ $('.' + pfx + 'progress').html( '<div class="trumbowyg-progress-bar" style="overflow: hidden; width: '+e.loaded * 100 / e.total+'%;"></div>'+ parseInt(e.loaded * 100 / e.total) + '%' ); }, success: function(data){ if (data.success == true) { for(var i in data.data){ tbw.execCommand('insertImage', data.data[i]); } setTimeout(function(){ tbw.closeModal(); }, 250); } else { tbw.addErrorOnModalField( $('input[type=file]', $modal), tbw.lang[data.message] ); } }, error: function(data){ tbw.addErrorOnModalField( $('input[type=file]', $modal), tbw.lang['uploadError'] ); } }); } ); $('input[type=file]').on('change', function(e){ file = e.target.files[0]; }); } } } } }); function addXhrProgressEvent(){ var originalXhr = $.ajaxSettings.xhr; $.ajaxSetup({ xhr: function() { var req = originalXhr(), that = this; if(req && typeof req.upload == "object" && that.progressUpload !== undefined) req.upload.addEventListener("progress", function(e){ that.progressUpload(e); }, false); return req; } }); } })(jQuery);
附件下载地址(包含图标和js): https://files.cnblogs.com/files/007sx/assets.zip