2015-10-17 23:49:24 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
var Backbone = require('backbone'),
|
|
|
|
AppSettingsModel = require('./app-settings-model'),
|
|
|
|
MenuModel = require('./menu/menu-model'),
|
|
|
|
EntryModel = require('./entry-model'),
|
2015-10-31 20:09:32 +01:00
|
|
|
GroupModel = require('./group-model'),
|
2015-10-17 23:49:24 +02:00
|
|
|
FileCollection = require('../collections/file-collection'),
|
2015-12-06 21:32:41 +01:00
|
|
|
EntryCollection = require('../collections/entry-collection'),
|
|
|
|
FileInfoCollection = require('../collections/file-info-collection'),
|
|
|
|
FileModel = require('./file-model'),
|
|
|
|
FileInfoModel = require('./file-info-model'),
|
2015-12-06 23:13:29 +01:00
|
|
|
Storage = require('../storage'),
|
|
|
|
IdGenerator = require('../util/id-generator');
|
2015-10-17 23:49:24 +02:00
|
|
|
|
|
|
|
var AppModel = Backbone.Model.extend({
|
|
|
|
defaults: {},
|
|
|
|
|
|
|
|
initialize: function() {
|
|
|
|
this.tags = [];
|
|
|
|
this.files = new FileCollection();
|
2015-12-06 21:32:41 +01:00
|
|
|
this.fileInfos = FileInfoCollection.load();
|
2015-10-17 23:49:24 +02:00
|
|
|
this.menu = new MenuModel();
|
|
|
|
this.filter = {};
|
|
|
|
this.sort = 'title';
|
|
|
|
this.settings = AppSettingsModel.instance;
|
|
|
|
|
|
|
|
this.listenTo(Backbone, 'refresh', this.refresh);
|
|
|
|
this.listenTo(Backbone, 'set-filter', this.setFilter);
|
|
|
|
this.listenTo(Backbone, 'add-filter', this.addFilter);
|
|
|
|
this.listenTo(Backbone, 'set-sort', this.setSort);
|
2015-11-07 21:37:54 +01:00
|
|
|
this.listenTo(Backbone, 'close-file', this.closeFile);
|
2015-11-08 22:25:00 +01:00
|
|
|
this.listenTo(Backbone, 'empty-trash', this.emptyTrash);
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
addFile: function(file) {
|
2015-12-06 21:32:41 +01:00
|
|
|
if (this.files.getById(file.id)) {
|
2015-12-02 21:50:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
2015-10-17 23:49:24 +02:00
|
|
|
this.files.add(file);
|
2015-12-02 21:50:31 +01:00
|
|
|
file.get('groups').forEach(function (group) {
|
|
|
|
this.menu.groupsSection.addItem(group);
|
|
|
|
}, this);
|
2015-12-07 20:07:56 +01:00
|
|
|
this._addTags(file);
|
2015-10-17 23:49:24 +02:00
|
|
|
this._tagsChanged();
|
|
|
|
this.menu.filesSection.addItem({
|
|
|
|
icon: 'lock',
|
|
|
|
title: file.get('name'),
|
|
|
|
page: 'file',
|
|
|
|
file: file
|
|
|
|
});
|
|
|
|
this.refresh();
|
2015-12-05 14:04:09 +01:00
|
|
|
this.listenTo(file, 'reload', this.reloadFile);
|
2015-12-02 21:50:31 +01:00
|
|
|
return true;
|
2015-10-17 23:49:24 +02:00
|
|
|
},
|
|
|
|
|
2015-12-05 14:04:09 +01:00
|
|
|
reloadFile: function(file) {
|
|
|
|
this.menu.groupsSection.removeByFile(file, true);
|
|
|
|
file.get('groups').forEach(function (group) {
|
|
|
|
this.menu.groupsSection.addItem(group);
|
|
|
|
}, this);
|
|
|
|
this.updateTags();
|
|
|
|
},
|
|
|
|
|
2015-12-07 20:07:56 +01:00
|
|
|
_addTags: function(file) {
|
2015-10-17 23:49:24 +02:00
|
|
|
var tagsHash = {};
|
|
|
|
this.tags.forEach(function(tag) {
|
|
|
|
tagsHash[tag.toLowerCase()] = true;
|
|
|
|
});
|
2015-12-07 20:07:56 +01:00
|
|
|
var that = this;
|
|
|
|
file.forEachEntry({}, function(entry) {
|
2015-10-17 23:49:24 +02:00
|
|
|
_.forEach(entry.tags, function(tag) {
|
|
|
|
if (!tagsHash[tag.toLowerCase()]) {
|
|
|
|
tagsHash[tag.toLowerCase()] = true;
|
2015-12-07 20:07:56 +01:00
|
|
|
that.tags.push(tag);
|
2015-10-17 23:49:24 +02:00
|
|
|
}
|
2015-12-07 20:07:56 +01:00
|
|
|
});
|
|
|
|
});
|
2015-10-17 23:49:24 +02:00
|
|
|
this.tags.sort();
|
|
|
|
},
|
|
|
|
|
|
|
|
_tagsChanged: function() {
|
|
|
|
if (this.tags.length) {
|
|
|
|
this.menu.tagsSection.set('scrollable', true);
|
|
|
|
this.menu.tagsSection.setItems(this.tags.map(function (tag) {
|
|
|
|
return {title: tag, icon: 'tag', filterKey: 'tag', filterValue: tag};
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
this.menu.tagsSection.set('scrollable', false);
|
|
|
|
this.menu.tagsSection.removeAllItems();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
updateTags: function() {
|
|
|
|
var oldTags = this.tags.slice();
|
|
|
|
this.tags.splice(0, this.tags.length);
|
|
|
|
this.files.forEach(function(file) {
|
2015-12-07 20:07:56 +01:00
|
|
|
this._addTags(file);
|
2015-10-17 23:49:24 +02:00
|
|
|
}, this);
|
|
|
|
if (!_.isEqual(oldTags, this.tags)) {
|
|
|
|
this._tagsChanged();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
closeAllFiles: function() {
|
2015-11-17 21:57:32 +01:00
|
|
|
this.files.each(function(file) { file.close(); });
|
2015-10-17 23:49:24 +02:00
|
|
|
this.files.reset();
|
|
|
|
this.menu.groupsSection.removeAllItems();
|
|
|
|
this.menu.tagsSection.set('scrollable', false);
|
|
|
|
this.menu.tagsSection.removeAllItems();
|
|
|
|
this.menu.filesSection.removeAllItems();
|
|
|
|
this.tags.splice(0, this.tags.length);
|
|
|
|
this.setFilter({});
|
|
|
|
},
|
|
|
|
|
2015-11-07 21:37:54 +01:00
|
|
|
closeFile: function(file) {
|
|
|
|
this.files.remove(file);
|
|
|
|
this._tagsChanged();
|
|
|
|
this.menu.groupsSection.removeByFile(file);
|
|
|
|
this.menu.filesSection.removeByFile(file);
|
|
|
|
this.refresh();
|
|
|
|
},
|
|
|
|
|
2015-11-08 22:25:00 +01:00
|
|
|
emptyTrash: function() {
|
|
|
|
this.files.forEach(function(file) {
|
|
|
|
file.emptyTrash();
|
|
|
|
}, this);
|
|
|
|
this.refresh();
|
|
|
|
},
|
|
|
|
|
2015-10-17 23:49:24 +02:00
|
|
|
setFilter: function(filter) {
|
|
|
|
this.filter = filter;
|
2015-11-06 21:42:37 +01:00
|
|
|
this.filter.subGroups = this.settings.get('expandGroups');
|
2015-10-17 23:49:24 +02:00
|
|
|
var entries = this.getEntries();
|
|
|
|
Backbone.trigger('filter', { filter: this.filter, sort: this.sort, entries: entries });
|
|
|
|
Backbone.trigger('select-entry', entries.length ? entries.first() : null);
|
|
|
|
},
|
|
|
|
|
|
|
|
refresh: function() {
|
|
|
|
this.setFilter(this.filter);
|
|
|
|
},
|
|
|
|
|
|
|
|
addFilter: function(filter) {
|
|
|
|
this.setFilter(_.extend(this.filter, filter));
|
|
|
|
},
|
|
|
|
|
|
|
|
setSort: function(sort) {
|
|
|
|
this.sort = sort;
|
|
|
|
this.setFilter(this.filter);
|
|
|
|
},
|
|
|
|
|
|
|
|
getEntries: function() {
|
|
|
|
var entries = new EntryCollection();
|
|
|
|
var filter = this.prepareFilter();
|
|
|
|
this.files.forEach(function(file) {
|
|
|
|
file.forEachEntry(filter, function(entry) {
|
|
|
|
entries.push(entry);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
entries.sortEntries(this.sort);
|
2015-11-08 22:25:00 +01:00
|
|
|
if (this.filter.trash) {
|
|
|
|
this.addTrashGroups(entries);
|
|
|
|
}
|
2015-10-17 23:49:24 +02:00
|
|
|
if (entries.length) {
|
|
|
|
entries.setActive(entries.first());
|
|
|
|
}
|
|
|
|
return entries;
|
|
|
|
},
|
|
|
|
|
2015-11-08 22:25:00 +01:00
|
|
|
addTrashGroups: function(collection) {
|
|
|
|
this.files.forEach(function(file) {
|
|
|
|
var trashGroup = file.getTrashGroup();
|
|
|
|
if (trashGroup) {
|
|
|
|
trashGroup.getOwnSubGroups().forEach(function(group) {
|
|
|
|
collection.unshift(GroupModel.fromGroup(group, file, trashGroup));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-10-17 23:49:24 +02:00
|
|
|
prepareFilter: function() {
|
|
|
|
var filter = _.clone(this.filter);
|
|
|
|
if (filter.text) {
|
|
|
|
filter.textLower = filter.text.toLowerCase();
|
|
|
|
}
|
|
|
|
if (filter.tag) {
|
|
|
|
filter.tagLower = filter.tag.toLowerCase();
|
|
|
|
}
|
|
|
|
return filter;
|
|
|
|
},
|
|
|
|
|
2015-10-31 20:09:32 +01:00
|
|
|
getFirstSelectedGroup: function() {
|
2015-10-17 23:49:24 +02:00
|
|
|
var selGroupId = this.filter.group;
|
|
|
|
var file, group;
|
|
|
|
if (selGroupId) {
|
|
|
|
this.files.forEach(function(f) {
|
|
|
|
group = f.getGroup(selGroupId);
|
|
|
|
if (group) {
|
|
|
|
file = f;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
if (!group) {
|
|
|
|
file = this.files.first();
|
|
|
|
group = file.get('groups').first();
|
|
|
|
}
|
2015-10-31 20:09:32 +01:00
|
|
|
return { group: group, file: file };
|
|
|
|
},
|
|
|
|
|
|
|
|
createNewEntry: function() {
|
|
|
|
var sel = this.getFirstSelectedGroup();
|
|
|
|
return EntryModel.newEntry(sel.group, sel.file);
|
|
|
|
},
|
|
|
|
|
|
|
|
createNewGroup: function() {
|
|
|
|
var sel = this.getFirstSelectedGroup();
|
|
|
|
return GroupModel.newGroup(sel.group, sel.file);
|
2015-12-06 21:32:41 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
createDemoFile: function() {
|
|
|
|
var that = this;
|
|
|
|
if (!this.files.getByName('Demo')) {
|
|
|
|
var demoFile = new FileModel();
|
|
|
|
demoFile.openDemo(function() {
|
|
|
|
that.addFile(demoFile);
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
createNewFile: function() {
|
|
|
|
var name;
|
|
|
|
for (var i = 0; ; i++) {
|
|
|
|
name = 'New' + (i || '');
|
|
|
|
if (!this.files.getByName(name)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var newFile = new FileModel();
|
|
|
|
newFile.create(name);
|
|
|
|
this.addFile(newFile);
|
|
|
|
},
|
|
|
|
|
|
|
|
openFile: function(params, callback) {
|
|
|
|
var that = this;
|
2015-12-07 20:07:56 +01:00
|
|
|
var fileInfo = params.id ? this.fileInfos.get(params.id) : this.fileInfos.getMatch(params.storage, params.name, params.path);
|
|
|
|
if (fileInfo && fileInfo.get('availOffline') && fileInfo.get('modified')) {
|
|
|
|
// modified offline, cannot overwrite: load from cache
|
|
|
|
this.openFileFromCache(params, callback, fileInfo);
|
|
|
|
} else if (params.fileData) {
|
|
|
|
// has user content: load it
|
|
|
|
this.openFileWithData(params, callback, fileInfo, params.fileData, true);
|
|
|
|
} else if (fileInfo && fileInfo.get('availOffline') && fileInfo.rev === params.rev) {
|
|
|
|
// already latest in cache: use it
|
|
|
|
this.openFileFromCache(params, callback, fileInfo);
|
|
|
|
} else {
|
|
|
|
// try to load from storage and update cache
|
|
|
|
Storage[params.storage].load(params.path, function(err, data, rev) {
|
|
|
|
if (err) {
|
|
|
|
// failed to load from storage: fallback to cache if we can
|
|
|
|
if (fileInfo && fileInfo.get('availOffline')) {
|
|
|
|
that.openFileFromCache(params, callback, fileInfo);
|
|
|
|
} else {
|
|
|
|
callback(err);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
params.fileData = data;
|
|
|
|
params.rev = rev;
|
|
|
|
that.openFileWithData(params, callback, fileInfo, data, true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
openFileFromCache: function(params, callback, fileInfo) {
|
|
|
|
var that = this;
|
|
|
|
Storage.cache.load(fileInfo.id, function(data, err) {
|
|
|
|
if (err) {
|
|
|
|
callback(err);
|
|
|
|
} else {
|
|
|
|
that.openFileWithData(params, callback, fileInfo, data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
openFileWithData: function(params, callback, fileInfo, data, updateCacheOnSuccess) {
|
2015-12-06 21:32:41 +01:00
|
|
|
var file = new FileModel({
|
|
|
|
name: params.name,
|
|
|
|
availOffline: params.availOffline,
|
|
|
|
storage: params.storage,
|
|
|
|
path: params.path,
|
|
|
|
keyFileName: params.keyFileName
|
|
|
|
});
|
2015-12-07 20:07:56 +01:00
|
|
|
var that = this;
|
|
|
|
file.open(params.password, data, params.keyFileData, function(err) {
|
|
|
|
if (err) {
|
2015-12-06 21:32:41 +01:00
|
|
|
return callback(err);
|
|
|
|
}
|
2015-12-07 20:07:56 +01:00
|
|
|
if (that.files.get(file.id)) {
|
|
|
|
return callback('Duplicate file id');
|
|
|
|
}
|
|
|
|
if (params.availOffline && updateCacheOnSuccess) {
|
|
|
|
var cacheId = fileInfo && fileInfo.id || IdGenerator.uuid();
|
|
|
|
Storage.cache.save(cacheId, params.fileData, function(err) {
|
|
|
|
if (err) {
|
|
|
|
file.set('availOffline', false);
|
|
|
|
if (!params.storage) { return; }
|
|
|
|
}
|
|
|
|
that.addToLastOpenFiles(file, cacheId, params.rev);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (!params.availOffline && fileInfo && !fileInfo.get('modified')) {
|
|
|
|
that.removeFileInfo(fileInfo.id);
|
2015-12-06 21:32:41 +01:00
|
|
|
}
|
|
|
|
that.addFile(file);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-12-07 20:07:56 +01:00
|
|
|
addToLastOpenFiles: function(file, id, rev) {
|
|
|
|
var dt = new Date();
|
2015-12-06 21:32:41 +01:00
|
|
|
var fileInfo = new FileInfoModel({
|
2015-12-06 23:13:29 +01:00
|
|
|
id: id,
|
2015-12-06 21:32:41 +01:00
|
|
|
name: file.get('name'),
|
|
|
|
storage: file.get('storage'),
|
|
|
|
path: file.get('path'),
|
|
|
|
availOffline: file.get('availOffline'),
|
|
|
|
modified: file.get('modified'),
|
|
|
|
editState: null,
|
2015-12-07 20:07:56 +01:00
|
|
|
pullRev: rev,
|
|
|
|
pullDate: dt,
|
|
|
|
openDate: dt
|
2015-12-06 21:32:41 +01:00
|
|
|
});
|
2015-12-06 23:13:29 +01:00
|
|
|
this.fileInfos.remove(id);
|
|
|
|
this.fileInfos.unshift(fileInfo);
|
2015-12-06 21:32:41 +01:00
|
|
|
this.fileInfos.save();
|
|
|
|
},
|
|
|
|
|
|
|
|
removeFileInfo: function(id) {
|
|
|
|
Storage.cache.remove(id);
|
|
|
|
this.fileInfos.remove(id);
|
|
|
|
this.fileInfos.save();
|
2015-10-17 23:49:24 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = AppModel;
|