password-manager-mirror/frontend/delta/js/Clipperz/PM/DataModel/Record.js

1759 lines
61 KiB
JavaScript

/*
Copyright 2008-2018 Clipperz Srl
This file is part of Clipperz, the online password manager.
For further information about its features and functionalities please
refer to http://www.clipperz.com.
* Clipperz is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
* Clipperz is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public
License along with Clipperz. If not, see http://www.gnu.org/licenses/.
*/
"use strict";
Clipperz.Base.module('Clipperz.PM.DataModel');
//if (typeof(Clipperz) == 'undefined') { Clipperz = {}; }
//if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; }
//if (typeof(Clipperz.PM.DataModel) == 'undefined') { Clipperz.PM.DataModel = {}; }
Clipperz.PM.DataModel.Record = function(args) {
Clipperz.PM.DataModel.Record.superclass.constructor.apply(this, arguments);
this._updateDate = (args.updateDate ? Clipperz.PM.Date.parse(args.updateDate) : Clipperz.Base.exception.raise('MandatoryParameter'));
// Disabled due to invalid data and the raised exception
// this._accessDate = (args.accessDate ? Clipperz.PM.Date.parse(args.accessDate) : Clipperz.Base.exception.raise('MandatoryParameter'));
this._retrieveIndexDataFunction = args.retrieveIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
this._updateIndexDataFunction = args.updateIndexDataFunction || Clipperz.Base.exception.raise('MandatoryParameter');
this._retrieveDirectLoginIndexDataFunction = args.retrieveDirectLoginIndexDataFunction || null;
this._setDirectLoginIndexDataFunction = args.setDirectLoginIndexDataFunction || null;
this._removeDirectLoginIndexDataFunction = args.removeDirectLoginIndexDataFunction || null;
this._createNewDirectLoginFunction = args.createNewDirectLoginFunction || null;
this._retrieveAttachmentIndexDataFunction = args.retrieveAttachmentIndexDataFunction || null;
this._setAttachmentIndexDataFunction = args.setAttachmentIndexDataFunction || null;
this._removeAttachmentIndexDataFunction = args.removeAttachmentIndexDataFunction || null;
this._createNewAttachmentFunction = args.createNewAttachmentFunction || null;
this._attachmentServerStatus = {};
this._certificateInfo = null;
this._tags = [];
this._directLogins = {};
this._attachments = {};
this._versions = {};
this._currentRecordVersion = null;
if (this.isBrandNew()) {
var newVersion;
this.setNotes('');
newVersion = new Clipperz.PM.DataModel.Record.Version({
'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
'getVersion': MochiKit.Base.method(this, 'getVersion')
});
this._versions[newVersion.reference()] = newVersion;
this._currentVersionReference = newVersion.reference();
// this.setLabel('');
}
return this;
}
Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.EncryptedRemoteObject, {
'toString': function() {
return "Record (" + this.reference() + ")";
},
//-------------------------------------------------------------------------
// 'reference': function () {
// return this._reference;
// },
//=========================================================================
setID: function (anID) {
//console.log("RECORD - SET ID", this, anID);
return this.setValue('ID', anID);
},
getID: function () {
return this.getValue('ID');
},
//=========================================================================
collectFieldInfo: function (aField) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('Record.collectFieldInfo', {trace:false});
deferredResult.setValue('_field');
deferredResult.addMethod(aField, 'reference');
deferredResult.setValue('_reference');
deferredResult.addMethod(aField, 'label');
deferredResult.setValue('label');
deferredResult.addMethod(aField, 'value');
deferredResult.setValue('value');
deferredResult.addMethod(aField, 'actionType');
deferredResult.setValue('actionType');
deferredResult.addMethod(aField, 'isHidden');
deferredResult.setValue('isHidden');
deferredResult.values();
deferredResult.callback(aField);
return deferredResult;
},
collectDirectLoginInfo: function (aDirectLogin) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('Record.collectDirectLoginInfo', {trace:false});
deferredResult.addMethod(aDirectLogin, 'reference');
deferredResult.setValue('_reference');
deferredResult.addMethod(aDirectLogin, 'label');
deferredResult.setValue('label');
deferredResult.addMethod(aDirectLogin, 'favicon');
deferredResult.setValue('favicon');
deferredResult.values();
deferredResult.callback();
return deferredResult;
},
collectAttachmentInfo: function(anAttachment) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('Record.collectAttachmentInfo', {trace:false});
deferredResult.setValue('_attachment'); // The object itself, maybe this should be the only value being passed
deferredResult.addMethod(anAttachment, 'reference');
deferredResult.setValue('_reference');
deferredResult.addMethod(anAttachment, 'name');
deferredResult.setValue('name');
deferredResult.addMethod(anAttachment, 'contentType');
deferredResult.setValue('contentType');
deferredResult.addMethod(anAttachment, 'size');
deferredResult.setValue('size');
deferredResult.addMethod(anAttachment, 'hash');
deferredResult.setValue('hash');
deferredResult.values();
deferredResult.callback(anAttachment);
return deferredResult;
},
collectRecordInfo: function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('Record.collectRecordInfo', {trace:false});
deferredResult.setValue('_record');
deferredResult.addMethod(this, 'reference');
deferredResult.setValue('_reference');
deferredResult.addMethod(this, 'isArchived');
deferredResult.setValue('_isArchived');
deferredResult.addMethod(this, 'isBrandNew');
deferredResult.setValue('_isBrandNew');
deferredResult.addMethod(this, 'label');
deferredResult.setValue('label');
deferredResult.addMethod(this, 'notes');
deferredResult.setValue('notes');
deferredResult.addMethod(this, 'tags');
deferredResult.setValue('tags');
deferredResult.addMethod(this, 'getID');
deferredResult.setValue('ID');
deferredResult.addMethod(this, 'fields');
deferredResult.addCallback(MochiKit.Base.values);
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'collectFieldInfo'));
deferredResult.addCallback(Clipperz.Async.collectAll);
deferredResult.setValue('fields');
deferredResult.addMethod(this, 'certificateInfo');
deferredResult.addCallback(MochiKit.Base.bind(function (someCertificateInfo) {
var result;
if ((someCertificateInfo) && (someCertificateInfo['status'] == 'requested')) {
result = this.updateCertificateInfo()
} else {
result = MochiKit.Async.succeed(someCertificateInfo);
}
return result;
}, this));
deferredResult.setValue('certificateInfo');
deferredResult.addMethod(this, 'attachmentsInfo');
deferredResult.setValue('attachments');
deferredResult.addMethod(this, 'directLogins');
deferredResult.addCallback(MochiKit.Base.values);
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'collectDirectLoginInfo'));
deferredResult.addCallback(Clipperz.Async.collectAll);
deferredResult.setValue('directLogins');
deferredResult.addMethod(this, 'getAttachmentServerStatus');
deferredResult.setValue('attachmentServerStatus');
deferredResult.values();
deferredResult.callback(this);
return deferredResult;
},
//=========================================================================
certificateMetadata: function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred('Record.certificateMetadata', {trace:false});
deferredResult.addMethod(this, 'collectRecordInfo');
deferredResult.addCallback(function (someValues) {
var result = {};
var propertiesToCollect = ['label', 'notes'];
var fieldPropertiesToCollect = ['label', 'value'];
var attachmentPropertiesToCollect = ['name', 'contentType', 'size', 'hash'];
MochiKit.Iter.forEach(propertiesToCollect, function (aKey) { result[aKey] = someValues[aKey]});
result['fields'] = MochiKit.Base.map(function (aField) {
var fieldResult = {};
MochiKit.Iter.forEach(fieldPropertiesToCollect, function (aKey) { fieldResult[aKey] = aField[aKey]});
return fieldResult;
}, someValues['fields']);
result['attachments'] = MochiKit.Base.map(function (anAttachment) {
var attachmentResult = {};
MochiKit.Iter.forEach(attachmentPropertiesToCollect, function (aKey) { attachmentResult[aKey] = anAttachment[aKey]});
return attachmentResult;
}, someValues['attachments']);
if (result['notes'] == "") {
delete result['notes'];
}
return result;
});
deferredResult.addCallback(Clipperz.Base.serializeJSON);
//deferredResult.addCallback(function (aValue) { console.log("<<< CERTIFICATE METADATA", aValue); return aValue;});
deferredResult.callback();
return deferredResult;
},
//=========================================================================
/*
publicKeyBufferFromHash256: function (aBuffer) {
// var fixedBuffer;
//
// fixedBuffer = new npm.buffer.Buffer(33);
// fixedBuffer.writeUInt8(0x03, 0); // https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
// aBuffer.copy(fixedBuffer, 1);
//
// return npm.bitcoin.ECPair.fromPublicKeyBuffer(fixedBuffer, NETWORK);
return npm.bitcoin.HDNode.fromSeedHex(aBuffer.toString('hex')).getPublicKeyBuffer();
},
*/
keyFromHash256: function (aBuffer) {
// return npm.bitcoin.HDNode.fromSeedHex(aBuffer.toString('hex'), NETWORK);
var d = npm.BigInteger.fromHex(aBuffer.toString('hex'));
return new npm.bitcoin.ECPair(d, null, {'network':NETWORK, 'compressed':true})
},
computeAttachmentsCertificateInfo: function (attachmentController) {
var deferredResult;
var collectResultsInfo = {};
var shouldComputeResult = false;
var self = this;
MochiKit.Iter.forEach(MochiKit.Base.values(this.attachments()), function (anAttachment) {
shouldComputeResult = true;
collectResultsInfo[anAttachment.reference()] = [
MochiKit.Base.method(anAttachment, 'metadata'),
MochiKit.Base.itemgetter('hash'),
function (anHashValue) {
var hashBuffer = new npm.buffer.Buffer(anHashValue, 'hex');
var key = self.keyFromHash256(hashBuffer);
return {
'hash': anHashValue,
'publicKey': key.getPublicKeyBuffer().toString('hex'),
'address': key.getAddress(),
}
}
];
});
if (shouldComputeResult) {
deferredResult = new Clipperz.Async.Deferred("Record.computeAttachmentsCertificateInfo", {trace:false});
deferredResult.collectResults(collectResultsInfo);
deferredResult.callback();
} else {
deferredResult = MochiKit.Async.succeed(collectResultsInfo);
}
return deferredResult;
},
computeCertificateInfo: function (aWallet) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Record.computeCertificateInfo", {trace:false});
deferredResult.collectResults({
'reference': MochiKit.Base.method(this, 'reference'),
'version': MochiKit.Base.partial(MochiKit.Async.succeed, "1.0"),
'card.publicKey': [
MochiKit.Base.method(this, 'getID'),
MochiKit.Base.method(aWallet, 'getKeyForDocumentWithID'),
MochiKit.Base.methodcaller('getPublicKeyBuffer'),
MochiKit.Base.methodcaller('toString', 'hex'),
],
'card.address': [
MochiKit.Base.method(this, 'getID'),
MochiKit.Base.method(aWallet, 'getKeyForDocumentWithID'),
MochiKit.Base.methodcaller('getAddress')
],
/*
'metadata': [
MochiKit.Base.method(this, 'certificateMetadata'),
function (aString) { return new npm.buffer.Buffer(aString, 'utf8') },
npm.bitcoin.crypto.hash160,
function (anHash160) { return npm.bitcoin.address.toBase58Check(anHash160, NETWORK.pubKeyHash);}
],
*/
'metadata.publicKey': [
MochiKit.Base.method(this, 'certificateMetadata'),
function (aString) { return new npm.buffer.Buffer(aString, 'utf8') },
npm.bitcoin.crypto.hash256,
MochiKit.Base.method(this, 'keyFromHash256'),
MochiKit.Base.methodcaller('getPublicKeyBuffer'),
MochiKit.Base.methodcaller('toString', 'hex'),
],
'metadata.address': [
MochiKit.Base.method(this, 'certificateMetadata'),
function (aString) { return new npm.buffer.Buffer(aString, 'utf8') },
npm.bitcoin.crypto.hash256,
MochiKit.Base.method(this, 'keyFromHash256'),
MochiKit.Base.methodcaller('getAddress')
],
// 'attachments': MochiKit.Base.method(this, 'computeAttachmentsCertificateInfo')
});
//deferredResult.addCallback(function (aValue) { console.log("=== CERTIFICATE INFO", MochiKit.Base.serializeJSON(aValue)); return aValue;});
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'getIndexData': function () {
return this._retrieveIndexDataFunction(this.reference());
},
//.........................................................................
'getIndexDataForKey': function (aKey) {
return Clipperz.Async.callbacks("Record.getIndexDataForKey", [
MochiKit.Base.method(this, 'getIndexData'),
MochiKit.Base.itemgetter(aKey)
], {trace:false});
},
//-------------------------------------------------------------------------
'setIndexDataForKey': function (aKey, aValue) {
// return this._updateIndexDataFunction(this.reference(), aKey, aValue);
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Record.setIndexDataForKey", {trace:false});
deferredResult.addMethod(this, 'getIndexDataForKey', aKey);
deferredResult.addCallback(MochiKit.Base.bind(function (aCurrentValue) {
var result;
var originalValue;
originalValue = this.transientState().getValue('originalValues.indexData.' + aKey);
if (originalValue == null) {
originalValue = this.transientState().setValue('originalValues.indexData.' + aKey, aCurrentValue);
}
if (aCurrentValue != aValue) {
if (originalValue != aValue) {
this.transientState().setValue('hasPendingChanges.indexData.' + aKey, true);
} else {
this.transientState().setValue('hasPendingChanges.indexData.' + aKey, false);
}
result = this._updateIndexDataFunction(this.reference(), aKey, aValue);
} else {
result = MochiKit.Async.succeed(aValue);
}
return result;
}, this));
deferredResult.callback();
return deferredResult;
},
//----------------------------------------------------------------------------
setAttachmentServerStatus: function(anObject) {
this._attachmentServerStatus = anObject;
},
getAttachmentServerStatus: function() {
return this._attachmentServerStatus;
},
updatedAttachmentServerStatus: function () {
return Clipperz.Async.callbacks("Record.updatedAttachmentServerStatus", [
MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this),
MochiKit.Base.itemgetter('attachmentStatus'),
MochiKit.Base.method(this, 'setAttachmentServerStatus'),
], {trace:false});
},
//----------------------------------------------------------------------------
setCertificateInfo: function (aValue) {
this._certificateInfo = aValue;
return this._certificateInfo;
},
certificateInfo: function () {
return this._certificateInfo;
},
updateCertificateInfo: function () {
return Clipperz.Async.callbacks("Record.updateCertificateInfo", [
MochiKit.Base.partial(this.retrieveRemoteDataFunction(), this),
MochiKit.Base.itemgetter('certificateInfo'),
MochiKit.Base.method(this, 'setCertificateInfo'),
], {trace:false});
},
//============================================================================
/*
'key': function () {
return this.getIndexDataForKey('key');
},
*/
//============================================================================
'fullLabel': function () {
return this.getIndexDataForKey('label');
},
'setFullLabel': function (aLabel, someTags) {
var fullLabel = MochiKit.Base.extend([aLabel], MochiKit.Base.map(function (aTag) { return Clipperz.PM.DataModel.Record.tagChar + aTag; }, someTags)).join(' ');
return this.setIndexDataForKey('label', fullLabel);
},
'updateTags': function (someTagInfo) {
return Clipperz.Async.callbacks("Record.updateTags", [
MochiKit.Base.method(this, 'label'),
MochiKit.Base.bind(function (aLabel) {
return this.setFullLabel(aLabel, MochiKit.Base.keys(someTagInfo));
}, this)
], {trace:false});
},
//............................................................................
'extractLabelFromFullLabel': function (aValue) {
return Clipperz.PM.DataModel.Record.extractLabelFromFullLabel(aValue);
},
'extractTagsFromFullLabel': function (aLabel) {
return Clipperz.PM.DataModel.Record.extractTagsFromFullLabel(aLabel);
},
//............................................................................
'label': function () {
return Clipperz.Async.callbacks("Record.label", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'extractLabelFromFullLabel')
], {trace:false});
},
'setLabel': function (aValue) {
// var tags;
return Clipperz.Async.callbacks("Record.setLabel", [
MochiKit.Base.method(this, 'tags'),
// function (someValues) { console.log("TAGS", someValues); tags = someValues; },
// MochiKit.Base.method(this, 'setIndexDataForKey', 'label', aValue),
// MochiKit.Base.method(this, 'updateFullLabelWithTags', tags),
MochiKit.Base.method(this, 'setFullLabel', aValue),
MochiKit.Base.method(this, 'label'),
], {trace:false});
// return this.setIndexDataForKey('label', aValue); // [???]
},
//.........................................................................
'tags': function () {
return Clipperz.Async.callbacks("Record.label", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'extractTagsFromFullLabel'),
MochiKit.Base.keys
], {trace:false});
},
'addTag': function (aNewTag) {
// var tag = aNewTag.replace(/\s/g, Clipperz.PM.DataModel.Record.tagSpace);
//console.log("TAG", aNewTag, tag);
return Clipperz.Async.callbacks("Record.addTag", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'extractTagsFromFullLabel'),
function (someTags) { someTags[aNewTag] = true; return someTags; },
MochiKit.Base.method(this, 'updateTags')
], {trace:false});
},
'removeTag': function (aTag) {
//console.log("ADD TAG", aNewTag);
return Clipperz.Async.callbacks("Record.removeTag", [
MochiKit.Base.method(this, 'fullLabel'),
MochiKit.Base.method(this, 'extractTagsFromFullLabel'),
function (someTags) { delete someTags[aTag]; return someTags; },
MochiKit.Base.method(this, 'updateTags')
], {trace:false});
},
'toggleArchive': function () {
return Clipperz.Async.callbacks("Record.toggleArchive", [
MochiKit.Base.method(this, 'isArchived'),
Clipperz.Async.deferredIf("Record is archived", [
MochiKit.Base.method(this, 'removeTag', Clipperz.PM.DataModel.Record.archivedTag)
], [
MochiKit.Base.method(this, 'addTag', Clipperz.PM.DataModel.Record.archivedTag)
]),
], {trace:false});
},
'isArchived': function () {
return Clipperz.Async.callbacks("Record.isArchived", [
MochiKit.Base.method(this, 'tags'),
function (someTags) { return MochiKit.Iter.some(someTags, MochiKit.Base.partial(MochiKit.Base.objEqual, Clipperz.PM.DataModel.Record.archivedTag))},
], {trace:false});
},
//=========================================================================
'headerNotes': function () {
return this.getIndexDataForKey('notes');
},
//-------------------------------------------------------------------------
'notes': function () {
return Clipperz.Async.callbacks("Record.notes", [
MochiKit.Base.method(this, 'headerNotes'),
MochiKit.Base.bind(function (someHeaderNotes) {
var result;
if ((someHeaderNotes == null) || (typeof(someHeaderNotes) == 'undefined')) {
result = this.getValue('notes');
} else {
result = MochiKit.Async.succeed(someHeaderNotes);
}
return result;
}, this)
], {trace:false});
},
//.........................................................................
'setNotes': function (aValue) {
return this.setValue('notes', aValue);
},
//=========================================================================
'updateDate': function () {
return MochiKit.Async.succeed(this._updateDate);
},
'accessDate': function () {
return MochiKit.Async.succeed(this._accessDate);
},
//=========================================================================
certificateStatus: function() {
// TODO: FAKE IMPLEMENTATION, just enough to test the UI
var result;
// if (this.reference().charAt(0) == 'a') {
// result = 'published';
// } else if (this.reference().charAt(0) == '0') {
// result = 'requested';
// } else {
// result = '';
// }
result = '';
return result; // 'requested' | 'published' | ''
},
markAsCertified: function () {
return this.addTag(Clipperz.PM.DataModel.Record.certifiedTag);
},
hasBeenCertified: function () {
return Clipperz.Async.callbacks("Record.isArchived", [
MochiKit.Base.method(this, 'tags'),
function (someTags) { return MochiKit.Iter.some(someTags, MochiKit.Base.partial(MochiKit.Base.objEqual, Clipperz.PM.DataModel.Record.certifiedTag))},
], {trace:false});
},
//=========================================================================
'favicon': function () {
var result;
var directLogins;
directLogins = MochiKit.Base.values(this.directLogins());
if (directLogins.length > 0) {
result = directLogins[0].favicon();
// } else if (/* is there an URL to use for searching a favicon */){
} else {
result = null; // MochiKit.Async.succeed(Clipperz.PM.Strings['defaultFaviconUrl']);
}
return result;
},
//-------------------------------------------------------------------------
'searchableContent': function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Record.searchableContent", {trace:false});
deferredResult.collectResults({
'recordLabel': MochiKit.Base.method(this, 'fullLabel'),
'directLoginLabels': [
MochiKit.Base.method(this, 'directLoginReferences'),
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.itemgetter('label'))
]
})
deferredResult.addCallback(function (someValues) {
return someValues['recordLabel'] + ' ' + someValues['directLoginLabels'].join(' ');
});
deferredResult.callback();
return deferredResult;
},
//-------------------------------------------------------------------------
'isMatching': function (aRegExp) {
return Clipperz.Async.callbacks("deferredFilterFunction", [
MochiKit.Base.method(this, 'searchableContent'),
MochiKit.Base.method(aRegExp, 'test'),
function (doesItMatch) {
var result;
if (doesItMatch) {
result = MochiKit.Async.succeed('match');
} else {
result = MochiKit.Async.fail('miss');
}
return result;
}
], {trace:false});
},
//=========================================================================
'content': function () {
var deferredResult;
var result;
result = {
'fields': [],
'directLogins': []
};
deferredResult = new Clipperz.Async.Deferred("Record.content", {trace:false});
deferredResult.addMethod(this, 'reference');
deferredResult.addCallback(function (aValue) { result['reference'] = aValue; });
deferredResult.addMethod(this, 'label');
deferredResult.addCallback(function (aValue) { result['title'] = aValue; });
deferredResult.addMethod(this, 'notes');
deferredResult.addCallback(function (aValue) { result['notes'] = aValue; });
deferredResult.addMethod(this, 'fields');
deferredResult.addCallback(MochiKit.Base.values);
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
deferredResult.addCallback(Clipperz.Async.collectAll);
deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['fields'].push(aValue); });
deferredResult.addMethod(this, 'directLogins');
deferredResult.addCallback(MochiKit.Base.values);
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
deferredResult.addCallback(Clipperz.Async.collectAll);
deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['directLogins'].push(aValue); });
deferredResult.addCallback(function () { return result; });
deferredResult.addMethod(this, 'attachments');
deferredResult.addCallback(MochiKit.Base.values);
deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.methodcaller('content'));
deferredResult.addCallback(Clipperz.Async.collectAll);
deferredResult.addCallback(MochiKit.Base.map, function (aValue) { result['directLogins'].push(aValue); });
deferredResult.addCallback(function () { return result; });
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'directLogins': function () {
return this._directLogins;
},
'bindDirectLogin': function (aDirectLogin) {
this._directLogins[aDirectLogin.reference()] = aDirectLogin;
},
'directLoginWithReference': function (aDirectLoginReference) {
return this._directLogins[aDirectLoginReference];
},
'createNewDirectLoginFunction': function () {
return this._createNewDirectLoginFunction;
},
'saveOriginalDirectLoginStatusToTransientState': function () {
if (this.transientState().getValue('directLogins') == null) {
// this.transientState().setValue('directLogins', this._directLogins)
this.transientState().setValue('directLogins', {});
MochiKit.Iter.forEach(MochiKit.Base.keys(this._directLogins), MochiKit.Base.bind(function(aKey) {
this.transientState().setValue('directLogins' + '.' + aKey, this._directLogins[aKey])
}, this))
}
},
'createNewDirectLogin': function () {
this.saveOriginalDirectLoginStatusToTransientState();
return this.createNewDirectLoginFunction()(this);
},
'removeDirectLogin': function(aDirectLogin) {
this.saveOriginalDirectLoginStatusToTransientState();
return Clipperz.Async.callbacks("Record.removeDirectLogin", [
MochiKit.Base.method(this, 'removeValue', 'directLogins' + '.' + aDirectLogin.reference()),
MochiKit.Base.bind(function () {
delete this._directLogins[aDirectLogin.reference()]
}, this)
], {trace:false});
},
'directLoginReferences': function () {
var result;
result = Clipperz.Async.callbacks("Record.directLoginReferences", [
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
function (someDirectLogins) {
var result;
var i,c;
result = [];
c = someDirectLogins.length;
for (i=0; i<c; i++) {
result.push(Clipperz.Async.collectResults("Record.directLoginReferences - collectResults", {
'_rowObject': MochiKit.Async.succeed,
'_reference': MochiKit.Base.methodcaller('reference'),
'label': MochiKit.Base.methodcaller('label'),
'favicon': MochiKit.Base.methodcaller('favicon')
}, {trace:false})(someDirectLogins[i]));
};
return result;
},
Clipperz.Async.collectAll
], {trace:false});
return result;
},
//=========================================================================
// TODO: !!!!
'attachmentsCount': function() {
return MochiKit.Base.keys(this.attachments()).length;
},
'attachmentsInfo': function () {
// deferredResult.addMethod(this, 'attachments');
// deferredResult.addCallback(MochiKit.Base.values);
// deferredResult.addCallback(MochiKit.Base.map, MochiKit.Base.method(this, 'collectAttachmentInfo'));
// deferredResult.addCallback(Clipperz.Async.collectAll);
return Clipperz.Async.callbacks("Record.attachmentsInfo", [
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'collectAttachmentInfo')),
Clipperz.Async.collectAll
], {trace:false});
},
'attachments': function () {
return this._attachments;
},
'bindAttachment': function (anAttachment) {
this._attachments[anAttachment.reference()] = anAttachment;
},
'attachmentWithReference': function (anAttachmentReference) {
return this._attachments[anAttachmentReference];
},
'createNewAttachmentFunction': function () {
return this._createNewAttachmentFunction;
},
'saveOriginalAttachmentStatusToTransientState': function () {
if (this.transientState().getValue('attachments') == null) {
// console.log("saving attachments to transient state: ts:", this.transientState());
this.transientState().setValue('attachments', {});
MochiKit.Iter.forEach(MochiKit.Base.keys(this._attachments), MochiKit.Base.bind(function(aKey) {
// console.log("saving attachment to transient state:", this._attachments[aKey]);
this.transientState().setValue('attachments' + '.' + aKey, this._attachments[aKey])
}, this))
}
},
'createNewAttachment': function () {
this.saveOriginalAttachmentStatusToTransientState();
return this.createNewAttachmentFunction()(this);
},
'removeAttachment': function(anAttachment) {
this.saveOriginalAttachmentStatusToTransientState();
return Clipperz.Async.callbacks("Record.removeAttachment", [
MochiKit.Base.method(this, 'removeValue', 'attachments' + '.' + anAttachment.reference()),
MochiKit.Base.bind(function () {
this._removeAttachmentIndexDataFunction(anAttachment.reference());
delete this._attachments[anAttachment.reference()]
}, this)
], {trace:false});
},
'attachmentReferences': function () {
var result;
result = Clipperz.Async.callbacks("Record.attachmentReferences", [
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
function (someAttachments) {
var result;
var i,c;
result = [];
c = someAttachments.length;
for (i=0; i<c; i++) {
result.push(Clipperz.Async.collectResults("Record.attachmentReferences - collectResults", {
'_rowObject': MochiKit.Async.succeed,
'_reference': MochiKit.Base.methodcaller('reference'),
// 'label': MochiKit.Base.methodcaller('label'),
// 'favicon': MochiKit.Base.methodcaller('favicon')
}, {trace:false})(someAttachments[i]));
};
return result;
},
Clipperz.Async.collectAll
], {trace:false});
return result;
},
//=========================================================================
'unpackRemoteData': function (someData) {
var result;
var versionKey;
this.setAttachmentServerStatus(someData['attachmentStatus']);
this.setCertificateInfo(someData['certificateInfo']);
for (versionKey in someData['versions']) {
this._versions[versionKey] = new Clipperz.PM.DataModel.Record.Version({
'reference': versionKey,
'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
'remoteData': someData['versions'][versionKey],
'getVersion': MochiKit.Base.method(this, 'getVersion')
})
}
this._currentVersionReference = someData['currentVersion'];
result = Clipperz.PM.DataModel.Record.superclass.unpackRemoteData.apply(this, arguments);
return result;
},
//-------------------------------------------------------------------------
'unpackData': function (someData) {
var result;
result = Clipperz.PM.DataModel.Record.superclass.unpackData.apply(this, arguments);
if (MochiKit.Base.isUndefinedOrNull(result['notes'])) {
result['notes'] = ''
}
return result;
},
//-------------------------------------------------------------------------
'prepareRemoteDataWithKey': function (aKey) {
var deferredResult;
var newVersionKey;
var result;
newVersionKey = Clipperz.PM.Crypto.randomKey();
result = {};
result['attachments'] = MochiKit.Base.keys(this.attachments());
deferredResult = new Clipperz.Async.Deferred("Record.prepareRemoteDataWithKey", {trace:false});
deferredResult.addCallbackList([
Clipperz.Async.collectResults("Record.prepareRemoteDataWithKey - collect results", {
'isBrandNew': MochiKit.Base.method(this, 'isBrandNew'),
'versionHasPendingChanges': [
// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
// MochiKit.Base.methodcaller('hasPendingChanges')
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'hasPendingChanges')
]
}),
Clipperz.Async.or,
Clipperz.Async.deferredIf("Current Version has pending changes", [
MochiKit.Base.method(this, 'createNewRecordVersion'),
MochiKit.Base.methodcaller('prepareRemoteDataWithKey', newVersionKey),
MochiKit.Base.partial(Clipperz.Async.setItem, result, 'currentRecordVersion'),
MochiKit.Base.method(this, 'setCurrentRecordVersionKey', newVersionKey)
], []),
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.prepareRemoteDataWithKey, this, aKey),
MochiKit.Base.partial(Clipperz.Async.setItem, result, 'record'),
MochiKit.Base.partial(MochiKit.Async.succeed, result)
]);
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'fields': function () {
return this.invokeCurrentRecordVersionMethod('fields');
},
'addField': function (someParameters) {
return this.invokeCurrentRecordVersionMethod('addField', someParameters);
},
'removeField': function (someParameters) {
return this.invokeCurrentRecordVersionMethod('removeField', someParameters);
},
// 'sortFieldReference': function (someSortedFieldReferences) {
// return this.invokeCurrentRecordVersionMethod('sortFieldReference', someSortedFieldReferences);
// },
'getFieldsValues': function () {
return this.invokeCurrentRecordVersionMethod('getFieldsValues');
},
'fieldWithLabel': function (aLabel) {
return Clipperz.Async.callbacks("Record.fieldWithLabel", [
MochiKit.Base.method(this, 'fields'),
MochiKit.Base.values,
MochiKit.Base.partial(Clipperz.Async.deferredFilter, function (aField) {
return Clipperz.Async.callbacks("Record.fieldWithLabel - check field label", [
MochiKit.Base.methodcaller('label'),
MochiKit.Base.partial(MochiKit.Base.operator.eq, aLabel)
], {trace:false}, aField);
}),
function (someFilteredResults) {
var result;
switch (someFilteredResults.length) {
case 0:
result = null;
break;
case 1:
result = someFilteredResults[0];
break;
default:
Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more than one result: " + someFilteredResults.length);
result = someFilteredResults[0];
break;
}
return result;
}
], {trace:false});
},
//=========================================================================
'getVersion': function (aVersionReference) {
return Clipperz.Async.callbacks("Record.getVersion", [
MochiKit.Base.method(this, 'getVersions'),
MochiKit.Base.itemgetter(aVersionReference)
], {trace:false});
},
//-------------------------------------------------------------------------
'getVersionKey': function (aVersionReference) {
var deferredResult;
var transientStateKey;
if (typeof(aVersionReference) == 'undefined') {
Clipperz.log("ERROR; getVersionKey aVersionReference is undefined");
}
transientStateKey = 'versionKeys' + '.' + aVersionReference;
if (this.transientState().getValue(transientStateKey) != null) {
deferredResult = MochiKit.Async.succeed(this.transientState().getValue(transientStateKey));
} else {
deferredResult = Clipperz.Async.callbacks("Record.getVersionKey", [
//MochiKit.Base.bind(function () {console.log("VERSION REFERENCE", aVersionReference, this.currentVersionReference()); }, this),
MochiKit.Base.method(this, 'getVersions'),
//function (aValue) { console.log("VERSIONs", aValue); return aValue; },
MochiKit.Base.partial(MochiKit.Base.operator.eq, aVersionReference, this.currentVersionReference()),
Clipperz.Async.deferredIf("getVersionKey for current version", [
MochiKit.Base.method(this, 'getCurrentRecordVersionKey'),
MochiKit.Base.method(this.transientState(), 'setValue', transientStateKey)
],[
MochiKit.Async.fail
])
], {trace:false});
}
return deferredResult;
},
//-------------------------------------------------------------------------
'versions': function () {
return this._versions;
},
'getVersions': function () {
return Clipperz.Async.callbacks("Record.versions", [
MochiKit.Base.method(this, 'getValue', 'fakeKey, just to trigger unpackRemoteData'),
MochiKit.Base.bind(function () { return this._versions; }, this)
], {trace:false});
},
//-------------------------------------------------------------------------
'getCurrentRecordVersion': function () {
return Clipperz.Async.callbacks("Record.getCurrentRecordVersion", [
// MochiKit.Base.method(this, 'getValue', 'fakeKey, just to trigger unpackRemoteData'),
// MochiKit.Base.bind(function () { return this._currentRecordVersion; }, this)
MochiKit.Base.method(this, 'versions'),
MochiKit.Base.itemgetter(this.currentVersionReference()),
Clipperz.Async.deferredIf("The current version is available", [
MochiKit.Async.succeed
], [
MochiKit.Base.method(this, 'getVersions'),
MochiKit.Base.bind(function (someVersions) { return someVersions[this.currentVersionReference()]}, this)
])
], {trace:false});
},
'setCurrentRecordVersion': function (aRecordVersion) {
this._currentVersionReference = aRecordVersion.reference();
},
//.........................................................................
'currentVersionReference': function () {
return this._currentVersionReference;
},
//-------------------------------------------------------------------------
'createNewRecordVersion': function () {
var deferredResult;
if (this.isBrandNew()) {
deferredResult = this.getCurrentRecordVersion();
} else {
var newVersion;
newVersion = new Clipperz.PM.DataModel.Record.Version({
// 'reference': versionKey,
'retrieveKeyFunction': MochiKit.Base.method(this, 'getVersionKey'),
// 'remoteData': {},
'getVersion': MochiKit.Base.method(this, 'getVersion')
})
this._versions[newVersion.reference()] = newVersion;
deferredResult = Clipperz.Async.callbacks("Record.createNewRecordVersion", [
// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
// MochiKit.Base.methodcaller('values'),
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'values'),
MochiKit.Base.method(newVersion, 'setValues'),
Clipperz.Async.collectResults("Record.createNewRecordVersion [collect results]", {
'reference': MochiKit.Base.method(this, 'currentVersionReference'),
'key': MochiKit.Base.method(this, 'getCurrentRecordVersionKey')
}, {trace:false}),
MochiKit.Base.method(newVersion, 'setPreviousVersionReferenceAndKey'),
// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
// MochiKit.Base.method(this, 'revertChanges'),
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'revertChanges'),
MochiKit.Base.method(this, 'setCurrentRecordVersion', newVersion),
MochiKit.Base.partial(MochiKit.Async.succeed, newVersion)
], {trace:false});
}
return deferredResult;
},
//-------------------------------------------------------------------------
'getCurrentRecordVersionKey': function () {
return Clipperz.Async.callbacks("Record.getCurrentRecordVersionKey", [
MochiKit.Base.method(this, 'getValue', 'currentVersionKey'),
Clipperz.Async.deferredIf("currentVersionKey is NOT null", [
MochiKit.Async.succeed
], [
MochiKit.Base.method(this, 'getKey')
])
], {trace:false});
},
'setCurrentRecordVersionKey': function (aValue) {
// TODO: triple check this method!
return Clipperz.Async.callbacks("Record.setCurrentRecordVersionKey", [
MochiKit.Base.method(this, 'setValue', 'currentVersionKey', aValue)
], {trace:false});
},
//-------------------------------------------------------------------------
'invokeCurrentRecordVersionMethod': function (aMethodName, someValues) {
return Clipperz.Async.callbacks("Record.invokeCurrentRecordVersionMethod", [
MochiKit.Base.method(this, 'getCurrentRecordVersion'),
MochiKit.Base.methodcaller(aMethodName, someValues),
], {trace:false});
},
'lazilyinvokeCurrentRecordVersionMethod': function (aMethodName, someValues, defaultResult) {
return Clipperz.Async.callbacks("Record.lazilyinvokeCurrentRecordVersionMethod", [
MochiKit.Base.method(this, 'currentVersionReference'),
Clipperz.Async.deferredIf("versions has been loaded", [
MochiKit.Base.method(this, 'getCurrentRecordVersion'),
MochiKit.Base.methodcaller(aMethodName, someValues),
], [
MochiKit.Base.partial(MochiKit.Async.succeed, defaultResult),
])
], {trace:false});
},
//=========================================================================
'hasPendingChanges': function () {
var deferredResult;
var recordReference = this.reference();
// var self = this;
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.hasPendingChanges", {trace:false});
deferredResult.collectResults({
'super': [
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasPendingChanges, this),
],
'currentVersion': [
MochiKit.Base.method(this, 'hasInitiatedObjectDataStore'),
Clipperz.Async.deferredIf("Record.hasPendingChanges - hasInitiatedObjectDataStore", [
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'hasPendingChanges'),
], [
MochiKit.Base.partial(MochiKit.Async.succeed, false),
]),
],
'directLogins': [
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
Clipperz.Async.collectAll,
Clipperz.Async.or
],
'attachments': [
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasPendingChanges')),
Clipperz.Async.collectAll,
Clipperz.Async.or
]
}, {trace:false});
deferredResult.addCallback(MochiKit.Base.values);
deferredResult.addCallback(MochiKit.Base.bind(function(someValues) {
return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
}, this));
deferredResult.callback();
return deferredResult;
},
//-------------------------------------------------------------------------
'hasPendingChangesWhenBrandNew': function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.hasPendingChangesWhenBrandNew", {trace:false});
deferredResult.collectResults({
'label': [
MochiKit.Base.method(this, 'label'),
MochiKit.Base.partial(MochiKit.Base.operator.ne, '')
],
'notes': [
MochiKit.Base.method(this, 'notes'),
MochiKit.Base.partial(MochiKit.Base.operator.ne, '')
]
});
// deferredResult.addCallback(MochiKit.Base.values);
// deferredResult.addCallback(function(someValues) {
// return MochiKit.Iter.some(someValues, MochiKit.Base.operator.identity);
// });
deferredResult.addCallback(Clipperz.Async.or);
deferredResult.callback();
return deferredResult;
},
//-------------------------------------------------------------------------
'isBrandNewWithNoPendingChanges': function () {
var deferredResult;
if (this.isBrandNew() == false) {
deferredResult = MochiKit.Async.succeed(false);
} else {
deferredResult = Clipperz.Async.callbacks("Record.isBrandNewWithNoPendingChanges", [
MochiKit.Base.method(this, 'hasPendingChanges'),
MochiKit.Base.operator.lognot
], {trace:false});
}
return deferredResult;
},
//=========================================================================
'revertChanges': function () {
// console.log("Revert changes: attachments in transient state", this.transientState().getValue('attachments'));
var deferredResult;
// var recordReference = this.reference();
if (this.isBrandNew() == false) {
/* */
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.revertChanges", {trace:false});
deferredResult.addMethod(this, 'hasPendingChanges');
//deferredResult.addCallback(function (aValue) {
// if (recordReference == 'd620764a656bfd4e1d3758500d5db72e460a0cf729d56ed1a7755b5725c50045') {
// console.log("Record.revertChanges - hasPendingChanges", aValue);
// }
// return aValue;
// return true;
//});
deferredResult.addIf([
MochiKit.Base.method(this, 'cancelRevertedAttachmentsUpload'),
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'revertChanges'),
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.revertChanges, this),
MochiKit.Base.method(this, '_getObjectDataStore'),
], [
MochiKit.Async.succeed
]);
deferredResult.callback();
/* * /
deferredResult = Clipperz.Async.callbacks("Clipperz.PM.DataModel.Record.revertChanges", [
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('revertChanges')),
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'revertChanges'),
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.revertChanges, this),
], {trace:false});
/ * */
} else {
// this.deleteAllCleanTextData();
deferredResult = MochiKit.Async.succeed();
}
return deferredResult;
},
cancelRevertedAttachmentsUpload: function() {
var reference;
var transientStateAttachments = (this.transientState() ? this.transientState().values()['attachments'] : {}) || {};
var savedReferences = MochiKit.Base.keys(transientStateAttachments);
for (reference in this.attachments()) {
if (savedReferences.indexOf(reference) < 0) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'cancelAttachment', this.attachments()[reference]);
}
}
},
//-------------------------------------------------------------------------
'resetTransientState': function (isCommitting) {
// if ((isCommitting == false) && (this.transientState().getValue('directLogins') != null)) {
// this._directLogins = this.transientState().getValue('directLogins');
// }
// console.log("Reset transient state: attachments:", this.transientState().getValue('directLogins'));
return Clipperz.Async.callbacks("Record.resetTransientState", [
//- MochiKit.Base.method(this, 'getCurrentRecordVersion'),
//- MochiKit.Base.methodcaller('resetTransientState'),
// MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'resetTransientState'),
MochiKit.Base.method(this, 'lazilyinvokeCurrentRecordVersionMethod', 'resetTransientState'),
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('resetTransientState')),
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('resetTransientState')),
MochiKit.Base.bind(function () {
if (isCommitting == false) {
if (this.transientState().getValue('directLogins') != null) {
this._directLogins = this.transientState().getValue('directLogins');
}
if (this.transientState().getValue('attachments') != null) {
this._attachments = this.transientState().getValue('attachments');
}
}
}, this),
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.resetTransientState, this, isCommitting)
], {trace:false})
},
//-------------------------------------------------------------------------
'commitTransientState': function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.commitTransientState", {trace:false});
deferredResult.addMethod(this, 'hasPendingChanges');
deferredResult.addIf([
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.commitTransientState, this),
// MochiKit.Base.method(this, 'getCurrentRecordVersion'),
// MochiKit.Base.methodcaller('commitTransientState'),
MochiKit.Base.method(this, 'invokeCurrentRecordVersionMethod', 'commitTransientState'),
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('commitTransientState')),
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('commitTransientState'))
], [
MochiKit.Async.succeed
]);
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'retrieveDirectLoginIndexDataFunction': function () {
return this._retrieveDirectLoginIndexDataFunction;
},
'setDirectLoginIndexDataFunction': function () {
return this._setDirectLoginIndexDataFunction;
},
'removeDirectLoginIndexDataFunction': function () {
return this._removeDirectLoginIndexDataFunction;
},
//=========================================================================
'retrieveAttachmentIndexDataFunction': function () {
return this._retrieveAttachmentIndexDataFunction;
},
'setAttachmentIndexDataFunction': function () {
return this._setAttachmentIndexDataFunction;
},
'removeAttachmentIndexDataFunction': function () {
return this._removeAttachmentIndexDataFunction;
},
//=========================================================================
'deleteAllCleanTextData': function () {
// return Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData.apply(this, arguments);
return Clipperz.Async.callbacks("Record.deleteAllCleanTextData", [
MochiKit.Base.method(this, 'versions'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('deleteAllCleanTextData')),
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.deleteAllCleanTextData, this)
], {trace:false});
},
'hasAnyCleanTextData': function () {
// return Clipperz.PM.DataModel.Record.superclass.hasAnyCleanTextData.apply(this, arguments);
return Clipperz.Async.callbacks("Record.hasAnyCleanTextData", [
Clipperz.Async.collectResults("Record.hasAnyCleanTextData [collect results]", {
'versions': [
MochiKit.Base.method(this, 'versions'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
Clipperz.Async.collectAll
],
'directLogins': [
MochiKit.Base.method(this, 'directLogins'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
Clipperz.Async.collectAll
],
'attachments': [
MochiKit.Base.method(this, 'attachments'),
MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.methodcaller('hasAnyCleanTextData')),
Clipperz.Async.collectAll
],
'super': [
MochiKit.Base.bind(Clipperz.PM.DataModel.Record.superclass.hasAnyCleanTextData, this)
]
}, {trace:false}),
Clipperz.Async.or
])
},
//-------------------------------------------------------------------------
'moveFieldToPosition': function (aFieldReference, aPosition) {
var deferredResult;
var currentFieldValues;
var fromPosition;
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.DataModel.Record.moveFieldToPosition", {trace:false});
deferredResult.addMethod(this, 'getFieldsValues');
deferredResult.addCallback(function (someValues) {
fromPosition = MochiKit.Base.keys(someValues).indexOf(aFieldReference);
return ((fromPosition != -1) && (fromPosition!= aPosition));
});
deferredResult.addIf([
MochiKit.Base.method(this, 'getFieldsValues'),
function (someValues) { currentFieldValues = Clipperz.Base.deepClone(someValues); return currentFieldValues},
MochiKit.Base.method(this, 'fields'), MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'removeField')),
Clipperz.Async.collectAll,
function () {
var currentFieldKeys = MochiKit.Base.keys(currentFieldValues);
currentFieldKeys.splice(aPosition, 0, currentFieldKeys.splice(fromPosition, 1)[0]);
return currentFieldKeys;
},
//function (aValue) { console.log("Sorted Keys", aValue); return aValue; },
MochiKit.Base.partial(MochiKit.Base.map, function (aReference) { return currentFieldValues[aReference]; }),
//function (aValue) { console.log("Sorted Field values", aValue); return aValue; },
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addField')),
Clipperz.Async.collectAll,
], [
MochiKit.Async.succeed
]);
deferredResult.callback();
return deferredResult;
},
//=========================================================================
'setUpWithRecord': function (aRecord) {
return Clipperz.Async.callbacks("Record.setUpWithRecord", [
MochiKit.Base.method(aRecord, 'label'),
MochiKit.Base.bind(function (aLabel) {
return this.setLabel(aLabel + " - copy");
}, this),
MochiKit.Base.method(aRecord, 'fullLabel'),
MochiKit.Base.method(aRecord, 'extractTagsFromFullLabel'),
//function (someValues) { console.log(">>> TAGS", someValues); return someValues; },
MochiKit.Base.keys,
MochiKit.Base.partial(MochiKit.Base.filter, Clipperz.PM.DataModel.Record.isRegularTag),
MochiKit.Base.partial(MochiKit.Base.filter, Clipperz.PM.DataModel.Record.isNotImportTag),
function (tags) { return MochiKit.Iter.reduce(function (result, value) { result[value] = true; return result; }, tags, {}); },
//function (someValues) { console.log("<<< TAGS", someValues); return someValues; },
MochiKit.Base.method(this, 'updateTags'),
MochiKit.Base.method(aRecord, 'notes'),
MochiKit.Base.method(this, 'setNotes'),
MochiKit.Base.method(aRecord, 'getFieldsValues'), MochiKit.Base.values,
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addField')),
Clipperz.Async.collectAll,
MochiKit.Base.method(aRecord, 'directLogins'), MochiKit.Base.values,
//function (aValue) { console.log("-> SETUP WITH RECORD: DirectLogin Values", aValue); return aValue; },
// TODO: possibly broken implementation of direct login cloning
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'bindDirectLogin')),
//function (aValue) { console.log("-> DirectLogin Values", aValue); return aValue; },
// Clipperz.Async.collectAll,
// TODO: attachments
MochiKit.Base.bind(function () { return this; }, this)
], {trace:false});
},
'directLoginWithJsonData': function (someData) {
var result;
result = new Clipperz.PM.DataModel.DirectLogin({'record': this});
return result;
},
'setUpWithJSON': function(data, labelPostfix) {
return Clipperz.Async.callbacks("Record.setUpWithJSON", [
// TODO: proper tag handling
MochiKit.Base.method(this,'setLabel', data['label'] + ((labelPostfix) ? labelPostfix : '')),
MochiKit.Base.method(this,'setNotes', data['data']['notes']),
// TODO: check whether fields' order is kept or not
MochiKit.Base.partial(MochiKit.Base.values, data['currentVersion']['fields']),
MochiKit.Base.partial(MochiKit.Base.map, MochiKit.Base.method(this, 'addField')),
Clipperz.Async.collectAll,
MochiKit.Base.partial(MochiKit.Async.succeed, this),
], {trace:false});
},
//=========================================================================
'exportDirectLogins': function() {
var result;
var directLoginsObject = this.directLogins();
if (MochiKit.Base.keys(directLoginsObject).length == 0) {
result = {};
} else {
var callbackObject = Object.keys(directLoginsObject).reduce(function(previous, current) {
previous[current] = MochiKit.Base.method( directLoginsObject[current], 'serializedData' );
return previous;
}, {});
result = Clipperz.Async.collectResults("Record.exportDirectLogins", callbackObject,{trace:false})();
}
return result;
},
'export': function() {
var deferredResult;
var label;
var data;
var currentVersion;
// var directLogins;
var currentVersionObject;
data = {};
currentVersion = {};
// directLogins = {};
deferredResult = new Clipperz.Async.Deferred('Record.export', {trace:false});
deferredResult.addMethod(this, 'getCurrentRecordVersion');
deferredResult.addCallback(function(recordVersionIn) { currentVersionObject = recordVersionIn; })
deferredResult.addMethod(this, 'fullLabel');
deferredResult.addMethod(this, function(labelIn) {label = labelIn});
deferredResult.addMethod(this, 'exportDirectLogins');
deferredResult.addCallback(function(directLoginsIn) { data['directLogins'] = directLoginsIn; });
deferredResult.addCallback(function() { return currentVersionObject.getKey(); }),
// deferredResult.addMethod(this,function(keyIn) { data['currentVersionKey'] = keyIn; });
deferredResult.addMethod(this, 'notes');
deferredResult.addMethod(this, function(notesIn) { data['notes'] = notesIn; });
// deferredResult.addMethod(this, function() { currentVersion['reference'] = this.currentVersionReference(); });
deferredResult.addCallback(function() { return currentVersionObject.exportFields(); }),
deferredResult.addCallback(function(fieldsIn) { currentVersion['fields'] = fieldsIn; });
deferredResult.addMethod(this, function() {
return {
'label': label,
'data': data,
'currentVersion': currentVersion
};
});
deferredResult.callback();
return deferredResult;
},
//=========================================================================
__syntaxFix__: "syntax fix"
});
Clipperz.PM.DataModel.Record.defaultCardInfo = {
'_rowObject': MochiKit.Async.succeed,
'_reference': MochiKit.Base.methodcaller('reference'),
'_searchableContent': MochiKit.Base.methodcaller('searchableContent'),
'_accessDate': MochiKit.Base.methodcaller('accessDate'),
'_isArchived': MochiKit.Base.methodcaller('isArchived'),
'_isBrandNew': MochiKit.Base.methodcaller('isBrandNew'),
'label': MochiKit.Base.methodcaller('label'),
'favicon': MochiKit.Base.methodcaller('favicon'),
'attachmentsCount': MochiKit.Base.methodcaller('attachmentsCount'),
'hasBeenCertified': MochiKit.Base.methodcaller('hasBeenCertified'),
};
Clipperz.PM.DataModel.Record.defaultSearchField = '_searchableContent';
Clipperz.PM.DataModel.Record.tagChar = '\uE009';
Clipperz.PM.DataModel.Record.specialTagChar = '\uE010';
//Clipperz.PM.DataModel.Record.tagSpace = '\uE011';
Clipperz.PM.DataModel.Record.specialTagsConstructor = function (aTag) {
return Clipperz.PM.DataModel.Record.specialTagChar + aTag; //.replace(/\s/g, Clipperz.PM.DataModel.Record.tagSpace);
}
Clipperz.PM.DataModel.Record.archivedTag = Clipperz.PM.DataModel.Record.specialTagsConstructor('ARCH');
Clipperz.PM.DataModel.Record.certifiedTag = Clipperz.PM.DataModel.Record.specialTagsConstructor('CERT');
Clipperz.PM.DataModel.Record.regExpForTag = function (aTag) {
return new RegExp('\\' + Clipperz.PM.DataModel.Record.tagChar + aTag, 'g');
};
Clipperz.PM.DataModel.Record.regExpForNoTag = function () {
return new RegExp('^((?!\\' + Clipperz.PM.DataModel.Record.tagChar + '[^' + Clipperz.PM.DataModel.Record.specialTagChar + ']).)*$', 'g');
}
Clipperz.PM.DataModel.Record.isSpecialTag = function (aTag) {
return aTag.indexOf(Clipperz.PM.DataModel.Record.specialTagChar) == 0;
};
Clipperz.PM.DataModel.Record.isRegularTag = function (aTag) {
return !Clipperz.PM.DataModel.Record.isSpecialTag(aTag);
};
Clipperz.PM.DataModel.Record.isNotImportTag = function (aTag) {
return aTag.indexOf('Import_') != 0;
};
Clipperz.PM.DataModel.Record.regExpForSearch = function (aSearch) {
return new RegExp(aSearch.replace(/[^A-Za-z0-9]/g, '\\$&'), 'i');
};
// Slightly more readable: http://rubular.com/r/4O8xGEsLgw
Clipperz.PM.DataModel.Record.tagRegExp = new RegExp('\\' + Clipperz.PM.DataModel.Record.tagChar + '(' + Clipperz.PM.DataModel.Record.specialTagChar + '?[^'+Clipperz.PM.DataModel.Record.tagChar+']+?)\\s*(?='+Clipperz.PM.DataModel.Record.tagChar+'|$)', 'g');
Clipperz.PM.DataModel.Record.trimSpacesRegExp = new RegExp('^\\s+|\\s+$', 'g');
Clipperz.PM.DataModel.Record.extractLabelFromFullLabel = function (aValue) {
var value;
value = aValue;
value = value.replace(Clipperz.PM.DataModel.Record.tagRegExp, '');
value = value.replace(Clipperz.PM.DataModel.Record.trimSpacesRegExp, '');
return value;
};
Clipperz.PM.DataModel.Record.extractTagsFromFullLabel = function (aLabel) {
var tagRegEx;
var result;
var match;
result = {};
tagRegEx = Clipperz.PM.DataModel.Record.tagRegExp;
match = tagRegEx.exec(aLabel);
while (match != null) {
result[match[1]] = true;
match = tagRegEx.exec(aLabel);
}
return result;
};
Clipperz.PM.DataModel.Record.labelContainsTag = function (aLabel, aTag) {
return MochiKit.Iter.some(
MochiKit.Base.keys(Clipperz.PM.DataModel.Record.extractTagsFromFullLabel(aLabel)),
MochiKit.Base.partial(MochiKit.Base.operator.eq, aTag)
);
};
Clipperz.PM.DataModel.Record.labelContainsArchiveTag = function (aLabel) {
return Clipperz.PM.DataModel.Record.labelContainsTag(aLabel, Clipperz.PM.DataModel.Record.archivedTag);
};