/*
Redactor v10.0.7
Updated: January 31, 2015
http://imperavi.com/redactor/
Copyright (c) 2009-2015, Imperavi LLC.
License: http://imperavi.com/redactor/license/
Usage: $('#content').redactor();
*/
(function($)
{
'use strict';
if (!Function.prototype.bind)
{
Function.prototype.bind = function(scope)
{
var fn = this;
return function()
{
return fn.apply(scope);
};
};
}
var uuid = 0;
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');
var func;
if (options.search(/\./) != '-1')
{
func = options.split('.');
if (typeof instance[func[0]] != 'undefined')
{
func = instance[func[0]][func[1]];
}
}
else
{
func = instance[options];
}
if (typeof instance !== 'undefined' && $.isFunction(func))
{
var methodVal = func.apply(instance, args);
if (methodVal !== undefined && methodVal !== instance)
{
val.push(methodVal);
}
}
else
{
$.error('No such method "' + options + '" for Redactor');
}
});
}
else
{
this.each(function()
{
$.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);
}
// Functionality
$.Redactor = Redactor;
$.Redactor.VERSION = '10.0.7';
$.Redactor.modules = ['alignment', 'autosave', 'block', 'buffer', 'build', 'button',
'caret', 'clean', 'code', 'core', 'dropdown', 'file', 'focus',
'image', 'indent', 'inline', 'insert', 'keydown', 'keyup',
'lang', 'line', 'link', 'list', 'modal', 'observe', 'paragraphize',
'paste', 'placeholder', 'progress', 'selection', 'shortcuts',
'tabifier', 'tidy', 'toolbar', 'upload', 'utils'];
$.Redactor.opts = {
// settings
lang: 'en',
direction: 'ltr', // ltr or rtl
plugins: false, // array
focus: false,
focusEnd: false,
placeholder: false,
visual: true,
tabindex: false,
minHeight: false,
maxHeight: false,
linebreaks: false,
replaceDivs: true,
paragraphize: true,
cleanStyleOnEnter: false,
enterKey: true,
cleanOnPaste: true,
cleanSpaces: true,
pastePlainText: false,
autosave: false, // false or url
autosaveName: false,
autosaveInterval: 60, // seconds
autosaveOnChange: false,
linkTooltip: true,
linkProtocol: 'http',
linkNofollow: false,
linkSize: 50,
imageEditable: true,
imageLink: true,
imagePosition: true,
imageFloatMargin: '10px',
imageResizable: true,
imageUpload: null,
imageUploadParam: 'file',
uploadImageField: false,
dragImageUpload: true,
fileUpload: null,
fileUploadParam: 'file',
dragFileUpload: true,
s3: false,
convertLinks: true,
convertUrlLinks: true,
convertImageLinks: true,
convertVideoLinks: true,
preSpaces: 4, // or false
tabAsSpaces: false, // true or number of spaces
tabKey: true,
scrollTarget: false,
toolbar: true,
toolbarFixed: true,
toolbarFixedTarget: document,
toolbarFixedTopOffset: 0, // pixels
toolbarExternal: false, // ID selector
toolbarOverflow: false,
source: true,
buttons: ['html', 'formatting', 'bold', 'italic', 'deleted', 'unorderedlist', 'orderedlist',
'outdent', 'indent', 'image', 'file', 'link', 'alignment', 'horizontalrule'], // + 'underline'
buttonsHide: [],
buttonsHideOnMobile: [],
formatting: ['p', 'blockquote', 'pre', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
formattingAdd: false,
tabifier: true,
deniedTags: ['html', 'head', 'link', 'body', 'meta', 'script', 'style', 'applet'],
allowedTags: false, // or array
removeComments: false,
replaceTags: [
['strike', 'del']
],
replaceStyles: [
['font-weight:\\s?bold', "strong"],
['font-style:\\s?italic', "em"],
['text-decoration:\\s?underline', "u"],
['text-decoration:\\s?line-through', 'del']
],
removeDataAttr: false,
removeAttr: false, // or multi array
allowedAttr: false, // or multi array
removeWithoutAttr: ['span'], // or false
removeEmpty: ['p'], // or false;
activeButtons: ['deleted', 'italic', 'bold', 'underline', 'unorderedlist', 'orderedlist',
'alignleft', 'aligncenter', 'alignright', 'justify'],
activeButtonsStates: {
b: 'bold',
strong: 'bold',
i: 'italic',
em: 'italic',
del: 'deleted',
strike: 'deleted',
ul: 'unorderedlist',
ol: 'orderedlist',
u: 'underline'
},
shortcuts: {
'ctrl+shift+m, meta+shift+m': { func: 'inline.removeFormat' },
'ctrl+b, meta+b': { func: 'inline.format', params: ['bold'] },
'ctrl+i, meta+i': { func: 'inline.format', params: ['italic'] },
'ctrl+h, meta+h': { func: 'inline.format', params: ['superscript'] },
'ctrl+l, meta+l': { func: 'inline.format', params: ['subscript'] },
'ctrl+k, meta+k': { func: 'link.show' },
'ctrl+shift+7': { func: 'list.toggle', params: ['orderedlist'] },
'ctrl+shift+8': { func: 'list.toggle', params: ['unorderedlist'] }
},
shortcutsAdd: false,
// private
buffer: [],
rebuffer: [],
emptyHtml: '
',
invisibleSpace: '',
imageTypes: ['image/png', 'image/jpeg', 'image/gif'],
indentValue: 20,
verifiedTags: ['a', 'img', 'b', 'strong', 'sub', 'sup', 'i', 'em', 'u', 'small', 'strike', 'del', 'cite', 'ul', 'ol', 'li'], // and for span tag special rule
inlineTags: ['strong', 'b', 'u', 'em', 'i', 'code', 'del', 'ins', 'samp', 'kbd', 'sup', 'sub', 'mark', 'var', 'cite', 'small'],
alignmentTags: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DL', 'DT', 'DD', 'DIV', 'TD', 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'ADDRESS', 'SECTION', 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE'],
blockLevelElements: ['PRE', 'UL', 'OL', 'LI'],
// 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 or Youtube/Vimeo Link',
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,
SPACE: 32,
ESC: 27,
TAB: 9,
CTRL: 17,
META: 91,
SHIFT: 16,
ALT: 18,
RIGHT: 39,
LEFT: 37,
LEFT_WIN: 91
},
// Initialization
init: function(el, options)
{
this.$element = $(el);
this.uuid = uuid++;
// if paste event detected = true
this.rtePaste = false;
this.$pasteBox = false;
this.loadOptions(options);
this.loadModules();
// formatting storage
this.formatting = {};
// block level tags
$.merge(this.opts.blockLevelElements, this.opts.alignmentTags);
this.reIsBlock = new RegExp('^(' + this.opts.blockLevelElements.join('|' ) + ')$', 'i');
// setup allowed and denied tags
this.tidy.setupAllowed();
// load lang
this.lang.load();
// extend shortcuts
$.extend(this.opts.shortcuts, this.opts.shortcutsAdd);
// start callback
this.core.setCallback('start');
// build
this.start = true;
this.build.run();
},
loadOptions: function(options)
{
this.opts = $.extend(
{},
$.extend(true, {}, $.Redactor.opts),
this.$element.data(),
options
);
},
getModuleMethods: function(object)
{
return Object.getOwnPropertyNames(object).filter(function(property)
{
return typeof object[property] == 'function';
});
},
loadModules: function()
{
var len = $.Redactor.modules.length;
for (var i = 0; i < len; i++)
{
this.bindModuleMethods($.Redactor.modules[i]);
}
},
bindModuleMethods: function(module)
{
if (typeof this[module] == 'undefined') return;
// init module
this[module] = this[module]();
var methods = this.getModuleMethods(this[module]);
var len = methods.length;
// bind methods
for (var z = 0; z < len; z++)
{
this[module][methods[z]] = this[module][methods[z]].bind(this);
}
},
alignment: function()
{
return {
left: function()
{
this.alignment.set('');
},
right: function()
{
this.alignment.set('right');
},
center: function()
{
this.alignment.set('center');
},
justify: function()
{
this.alignment.set('justify');
},
set: function(type)
{
// focus
if (!this.utils.browser('msie')) this.$editor.focus();
this.buffer.set();
this.selection.save();
// get blocks
this.alignment.blocks = this.selection.getBlocks();
this.alignment.type = type;
// set alignment
if (this.alignment.isLinebreaksOrNoBlocks())
{
this.alignment.setText();
}
else
{
this.alignment.setBlocks();
}
// sync
this.selection.restore();
this.code.sync();
},
setText: function()
{
var wrapper = this.selection.wrap('div');
$(wrapper).attr('data-tagblock', 'redactor').css('text-align', this.alignment.type);
},
setBlocks: function()
{
$.each(this.alignment.blocks, $.proxy(function(i, el)
{
var $el = this.utils.getAlignmentElement(el);
if (!$el) return;
if (this.alignment.isNeedReplaceElement($el))
{
this.alignment.replaceElement($el);
}
else
{
this.alignment.alignElement($el);
}
}, this));
},
isLinebreaksOrNoBlocks: function()
{
return (this.opts.linebreaks && this.alignment.blocks[0] === false);
},
isNeedReplaceElement: function($el)
{
return (this.alignment.type === '' && typeof($el.data('tagblock')) !== 'undefined');
},
replaceElement: function($el)
{
$el.replaceWith($el.html());
},
alignElement: function($el)
{
$el.css('text-align', this.alignment.type);
this.utils.removeEmptyAttr($el, 'style');
}
};
},
autosave: function()
{
return {
enable: function()
{
if (!this.opts.autosave) return;
this.autosave.html = false;
this.autosave.name = (this.opts.autosaveName) ? this.opts.autosaveName : this.$textarea.attr('name');
if (this.opts.autosaveOnChange) return;
this.autosaveInterval = setInterval(this.autosave.load, this.opts.autosaveInterval * 1000);
},
onChange: function()
{
if (!this.opts.autosaveOnChange) return;
this.autosave.load();
},
load: function()
{
this.autosave.source = this.code.get();
if (this.autosave.html === this.autosave.source) return;
if (this.utils.isEmpty(this.autosave.source)) return;
// data
var data = {};
data['name'] = this.autosave.name;
data[this.autosave.name] = escape(encodeURIComponent(this.autosave.source));
// ajax
var jsxhr = $.ajax({
url: this.opts.autosave,
type: 'post',
data: data
});
jsxhr.done(this.autosave.success);
},
success: function(data)
{
var json;
try
{
json = $.parseJSON(data);
}
catch(e)
{
//data has already been parsed
json = data;
}
var callbackName = (typeof json.error == 'undefined') ? 'autosave' : 'autosaveError';
this.core.setCallback(callbackName, this.autosave.name, json);
this.autosave.html = this.autosave.source;
},
disable: function()
{
clearInterval(this.autosaveInterval);
}
};
},
block: function()
{
return {
formatting: function(name)
{
this.block.clearStyle = false;
var type, value;
if (typeof this.formatting[name].data != 'undefined') type = 'data';
else if (typeof this.formatting[name].attr != 'undefined') type = 'attr';
else if (typeof this.formatting[name].class != 'undefined') type = 'class';
if (typeof this.formatting[name].clear != 'undefined')
{
this.block.clearStyle = true;
}
if (type) value = this.formatting[name][type];
this.block.format(this.formatting[name].tag, type, value);
},
format: function(tag, type, value)
{
if (tag == 'quote') tag = 'blockquote';
var formatTags = ['p', 'pre', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
if ($.inArray(tag, formatTags) == -1) return;
this.block.isRemoveInline = (tag == 'pre' || tag.search(/h[1-6]/i) != -1);
// focus
if (!this.utils.browser('msie')) this.$editor.focus();
this.block.blocks = this.selection.getBlocks();
this.block.blocksSize = this.block.blocks.length;
this.block.type = type;
this.block.value = value;
this.buffer.set();
this.selection.save();
this.block.set(tag);
this.selection.restore();
this.code.sync();
},
set: function(tag)
{
this.selection.get();
this.block.containerTag = this.range.commonAncestorContainer.tagName;
if (this.range.collapsed)
{
this.block.setCollapsed(tag);
}
else
{
this.block.setMultiple(tag);
}
},
setCollapsed: function(tag)
{
var block = this.block.blocks[0];
if (block === false) return;
if (block.tagName == 'LI')
{
if (tag != 'blockquote') return;
this.block.formatListToBlockquote();
return;
}
var isContainerTable = (this.block.containerTag == 'TD' || this.block.containerTag == 'TH');
if (isContainerTable && !this.opts.linebreaks)
{
document.execCommand('formatblock', false, '<' + tag + '>');
block = this.selection.getBlock();
this.block.toggle($(block));
}
else if (block.tagName.toLowerCase() != tag)
{
if (this.opts.linebreaks && tag == 'p')
{
$(block).prepend('
').append('
');
this.utils.replaceWithContents(block);
}
else
{
var $formatted = this.utils.replaceToTag(block, tag);
this.block.toggle($formatted);
if (tag != 'p' && tag != 'blockquote') $formatted.find('img').remove();
if (this.block.isRemoveInline) this.utils.removeInlineTags($formatted);
if (tag == 'p' || this.block.headTag) $formatted.find('p').contents().unwrap();
this.block.formatTableWrapping($formatted);
}
}
else if (tag == 'blockquote' && block.tagName.toLowerCase() == tag)
{
// blockquote off
if (this.opts.linebreaks)
{
$(block).prepend('
').append('
');
this.utils.replaceWithContents(block);
}
else
{
var $el = this.utils.replaceToTag(block, 'p');
this.block.toggle($el);
}
}
else if (block.tagName.toLowerCase() == tag)
{
this.block.toggle($(block));
}
},
setMultiple: function(tag)
{
var block = this.block.blocks[0];
var isContainerTable = (this.block.containerTag == 'TD' || this.block.containerTag == 'TH');
if (block !== false && this.block.blocksSize === 1)
{
if (block.tagName.toLowerCase() == tag && tag == 'blockquote')
{
// blockquote off
if (this.opts.linebreaks)
{
$(block).prepend('
').append('
');
this.utils.replaceWithContents(block);
}
else
{
var $el = this.utils.replaceToTag(block, 'p');
this.block.toggle($el);
}
}
else if (block.tagName == 'LI')
{
if (tag != 'blockquote') return;
this.block.formatListToBlockquote();
}
else if (this.block.containerTag == 'BLOCKQUOTE')
{
this.block.formatBlockquote(tag);
}
else if (this.opts.linebreaks && ((isContainerTable) || (this.range.commonAncestorContainer != block)))
{
this.block.formatWrap(tag);
}
else
{
if (this.opts.linebreaks && tag == 'p')
{
$(block).prepend('
').append('
');
this.utils.replaceWithContents(block);
}
else if (block.tagName === 'TD')
{
this.block.formatWrap(tag);
}
else
{
var $formatted = this.utils.replaceToTag(block, tag);
this.block.toggle($formatted);
if (this.block.isRemoveInline) this.utils.removeInlineTags($formatted);
if (tag == 'p' || this.block.headTag) $formatted.find('p').contents().unwrap();
}
}
}
else
{
if (this.opts.linebreaks || tag != 'p')
{
if (tag == 'blockquote')
{
var count = 0;
for (var i = 0; i < this.block.blocksSize; i++)
{
if (this.block.blocks[i].tagName == 'BLOCKQUOTE') count++;
}
// only blockquote selected
if (count == this.block.blocksSize)
{
$.each(this.block.blocks, $.proxy(function(i,s)
{
if (this.opts.linebreaks)
{
$(s).prepend('
').append('
');
this.utils.replaceWithContents(s);
}
else
{
this.utils.replaceToTag(s, 'p');
}
}, this));
return;
}
}
this.block.formatWrap(tag);
}
else
{
var classSize = 0;
var toggleType = false;
if (this.block.type == 'class')
{
toggleType = 'toggle';
classSize = $(this.block.blocks).filter('.' + this.block.value).length;
if (this.block.blocksSize == classSize) toggleType = 'toggle';
else if (this.block.blocksSize > classSize) toggleType = 'set';
else if (classSize === 0) toggleType = 'set';
}
var exceptTags = ['ul', 'ol', 'li', 'td', 'th', 'dl', 'dt', 'dd'];
$.each(this.block.blocks, $.proxy(function(i,s)
{
if ($.inArray(s.tagName.toLowerCase(), exceptTags) != -1) return;
var $formatted = this.utils.replaceToTag(s, tag);
if (toggleType)
{
if (toggleType == 'toggle') this.block.toggle($formatted);
else if (toggleType == 'remove') this.block.remove($formatted);
else if (toggleType == 'set') this.block.setForce($formatted);
}
else this.block.toggle($formatted);
if (tag != 'p' && tag != 'blockquote') $formatted.find('img').remove();
if (this.block.isRemoveInline) this.utils.removeInlineTags($formatted);
if (tag == 'p' || this.block.headTag) $formatted.find('p').contents().unwrap();
}, this));
}
}
},
setForce: function($el)
{
// remove style and class if the specified setting
if (this.block.clearStyle)
{
$el.removeAttr('class').removeAttr('style');
}
if (this.block.type == 'class')
{
$el.addClass(this.block.value);
return;
}
else if (this.block.type == 'attr' || this.block.type == 'data')
{
$el.attr(this.block.value.name, this.block.value.value);
return;
}
},
toggle: function($el)
{
// remove style and class if the specified setting
if (this.block.clearStyle)
{
$el.removeAttr('class').removeAttr('style');
}
if (this.block.type == 'class')
{
$el.toggleClass(this.block.value);
return;
}
else if (this.block.type == 'attr' || this.block.type == 'data')
{
if ($el.attr(this.block.value.name) == this.block.value.value)
{
$el.removeAttr(this.block.value.name);
}
else
{
$el.attr(this.block.value.name, this.block.value.value);
}
return;
}
else
{
$el.removeAttr('style class');
return;
}
},
remove: function($el)
{
$el.removeClass(this.block.value);
},
formatListToBlockquote: function()
{
var block = $(this.block.blocks[0]).closest('ul, ol');
$(block).find('ul, ol').contents().unwrap();
$(block).find('li').append($('
')).contents().unwrap();
var $el = this.utils.replaceToTag(block, 'blockquote');
this.block.toggle($el);
},
formatBlockquote: function(tag)
{
document.execCommand('outdent');
document.execCommand('formatblock', false, tag);
this.clean.clearUnverified();
this.$editor.find('p:empty').remove();
var formatted = this.selection.getBlock();
if (tag != 'p')
{
$(formatted).find('img').remove();
}
if (!this.opts.linebreaks)
{
this.block.toggle($(formatted));
}
this.$editor.find('ul, ol, tr, blockquote, p').each($.proxy(this.utils.removeEmpty, this));
if (this.opts.linebreaks && tag == 'p')
{
this.utils.replaceWithContents(formatted);
}
},
formatWrap: function(tag)
{
if (this.block.containerTag == 'UL' || this.block.containerTag == 'OL')
{
if (tag == 'blockquote')
{
this.block.formatListToBlockquote();
}
else
{
return;
}
}
var formatted = this.selection.wrap(tag);
if (formatted === false) return;
var $formatted = $(formatted);
this.block.formatTableWrapping($formatted);
var $elements = $formatted.find(this.opts.blockLevelElements.join(',') + ', td, table, thead, tbody, tfoot, th, tr');
if ((this.opts.linebreaks && tag == 'p') || tag == 'pre' || tag == 'blockquote')
{
$elements.append('
');
}
$elements.contents().unwrap();
if (tag != 'p' && tag != 'blockquote') $formatted.find('img').remove();
$.each(this.block.blocks, $.proxy(this.utils.removeEmpty, this));
$formatted.append(this.selection.getMarker(2));
if (!this.opts.linebreaks)
{
this.block.toggle($formatted);
}
this.$editor.find('ul, ol, tr, blockquote, p').each($.proxy(this.utils.removeEmpty, this));
$formatted.find('blockquote:empty').remove();
if (this.block.isRemoveInline)
{
this.utils.removeInlineTags($formatted);
}
if (this.opts.linebreaks && tag == 'p')
{
this.utils.replaceWithContents($formatted);
}
},
formatTableWrapping: function($formatted)
{
if ($formatted.closest('table').length === 0) return;
if ($formatted.closest('tr').length === 0) $formatted.wrap('');
if ($formatted.closest('td').length === 0 && $formatted.closest('th').length === 0)
{
$formatted.wrap('');
}
},
removeData: function(name, value)
{
var blocks = this.selection.getBlocks();
$(blocks).removeAttr('data-' + name);
this.code.sync();
},
setData: function(name, value)
{
var blocks = this.selection.getBlocks();
$(blocks).attr('data-' + name, value);
this.code.sync();
},
toggleData: function(name, value)
{
var blocks = this.selection.getBlocks();
$.each(blocks, function()
{
if ($(this).attr('data-' + name))
{
$(this).removeAttr('data-' + name);
}
else
{
$(this).attr('data-' + name, value);
}
});
},
removeAttr: function(attr, value)
{
var blocks = this.selection.getBlocks();
$(blocks).removeAttr(attr);
this.code.sync();
},
setAttr: function(attr, value)
{
var blocks = this.selection.getBlocks();
$(blocks).attr(attr, value);
this.code.sync();
},
toggleAttr: function(attr, value)
{
var blocks = this.selection.getBlocks();
$.each(blocks, function()
{
if ($(this).attr(name))
{
$(this).removeAttr(name);
}
else
{
$(this).attr(name, value);
}
});
},
removeClass: function(className)
{
var blocks = this.selection.getBlocks();
$(blocks).removeClass(className);
this.utils.removeEmptyAttr(blocks, 'class');
this.code.sync();
},
setClass: function(className)
{
var blocks = this.selection.getBlocks();
$(blocks).addClass(className);
this.code.sync();
},
toggleClass: function(className)
{
var blocks = this.selection.getBlocks();
$(blocks).toggleClass(className);
this.code.sync();
}
};
},
buffer: function()
{
return {
set: function(type)
{
if (typeof type == 'undefined' || type == 'undo')
{
this.buffer.setUndo();
}
else
{
this.buffer.setRedo();
}
},
setUndo: function()
{
this.selection.save();
this.opts.buffer.push(this.$editor.html());
this.selection.restore();
},
setRedo: function()
{
this.selection.save();
this.opts.rebuffer.push(this.$editor.html());
this.selection.restore();
},
getUndo: function()
{
this.$editor.html(this.opts.buffer.pop());
},
getRedo: function()
{
this.$editor.html(this.opts.rebuffer.pop());
},
add: function()
{
this.opts.buffer.push(this.$editor.html());
},
undo: function()
{
if (this.opts.buffer.length === 0) return;
this.buffer.set('redo');
this.buffer.getUndo();
this.selection.restore();
setTimeout($.proxy(this.observe.load, this), 50);
},
redo: function()
{
if (this.opts.rebuffer.length === 0) return;
this.buffer.set('undo');
this.buffer.getRedo();
this.selection.restore();
setTimeout($.proxy(this.observe.load, this), 50);
}
};
},
build: function()
{
return {
run: function()
{
this.build.createContainerBox();
this.build.loadContent();
this.build.loadEditor();
this.build.enableEditor();
this.build.setCodeAndCall();
},
isTextarea: function()
{
return (this.$element[0].tagName === 'TEXTAREA');
},
createContainerBox: function()
{
this.$box = $('');
},
createTextarea: function()
{
this.$textarea = $('').attr('name', this.build.getTextareaName());
},
getTextareaName: function()
{
return ((typeof(name) == 'undefined')) ? 'content-' + this.uuid : this.$element.attr('id');
},
loadContent: function()
{
var func = (this.build.isTextarea()) ? 'val' : 'html';
this.content = $.trim(this.$element[func]());
},
enableEditor: function()
{
this.$editor.attr({ 'contenteditable': true, 'dir': this.opts.direction });
},
loadEditor: function()
{
var func = (this.build.isTextarea()) ? 'fromTextarea' : 'fromElement';
this.build[func]();
},
fromTextarea: function()
{
this.$editor = $('');
this.$textarea = this.$element;
this.$box.insertAfter(this.$element).append(this.$editor).append(this.$element);
this.$editor.addClass('redactor-editor');
this.$element.hide();
},
fromElement: function()
{
this.$editor = this.$element;
this.build.createTextarea();
this.$box.insertAfter(this.$editor).append(this.$editor).append(this.$textarea);
this.$editor.addClass('redactor-editor');
this.$textarea.hide();
},
setCodeAndCall: function()
{
// set code
this.code.set(this.content);
this.build.setOptions();
this.build.callEditor();
// code mode
if (this.opts.visual) return;
setTimeout($.proxy(this.code.showCode, this), 200);
},
callEditor: function()
{
this.build.disableMozillaEditing();
this.build.setEvents();
this.build.setHelpers();
// load toolbar
if (this.opts.toolbar)
{
this.opts.toolbar = this.toolbar.init();
this.toolbar.build();
}
// modal templates init
this.modal.loadTemplates();
// plugins
this.build.plugins();
// observers
setTimeout($.proxy(this.observe.load, this), 4);
// init callback
this.core.setCallback('init');
},
setOptions: function()
{
// textarea direction
$(this.$textarea).attr('dir', this.opts.direction);
if (this.opts.linebreaks) this.$editor.addClass('redactor-linebreaks');
if (this.opts.tabindex) this.$editor.attr('tabindex', this.opts.tabindex);
if (this.opts.minHeight) this.$editor.css('minHeight', this.opts.minHeight);
if (this.opts.maxHeight) this.$editor.css('maxHeight', this.opts.maxHeight);
},
setEventDropUpload: function(e)
{
e.preventDefault();
if (!this.opts.dragImageUpload || !this.opts.dragFileUpload) return;
var files = e.dataTransfer.files;
this.upload.directUpload(files[0], e);
},
setEventDrop: function(e)
{
this.code.sync();
setTimeout(this.clean.clearUnverified, 1);
this.core.setCallback('drop', e);
},
setEvents: function()
{
// drop
this.$editor.on('drop.redactor', $.proxy(function(e)
{
e = e.originalEvent || e;
if (window.FormData === undefined || !e.dataTransfer) return true;
if (e.dataTransfer.files.length === 0)
{
return this.build.setEventDrop(e);
}
else
{
this.build.setEventDropUpload(e);
}
setTimeout(this.clean.clearUnverified, 1);
this.core.setCallback('drop', e);
}, this));
// click
this.$editor.on('click.redactor', $.proxy(function(e)
{
var event = this.core.getEvent();
var type = (event == 'click' || event == 'arrow') ? false : 'click';
this.core.addEvent(type);
this.utils.disableSelectAll();
this.core.setCallback('click', e);
}, this));
// paste
this.$editor.on('paste.redactor', $.proxy(this.paste.init, this));
// keydown
this.$editor.on('keydown.redactor', $.proxy(this.keydown.init, this));
// keyup
this.$editor.on('keyup.redactor', $.proxy(this.keyup.init, this));
// textarea keydown
if ($.isFunction(this.opts.codeKeydownCallback))
{
this.$textarea.on('keydown.redactor-textarea', $.proxy(this.opts.codeKeydownCallback, this));
}
// textarea keyup
if ($.isFunction(this.opts.codeKeyupCallback))
{
this.$textarea.on('keyup.redactor-textarea', $.proxy(this.opts.codeKeyupCallback, this));
}
// focus
if ($.isFunction(this.opts.focusCallback))
{
this.$editor.on('focus.redactor', $.proxy(this.opts.focusCallback, this));
}
var clickedElement;
$(document).on('mousedown', function(e) { clickedElement = e.target; });
// blur
this.$editor.on('blur.redactor', $.proxy(function(e)
{
if (this.rtePaste) return;
if (!this.build.isBlured(clickedElement)) return;
this.utils.disableSelectAll();
if ($.isFunction(this.opts.blurCallback)) this.core.setCallback('blur', e);
}, this));
},
isBlured: function(clickedElement)
{
var $el = $(clickedElement);
return (!$el.hasClass('redactor-toolbar, redactor-dropdown') && !$el.is('#redactor-modal') && $el.parents('.redactor-toolbar, .redactor-dropdown, #redactor-modal').length === 0);
},
setHelpers: function()
{
// autosave
this.autosave.enable();
// placeholder
this.placeholder.enable();
// focus
if (this.opts.focus) setTimeout(this.focus.setStart, 100);
if (this.opts.focusEnd) setTimeout(this.focus.setEnd, 100);
},
plugins: function()
{
if (!this.opts.plugins) return;
if (!RedactorPlugins) return;
$.each(this.opts.plugins, $.proxy(function(i, s)
{
if (typeof RedactorPlugins[s] === 'undefined') return;
if ($.inArray(s, $.Redactor.modules) !== -1)
{
$.error('Plugin name "' + s + '" matches the name of the Redactor\'s module.');
return;
}
if (!$.isFunction(RedactorPlugins[s])) return;
this[s] = RedactorPlugins[s]();
// get methods
var methods = this.getModuleMethods(this[s]);
var len = methods.length;
// bind methods
for (var z = 0; z < len; z++)
{
this[s][methods[z]] = this[s][methods[z]].bind(this);
}
if ($.isFunction(this[s].init)) this[s].init();
}, this));
},
disableMozillaEditing: function()
{
if (!this.utils.browser('mozilla')) return;
// FF fix
try {
document.execCommand('enableObjectResizing', false, false);
document.execCommand('enableInlineTableEditing', false, false);
} catch (e) {}
}
};
},
button: function()
{
return {
build: function(btnName, btnObject)
{
var $button = $('').attr('tabindex', '-1');
// click
if (btnObject.func || btnObject.command || btnObject.dropdown)
{
this.button.setEvent($button, btnName, btnObject);
}
// dropdown
if (btnObject.dropdown)
{
var $dropdown = $('');
$button.data('dropdown', $dropdown);
this.dropdown.build(btnName, $dropdown, btnObject.dropdown);
}
// tooltip
if (this.utils.isDesktop())
{
this.button.createTooltip($button, btnName, btnObject.title);
}
return $button;
},
setEvent: function($button, btnName, btnObject)
{
$button.on('touchstart click', $.proxy(function(e)
{
if ($button.hasClass('redactor-button-disabled')) return false;
var type = 'func';
var callback = btnObject.func;
if (btnObject.command)
{
type = 'command';
callback = btnObject.command;
}
else if (btnObject.dropdown)
{
type = 'dropdown';
callback = false;
}
this.button.onClick(e, btnName, type, callback);
}, this));
},
createTooltip: function($button, name, title)
{
var $tooltip = $(' ').addClass('redactor-toolbar-tooltip redactor-toolbar-tooltip-' + name).hide().html(title);
$tooltip.appendTo('body');
$button.on('mouseover', function()
{
if ($(this).hasClass('redactor-button-disabled')) return;
var pos = $button.offset();
$tooltip.show();
$tooltip.css({
top: (pos.top + $button.innerHeight()) + 'px',
left: (pos.left + $button.innerWidth()/2 - $tooltip.innerWidth()/2) + 'px'
});
});
$button.on('mouseout', function()
{
$tooltip.hide();
});
},
onClick: function(e, btnName, type, callback)
{
this.button.caretOffset = this.caret.getOffset();
e.preventDefault();
if (this.utils.browser('msie')) e.returnValue = false;
if (type == 'command') this.inline.format(callback);
else if (type == 'dropdown') this.dropdown.show(e, btnName);
else this.button.onClickCallback(e, callback, btnName);
},
onClickCallback: function(e, callback, btnName)
{
var func;
if ($.isFunction(callback)) callback.call(this, btnName);
else if (callback.search(/\./) != '-1')
{
func = callback.split('.');
if (typeof this[func[0]] == 'undefined') return;
this[func[0]][func[1]](btnName);
}
else this[callback](btnName);
this.observe.buttons(e, btnName);
},
get: function(key)
{
return this.$toolbar.find('a.re-' + key);
},
setActive: function(key)
{
this.button.get(key).addClass('redactor-act');
},
setInactive: function(key)
{
this.button.get(key).removeClass('redactor-act');
},
setInactiveAll: function(key)
{
if (typeof key == 'undefined')
{
this.$toolbar.find('a.re-icon').removeClass('redactor-act');
}
else
{
this.$toolbar.find('a.re-icon').not('.re-' + key).removeClass('redactor-act');
}
},
setActiveInVisual: function()
{
this.$toolbar.find('a.re-icon').not('a.re-html').removeClass('redactor-button-disabled');
},
setInactiveInCode: function()
{
this.$toolbar.find('a.re-icon').not('a.re-html').addClass('redactor-button-disabled');
},
changeIcon: function(key, classname)
{
this.button.get(key).addClass('re-' + classname);
},
removeIcon: function(key, classname)
{
this.button.get(key).removeClass('re-' + classname);
},
setAwesome: function(key, name)
{
var $button = this.button.get(key);
$button.removeClass('redactor-btn-image').addClass('fa-redactor-btn');
$button.html('');
},
addCallback: function($btn, callback)
{
var type = (callback == 'dropdown') ? 'dropdown' : 'func';
var key = $btn.attr('rel');
$btn.on('touchstart click', $.proxy(function(e)
{
if ($btn.hasClass('redactor-button-disabled')) return false;
this.button.onClick(e, key, type, callback);
}, this));
},
addDropdown: function($btn, dropdown)
{
var key = $btn.attr('rel');
this.button.addCallback($btn, 'dropdown');
var $dropdown = $('');
$btn.data('dropdown', $dropdown);
// build dropdown
if (dropdown) this.dropdown.build(key, $dropdown, dropdown);
return $dropdown;
},
add: function(key, title)
{
if (!this.opts.toolbar) return;
var btn = this.button.build(key, { title: title });
btn.addClass('redactor-btn-image');
this.$toolbar.append($(' ').append(btn));
return btn;
},
addFirst: function(key, title)
{
if (!this.opts.toolbar) return;
var btn = this.button.build(key, { title: title });
this.$toolbar.prepend($('').append(btn));
return btn;
},
addAfter: function(afterkey, key, title)
{
if (!this.opts.toolbar) return;
var btn = this.button.build(key, { title: title });
var $btn = this.button.get(afterkey);
if ($btn.length !== 0) $btn.parent().after($('').append(btn));
else this.$toolbar.append($('').append(btn));
return btn;
},
addBefore: function(beforekey, key, title)
{
if (!this.opts.toolbar) return;
var btn = this.button.build(key, { title: title });
var $btn = this.button.get(beforekey);
if ($btn.length !== 0) $btn.parent().before($('').append(btn));
else this.$toolbar.append($('').append(btn));
return btn;
},
remove: function(key)
{
this.button.get(key).remove();
}
};
},
caret: function()
{
return {
setStart: function(node)
{
// inline tag
if (!this.utils.isBlock(node))
{
var space = this.utils.createSpaceElement();
$(node).prepend(space);
this.caret.setEnd(space);
}
else
{
this.caret.set(node, 0, node, 0);
}
},
setEnd: function(node)
{
this.caret.set(node, 1, node, 1);
},
set: function(orgn, orgo, focn, foco)
{
// focus
// disabled in 10.0.7
// if (!this.utils.browser('msie')) this.$editor.focus();
orgn = orgn[0] || orgn;
focn = focn[0] || focn;
if (this.utils.isBlockTag(orgn.tagName) && orgn.innerHTML === '')
{
orgn.innerHTML = this.opts.invisibleSpace;
}
if (orgn.tagName == 'BR' && this.opts.linebreaks === false)
{
var parent = $(this.opts.emptyHtml)[0];
$(orgn).replaceWith(parent);
orgn = parent;
focn = orgn;
}
this.selection.get();
try
{
this.range.setStart(orgn, orgo);
this.range.setEnd(focn, foco);
}
catch (e) {}
this.selection.addRange();
},
setAfter: function(node)
{
try
{
var tag = $(node)[0].tagName;
// inline tag
if (tag != 'BR' && !this.utils.isBlock(node))
{
var space = this.utils.createSpaceElement();
$(node).after(space);
this.caret.setEnd(space);
}
else
{
if (tag != 'BR' && this.utils.browser('msie'))
{
this.caret.setStart($(node).next());
}
else
{
this.caret.setAfterOrBefore(node, 'after');
}
}
}
catch (e)
{
var space = this.utils.createSpaceElement();
$(node).after(space);
this.caret.setEnd(space);
}
},
setBefore: function(node)
{
// block tag
if (this.utils.isBlock(node))
{
this.caret.setEnd($(node).prev());
}
else
{
this.caret.setAfterOrBefore(node, 'before');
}
},
setAfterOrBefore: function(node, type)
{
// focus
if (!this.utils.browser('msie')) this.$editor.focus();
node = node[0] || node;
this.selection.get();
if (type == 'after')
{
try {
this.range.setStartAfter(node);
this.range.setEndAfter(node);
}
catch (e) {}
}
else
{
try {
this.range.setStartBefore(node);
this.range.setEndBefore(node);
}
catch (e) {}
}
this.range.collapse(false);
this.selection.addRange();
},
getOffsetOfElement: function(node)
{
node = node[0] || node;
this.selection.get();
var cloned = this.range.cloneRange();
cloned.selectNodeContents(node);
cloned.setEnd(this.range.endContainer, this.range.endOffset);
return $.trim(cloned.toString()).length;
},
getOffset: function()
{
var offset = 0;
var sel = window.getSelection();
if (sel.rangeCount > 0)
{
var range = window.getSelection().getRangeAt(0);
var caretRange = range.cloneRange();
caretRange.selectNodeContents(this.$editor[0]);
caretRange.setEnd(range.endContainer, range.endOffset);
offset = caretRange.toString().length;
}
return offset;
},
setOffset: function(start, end)
{
if (typeof end == 'undefined') end = start;
if (!this.focus.isFocused()) this.focus.setStart();
var sel = this.selection.get();
var node, offset = 0;
var walker = document.createTreeWalker(this.$editor[0], NodeFilter.SHOW_TEXT, null, null);
while (node = walker.nextNode())
{
offset += node.nodeValue.length;
if (offset > start)
{
this.range.setStart(node, node.nodeValue.length + start - offset);
start = Infinity;
}
if (offset >= end)
{
this.range.setEnd(node, node.nodeValue.length + end - offset);
break;
}
}
this.range.collapse(false);
this.selection.addRange();
},
// deprecated
setToPoint: function(start, end)
{
this.caret.setOffset(start, end);
},
getCoords: function()
{
return this.caret.getOffset();
}
};
},
clean: function()
{
return {
onSet: function(html)
{
html = this.clean.savePreCode(html);
// convert script tag
html = html.replace(/');
// restore form tag
html = this.clean.restoreFormTags(html);
var chars = {
'\u2122': '™',
'\u00a9': '©',
'\u2026': '…',
'\u2014': '—',
'\u2010': '‐'
};
// replace special characters
$.each(chars, function(i,s)
{
html = html.replace(new RegExp(i, 'g'), s);
});
// remove br in the of li
html = html.replace(new RegExp('
', 'gi'), '');
html = html.replace(new RegExp(' ', 'gi'), '');
// remove verified
html = html.replace(new RegExp(' ]) data-tagblock="redactor"(.*?[^>])>', 'gi'), ' ');
html = html.replace(new RegExp('<(.*?) data-verified="redactor"(.*?[^>])>', 'gi'), '<$1$2>');
html = html.replace(new RegExp(' ])\srel="(.*?[^>])"(.*?[^>])>', 'gi'), '');
html = html.replace(new RegExp('])\srel="(.*?[^>])"(.*?[^>])>', 'gi'), '');
html = html.replace(new RegExp('])\sstyle="" (.*?[^>])>', 'gi'), '');
html = html.replace(new RegExp('])\sstyle (.*?[^>])>', 'gi'), '');
html = html.replace(new RegExp('(.*?)', 'gi'), '$1');
html = html.replace(/ data-save-url="(.*?[^>])"/gi, '');
// remove image resize
html = html.replace(/])>([\w\W]*?)<\/span>/gi, '$3');
html = html.replace(/])>(.*?)<\/span>/gi, '');
html = html.replace(/])>(.*?)<\/span>/gi, '');
// remove font tag
html = html.replace(//gi, '');
html = html.replace(/<\/font>/gi, '');
// tidy html
html = this.tidy.load(html);
// link nofollow
if (this.opts.linkNofollow)
{
html = html.replace(/])>/gi, '');
html = html.replace(/])>/gi, '');
}
// reconvert inline
html = html.replace(/\sdata-redactor-(tag|class|style)="(.*?[^>])"/gi, '');
html = html.replace(new RegExp('<(.*?) data-verified="redactor"(.*?[^>])>', 'gi'), '<$1$2>');
html = html.replace(new RegExp('<(.*?) data-verified="redactor">', 'gi'), '<$1>');
return html;
},
onPaste: function(html, setMode)
{
html = $.trim(html);
html = html.replace(/\$/g, '$');
html = html.replace(/‘/g, '\'');
html = html.replace(/’/g, '\'');
// convert dirty spaces
html = html.replace(//gi, '');
html = html.replace(/ <\/span>/gi, ' ');
html = html.replace(/]*>\t<\/span>/gi, '\t');
html = html.replace(/]*>(\s| )<\/span>/gi, ' ');
if (this.opts.pastePlainText)
{
return this.clean.getPlainText(html);
}
if (!this.utils.isSelectAll() && typeof setMode == 'undefined')
{
if (this.utils.isCurrentOrParent(['FIGCAPTION', 'A']))
{
return this.clean.getPlainText(html, false);
}
if (this.utils.isCurrentOrParent('PRE'))
{
html = html.replace(/”/g, '"');
html = html.replace(/“/g, '"');
return this.clean.getPreCode(html);
}
if (this.utils.isCurrentOrParent(['BLOCKQUOTE', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6']))
{
html = this.clean.getOnlyImages(html);
if (!this.utils.browser('msie'))
{
var block = this.selection.getBlock();
if (block && block.tagName == 'P')
{
html = html.replace(//gi, '');
}
}
return html;
}
if (this.utils.isCurrentOrParent(['TD']))
{
html = this.clean.onPasteTidy(html, 'td');
if (this.opts.linebreaks) html = this.clean.replaceParagraphsToBr(html);
html = this.clean.replaceDivsToBr(html);
return html;
}
if (this.utils.isCurrentOrParent(['LI']))
{
return this.clean.onPasteTidy(html, 'li');
}
}
html = this.clean.isSingleLine(html, setMode);
if (!this.clean.singleLine)
{
if (this.opts.linebreaks) html = this.clean.replaceParagraphsToBr(html);
if (this.opts.replaceDivs) html = this.clean.replaceDivs(html);
html = this.clean.saveFormTags(html);
}
html = this.clean.onPasteWord(html);
html = this.clean.onPasteExtra(html);
html = this.clean.onPasteTidy(html, 'all');
// paragraphize
if (!this.clean.singleLine && this.opts.paragraphize)
{
html = this.paragraphize.load(html);
}
html = this.clean.removeDirtyStyles(html);
html = this.clean.onPasteRemoveSpans(html);
html = this.clean.onPasteRemoveEmpty(html);
html = this.clean.convertInline(html);
return html;
},
onPasteWord: function(html)
{
// comments
html = html.replace(//gi, '');
// style
html = html.replace(/ |