jQuery.trumbowyg = { langs: { en: { viewHTML: 'View HTML', undo: 'Undo', redo: 'Redo', formatting: 'Formatting', p: 'Paragraph', blockquote: 'Quote', code: 'Code', header: 'Header', bold: 'Bold', italic: 'Italic', strikethrough: 'Stroke', underline: 'Underline', strong: 'Strong', em: 'Emphasis', del: 'Deleted', superscript: 'Superscript', subscript: 'Subscript', unorderedList: 'Unordered list', orderedList: 'Ordered list', insertImage: 'Insert Image', link: 'Link', createLink: 'Insert link', unlink: 'Remove link', justifyLeft: 'Align Left', justifyCenter: 'Align Center', justifyRight: 'Align Right', justifyFull: 'Align Justify', horizontalRule: 'Insert horizontal rule', removeformat: 'Remove format', fullscreen: 'Fullscreen', close: 'Close', submit: 'Confirm', reset: 'Cancel', required: 'Required', description: 'Description', title: 'Title', text: 'Text', target: 'Target' } }, // Plugins plugins: {}, // SVG Path globally svgPath: null }; (function (navigator, window, document, $) { 'use strict'; $.fn.trumbowyg = function (options, params) { var trumbowygDataName = 'trumbowyg'; if (options === Object(options) || !options) { return this.each(function () { if (!$(this).data(trumbowygDataName)) { $(this).data(trumbowygDataName, new Trumbowyg(this, options)); } }); } if (this.length === 1) { try { var t = $(this).data(trumbowygDataName); switch (options) { // Exec command case 'execCmd': return t.execCmd(params.cmd, params.param, params.forceCss); // 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); // Range case 'saveRange': return t.saveRange(); case 'getRange': return t.range; case 'getRangeText': return t.getRangeText(); case 'restoreRange': return t.restoreRange(); // Enable/disable case 'enable': return t.toggleDisable(false); case 'disable': return t.toggleDisable(true); // Destroy case 'destroy': return t.destroy(); // Empty case 'empty': return t.empty(); // HTML case 'html': return t.html(params); } } catch (c) { } } return false; }; // @param: editorElem is the DOM element var Trumbowyg = function (editorElem, options) { var t = this, trumbowygIconsId = 'trumbowyg-icons'; // Get the document of the element. It use to makes the plugin // compatible on iframes. t.doc = editorElem.ownerDocument || document; // jQuery object of the editor t.$ta = $(editorElem); // $ta : Textarea t.$c = $(editorElem); // $c : creator options = options || {}; // Localization management if (options.lang != null || $.trumbowyg.langs[options.lang] != null) { t.lang = $.extend(true, {}, $.trumbowyg.langs.en, $.trumbowyg.langs[options.lang]); } else { t.lang = $.trumbowyg.langs.en; } // SVG path var svgPathOption = $.trumbowyg.svgPath != null ? $.trumbowyg.svgPath : options.svgPath; t.hasSvg = svgPathOption !== false; t.svgPath = !!t.doc.querySelector('base') ? window.location : ''; if ($('#' + trumbowygIconsId, t.doc).length === 0 && svgPathOption !== false) { if (svgPathOption == null) { try { throw new Error(); } catch (e) { var stackLines = e.stack.split('\n'); for (var i in stackLines) { if (!stackLines[i].match(/http[s]?:\/\//)) { continue; } svgPathOption = stackLines[Number(i)].match(/((http[s]?:\/\/.+\/)([^\/]+\.js)):/)[1].split('/'); svgPathOption.pop(); svgPathOption = svgPathOption.join('/') + '/ui/icons.svg'; break; } } } var div = t.doc.createElement('div'); div.id = trumbowygIconsId; t.doc.body.insertBefore(div, t.doc.body.childNodes[0]); $.get(svgPathOption, function (data) { div.innerHTML = new XMLSerializer().serializeToString(data.documentElement); }); } /** * When the button is associated to a empty object * fn and title attributs are defined from the button key value * * For example * foo: {} * is equivalent to : * foo: { * fn: 'foo', * title: this.lang.foo * } */ var h = t.lang.header, // Header translation isBlinkFunction = function () { return (window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window; }; t.btnsDef = { viewHTML: { fn: 'toggle' }, undo: { isSupported: isBlinkFunction, key: 'Z' }, redo: { isSupported: isBlinkFunction, key: 'Y' }, p: { fn: 'formatBlock' }, blockquote: { fn: 'formatBlock' }, h1: { fn: 'formatBlock', title: h + ' 1' }, h2: { fn: 'formatBlock', title: h + ' 2' }, h3: { fn: 'formatBlock', title: h + ' 3' }, h4: { fn: 'formatBlock', title: h + ' 4' }, subscript: { tag: 'sub' }, superscript: { tag: 'sup' }, bold: { key: 'B' }, italic: { key: 'I' }, underline: { tag: 'u' }, strikethrough: { tag: 'strike' }, strong: { fn: 'bold', key: 'B' }, em: { fn: 'italic', key: 'I' }, del: { fn: 'strikethrough' }, createLink: { key: 'K', tag: 'a' }, unlink: {}, insertImage: {}, justifyLeft: { tag: 'left', forceCss: true }, justifyCenter: { tag: 'center', forceCss: true }, justifyRight: { tag: 'right', forceCss: true }, justifyFull: { tag: 'justify', forceCss: true }, unorderedList: { fn: 'insertUnorderedList', tag: 'ul' }, orderedList: { fn: 'insertOrderedList', tag: 'ol' }, horizontalRule: { fn: 'insertHorizontalRule' }, removeformat: {}, fullscreen: { class: 'trumbowyg-not-disable' }, close: { fn: 'destroy', class: 'trumbowyg-not-disable' }, // Dropdowns formatting: { dropdown: ['p', 'blockquote', 'h1', 'h2', 'h3', 'h4'], ico: 'p' }, link: { dropdown: ['createLink', 'unlink'] } }; // Defaults Options t.o = $.extend(true, {}, { lang: 'en', fixedBtnPane: false, fixedFullWidth: false, autogrow: false, prefix: 'trumbowyg-', semantic: true, resetCss: false, removeformatPasted: false, tagsToRemove: [], btnsGrps: { design: ['bold', 'italic', 'underline', 'strikethrough'], semantic: ['strong', 'em', 'del'], justify: ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], lists: ['unorderedList', 'orderedList'] }, btns: [ ['viewHTML'], ['undo', 'redo'], ['formatting'], 'btnGrp-semantic', ['superscript', 'subscript'], ['link'], ['insertImage'], 'btnGrp-justify', 'btnGrp-lists', ['horizontalRule'], ['removeformat'], ['fullscreen'] ], // For custom button definitions btnsDef: {}, inlineElementsSelector: 'a,abbr,acronym,b,caption,cite,code,col,dfn,dir,dt,dd,em,font,hr,i,kbd,li,q,span,strikeout,strong,sub,sup,u', pasteHandlers: [], imgDblClickHandler: function () { var $img = $(this), src = $img.attr('src'), base64 = '(Base64)'; if (src.indexOf('data:image') === 0) { src = base64; } t.openModalInsert(t.lang.insertImage, { url: { label: 'URL', value: src, required: true }, alt: { label: t.lang.description, value: $img.attr('alt') } }, function (v) { if (v.src !== base64) { $img.attr({ src: v.src }); } $img.attr({ alt: v.alt }); return true; }); return false; }, plugins: {} }, options); t.disabled = t.o.disabled || (editorElem.nodeName === 'TEXTAREA' && editorElem.disabled); if (options.btns) { t.o.btns = options.btns; } else if (!t.o.semantic) { t.o.btns[4] = 'btnGrp-design'; } $.each(t.o.btnsDef, function (btnName, btnDef) { t.addBtnDef(btnName, btnDef); }); // Keyboard shortcuts are load in this array t.keys = []; // Tag to button dynamically hydrated t.tagToButton = {}; t.tagHandlers = []; // Admit multiple paste handlers t.pasteHandlers = [].concat(t.o.pasteHandlers); t.init(); }; Trumbowyg.prototype = { init: function () { var t = this; t.height = t.$ta.height(); t.initPlugins(); // Disable image resize in Firefox t.doc.execCommand('enableObjectResizing', false, false); t.doc.execCommand('defaultParagraphSeparator', false, 'p'); t.buildEditor(); t.buildBtnPane(); t.fixedBtnPaneEvents(); t.buildOverlay(); setTimeout(function () { if (t.disabled) { t.toggleDisable(true); } t.$c.trigger('tbwinit'); }); }, addBtnDef: function (btnName, btnDef) { this.btnsDef[btnName] = btnDef; }, buildEditor: function () { var t = this, prefix = t.o.prefix, html = ''; t.$box = $('
', { class: prefix + 'box ' + prefix + 'editor-visible ' + prefix + t.o.lang + ' trumbowyg' }); // $ta = Textarea // $ed = Editor t.isTextarea = t.$ta.is('textarea'); if (t.isTextarea) { html = t.$ta.val(); t.$ed = $('
'); t.$box .insertAfter(t.$ta) .append(t.$ed, t.$ta); } else { t.$ed = t.$ta; html = t.$ed.html(); t.$ta = $('