summernote-webpack
Version:
Super simple WYSIWYG editor
238 lines (197 loc) • 6.4 kB
JavaScript
define([
'summernote/base/core/func',
'summernote/base/core/list',
'summernote/base/core/dom',
'summernote/base/core/range',
'summernote/base/core/key'
], function (func, list, dom, range, key) {
var HintPopover = function (context) {
var self = this;
var ui = $.summernote.ui;
var POPOVER_DIST = 5;
var hint = context.options.hint || [];
var direction = context.options.hintDirection || 'bottom';
var hints = $.isArray(hint) ? hint : [hint];
this.events = {
'summernote.keyup': function (we, e) {
if (!e.isDefaultPrevented()) {
self.handleKeyup(e);
}
},
'summernote.keydown': function (we, e) {
self.handleKeydown(e);
},
'summernote.dialog.shown': function () {
self.hide();
}
};
this.shouldInitialize = function () {
return hints.length > 0;
};
this.initialize = function () {
this.lastWordRange = null;
this.$popover = ui.popover({
className: 'note-hint-popover',
hideArrow: true,
direction: ''
}).render().appendTo('body');
this.$popover.hide();
this.$content = this.$popover.find('.popover-content');
this.$content.on('click', '.note-hint-item', function () {
self.$content.find('.active').removeClass('active');
$(this).addClass('active');
self.replace();
});
};
this.destroy = function () {
this.$popover.remove();
};
this.selectItem = function ($item) {
this.$content.find('.active').removeClass('active');
$item.addClass('active');
this.$content[0].scrollTop = $item[0].offsetTop - (this.$content.innerHeight() / 2);
};
this.moveDown = function () {
var $current = this.$content.find('.note-hint-item.active');
var $next = $current.next();
if ($next.length) {
this.selectItem($next);
} else {
var $nextGroup = $current.parent().next();
if (!$nextGroup.length) {
$nextGroup = this.$content.find('.note-hint-group').first();
}
this.selectItem($nextGroup.find('.note-hint-item').first());
}
};
this.moveUp = function () {
var $current = this.$content.find('.note-hint-item.active');
var $prev = $current.prev();
if ($prev.length) {
this.selectItem($prev);
} else {
var $prevGroup = $current.parent().prev();
if (!$prevGroup.length) {
$prevGroup = this.$content.find('.note-hint-group').last();
}
this.selectItem($prevGroup.find('.note-hint-item').last());
}
};
this.replace = function () {
var $item = this.$content.find('.note-hint-item.active');
if ($item.length) {
var node = this.nodeFromItem($item);
this.lastWordRange.insertNode(node);
range.createFromNode(node).collapse().select();
this.lastWordRange = null;
this.hide();
context.invoke('editor.focus');
}
};
this.nodeFromItem = function ($item) {
var hint = hints[$item.data('index')];
var item = $item.data('item');
var node = hint.content ? hint.content(item) : item;
if (typeof node === 'string') {
node = dom.createText(node);
}
return node;
};
this.createItemTemplates = function (hintIdx, items) {
var hint = hints[hintIdx];
return items.map(function (item, idx) {
var $item = $('<div class="note-hint-item"/>');
$item.append(hint.template ? hint.template(item) : item + '');
$item.data({
'index': hintIdx,
'item': item
});
if (hintIdx === 0 && idx === 0) {
$item.addClass('active');
}
return $item;
});
};
this.handleKeydown = function (e) {
if (!this.$popover.is(':visible')) {
return;
}
if (e.keyCode === key.code.ENTER) {
e.preventDefault();
this.replace();
} else if (e.keyCode === key.code.UP) {
e.preventDefault();
this.moveUp();
} else if (e.keyCode === key.code.DOWN) {
e.preventDefault();
this.moveDown();
}
};
this.searchKeyword = function (index, keyword, callback) {
var hint = hints[index];
if (hint && hint.match.test(keyword) && hint.search) {
var matches = hint.match.exec(keyword);
hint.search(matches[1], callback);
} else {
callback();
}
};
this.createGroup = function (idx, keyword) {
var $group = $('<div class="note-hint-group note-hint-group-' + idx + '"/>');
this.searchKeyword(idx, keyword, function (items) {
items = items || [];
if (items.length) {
$group.html(self.createItemTemplates(idx, items));
self.show();
}
});
return $group;
};
this.handleKeyup = function (e) {
if (list.contains([key.code.ENTER, key.code.UP, key.code.DOWN], e.keyCode)) {
if (e.keyCode === key.code.ENTER) {
if (this.$popover.is(':visible')) {
return;
}
}
} else {
var wordRange = context.invoke('editor.createRange').getWordRange();
var keyword = wordRange.toString();
if (hints.length && keyword) {
this.$content.empty();
var bnd = func.rect2bnd(list.last(wordRange.getClientRects()));
if (bnd) {
this.$popover.hide();
this.lastWordRange = wordRange;
hints.forEach(function (hint, idx) {
if (hint.match.test(keyword)) {
self.createGroup(idx, keyword).appendTo(self.$content);
}
});
// set position for popover after group is created
if (direction === 'top') {
this.$popover.css({
left: bnd.left,
top: bnd.top - this.$popover.outerHeight() - POPOVER_DIST
});
} else {
this.$popover.css({
left: bnd.left,
top: bnd.top + bnd.height + POPOVER_DIST
});
}
}
} else {
this.hide();
}
}
};
this.show = function () {
this.$popover.show();
};
this.hide = function () {
this.$popover.hide();
};
};
return HintPopover;
});