mirror of https://github.com/keeweb/keeweb.git
bugfixes
This commit is contained in:
parent
4d7cef70d2
commit
0367966ae6
|
@ -25,7 +25,7 @@ class EntryCollection extends Collection {
|
|||
|
||||
defaultComparator = 'title';
|
||||
|
||||
filter = null;
|
||||
entryFilter = null;
|
||||
|
||||
constructor(models, options) {
|
||||
super(models);
|
||||
|
@ -34,7 +34,7 @@ class EntryCollection extends Collection {
|
|||
}
|
||||
|
||||
sortEntries(comparator, filter) {
|
||||
this.filter = filter;
|
||||
this.entryFilter = filter;
|
||||
this.comparator = this.comparators[comparator] || this.comparators[this.defaultComparator];
|
||||
this.sort();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { MenuOptionModel } from 'models/menu/menu-option-model';
|
|||
|
||||
class MenuOptionCollection extends Collection {
|
||||
static model = MenuOptionModel;
|
||||
static wantsArrayProperties = true;
|
||||
}
|
||||
|
||||
export { MenuOptionCollection };
|
||||
|
|
|
@ -1,93 +1,148 @@
|
|||
import EventEmitter from 'events';
|
||||
|
||||
const SymbolEvents = Symbol('events');
|
||||
const SymbolArray = Symbol('array');
|
||||
|
||||
function emitSet(target, property, value, receiver, prevValue) {
|
||||
function emitSet(target, value, prevValue) {
|
||||
const emitter = target[SymbolEvents];
|
||||
if (!emitter.paused) {
|
||||
const updates = { added: [], removed: [] };
|
||||
if (prevValue) {
|
||||
emitter.emit('remove', prevValue, receiver);
|
||||
emitter.emit('remove', prevValue, target);
|
||||
updates.removed.push(prevValue);
|
||||
}
|
||||
if (value) {
|
||||
emitter.emit('add', value, receiver);
|
||||
emitter.emit('add', value, target);
|
||||
updates.added.push(value);
|
||||
}
|
||||
emitter.emit('change', updates, this);
|
||||
}
|
||||
}
|
||||
|
||||
function emitRemoved(target, removed, receiver) {
|
||||
function emitRemoved(target, removed) {
|
||||
const emitter = target[SymbolEvents];
|
||||
if (!emitter.paused) {
|
||||
for (const item of removed) {
|
||||
emitter.emit('remove', item, receiver);
|
||||
emitter.emit('remove', item, target);
|
||||
}
|
||||
emitter.emit('change', { added: [], removed }, this);
|
||||
}
|
||||
}
|
||||
|
||||
const ProxyDef = {
|
||||
set(target, property, value, receiver) {
|
||||
if (property === 'length') {
|
||||
if (value < target.length) {
|
||||
const removed = target.slice(value);
|
||||
emitRemoved(target, removed, receiver);
|
||||
}
|
||||
target.length = value;
|
||||
return true;
|
||||
function checkType(target, value) {
|
||||
const modelClass = target.constructor.model;
|
||||
if (!modelClass) {
|
||||
throw new Error(`Model type not defined for ${target.constructor.name}`);
|
||||
}
|
||||
if (!(value instanceof modelClass)) {
|
||||
const valueType = value && value.constructor ? value.constructor.name : typeof value;
|
||||
throw new Error(`Attempt to write ${valueType} into ${target.constructor.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
function makeArrayProp(i) {
|
||||
return {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() {
|
||||
return this[SymbolArray][i];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setArrayProperties(object) {
|
||||
if (!object.constructor.wantsArrayProperties) {
|
||||
return;
|
||||
}
|
||||
const length = object[SymbolArray].length;
|
||||
for (let i = length; i >= 0; i--) {
|
||||
if (!Object.prototype.hasOwnProperty.call(object, i)) {
|
||||
Object.defineProperty(object, i, makeArrayProp(i));
|
||||
}
|
||||
}
|
||||
for (let i = length; ; i++) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, i)) {
|
||||
delete object[i];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ProxyDef = {
|
||||
set(target, property, value) {
|
||||
const numProp = parseInt(property);
|
||||
if (isNaN(numProp)) {
|
||||
target[property] = value;
|
||||
return true;
|
||||
}
|
||||
const modelClass = target.constructor.model;
|
||||
if (!modelClass) {
|
||||
throw new Error(`Model type not defined for ${receiver.constructor.name}`);
|
||||
}
|
||||
if (value && !(value instanceof modelClass)) {
|
||||
throw new Error(
|
||||
`Attempt to write ${value.constructor.name} into ${receiver.constructor.name}`
|
||||
);
|
||||
}
|
||||
const prevValue = target[property];
|
||||
checkType(target, value);
|
||||
const array = target[SymbolArray];
|
||||
const prevValue = array[property];
|
||||
if (prevValue !== value) {
|
||||
target[property] = value;
|
||||
emitSet(target, property, value, receiver, prevValue);
|
||||
array[property] = value;
|
||||
emitSet(target, value, prevValue);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
get(target, property) {
|
||||
if (typeof property !== 'string') {
|
||||
return target[property];
|
||||
}
|
||||
const numProp = parseInt(property);
|
||||
if (isNaN(numProp)) {
|
||||
return target[property];
|
||||
}
|
||||
return target[SymbolArray][property];
|
||||
}
|
||||
};
|
||||
|
||||
class Collection extends Array {
|
||||
class Collection {
|
||||
constructor(items) {
|
||||
super();
|
||||
|
||||
const emitter = new EventEmitter();
|
||||
emitter.setMaxListeners(100);
|
||||
|
||||
const properties = {
|
||||
[SymbolEvents]: { value: emitter }
|
||||
[SymbolEvents]: { value: emitter },
|
||||
[SymbolArray]: { value: [] }
|
||||
};
|
||||
|
||||
Object.defineProperties(this, properties);
|
||||
|
||||
const object = new Proxy(this, ProxyDef);
|
||||
|
||||
if (items) {
|
||||
object.push(...items);
|
||||
this.push(...items);
|
||||
}
|
||||
|
||||
return object;
|
||||
return new Proxy(this, ProxyDef);
|
||||
}
|
||||
|
||||
get length() {
|
||||
return this[SymbolArray].length;
|
||||
}
|
||||
|
||||
set length(value) {
|
||||
const array = this[SymbolArray];
|
||||
let removed;
|
||||
if (value < array.length) {
|
||||
removed = array.slice(value);
|
||||
}
|
||||
array.length = value;
|
||||
setArrayProperties(this, array.length);
|
||||
if (removed) {
|
||||
emitRemoved(this, removed);
|
||||
}
|
||||
}
|
||||
|
||||
push(...items) {
|
||||
if (items.length) {
|
||||
for (const item of items) {
|
||||
checkType(this, item);
|
||||
}
|
||||
this[SymbolEvents].paused = true;
|
||||
super.push(...items);
|
||||
this[SymbolArray].push(...items);
|
||||
this[SymbolEvents].paused = false;
|
||||
setArrayProperties(this);
|
||||
for (const item of items) {
|
||||
this[SymbolEvents].emit('add', item, this);
|
||||
}
|
||||
|
@ -97,9 +152,10 @@ class Collection extends Array {
|
|||
|
||||
pop() {
|
||||
this[SymbolEvents].paused = true;
|
||||
const item = super.pop();
|
||||
const item = this[SymbolArray].pop();
|
||||
this[SymbolEvents].paused = false;
|
||||
if (item) {
|
||||
setArrayProperties(this);
|
||||
this[SymbolEvents].emit('remove', item, this);
|
||||
this[SymbolEvents].emit('change', { added: [], removed: [item] }, this);
|
||||
}
|
||||
|
@ -107,9 +163,10 @@ class Collection extends Array {
|
|||
|
||||
shift() {
|
||||
this[SymbolEvents].paused = true;
|
||||
const item = super.shift();
|
||||
const item = this[SymbolArray].shift();
|
||||
this[SymbolEvents].paused = false;
|
||||
if (item) {
|
||||
setArrayProperties(this);
|
||||
this[SymbolEvents].emit('remove', item, this);
|
||||
this[SymbolEvents].emit('change', { added: [], removed: [item] }, this);
|
||||
}
|
||||
|
@ -117,9 +174,13 @@ class Collection extends Array {
|
|||
|
||||
unshift(...items) {
|
||||
if (items.length) {
|
||||
for (const item of items) {
|
||||
checkType(this, item);
|
||||
}
|
||||
this[SymbolEvents].paused = true;
|
||||
super.unshift(...items);
|
||||
this[SymbolArray].unshift(...items);
|
||||
this[SymbolEvents].paused = false;
|
||||
setArrayProperties(this);
|
||||
for (const item of items) {
|
||||
this[SymbolEvents].emit('add', item, this);
|
||||
}
|
||||
|
@ -128,9 +189,13 @@ class Collection extends Array {
|
|||
}
|
||||
|
||||
splice(start, deleteCount, ...items) {
|
||||
for (const item of items) {
|
||||
checkType(this, item);
|
||||
}
|
||||
this[SymbolEvents].paused = true;
|
||||
const removed = super.splice(start, deleteCount, ...items);
|
||||
const removed = this[SymbolArray].splice(start, deleteCount, ...items);
|
||||
this[SymbolEvents].paused = false;
|
||||
setArrayProperties(this);
|
||||
for (const item of removed) {
|
||||
this[SymbolEvents].emit('remove', item, this);
|
||||
}
|
||||
|
@ -165,6 +230,48 @@ class Collection extends Array {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fill() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
copyWithin() {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
|
||||
const ProxiedArrayMethods = [
|
||||
Symbol.iterator,
|
||||
'concat',
|
||||
'entries',
|
||||
'every',
|
||||
'filter',
|
||||
'find',
|
||||
'findIndex',
|
||||
'flat',
|
||||
'flatMap',
|
||||
'forEach',
|
||||
'includes',
|
||||
'indexOf',
|
||||
'join',
|
||||
'keys',
|
||||
'lastIndexOf',
|
||||
'map',
|
||||
'reduce',
|
||||
'reduceRight',
|
||||
'reverse',
|
||||
'slice',
|
||||
'some',
|
||||
'sort',
|
||||
'values'
|
||||
];
|
||||
|
||||
for (const method of ProxiedArrayMethods) {
|
||||
Object.defineProperty(Collection.prototype, method, {
|
||||
value: function proxyMethod(...args) {
|
||||
return this[SymbolArray][method](...args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { Collection };
|
||||
|
|
|
@ -5,10 +5,10 @@ const SymbolEvents = Symbol('events');
|
|||
const SymbolDefaults = Symbol('defaults');
|
||||
const SymbolExtensions = Symbol('extensions');
|
||||
|
||||
function emitPropChange(target, property, value, receiver) {
|
||||
function emitPropChange(target, property, value, prevValue, receiver) {
|
||||
const emitter = target[SymbolEvents];
|
||||
if (!emitter.paused) {
|
||||
emitter.emit('change:' + property, receiver, value);
|
||||
emitter.emit('change:' + property, receiver, value, prevValue);
|
||||
if (!emitter.noChange) {
|
||||
emitter.emit('change', receiver, { [property]: value });
|
||||
}
|
||||
|
@ -20,13 +20,14 @@ const ProxyDef = {
|
|||
if (Object.prototype.hasOwnProperty.call(target, property)) {
|
||||
const defaults = target[SymbolDefaults];
|
||||
const value = defaults[property];
|
||||
if (target[property] !== value) {
|
||||
const prevValue = target[property];
|
||||
if (prevValue !== value) {
|
||||
if (Object.prototype.hasOwnProperty.call(defaults, property)) {
|
||||
target[property] = value;
|
||||
} else {
|
||||
delete target[property];
|
||||
}
|
||||
emitPropChange(target, property, value, receiver);
|
||||
emitPropChange(target, property, value, prevValue, receiver);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ class EntryModel extends Model {
|
|||
}
|
||||
|
||||
_fieldsToModel(fields) {
|
||||
return omit(fields, this.builtInFields);
|
||||
return omit(fields, BuiltInFields);
|
||||
}
|
||||
|
||||
_attachmentsToModel(binaries) {
|
||||
|
|
|
@ -342,6 +342,8 @@ class GroupModel extends MenuItemModel {
|
|||
}
|
||||
|
||||
GroupModel.defineModelProperties({
|
||||
id: '',
|
||||
uuid: '',
|
||||
iconId: 0,
|
||||
entries: null,
|
||||
filterKey: 'group',
|
||||
|
@ -351,7 +353,11 @@ GroupModel.defineModelProperties({
|
|||
drop: true,
|
||||
enableSearching: true,
|
||||
enableAutoType: null,
|
||||
autoTypeSeq: null
|
||||
autoTypeSeq: null,
|
||||
group: null,
|
||||
file: null,
|
||||
parentGroup: null,
|
||||
customIconId: null
|
||||
});
|
||||
|
||||
export { GroupModel };
|
||||
|
|
|
@ -4,7 +4,7 @@ import { MenuOptionModel } from 'models/menu/menu-option-model';
|
|||
|
||||
class MenuItemModel extends Model {
|
||||
constructor(model) {
|
||||
super();
|
||||
super(model);
|
||||
if (model && model.file) {
|
||||
model.file.on('change:name', this.changeTitle.bind(this));
|
||||
}
|
||||
|
@ -36,7 +36,9 @@ class MenuItemModel extends Model {
|
|||
}
|
||||
|
||||
MenuItemModel.defineModelProperties({
|
||||
id: '',
|
||||
title: '',
|
||||
locTitle: '',
|
||||
icon: '',
|
||||
customIcon: null,
|
||||
active: false,
|
||||
|
@ -51,7 +53,11 @@ MenuItemModel.defineModelProperties({
|
|||
drop: false,
|
||||
filterKey: null,
|
||||
filterValue: null,
|
||||
collapsible: false
|
||||
collapsible: false,
|
||||
defaultItem: false,
|
||||
page: null,
|
||||
editable: false,
|
||||
file: null
|
||||
});
|
||||
|
||||
export { MenuItemModel };
|
||||
|
|
|
@ -2,15 +2,17 @@ import { Model } from 'framework/model';
|
|||
import { MenuItemCollection } from 'collections/menu/menu-item-collection';
|
||||
import { MenuItemModel } from './menu-item-model';
|
||||
|
||||
function convertItem(item) {
|
||||
return item instanceof MenuItemModel ? item : new MenuItemModel(item);
|
||||
}
|
||||
|
||||
class MenuSectionModel extends Model {
|
||||
constructor(items = []) {
|
||||
super({
|
||||
items: new MenuItemCollection(items.map(item => new MenuItemModel(item)))
|
||||
});
|
||||
super({ items: new MenuItemCollection(items.map(convertItem)) });
|
||||
}
|
||||
|
||||
addItem(item) {
|
||||
this.items.push(item);
|
||||
this.items.push(convertItem(item));
|
||||
this.emit('change-items');
|
||||
}
|
||||
|
||||
|
@ -48,7 +50,7 @@ class MenuSectionModel extends Model {
|
|||
|
||||
setItems(items) {
|
||||
this.items.length = 0;
|
||||
this.items.push(...items);
|
||||
this.items.push(...items.map(convertItem));
|
||||
this.emit('change-items');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,9 +310,7 @@ class AppView extends View {
|
|||
}
|
||||
|
||||
showFileSettings(e) {
|
||||
const menuItem = this.model.menu.filesSection
|
||||
.items
|
||||
.find(item => item.file.id === e.fileId);
|
||||
const menuItem = this.model.menu.filesSection.items.find(item => item.file.id === e.fileId);
|
||||
if (this.views.settings) {
|
||||
if (this.views.settings.file === menuItem.file) {
|
||||
this.showEntries();
|
||||
|
|
|
@ -54,6 +54,9 @@ class MenuItemView extends View {
|
|||
render() {
|
||||
this.removeInnerViews();
|
||||
super.render(this.model);
|
||||
if (this.model.options) {
|
||||
window.model = this.model;
|
||||
}
|
||||
this.iconEl = this.$el.find('.menu__item-icon');
|
||||
const items = this.model.items;
|
||||
if (items) {
|
||||
|
@ -98,8 +101,7 @@ class MenuItemView extends View {
|
|||
this.model.setExpanded(expanded);
|
||||
}
|
||||
|
||||
changeCls(model, cls) {
|
||||
const oldCls = model.previousAttributes().cls;
|
||||
changeCls(model, cls, oldCls) {
|
||||
if (oldCls) {
|
||||
this.$el.removeClass(oldCls);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue