: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param {String|Function} param
this.insertImage = function (src, param) {
return async.createImage(src, param).then(function ($image) {
if (typeof param === 'function') {
if (typeof param === 'string') {
$image.attr('data-filename', param);
$image.css('width', Math.min($editable.width(), $image.width()));
range.create().insertNode($image[0]);
range.createFromNodeAfter($image[0]).select();
context.triggerEvent('image.upload.error');
this.insertImages = function (files) {
$.each(files, function (idx, file) {
var filename = file.name;
if (options.maximumImageFileSize && options.maximumImageFileSize < file.size) {
context.triggerEvent('image.upload.error', lang.image.maximumFileSizeError);
async.readFileAsDataURL(file).then(function (dataURL) {
return self.insertImage(dataURL, filename);
context.triggerEvent('image.upload.error');
this.insertImagesOrCallback = function (files) {
var callbacks = options.callbacks;
// If onImageUpload options setted
if (callbacks.onImageUpload) {
context.triggerEvent('image.upload', files);
// else insert Image as dataURL
this.insertImages(files);
this.insertNode = this.wrapCommand(function (node) {
range.create().insertNode(node);
range.createFromNodeAfter(node).select();
this.insertText = this.wrapCommand(function (text) {
var textNode = range.create().insertNode(dom.createText(text));
range.create(textNode, dom.nodeLength(textNode)).select();
* return selected plain text
this.getSelectedText = function () {
var rng = this.createRange();
// if range on anchor, expand range with anchor
rng = range.createFromNode(dom.ancestor(rng.sc, dom.isAnchor));
this.pasteHTML = this.wrapCommand(function (markup) {
var contents = range.create().pasteHTML(markup);
range.createFromNodeAfter(list.last(contents)).select();
* @param {String} tagName
this.formatBlock = this.wrapCommand(function (tagName) {
// [workaround] for MSIE, IE need `<`
tagName = agent.isMSIE ? '<' + tagName + '>' : tagName;
document.execCommand('FormatBlock', false, tagName);
this.formatPara = function () {
context.memo('help.formatPara', lang.help.formatPara);
/* jshint ignore:start */
for (var idx = 1; idx <= 6; idx ++) {
this['formatH' + idx] = function (idx) {
this.formatBlock('H' + idx);
context.memo('help.formatH'+idx, lang.help['formatH' + idx]);
* @param {String} value - px
this.fontSize = function (value) {
var rng = range.create();
if (rng && rng.isCollapsed()) {
var spans = style.styleNodes(rng);
var firstSpan = list.head(spans);
'font-size': value + 'px'
// [workaround] added styled bogus span for style
// - also bogus character needed for cursor position
if (firstSpan && !dom.nodeLength(firstSpan)) {
firstSpan.innerHTML = dom.ZERO_WIDTH_NBSP_CHAR;
range.createFromNodeAfter(firstSpan.firstChild).select();
$editable.data(KEY_BOGUS, firstSpan);
$(style.styleNodes(rng)).css({
'font-size': value + 'px'
this.insertHorizontalRule = this.wrapCommand(function () {
var rng = range.create();
var hrNode = rng.insertNode($('<HR/>')[0]);
if (hrNode.nextSibling) {
range.create(hrNode.nextSibling, 0).normalize().select();
context.memo('help.insertHorizontalRule', lang.help.insertHorizontalRule);
* remove bogus node and character
this.removeBogus = function () {
var bogusNode = $editable.data(KEY_BOGUS);
var textNode = list.find(list.from(bogusNode.childNodes), dom.isText);
var bogusCharIdx = textNode.nodeValue.indexOf(dom.ZERO_WIDTH_NBSP_CHAR);
if (bogusCharIdx !== -1) {
textNode.deleteData(bogusCharIdx, 1);
if (dom.isEmpty(bogusNode)) {
$editable.removeData(KEY_BOGUS);
this.lineHeight = this.wrapCommand(function (value) {
style.stylePara(range.create(), {
this.unlink = function () {
var rng = this.createRange();
var anchor = dom.ancestor(rng.sc, dom.isAnchor);
rng = range.createFromNode(anchor);
document.execCommand('unlink');
* @param {Object} linkInfo
this.createLink = this.wrapCommand(function (linkInfo) {
var linkUrl = linkInfo.url;
var linkText = linkInfo.text;
var isNewWindow = linkInfo.isNewWindow;
var rng = linkInfo.range || this.createRange();
var isTextChanged = rng.toString() !== linkText;
if (options.onCreateLink) {
linkUrl = options.onCreateLink(linkUrl);
// Create a new link when text changed.
var anchor = rng.insertNode($('<A>' + linkText + '</A>')[0]);
anchors = style.styleNodes(rng, {
expandClosestSibling: true,
onlyPartialContains: true
$.each(anchors, function (idx, anchor) {
$(anchor).attr('href', linkUrl);
$(anchor).attr('target', '_blank');
$(anchor).removeAttr('target');
var startRange = range.createFromNodeBefore(list.head(anchors));
var startPoint = startRange.getStartPoint();
var endRange = range.createFromNodeAfter(list.last(anchors));
var endPoint = endRange.getEndPoint();
* @return {WrappedRange} return.range
* @return {String} return.text
* @return {Boolean} [return.isNewWindow=true]
* @return {String} [return.url=""]
this.getLinkInfo = function () {
var rng = range.create().expand(dom.isAnchor);
// Get the first anchor on range(for edit).
var $anchor = $(list.head(rng.nodes(dom.isAnchor)));
isNewWindow: $anchor.length ? $anchor.attr('target') === '_blank' : false,
url: $anchor.length ? $anchor.attr('href') : ''
* @param {Object} sObjColor color code
* @param {String} sObjColor.foreColor foreground color
* @param {String} sObjColor.backColor background color
this.color = this.wrapCommand(function (colorInfo) {
var foreColor = colorInfo.foreColor;
var backColor = colorInfo.backColor;
if (foreColor) { document.execCommand('foreColor', false, foreColor); }
if (backColor) { document.execCommand('backColor', false, backColor); }
* @param {String} sDim dimension of table (ex : "5x5")
this.insertTable = this.wrapCommand(function (sDim) {
var dimension = sDim.split('x');
var rng = range.create().deleteContents();
rng.insertNode(table.createTable(dimension[0], dimension[1], options));
this.floatMe = this.wrapCommand(function (value) {
var $target = $(this.restoreTarget());
$target.css('float', value);
this.resize = this.wrapCommand(function (value) {
var $target = $(this.restoreTarget());
width: value * 100 + '%',
* @param {jQuery} $target - target element
* @param {Boolean} [bKeepRatio] - keep ratio
this.resizeTo = function (pos, $target, bKeepRatio) {
var newRatio = pos.y / pos.x;
var ratio = $target.data('ratio');
width: ratio > newRatio ? pos.x : pos.y / ratio,
height: ratio > newRatio ? pos.x * ratio : pos.y
this.removeMedia = this.wrapCommand(function () {
var $target = $(this.restoreTarget()).detach();
context.triggerEvent('media.delete', $target, $editable);
this.focus = function () {
// [workaround] Screen will move when page is scolled in IE.
// - do focus when not focused
if (!$editable.is(':focus')) {
// [workaround] for firefox bug http://goo.gl/lVfAaI
if (!$editable.is(':focus') && agent.isFF) {
range.createFromNode($editable[0])
* returns whether contents is empty or not.
this.isEmpty = function () {
return dom.isEmpty($editable[0]) || dom.emptyPara === $editable.html();
var Clipboard = function (context) {
var $editable = context.layoutInfo.editable;
'summernote.keydown': function (we, e) {
if (self.needKeydownHook()) {
if ((e.ctrlKey || e.metaKey) && e.keyCode === key.code.V) {
context.invoke('editor.saveRange');
this.needKeydownHook = function () {
return (agent.isMSIE && agent.browserVersion > 10) || agent.isFF;
this.initialize = function () {
// [workaround] getting image from clipboard
// - IE11 and Firefox: CTRL+v hook
// - Webkit: event.clipboardData
if (this.needKeydownHook()) {
this.$paste = $('<div />').attr('contenteditable', true).css({
$editable.before(this.$paste);
this.$paste.on('paste', function (event) {
context.triggerEvent('paste', event);
$editable.on('paste', this.pasteByEvent);
this.destroy = function () {
if (this.needKeydownHook()) {
this.pasteByHook = function () {
var node = this.$paste[0].firstChild;
var decodedData = atob(dataURI.split(',')[1]);
var array = new Uint8Array(decodedData.length);
for (var i = 0; i < decodedData.length; i++) {
array[i] = decodedData.charCodeAt(i);
var blob = new Blob([array], { type : 'image/png' });
blob.name = 'clipboard.png';
context.invoke('editor.restoreRange');
context.invoke('editor.focus');
context.invoke('editor.insertImagesOrCallback', [blob]);
var pasteContent = $('<div />').html(this.$paste.html()).html();
context.invoke('editor.restoreRange');
context.invoke('editor.focus');
context.invoke('editor.pasteHTML', pasteContent);