/* Redactor v9.2.6 Updated: Jul 19, 2014 http://imperavi.com/redactor/ Copyright (c) 2009-2014, Imperavi LLC. License: http://imperavi.com/redactor/license/ Usage: $('#content').redactor(); */ (function($) { var uuid = 0; "use strict"; var Range = function(range) { this[0] = range.startOffset; this[1] = range.endOffset; this.range = range; return this; }; Range.prototype.equals = function() { return this[0] === this[1]; }; var reUrlYoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig; var reUrlVimeo = /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/; // Plugin $.fn.redactor = function(options) { var val = []; var args = Array.prototype.slice.call(arguments, 1); if (typeof options === 'string') { this.each(function() { var instance = $.data(this, 'redactor'); if (typeof instance !== 'undefined' && $.isFunction(instance[options])) { var methodVal = instance[options].apply(instance, args); if (methodVal !== undefined && methodVal !== instance) val.push(methodVal); } else return $.error('No such method "' + options + '" for Redactor'); }); } else { this.each(function() { if (!$.data(this, 'redactor')) $.data(this, 'redactor', Redactor(this, options)); }); } if (val.length === 0) return this; else if (val.length === 1) return val[0]; else return val; }; // Initialization function Redactor(el, options) { return new Redactor.prototype.init(el, options); } $.Redactor = Redactor; $.Redactor.VERSION = '9.2.6'; $.Redactor.opts = { // settings rangy: false, iframe: false, fullpage: false, css: false, // url lang: 'en', direction: 'ltr', // ltr or rtl placeholder: false, typewriter: false, wym: false, mobile: true, cleanup: true, tidyHtml: true, pastePlainText: false, removeEmptyTags: true, cleanSpaces: true, cleanFontTag: true, templateVars: false, xhtml: false, visual: true, focus: false, tabindex: false, autoresize: true, minHeight: false, maxHeight: false, shortcuts: { 'ctrl+m, meta+m': "this.execCommand('removeFormat', false)", 'ctrl+b, meta+b': "this.execCommand('bold', false)", 'ctrl+i, meta+i': "this.execCommand('italic', false)", 'ctrl+h, meta+h': "this.execCommand('superscript', false)", 'ctrl+l, meta+l': "this.execCommand('subscript', false)", 'ctrl+k, meta+k': "this.linkShow()", 'ctrl+shift+7': "this.execCommand('insertorderedlist', false)", 'ctrl+shift+8': "this.execCommand('insertunorderedlist', false)" }, shortcutsAdd: false, autosave: false, // false or url autosaveInterval: 60, // seconds plugins: false, // array //linkAnchor: true, //linkEmail: true, linkProtocol: 'http://', linkNofollow: false, linkSize: 50, predefinedLinks: false, // json url (ex. /some-url.json ) or false imageFloatMargin: '10px', imageGetJson: false, // json url (ex. /some-images.json ) or false dragUpload: true, // false imageTabLink: true, imageUpload: false, // url imageUploadParam: 'file', // input name imageResizable: true, fileUpload: false, // url fileUploadParam: 'file', // input name clipboardUpload: true, // or false clipboardUploadUrl: false, // url dnbImageTypes: ['image/png', 'image/jpeg', 'image/gif'], // or false s3: false, uploadFields: false, observeImages: true, observeLinks: true, modalOverlay: true, tabSpaces: false, // true or number of spaces tabFocus: true, air: false, airButtons: ['formatting', 'bold', 'italic', 'deleted', 'unorderedlist', 'orderedlist', 'outdent', 'indent'], toolbar: true, toolbarFixed: false, toolbarFixedTarget: document, toolbarFixedTopOffset: 0, // pixels toolbarFixedBox: false, toolbarExternal: false, // ID selector toolbarOverflow: false, buttonSource: true, buttons: ['html', 'formatting', 'bold', 'italic', 'deleted', 'unorderedlist', 'orderedlist', 'outdent', 'indent', 'image', 'video', 'file', 'table', 'link', 'alignment', '|', 'horizontalrule'], // 'underline', 'alignleft', 'aligncenter', 'alignright', 'justify' buttonsHideOnMobile: [], activeButtons: ['deleted', 'italic', 'bold', 'underline', 'unorderedlist', 'orderedlist', 'alignleft', 'aligncenter', 'alignright', 'justify', 'table'], activeButtonsStates: { b: 'bold', strong: 'bold', i: 'italic', em: 'italic', del: 'deleted', strike: 'deleted', ul: 'unorderedlist', ol: 'orderedlist', u: 'underline', tr: 'table', td: 'table', table: 'table' }, formattingTags: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'], linebreaks: false, paragraphy: true, convertDivs: true, convertLinks: true, convertImageLinks: false, convertVideoLinks: false, formattingPre: false, phpTags: false, allowedTags: false, deniedTags: ['html', 'head', 'link', 'body', 'meta', 'script', 'style', 'applet'], boldTag: 'strong', italicTag: 'em', // private indentValue: 20, buffer: [], rebuffer: [], textareamode: false, emptyHtml: '

', invisibleSpace: '​', rBlockTest: /^(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)$/i, alignmentTags: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DD', 'DL', 'DT', 'DIV', 'TD', 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'ADDRESS', 'SECTION', 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE'], ownLine: ['area', 'body', 'head', 'hr', 'i?frame', 'link', 'meta', 'noscript', 'style', 'script', 'table', 'tbody', 'thead', 'tfoot'], contOwnLine: ['li', 'dt', 'dt', 'h[1-6]', 'option', 'script'], newLevel: ['blockquote', 'div', 'dl', 'fieldset', 'form', 'frameset', 'map', 'ol', 'p', 'pre', 'select', 'td', 'th', 'tr', 'ul'], blockLevelElements: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DD', 'DL', 'DT', 'DIV', 'LI', 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'PRE', 'ADDRESS', 'SECTION', 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE', 'TD'], // lang langs: { en: { html: 'HTML', video: 'Insert Video', image: 'Insert Image', table: 'Table', link: 'Link', link_insert: 'Insert link', link_edit: 'Edit link', unlink: 'Unlink', formatting: 'Formatting', paragraph: 'Normal text', quote: 'Quote', code: 'Code', header1: 'Header 1', header2: 'Header 2', header3: 'Header 3', header4: 'Header 4', header5: 'Header 5', bold: 'Bold', italic: 'Italic', fontcolor: 'Font Color', backcolor: 'Back Color', unorderedlist: 'Unordered List', orderedlist: 'Ordered List', outdent: 'Outdent', indent: 'Indent', cancel: 'Cancel', insert: 'Insert', save: 'Save', _delete: 'Delete', insert_table: 'Insert Table', insert_row_above: 'Add Row Above', insert_row_below: 'Add Row Below', insert_column_left: 'Add Column Left', insert_column_right: 'Add Column Right', delete_column: 'Delete Column', delete_row: 'Delete Row', delete_table: 'Delete Table', rows: 'Rows', columns: 'Columns', add_head: 'Add Head', delete_head: 'Delete Head', title: 'Title', image_position: 'Position', none: 'None', left: 'Left', right: 'Right', center: 'Center', image_web_link: 'Image Web Link', text: 'Text', mailto: 'Email', web: 'URL', video_html_code: 'Video Embed Code', file: 'Insert File', upload: 'Upload', download: 'Download', choose: 'Choose', or_choose: 'Or choose', drop_file_here: 'Drop file here', align_left: 'Align text to the left', align_center: 'Center text', align_right: 'Align text to the right', align_justify: 'Justify text', horizontalrule: 'Insert Horizontal Rule', deleted: 'Deleted', anchor: 'Anchor', link_new_tab: 'Open link in new tab', underline: 'Underline', alignment: 'Alignment', filename: 'Name (optional)', edit: 'Edit' } } }; // Functionality Redactor.fn = $.Redactor.prototype = { keyCode: { BACKSPACE: 8, DELETE: 46, DOWN: 40, ENTER: 13, ESC: 27, TAB: 9, CTRL: 17, META: 91, LEFT: 37, LEFT_WIN: 91 }, // Initialization init: function(el, options) { this.rtePaste = false; this.$element = this.$source = $(el); this.uuid = uuid++; // clonning options var opts = $.extend(true, {}, $.Redactor.opts); // current settings this.opts = $.extend( {}, opts, this.$element.data(), options ); this.start = true; this.dropdowns = []; // get sizes this.sourceHeight = this.$source.css('height'); this.sourceWidth = this.$source.css('width'); // dependency of the editor modes if (this.opts.fullpage) this.opts.iframe = true; if (this.opts.linebreaks) this.opts.paragraphy = false; if (this.opts.paragraphy) this.opts.linebreaks = false; if (this.opts.toolbarFixedBox) this.opts.toolbarFixed = true; // the alias for iframe mode this.document = document; this.window = window; // selection saved this.savedSel = false; // clean setup this.cleanlineBefore = new RegExp('^<(/?' + this.opts.ownLine.join('|/?' ) + '|' + this.opts.contOwnLine.join('|') + ')[ >]'); this.cleanlineAfter = new RegExp('^<(br|/?' + this.opts.ownLine.join('|/?' ) + '|/' + this.opts.contOwnLine.join('|/') + ')[ >]'); this.cleannewLevel = new RegExp('^]'); // block level this.rTestBlock = new RegExp('^(' + this.opts.blockLevelElements.join('|' ) + ')$', 'i'); // setup formatting permissions if (this.opts.linebreaks === false) { if (this.opts.allowedTags !== false) { var arrSearch = ['strong', 'em', 'del']; var arrAdd = ['b', 'i', 'strike']; if ($.inArray('p', this.opts.allowedTags) === '-1') this.opts.allowedTags.push('p'); for (i in arrSearch) { if ($.inArray(arrSearch[i], this.opts.allowedTags) != '-1') this.opts.allowedTags.push(arrAdd[i]); } } if (this.opts.deniedTags !== false) { var pos = $.inArray('p', this.opts.deniedTags); if (pos !== '-1') this.opts.deniedTags.splice(pos, pos); } } // ie & opera if (this.browser('msie') || this.browser('opera')) { this.opts.buttons = this.removeFromArrayByValue(this.opts.buttons, 'horizontalrule'); } // load lang this.opts.curLang = this.opts.langs[this.opts.lang]; // extend shortcuts $.extend(this.opts.shortcuts, this.opts.shortcutsAdd); // init placeholder this.placeholderInit(); // Build this.buildStart(); }, toolbarInit: function(lang) { return { html: { title: lang.html, func: 'toggle' }, formatting: { title: lang.formatting, func: 'show', dropdown: { p: { title: lang.paragraph, func: 'formatBlocks' }, blockquote: { title: lang.quote, func: 'formatQuote', className: 'redactor_format_blockquote' }, pre: { title: lang.code, func: 'formatBlocks', className: 'redactor_format_pre' }, h1: { title: lang.header1, func: 'formatBlocks', className: 'redactor_format_h1' }, h2: { title: lang.header2, func: 'formatBlocks', className: 'redactor_format_h2' }, h3: { title: lang.header3, func: 'formatBlocks', className: 'redactor_format_h3' }, h4: { title: lang.header4, func: 'formatBlocks', className: 'redactor_format_h4' }, h5: { title: lang.header5, func: 'formatBlocks', className: 'redactor_format_h5' } } }, bold: { title: lang.bold, exec: 'bold' }, italic: { title: lang.italic, exec: 'italic' }, deleted: { title: lang.deleted, exec: 'strikethrough' }, underline: { title: lang.underline, exec: 'underline' }, unorderedlist: { title: '• ' + lang.unorderedlist, exec: 'insertunorderedlist' }, orderedlist: { title: '1. ' + lang.orderedlist, exec: 'insertorderedlist' }, outdent: { title: '< ' + lang.outdent, func: 'indentingOutdent' }, indent: { title: '> ' + lang.indent, func: 'indentingIndent' }, image: { title: lang.image, func: 'imageShow' }, video: { title: lang.video, func: 'videoShow' }, file: { title: lang.file, func: 'fileShow' }, table: { title: lang.table, func: 'show', dropdown: { insert_table: { title: lang.insert_table, func: 'tableShow' }, separator_drop1: { name: 'separator' }, insert_row_above: { title: lang.insert_row_above, func: 'tableAddRowAbove' }, insert_row_below: { title: lang.insert_row_below, func: 'tableAddRowBelow' }, insert_column_left: { title: lang.insert_column_left, func: 'tableAddColumnLeft' }, insert_column_right: { title: lang.insert_column_right, func: 'tableAddColumnRight' }, separator_drop2: { name: 'separator' }, add_head: { title: lang.add_head, func: 'tableAddHead' }, delete_head: { title: lang.delete_head, func: 'tableDeleteHead' }, separator_drop3: { name: 'separator' }, delete_column: { title: lang.delete_column, func: 'tableDeleteColumn' }, delete_row: { title: lang.delete_row, func: 'tableDeleteRow' }, delete_table: { title: lang.delete_table, func: 'tableDeleteTable' } } }, link: { title: lang.link, func: 'show', dropdown: { link: { title: lang.link_insert, func: 'linkShow' }, unlink: { title: lang.unlink, exec: 'unlink' } } }, alignment: { title: lang.alignment, func: 'show', dropdown: { alignleft: { title: lang.align_left, func: 'alignmentLeft' }, aligncenter: { title: lang.align_center, func: 'alignmentCenter' }, alignright: { title: lang.align_right, func: 'alignmentRight' }, justify: { title: lang.align_justify, func: 'alignmentJustify' } } }, alignleft: { title: lang.align_left, func: 'alignmentLeft' }, aligncenter: { title: lang.align_center, func: 'alignmentCenter' }, alignright: { title: lang.align_right, func: 'alignmentRight' }, alignjustify: { title: lang.align_justify, func: 'alignmentJustify' }, horizontalrule: { exec: 'inserthorizontalrule', title: lang.horizontalrule } } }, // CALLBACKS callback: function(type, event, data) { var callback = this.opts[ type + 'Callback' ]; if ($.isFunction(callback)) { if (event === false) return callback.call(this, data); else return callback.call(this, event, data); } else return data; }, // DESTROY destroy: function() { clearInterval(this.autosaveInterval); $(window).off('.redactor'); this.$source.off('redactor-textarea'); this.$element.off('.redactor').removeData('redactor'); var html = this.get(); if (this.opts.textareamode) { this.$box.after(this.$source); this.$box.remove(); this.$source.val(html).show(); } else { var $elem = this.$editor; if (this.opts.iframe) $elem = this.$element; this.$box.after($elem); this.$box.remove(); $elem.removeClass('redactor_editor').removeClass('redactor_editor_wym').removeAttr('contenteditable').html(html).show(); } if (this.opts.toolbarExternal) { $(this.opts.toolbarExternal).html(''); } if (this.opts.air) { $('#redactor_air_' + this.uuid).remove(); } }, // API GET getObject: function() { return $.extend({}, this); }, getEditor: function() { return this.$editor; }, getBox: function() { return this.$box; }, getIframe: function() { return (this.opts.iframe) ? this.$frame : false; }, getToolbar: function() { return (this.$toolbar) ? this.$toolbar : false; }, // CODE GET & SET get: function() { return this.$source.val(); }, getCodeIframe: function() { this.$editor.removeAttr('contenteditable').removeAttr('dir'); var html = this.outerHtml(this.$frame.contents().children()); this.$editor.attr({ 'contenteditable': true, 'dir': this.opts.direction }); return html; }, set: function(html, strip, placeholderRemove) { html = html.toString(); html = html.replace(/\$/g, '$'); if (this.opts.fullpage) this.setCodeIframe(html); else this.setEditor(html, strip); if (html == '') placeholderRemove = false; if (placeholderRemove !== false) this.placeholderRemoveFromEditor(); }, setEditor: function(html, strip) { if (strip !== false) { html = this.cleanSavePreCode(html); html = this.cleanStripTags(html); html = this.cleanConvertProtected(html); html = this.cleanConvertInlineTags(html, true); if (this.opts.linebreaks === false) html = this.cleanConverters(html); else html = html.replace(/([\w\W]*?)<\/p>/gi, '$2
'); } // $ fix html = html.replace(/&#36;/g, '$'); html = this.cleanEmpty(html); this.$editor.html(html); // set no editable this.setNonEditable(); this.setSpansVerified(); this.sync(); }, setCodeIframe: function(html) { var doc = this.iframePage(); this.$frame[0].src = "about:blank"; html = this.cleanConvertProtected(html); html = this.cleanConvertInlineTags(html); html = this.cleanRemoveSpaces(html); doc.open(); doc.write(html); doc.close(); // redefine editor for fullpage mode if (this.opts.fullpage) { this.$editor = this.$frame.contents().find('body').attr({ 'contenteditable': true, 'dir': this.opts.direction }); } // set no editable this.setNonEditable(); this.setSpansVerified(); this.sync(); }, setFullpageOnInit: function(html) { this.fullpageDoctype = html.match(/^<\!doctype[^>]*>/i); if (this.fullpageDoctype && this.fullpageDoctype.length == 1) { html = html.replace(/^<\!doctype[^>]*>/i, ''); } html = this.cleanSavePreCode(html, true); html = this.cleanConverters(html); html = this.cleanEmpty(html); // set code this.$editor.html(html); // set no editable this.setNonEditable(); this.setSpansVerified(); this.sync(); }, setFullpageDoctype: function() { if (this.fullpageDoctype && this.fullpageDoctype.length == 1) { var source = this.fullpageDoctype[0] + '\n' + this.$source.val(); this.$source.val(source); } }, setSpansVerified: function() { var spans = this.$editor.find('span'); var replacementTag = 'inline'; $.each(spans, function() { var outer = this.outerHTML; // Replace opening tag var regex = new RegExp('<' + this.tagName, 'gi'); var newTag = outer.replace(regex, '<' + replacementTag); // Replace closing tag regex = new RegExp('/, ''); return html.replace(/<\/span>/, ''); }, setNonEditable: function() { this.$editor.find('.noneditable').attr('contenteditable', false); }, // SYNC sync: function(e) { var html = ''; this.cleanUnverified(); if (this.opts.fullpage) html = this.getCodeIframe(); else html = this.$editor.html(); html = this.syncClean(html); html = this.cleanRemoveEmptyTags(html); // is there a need to synchronize var source = this.cleanRemoveSpaces(this.$source.val(), false); var editor = this.cleanRemoveSpaces(html, false); if (source == editor) { // do not sync return false; } // fix second level up ul, ol html = html.replace(/<\/li><(ul|ol)>([\w\W]*?)<\/(ul|ol)>/gi, '<$1>$2'); if ($.trim(html) === '
') html = ''; // xhtml if (this.opts.xhtml) { var xhtmlTags = ['br', 'hr', 'img', 'link', 'input', 'meta']; $.each(xhtmlTags, function(i,s) { html = html.replace(new RegExp('<' + s + '(.*?[^\/$]?)>', 'gi'), '<' + s + '$1 />'); }); } // before callback html = this.callback('syncBefore', false, html); this.$source.val(html); this.setFullpageDoctype(); // onchange & after callback this.callback('syncAfter', false, html); if (this.start === false) { if (typeof e != 'undefined') { switch(e.which) { case 37: // left break; case 38: // up break; case 39: // right break; case 40: // down break; default: this.callback('change', false, html); } } else { this.callback('change', false, html); } } }, syncClean: function(html) { if (!this.opts.fullpage) html = this.cleanStripTags(html); // trim html = $.trim(html); // removeplaceholder html = this.placeholderRemoveFromCode(html); // remove space html = html.replace(/​/gi, ''); html = html.replace(/​/gi, ''); html = html.replace(/<\/a> /gi, '<\/a> '); html = html.replace(/\u200B/g, ''); if (html == '

' || html == '

' || html == '

 

') { html = ''; } // link nofollow if (this.opts.linkNofollow) { html = html.replace(//gi, ''); html = html.replace(//gi, ''); } // php code fix html = html.replace('', '?>'); // revert no editable html = html.replace(/<(.*?)class="noeditable"(.*?) contenteditable="false"(.*?)>/gi, '<$1class="noeditable"$2$3>'); html = html.replace(/ data-tagblock=""/gi, ''); html = html.replace(/\n?<\/(P|H[1-6]|LI|ADDRESS|SECTION|HEADER|FOOTER|ASIDE|ARTICLE)>/gi, ''); // remove image resize html = html.replace(/([\w\W]*?)<\/span>/gi, '$3'); html = html.replace(/(.*?)<\/span>/gi, ''); html = html.replace(/(.*?)<\/span>/gi, ''); // remove empty lists html = html.replace(/<(ul|ol)>\s*\t*\n*<\/(ul|ol)>/gi, ''); // remove font if (this.opts.cleanFontTag) { html = html.replace(/([\w\W]*?)<\/font>/gi, '$2'); } // remove spans html = html.replace(/([\w\W]*?)<\/span>/gi, '$2'); html = html.replace(/([\w\W]*?)<\/inline>/gi, '$1'); html = html.replace(//gi, ''); html = html.replace(//gi, ''); if (this.opts.removeEmptyTags) { html = html.replace(/([\w\W]*?)<\/span>/gi, '$1'); } html = html.replace(/([\w\W]*?)<\/span>/gi, ''); html = html.replace(//gi, ''); // special characters html = html.replace(/&/gi, '&'); html = html.replace(/\u2122/gi, '™'); html = html.replace(/\u00a9/gi, '©'); html = html.replace(/\u2026/gi, '…'); html = html.replace(/\u2014/gi, '—'); html = html.replace(/\u2010/gi, '‐'); html = this.cleanReConvertProtected(html); return html; }, // BUILD buildStart: function() { // content this.content = ''; // container this.$box = $('
'); // textarea test if (this.$source[0].tagName === 'TEXTAREA') this.opts.textareamode = true; // mobile if (this.opts.mobile === false && this.isMobile()) { this.buildMobile(); } else { // get the content at the start this.buildContent(); if (this.opts.iframe) { // build as iframe this.opts.autoresize = false; this.iframeStart(); } else if (this.opts.textareamode) this.buildFromTextarea(); else this.buildFromElement(); // options and final setup if (!this.opts.iframe) { this.buildOptions(); this.buildAfter(); } } }, buildMobile: function() { if (!this.opts.textareamode) { this.$editor = this.$source; this.$editor.hide(); this.$source = this.buildCodearea(this.$editor); this.$source.val(this.content); } this.$box.insertAfter(this.$source).append(this.$source); }, buildContent: function() { if (this.opts.textareamode) this.content = $.trim(this.$source.val()); else this.content = $.trim(this.$source.html()); }, buildFromTextarea: function() { this.$editor = $('
'); this.$box.insertAfter(this.$source).append(this.$editor).append(this.$source); // enable this.buildAddClasses(this.$editor); this.buildEnable(); }, buildFromElement: function() { this.$editor = this.$source; this.$source = this.buildCodearea(this.$editor); this.$box.insertAfter(this.$editor).append(this.$editor).append(this.$source); // enable this.buildEnable(); }, buildCodearea: function($source) { return $('' + '' + '' + '
' + '' + '' + '
' }); }, modalInit: function(title, content, width, callback) { this.modalSetOverlay(); this.$redactorModalWidth = width; this.$redactorModal = $('#redactor_modal'); if (!this.$redactorModal.length) { this.$redactorModal = $('