This commit is contained in:
antelle 2019-09-19 17:42:53 +02:00
parent 4d7cef70d2
commit 0367966ae6
10 changed files with 182 additions and 59 deletions

View File

@ -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();
}

View File

@ -3,6 +3,7 @@ import { MenuOptionModel } from 'models/menu/menu-option-model';
class MenuOptionCollection extends Collection {
static model = MenuOptionModel;
static wantsArrayProperties = true;
}
export { MenuOptionCollection };

View File

@ -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 };

View File

@ -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;
}

View File

@ -157,7 +157,7 @@ class EntryModel extends Model {
}
_fieldsToModel(fields) {
return omit(fields, this.builtInFields);
return omit(fields, BuiltInFields);
}
_attachmentsToModel(binaries) {

View File

@ -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 };

View File

@ -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 };

View File

@ -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');
}
}

View File

@ -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();

View File

@ -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);
}