mirror of https://github.com/keeweb/keeweb
view refactoring
parent
9bbea458c6
commit
7589a112d0
2
.babelrc
2
.babelrc
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"presets": [],
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-class-properties", { "loose": true }],
|
||||
"@babel/plugin-external-helpers"
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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 };
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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`,
|
||||
|
|
Loading…
Reference in New Issue