From ccace95a6cc12b574b8cc24e5d9d4612b1ae87a0 Mon Sep 17 00:00:00 2001 From: antelle Date: Mon, 16 Sep 2019 19:24:15 +0200 Subject: [PATCH] details view --- app/scripts/view-engine/view.js | 2 +- app/scripts/views/app-view.js | 4 +- app/scripts/views/details/details-view.js | 255 ++++++++++------------ 3 files changed, 119 insertions(+), 142 deletions(-) diff --git a/app/scripts/view-engine/view.js b/app/scripts/view-engine/view.js index e7bb53d8..75c56eb3 100644 --- a/app/scripts/view-engine/view.js +++ b/app/scripts/view-engine/view.js @@ -16,7 +16,7 @@ class View extends EventEmitter { boundEvents = []; debugLogger = localStorage.debugViews ? new Logger('view', this.constructor.name) : undefined; - constructor(model, options = {}) { + constructor(model = undefined, options = {}) { super(); this.model = model; diff --git a/app/scripts/views/app-view.js b/app/scripts/views/app-view.js index ef0a8a4f..e4c85e3d 100644 --- a/app/scripts/views/app-view.js +++ b/app/scripts/views/app-view.js @@ -50,7 +50,7 @@ class AppView extends View { this.views.list = new ListView(this.model, { ownParent: true }); this.views.listDrag = new DragView('x', { parent: '.app__list-drag' }); this.views.list.dragView = this.views.listDrag; - this.views.details = new DetailsView(); + this.views.details = new DetailsView(undefined, { ownParent: true }); this.views.details.appModel = this.model; this.views.menu.listenDrag(this.views.menuDrag); @@ -146,7 +146,7 @@ class AppView extends View { this.views.footer.render(); this.views.list.render(); this.views.listDrag.render(); - this.views.details.setElement(this.$el.find('.app__details')).render(); + this.views.details.render(); this.showLastOpenFile(); return this; } diff --git a/app/scripts/views/details/details-view.js b/app/scripts/views/details/details-view.js index afc641a3..1dc56939 100644 --- a/app/scripts/views/details/details-view.js +++ b/app/scripts/views/details/details-view.js @@ -1,6 +1,7 @@ -import { AutoType } from 'auto-type'; import Backbone from 'backbone'; import kdbxweb from 'kdbxweb'; +import { View } from 'view-engine/view'; +import { AutoType } from 'auto-type'; import { CopyPaste } from 'comp/browser/copy-paste'; import { KeyHandler } from 'comp/browser/key-handler'; import { OtpQrReader } from 'comp/format/otp-qr-reader'; @@ -33,20 +34,19 @@ import { FieldViewTags } from 'views/fields/field-view-tags'; import { FieldViewText } from 'views/fields/field-view-text'; import { FieldViewUrl } from 'views/fields/field-view-url'; import { IconSelectView } from 'views/icon-select-view'; +import template from 'templates/details/details.hbs'; +import emptyTemplate from 'templates/details/details-empty.hbs'; +import groupTemplate from 'templates/details/details-group.hbs'; -const DetailsView = Backbone.View.extend({ - template: require('templates/details/details.hbs'), - emptyTemplate: require('templates/details/details-empty.hbs'), - groupTemplate: require('templates/details/details-group.hbs'), +class DetailsView extends View { + parent = '.app__details'; + fieldViews = null; + passEditView = null; + userEditView = null; + urlEditView = null; + fieldCopyTip = null; - fieldViews: null, - views: null, - passEditView: null, - userEditView: null, - urlEditView: null, - fieldCopyTip: null, - - events: { + events = { 'click .details__colors-popup-item': 'selectColor', 'click .details__header-icon': 'toggleIcons', 'click .details__attachment': 'toggleAttachment', @@ -61,11 +61,11 @@ const DetailsView = Backbone.View.extend({ 'dragleave .details': 'dragleave', 'drop .details': 'drop', 'contextmenu .details': 'contextMenu' - }, + }; - initialize() { + constructor(model, options) { + super(model, options); this.fieldViews = []; - this.views = {}; this.initScroll(); this.listenTo(Backbone, 'entry-selected', this.showEntry); this.listenTo(Backbone, 'copy-password', this.copyPassword); @@ -76,62 +76,42 @@ const DetailsView = Backbone.View.extend({ this.listenTo(Backbone, 'set-locale', this.render); this.listenTo(OtpQrReader, 'qr-read', this.otpCodeRead); this.listenTo(OtpQrReader, 'enter-manually', this.otpEnterManually); - KeyHandler.onKey( + this.onKey( Keys.DOM_VK_C, this.copyPasswordFromShortcut, - this, KeyHandler.SHORTCUT_ACTION, false, true ); - KeyHandler.onKey(Keys.DOM_VK_B, this.copyUserName, this, KeyHandler.SHORTCUT_ACTION); - KeyHandler.onKey(Keys.DOM_VK_U, this.copyUrl, this, KeyHandler.SHORTCUT_ACTION); + this.onKey(Keys.DOM_VK_B, this.copyUserName, KeyHandler.SHORTCUT_ACTION); + this.onKey(Keys.DOM_VK_U, this.copyUrl, KeyHandler.SHORTCUT_ACTION); if (AutoType.enabled) { - KeyHandler.onKey(Keys.DOM_VK_T, this.autoType, this, KeyHandler.SHORTCUT_ACTION); + this.onKey(Keys.DOM_VK_T, this.autoType, KeyHandler.SHORTCUT_ACTION); } - KeyHandler.onKey( + this.onKey( Keys.DOM_VK_DELETE, this.deleteKeyPress, - this, KeyHandler.SHORTCUT_ACTION, false, true ); - KeyHandler.onKey( + this.onKey( Keys.DOM_VK_BACK_SPACE, this.deleteKeyPress, - this, KeyHandler.SHORTCUT_ACTION, false, true ); - }, - - remove() { - KeyHandler.offKey(Keys.DOM_VK_C, this.copyPassword, this); - KeyHandler.offKey(Keys.DOM_VK_B, this.copyUserName, this); - KeyHandler.offKey(Keys.DOM_VK_U, this.copyUrl, this); - KeyHandler.offKey( - Keys.DOM_VK_DELETE, - this.deleteKeyPress, - this, - KeyHandler.SHORTCUT_ACTION - ); - KeyHandler.offKey( - Keys.DOM_VK_BACK_SPACE, - this.deleteKeyPress, - this, - KeyHandler.SHORTCUT_ACTION - ); - this.removeFieldViews(); - Backbone.View.prototype.remove.call(this); - }, + this.once('remove', () => { + this.removeFieldViews(); + }); + } removeFieldViews() { this.fieldViews.forEach(fieldView => fieldView.remove()); this.fieldViews = []; this.hideFieldCopyTip(); - }, + } render() { Tip.destroyTips(this.$el); @@ -139,17 +119,18 @@ const DetailsView = Backbone.View.extend({ this.removeFieldViews(); this.removeInnerViews(); if (!this.model) { - this.$el.html(this.emptyTemplate()); + this.template = emptyTemplate; + super.render(); return; } if (this.model instanceof GroupModel) { - this.$el.html(this.groupTemplate()); - Tip.createTips(this.$el); + this.template = groupTemplate; + super.render(); return; } - const model = $.extend({ deleted: this.appModel.filter.trash }, this.model); - this.$el.html(this.template(model)); - Tip.createTips(this.$el); + const model = Object.assign({ deleted: this.appModel.filter.trash }, this.model); + this.template = template; + super.render(model); this.setSelectedColor(this.model.color); this.model.initOtpGenerator(); this.addFieldViews(); @@ -166,7 +147,7 @@ const DetailsView = Backbone.View.extend({ this.pageResized(); this.showCopyTip(); return this; - }, + } addFieldViews() { const model = this.model; @@ -313,44 +294,40 @@ const DetailsView = Backbone.View.extend({ } }) ); - _.forEach( - model.fields, - function(value, field) { - if (field === 'otp' && this.model.otpGenerator) { - this.fieldViews.push( - new FieldViewOtp({ - model: { - name: '$' + field, - title: field, - value() { - return model.otpGenerator; - } + _.forEach(model.fields, (value, field) => { + if (field === 'otp' && this.model.otpGenerator) { + this.fieldViews.push( + new FieldViewOtp({ + model: { + name: '$' + field, + title: field, + value() { + return model.otpGenerator; } - }) - ); - } else { - this.fieldViews.push( - new FieldViewCustom({ - model: { - name: '$' + field, - title: field, - multiline: true, - value() { - return model.fields[field]; - } + } + }) + ); + } else { + this.fieldViews.push( + new FieldViewCustom({ + model: { + name: '$' + field, + title: field, + multiline: true, + value() { + return model.fields[field]; } - }) - ); - } - }, - this - ); + } + }) + ); + } + }); const hideEmptyFields = AppSettingsModel.instance.get('hideEmptyFields'); const fieldsMainEl = this.$el.find('.details__body-fields'); const fieldsAsideEl = this.$el.find('.details__body-aside'); - this.fieldViews.forEach(function(fieldView) { + this.fieldViews.forEach(fieldView => { fieldView.setElement(fieldView.readonly ? fieldsAsideEl : fieldsMainEl).render(); fieldView.on('change', this.fieldChanged.bind(this)); fieldView.on('copy', this.fieldCopied.bind(this)); @@ -366,13 +343,13 @@ const DetailsView = Backbone.View.extend({ fieldView.hide(); } } - }, this); + }); this.moreView = new DetailsAddFieldView(); this.moreView.render(); this.moreView.on('add-field', this.addNewField.bind(this)); this.moreView.on('more-click', this.toggleMoreOptions.bind(this)); - }, + } addNewField() { this.moreView.remove(); @@ -402,7 +379,7 @@ const DetailsView = Backbone.View.extend({ fieldView.setElement(this.$el.find('.details__body-fields')).render(); fieldView.edit(); this.fieldViews.push(fieldView); - }, + } toggleMoreOptions() { if (this.views.dropdownView) { @@ -469,7 +446,7 @@ const DetailsView = Backbone.View.extend({ this.views.dropdownView = dropdownView; }); } - }, + } moreOptionsSelect(e) { this.views.dropdownView.remove(); @@ -504,11 +481,11 @@ const DetailsView = Backbone.View.extend({ fieldView.edit(); } } - }, + } getUserNameCompletions(part) { return this.appModel.completeUserNames(part); - }, + } setSelectedColor(color) { this.$el @@ -526,7 +503,7 @@ const DetailsView = Backbone.View.extend({ .addClass('details__colors-popup-item--active'); colorEl.classList.add(color + '-color'); } - }, + } selectColor(e) { let color = $(e.target) @@ -540,7 +517,7 @@ const DetailsView = Backbone.View.extend({ } this.model.setColor(color); this.entryUpdated(); - }, + } toggleIcons() { if (this.views.sub && this.views.sub instanceof IconSelectView) { @@ -563,7 +540,7 @@ const DetailsView = Backbone.View.extend({ subView.render(); this.pageResized(); this.views.sub = subView; - }, + } toggleAttachment(e) { const attBtn = $(e.target).closest('.details__attachment'); @@ -586,7 +563,7 @@ const DetailsView = Backbone.View.extend({ subView.render(this.pageResized.bind(this)); this.views.sub = subView; attBtn.addClass('details__attachment--active'); - }, + } removeSubView() { this.$el.find('.details__attachment').removeClass('details__attachment--active'); @@ -594,7 +571,7 @@ const DetailsView = Backbone.View.extend({ this.views.sub.remove(); delete this.views.sub; } - }, + } downloadAttachment(attachment) { const data = attachment.getBinary(); @@ -604,7 +581,7 @@ const DetailsView = Backbone.View.extend({ const mimeType = attachment.mimeType || 'application/octet-stream'; const blob = new Blob([data], { type: mimeType }); FileSaver.saveAs(blob, attachment.title); - }, + } iconSelected(sel) { if (sel.custom) { @@ -620,7 +597,7 @@ const DetailsView = Backbone.View.extend({ } else { this.render(); } - }, + } showEntry(entry) { this.model = entry; @@ -628,7 +605,7 @@ const DetailsView = Backbone.View.extend({ if (entry && !entry.title && entry.isJustCreated) { this.editTitle(); } - }, + } copyKeyPress(editView) { if (this.isHidden()) { @@ -649,26 +626,26 @@ const DetailsView = Backbone.View.extend({ return true; } return false; - }, + } copyPasswordFromShortcut(e) { const copied = this.copyKeyPress(this.passEditView); if (copied) { e.preventDefault(); } - }, + } copyPassword() { this.copyKeyPress(this.passEditView); - }, + } copyUserName() { this.copyKeyPress(this.userEditView); - }, + } copyUrl() { this.copyKeyPress(this.urlEditView); - }, + } showCopyTip() { if (this.helpTipCopyShown) { @@ -687,11 +664,11 @@ const DetailsView = Backbone.View.extend({ setTimeout(() => { tip.hide(); }, Timeouts.AutoHideHint); - }, + } settingsToggled() { this.hideFieldCopyTip(); - }, + } fieldChanged(e) { if (e.field) { @@ -755,7 +732,7 @@ const DetailsView = Backbone.View.extend({ if (e.tab) { this.focusNextField(e.tab); } - }, + } otpFieldChanged(value) { let oldValue = this.model.fields.otp; @@ -771,7 +748,7 @@ const DetailsView = Backbone.View.extend({ } this.model.setOtpUrl(value); return true; - }, + } dragover(e) { e.preventDefault(); @@ -792,7 +769,7 @@ const DetailsView = Backbone.View.extend({ this.dragging = true; this.$el.find('.details').addClass('details--drag'); } - }, + } dragleave() { if (this.dragTimeout) { @@ -802,7 +779,7 @@ const DetailsView = Backbone.View.extend({ this.$el.find('.details').removeClass('details--drag'); this.dragging = false; }, 100); - }, + } drop(e) { e.preventDefault(); @@ -816,15 +793,15 @@ const DetailsView = Backbone.View.extend({ this.dragging = false; const files = e.target.files || e.originalEvent.dataTransfer.files; this.addAttachedFiles(files); - }, + } attachmentBtnClick() { this.$el.find('.details__attachment-input-file')[0].click(); - }, + } attachmentFileChange(e) { this.addAttachedFiles(e.target.files); - }, + } addAttachedFiles(files) { _.forEach( @@ -838,13 +815,13 @@ const DetailsView = Backbone.View.extend({ }, this ); - }, + } addAttachment(name, data) { this.model.addAttachment(name, data).then(() => { this.entryUpdated(); }); - }, + } deleteKeyPress(e) { if (this.views.sub && this.views.sub.attId !== undefined) { @@ -853,7 +830,7 @@ const DetailsView = Backbone.View.extend({ this.model.removeAttachment(attachment.title); this.render(); } - }, + } editTitle() { const input = $('') @@ -868,15 +845,15 @@ const DetailsView = Backbone.View.extend({ }); $('.details__header-title').replaceWith(input); input.focus()[0].setSelectionRange(this.model.title.length, this.model.title.length); - }, + } titleInputBlur(e) { this.setTitle(e.target.value); - }, + } titleInputInput(e) { e.stopPropagation(); - }, + } titleInputKeydown(e) { KeyHandler.reg(); @@ -901,7 +878,7 @@ const DetailsView = Backbone.View.extend({ this.focusNextField({ field: '$Title' }); } } - }, + } setTitle(title) { if (this.model.title instanceof kdbxweb.ProtectedValue) { @@ -913,14 +890,14 @@ const DetailsView = Backbone.View.extend({ } const newTitle = $('

').text(title || '(no title)'); this.$el.find('.details__header-title-input').replaceWith(newTitle); - }, + } entryUpdated(skipRender) { Backbone.trigger('entry-updated', { entry: this.model }); if (!skipRender) { this.render(); } - }, + } focusNextField(config) { let found = false, @@ -943,7 +920,7 @@ const DetailsView = Backbone.View.extend({ if (nextFieldView) { nextFieldView.edit(); } - }, + } showHistory() { this.removeSubView(); @@ -955,7 +932,7 @@ const DetailsView = Backbone.View.extend({ subView.render(); this.pageResized(); this.views.sub = subView; - }, + } historyClosed(e) { if (e.updated) { @@ -963,7 +940,7 @@ const DetailsView = Backbone.View.extend({ } else { this.render(); } - }, + } moveToTrash() { const doMove = () => { @@ -980,16 +957,16 @@ const DetailsView = Backbone.View.extend({ } else { doMove(); } - }, + } clone() { const newEntry = this.model.cloneEntry(' ' + Locale.detClonedName); Backbone.trigger('select-entry', newEntry); - }, + } copyToClipboard() { CopyPaste.copyHtml(this.model.getHtml()); - }, + } deleteFromTrash() { Alerts.yesno({ @@ -1005,11 +982,11 @@ const DetailsView = Backbone.View.extend({ Backbone.trigger('refresh'); } }); - }, + } backClick() { Backbone.trigger('toggle-details', false); - }, + } contextMenu(e) { const canCopy = document.queryCommandSupported('copy'); @@ -1039,7 +1016,7 @@ const DetailsView = Backbone.View.extend({ options.push({ value: 'det-auto-type', icon: 'keyboard-o', text: Locale.detAutoType }); } Backbone.trigger('show-context-menu', _.extend(e, { options })); - }, + } contextMenuSelect(e) { switch (e.item) { @@ -1062,16 +1039,16 @@ const DetailsView = Backbone.View.extend({ this.copyToClipboard(); break; } - }, + } setupOtp() { OtpQrReader.read(); - }, + } otpCodeRead(otp) { this.model.setOtp(otp); this.entryUpdated(); - }, + } otpEnterManually() { if (this.model.fields.otp) { @@ -1095,7 +1072,7 @@ const DetailsView = Backbone.View.extend({ fieldView.edit(); this.fieldViews.push(fieldView); } - }, + } toggleAutoType() { if (this.views.autoType) { @@ -1105,14 +1082,14 @@ const DetailsView = Backbone.View.extend({ } this.views.autoType = new DetailsAutoTypeView(this.model); this.views.autoType.render(); - }, + } autoType() { Backbone.trigger('auto-type', { entry: this.model }); } -}); +} -_.extend(DetailsView.prototype, Scrollable); -_.extend(DetailsView.prototype, Copyable); +Object.assign(DetailsView.prototype, Scrollable); +Object.assign(DetailsView.prototype, Copyable); export { DetailsView };