alpaca
Version:
Alpaca provides the easiest and fastest way to generate interactive forms for the web and mobile devices. It runs simply as HTML5 or more elaborately using Bootstrap, jQuery Mobile or jQuery UI. Alpaca uses Handlebars to process JSON schema and provide
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;
});