([\w\W]*?)<\/p>/gi, '$2 ');
}
// $ fix
html = html.replace(/$/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('' + this.tagName, 'gi');
newTag = newTag.replace(regex, '' + replacementTag);
$(this).replaceWith(newTag);
});
},
setSpansVerifiedHtml: function(html)
{
html = html.replace(//, '');
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$1>');
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 == '
').append(btn));
},
buttonAddAfter: function(afterkey, key, title, callback, dropdown)
{
if (!this.opts.toolbar) return;
var btn = this.buttonBuild(key, { title: title, callback: callback, dropdown: dropdown }, true);
var $btn = this.buttonGet(afterkey);
if ($btn.size() !== 0) $btn.parent().after($('
').append(btn));
else this.$toolbar.append($('
').append(btn));
return btn;
},
buttonAddBefore: function(beforekey, key, title, callback, dropdown)
{
if (!this.opts.toolbar) return;
var btn = this.buttonBuild(key, { title: title, callback: callback, dropdown: dropdown }, true);
var $btn = this.buttonGet(beforekey);
if ($btn.size() !== 0) $btn.parent().before($('
').append(btn));
else this.$toolbar.append($('
').append(btn));
return btn;
},
buttonRemove: function (key)
{
var $btn = this.buttonGet(key);
$btn.remove();
},
buttonActiveObserver: function(e, btnName)
{
var parent = this.getParent();
this.buttonInactiveAll(btnName);
if (e === false && btnName !== 'html')
{
if ($.inArray(btnName, this.opts.activeButtons) != -1)
{
this.buttonActiveToggle(btnName);
}
return;
}
if (parent && parent.tagName === 'A') this.$toolbar.find('a.redactor_dropdown_link').text(this.opts.curLang.link_edit);
else this.$toolbar.find('a.redactor_dropdown_link').text(this.opts.curLang.link_insert);
$.each(this.opts.activeButtonsStates, $.proxy(function(key, value)
{
if ($(parent).closest(key, this.$editor.get()[0]).length != 0)
{
this.buttonActive(value);
}
}, this));
var $parent = $(parent).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]);
if ($parent.length)
{
var align = $parent.css('text-align');
if (align == '')
{
align = 'left';
}
this.buttonActive('align' + align);
}
},
// EXEC
execPasteFrag: function(html)
{
var sel = this.getSelection();
if (sel.getRangeAt && sel.rangeCount)
{
var range = this.getRange();
range.deleteContents();
var el = this.document.createElement("div");
el.innerHTML = html;
var frag = this.document.createDocumentFragment(), node, lastNode;
while ((node = el.firstChild))
{
lastNode = frag.appendChild(node);
}
var firstNode = frag.firstChild;
range.insertNode(frag);
if (lastNode)
{
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
}
sel.removeAllRanges();
sel.addRange(range);
}
},
exec: function(cmd, param, sync)
{
if (cmd === 'formatblock' && this.browser('msie'))
{
param = '<' + param + '>';
}
if (cmd === 'inserthtml' && this.browser('msie'))
{
if (!this.isIe11())
{
this.focusWithSaveScroll();
this.document.selection.createRange().pasteHTML(param);
}
else this.execPasteFrag(param);
}
else
{
this.document.execCommand(cmd, false, param);
}
if (sync !== false) this.sync();
this.callback('execCommand', cmd, param);
},
execCommand: function(cmd, param, sync)
{
if (!this.opts.visual)
{
this.$source.focus();
return false;
}
if ( cmd === 'bold'
|| cmd === 'italic'
|| cmd === 'underline'
|| cmd === 'strikethrough')
{
this.bufferSet();
}
if (cmd === 'superscript' || cmd === 'subscript')
{
var parent = this.getParent();
if (parent.tagName === 'SUP' || parent.tagName === 'SUB')
{
this.inlineRemoveFormatReplace(parent);
}
}
if (cmd === 'inserthtml')
{
this.insertHtml(param, sync);
this.callback('execCommand', cmd, param);
return;
}
// Stop formatting pre
if (this.currentOrParentIs('PRE') && !this.opts.formattingPre) return false;
// Lists
if (cmd === 'insertunorderedlist' || cmd === 'insertorderedlist') return this.execLists(cmd, param);
// Unlink
if (cmd === 'unlink') return this.execUnlink(cmd, param);
// Usual exec
this.exec(cmd, param, sync);
// Line
if (cmd === 'inserthorizontalrule') this.$editor.find('hr').removeAttr('id');
},
execUnlink: function(cmd, param)
{
this.bufferSet();
var link = this.currentOrParentIs('A');
if (link)
{
$(link).replaceWith($(link).text());
this.sync();
this.callback('execCommand', cmd, param);
return;
}
},
execLists: function(cmd, param)
{
this.bufferSet();
var parent = this.getParent();
var $list = $(parent).closest('ol, ul');
if (!this.isParentRedactor($list) && $list.size() != 0)
{
$list = false;
}
var remove = false;
if ($list && $list.length)
{
remove = true;
var listTag = $list[0].tagName;
if ((cmd === 'insertunorderedlist' && listTag === 'OL')
|| (cmd === 'insertorderedlist' && listTag === 'UL'))
{
remove = false;
}
}
this.selectionSave();
// remove lists
if (remove)
{
var nodes = this.getNodes();
var elems = this.getBlocks(nodes);
if (typeof nodes[0] != 'undefined' && nodes.length > 1 && nodes[0].nodeType == 3)
{
// fix the adding the first li to the array
elems.unshift(this.getBlock());
}
var data = '', replaced = '';
$.each(elems, $.proxy(function(i,s)
{
if (s.tagName == 'LI')
{
var $s = $(s);
var cloned = $s.clone();
cloned.find('ul', 'ol').remove();
if (this.opts.linebreaks === false)
{
data += this.outerHtml($('
').append(cloned.contents()));
}
else
{
var clonedHtml = cloned.html().replace(/ $/i, '');
data += clonedHtml + ' ';
}
if (i == 0)
{
$s.addClass('redactor-replaced').empty();
replaced = this.outerHtml($s);
}
else $s.remove();
}
}, this));
html = this.$editor.html().replace(replaced, '' + listTag + '>' + data + '<' + listTag + '>');
this.$editor.html(html);
this.$editor.find(listTag + ':empty').remove();
}
// insert lists
else
{
var firstParent = $(this.getParent()).closest('td');
if (this.browser('msie') && !this.isIe11() && this.opts.linebreaks)
{
var wrapper = this.selectionWrap('div');
var wrapperHtml = $(wrapper).html();
var tmpList = $('
');
if (cmd == 'insertorderedlist')
{
tmpList = $('');
}
var tmpLi = $('
');
if ($.trim(wrapperHtml) == '')
{
tmpLi.append(wrapperHtml + '' + this.opts.invisibleSpace + '');
tmpList.append(tmpLi);
this.$editor.find('#selection-marker-1').replaceWith(tmpList);
}
else
{
tmpLi.append(wrapperHtml);
tmpList.append(tmpLi);
$(wrapper).replaceWith(tmpList);
}
}
else
{
this.document.execCommand(cmd);
}
var parent = this.getParent();
var $list = $(parent).closest('ol, ul');
if (this.opts.linebreaks === false)
{
var listText = $.trim($list.text());
if (listText == '')
{
$list.children('li').find('br').remove();
$list.children('li').append('' + this.opts.invisibleSpace + '');
}
}
if (firstParent.size() != 0)
{
$list.wrapAll('
');
}
if ($list.length)
{
// remove block-element list wrapper
var $listParent = $list.parent();
if (this.isParentRedactor($listParent) && $listParent[0].tagName != 'LI' && this.nodeTestBlocks($listParent[0]))
{
$listParent.replaceWith($listParent.contents());
}
}
if (this.browser('mozilla'))
{
this.$editor.focus();
}
}
this.selectionRestore();
this.$editor.find('#selection-marker-1').removeAttr('id');
this.sync();
this.callback('execCommand', cmd, param);
return;
},
// INDENTING
indentingIndent: function()
{
this.indentingStart('indent');
},
indentingOutdent: function()
{
this.indentingStart('outdent');
},
indentingStart: function(cmd)
{
this.bufferSet();
if (cmd === 'indent')
{
var block = this.getBlock();
this.selectionSave();
if (block && block.tagName == 'LI')
{
// li
var parent = this.getParent();
var $list = $(parent).closest('ol, ul');
var listTag = $list[0].tagName;
var elems = this.getBlocks();
$.each(elems, function(i,s)
{
if (s.tagName == 'LI')
{
var $prev = $(s).prev();
if ($prev.size() != 0 && $prev[0].tagName == 'LI')
{
var $childList = $prev.children('ul, ol');
if ($childList.size() == 0)
{
$prev.append($('<' + listTag + '>').append(s));
}
else $childList.append(s);
}
}
});
}
// linebreaks
else if (block === false && this.opts.linebreaks === true)
{
this.exec('formatBlock', 'blockquote');
var newblock = this.getBlock();
var block = $('
').html($(newblock).html());
$(newblock).replaceWith(block);
var left = this.normalize($(block).css('margin-left')) + this.opts.indentValue;
$(block).css('margin-left', left + 'px');
}
else
{
// all block tags
var elements = this.getBlocks();
$.each(elements, $.proxy(function(i, elem)
{
var $el = false;
if (elem.tagName === 'TD') return;
if ($.inArray(elem.tagName, this.opts.alignmentTags) !== -1)
{
$el = $(elem);
}
else
{
$el = $(elem).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]);
}
var left = this.normalize($el.css('margin-left')) + this.opts.indentValue;
$el.css('margin-left', left + 'px');
}, this));
}
this.selectionRestore();
}
// outdent
else
{
this.selectionSave();
var block = this.getBlock();
if (block && block.tagName == 'LI')
{
// li
var elems = this.getBlocks();
var index = 0;
this.insideOutdent(block, index, elems);
}
else
{
// all block tags
var elements = this.getBlocks();
$.each(elements, $.proxy(function(i, elem)
{
var $el = false;
if ($.inArray(elem.tagName, this.opts.alignmentTags) !== -1)
{
$el = $(elem);
}
else
{
$el = $(elem).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]);
}
var left = this.normalize($el.css('margin-left')) - this.opts.indentValue;
if (left <= 0)
{
// linebreaks
if (this.opts.linebreaks === true && typeof($el.data('tagblock')) !== 'undefined')
{
$el.replaceWith($el.html() + ' ');
}
// all block tags
else
{
$el.css('margin-left', '');
this.removeEmptyAttr($el, 'style');
}
}
else
{
$el.css('margin-left', left + 'px');
}
}, this));
}
this.selectionRestore();
}
this.sync();
},
insideOutdent: function (li, index, elems)
{
if (li && li.tagName == 'LI')
{
var $parent = $(li).parent().parent();
if ($parent.size() != 0 && $parent[0].tagName == 'LI')
{
$parent.after(li);
}
else
{
if (typeof elems[index] != 'undefined')
{
li = elems[index];
index++;
this.insideOutdent(li, index, elems);
}
else
{
this.execCommand('insertunorderedlist');
}
}
}
},
// ALIGNMENT
alignmentLeft: function()
{
this.alignmentSet('', 'JustifyLeft');
},
alignmentRight: function()
{
this.alignmentSet('right', 'JustifyRight');
},
alignmentCenter: function()
{
this.alignmentSet('center', 'JustifyCenter');
},
alignmentJustify: function()
{
this.alignmentSet('justify', 'JustifyFull');
},
alignmentSet: function(type, cmd)
{
this.bufferSet();
if (this.oldIE())
{
this.document.execCommand(cmd, false, false);
return true;
}
this.selectionSave();
var block = this.getBlock();
if (!block && this.opts.linebreaks)
{
// one element
this.exec('formatblock', 'div');
var newblock = this.getBlock();
var block = $('
').html($(newblock).html());
$(newblock).replaceWith(block);
$(block).css('text-align', type);
this.removeEmptyAttr(block, 'style');
if (type == '' && typeof($(block).data('tagblock')) !== 'undefined')
{
$(block).replaceWith($(block).html());
}
}
else
{
var elements = this.getBlocks();
$.each(elements, $.proxy(function(i, elem)
{
var $el = false;
if ($.inArray(elem.tagName, this.opts.alignmentTags) !== -1)
{
$el = $(elem);
}
else
{
$el = $(elem).closest(this.opts.alignmentTags.toString().toLowerCase(), this.$editor[0]);
}
if ($el)
{
$el.css('text-align', type);
this.removeEmptyAttr($el, 'style');
}
}, this));
}
this.selectionRestore();
this.sync();
},
// CLEAN
cleanEmpty: function(html)
{
var ph = this.placeholderStart(html);
if (ph !== false) return ph;
if (this.opts.linebreaks === false)
{
if (html === '') html = this.opts.emptyHtml;
else if (html.search(/^$/gi) !== -1) html = '' + this.opts.emptyHtml;
}
return html;
},
cleanConverters: function(html)
{
// convert div to p
if (this.opts.convertDivs && !this.opts.gallery)
{
html = html.replace(/
([\w\W]*?)<\/div>/gi, '
$2
');
}
if (this.opts.paragraphy) html = this.cleanParagraphy(html);
return html;
},
cleanConvertProtected: function(html)
{
if (this.opts.templateVars)
{
html = html.replace(/\{\{(.*?)\}\}/gi, '');
html = html.replace(/\{(.*?)\}/gi, '');
}
html = html.replace(/');
html = html.replace(/([\w\W]*?)<\/section>/gi, '');
html = html.replace(/([\w\W]*?)<\/section>/gi, '');
// php tags convertation
if (this.opts.phpTags) html = html.replace(/([\w\W]*?)<\/section>/gi, '');
return html;
},
cleanRemoveSpaces: function(html, buffer)
{
if (buffer !== false)
{
var buffer = []
var matches = html.match(/<(pre|style|script|title)(.*?)>([\w\W]*?)<\/(pre|style|script|title)>/gi);
if (matches === null) matches = [];
if (this.opts.phpTags)
{
var phpMatches = html.match(/<\?php([\w\W]*?)\?>/gi);
if (phpMatches) matches = $.merge(matches, phpMatches);
}
if (matches)
{
$.each(matches, function(i, s)
{
html = html.replace(s, 'buffer_' + i);
buffer.push(s);
});
}
}
html = html.replace(/\n/g, ' ');
html = html.replace(/[\t]*/g, '');
html = html.replace(/\n\s*\n/g, "\n");
html = html.replace(/^[\s\n]*/g, ' ');
html = html.replace(/[\s\n]*$/g, ' ');
html = html.replace( />\s{2,} <'); // between inline tags can be only one space
html = this.cleanReplacer(html, buffer);
html = html.replace(/\n\n/g, "\n");
return html;
},
cleanReplacer: function(html, buffer)
{
if (buffer === false) return html;
$.each(buffer, function(i,s)
{
html = html.replace('buffer_' + i, s);
});
return html;
},
cleanRemoveEmptyTags: function(html)
{
// remove zero width-space
html = html.replace(/[\u200B-\u200D\uFEFF]/g, '');
var etagsInline = ["\\s*", "", "\\s*"]
var etags = ["", "
\\s*
", "", "", "
", "", "", "
", "
", "\\s*", "", "
\\s*
", "", "
", "
\\s* \\s*
", "
\\s*
", "
\\s* \\s*
"];
if (this.opts.removeEmptyTags)
{
etags = etags.concat(etagsInline);
}
else etags = etagsInline;
var len = etags.length;
for (var i = 0; i < len; ++i)
{
html = html.replace(new RegExp(etags[i], 'gi'), "");
}
return html;
},
cleanParagraphy: function(html)
{
html = $.trim(html);
if (this.opts.linebreaks === true) return html;
if (html === '' || html === '') return this.opts.emptyHtml;
html = html + "\n";
if (this.opts.removeEmptyTags === false)
{
return html;
}
var safes = [];
var matches = html.match(/<(table|div|pre|object)(.*?)>([\w\W]*?)<\/(table|div|pre|object)>/gi);
if (!matches) matches = [];
var commentsMatches = html.match(//gi);
if (commentsMatches) matches = $.merge(matches, commentsMatches);
if (this.opts.phpTags)
{
var phpMatches = html.match(/([\w\W]*?)<\/section>/gi);
if (phpMatches) matches = $.merge(matches, phpMatches);
}
if (matches)
{
$.each(matches, function(i,s)
{
safes[i] = s;
html = html.replace(s, '{replace' + i + '}\n');
});
}
html = html.replace(/ \s* /gi, "\n\n");
html = html.replace(/
/gi, "\n\n");
function R(str, mod, r)
{
return html.replace(new RegExp(str, mod), r);
}
var blocks = '(comment|html|body|head|title|meta|style|script|link|iframe|table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|option|form|map|area|blockquote|address|math|style|p|h[1-6]|hr|fieldset|legend|section|article|aside|hgroup|header|footer|nav|figure|figcaption|details|menu|summary)';
html = R('(<' + blocks + '[^>]*>)', 'gi', "\n$1");
html = R('(' + blocks + '>)', 'gi', "$1\n\n");
html = R("\r\n", 'g', "\n");
html = R("\r", 'g', "\n");
html = R("/\n\n+/", 'g', "\n\n");
var htmls = html.split(new RegExp('\n\s*\n', 'g'), -1);
html = '';
for (var i in htmls)
{
if (htmls.hasOwnProperty(i))
{
if (htmls[i].search('{replace') == -1)
{
htmls[i] = htmls[i].replace(/
', 'gi', "$1");
html = R('(?' + blocks + '[^>]*>)\s? ', 'gi', "$1");
html = R(' (\s*?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)', 'gi', '$1');
html = R("\n", 'gi', '');
html = R('
', 'gi', '
');
html = R('
', 'gi', '');
html = R('
', 'gi', '');
//html = R('
(.*?)', 'gi', '');
// html = R('
', 'gi', '');
html = R('
\t?\n?
', 'gi', '
');
html = R('
', 'gi', '');
html = R('
', 'gi', '');
html = R('
', 'gi', '');
html = R('
\t*
', 'gi', '');
// restore safes
$.each(safes, function(i,s)
{
html = html.replace('{replace' + i + '}', s);
});
return $.trim(html);
},
cleanConvertInlineTags: function(html, set)
{
var boldTag = 'strong';
if (this.opts.boldTag === 'b') boldTag = 'b';
var italicTag = 'em';
if (this.opts.italicTag === 'i') italicTag = 'i';
html = html.replace(/([\w\W]*?)<\/span>/gi, '<' + italicTag + '>$1' + italicTag + '>');
html = html.replace(/([\w\W]*?)<\/span>/gi, '<' + boldTag + '>$1' + boldTag + '>');
// bold, italic, del
if (this.opts.boldTag === 'strong') html = html.replace(/([\w\W]*?)<\/b>/gi, '$1');
else html = html.replace(/([\w\W]*?)<\/strong>/gi, '$1');
if (this.opts.italicTag === 'em') html = html.replace(/([\w\W]*?)<\/i>/gi, '$1');
else html = html.replace(/([\w\W]*?)<\/em>/gi, '$1');
html = html.replace(/([\w\W]*?)<\/span>/gi, '$1');
if (set !== true) html = html.replace(/([\w\W]*?)<\/strike>/gi, '$1');
else html = html.replace(/([\w\W]*?)<\/del>/gi, '$1');
return html;
},
cleanStripTags: function(html)
{
if (html == '' || typeof html == 'undefined') return html;
var allowed = false;
if (this.opts.allowedTags !== false) allowed = true;
var arr = allowed === true ? this.opts.allowedTags : this.opts.deniedTags;
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi;
html = html.replace(tags, function ($0, $1)
{
if (allowed === true) return $.inArray($1.toLowerCase(), arr) > '-1' ? $0 : '';
else return $.inArray($1.toLowerCase(), arr) > '-1' ? '' : $0;
});
html = this.cleanConvertInlineTags(html);
return html;
},
cleanSavePreCode: function(html, encode)
{
var pre = html.match(/<(pre|code)(.*?)>([\w\W]*?)<\/(pre|code)>/gi);
if (pre !== null)
{
$.each(pre, $.proxy(function(i,s)
{
var arr = s.match(/<(pre|code)(.*?)>([\w\W]*?)<\/(pre|code)>/i);
arr[3] = arr[3].replace(/ /g, ' ');
if (encode !== false) arr[3] = this.cleanEncodeEntities(arr[3]);
// $ fix
arr[3] = arr[3].replace(/\$/g, '$');
html = html.replace(s, '<' + arr[1] + arr[2] + '>' + arr[3] + '' + arr[1] + '>');
}, this));
}
return html;
},
cleanEncodeEntities: function(str)
{
str = String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"');
},
cleanUnverified: function()
{
// label, abbr, mark, meter, code, q, dfn, ins, time, kbd, var
var $elem = this.$editor.find('li, img, a, b, strong, sub, sup, i, em, u, small, strike, del, span, cite');
$elem.filter('[style*="background-color: transparent;"][style*="line-height"]')
.css('background-color', '')
.css('line-height', '');
$elem.filter('[style*="background-color: transparent;"]')
.css('background-color', '');
$elem.css('line-height', '');
$.each($elem, $.proxy(function(i,s)
{
this.removeEmptyAttr(s, 'style');
}, this));
var $elem2 = this.$editor.find('b, strong, i, em, u, strike, del');
$elem2.css('font-size', '');
$.each($elem2, $.proxy(function(i,s)
{
this.removeEmptyAttr(s, 'style');
}, this));
// When we paste text in Safari is wrapping inserted div (remove it)
this.$editor.find('div[style="text-align: -webkit-auto;"]').contents().unwrap();
// Remove all styles in ul, ol, li
this.$editor.find('ul, ol, li').removeAttr('style');
},
// TEXTAREA CODE FORMATTING
cleanHtml: function(code)
{
var i = 0,
codeLength = code.length,
point = 0,
start = null,
end = null,
tag = '',
out = '',
cont = '';
this.cleanlevel = 0;
for (; i < codeLength; i++)
{
point = i;
// if no more tags, copy and exit
if (-1 == code.substr(i).indexOf( '<' ))
{
out += code.substr(i);
return this.cleanFinish(out);
}
// copy verbatim until a tag
while (point < codeLength && code.charAt(point) != '<')
{
point++;
}
if (i != point)
{
cont = code.substr(i, point - i);
if (!cont.match(/^\s{2,}$/g))
{
if ('\n' == out.charAt(out.length - 1)) out += this.cleanGetTabs();
else if ('\n' == cont.charAt(0))
{
out += '\n' + this.cleanGetTabs();
cont = cont.replace(/^\s+/, '');
}
out += cont;
}
if (cont.match(/\n/)) out += '\n' + this.cleanGetTabs();
}
start = point;
// find the end of the tag
while (point < codeLength && '>' != code.charAt(point))
{
point++;
}
tag = code.substr(start, point - start);
i = point;
var t;
if ('!--' == tag.substr(1, 3))
{
if (!tag.match(/--$/))
{
while ('-->' != code.substr(point, 3))
{
point++;
}
point += 2;
tag = code.substr(start, point - start);
i = point;
}
if ('\n' != out.charAt(out.length - 1)) out += '\n';
out += this.cleanGetTabs();
out += tag + '>\n';
}
else if ('!' == tag[1])
{
out = this.placeTag(tag + '>', out);
}
else if ('?' == tag[1])
{
out += tag + '>\n';
}
else if (t = tag.match(/^<(script|style|pre)/i))
{
t[1] = t[1].toLowerCase();
tag = this.cleanTag(tag);
out = this.placeTag(tag, out);
end = String(code.substr(i + 1)).toLowerCase().indexOf('' + t[1]);
if (end)
{
cont = code.substr(i + 1, end);
i += end;
out += cont;
}
}
else
{
tag = this.cleanTag(tag);
out = this.placeTag(tag, out);
}
}
return this.cleanFinish(out);
},
cleanGetTabs: function()
{
var s = '';
for ( var j = 0; j < this.cleanlevel; j++ )
{
s += '\t';
}
return s;
},
cleanFinish: function(code)
{
code = code.replace(/\n\s*\n/g, '\n');
code = code.replace(/^[\s\n]*/, '');
code = code.replace(/[\s\n]*$/, '');
code = code.replace(/');
this.cleanlevel = 0;
return code;
},
cleanTag: function (tag)
{
var tagout = '';
tag = tag.replace(/\n/g, ' ');
tag = tag.replace(/\s{2,}/g, ' ');
tag = tag.replace(/^\s+|\s+$/g, ' ');
var suffix = '';
if (tag.match(/\/$/))
{
suffix = '/';
tag = tag.replace(/\/+$/, '');
}
var m;
while (m = /\s*([^= ]+)(?:=((['"']).*?\3|[^ ]+))?/.exec(tag))
{
if (m[2]) tagout += m[1].toLowerCase() + '=' + m[2];
else if (m[1]) tagout += m[1].toLowerCase();
tagout += ' ';
tag = tag.substr(m[0].length);
}
return tagout.replace(/\s*$/, '') + suffix + '>';
},
placeTag: function (tag, out)
{
var nl = tag.match(this.cleannewLevel);
if (tag.match(this.cleanlineBefore) || nl)
{
out = out.replace(/\s*$/, '');
out += '\n';
}
if (nl && '/' == tag.charAt(1)) this.cleanlevel--;
if ('\n' == out.charAt(out.length - 1)) out += this.cleanGetTabs();
if (nl && '/' != tag.charAt(1)) this.cleanlevel++;
out += tag;
if (tag.match(this.cleanlineAfter) || tag.match(this.cleannewLevel))
{
out = out.replace(/ *$/, '');
out += '\n';
}
return out;
},
// FORMAT
formatEmpty: function(e)
{
var html = $.trim(this.$editor.html());
if (this.opts.linebreaks)
{
if (html == '')
{
e.preventDefault();
this.$editor.html('');
this.focus();
}
}
else
{
html = html.replace(/ /i, '');
var thtml = html.replace(/
\s?<\/p>/gi, '');
if (html === '' || thtml === '')
{
e.preventDefault();
var node = $(this.opts.emptyHtml).get(0);
this.$editor.html(node);
this.focus();
}
}
this.sync();
},
formatBlocks: function(tag)
{
if (this.browser('mozilla') && this.isFocused())
{
this.$editor.focus();
}
this.bufferSet();
var nodes = this.getBlocks();
this.selectionSave();
$.each(nodes, $.proxy(function(i, node)
{
if (node.tagName !== 'LI')
{
var parent = $(node).parent();
if (tag === 'p')
{
if ((node.tagName === 'P'
&& parent.size() != 0
&& parent[0].tagName === 'BLOCKQUOTE')
||
node.tagName === 'BLOCKQUOTE')
{
this.formatQuote();
return;
}
else if (this.opts.linebreaks)
{
if (node && node.tagName.search(/H[1-6]/) == 0)
{
$(node).replaceWith(node.innerHTML + ' ');
}
else return;
}
else
{
this.formatBlock(tag, node);
}
}
else
{
this.formatBlock(tag, node);
}
}
}, this));
this.selectionRestore();
this.sync();
},
formatBlock: function(tag, block)
{
if (block === false) block = this.getBlock();
if (block === false && this.opts.linebreaks === true)
{
this.execCommand('formatblock', tag);
return true;
}
var contents = '';
if (tag !== 'pre')
{
contents = $(block).contents();
}
else
{
//contents = this.cleanEncodeEntities($(block).text());
contents = $(block).html();
if ($.trim(contents) === '')
{
contents = '';
}
}
if (block.tagName === 'PRE') tag = 'p';
if (this.opts.linebreaks === true && tag === 'p')
{
$(block).replaceWith($('
').append(contents).html() + ' ');
}
else
{
var parent = this.getParent();
var node = $('<' + tag + '>').append(contents);
$(block).replaceWith(node);
if (parent && parent.tagName == 'TD')
{
$(node).wrapAll('
');
}
}
},
formatChangeTag: function(fromElement, toTagName, save)
{
if (save !== false) this.selectionSave();
var newElement = $('<' + toTagName + '/>');
$(fromElement).replaceWith(function() { return newElement.append($(this).contents()); });
if (save !== false) this.selectionRestore();
return newElement;
},
// QUOTE
formatQuote: function()
{
if (this.browser('mozilla') && this.isFocused())
{
this.$editor.focus();
}
this.bufferSet();
// paragraphy
if (this.opts.linebreaks === false)
{
this.selectionSave();
var blocks = this.getBlocks();
var blockquote = false;
var blocksLen = blocks.length;
if (blocks)
{
var data = '';
var replaced = '';
var replace = false;
var paragraphsOnly = true;
$.each(blocks, function(i,s)
{
if (s.tagName !== 'P') paragraphsOnly = false;
});
$.each(blocks, $.proxy(function(i,s)
{
if (s.tagName === 'BLOCKQUOTE')
{
this.formatBlock('p', s, false);
}
else if (s.tagName === 'P')
{
blockquote = $(s).parent();
// from blockquote
if (blockquote[0].tagName == 'BLOCKQUOTE')
{
var count = $(blockquote).children('p').size();
// one
if (count == 1)
{
$(blockquote).replaceWith(s);
}
// all
else if (count == blocksLen)
{
replace = 'blockquote';
data += this.outerHtml(s);
}
// some
else
{
replace = 'html';
data += this.outerHtml(s);
if (i == 0)
{
$(s).addClass('redactor-replaced').empty();
replaced = this.outerHtml(s);
}
else $(s).remove();
}
}
// to blockquote
else
{
if (paragraphsOnly === false || blocks.length == 1)
{
this.formatBlock('blockquote', s, false);
}
else
{
replace = 'paragraphs';
data += this.outerHtml(s);
}
}
}
else if (s.tagName !== 'LI')
{
this.formatBlock('blockquote', s, false);
}
}, this));
if (replace)
{
if (replace == 'paragraphs')
{
$(blocks[0]).replaceWith('
' + data + '
');
$(blocks).remove();
}
else if (replace == 'blockquote')
{
$(blockquote).replaceWith(data);
}
else if (replace == 'html')
{
var html = this.$editor.html().replace(replaced, '' + data + '
');
this.$editor.html(html);
this.$editor.find('blockquote').each(function()
{
if ($.trim($(this).html()) == '') $(this).remove();
})
}
}
}
this.selectionRestore();
}
// linebreaks
else
{
var block = this.getBlock();
if (block.tagName === 'BLOCKQUOTE')
{
this.selectionSave();
var html = $.trim($(block).html());
var selection = $.trim(this.getSelectionHtml());
html = html.replace(//gi, '');
if (html == selection)
{
$(block).replaceWith($(block).html() + ' ');
}
else
{
// replace
this.inlineFormat('tmp');
var tmp = this.$editor.find('tmp');
tmp.empty();
var newhtml = this.$editor.html().replace('', '
' + this.opts.invisibleSpace + '' + selection + '
');
this.$editor.html(newhtml);
tmp.remove();
this.$editor.find('blockquote').each(function()
{
if ($.trim($(this).html()) == '') $(this).remove();
})
}
this.selectionRestore();
this.$editor.find('span#selection-marker-1').attr('id', false);
}
else
{
var wrapper = this.selectionWrap('blockquote');
var html = $(wrapper).html();
var blocksElemsRemove = ['ul', 'ol', 'table', 'tr', 'tbody', 'thead', 'tfoot', 'dl'];
$.each(blocksElemsRemove, function(i,s)
{
html = html.replace(new RegExp('<' + s + '(.*?)>', 'gi'), '');
html = html.replace(new RegExp('' + s + '>', 'gi'), '');
});
var blocksElems = this.opts.blockLevelElements;
$.each(blocksElems, function(i,s)
{
html = html.replace(new RegExp('<' + s + '(.*?)>', 'gi'), '');
html = html.replace(new RegExp('' + s + '>', 'gi'), ' ');
});
$(wrapper).html(html);
this.selectionElement(wrapper);
var next = $(wrapper).next();
if (next.size() != 0 && next[0].tagName === 'BR')
{
next.remove();
}
}
}
this.sync();
},
// BLOCK
blockRemoveAttr: function(attr, value)
{
var nodes = this.getBlocks();
$(nodes).removeAttr(attr);
this.sync();
},
blockSetAttr: function(attr, value)
{
var nodes = this.getBlocks();
$(nodes).attr(attr, value);
this.sync();
},
blockRemoveStyle: function(rule)
{
var nodes = this.getBlocks();
$(nodes).css(rule, '');
this.removeEmptyAttr(nodes, 'style');
this.sync();
},
blockSetStyle: function (rule, value)
{
var nodes = this.getBlocks();
$(nodes).css(rule, value);
this.sync();
},
blockRemoveClass: function(className)
{
var nodes = this.getBlocks();
$(nodes).removeClass(className);
this.removeEmptyAttr(nodes, 'class');
this.sync();
},
blockSetClass: function(className)
{
var nodes = this.getBlocks();
$(nodes).addClass(className);
this.sync();
},
// INLINE
inlineRemoveClass: function(className)
{
this.selectionSave();
this.inlineEachNodes(function(node)
{
$(node).removeClass(className);
this.removeEmptyAttr(node, 'class');
});
this.selectionRestore();
this.sync();
},
inlineSetClass: function(className)
{
var current = this.getCurrent();
if (!$(current).hasClass(className)) this.inlineMethods('addClass', className);
},
inlineRemoveStyle: function (rule)
{
this.selectionSave();
this.inlineEachNodes(function(node)
{
$(node).css(rule, '');
this.removeEmptyAttr(node, 'style');
});
this.selectionRestore();
this.sync();
},
inlineSetStyle: function(rule, value)
{
this.inlineMethods('css', rule, value);
},
inlineRemoveAttr: function (attr)
{
this.selectionSave();
var range = this.getRange(), node = this.getElement(), nodes = this.getNodes();
if (range.collapsed || range.startContainer === range.endContainer && node)
{
nodes = $( node );
}
$(nodes).removeAttr(attr);
this.inlineUnwrapSpan();
this.selectionRestore();
this.sync();
},
inlineSetAttr: function(attr, value)
{
this.inlineMethods('attr', attr, value );
},
inlineMethods: function(type, attr, value)
{
this.bufferSet();
this.selectionSave();
var range = this.getRange()
var el = this.getElement();
if ((range.collapsed || range.startContainer === range.endContainer) && el && !this.nodeTestBlocks(el))
{
$(el)[type](attr, value);
}
else
{
var cmd, arg = value;
switch (attr)
{
case 'font-size':
cmd = 'fontSize';
arg = 4;
break;
case 'font-family':
cmd = 'fontName';
break;
case 'color':
cmd = 'foreColor';
break;
case 'background-color':
cmd = 'backColor';
break;
}
this.document.execCommand(cmd, false, arg);
var fonts = this.$editor.find('font');
$.each(fonts, $.proxy(function(i, s)
{
this.inlineSetMethods(type, s, attr, value);
}, this));
}
this.selectionRestore();
this.sync();
},
inlineSetMethods: function(type, s, attr, value)
{
var parent = $(s).parent(), el;
var selectionHtml = this.getSelectionText();
var parentHtml = $(parent).text();
var selected = selectionHtml == parentHtml;
if (selected && parent && parent[0].tagName === 'INLINE' && parent[0].attributes.length != 0)
{
el = parent;
$(s).replaceWith($(s).html());
}
else
{
el = $('').append($(s).contents());
$(s).replaceWith(el);
}
$(el)[type](attr, value);
return el;
},
// Sort elements and execute callback
inlineEachNodes: function(callback)
{
var range = this.getRange(),
node = this.getElement(),
nodes = this.getNodes(),
collapsed;
if (range.collapsed || range.startContainer === range.endContainer && node)
{
nodes = $(node);
collapsed = true;
}
$.each(nodes, $.proxy(function(i, node)
{
if (!collapsed && node.tagName !== 'INLINE')
{
var selectionHtml = this.getSelectionText();
var parentHtml = $(node).parent().text();
var selected = selectionHtml == parentHtml;
if (selected && node.parentNode.tagName === 'INLINE' && !$(node.parentNode).hasClass('redactor_editor'))
{
node = node.parentNode;
}
else return;
}
callback.call(this, node);
}, this ) );
},
inlineUnwrapSpan: function()
{
var $spans = this.$editor.find('inline');
$.each($spans, $.proxy(function(i, span)
{
var $span = $(span);
if ($span.attr('class') === undefined && $span.attr('style') === undefined)
{
$span.contents().unwrap();
}
}, this));
},
inlineFormat: function(tag)
{
this.selectionSave();
this.document.execCommand('fontSize', false, 4 );
var fonts = this.$editor.find('font');
var last;
$.each(fonts, function(i, s)
{
var el = $('<' + tag + '/>').append($(s).contents());
$(s).replaceWith(el);
last = el;
});
this.selectionRestore();
this.sync();
},
inlineRemoveFormat: function(tag)
{
this.selectionSave();
var utag = tag.toUpperCase();
var nodes = this.getNodes();
var parent = $(this.getParent()).parent();
$.each(nodes, function(i, s)
{
if (s.tagName === utag) this.inlineRemoveFormatReplace(s);
});
if (parent && parent[0].tagName === utag) this.inlineRemoveFormatReplace(parent);
this.selectionRestore();
this.sync();
},
inlineRemoveFormatReplace: function(el)
{
$(el).replaceWith($(el).contents());
},
// INSERT
insertHtml: function (html, sync)
{
var current = this.getCurrent();
var parent = current.parentNode;
this.focusWithSaveScroll();
this.bufferSet();
var $html = $('
').append($.parseHTML(html));
html = $html.html();
html = this.cleanRemoveEmptyTags(html);
// Update value
$html = $('
').append($.parseHTML(html));
var currBlock = this.getBlock();
if ($html.contents().length == 1)
{
var htmlTagName = $html.contents()[0].tagName;
// If the inserted and received text tags match
if (htmlTagName != 'P' && htmlTagName == currBlock.tagName || htmlTagName == 'PRE')
{
//html = $html.html();
$html = $('
').append(html);
}
}
if (this.opts.linebreaks)
{
html = html.replace(/
([\w\W]*?)<\/p>/gi, '$2 ');
}
// add text in a paragraph
if (!this.opts.linebreaks && $html.contents().length == 1 && $html.contents()[0].nodeType == 3
&& (this.getRangeSelectedNodes().length > 2 || (!current || current.tagName == 'BODY' && !parent || parent.tagName == 'HTML')))
{
html = '
' + html + '
';
}
html = this.setSpansVerifiedHtml(html);
if ($html.contents().length > 1 && currBlock
|| $html.contents().is('p, :header, ul, ol, li, div, table, td, blockquote, pre, address, section, header, footer, aside, article'))
{
if (this.browser('msie'))
{
if (!this.isIe11())
{
this.document.selection.createRange().pasteHTML(html);
}
else
{
this.execPasteFrag(html);
}
}
else
{
this.document.execCommand('inserthtml', false, html);
}
}
else this.insertHtmlAdvanced(html, false);
if (this.selectall)
{
this.window.setTimeout($.proxy(function()
{
if (!this.opts.linebreaks) this.selectionEnd(this.$editor.contents().last());
else this.focusEnd();
}, this), 1);
}
this.observeStart();
// set no editable
this.setNonEditable();
if (sync !== false) this.sync();
},
insertHtmlAdvanced: function(html, sync)
{
html = this.setSpansVerifiedHtml(html);
var sel = this.getSelection();
if (sel.getRangeAt && sel.rangeCount)
{
var range = sel.getRangeAt(0);
range.deleteContents();
var el = document.createElement('div');
el.innerHTML = html;
var frag = document.createDocumentFragment(), node, lastNode;
while ((node = el.firstChild))
{
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
if (lastNode)
{
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
if (sync !== false)
{
this.sync();
}
},
insertBeforeCursor: function(html)
{
html = this.setSpansVerifiedHtml(html);
var node = $(html);
var space = document.createElement("span");
space.innerHTML = "\u200B";
var range = this.getRange();
range.insertNode(space);
range.insertNode(node[0]);
range.collapse(false);
var sel = this.getSelection();
sel.removeAllRanges();
sel.addRange(range);
this.sync();
},
insertText: function(html)
{
var $html = $($.parseHTML(html));
if ($html.length) html = $html.text();
this.focusWithSaveScroll();
if (this.browser('msie'))
{
if (!this.isIe11())
{
this.document.selection.createRange().pasteHTML(html);
}
else
{
this.execPasteFrag(html);
}
}
else
{
this.document.execCommand('inserthtml', false, html);
}
this.sync();
},
insertNode: function(node)
{
node = node[0] || node;
if (node.tagName == 'SPAN')
{
var replacementTag = 'inline';
var outer = node.outerHTML;
// Replace opening tag
var regex = new RegExp('<' + node.tagName, 'i');
var newTag = outer.replace(regex, '<' + replacementTag);
// Replace closing tag
regex = new RegExp('' + node.tagName, 'i');
newTag = newTag.replace(regex, '' + replacementTag);
node = $(newTag)[0];
}
var sel = this.getSelection();
if (sel.getRangeAt && sel.rangeCount)
{
// with delete contents
range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode(node);
range.setEndAfter(node);
range.setStartAfter(node);
sel.removeAllRanges();
sel.addRange(range);
}
return node;
},
insertNodeToCaretPositionFromPoint: function(e, node)
{
var range;
var x = e.clientX, y = e.clientY;
if (this.document.caretPositionFromPoint)
{
var pos = this.document.caretPositionFromPoint(x, y);
range = this.getRange();
range.setStart(pos.offsetNode, pos.offset);
range.collapse(true);
range.insertNode(node);
}
else if (this.document.caretRangeFromPoint)
{
range = this.document.caretRangeFromPoint(x, y);
range.insertNode(node);
}
else if (typeof document.body.createTextRange != "undefined")
{
range = this.document.body.createTextRange();
range.moveToPoint(x, y);
var endRange = range.duplicate();
endRange.moveToPoint(x, y);
range.setEndPoint("EndToEnd", endRange);
range.select();
}
},
insertAfterLastElement: function(element, parent)
{
if (typeof(parent) != 'undefined') element = parent;
if (this.isEndOfElement())
{
if (this.opts.linebreaks)
{
var contents = $('
').append($.trim(this.$editor.html())).contents();
var last = contents.last()[0];
if (last.tagName == 'SPAN' && last.innerHTML == '')
{
last = contents.prev()[0];
}
if (this.outerHtml(last) != this.outerHtml(element))
{
return false;
}
}
else
{
if (this.$editor.contents().last()[0] !== element)
{
return false;
}
}
this.insertingAfterLastElement(element);
}
},
insertingAfterLastElement: function(element)
{
this.bufferSet();
if (this.opts.linebreaks === false)
{
var node = $(this.opts.emptyHtml);
$(element).after(node);
this.selectionStart(node);
}
else
{
var node = $('' + this.opts.invisibleSpace + '', this.document)[0];
$(element).after(node);
$(node).after(this.opts.invisibleSpace);
this.selectionRestore();
this.$editor.find('span#selection-marker-1').removeAttr('id');
}
},
insertLineBreak: function(twice)
{
this.selectionSave();
var br = ' ';
if (twice == true)
{
br = '