keeweb/app/scripts/views/auto-type/auto-type-select-view.js

312 lines
9.3 KiB
JavaScript
Raw Normal View History

2020-03-14 20:29:55 +01:00
import { View, DefaultTemplateOptions } from 'framework/views/view';
2019-09-16 22:57:56 +02:00
import { Events } from 'framework/events';
2019-09-15 14:16:32 +02:00
import { Shortcuts } from 'comp/app/shortcuts';
import { KeyHandler } from 'comp/browser/key-handler';
import { Keys } from 'const/keys';
import { AppSettingsModel } from 'models/app-settings-model';
import { EntryPresenter } from 'presenters/entry-presenter';
import { StringFormat } from 'util/formatting/string-format';
import { Locale } from 'util/locale';
2019-09-16 20:42:33 +02:00
import { Scrollable } from 'framework/views/scrollable';
2019-09-15 14:16:32 +02:00
import { DropdownView } from 'views/dropdown-view';
2019-09-15 23:02:51 +02:00
import template from 'templates/auto-type/auto-type-select.hbs';
import itemTemplate from 'templates/auto-type/auto-type-select-item.hbs';
2019-09-15 14:16:32 +02:00
2019-09-15 23:02:51 +02:00
class AutoTypeSelectView extends View {
parent = 'body';
2019-10-12 08:20:44 +02:00
modal = 'auto-type';
2016-07-24 22:57:12 +02:00
2019-09-15 23:02:51 +02:00
template = template;
2016-07-24 22:57:12 +02:00
2019-09-15 23:02:51 +02:00
itemTemplate = itemTemplate;
events = {
2016-07-25 20:27:22 +02:00
'click .at-select__header-filter-clear': 'clearFilterText',
2019-09-12 19:59:35 +02:00
'click .at-select__item': 'itemClicked',
'contextmenu .at-select__item': 'itemRightClicked'
2019-09-15 23:02:51 +02:00
};
2016-07-24 22:57:12 +02:00
2019-09-15 23:02:51 +02:00
result = null;
entries = null;
2016-07-24 22:57:12 +02:00
2019-09-15 23:02:51 +02:00
constructor(model) {
super(model);
2016-07-25 20:27:22 +02:00
this.initScroll();
2019-09-16 22:57:56 +02:00
this.listenTo(Events, 'main-window-will-close', this.mainWindowWillClose);
2019-09-17 19:01:12 +02:00
this.listenTo(Events, 'keypress:auto-type', this.keyPressed);
2019-03-31 14:30:14 +02:00
this.setupKeys();
2019-09-15 23:02:51 +02:00
}
2019-03-31 14:30:14 +02:00
setupKeys() {
2019-09-15 23:02:51 +02:00
this.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, false, 'auto-type');
this.onKey(Keys.DOM_VK_RETURN, this.enterPressed, false, 'auto-type');
this.onKey(
2019-08-18 08:05:38 +02:00
Keys.DOM_VK_RETURN,
this.actionEnterPressed,
KeyHandler.SHORTCUT_ACTION,
2019-09-12 19:59:35 +02:00
'auto-type'
2019-08-18 08:05:38 +02:00
);
2019-09-15 23:02:51 +02:00
this.onKey(Keys.DOM_VK_RETURN, this.optEnterPressed, KeyHandler.SHORTCUT_OPT, 'auto-type');
this.onKey(
2019-09-12 19:59:35 +02:00
Keys.DOM_VK_RETURN,
this.shiftEnterPressed,
KeyHandler.SHORTCUT_SHIFT,
'auto-type'
);
2019-09-15 23:02:51 +02:00
this.onKey(Keys.DOM_VK_UP, this.upPressed, false, 'auto-type');
this.onKey(Keys.DOM_VK_DOWN, this.downPressed, false, 'auto-type');
this.onKey(Keys.DOM_VK_BACK_SPACE, this.backSpacePressed, false, 'auto-type');
2019-09-15 23:18:09 +02:00
this.onKey(Keys.DOM_VK_O, this.openKeyPressed, KeyHandler.SHORTCUT_ACTION, 'auto-type');
2019-09-15 23:02:51 +02:00
}
2019-03-31 14:30:14 +02:00
2016-07-24 22:57:12 +02:00
render() {
2017-05-07 19:45:53 +02:00
let topMessage;
2016-07-25 20:27:22 +02:00
if (this.model.filter.title || this.model.filter.url) {
2019-08-16 23:05:39 +02:00
topMessage = Locale.autoTypeMsgMatchedByWindow.replace(
'{}',
this.model.filter.title || this.model.filter.url
);
2016-07-25 20:27:22 +02:00
} else {
topMessage = Locale.autoTypeMsgNoWindow;
}
2019-09-17 19:50:42 +02:00
const noColor = AppSettingsModel.colorfulIcons ? '' : 'grayscale';
2016-07-26 22:21:20 +02:00
this.entries = this.model.filter.getEntries();
2019-09-18 23:37:57 +02:00
this.result = this.entries[0];
2017-01-31 07:50:28 +01:00
const presenter = new EntryPresenter(null, noColor, this.result && this.result.id);
2016-07-25 20:27:22 +02:00
let itemsHtml = '';
2017-01-31 07:50:28 +01:00
const itemTemplate = this.itemTemplate;
2020-06-01 16:53:51 +02:00
this.entries.forEach((entry) => {
2016-07-25 20:27:22 +02:00
presenter.present(entry);
2020-03-14 20:29:55 +01:00
itemsHtml += itemTemplate(presenter, DefaultTemplateOptions);
2016-07-25 20:27:22 +02:00
});
2019-09-15 23:02:51 +02:00
super.render({
2016-07-25 20:27:22 +02:00
filterText: this.model.filter.text,
2019-08-18 10:17:09 +02:00
topMessage,
itemsHtml,
actionSymbol: Shortcuts.actionShortcutSymbol(true),
altSymbol: Shortcuts.altShortcutSymbol(true),
shiftSymbol: Shortcuts.shiftShortcutSymbol(true),
keyEnter: Locale.keyEnter
2016-07-24 23:53:42 +02:00
});
2016-07-24 22:57:12 +02:00
document.activeElement.blur();
2016-07-25 20:27:22 +02:00
this.createScroll({
root: this.$el.find('.at-select__items')[0],
scroller: this.$el.find('.scroller')[0],
bar: this.$el.find('.scroller__bar')[0]
});
2019-09-15 23:02:51 +02:00
}
2016-07-24 22:57:12 +02:00
2016-07-24 23:10:03 +02:00
cancelAndClose() {
2016-07-24 22:57:12 +02:00
this.result = null;
2019-09-15 23:02:51 +02:00
this.emit('result', this.result);
}
2016-07-24 22:57:12 +02:00
2019-09-12 19:59:35 +02:00
closeWithResult(sequence) {
2019-09-15 23:02:51 +02:00
this.emit('result', {
2018-10-13 11:04:58 +02:00
entry: this.result,
2019-09-12 19:59:35 +02:00
sequence
2018-10-13 11:04:58 +02:00
});
2019-09-15 23:02:51 +02:00
}
2016-07-26 22:21:20 +02:00
2016-07-24 23:10:03 +02:00
escPressed() {
2016-07-25 20:27:22 +02:00
if (this.model.filter.text) {
this.clearFilterText();
} else {
this.cancelAndClose();
}
2019-09-15 23:02:51 +02:00
}
2016-07-24 23:10:03 +02:00
2016-07-24 22:57:12 +02:00
enterPressed() {
2016-07-26 22:21:20 +02:00
this.closeWithResult();
2019-09-15 23:02:51 +02:00
}
2016-07-24 22:57:12 +02:00
actionEnterPressed() {
2019-09-12 19:59:35 +02:00
this.closeWithResult('{PASSWORD}');
2019-09-15 23:02:51 +02:00
}
optEnterPressed() {
2019-09-12 19:59:35 +02:00
this.closeWithResult('{USERNAME}');
2019-09-15 23:02:51 +02:00
}
2019-03-31 14:30:14 +02:00
openKeyPressed() {
2019-09-15 23:02:51 +02:00
this.emit('show-open-files');
}
2019-03-31 14:30:14 +02:00
2019-09-12 19:59:35 +02:00
shiftEnterPressed(e) {
const activeItem = this.$el.find('.at-select__item[data-id="' + this.result.id + '"]');
this.showItemOptions(activeItem, e);
2019-09-15 23:02:51 +02:00
}
2019-09-12 19:59:35 +02:00
2016-07-26 22:21:20 +02:00
upPressed(e) {
e.preventDefault();
2017-01-31 07:50:28 +01:00
const activeIndex = this.entries.indexOf(this.result) - 1;
2016-07-26 22:21:20 +02:00
if (activeIndex >= 0) {
2019-09-19 18:28:55 +02:00
this.result = this.entries[activeIndex];
2016-07-26 22:21:20 +02:00
this.highlightActive();
}
2019-09-15 23:02:51 +02:00
}
2016-07-24 22:57:12 +02:00
2016-07-26 22:21:20 +02:00
downPressed(e) {
e.preventDefault();
2017-01-31 07:50:28 +01:00
const activeIndex = this.entries.indexOf(this.result) + 1;
2016-07-26 22:21:20 +02:00
if (activeIndex < this.entries.length) {
2019-09-19 18:28:55 +02:00
this.result = this.entries[activeIndex];
2016-07-26 22:21:20 +02:00
this.highlightActive();
}
2019-09-15 23:02:51 +02:00
}
2016-07-26 22:21:20 +02:00
highlightActive() {
this.$el.find('.at-select__item').removeClass('at-select__item--active');
2017-01-31 07:50:28 +01:00
const activeItem = this.$el.find('.at-select__item[data-id="' + this.result.id + '"]');
2016-07-26 22:21:20 +02:00
activeItem.addClass('at-select__item--active');
2017-01-31 07:50:28 +01:00
const itemRect = activeItem[0].getBoundingClientRect();
const listRect = this.scroller[0].getBoundingClientRect();
2016-07-26 22:21:20 +02:00
if (itemRect.top < listRect.top) {
this.scroller[0].scrollTop += itemRect.top - listRect.top;
} else if (itemRect.bottom > listRect.bottom) {
this.scroller[0].scrollTop += itemRect.bottom - listRect.bottom;
}
2019-09-15 23:02:51 +02:00
}
2016-07-24 22:57:12 +02:00
keyPressed(e) {
2019-09-12 19:59:35 +02:00
if (e.which && e.which !== Keys.DOM_VK_RETURN) {
2016-07-24 23:53:42 +02:00
this.model.filter.text += String.fromCharCode(e.which);
this.render();
}
2019-09-15 23:02:51 +02:00
}
2016-07-24 23:53:42 +02:00
backSpacePressed() {
if (this.model.filter.text) {
2019-08-18 08:05:38 +02:00
this.model.filter.text = this.model.filter.text.substr(
0,
this.model.filter.text.length - 1
);
2016-07-24 23:53:42 +02:00
this.render();
}
2019-09-15 23:02:51 +02:00
}
2016-07-24 23:53:42 +02:00
clearFilterText() {
this.model.filter.text = '';
this.render();
2019-09-15 23:02:51 +02:00
}
2016-07-24 23:10:03 +02:00
2016-07-26 22:21:20 +02:00
itemClicked(e) {
2017-01-31 07:50:28 +01:00
const itemEl = $(e.target).closest('.at-select__item');
const optionsClicked = $(e.target).closest('.at-select__item-options').length;
2019-09-12 19:59:35 +02:00
if (optionsClicked) {
this.showItemOptions(itemEl, e);
} else {
const id = itemEl.data('id');
this.result = this.entries.get(id);
this.closeWithResult();
}
2019-09-15 23:02:51 +02:00
}
2019-09-12 19:59:35 +02:00
itemRightClicked(e) {
const itemEl = $(e.target).closest('.at-select__item');
this.showItemOptions(itemEl, e);
2019-09-15 23:02:51 +02:00
}
mainWindowWillClose(e) {
e.preventDefault();
2019-09-15 23:02:51 +02:00
}
2019-09-12 19:59:35 +02:00
showItemOptions(itemEl, event) {
if (event) {
event.stopImmediatePropagation();
}
const id = itemEl.data('id');
const entry = this.entries.get(id);
if (this.views.optionsDropdown) {
this.hideItemOptionsDropdown();
if (this.result && this.result.id === entry.id) {
return;
}
}
this.result = entry;
if (!itemEl.hasClass('at-select__item--active')) {
this.highlightActive();
}
const view = new DropdownView();
this.listenTo(view, 'cancel', this.hideItemOptionsDropdown);
this.listenTo(view, 'select', this.itemOptionsDropdownSelect);
const options = [];
if (entry.fields.otp) {
options.push({
value: '{TOTP}',
icon: 'clock-o',
text: Locale.autoTypeSelectionOtp
});
}
if (entry.user) {
options.push({
value: '{USERNAME}',
icon: 'user',
2019-09-15 08:11:11 +02:00
text: StringFormat.capFirst(Locale.user)
2019-09-12 19:59:35 +02:00
});
}
if (entry.password) {
options.push({
value: '{PASSWORD}',
icon: 'key',
2019-09-15 08:11:11 +02:00
text: StringFormat.capFirst(Locale.password)
2019-09-12 19:59:35 +02:00
});
}
for (const field of Object.keys(entry.fields)) {
if (field !== 'otp') {
options.push({
value: `{S:${field}}`,
icon: 'th-list',
text: field
});
}
}
let position;
if (event && event.button === 2) {
position = {
top: event.pageY,
left: event.pageX
};
} else {
const targetElRect = itemEl[0].getBoundingClientRect();
position = {
top: targetElRect.bottom,
right: targetElRect.right
};
}
view.render({
position,
options
});
this.views.optionsDropdown = view;
2019-09-15 23:02:51 +02:00
}
2019-09-12 19:59:35 +02:00
hideItemOptionsDropdown() {
if (this.views.optionsDropdown) {
this.views.optionsDropdown.remove();
delete this.views.optionsDropdown;
}
2019-09-15 23:02:51 +02:00
}
2019-09-12 19:59:35 +02:00
itemOptionsDropdownSelect(e) {
this.hideItemOptionsDropdown();
const sequence = e.item;
this.closeWithResult(sequence);
2016-07-24 22:57:12 +02:00
}
2019-09-15 23:02:51 +02:00
}
2016-07-24 22:57:12 +02:00
2019-09-15 23:02:51 +02:00
Object.assign(AutoTypeSelectView.prototype, Scrollable);
2016-07-25 20:27:22 +02:00
2019-09-15 14:16:32 +02:00
export { AutoTypeSelectView };