view refactoring

This commit is contained in:
antelle 2019-09-15 18:33:45 +02:00
parent 9bbea458c6
commit 7589a112d0
14 changed files with 369 additions and 183 deletions

View File

@ -1,5 +1,5 @@
{
"presets": ["@babel/preset-env"],
"presets": [],
"plugins": [
["@babel/plugin-proposal-class-properties", { "loose": true }],
"@babel/plugin-external-helpers"

View File

@ -11,6 +11,9 @@ const Tip = function(el, config) {
this.hideTimeout = null;
this.force = (config && config.force) || false;
this.hide = this.hide.bind(this);
this.destroy = this.destroy.bind(this);
this.mouseenter = this.mouseenter.bind(this);
this.mouseleave = this.mouseleave.bind(this);
};
Tip.enabled = !Features.isMobile;
@ -21,8 +24,8 @@ Tip.prototype.init = function() {
}
this.el.removeAttr('title');
this.el.attr('data-title', this.title);
this.el.mouseenter(this.mouseenter.bind(this)).mouseleave(this.mouseleave.bind(this));
this.el.click(this.mouseleave.bind(this));
this.el.mouseenter(this.mouseenter).mouseleave(this.mouseleave);
this.el.click(this.mouseleave);
};
Tip.prototype.show = function() {
@ -80,8 +83,15 @@ Tip.prototype.hide = function() {
if (this.tipEl) {
this.tipEl.remove();
this.tipEl = null;
Backbone.off('page-geometry', this.hide);
}
Backbone.off('page-geometry', this.hide);
};
Tip.prototype.destroy = function() {
this.hide();
this.el.off('mouseenter', this.mouseenter);
this.el.off('mouseleave', this.mouseleave);
this.el.off('click', this.mouseleave);
};
Tip.prototype.mouseenter = function() {
@ -138,7 +148,7 @@ Tip.createTips = function(container) {
if (!Tip.enabled) {
return;
}
container.find('[title]').each((ix, el) => {
$('[title]', container).each((ix, el) => {
Tip.createTip(el);
});
};
@ -156,10 +166,10 @@ Tip.createTip = function(el, options) {
};
Tip.hideTips = function(container) {
if (!Tip.enabled) {
if (!Tip.enabled || !container) {
return;
}
container.find('[data-title]').each((ix, el) => {
$('[data-title]', container).each((ix, el) => {
Tip.hideTip(el);
});
};
@ -180,4 +190,13 @@ Tip.updateTip = function(el, props) {
}
};
Tip.destroyTips = function(container) {
$('[data-title]', container).each((ix, el) => {
if (el._tip) {
el._tip.destroy();
el._tip = undefined;
}
});
};
export { Tip };

View File

@ -13,6 +13,7 @@ const Scrollable = {
this.removeScroll();
}
this.scroll = baron(opts);
this.once('remove', () => this.removeScroll);
}
this.scroller = this.$el.find('.scroller');
this.scrollerBar = this.$el.find('.scroller__bar');
@ -21,7 +22,9 @@ const Scrollable = {
removeScroll() {
if (this.scroll) {
this.scroll.dispose();
try {
this.scroll.dispose();
} catch {}
this.scroll = null;
}
},

View File

@ -0,0 +1,177 @@
import morphdom from 'morphdom';
import EventEmitter from 'events';
import { Tip } from 'util/ui/tip';
import { KeyHandler } from 'comp/browser/key-handler';
import { Logger } from 'util/logger';
class View extends EventEmitter {
parent = undefined;
template = undefined;
events = {};
views = {};
hidden = false;
removed = false;
boundEvents = [];
debugLogger = localStorage.debugViews ? new Logger('view', this.constructor.name) : undefined;
constructor(model) {
super();
this.model = model;
}
render(templateData) {
if (this.removed) {
return;
}
this.debugLogger && this.debugLogger.debug('Render start');
if (this.el) {
Tip.destroyTips(this.el);
}
this.unbindEvents();
this.renderElement(templateData);
this.bindEvents();
Tip.createTips(this.el);
this.debugLogger && this.debugLogger.debug('Render finished');
return this;
}
renderElement(templateData) {
const html = this.template(templateData);
if (this.el) {
morphdom(this.el, html);
} else {
const el = document.createElement('div');
el.innerHTML = html;
this.el = el.firstChild;
if (this.parent) {
const parent = document.querySelector(this.parent);
if (!parent) {
throw new Error(
`Error rendering ${this.constructor.name}: parent not found: ${parent}`
);
}
parent.appendChild(this.el);
} else {
throw new Error(
`Error rendering ${this.constructor.name}: I don't know how to insert the view`
);
}
this.$el = $(this.el); // legacy
}
}
bindEvents() {
for (const [eventDef, method] of Object.entries(this.events)) {
const spaceIx = eventDef.indexOf(' ');
let event, targets;
if (spaceIx > 0) {
event = eventDef.substr(0, spaceIx);
const selector = eventDef.substr(spaceIx + 1);
targets = this.el.querySelectorAll(selector);
} else {
event = eventDef;
targets = [this.el];
}
for (const target of targets) {
const listener = e => {
this.debugLogger && this.debugLogger.debug('Listener', method);
this[method](e);
};
target.addEventListener(event, listener);
this.boundEvents.push({ target, event, listener });
}
}
}
unbindEvents() {
for (const boundEvent of this.boundEvents) {
const { target, event, listener } = boundEvent;
target.removeEventListener(event, listener);
}
}
remove() {
this.emit('remove');
this.removeInnerViews();
Tip.hideTips(this.el);
this.el.remove();
this.removed = true;
this.debugLogger && this.debugLogger.debug('Remove');
}
removeInnerViews() {
if (this.views) {
for (const view of Object.values(this.views)) {
if (view) {
if (view instanceof Array) {
view.forEach(v => v.remove());
} else {
view.remove();
}
}
}
this.views = {};
}
}
listenTo(model, event, callback) {
const boundCallback = callback.bind(this);
model.on(event, boundCallback);
this.once('remove', () => model.off(event, boundCallback));
}
stopListening(model, event, callback) {
model.off(event, callback);
}
hide() {
Tip.hideTips(this.el);
return this.toggle(false);
}
show() {
return this.toggle(true);
}
toggle(visible) {
if (visible === undefined) {
visible = this.hidden;
}
this.el.classList.toggle('show', !!visible);
this.el.classList.toggle('hide', !visible);
this.hidden = !visible;
this.emit(visible ? 'show' : 'hide');
if (!visible) {
Tip.hideTips(this.el);
}
return this;
}
isHidden() {
return this.hidden;
}
isVisible() {
return !this.hidden;
}
afterPaint(callback) {
requestAnimationFrame(() => requestAnimationFrame(callback));
}
onKey(key, handler, shortcut, modal, noPrevent) {
KeyHandler.onKey(key, handler, this, shortcut, modal, noPrevent);
this.once('remove', () => KeyHandler.offKey(key, handler, this));
}
}
export { View };

View File

@ -45,7 +45,7 @@ const AppView = Backbone.View.extend({
this.views = {};
this.views.menu = new MenuView({ model: this.model.menu });
this.views.menuDrag = new DragView('x');
this.views.footer = new FooterView({ model: this.model });
this.views.footer = new FooterView(this.model);
this.views.listWrap = new ListWrapView({ model: this.model });
this.views.list = new ListView({ model: this.model });
this.views.listDrag = new DragView('x');
@ -146,7 +146,7 @@ const AppView = Backbone.View.extend({
this.views.listWrap.setElement(this.$el.find('.app__list-wrap')).render();
this.views.menu.setElement(this.$el.find('.app__menu')).render();
this.views.menuDrag.setElement(this.$el.find('.app__menu-drag')).render();
this.views.footer.setElement(this.$el.find('.app__footer')).render();
this.views.footer.render();
this.views.list.setElement(this.$el.find('.app__list')).render();
this.views.listDrag.setElement(this.$el.find('.app__list-drag')).render();
this.views.details.setElement(this.$el.find('.app__details')).render();
@ -240,7 +240,8 @@ const AppView = Backbone.View.extend({
this.views.listDrag.hide();
this.views.details.hide();
this.hidePanelView();
this.views.panel = view.setElement(this.panelEl).render();
view.render();
this.views.panel = view;
this.panelEl.removeClass('hide');
},
@ -280,11 +281,11 @@ const AppView = Backbone.View.extend({
},
showEditGroup(group) {
this.showPanelView(new GrpView({ model: group }));
this.showPanelView(new GrpView(group));
},
showEditTag() {
this.showPanelView(new TagView({ model: this.model }));
this.showPanelView(new TagView(this.model));
},
showKeyChange(file, viewConfig) {
@ -688,7 +689,7 @@ const AppView = Backbone.View.extend({
if (this.views.settings) {
this.showEntries();
}
this.showPanelView(new GeneratorPresetsView({ model: this.model }));
this.showPanelView(new GeneratorPresetsView(this.model));
} else {
this.showEntries();
}

View File

@ -93,10 +93,8 @@ const FieldViewText = FieldView.extend({
const fieldRect = this.input[0].getBoundingClientRect();
const shadowSpread = parseInt(this.input.css('--focus-shadow-spread'));
this.gen = new GeneratorView({
model: {
pos: { left: fieldRect.left, top: fieldRect.bottom + shadowSpread },
password: this.value
}
pos: { left: fieldRect.left, top: fieldRect.bottom + shadowSpread },
password: this.value
}).render();
this.gen.once('remove', this.generatorClosed.bind(this));
this.gen.once('result', this.generatorResult.bind(this));

View File

@ -1,68 +1,61 @@
import Backbone from 'backbone';
import { View } from 'view-engine/view';
import { KeyHandler } from 'comp/browser/key-handler';
import { Keys } from 'const/keys';
import { UpdateModel } from 'models/update-model';
import { GeneratorView } from 'views/generator-view';
import template from 'templates/footer.hbs';
const FooterView = Backbone.View.extend({
template: require('templates/footer.hbs'),
class FooterView extends View {
parent = '.app__footer';
events: {
template = template;
events = {
'click .footer__db-item': 'showFile',
'click .footer__db-open': 'openFile',
'click .footer__btn-help': 'toggleHelp',
'click .footer__btn-settings': 'toggleSettings',
'click .footer__btn-generate': 'genPass',
'click .footer__btn-lock': 'lockWorkspace'
},
};
initialize() {
this.views = {};
constructor(model) {
super(model);
KeyHandler.onKey(
Keys.DOM_VK_L,
this.lockWorkspace,
this,
KeyHandler.SHORTCUT_ACTION,
false,
true
);
KeyHandler.onKey(Keys.DOM_VK_G, this.genPass, this, KeyHandler.SHORTCUT_ACTION);
KeyHandler.onKey(Keys.DOM_VK_O, this.openFile, this, KeyHandler.SHORTCUT_ACTION);
KeyHandler.onKey(Keys.DOM_VK_S, this.saveAll, this, KeyHandler.SHORTCUT_ACTION);
KeyHandler.onKey(Keys.DOM_VK_COMMA, this.toggleSettings, this, KeyHandler.SHORTCUT_ACTION);
this.onKey(Keys.DOM_VK_L, this.lockWorkspace, KeyHandler.SHORTCUT_ACTION, false, true);
this.onKey(Keys.DOM_VK_G, this.genPass, KeyHandler.SHORTCUT_ACTION);
this.onKey(Keys.DOM_VK_O, this.openFile, KeyHandler.SHORTCUT_ACTION);
this.onKey(Keys.DOM_VK_S, this.saveAll, KeyHandler.SHORTCUT_ACTION);
this.onKey(Keys.DOM_VK_COMMA, this.toggleSettings, KeyHandler.SHORTCUT_ACTION);
this.listenTo(this, 'hide', this.viewHidden);
this.listenTo(this.model.files, 'update reset change', this.render);
this.listenTo(Backbone, 'set-locale', this.render);
this.listenTo(UpdateModel.instance, 'change:updateStatus', this.render);
},
}
render() {
this.renderTemplate(
{
files: this.model.files,
updateAvailable:
['ready', 'found'].indexOf(UpdateModel.instance.get('updateStatus')) >= 0
},
{ plain: true }
);
return this;
},
super.render({
files: this.model.files,
updateAvailable:
['ready', 'found'].indexOf(UpdateModel.instance.get('updateStatus')) >= 0
});
}
viewHidden() {
if (this.views.gen) {
this.views.gen.remove();
delete this.views.gen;
}
},
}
lockWorkspace(e) {
if (this.model.files.hasOpenFiles()) {
e.preventDefault();
Backbone.trigger('lock-workspace');
}
},
}
genPass(e) {
e.stopPropagation();
@ -75,14 +68,12 @@ const FooterView = Backbone.View.extend({
const bodyRect = document.body.getBoundingClientRect();
const right = bodyRect.right - rect.right;
const bottom = bodyRect.bottom - rect.top;
const generator = new GeneratorView({
model: { copy: true, pos: { right, bottom } }
}).render();
const generator = new GeneratorView({ copy: true, pos: { right, bottom } }).render();
generator.once('remove', () => {
delete this.views.gen;
});
this.views.gen = generator;
},
}
showFile(e) {
const fileId = $(e.target)
@ -91,23 +82,23 @@ const FooterView = Backbone.View.extend({
if (fileId) {
Backbone.trigger('show-file', { fileId });
}
},
}
openFile() {
Backbone.trigger('open-file');
},
}
saveAll() {
Backbone.trigger('save-all');
},
}
toggleHelp() {
Backbone.trigger('toggle-settings', 'help');
},
}
toggleSettings() {
Backbone.trigger('toggle-settings', 'general');
}
});
}
export { FooterView };

View File

@ -1,13 +1,17 @@
import Backbone from 'backbone';
import { View } from 'view-engine/view';
import { GeneratorPresets } from 'comp/app/generator-presets';
import { PasswordGenerator } from 'util/generators/password-generator';
import { Locale } from 'util/locale';
import { Scrollable } from 'view-engine/scrollable';
import template from 'templates/generator-presets.hbs';
const GeneratorPresetsView = Backbone.View.extend({
template: require('templates/generator-presets.hbs'),
class GeneratorPresetsView extends View {
parent = '.app__panel';
events: {
template = template;
events = {
'click .back-button': 'returnToApp',
'change .gen-ps__list': 'changePreset',
'click .gen-ps__btn-create': 'createPreset',
@ -18,44 +22,36 @@ const GeneratorPresetsView = Backbone.View.extend({
'input #gen-ps__field-length': 'changeLength',
'change .gen-ps__check-range': 'changeRange',
'input #gen-ps__field-include': 'changeInclude'
},
};
selected: null,
selected = null;
reservedTitles: [Locale.genPresetDerived],
initialize() {
this.appModel = this.model;
},
reservedTitles = [Locale.genPresetDerived];
render() {
this.presets = GeneratorPresets.all;
if (!this.selected || !this.presets.some(p => p.name === this.selected)) {
this.selected = (this.presets.filter(p => p.default)[0] || this.presets[0]).name;
}
this.renderTemplate(
{
presets: this.presets,
selected: this.getPreset(this.selected),
ranges: this.getSelectedRanges()
},
true
);
super.render({
presets: this.presets,
selected: this.getPreset(this.selected),
ranges: this.getSelectedRanges()
});
this.createScroll({
root: this.$el.find('.gen-ps')[0],
scroller: this.$el.find('.scroller')[0],
bar: this.$el.find('.scroller__bar')[0]
});
this.renderExample();
return this;
},
}
renderExample() {
const selectedPreset = this.getPreset(this.selected);
const example = PasswordGenerator.generate(selectedPreset);
this.$el.find('.gen-ps__example').text(example);
this.pageResized();
},
}
getSelectedRanges() {
const sel = this.getPreset(this.selected);
@ -73,20 +69,20 @@ const GeneratorPresetsView = Backbone.View.extend({
};
}
);
},
}
getPreset(name) {
return this.presets.filter(p => p.name === name)[0];
},
}
returnToApp() {
Backbone.trigger('edit-generator-presets');
},
}
changePreset(e) {
this.selected = e.target.value;
this.render();
},
}
createPreset() {
let name;
@ -116,12 +112,12 @@ const GeneratorPresetsView = Backbone.View.extend({
GeneratorPresets.add(preset);
this.selected = name;
this.render();
},
}
deletePreset() {
GeneratorPresets.remove(this.selected);
this.render();
},
}
changeTitle(e) {
const title = $.trim(e.target.value);
@ -139,17 +135,17 @@ const GeneratorPresetsView = Backbone.View.extend({
GeneratorPresets.setPreset(this.selected, { title });
this.$el.find('.gen-ps__list option[selected]').text(title);
}
},
}
changeEnabled(e) {
const enabled = e.target.checked;
GeneratorPresets.setDisabled(this.selected, !enabled);
},
}
changeDefault(e) {
const isDefault = e.target.checked;
GeneratorPresets.setDefault(isDefault ? this.selected : null);
},
}
changeLength(e) {
const length = +e.target.value;
@ -161,7 +157,7 @@ const GeneratorPresetsView = Backbone.View.extend({
}
this.presets = GeneratorPresets.all;
this.renderExample();
},
}
changeRange(e) {
const enabled = e.target.checked;
@ -169,7 +165,7 @@ const GeneratorPresetsView = Backbone.View.extend({
GeneratorPresets.setPreset(this.selected, { [range]: enabled });
this.presets = GeneratorPresets.all;
this.renderExample();
},
}
changeInclude(e) {
const include = e.target.value;
@ -179,8 +175,8 @@ const GeneratorPresetsView = Backbone.View.extend({
this.presets = GeneratorPresets.all;
this.renderExample();
}
});
}
_.extend(GeneratorPresetsView.prototype, Scrollable);
Object.assign(GeneratorPresetsView.prototype, Scrollable);
export { GeneratorPresetsView };

View File

@ -1,20 +1,21 @@
import Backbone from 'backbone';
import { View } from 'view-engine/view';
import { GeneratorPresets } from 'comp/app/generator-presets';
import { CopyPaste } from 'comp/browser/copy-paste';
import { AppSettingsModel } from 'models/app-settings-model';
import { PasswordGenerator } from 'util/generators/password-generator';
import { Locale } from 'util/locale';
import { Tip } from 'util/ui/tip';
import template from 'templates/generator.hbs';
const GeneratorView = Backbone.View.extend({
el: 'body',
class GeneratorView extends View {
parent = 'body';
template: require('templates/generator.hbs'),
template = template;
events: {
events = {
'click': 'click',
'mousedown .gen__length-range': 'generate',
'mousemove .gen__length-range': 'lengthMouseMove',
'input .gen__length-range': 'lengthChange',
'change .gen__length-range': 'lengthChange',
'change .gen__check input[type=checkbox]': 'checkChange',
@ -22,9 +23,9 @@ const GeneratorView = Backbone.View.extend({
'click .gen__btn-ok': 'btnOkClick',
'change .gen__sel-tpl': 'presetChange',
'click .gen__btn-refresh': 'newPass'
},
};
valuesMap: [
valuesMap = [
3,
4,
5,
@ -51,19 +52,20 @@ const GeneratorView = Backbone.View.extend({
32,
48,
64
],
];
presets: null,
preset: null,
presets = null;
preset = null;
initialize() {
constructor(model) {
super(model);
this.createPresets();
const preset = this.preset;
this.gen = _.clone(_.find(this.presets, pr => pr.name === preset));
this.hide = AppSettingsModel.instance.get('generatorHidePassword');
$('body').one('click', this.remove.bind(this));
this.listenTo(Backbone, 'lock-workspace', this.remove.bind(this));
},
}
render() {
const canCopy = document.queryCommandSupported('copy');
@ -72,7 +74,7 @@ const GeneratorView = Backbone.View.extend({
? Locale.alertCopy
: Locale.alertClose
: Locale.alertOk;
this.renderTemplate({
super.render({
btnTitle,
showToggleButton: this.model.copy,
opt: this.gen,
@ -84,7 +86,7 @@ const GeneratorView = Backbone.View.extend({
this.$el.css(this.model.pos);
this.generate();
return this;
},
}
createPresets() {
this.presets = GeneratorPresets.enabled;
@ -100,10 +102,10 @@ const GeneratorView = Backbone.View.extend({
const defaultPreset = this.presets.filter(p => p.default)[0] || this.presets[0];
this.preset = defaultPreset.name;
}
this.presets.forEach(function(pr) {
this.presets.forEach(pr => {
pr.pseudoLength = this.lengthToPseudoValue(pr.length);
}, this);
},
});
}
lengthToPseudoValue(length) {
for (let ix = 0; ix < this.valuesMap.length; ix++) {
@ -112,7 +114,7 @@ const GeneratorView = Backbone.View.extend({
}
}
return this.valuesMap.length - 1;
},
}
showPassword() {
if (this.hide && !this.model.copy) {
@ -120,11 +122,11 @@ const GeneratorView = Backbone.View.extend({
} else {
this.resultEl.text(this.password);
}
},
}
click(e) {
e.stopPropagation();
},
}
lengthChange(e) {
const val = this.valuesMap[e.target.value];
@ -134,7 +136,7 @@ const GeneratorView = Backbone.View.extend({
this.optionChanged('length');
this.generate();
}
},
}
checkChange(e) {
const id = $(e.target).data('id');
@ -143,7 +145,7 @@ const GeneratorView = Backbone.View.extend({
}
this.optionChanged(id);
this.generate();
},
}
optionChanged(option) {
if (
@ -154,32 +156,31 @@ const GeneratorView = Backbone.View.extend({
}
this.preset = this.gen.name = 'Custom';
this.$el.find('.gen__sel-tpl').val('');
},
}
generate() {
this.password = PasswordGenerator.generate(this.gen);
this.showPassword();
const isLong = this.password.length > 32;
this.resultEl.toggleClass('gen__result--long-pass', isLong);
},
}
hideChange(e) {
this.hide = e.target.checked;
// AppSettingsModel.instance.unset('generatorHidePassword', { silent: true });
AppSettingsModel.instance.set('generatorHidePassword', this.hide);
const label = this.$el.find('.gen__check-hide-label');
Tip.updateTip(label[0], { title: this.hide ? Locale.genShowPass : Locale.genHidePass });
this.showPassword();
},
}
btnOkClick() {
if (!CopyPaste.simpleCopy) {
CopyPaste.createHiddenInput(this.password);
}
CopyPaste.copy(this.password);
this.trigger('result', this.password);
this.emit('result', this.password);
this.remove();
},
}
presetChange(e) {
const name = e.target.value;
@ -192,11 +193,11 @@ const GeneratorView = Backbone.View.extend({
const preset = _.find(this.presets, t => t.name === name);
this.gen = _.clone(preset);
this.render();
},
}
newPass() {
this.generate();
}
});
}
export { GeneratorView };

View File

@ -1,13 +1,17 @@
import { View } from 'view-engine/view';
import { AutoType } from 'auto-type';
import Backbone from 'backbone';
import { Scrollable } from 'view-engine/scrollable';
import { AutoTypeHintView } from 'views/auto-type-hint-view';
import { IconSelectView } from 'views/icon-select-view';
import template from 'templates/grp.hbs';
const GrpView = Backbone.View.extend({
template: require('templates/grp.hbs'),
class GrpView extends View {
parent = '.app__panel';
events: {
template = template;
events = {
'click .grp__icon': 'showIconsSelect',
'click .grp__buttons-trash': 'moveToTrash',
'click .back-button': 'returnToApp',
@ -16,28 +20,21 @@ const GrpView = Backbone.View.extend({
'input #grp__field-auto-type-seq': 'changeAutoTypeSeq',
'change #grp__check-search': 'setEnableSearching',
'change #grp__check-auto-type': 'setEnableAutoType'
},
initialize() {
this.views = {};
},
};
render() {
this.removeSubView();
this.renderTemplate(
{
title: this.model.get('title'),
icon: this.model.get('icon') || 'folder',
customIcon: this.model.get('customIcon'),
enableSearching: this.model.getEffectiveEnableSearching(),
readonly: this.model.get('top'),
canAutoType: AutoType.enabled,
autoTypeSeq: this.model.get('autoTypeSeq'),
autoTypeEnabled: this.model.getEffectiveEnableAutoType(),
defaultAutoTypeSeq: this.model.getParentEffectiveAutoTypeSeq()
},
true
);
super.render({
title: this.model.get('title'),
icon: this.model.get('icon') || 'folder',
customIcon: this.model.get('customIcon'),
enableSearching: this.model.getEffectiveEnableSearching(),
readonly: this.model.get('top'),
canAutoType: AutoType.enabled,
autoTypeSeq: this.model.get('autoTypeSeq'),
autoTypeEnabled: this.model.getEffectiveEnableAutoType(),
defaultAutoTypeSeq: this.model.getParentEffectiveAutoTypeSeq()
});
if (!this.model.get('title')) {
this.$el.find('#grp__field-title').focus();
}
@ -47,15 +44,14 @@ const GrpView = Backbone.View.extend({
bar: this.$el.find('.scroller__bar')[0]
});
this.pageResized();
return this;
},
}
removeSubView() {
if (this.views.sub) {
this.views.sub.remove();
delete this.views.sub;
}
},
}
changeTitle(e) {
const title = $.trim(e.target.value);
@ -69,7 +65,7 @@ const GrpView = Backbone.View.extend({
Backbone.trigger('edit-group');
}
}
},
}
changeAutoTypeSeq(e) {
const el = e.target;
@ -80,7 +76,7 @@ const GrpView = Backbone.View.extend({
this.model.setAutoTypeSeq(seq);
}
});
},
}
focusAutoTypeSeq(e) {
if (!this.views.hint) {
@ -89,7 +85,7 @@ const GrpView = Backbone.View.extend({
delete this.views.hint;
});
}
},
}
showIconsSelect() {
if (this.views.sub) {
@ -107,7 +103,7 @@ const GrpView = Backbone.View.extend({
this.views.sub = subView;
}
this.pageResized();
},
}
iconSelected(sel) {
if (sel.custom) {
@ -118,28 +114,28 @@ const GrpView = Backbone.View.extend({
this.model.setIcon(+sel.id);
}
this.render();
},
}
moveToTrash() {
this.model.moveToTrash();
Backbone.trigger('select-all');
},
}
setEnableSearching(e) {
const enabled = e.target.checked;
this.model.setEnableSearching(enabled);
},
}
setEnableAutoType(e) {
const enabled = e.target.checked;
this.model.setEnableAutoType(enabled);
},
}
returnToApp() {
Backbone.trigger('edit-group');
}
});
}
_.extend(GrpView.prototype, Scrollable);
Object.assign(GrpView.prototype, Scrollable);
export { GrpView };

View File

@ -1,40 +1,36 @@
import Backbone from 'backbone';
import { View } from 'view-engine/view';
import { Alerts } from 'comp/ui/alerts';
import { Locale } from 'util/locale';
import template from 'templates/tag.hbs';
const TagView = Backbone.View.extend({
template: require('templates/tag.hbs'),
class TagView extends View {
parent = '.app__panel';
events: {
template = template;
events = {
'click .tag__buttons-trash': 'moveToTrash',
'click .back-button': 'returnToApp',
'click .tag__btn-rename': 'renameTag'
},
initialize() {
this.appModel = this.model;
},
};
render() {
if (this.model) {
this.renderTemplate(
{
title: this.model.get('title')
},
true
);
if (this.tag) {
super.render({
title: this.tag.get('title')
});
}
return this;
},
}
showTag(tag) {
this.model = tag;
this.tag = tag;
this.render();
},
}
renameTag() {
const title = $.trim(this.$el.find('#tag__field-title').val());
if (!title || title === this.model.get('title')) {
if (!title || title === this.tag.get('title')) {
return;
}
if (/[;,:]/.test(title)) {
@ -44,13 +40,13 @@ const TagView = Backbone.View.extend({
});
return;
}
if (this.appModel.tags.some(t => t.toLowerCase() === title.toLowerCase())) {
if (this.model.tags.some(t => t.toLowerCase() === title.toLowerCase())) {
Alerts.error({ header: Locale.tagExists, body: Locale.tagExistsBody });
return;
}
this.appModel.renameTag(this.model.get('title'), title);
this.model.renameTag(this.tag.get('title'), title);
Backbone.trigger('select-all');
},
}
moveToTrash() {
this.title = null;
@ -58,15 +54,15 @@ const TagView = Backbone.View.extend({
header: Locale.tagTrashQuestion,
body: Locale.tagTrashQuestionBody,
success: () => {
this.appModel.renameTag(this.model.get('title'), undefined);
this.model.renameTag(this.tag.get('title'), undefined);
Backbone.trigger('select-all');
}
});
},
}
returnToApp() {
Backbone.trigger('edit-tag');
}
});
}
export { TagView };

5
package-lock.json generated
View File

@ -8189,6 +8189,11 @@
}
}
},
"morphdom": {
"version": "2.5.6",
"resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.5.6.tgz",
"integrity": "sha512-uw+fgVRCV7DK9EWJ87NeiFXTDdLklajJQNLHCAJStqTY/uwFpK5ormeU2PYSX5DDk+cI9dtFli/MHKd2wP/KGg=="
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",

View File

@ -60,6 +60,7 @@
"load-grunt-tasks": "5.1.0",
"marked": "^0.7.0",
"mini-css-extract-plugin": "^0.8.0",
"morphdom": "^2.5.6",
"node-sass": "^4.12.0",
"node-stream-zip": "1.8.2",
"normalize.css": "8.0.1",

View File

@ -25,6 +25,7 @@ function config(grunt, mode = 'production') {
'jquery',
'underscore',
'backbone',
'morphdom',
'kdbxweb',
'baron',
'pikaday',
@ -61,6 +62,7 @@ function config(grunt, mode = 'production') {
underscore: `underscore/underscore${devMode ? '-min' : ''}.js`,
_: `underscore/underscore${devMode ? '-min' : ''}.js`,
jquery: `jquery/dist/jquery${devMode ? '.min' : ''}.js`,
morphdom: `morphdom/dist/morphdom-umd${devMode ? '.min' : ''}.js`,
kdbxweb: 'kdbxweb/dist/kdbxweb.js',
baron: `baron/baron${devMode ? '.min' : ''}.js`,
qrcode: `jsqrcode/dist/qrcode${devMode ? '.min' : ''}.js`,