1
0
mirror of http://git.whoc.org.uk/git/password-manager.git synced 2025-10-23 16:57:36 +02:00

BTC Certificate feature

Added the ability to register card content on the BTC blockchain in order to certify its existance/content
This commit is contained in:
Giulio Cesare Solaroli
2016-03-29 11:45:50 +02:00
parent f84d05240b
commit fbcd02dffd
102 changed files with 23275 additions and 8645 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,138 @@
{
"IcoMoonType": "selection",
"icons": [
{
"icon": {
"paths": [
"M108.018 534.899l148.392 76.747h576.168l-190.68-98.62h-385.488l-208.41-107.792v98.62l7.467 3.858z",
"M48.001 63.785v242.833l208.41 107.788h576.168l-536.16-277.295h593.28v768.365h-768.387v-166.462l135.098 69.865h576.168l-207.951-107.554h-368.217l-208.41-107.799v385.279h915.005v-915.020z"
],
"attrs": [],
"isMulticolor": false,
"grid": 0,
"tags": [
"CLIPPERZ_SOLO_LOGO-nero"
],
"width": 1024
},
"attrs": [],
"properties": {
"order": 42,
"id": 0,
"prevSize": 32,
"code": 59650,
"name": "CLIPPERZ_SOLO_LOGO-nero",
"ligatures": "clipperz"
},
"setIdx": 0,
"setId": 13,
"iconIdx": 0
},
{
"icon": {
"paths": [
"M786.286 512l78.857 77.143q17.143 16 11.429 40-6.857 23.429-29.714 29.143l-107.429 27.429 30.286 106.286q6.857 23.429-10.857 40-16.571 17.714-40 10.857l-106.286-30.286-27.429 107.429q-5.714 22.857-29.143 29.714-6.857 1.143-10.857 1.143-17.714 0-29.143-12.571l-77.143-78.857-77.143 78.857q-16 17.143-40 11.429-23.429-6.286-29.143-29.714l-27.429-107.429-106.286 30.286q-23.429 6.857-40-10.857-17.714-16.571-10.857-40l30.286-106.286-107.429-27.429q-22.857-5.714-29.714-29.143-5.714-24 11.429-40l78.857-77.143-78.857-77.143q-17.143-16-11.429-40 6.857-23.429 29.714-29.143l107.429-27.429-30.286-106.286q-6.857-23.429 10.857-40 16.571-17.714 40-10.857l106.286 30.286 27.429-107.429q5.714-23.429 29.143-29.143 23.429-6.857 40 10.857l77.143 79.429 77.143-79.429q16.571-17.143 40-10.857 23.429 5.714 29.143 29.143l27.429 107.429 106.286-30.286q23.429-6.857 40 10.857 17.714 16.571 10.857 40l-30.286 106.286 107.429 27.429q22.857 5.714 29.714 29.143 5.714 24-11.429 40z"
],
"attrs": [],
"isMulticolor": false,
"width": 878,
"tags": [
"certificate"
],
"grid": 0
},
"attrs": [],
"properties": {
"order": 1,
"id": 0,
"prevSize": 32,
"code": 59648,
"name": "certificate",
"ligatures": "certificate"
},
"setIdx": 2,
"setId": 11,
"iconIdx": 0
},
{
"icon": {
"paths": [
"M2.259 734.824h279.462v286.917h-279.462v-286.917z",
"M2.259 2.259h279.462v286.917h-279.462v-286.917z",
"M2.259 368.519h279.462v286.917h-279.462v-286.917z"
],
"width": 284,
"grid": 0,
"tags": [
"commands"
],
"attrs": []
},
"attrs": [],
"properties": {
"order": 18,
"id": 1,
"prevSize": 32,
"code": 58899,
"name": "commands",
"ligatures": "commands"
},
"setIdx": 8,
"setId": 4,
"iconIdx": 0
},
{
"icon": {
"paths": [
"M667.586 345.635v-338.544h-268.055v338.544l-299.967-121.263-90.486 257.986 310.037 104.527-198.134 262.808 224.514 164.237 184.802-279.685 203.382 279.685 221.11-164.237-203.382-262.808 319.397-107.080-90.486-257.986zM1220.574 2319.317z"
],
"width": 1080,
"grid": 0,
"tags": [
"logo"
],
"attrs": []
},
"attrs": [],
"properties": {
"order": 4,
"id": 0,
"prevSize": 32,
"code": 58884,
"name": "logo",
"ligatures": "qibberz"
},
"setIdx": 8,
"setId": 4,
"iconIdx": 1
},
{
"icon": {
"paths": [
"M429.696 257.6c54.88 0 68.608 27.424 105.152 22.848 36.576-4.576 4.576-68.576-27.424-114.272s-105.12-22.848-160 9.152c-54.848 32-346.272 209.152-346.272 209.152h254.848c0-0.032 118.848-126.88 173.696-126.88zM0 417.568v416c0 35.328 28.64 64 64 64 0 0 246.72 0 514.656 0 48.064 0 92-149.344 141.344-149.344 42.848 0 75.072 149.344 117.312 149.344 284.576 0 538.656 0 538.656 0 35.36 0 64.032-28.672 64.032-64v-416h-1440zM576 705.6c0 52.992-43.008 96-96 96h-288c-53.024 0-96-43.008-96-96v-96c0-53.024 42.976-96 96-96h288c52.992 0 96 42.976 96 96v96zM1344 705.6c0 52.992-43.008 96-96 96h-288c-53.024 0-96-43.008-96-96v-96c0-53.024 42.976-96 96-96h288c52.992 0 96 42.976 96 96v96zM905.12 280.448c36.576 4.576 50.272-22.848 105.152-22.848 54.848 0 173.728 126.848 173.728 126.848h254.848c0 0-291.424-177.152-346.272-209.152-54.88-32-128-54.848-160-9.152s-64 109.728-27.456 114.304z"
],
"attrs": [],
"isMulticolor": false,
"width": 1440,
"tags": [
"3d glasses",
"glasses"
],
"grid": 32
},
"attrs": [],
"properties": {
"order": 1,
"id": 0,
"prevSize": 32,
"code": 59649,
"name": "3dglasses",
"ligatures": "preview"
},
"setIdx": 1,
"setId": 12,
"iconIdx": 0
},
{
"icon": {
"paths": [
@@ -26,7 +158,7 @@
"name": "log-out",
"ligatures": "logout"
},
"setIdx": 0,
"setIdx": 3,
"setId": 10,
"iconIdx": 0
},
@@ -55,7 +187,7 @@
"name": "mail",
"ligatures": "email"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 4
},
@@ -80,7 +212,7 @@
"name": "paperclip",
"ligatures": "attachment"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 8
},
@@ -106,7 +238,7 @@
"name": "popup",
"ligatures": "url, direct login"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 35
},
@@ -132,7 +264,7 @@
"name": "plus",
"ligatures": "add new field, create new OTP"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 125
},
@@ -160,7 +292,7 @@
"name": "cross",
"ligatures": "remove field, remove tag, remove OTP"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 126
},
@@ -186,7 +318,7 @@
"name": "plus3",
"ligatures": "add card"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 128
},
@@ -212,7 +344,7 @@
"name": "arrow-left",
"ligatures": "back"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 205
},
@@ -238,7 +370,7 @@
"name": "arrow-right",
"ligatures": "show detail"
},
"setIdx": 8,
"setIdx": 11,
"setId": 0,
"iconIdx": 208
},
@@ -268,7 +400,7 @@
"name": "security",
"ligatures": "lock"
},
"setIdx": 1,
"setIdx": 4,
"setId": 9,
"iconIdx": 293
},
@@ -298,7 +430,7 @@
"name": "label",
"ligatures": "label"
},
"setIdx": 1,
"setIdx": 4,
"setId": 9,
"iconIdx": 678
},
@@ -325,7 +457,7 @@
"name": "gear",
"ligatures": "options"
},
"setIdx": 2,
"setIdx": 5,
"setId": 8,
"iconIdx": 64
},
@@ -358,7 +490,7 @@
"ligatures": "other file",
"name": "file-empty"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 36
},
@@ -392,7 +524,7 @@
"ligatures": "text file",
"name": "file-text2"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 38
},
@@ -423,7 +555,7 @@
"ligatures": "image file",
"name": "file-picture"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 39
},
@@ -454,7 +586,7 @@
"ligatures": "audio file",
"name": "file-music"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 40
},
@@ -485,7 +617,7 @@
"ligatures": "video file",
"name": "file-video"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 42
},
@@ -525,7 +657,7 @@
"ligatures": "archive file",
"name": "file-zip"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 43
},
@@ -554,7 +686,7 @@
"ligatures": "password generator",
"name": "key"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 141
},
@@ -583,7 +715,7 @@
"ligatures": "view password",
"name": "eye"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 206
},
@@ -617,7 +749,7 @@
"ligatures": "generate password",
"name": "loop2"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 302
},
@@ -643,10 +775,10 @@
"order": 32,
"prevSize": 32,
"code": 59958,
"ligatures": "set password",
"ligatures": "set password, download",
"name": "arrow-down"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 310
},
@@ -675,7 +807,7 @@
"ligatures": "pdf file",
"name": "file-pdf"
},
"setIdx": 3,
"setIdx": 6,
"setId": 7,
"iconIdx": 474
},
@@ -701,7 +833,7 @@
"name": "tag",
"ligatures": "tag"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 0
},
@@ -728,7 +860,7 @@
"name": "tags",
"ligatures": "tags"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 1
},
@@ -755,7 +887,7 @@
"name": "clock",
"ligatures": "recent"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 2
},
@@ -784,7 +916,7 @@
"name": "spinner",
"ligatures": "loading"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 3
},
@@ -812,7 +944,7 @@
"name": "search",
"ligatures": "search"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 4
},
@@ -840,7 +972,7 @@
"name": "locked",
"ligatures": "locked"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 6
},
@@ -866,7 +998,7 @@
"name": "unlocked",
"ligatures": "unlocked"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 7
},
@@ -895,7 +1027,7 @@
"name": "menu",
"ligatures": "menu"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 8
},
@@ -924,7 +1056,7 @@
"name": "close",
"ligatures": "failure, failed, delete, clear, cancel, close"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 9
},
@@ -953,61 +1085,9 @@
"name": "checkmark",
"ligatures": "done, ok, save"
},
"setIdx": 4,
"setIdx": 7,
"setId": 6,
"iconIdx": 10
},
{
"icon": {
"paths": [
"M2.259 734.824h279.462v286.917h-279.462v-286.917z",
"M2.259 2.259h279.462v286.917h-279.462v-286.917z",
"M2.259 368.519h279.462v286.917h-279.462v-286.917z"
],
"width": 284,
"grid": 0,
"tags": [
"commands"
],
"attrs": []
},
"attrs": [],
"properties": {
"order": 18,
"id": 1,
"prevSize": 24,
"code": 58899,
"name": "commands",
"ligatures": "commands"
},
"setIdx": 5,
"setId": 4,
"iconIdx": 0
},
{
"icon": {
"paths": [
"M667.586 345.635v-338.544h-268.055v338.544l-299.967-121.263-90.486 257.986 310.037 104.527-198.134 262.808 224.514 164.237 184.802-279.685 203.382 279.685 221.11-164.237-203.382-262.808 319.397-107.080-90.486-257.986zM1220.574 2319.317z"
],
"width": 1080,
"grid": 0,
"tags": [
"logo"
],
"attrs": []
},
"attrs": [],
"properties": {
"order": 4,
"id": 0,
"prevSize": 24,
"code": 58884,
"name": "logo",
"ligatures": "clipperz"
},
"setIdx": 5,
"setId": 4,
"iconIdx": 1
}
],
"height": 1024,

File diff suppressed because one or more lines are too long

View File

@@ -69,7 +69,7 @@ Clipperz.Base.extend(Clipperz.Async.Deferred, MochiKit.Async.Deferred, {
if (! (aResult instanceof MochiKit.Async.CancelledError)) {
message = "ERROR [" + deferredName + "]";
} else {
message = "CANCELLED - " + deferredName;
message = "CANCELED - " + deferredName;
}
Clipperz.log(message, aResult);

View File

@@ -497,7 +497,7 @@ var state = new array(4);
var count = new array(2);
count[0] = 0;
count[1] = 0;
var buffer = new array(64);
var md5_buffer = new array(64);
var transformBuffer = new array(16);
var digestBits = new array(16);
@@ -683,9 +683,9 @@ function md5_update(b) {
count[0] -= 0xFFFFFFFF + 1;
count[0] += 8;
}
buffer[index] = and(b, 0xff);
md5_buffer[index] = and(b, 0xff);
if (index >= 63) {
transform(buffer, 0);
transform(md5_buffer, 0);
}
}

View File

@@ -497,9 +497,11 @@ Clipperz.PM.Connection.SRP['1.0'].prototype = MochiKit.Base.update(new Clipperz.
Clipperz.log(">>> Connection.messageExceptionHandler: " + anError.message, anError);
if (anError instanceof MochiKit.Async.CancelledError) {
result = anError;
} else if (typeof(anError.req) == 'undefined') {
result = anError;
} else {
var errorPayload;
errorPayload = Clipperz.Base.evalJSON(anError.req.responseText);
if ((errorPayload.message == 'Trying to communicate without an active connection') ||
(errorPayload.message == 'No tollManager available for current session') ||

View File

@@ -104,18 +104,23 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Attachment, Object, {
},
'setFile': function (aFile) {
var hashValue;
//console.log("ATTACHMENT SET FILE", aFile);
this._file = aFile || null;
// hashValue = aFile ? npm.bitcoin.address.toBase58Check(npm.bitcoin.crypto.hash160(aFile), NETWORK.pubKeyHash) : null;
/* These ones will disappear when the application is closed */
this._name = aFile ? aFile['name'] : null;
this._contentType = aFile ? aFile['type'] : null;
this._size = aFile ? aFile['size'] : null;
// this._hash = hashValue;
/* These ones will be saved in the Record */
return Clipperz.Async.callbacks("Attachment.setFile", [
MochiKit.Base.method(this, 'setValue', 'name', aFile['name']),
MochiKit.Base.method(this, 'setValue', 'contentType', aFile['type']),
MochiKit.Base.method(this, 'setValue', 'size', aFile['size']),
// MochiKit.Base.method(this, 'setValue', 'hash', hashValue),
MochiKit.Base.partial(MochiKit.Async.succeed, this),
], {trace:false});
@@ -135,6 +140,10 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Attachment, Object, {
return this.getValue('size');
},
'hash': function () {
return this.getValue('hash');
},
'metadata': function () {
var deferredResult;
@@ -143,6 +152,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Attachment, Object, {
'name': MochiKit.Base.method(this, 'name'),
'type': MochiKit.Base.method(this, 'contentType'),
'size': MochiKit.Base.method(this, 'size'),
'hash': MochiKit.Base.method(this, 'hash'),
}, {trace:false});
deferredResult.callback();
@@ -346,6 +356,30 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Attachment, Object, {
},
//=========================================================================
/*
computeAttachmentCertificateInfo: function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Attachment.computeAttachmentCertificateInfo", {trace:false});
deferredResult.collectResults({
'name': MochiKit.Base.method(this, 'name'),
'contentType': MochiKit.Base.method(this, 'contentType'),
'size': MochiKit.Base.method(this, 'size'),
'hash': [
MochiKit.Base.method(attachmentController, 'getAttachment', this),
function (aValue) { console.log("STEP 1", aValue); return aValue; },
// npm.bitcoin.crypto.hash160,
//function (aValue) { console.log("STEP 2", aValue); return aValue; },
// function (anHash160) { return npm.bitcoin.address.toBase58Check(anHash160, NETWORK.pubKeyHash);}
function () { return MochiKit.Async.succeed("bingo"); }
]
});
deferredResult.callback();
return deferredResult;
},
*/
//=========================================================================
__syntaxFix__: "syntax fix"
});

View File

@@ -69,16 +69,17 @@ Clipperz.PM.DataModel.EncryptedRemoteObject = function(args) {
//
// Basic data workflow
// =======================
//
// # READ
// getRemoteData
// unpackRemoteData
// getDecryptedData [encryptedDataKeypath, encryptedVersionKeypath]
// unpackData
//
//
// ?? packData
// ?? encryptDataWithKey
// ?? packRemoteData [encryptedDataKeypath (?), encryptedVersionKeypath (?)]
//
// # WRITE
// prepareRemoteDataWithKey
// packData
// [encryptDataWithKey]
// packRemoteData [encryptedDataKeypath (?), encryptedVersionKeypath (?)]
//
Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(null, {
@@ -284,7 +285,18 @@ Clipperz.PM.DataModel.EncryptedRemoteObject.prototype = MochiKit.Base.update(nul
//-------------------------------------------------------------------------
'setValue': function(aKey, aValue) {
'getOrSetValue': function (aKey, aGetValueFunction) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.setValue", {trace:false});
deferredResult.addMethod(this, '_getObjectDataStore');
deferredResult.addCallback(MochiKit.Base.methodcaller('deferredGetOrSet', aKey, aGetValueFunction));
deferredResult.callback();
return deferredResult;
},
'setValue': function (aKey, aValue) {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("EncryptedRemoteObject.setValue", {trace:false});

View File

@@ -183,7 +183,7 @@ Clipperz.PM.DataModel.OneTimePassword.isValidOneTimePasswordValue = function(aPa
var result;
// "yaxx k7ww - f8y6 tqz5 - 58b6 th44 - 9cwv q0fg"
if (aPassword.replace(/[\s\-]/g, '').length == 32) {
if (aPassword.replace(/[^a-zA-Z0-9]/g, '').length == 32) {
try {
var passwordByteArray;

View File

@@ -49,6 +49,7 @@ Clipperz.PM.DataModel.Record = function(args) {
this._createNewAttachmentFunction = args.createNewAttachmentFunction || null;
this._attachmentServerStatus = {};
this._certificateInfo = null;
this._tags = [];
this._directLogins = {};
@@ -63,7 +64,6 @@ Clipperz.PM.DataModel.Record = function(args) {
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();
@@ -85,7 +85,288 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
// '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 () {
@@ -149,6 +430,33 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
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 () {
@@ -302,8 +610,32 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
//=========================================================================
attachmentsCount: function() {
return MochiKit.Base.keys(this.attachments()).length;
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});
},
//=========================================================================
@@ -486,6 +818,26 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
//=========================================================================
// 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;
},
@@ -527,7 +879,6 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
delete this._attachments[anAttachment.reference()]
}, this)
], {trace:false});
},
'attachmentReferences': function () {
@@ -563,16 +914,11 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
'unpackRemoteData': function (someData) {
var result;
/*
this._currentRecordVersion = new Clipperz.PM.DataModel.Record.Version({
'reference': someData['currentVersion']['reference'],
'retrieveKeyFunction': MochiKit.Base.method(this, 'getCurrentRecordVersionKey'),
'remoteData': someData['currentVersion'],
});
*/
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,
@@ -581,8 +927,6 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
'getVersion': MochiKit.Base.method(this, 'getVersion')
})
}
// this._currentVersionReference = someData['currentVersion']['reference'];
this._currentVersionReference = someData['currentVersion'];
result = Clipperz.PM.DataModel.Record.superclass.unpackRemoteData.apply(this, arguments);
@@ -596,11 +940,10 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.Record, Clipperz.PM.DataModel.Encrypt
var result;
result = Clipperz.PM.DataModel.Record.superclass.unpackData.apply(this, arguments);
if (MochiKit.Base.isUndefinedOrNull(result['notes'])) {
result['notes'] = ''
}
return result;
},
@@ -1218,6 +1561,12 @@ Clipperz.log("Warning: Record.fieldWithLabel('" + aLabel + "') is returning more
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'),
@@ -1333,6 +1682,7 @@ Clipperz.PM.DataModel.Record.defaultCardInfo = {
'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';
@@ -1342,7 +1692,9 @@ Clipperz.PM.DataModel.Record.specialTagChar = '\uE010';
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.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');
};
@@ -1355,6 +1707,9 @@ Clipperz.PM.DataModel.Record.isSpecialTag = function (aTag) {
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');
};

View File

@@ -456,27 +456,15 @@ Clipperz.log("SKIPPING record " + reference + " as there are no stats associated
//-------------------------------------------------------------------------
'createNewAttachment': function (aRecord) {
// TODO:
// var newDirectLogin;
var newAttachment;
// var newDirectLoginIndexValue;
var newAttachmentIndexValue;
// newDirectLogin = new Clipperz.PM.DataModel.DirectLogin({record:aRecord});
newAttachment = new Clipperz.PM.DataModel.Attachment({'record':aRecord});
// newDirectLoginIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.directLoginsIndex()))) + 1;
newAttachmentIndexValue = MochiKit.Base.listMax(MochiKit.Base.map(function (aValue) { return aValue * 1; }, MochiKit.Base.values(this.attachmentsIndex()))) + 1;
// this.transientState().setValue('newDirectLoginReferences' + '.' + newDirectLogin.reference(), newDirectLogin);
this.transientState().setValue('newAttachmentReferences' + '.' + newAttachment.reference(), newAttachment);
// this.directLoginsIndex()[newDirectLogin.reference()] = newDirectLoginIndexValue;
this.attachmentsIndex()[newAttachment.reference()] = newAttachmentIndexValue;
// this.directLoginsData().setValue(this.directLoginsIndex()[newDirectLogin.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
this.attachmentsData().setValue(this.attachmentsIndex()[newAttachment.reference()], {'record': this.recordsIndex()[aRecord.reference()]});
// return newDirectLogin;
return newAttachment;
},

View File

@@ -0,0 +1,174 @@
/*
Copyright 2008-2015 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/.
*/
try { if (typeof(Clipperz.PM.DataModel.User) == 'undefined') { throw ""; }} catch (e) {
throw "Clipperz.PM.DataModel.User.Header.Wallet depends on Clipperz.PM.DataModel.User!";
}
if (typeof(Clipperz.PM.DataModel.User.Header) == 'undefined') { Clipperz.PM.DataModel.User.Header = {}; }
Clipperz.PM.DataModel.User.Header.Wallet = function(args) {
Clipperz.PM.DataModel.User.Header.Wallet.superclass.constructor.apply(this, arguments);
return this;
}
Clipperz.Base.extend(Clipperz.PM.DataModel.User.Header.Wallet, Clipperz.PM.DataModel.EncryptedRemoteObject, {
'toString': function() {
return "Clipperz.PM.DataModel.User.Header.Wallet";
},
//-------------------------------------------------------------------------
getRandomSeed: function () {
return Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(512/8).toHexString().substring(2);
},
getWalletSeed: function () {
return this.getOrSetValue('walletSeed', MochiKit.Base.method(this, 'getRandomSeed'));
},
userMasterKey: function () {
return Clipperz.Async.callbacks("User.Header.Wallet.userMasterKey", [
MochiKit.Base.method(this, 'getWalletSeed'),
function (aValue) {
return npm.bitcoin.HDNode.fromSeedHex(aValue, NETWORK);
}
], {trace:false});
},
getNextID: function (aKeyPath) {
//console.log("WALLET - getNextID", aKeyPath);
return Clipperz.Async.callbacks("User.Header.Wallet.userMasterKey", [
MochiKit.Base.method(this, 'getValue', aKeyPath),
function (aValue) {
var result;
if (aValue == null) {
result = 0;
} else {
result = aValue + 1;
}
return result;
},
MochiKit.Base.method(this, 'setValue', aKeyPath)
], {trace:false});
},
getKeyForDocumentWithID: function (anID) {
return Clipperz.Async.callbacks("User.Header.Wallet.getKeyForDocumentWithID", [
MochiKit.Base.method(this, 'userMasterKey'),
MochiKit.Base.methodcaller('derive', 0),
MochiKit.Base.methodcaller('derive', anID)
], {trace:false});
},
// getPublicKeyForDocumentWithID: function (anID) {
// return Clipperz.Async.callbacks("User.Header.Wallet.getPublicKeyForDocumentWithID", [
// MochiKit.Base.method(this, 'getKeyForDocumentWithID', anID),
// MochiKit.Base.methodcaller('getPublicKeyBuffer'),
// MochiKit.Base.methodcaller('toString', 'hex'),
// ], {trace:false});
// },
// getAddressForDocumentWithID: function (anID) {
//console.log("WALLET - address for document with ID", anID);
// return Clipperz.Async.callbacks("User.Header.Wallet.getAddressForDocumentWithID", [
// MochiKit.Base.method(this, 'getKeyForDocumentWithID', anID),
// MochiKit.Base.methodcaller('getAddress')
// ], {trace:false});
// },
//#########################################################################
// prepareRemoteDataWithKey
// packData
// encryptDataWithKey
// packRemoteData [encryptedDataKeypath (?), encryptedVersionKeypath (?)]
/*
prepareRemoteDataWithKey: function (aKey) {
console.log("USER HEADER WALLET - prepareRemoteDataWithKey", aKey);
//console.log("super", Clipperz.PM.DataModel.User.Header.Wallet.superclass.prepareRemoteDataWithKey);
return Clipperz.PM.DataModel.User.Header.Wallet.superclass.prepareRemoteDataWithKey.apply(this, arguments);
},
packData: function (someData) { // ++
console.log("USER HEADER WALLET - packData", someData);
return Clipperz.PM.DataModel.User.Header.Wallet.superclass.packData.apply(this, arguments);
},
packRemoteData: function (someData) {
console.log("USER HEADER WALLET - packRemoteData", someData);
return Clipperz.PM.DataModel.User.Header.Wallet.superclass.packRemoteData.apply(this, arguments);
},
*/
//#########################################################################
/*
'mergePreferences': function(somePreferences, someOtherPreferences) {
var result;
result = new Clipperz.KeyValueObjectStore();
result.setValues(MochiKit.Base.updatetree(
Clipperz.Base.deepClone(someOtherPreferences),
somePreferences
));
return result;
},
'mergeDefaultPreferences': function(somePreferences) {
return this.mergePreferences(somePreferences, Clipperz.PM.DataModel.User.Header.Preferences.defaultPreferences);
},
'mergeUserPreferences': function(somePreferences) {
return this.mergePreferences(somePreferences, this._objectDataStore.values());
},
'getPreferences': function() {
return Clipperz.Async.callbacks("User.Header.Preferences.getPreferences", [
MochiKit.Base.method(this, 'values'),
MochiKit.Base.method(this, 'mergeDefaultPreferences')
], {trace:false});
},
'getPreference': function(aKey) {
return Clipperz.Async.callbacks("User.Header.Preferences.getPreference", [
MochiKit.Base.method(this, 'getPreferences'),
MochiKit.Base.methodcaller('getValue', aKey)
], {trace:false});
},
'setPreferences': function(anObject) {
return Clipperz.Async.callbacks("User.Header.Preferences.setPreferences", [
MochiKit.Base.method(this, 'mergeUserPreferences', anObject),
MochiKit.Base.methodcaller('values'),
MochiKit.Base.method(this, 'setValues')
], {trace:false});
},
*/
//=========================================================================
__syntaxFix__: "syntax fix"
});

View File

@@ -52,10 +52,6 @@ Clipperz.PM.DataModel.User = function (args) {
this._deferredLocks = {
'passphrase': new MochiKit.Async.DeferredLock(),
'serverData': new MochiKit.Async.DeferredLock(),
// 'recordsIndex': new MochiKit.Async.DeferredLock(),
// 'directLoginsIndex': new MochiKit.Async.DeferredLock()
// 'preferences': new MochiKit.Async.DeferredLock()
// 'oneTimePasswords': new MochiKit.Async.DeferredLock()
'__syntaxFix__': 'syntax fix'
};
@@ -267,6 +263,10 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'name': 'oneTimePasswords',
'username': this.username(),
'passphraseCallback': MochiKit.Base.method(this, 'getPassphrase')
}),
'wallet': new Clipperz.PM.DataModel.User.Header.Wallet({
'name': 'wallet',
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
})
}
};
@@ -391,6 +391,24 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
this.setAccountInfo(new Clipperz.PM.DataModel.User.AccountInfo(aValue['accountInfo']));
},
'updateAccountInfo': function () {
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("User.updateAccountInfo", {trace:false});
deferredResult.addMethod(this.connection(), 'message', 'accountInfo');
// deferredResult.addCallback(function (aValue) {
//console.log("ACCOUNT INFO", aValue);
// return new Clipperz.PM.DataModel.User.AccountInfo(aValue);
// })
deferredResult.addMethod(this, 'setupAccountInfo');
deferredResult.addErrback (MochiKit.Base.method(this, 'handleConnectionFallback'));
deferredResult.callback();
return deferredResult;
},
//-------------------------------------------------------------------------
'lock': function () {
@@ -439,8 +457,9 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
var headerVersion;
var recordsIndex;
var preferences;
var oneTimePasswords;
var preferences;
var oneTimePasswords;
var wallet;
headerVersion = this.headerFormatVersion(someServerData['header']);
switch (headerVersion) {
@@ -462,6 +481,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
recordsIndex = legacyHeader;
preferences = legacyHeader;
oneTimePasswords = legacyHeader;
wallet = legacyHeader; // TODO: does this make any sense?
break;
case '0.1':
var headerData;
@@ -516,6 +536,22 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
});
}
if (typeof(headerData['wallet']) != 'undefined') {
wallet = new Clipperz.PM.DataModel.User.Header.Wallet({
'name': 'wallet',
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase'),
'remoteData': {
'data': headerData['wallet']['data'],
'version': someServerData['version']
}
});
} else {
wallet = new Clipperz.PM.DataModel.User.Header.Wallet({
'name': 'wallet',
'retrieveKeyFunction': MochiKit.Base.method(this, 'getPassphrase')
});
}
break;
}
@@ -527,9 +563,10 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'data': someServerData['header'],
'version': headerVersion,
'recordsIndex': recordsIndex,
'preferences': preferences,
'oneTimePasswords': oneTimePasswords
'recordsIndex': recordsIndex,
'preferences': preferences,
'oneTimePasswords': oneTimePasswords,
'wallet': wallet
}
};
@@ -552,7 +589,9 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
if (this._serverData == null) {
innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'loadingUserDetails');
innerDeferredResult.addMethod(this.connection(), 'message', 'getUserDetails');
// innerDeferredResult.addCallback(function (aValue) { console.log("USER DETAILS", aValue); return aValue; });
innerDeferredResult.addMethod(this, 'unpackServerData');
// innerDeferredResult.addCallback(function (aValue) { console.log("UNPACKED SERVER DATA", aValue); return aValue; });
innerDeferredResult.addCallbackPass(MochiKit.Signal.signal, this, 'loadedUserDetails');
}
@@ -607,6 +646,7 @@ Clipperz.Base.extend(Clipperz.PM.DataModel.User, Object, {
'getHeaderIndex': function (aKey) {
return Clipperz.Async.callbacks("User.getHeaderIndex", [
MochiKit.Base.method(this, 'getServerData'),
// function (aValue) { console.log("HEADER INDEX", aValue); return aValue; },
MochiKit.Base.itemgetter('header'),
MochiKit.Base.itemgetter(aKey)
], {trace:false})
@@ -728,12 +768,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
//-------------------------------------------------------------------------
'getRecordDetail': function (aRecord) {
// return this.connection().message('getRecordDetail', {reference: aRecordReference});
return Clipperz.Async.callbacks("User.getRecordDetail", [
MochiKit.Base.method(this.connection(), 'message', 'getRecordDetail', {reference: aRecord.reference()}),
function(someInfo) { aRecord.setAttachmentServerStatus(someInfo['attachmentStatus']); return someInfo;}, // Couldn't find a better way...
], {trace:false});
return this.connection().message('getRecordDetail', {reference: aRecord.reference()});
},
//-------------------------------------------------------------------------
@@ -906,7 +941,11 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
'oneTimePasswords': [
MochiKit.Base.method(this, 'getHeaderIndex', 'oneTimePasswords'),
MochiKit.Base.methodcaller(aMethodName, aValue)
]//,
],
'wallet': [
MochiKit.Base.method(this, 'getHeaderIndex', 'wallet'),
MochiKit.Base.methodcaller(aMethodName, aValue)
]
// 'statistics': [
// MochiKit.Base.method(this, 'getStatistics'),
// MochiKit.Base.methodcaller(aMethodName, aValue)
@@ -926,14 +965,14 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
//-------------------------------------------------------------------------
'getPreferences': function() {
getPreferences: function() {
return Clipperz.Async.callbacks("User.getPreferences", [
MochiKit.Base.method(this, 'getHeaderIndex', 'preferences'),
MochiKit.Base.methodcaller('getPreferences')
], {trace:false});
},
'getPreference': function(aKey) {
getPreference: function(aKey) {
return Clipperz.Async.callbacks("User.getPreference", [
MochiKit.Base.method(this, 'getHeaderIndex', 'preferences'),
MochiKit.Base.methodcaller('getPreference', aKey)
@@ -950,11 +989,7 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
//-------------------------------------------------------------------------
/*
'addNewAttachment': function(anAttachment) {
console.log("Adding attachment", anAttachment);
},
*/
'uploadAttachment': function(aRecordReference, anAttachmentReference, someData) {
// TODO: pass a callback to handle onProgress events (and modify Connection accordingly)
this.connection().message('uploadAttachment', {
@@ -966,6 +1001,34 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
//=========================================================================
getWallet: function () {
return this.getHeaderIndex('wallet');
},
getUserMasterKey: function () {
//console.log("USER - GET USER MASTER KEY");
return Clipperz.Async.callbacks("User.getPreferences", [
MochiKit.Base.method(this, 'getHeaderIndex', 'wallet'),
MochiKit.Base.methodcaller('userMasterKey')
], {trace:false});
},
createCertificateForRecord: function (aRecord) {
var cardCertificatePath = 'm/0';
return Clipperz.Async.callbacks("User.createCertificateForRecord", [
MochiKit.Base.method(this, 'getWallet'),
MochiKit.Base.methodcaller('getNextID', cardCertificatePath),
MochiKit.Base.method(aRecord, 'setID'),
MochiKit.Base.method(aRecord, 'markAsCertified'),
MochiKit.Base.method(this, 'getWallet'),
MochiKit.Base.method(aRecord, 'computeCertificateInfo'),
function (certificateInfo) { return {'certificateInfo': certificateInfo}; },
], {trace:false});
},
//=========================================================================
'hasPendingChanges': function () {
var deferredResult;
@@ -1059,12 +1122,14 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
deferredResult.addCallback(MochiKit.Base.bind(function (aResult, someHeaderPackedData) {
var header;
//console.log("USER - prepareRemoteDataWithKey", someHeaderPackedData);
header = {};
header['records'] = someHeaderPackedData['recordIndex']['records'];
header['directLogins'] = someHeaderPackedData['recordIndex']['directLogins'];
header['attachments'] = someHeaderPackedData['recordIndex']['attachments'];
header['preferences'] = {'data': someHeaderPackedData['preferences']['data']};
header['oneTimePasswords'] = {'data': someHeaderPackedData['oneTimePasswords']['data']};
header['wallet'] = {'data': someHeaderPackedData['wallet']['data']};
header['version'] = '0.1';
aResult['header'] = Clipperz.Base.serializeJSON(header);
@@ -1089,10 +1154,17 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
//=========================================================================
'saveChanges': function () {
return this.saveChangesWithExtraParameters();
},
'saveChangesWithExtraParameters': function (extraParameters) {
var deferredResult;
var messageParameters;
messageParameters = {};
if (typeof(extraParameters) != 'undefined') {
messageParameters['extraParameters'] = extraParameters;
}
deferredResult = new Clipperz.Async.Deferred("User.saveChangaes", {trace:false});
@@ -1110,7 +1182,6 @@ Clipperz.log("Warning: User.recordWithLabel('" + aLabel + "') is returning more
deferredResult.addMethod(this, 'commitTransientState');
deferredResult.addErrbackPass(MochiKit.Base.method(this, 'revertChanges'));
deferredResult.callback();
return deferredResult;

View File

@@ -130,10 +130,10 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
deferredResult = new Clipperz.Async.Deferred("Proxy.JSON._sendMessage", {trace:false});
//deferredResult.addCallback(function(){console.log("About to send request");});
deferredResult.addCallback(Clipperz.Async.doXHR, this.url(), {
method:'POST',
sendContent:formData,
'method':'POST',
'sendContent':formData,
// headers:{"Content-Type":"application/x-www-form-urlencoded"},
uploadProgress: aProgressCallback || null,
'uploadProgress': aProgressCallback || null,
});
//deferredResult.addCallback(function(something){console.log("Done sending request"); return something;});
deferredResult.addCallback(function (someValues) {

View File

@@ -1031,6 +1031,7 @@ Clipperz.PM.Proxy.Offline.DataStore.defaultAccountInfo = {
'UPDATE_CREDENTIALS',
'EDIT_CARD',
'CARD_DETAILS',
'REGISTER_CARD',
'ADD_CARD',
'DELETE_CARD',
'OFFLINE_COPY',

View File

@@ -25,11 +25,11 @@ refer to http://www.clipperz.com.
Clipperz.Base.module('Clipperz.PM.UI');
Clipperz.PM.UI.AttachmentController = function(someParameters) {
this.MAX_SIMULTANEOUS_READ = 1;
this.MAX_SIMULTANEOUS_UPLOAD = 1;
this.MAX_SIMULTANEOUS_DOWNLOAD = 1;
this.MAX_SIMULTANEOUS_ENCRYPT = 1;
this.MAX_SIMULTANEOUS_DECRYPT = 1;
this.MAX_SIMULTANEOUS_READ = 1;
this.MAX_SIMULTANEOUS_UPLOAD = 1;
this.MAX_SIMULTANEOUS_DOWNLOAD = 1;
this.MAX_SIMULTANEOUS_ENCRYPT = 1;
this.MAX_SIMULTANEOUS_DECRYPT = 1;
this.LATEST_ENCRYPTION_VERSION = '1.0'; // Versions aren't handled completely yet!
@@ -74,7 +74,11 @@ MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
//=========================================================================
addAttachment: function (anAttachment) {
var deferredResult;
var deferredResult;
var actualDeferredResult;
actualDeferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.uploadAttachment-result", {trace:false});
actualDeferredResult.addMethod(anAttachment, 'setValue', 'hash');
deferredResult = new Clipperz.Async.Deferred("Clipperz.PM.UI.AttachmentController.uploadAttachment", {trace:false});
deferredResult.collectResults({
@@ -89,10 +93,12 @@ MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
'recordReference': MochiKit.Base.method(anAttachment.record(), 'reference'),
'process': MochiKit.Base.partial(MochiKit.Async.succeed, 'UPLOAD'), // Used only to differentiate notifications
}, {trace: false});
deferredResult.addCallback(function (someInfo) { someInfo['deferredResult'] = actualDeferredResult; return someInfo; });
deferredResult.addMethod(this, 'addFileToQueue');
deferredResult.callback();
return deferredResult;
// return deferredResult;
return actualDeferredResult;
},
getAttachment: function(anAttachment, aMessageCallback) {
@@ -162,8 +168,8 @@ MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
processNextElements = true;
for (i in this.fileQueue) {
if(processNextElements) {
currentElement = this.fileQueue[i];
if (processNextElements) {
currentElement = this.fileQueue[i];
switch (currentElement['status']) {
case 'WAITING_READ':
if ((count['READING']) < this.MAX_SIMULTANEOUS_READ) {
@@ -214,20 +220,20 @@ MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
var count;
count = {
'WAITING_READ': 0,
'READING': 0,
'WAITING_ENCRYPT': 0,
'ENCRYPTING': 0,
'WAITING_UPLOAD': 0,
'UPLOADING': 0,
'WAITING_DOWNLOAD': 0,
'DOWNLOADING': 0,
'WAITING_DECRYPT': 0,
'DECRYPTING': 0,
'WAITING_SAVE': 0,
'DONE': 0,
'CANCELED': 0,
'FAILED': 0,
'WAITING_READ': 0,
'READING': 0,
'WAITING_ENCRYPT': 0,
'ENCRYPTING': 0,
'WAITING_UPLOAD': 0,
'UPLOADING': 0,
'WAITING_DOWNLOAD': 0,
'DOWNLOADING': 0,
'WAITING_DECRYPT': 0,
'DECRYPTING': 0,
'WAITING_SAVE': 0,
'DONE': 0,
'CANCELED': 0,
'FAILED': 0,
};
for (var i in this.fileQueue) {
@@ -332,13 +338,19 @@ MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
},
readFileOnload: function(aFileReference, anEvent) {
var fileContent;
var fileContentSha256Hash;
fileContent = new Uint8Array(anEvent.target.result);
fileContentSha256Hash = npm.bitcoin.crypto.sha256(fileContent).toString('hex');
this.getQueueElement(aFileReference)['deferredResult'].callback(fileContentSha256Hash);
this.updateFileInQueue(aFileReference, {
'status': 'WAITING_ENCRYPT',
'originalArray': new Uint8Array(anEvent.target.result),
'originalArray': fileContent,
})
},
//=========================================================================
// Queue Processing: ENCRYPT
//=========================================================================
@@ -349,11 +361,11 @@ MochiKit.Base.update(Clipperz.PM.UI.AttachmentController.prototype, {
});
this.cryptoObject.subtle.importKey(
"raw",
aKey, //this is an example jwk key, "raw" would be an ArrayBuffer
{ name: "AES-CBC" }, //this is the algorithm options
false, //whether the key is extractable (i.e. can be used in exportKey)
["encrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
"raw",
aKey, //this is an example jwk key, "raw" would be an ArrayBuffer
{ name: "AES-CBC" }, //this is the algorithm options
false, //whether the key is extractable (i.e. can be used in exportKey)
["encrypt"] //can be "encrypt", "decrypt", "wrapKey", or "unwrapKey"
)
// .then(MochiKit.Base.method(this, 'doEncrypt', aFileReference, anArrayBuffer, aNonce))
.then(this.doEncrypt.bind(this,aFileReference, anArrayBuffer, aNonce))

File diff suppressed because one or more lines are too long

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.AccountStatusClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.AccountStatus',
propTypes: {
// 'currentSubscriptionType': React.PropTypes.oneOf(['EARLY_ADOPTER', 'FRIEND', 'FAN', 'DEVOTEE', 'PATRON', 'TRIAL', 'TRIAL_EXPIRED', 'PAYMENT_FAILED_2', 'EXPIRED', 'PAYMENT_FAILED', 'VERIFYING_PAYMENT', 'VERIFYING_PAYMENT_2']).isRequired,
'expirationDate': React.PropTypes.string /* .isRequired */,
@@ -54,15 +56,15 @@ Clipperz.PM.UI.Components.AccountStatusClass = React.createClass({
proxyInfoClasses[this.props['proxyType']] = true;
proxyInfoClasses['withReferenceDate'] = (this.props['referenceDate'] != null);
return React.DOM.div({'className':'miscInfo'}, [
React.DOM.div({'className':Clipperz.PM.UI.Components.classNames(proxyInfoClasses)}, [
React.DOM.span({'className':'proxyDescription'}, this.props['proxyTypeDescription']),
React.DOM.span({'className':'referenceDate'}, this.props['referenceDate'])
return React.DOM.div({'key':'miscInfo', 'className':'miscInfo'}, [
React.DOM.div({'key':'miscInfo_1', 'className':Clipperz.PM.UI.Components.classNames(proxyInfoClasses)}, [
React.DOM.span({'key':'proxyDescription', 'className':'proxyDescription'}, this.props['proxyTypeDescription']),
React.DOM.span({'key':'referenceDate', 'className':'referenceDate'}, this.props['referenceDate'])
]),
React.DOM.div({'className':Clipperz.PM.UI.Components.classNames(accountInfoClasses)}, [
React.DOM.span({'className':'level'}, Clipperz.PM.DataModel.Feature['featureSets'][this.props['featureSet']]),
React.DOM.span({'className':'expirationMessage'}, "expiring on"),
React.DOM.span({'className':'expirationDate'}, this.props['expirationDate'])
React.DOM.div({'key':'miscInfo_2', 'className':Clipperz.PM.UI.Components.classNames(accountInfoClasses)}, [
React.DOM.span({'key':'level', 'className':'level'}, Clipperz.PM.DataModel.Feature['featureSets'][this.props['featureSet']]),
React.DOM.span({'key':'expirationMessage', 'className':'expirationMessage'}, "expiring on"),
React.DOM.span({'key':'expirationDate', 'className':'expirationDate'}, this.props['expirationDate'])
])
]);

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.AttachmentQueueBoxClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.AttachmentQueueBox',
propTypes: {
'attachmentQueueInfo': React.PropTypes.object.isRequired,
'attachmentQueueBoxStatus': React.PropTypes.string.isRequired,
@@ -104,7 +106,7 @@ Clipperz.PM.UI.Components.AttachmentQueueBoxClass = React.createClass({
render: function () {
//test
this.renderNotifications(this.props['attachmentQueueInfo']['notifications']);
// this.renderNotifications(this.props['attachmentQueueInfo']['notifications']);
return React.DOM.div({
'className': 'attachmentQueueStatus '+this.props['attachmentQueueBoxStatus'],

View File

@@ -26,13 +26,15 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.ButtonClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Button',
propTypes: {
'eventName': React.PropTypes.string.isRequired,
'label': React.PropTypes.string.isRequired,
'handler': React.PropTypes.func.isRequired,
'className': React.PropTypes.string,
'badgeTopContent': React.PropTypes.string,
'badgeBottomContent': React.PropTypes.string,
'badgeTopContent': React.PropTypes.number,
'badgeBottomContent': React.PropTypes.number,
},
//=========================================================================

View File

@@ -25,14 +25,16 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.CardToolbarClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.CardToolbar',
propTypes: {
// 'style': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired,
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
'enableSidePanels': React.PropTypes.bool.isRequired,
'accountInfo': React.PropTypes.object.isRequired,
'proxyInfo': React.PropTypes.object.isRequired,
'messageBox': React.PropTypes.object.isRequired,
'filter': React.PropTypes.object /*.isRequired */,
// 'style': React.PropTypes.oneOf(['extra-short', 'narrow', 'wide', 'extra-wide']).isRequired,
'style': React.PropTypes.oneOf(Clipperz_PM_UI_availableStyles).isRequired,
'enableSidePanels': React.PropTypes.bool.isRequired,
'accountInfo': React.PropTypes.object.isRequired,
'proxyInfo': React.PropTypes.object.isRequired,
'messageBox': React.PropTypes.object.isRequired,
'filter': React.PropTypes.object /*.isRequired */,
'attachmentQueueInfo': React.PropTypes.object.isRequired,
},
@@ -55,6 +57,12 @@ Clipperz.PM.UI.Components.CardToolbarClass = React.createClass({
//============================================================================
certificateQueueToggleHandler: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'toggleCertificateQueueBox');
},
//============================================================================
renderWithSidePanels: function () {
var attachmentDownloadNotificationNumber = MochiKit.Base.filter(function(anElement) {
return anElement['queueElement']['process'] == 'DOWNLOAD';
@@ -62,18 +70,25 @@ Clipperz.PM.UI.Components.CardToolbarClass = React.createClass({
var attachmentUploadNotificationNumber = this.props['attachmentQueueInfo']['notifications'].length - attachmentDownloadNotificationNumber;
// attachmentDownloadNotificationNumber = 2;
// attachmentUploadNotificationNumber = 3;
var verifyingCertificateNotificationNumber = this.props['certificateQueueInfo'] ? MochiKit.Base.filter(function (anCertificationInfo) {
return anCertificationInfo['status'] == 'requested';
}, this.props['certificateQueueInfo']).length : 0;
var verifiedCertificateNotificationNumber = this.props['certificateQueueInfo'] ? MochiKit.Base.filter(function (anCertificationInfo) {
return anCertificationInfo['status'] == 'published';
}, this.props['certificateQueueInfo']).length : 0;
return [
React.DOM.div({className:'selectionToggle'}, [
Clipperz.PM.UI.Components.Button({eventName:'selectionToggleButton', label:"tags", handler:this.selectionToggleHandler})
]),
this.renderWithoutSidePanels(),
// React.DOM.div({className:'attachmentToggle'}, [
// Clipperz.PM.UI.Components.Button({eventName:'attachmentQueueToggleButton', label:"clipperz", handler:this.attachmentQueueToggleHandler})
// ]),
// TODO: validate and adjust names
React.DOM.div({className:'settingsToggle'}, [
Clipperz.PM.UI.Components.Button({eventName:'attachmentQueueToggleButton', label:"\u2191\u2193", handler:this.attachmentQueueToggleHandler, badgeTopContent: attachmentDownloadNotificationNumber, badgeBottomContent: attachmentUploadNotificationNumber}),
Clipperz.PM.UI.Components.Button({eventName:'certificateQueueToggleButton', label:"certificate", handler:this.certificateQueueToggleHandler, badgeTopContent:verifyingCertificateNotificationNumber, badgeBottomContent:verifiedCertificateNotificationNumber}),
Clipperz.PM.UI.Components.Button({eventName:'attachmentQueueToggleButton', label:"\u2191\u2193", handler:this.attachmentQueueToggleHandler, badgeTopContent:attachmentDownloadNotificationNumber, badgeBottomContent:attachmentUploadNotificationNumber}),
Clipperz.PM.UI.Components.Button({eventName:'settingsToggleButton', label:"menu", handler:this.settingsToggleHandler})
])
];
@@ -81,6 +96,7 @@ Clipperz.PM.UI.Components.CardToolbarClass = React.createClass({
renderWithoutSidePanels: function () {
var result;
//console.log("CARD TOOLBAR", this.props);
if (this.props['filter']) {
//console.log("CARD TOOLBAR", this.props['filter']['type']);

View File

@@ -0,0 +1,221 @@
/*
Copyright 2008-2015 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.UI.Components.Cards');
var textHeight = 12;
var outputSpacing = 8;
var arrowLength = 70;
var arrowWidth = 8;
var arrowHalfHeight = 2;
Clipperz.PM.UI.Components.Cards.CertificateRendererClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Cards.CertificateRenderer',
//==============================================================================
renderTransactionSchema: function () {
var outputDistance = 10;
// var numberOfAttachments = this.props['attachments'].length;
var numberOfAttachments = 0;
var numberOfOutputs = 1 + 1 + numberOfAttachments + 1;
var height = numberOfOutputs * (textHeight + outputSpacing) + outputDistance;
var rectWidth = arrowLength * 2;
var arrow = function (x, y) {
var pathItems = function (items) {
return (MochiKit.Base.map(function (coordinates) { return coordinates[0] + "," + coordinates[1]; }, items)).join(" ");
}
return [
React.DOM.line({'x1':x, 'y1':y, 'x2':x + arrowLength, 'y2':y, 'stroke':'black', 'strokeWidth':1}),
React.DOM.polygon({'points':pathItems([[x + arrowLength, y], [x + arrowLength - arrowWidth, y + arrowHalfHeight], [x + arrowLength - arrowWidth, y - arrowHalfHeight]]), 'fill':'black', 'stroke':'black', 'strokeWidth':1}),
];
};
var outputArrow = function (args) {
var index = args[0];
var label = args[1];
return [
// arrow(arrowLength + rectWidth, ((index + 0.5) * (textHeight + outputSpacing))),
React.DOM.text({'x':(2 * arrowLength) + rectWidth + 15, 'y':(((index + 1) * textHeight) + (index + 0.5) * outputSpacing), 'fill':'black'}, label),
];
};
return React.DOM.svg({'viewBox':'0 0 600 ' + height, 'width':'100%', 'height':height, 'fill':'currentcolor'}, [
arrow(0, height/2),
React.DOM.rect({'x':arrowLength, 'y':0, 'width':rectWidth, 'height':height, 'fill':'blue', 'opacity':0.3}),
React.DOM.rect({'x':arrowLength, 'y':0, 'width':rectWidth, 'height':height, 'fill':'none', 'stroke':'black', 'strokeWidth':1, 'opacity':1}),
React.DOM.g({'transform':'matrix(-0.2, 0, 0, ' + ((numberOfOutputs - 1) * 0.1225) + ', ' + ((arrowLength * 4) + 10) +', 0)'}, [
React.DOM.path({'d':'M 19.8,25.8 C 19.8,11.2 15.6,2.2 0,0.4 L 0.4,0 C 23,0.4 29.2,7.2 29.2,29 L 29.2,59.2 C 29.2,72.6 30.6,79 45.2,82.6 L 45.2,83 C 30.8,86.6 29.2,93 29.2,106.2 L 29.2,138.4 C 29.2,159.4 21.4,165.4 0.4,166 L 0,165.601 C 16,163.201 19.8,155.201 19.8,140 L 19.8,107.8 C 19.8,94.2 21.6,86.4 36.6,83 L 36.6,82.6 C 21.4,79 19.8,70.6 19.8,57 L 19.8,25.8 z '})
]),
arrow((arrowLength + rectWidth), ((numberOfOutputs - 1) * (textHeight + outputSpacing) / 2)),
React.DOM.text({'x':(arrowLength + rectWidth) + 5, 'y':((numberOfOutputs - 1) * (textHeight + outputSpacing) / 2) - 2, 'fill':'black', 'style':{'fontSize':'0.6em'}}, 'MULTI-SIG'),
arrow((arrowLength + rectWidth), ((numberOfOutputs - 0.5) * (textHeight + outputSpacing)) + outputDistance),
React.DOM.text({'x':(arrowLength + rectWidth) + 5, 'y':((numberOfOutputs - 0.5) * (textHeight + outputSpacing)) - 2 + outputDistance, 'fill':'black', 'style':{'fontSize':'0.6em'}}, 'OP_RETURN'),
React.DOM.text({'x':(2 * arrowLength) + rectWidth + 5, 'y':((numberOfOutputs * textHeight) + (numberOfOutputs - 0.5) * outputSpacing) + outputDistance, 'fill':'black'}, '"CLIPPERZ ' + this.props['certificateInfo']['version'] + ' REG"'),
this.props['certificateDetails'] ? MochiKit.Base.map(outputArrow,
MochiKit.Base.zip(
MochiKit.Iter.range(0, numberOfOutputs),
[this.props['certificateDetails']['transaction']['card.address'], this.props['certificateDetails']['transaction']['metadata.address']]
/*
MochiKit.Base.concat(
[this.props['certificateDetails']['transaction']['card.address'], this.props['certificateDetails']['transaction']['metadata.address']],
MochiKit.Base.map(
MochiKit.Base.itemgetter('address'),
MochiKit.Base.values(this.props['certificateDetails']['transaction']['attachments'])
)
)
*/
)
): null,
]);
},
//==============================================================================
renderCertificateDetails: function () {
return React.DOM.ul({'className':'transactionInfo'}, [
React.DOM.li({}, /*"OUT00 - " + */"BTC address of card: " + this.props['certificateDetails']['transaction']['card.address']),
React.DOM.li({}, [
React.DOM.div({'className':'metadata'}, [
React.DOM.header({}, 'Card data'),
React.DOM.dl({'className':'title'}, [React.DOM.dt({}, "Title"), React.DOM.dd({}, this.props['certificateDetails']['metadata']['label'])]),
MochiKit.Base.map(function (aField) { return React.DOM.dl({'className':'field'}, [React.DOM.dt({}, aField['label']), React.DOM.dd({}, aField['value'])]); }, this.props['certificateDetails']['metadata']['fields']),
this.props['certificateDetails']['metadata']['notes'] ? React.DOM.dl({'className':'notes'}, [React.DOM.dt({}, "Notes"), React.DOM.dd({}, this.props['certificateDetails']['metadata']['notes'])]) : null,
React.DOM.ul({'className':'attachments'},
MochiKit.Base.map(
function (anAttachmentInfo) {
var numberPadding = function (aValue) {
return ("00" + aValue).substr(-2);
}
return React.DOM.li({}, [
React.DOM.dl({'className':'file'}, [React.DOM.dt({}, "File"), React.DOM.dd({}, anAttachmentInfo[0]['name'])]),
React.DOM.dl({'className':'size'}, [React.DOM.dt({}, "Size"), React.DOM.dd({}, filesize(anAttachmentInfo[0]['size']))]),
React.DOM.dl({'className':'type'}, [React.DOM.dt({}, "Filetype"), React.DOM.dd({}, anAttachmentInfo[0]['contentType'])]),
React.DOM.dl({'className':'hash'}, [React.DOM.dt({}, "Sha256"), React.DOM.dd({}, anAttachmentInfo[1]['hash'])]),
// React.DOM.div({'className':'address'}, /*"OUT" + numberPadding(anAttachmentInfo[2]) + " - " + */"File BTC address: " + anAttachmentInfo[1]['address'])
]);
}, MochiKit.Base.zip(
this.props['certificateDetails']['metadata']['attachments'],
MochiKit.Base.values(this.props['certificateDetails']['transaction']['attachments']),
MochiKit.Iter.range(2, this.props['certificateDetails']['metadata']['attachments'].length + 2)
)
)
),
React.DOM.div({'className':'address'}, /*"OUT01 - " + */"BTC address from card data: " + this.props['certificateDetails']['transaction']['metadata.address'])
])
])
]);
},
renderSpinner: function () {
return React.DOM.div({'className':'spinner'}, [
React.DOM.div({'className':'bar01'}),
React.DOM.div({'className':'bar02'}),
React.DOM.div({'className':'bar03'}),
React.DOM.div({'className':'bar04'}),
React.DOM.div({'className':'bar05'}),
React.DOM.div({'className':'bar06'}),
React.DOM.div({'className':'bar07'}),
React.DOM.div({'className':'bar08'}),
React.DOM.div({'className':'bar09'}),
React.DOM.div({'className':'bar10'}),
React.DOM.div({'className':'bar11'}),
React.DOM.div({'className':'bar12'}),
]);
},
componentDidMount: function () {
// this.renderTransactionDiagram(this.refs['canvas']);
},
render: function () {
//console.log("CERTIFICATE RENDERER", this.props, NETWORK);
console.log("CERTIFICATE RENDERER", this.props);
var transactionInspectorPath = (NETWORK.pubKeyHash == 111) ? 'tBTC' : 'BTC';
var certificateInfo;
if (this.props['certificateDetails']) {
certificateInfo = this.props['certificateDetails'];
certificateInfo['tx'] = this.props['certificateInfo']['txID'];
certificateInfo['requestDate'] = this.props['certificateInfo']['requestDate'];
certificateInfo['creationDate'] = this.props['certificateInfo']['creationDate'];
} else {
certificateInfo = null;
}
return React.DOM.div({'className':'certificateContent'}, [
React.DOM.div({}, [
React.DOM.header({}, [
React.DOM.div({'className':'title'}, [
React.DOM.h3({}, "clipperz"),
React.DOM.h1({}, "Blockchain certificate")
]),
React.DOM.div({'className':'info'}, [
// React.DOM.dl({'className':'cardLabel'}, [React.DOM.dt({}, "Card"), React.DOM.dd({}, this.props['label'])]),
React.DOM.dl({'className':'cardLabel'}, [React.DOM.dt({}, "Card"), React.DOM.dd({}, this.props['certificateDetails'] ? this.props['certificateDetails']['metadata']['label'] : "")]),
React.DOM.dl({}, [React.DOM.dt({}, "Requested on"), React.DOM.dd({}, (new XDate(this.props['certificateInfo']['requestDate'])).toString("MMM d, yyyy - HH:mm"))]),
React.DOM.dl({}, [React.DOM.dt({}, "Validated on"), React.DOM.dd({}, (new XDate(this.props['certificateInfo']['creationDate'])).toString("MMM d, yyyy - HH:mm"))]),
React.DOM.dl({'className':'version'}, [React.DOM.dt({}, "Clipperz protocol version"), React.DOM.dd({}, this.props['certificateInfo']['version'])]),
]),
]),
React.DOM.div({'className':'details'}, this.props['certificateDetails'] ? this.renderCertificateDetails() : this.renderSpinner()),
]),
React.DOM.div({'className':'transactionInfo'}, [
React.DOM.div({'className':'description'}, [
React.DOM.p({}, "The following transaction has been added to the Bitcoin blockchain."),
React.DOM.p({}, [
React.DOM.span({}, "TX ID: "),
React.DOM.a({'href':'https://clipperz.is/tx/' + transactionInspectorPath + '/' + this.props['certificateInfo']['txID']}, this.props['certificateInfo']['txID'])
]),
]),
this.renderTransactionSchema(),
]),
React.DOM.div({'className':'reviewInfo'}, [
React.DOM.p({}, '----'),
React.DOM.p({'className':'verify'}, [
React.DOM.span({}, "You can check the authenticity of this certificate at "),
React.DOM.a({'href':'https://clipperz.is/verify'}, "clipperz.is/verify")
]),
React.DOM.p({'className':'instructions'}, [
React.DOM.span({}, "If you want to perform this check by yourself, just follow "),
React.DOM.a({'href':'https://clipperz.is/verify_howto'}, "these instructions")
]),
]),
certificateInfo ? React.DOM.textarea({'className':'certificateDetails', 'value':Clipperz.Base.serializeJSON(certificateInfo), 'readOnly':true}) : null,
]);
},
//===========================================================================
});
Clipperz.PM.UI.Components.Cards.CertificateRenderer = React.createFactory(Clipperz.PM.UI.Components.Cards.CertificateRendererClass);

View File

@@ -28,10 +28,12 @@ Clipperz.PM.UI.Components.Cards.CommandToolbarClass = React.createClass({
//============================================================================
displayName: 'Clipperz.PM.UI.Components.Cards.CommandToolbar',
propTypes: {
// 'label': React.PropTypes.string.isRequired,
// 'loading': React.PropTypes.bool,
'features': React.PropTypes.array.isRequired,
'features': React.PropTypes.array.isRequired,
},
features: function () {
@@ -42,6 +44,10 @@ Clipperz.PM.UI.Components.Cards.CommandToolbarClass = React.createClass({
return (this.features().indexOf(aValue) > -1);
},
isCardCertified: function () {
return ((typeof(this.props['certificateInfo']) != 'undefined') && (this.props['certificateInfo'] != null));
},
//----------------------------------------------------------------------------
getInitialState: function() {
@@ -57,9 +63,10 @@ Clipperz.PM.UI.Components.Cards.CommandToolbarClass = React.createClass({
return {
'delete': { 'label': "delete", 'broadcastEvent': 'deleteCard', 'enabled': this.isFeatureEnabled('DELETE_CARD')},
'archive': { 'label': archiveLabel, 'broadcastEvent': 'toggleArchiveCard', 'enabled': this.isFeatureEnabled('EDIT_CARD')},
// 'share': { 'label': "share", 'broadcastEvent': 'shareCard' },
// 'share': { 'label': "share", 'broadcastEvent': 'shareCard' },
'clone': { 'label': "clone", 'broadcastEvent': 'cloneCard', 'enabled': this.isFeatureEnabled('ADD_CARD')},
'edit': { 'label': "edit", 'broadcastEvent': 'editCard', 'enabled': this.isFeatureEnabled('EDIT_CARD')}
'register': { 'label': "register", 'broadcastEvent': 'createCertificate', 'enabled': this.isFeatureEnabled('REGISTER_CARD') && !this.isCardCertified()},
'edit': { 'label': "edit", 'broadcastEvent': 'editCard', 'enabled': this.isFeatureEnabled('EDIT_CARD') && !this.isCardCertified()}
};
},

View File

@@ -25,23 +25,39 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Cards');
Clipperz.PM.UI.Components.Cards.DetailClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Cards.Detail',
propTypes: {
'allTags': React.PropTypes.array,
},
viewComponentProps: function () {
var result;
var props = this.props;
var propertiesToPassAlong = [
'features',
'style',
'showGlobalMask',
'allTags',
'preferences',
'attachmentQueueInfo',
'proxyInfo',
'showCertificatePreview',
'certificateDetails'
];
result = this.props['selectedCard'];
result = props['selectedCard'];
if (result) {
result['features'] = this.props['features'];
result['style'] = this.props['style'];
result['ask'] = (this.props['style'] == 'narrow') ? this.props['ask'] : null;
result['showGlobalMask'] = this.props['showGlobalMask'];
result['allTags'] = this.props['allTags'];
result['preferences'] = this.props['preferences'];
result['attachmentQueueInfo'] = this.props['attachmentQueueInfo'];
result['proxyInfo'] = this.props['proxyInfo'];
// result['features'] = this.props['features'];
// result['style'] = this.props['style'];
result['ask'] = (props['style'] == 'narrow') ? props['ask'] : null;
// result['showGlobalMask'] = this.props['showGlobalMask'];
// result['allTags'] = this.props['allTags'];
// result['preferences'] = this.props['preferences'];
// result['attachmentQueueInfo'] = this.props['attachmentQueueInfo'];
// result['proxyInfo'] = this.props['proxyInfo'];
// result['showCertificatePreview'] = this.props['showCertificatePreview'];
MochiKit.Iter.forEach(propertiesToPassAlong, function (aProperty) { result[aProperty] = props[aProperty]; });
}
return result;

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.Cards.EditClass = React.createClass({
//============================================================================
displayName: 'Clipperz.PM.UI.Components.Cards.Edit',
propTypes: {
'allTags': React.PropTypes.array,
// 'label': React.PropTypes.string /*.isRequired */ ,
@@ -128,7 +130,7 @@ console.log("DROP"); //, anEvent);
], {trace:false});
} else {
//console.log("CANCELLED FIELD MOVE");
//console.log("CANCELED FIELD MOVE");
}
// Delayed because a quick touch would prevent the state to update correctly otherwise (don't know why)
@@ -453,7 +455,7 @@ console.log("DROP"); //, anEvent);
},
removeDirectLogin: function(aDirectLoginReference) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'removeDirectLogin', this.record(), aDirectLoginReference);
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'removeDirectLogin', {'record':this.record(), 'directLoginReference':aDirectLoginReference});
},
//============================================================================
@@ -614,7 +616,7 @@ console.log("DROP"); //, anEvent);
handleRemoveAttachment: function(anAttachment) {
// MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'cancelAttachment', anAttachment);
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'removeAttachment', this.record(), anAttachment);
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'removeAttachment', {'record':this.record(), 'attachment': anAttachment});
},
//............................................................................
@@ -622,13 +624,15 @@ console.log("DROP"); //, anEvent);
uploadFiles: function(someFiles) {
var i;
//console.log("uploadFiles", someFiles);
var newSkippedFiles = [];
for (i = 0; i < someFiles.length; i++) {
var file = someFiles[i];
//console.log("uploadFiles - file", file);
if (file.size <= Clipperz.PM.DataModel.Attachment.MAX_ATTACHMENT_SIZE) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'addAttachment', this.record(), file);
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'addAttachment', {'record':this.record(), 'file':file});
} else {
newSkippedFiles.push(file);
this.setState({'skippedFiles': newSkippedFiles});
@@ -636,7 +640,7 @@ console.log("DROP"); //, anEvent);
}
// TODO: check compatibility with all browsers
this.refs['attachmentInput'].getDOMNode().value = null;
this.refs['attachmentInput'].value = null;
},
//............................................................................
@@ -658,12 +662,14 @@ console.log("DROP"); //, anEvent);
// http://enome.github.io/javascript/2014/03/24/drag-and-drop-with-react-js.html
// https://code.google.com/p/chromium/issues/detail?id=168387
// http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
anEvent.stopPropagation();
anEvent.preventDefault();
anEvent.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
},
//............................................................................
renderSkippedFiles: function() {
renderSkippedFiles: function () {
var result;
result = null;
@@ -687,7 +693,7 @@ console.log("DROP"); //, anEvent);
return result;
},
renderAttachmentProgress: function(aStatus, aServerStatus, aProgress) {
renderAttachmentProgress: function (aStatus, aServerStatus, aProgress) {
var result;
var queueOperationsInProgress = (aStatus && aStatus != 'DONE' && aStatus != 'CANCELED' && aStatus != 'FAILED');
@@ -709,7 +715,7 @@ console.log("DROP"); //, anEvent);
return result;
},
renderAttachmentStatus: function(aStatus, aServerStatus, aProgress) {
renderAttachmentStatus: function (aStatus, aServerStatus, aProgress) {
var result;
var status = aStatus ? aStatus : false;
@@ -728,7 +734,7 @@ console.log("DROP"); //, anEvent);
result = React.DOM.span({'className': 'broken'}, "canceled");
break;
case 'DONE':
result = React.DOM.span({'className': 'broken'}, "failed");
result = React.DOM.span({'className': 'done'}, "done");
break;
case false:
result = React.DOM.span({'className': 'broken'}, "failed");
@@ -745,7 +751,7 @@ console.log("DROP"); //, anEvent);
return result;
},
renderAttachmentActions: function(aStatus, aServerStatus, anAttachment) {
renderAttachmentActions: function (aStatus, aServerStatus, anAttachment) {
var result;
result = null;
@@ -759,7 +765,7 @@ console.log("DROP"); //, anEvent);
return result;
},
renderAttachment: function(anAttachment) {
renderAttachment: function (anAttachment) {
var queueInfo = this.props['attachmentQueueInfo'].elementFetchCallback(anAttachment._reference) || [];
var queueStatus = queueInfo['status'];
var serverStatus = this.props['attachmentServerStatus'][anAttachment._reference];
@@ -767,10 +773,6 @@ console.log("DROP"); //, anEvent);
var broken = (! serverStatus && ! queueOperationsInProgress && ! this.props['_isBrandNew']);
var status = this.renderAttachmentStatus(queueStatus, serverStatus, queueInfo['requestProgress']);
var actions = this.renderAttachmentActions(queueStatus, serverStatus, anAttachment['_attachment']);
var progressIndicator = this.renderAttachmentProgress(queueStatus, serverStatus, queueInfo['requestProgress']);;
return React.DOM.li({
'className': (broken) ? 'broken' : '',
'key': anAttachment._reference
@@ -780,9 +782,9 @@ console.log("DROP"); //, anEvent);
React.DOM.span({'className': 'name'}, anAttachment.name),
React.DOM.span({'className': 'size'}, filesize(anAttachment.size)),
]),
React.DOM.span({'className': 'status'}, status),
React.DOM.span({'className': 'progress'}, progressIndicator),
React.DOM.span({'className': 'actions'}, actions),
React.DOM.span({'className': 'status'}, this.renderAttachmentStatus (queueStatus, serverStatus, queueInfo['requestProgress'])),
React.DOM.span({'className': 'progress'}, this.renderAttachmentProgress(queueStatus, serverStatus, queueInfo['requestProgress'])),
React.DOM.span({'className': 'actions'}, this.renderAttachmentActions (queueStatus, serverStatus, anAttachment['_attachment'])),
])
},
@@ -798,7 +800,7 @@ console.log("DROP"); //, anEvent);
]),
React.DOM.div({
'className': 'cardUploadAttachments',
'onClick': MochiKit.Base.bind(function() { this.refs['attachmentInput'].getDOMNode().click() }, this),
'onClick': MochiKit.Base.bind(function() { this.refs['attachmentInput'].click() }, this),
'onDragOver': this.handleOnDragOver,
'onDrop': this.handleOnDrop,
},[

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.Cards.EditToolbarClass = React.createClass({
//============================================================================
displayName: 'Clipperz.PM.UI.Components.Cards.EditToolbar',
propTypes: {
'hasPendingChanges': React.PropTypes.bool.isRequired,
},

View File

@@ -27,6 +27,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Cards');
Clipperz.PM.UI.Components.Cards.FavIconClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Cards.FavIcon',
propTypes: {
'src': React.PropTypes.string,
},

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.Cards.ListClass = React.createClass({
//=========================================================================
displayName: 'Clipperz.PM.UI.Components.Cards.List',
propTypes: {
'cards': React.PropTypes.array,
'featureSet': React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
@@ -45,7 +47,7 @@ Clipperz.PM.UI.Components.Cards.ListClass = React.createClass({
handleClick: function (anEvent) {
if (this.isFeatureEnabled('CARD_DETAILS')) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCard', {'reference':anEvent.currentTarget.dataset.reference, 'label':anEvent.currentTarget.dataset.label}, true);
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCard', {'cardInfo':{'reference':anEvent.currentTarget.dataset.reference, 'label':anEvent.currentTarget.dataset.label}, 'update':true});
}
},
@@ -63,7 +65,8 @@ Clipperz.PM.UI.Components.Cards.ListClass = React.createClass({
result = React.DOM.li({'className':Clipperz.PM.UI.Components.classNames(classes), 'onClick': this.handleClick, 'key':anItem['_reference'], 'data-reference':anItem['_reference'], 'data-label':anItem['label']}, [
// React.DOM.span({'className':'favicon'}, Clipperz.PM.UI.Components.Cards.FavIcon({'src':anItem['favicon']})),
React.DOM.span({'className':'label'}, anItem['label']),
React.DOM.span({'className':'attachmentsCount'}, anItem['attachmentsCount'] ? 'attachment' : ''),
anItem['hasBeenCertified'] != '' ? React.DOM.span({'className':'certificateStatus'}, 'certificate') : null,
anItem['attachmentsCount'] ? React.DOM.span({'className':'attachmentsCount'}, 'attachment') : null,
]);
}

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Cards');
Clipperz.PM.UI.Components.Cards.PasswordGeneratorClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Cards.PasswordGenerator',
charsetBlocks: {
'chars_AZ': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'chars_az': 'abcdefghijklmnopqrstuvwxyz',

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.Cards.TagEditorClass = React.createClass({
//============================================================================
displayName: 'Clipperz.PM.UI.Components.Cards.TagEditor',
propTypes: {
'allTags': React.PropTypes.array,
'selectedTags': React.PropTypes.array.isRequired,

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.Cards.TextAreaClass = React.createClass({
//----------------------------------------------------------------------------
displayName: 'Clipperz.PM.UI.Components.Cards.TextArea',
componentDidMount: function() {
this.recalculateSize();
this.getDOMNode().addEventListener('input', this.handleKeyDown, false);

View File

@@ -28,16 +28,39 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
//============================================================================
displayName: 'Clipperz.PM.UI.Components.Cards.View',
propTypes: {
'label': React.PropTypes.string /*.isRequired */ ,
'loading': React.PropTypes.bool,
'proxyInfo': React.PropTypes.object.isRequired,
// 'proxyInfo': React.PropTypes.object.isRequired,
},
getInitialState: function () {
return {};
return {
// 'showCertificatePreview': false,
};
},
downloadCertificate: function (anEvent) {
if (this.isCertificatePublished()) {
// console.log("DOWNLOAD CERTIFICATE");
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'downloadCertificate', this.props['_reference']);
}
},
previewCertificate: function (anEvent) {
if (this.isCertificatePublished()) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showCertificatePreview', this.props['_reference']);
// this.setState({'showCertificatePreview':true});
}
},
hideCertificatePreview: function (anEvent) {
// this.setState({'showCertificatePreview':false});
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'hideCertificatePreview', this.props['_reference']);
},
//----------------------------------------------------------------------------
handleDirectLoginClick: function (aDirectLogin) {
@@ -163,6 +186,85 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
//----------------------------------------------------------------------------
hasCertificate: function () {
return ((typeof(this.props['certificateInfo']) != 'undefined') && (this.props['certificateInfo'] != null));
},
isCertificatePublished: function () {
return (this.props['certificateInfo'] && (this.props['certificateInfo']['status'] == 'published'));
},
//----------------------------------------------------------------------------
renderCertificatePreview: function () {
return React.DOM.div({'className':'certificatePreview'}, [
React.DOM.div({'className':'mask'}),
React.DOM.div({'className':'previewContent'}, [
React.DOM.header({}, [
React.DOM.span({'onClick': this.hideCertificatePreview}, "close")
]),
React.DOM.div({'className':'preview'}, Clipperz.PM.UI.Components.Cards.CertificateRenderer(this.props)),
React.DOM.footer({}, null)
])
]);
},
renderCertificateInfo: function (someCertificateInfo) {
var result;
if (this.hasCertificate()) {
var description;
var statusDescription;
var dateLabel;
var dateValue;
var transactionInfo;
var classes = {
'cardCertificateInfo': true,
'published': this.isCertificatePublished(),
'requested': !this.isCertificatePublished(),
};
if (this.isCertificatePublished()) {
description = "This card has been registered on the Bitcoin blockchain";
statusDescription = "confirmed";
dateLabel = "Registration date";
dateValue = (new XDate(someCertificateInfo['creationDate'])).toString("MMM d, yyyy - HH:mm");
transactionInfo = React.DOM.span({}, someCertificateInfo['txID']);
} else {
description = "This card will soon be registered on the Bitcoin blockchain";
statusDescription = "pending";
dateLabel = "Request date";
dateValue = (new XDate(someCertificateInfo['requestDate'])).toString("MMM d, yyyy - HH:mm");
transactionInfo = React.DOM.span({}, "N.A.");
}
result = React.DOM.div({'className': Clipperz.PM.UI.Components.classNames(classes)}, [
React.DOM.div({}, [
React.DOM.h3({}, "certificate"),
React.DOM.p({}, description),
]),
React.DOM.div({'className':'info'}, [
React.DOM.div({'className':'details'}, [
React.DOM.dl({}, [ React.DOM.dt({}, dateLabel), React.DOM.dd({}, dateValue) ]),
React.DOM.dl({}, [ React.DOM.dt({}, 'transaction'), React.DOM.dd({'className':'transactionInfo'}, transactionInfo) ]),
React.DOM.dl({}, [ React.DOM.dt({}, 'status'), React.DOM.dd({}, statusDescription) ]),
]),
React.DOM.div({'className':'links'}, [
React.DOM.a({'className':'certificate', 'onClick':this.downloadCertificate}, "certificate"),
React.DOM.a({'className':'preview', 'onClick':this.previewCertificate}, "preview"),
]),
]),
])
} else {
result = null;
}
return result;
},
//----------------------------------------------------------------------------
renderLabel: function (aLabel) {
return React.DOM.h3({'className':'cardLabel'}, aLabel);
},
@@ -292,7 +394,7 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
var queueOperationsInProgress = (status && (status != 'DONE' && status != 'CANCELED' && status != 'FAILED'));
result = null;
if (status == 'FAILED') {
result = React.DOM.span({'className': 'failed'}, "failed");
} else if (status == 'UPLOADING' || status == 'DOWNLOADING') {
@@ -304,7 +406,7 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
result = React.DOM.span({'className': 'broken'}, "canceled");
break;
case 'DONE':
result = React.DOM.span({'className': 'broken'}, "failed");
result = React.DOM.span({'className': 'done'}, "done");
break;
case false:
result = React.DOM.span({'className': 'broken'}, "failed");
@@ -320,6 +422,7 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
return result;
},
renderAttachmentActions: function(aStatus, aServerStatus, anAttachment) {
var result;
@@ -401,20 +504,25 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
renderCard: function () {
var classes = {
'view': true,
'archived': this.props['_isArchived']
'archived': this.props['_isArchived'],
'registered': this.hasCertificate()
}
return React.DOM.div({'className':Clipperz.PM.UI.Components.classNames(classes)},[
Clipperz.PM.UI.Components.Cards.CommandToolbar(this.props),
React.DOM.div({'className':'content'}, [
this.renderCertificateInfo(this.props['certificateInfo']),
this.renderLabel(this.props['label']),
//React.DOM.div({}, "ID:" + this.props['ID']),
//React.DOM.div({}, "Reference:" + this.props['_reference']),
this.renderTags(this.props['tags']),
this.renderFields(this.props['fields']),
this.renderAttachments(MochiKit.Base.values(this.props['attachments'])),
this.renderNotes(this.props['notes']),
this.renderDirectLogins(this.props['directLogins']),
]),
this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null
this.props['ask'] ? Clipperz.PM.UI.Components.DialogBox(this.props['ask']) : null,
this.props['showCertificatePreview'] ? this.renderCertificatePreview() : null
]);
},
@@ -422,7 +530,7 @@ Clipperz.PM.UI.Components.Cards.ViewClass = React.createClass({
render: function () {
var result;
//console.log("VIEW", this.props);
if (this.props['loading'] == true) {
result = this.renderLoading();
} else if (this.props['_reference']) {

View File

@@ -0,0 +1,82 @@
/*
Copyright 2008-2015 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.UI.Components');
Clipperz.PM.UI.Components.CertificateQueueBoxClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.CertificateQueueBox',
propTypes: {
'certificateQueueInfo': React.PropTypes.object.isRequired,
'certificateQueueBoxStatus': React.PropTypes.string.isRequired,
},
acknowledgeCertificate: function (aRecordReference) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'closeCertificateNotification', aRecordReference);
},
showCertificateCard: function (aRecordReference) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showCertificateCard', aRecordReference);
},
renderCertificateInfo: function (aCertificateInfo) {
var result;
if (aCertificateInfo['status'] != 'acknowledged') {
result = React.DOM.li({'className':aCertificateInfo['status']}, [
React.DOM.span({'className':'icon'}, "certificate"),
React.DOM.span({'className':'name', 'onClick':MochiKit.Base.method(this, 'showCertificateCard', aCertificateInfo['reference'])}, aCertificateInfo['label']),
React.DOM.span({'className':'status'}, [
React.DOM.span({'className':'statusString'}, aCertificateInfo['status'] == 'requested' ? 'pending' : 'registered')
]),
React.DOM.span({'className':'close'}, [
React.DOM.a({'className':'close', 'onClick':MochiKit.Base.method(this, 'acknowledgeCertificate', aCertificateInfo['reference'])}, "remove field")
])
]);
} else {
result = null;
}
return result;
},
render: function () {
var certificatesInfo;
certificatesInfo = this.props['certificateQueueInfo'] ? MochiKit.Base.map(MochiKit.Base.method(this, 'renderCertificateInfo'), this.props['certificateQueueInfo']) : [];
certificatesInfo = MochiKit.Base.filter(function (aValue) { return aValue != null; }, certificatesInfo);
return React.DOM.div({
'className': 'certificateQueueStatus ' + this.props['certificateQueueBoxStatus'],
}, [
React.DOM.div({'className': 'arrow'}),
React.DOM.ul({'className':'certificateInfos'}, (certificatesInfo.length > 0) ? certificatesInfo : React.DOM.p({}, "No certificates pending")),
]);
}
//=========================================================================
});
Clipperz.PM.UI.Components.CertificateQueueBox = React.createFactory(Clipperz.PM.UI.Components.CertificateQueueBoxClass);

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.CheckboxClass = React.createClass({
// http://development.tobypitman.com/iphoneCheckboxes/iphoneCheckboxes2.html
displayName: 'Clipperz.PM.UI.Components.Checkbox',
propTypes: {
'checked': React.PropTypes.bool.isRequired,
'id': React.PropTypes.string.isRequired,

View File

@@ -25,6 +25,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.DialogBoxClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.DialogBox',
propTypes: {
'info': React.PropTypes.object.isRequired,
'deferred': React.PropTypes.object.isRequired
@@ -54,19 +56,28 @@ console.log("DIALOG BOX - key UP", anEvent);
var answerInfo = this.props['info']['possibleAnswers'][anAnswerInfoKey];
var classes = {
'button': true,
'isDefault': answerInfo['isDefault']
'isDefault': answerInfo['isDefault'],
'disabled': answerInfo['disabled']
};
return React.DOM.div({'className':Clipperz.PM.UI.Components.classNames(classes), 'onClick':this.handleAnswerButton, 'data-answer-key':anAnswerInfoKey}, answerInfo['label'])
return React.DOM.div({
'className':Clipperz.PM.UI.Components.classNames(classes),
'onClick':this.handleAnswerButton,
'data-answer-key':anAnswerInfoKey
}, answerInfo['label']);
},
//=========================================================================
render: function () {
//console.log("DIALOG BOX", this.props);
return React.DOM.div({'className':'dialogBox' /*, 'onKeyDown':this.handleKeyDown, 'onKeyPress':this.handleKeyPress, 'onKeyUp':this.handleKeyUp */ }, [
React.DOM.div({'className':'mask'}),
React.DOM.div({'className':'dialog'}, [
React.DOM.h3({'className': 'message'}, this.props['info']['question']),
this.props['info']['description']
? React.DOM.div({'className':'description'}, this.props['info']['description'])
: null,
React.DOM.div({'className': 'answers'}, MochiKit.Base.map(this.renderAnswerButton, MochiKit.Base.keys(this.props['info']['possibleAnswers'])))
])
]);

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.DocumentEncryptionSandboxClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.DocumentEncryptionSandbox',
getInitialState: function() {
return {
'documentMeta': null,

View File

@@ -25,6 +25,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.ExpiredPanelClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExpiredPanel',
propTypes: {
// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
// 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');
Clipperz.PM.UI.Components.ExtraFeatures.DataExportClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataExport',
propTypes: {
// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
// 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired

View File

@@ -25,7 +25,9 @@ refer to http://www.clipperz.com.
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');
Clipperz.PM.UI.Components.ExtraFeatures.DataImportClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport',
getInitialState: function() {
return {
'importContext': new Clipperz.PM.UI.ImportContext(this),

View File

@@ -25,6 +25,8 @@ refer to http://www.clipperz.com.
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.ColumnsClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.Columns',
getInitialState: function() {
return {

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.HiddenClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.Hidden',
getInitialState: function() {
return {
'hiddenFields': this.props.importContext.state('csvData.hiddenFields'),

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.LabelsClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.Labels',
getInitialState: function() {
return {
'useFirstRowAsLabels': this.props.importContext.state('csvData.useFirstRowAsLabels'),
@@ -57,7 +59,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.LabelsClass = React.creat
var newState;
newState = this.state;
newState['labels'][columnIndex] = this.refs['csv-labels-input-' + columnIndex].getDOMNode().value;
newState['labels'][columnIndex] = this.refs['csv-labels-input-' + columnIndex].value;
this.setState(newState);
this.updateImportContextState();

View File

@@ -25,7 +25,9 @@ refer to http://www.clipperz.com.
Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.NotesClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.Notes',
getInitialState: function() {
return {
'notesIndex': this.props.importContext.state('csvData.notesIndex'),

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.TitlesClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.CSV.Titles',
getInitialState: function() {
return {
'titleIndex': this.props.importContext.state('csvData.titleIndex'),

View File

@@ -27,6 +27,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.ImportClass = React.createClass({
//=========================================================================
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.Import',
importHandler: function (anEvent) {
var recordsToImport = this.props.importContext.state('recordsToImport');

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.InputClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.Input',
getInitialState: function() {
return {
'inputString': this.props.importContext.inputString(),
@@ -46,7 +48,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.DataImport.InputClass = React.createClas
var jsonData;
var parsedInput;
var inputString = this.refs['input-textarea'].getDOMNode().value.trim();
var inputString = this.refs['input-textarea'].value.trim();
// this.props.importContext.setData(inputString);
result = {'inputString': inputString};
@@ -80,7 +82,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.DataImport.InputClass = React.createClas
var re = new RegExp('.*<textarea>(.*)<\/textarea>.*','g');
if (re.test(someHtml)) {
textarea = this.refs['input-textarea'].getDOMNode();
textarea = this.refs['input-textarea'];
textarea.innerHTML = someHtml.replace(re, '$1');
result = textarea.innerHTML;
} else {
@@ -208,17 +210,19 @@ Clipperz.PM.UI.Components.ExtraFeatures.DataImport.InputClass = React.createClas
// http://enome.github.io/javascript/2014/03/24/drag-and-drop-with-react-js.html
// https://code.google.com/p/chromium/issues/detail?id=168387
// http://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
anEvent.stopPropagation();
anEvent.preventDefault();
anEvent.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
},
handleTextareaChange: function () {
// var newInputString;
//
// newInputString = this.refs['input-textarea'].getDOMNode().value;
// newInputString = this.refs['input-textarea'].value;
// this.setState({'inputString': newInputString});
// this.props.importContext.setInputString(newInputString);
this.updateTextAreaContent(this.refs['input-textarea'].getDOMNode().value, false);
this.updateTextAreaContent(this.refs['input-textarea'].value, false);
},
//=========================================================================
@@ -247,7 +251,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.DataImport.InputClass = React.createClas
React.DOM.br({}),
React.DOM.a({
'className': 'button',
'onClick': MochiKit.Base.bind(function() { this.refs['upload-input'].getDOMNode().click() }, this),
'onClick': MochiKit.Base.bind(function() { this.refs['upload-input'].click() }, this),
}, "select it manually")
]),
React.DOM.div({'className': 'description'},

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures.DataImport');
Clipperz.PM.UI.Components.ExtraFeatures.DataImport.PreviewClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DataImport.Preview',
getInitialState: function() {
var recordsToImport;
@@ -83,7 +85,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.DataImport.PreviewClass = React.createCl
//-------------------------------------------------------------------------
handleImportTagTextChange: function() {
var newTag = this.refs['importTagText'].getDOMNode().value;
var newTag = this.refs['importTagText'].value;
this.props.importContext.setState('importTag', newTag);
},

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.DeleteAccount');
Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccount',
propTypes: {
// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
// 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired
@@ -50,15 +52,15 @@ Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("DeleteAccount.handleDeleteAccount", {trace: false});
deferredResult.addCallback(this.props.userInfo['checkPassphraseCallback'], this.refs['passphrase'].getDOMNode().value);
deferredResult.addCallback(this.props.userInfo['checkPassphraseCallback'], this.refs['passphrase'].value);
deferredResult.addMethod(this, function(passCheck){
var username = this.refs['username'].getDOMNode().value;
var passphrase = this.refs['passphrase'].getDOMNode().value;
var username = this.refs['username'].value;
var passphrase = this.refs['passphrase'].value;
this.setState({
'username': (username != '') ? (username == this.props.userInfo['username']) ? 'valid' : 'invalid' : 'empty',
'passphrase': (passphrase != '') ? (passCheck) ? 'valid' : 'invalid' : 'empty',
'confirm': this.refs['confirm'].getDOMNode().checked,
'confirm': this.refs['confirm'].checked,
});
});
@@ -87,7 +89,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.DeleteAccountClass = React.createClass({
React.DOM.input({'key':'passphrase', 'className': this.state['passphrase'], 'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase"}),
React.DOM.p({}, [
React.DOM.input({'key':'confirm', 'className':'confirmCheckbox', 'type':'checkbox', 'id':'deleteAccountConfirmCheckbox', 'name':'confirm', 'ref':'confirm'}),
React.DOM.label({'htmlFor':'deleteAccountConfirmCheckbox'}, "All my data will be permanently deleted. I understand that this action cannot be undone or cancelled.")
React.DOM.label({'htmlFor':'deleteAccountConfirmCheckbox'}, "All my data will be permanently deleted. I understand that this action cannot be undone or canceled.")
]),
]),
React.DOM.button({'key':'button', 'type':'submit', 'disabled':!this.shouldEnableDeleteAccountButton(), 'className':'button'}, "Delete my account")

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');
Clipperz.PM.UI.Components.ExtraFeatures.DevicePINClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.DevicePIN',
propTypes: {
// featureSet: React.PropTypes.oneOf(['FULL', 'EXPIRED', 'TRIAL']).isRequired,
// 'level': React.PropTypes.oneOf(['hide', 'info', 'warning', 'error']).isRequired
@@ -59,7 +61,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.DevicePINClass = React.createClass({
},
setFocus: function() {
this.refs['pinValue'].getDOMNode().focus();
this.refs['pinValue'].focus();
},
resetPIN: function() {

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');
Clipperz.PM.UI.Components.ExtraFeatures.OTPClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.OTP',
getInitialState: function() {
return {
// 'selectedOTPs': [],
@@ -82,7 +84,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.OTPClass = React.createClass({
},
handleLabelSave: function (anOTP) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'changeOTPLabel', anOTP.reference(), this.state['otpLabel']);
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'changeOTPLabel', {'reference':anOTP.reference(), 'label':this.state['otpLabel']});
this.handleLabelCancel()
},

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');
Clipperz.PM.UI.Components.ExtraFeatures.PassphraseClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.Passphrase',
propTypes: {
},
@@ -44,18 +46,18 @@ Clipperz.PM.UI.Components.ExtraFeatures.PassphraseClass = React.createClass({
resetForm: function () {
this.setState(this.getInitialState());
this.refs['username'].getDOMNode().value = '';
this.refs['old-passphrase'].getDOMNode().value = '';
this.refs['new-passphrase'].getDOMNode().value = '';
this.refs['confirm-new-passphrase'].getDOMNode().value = '';
this.refs['confirm'].getDOMNode().checked = false;
this.refs['username'].value = '';
this.refs['old-passphrase'].value = '';
this.refs['new-passphrase'].value = '';
this.refs['confirm-new-passphrase'].value = '';
this.refs['confirm'].checked = false;
},
handleChangePassphrase: function(event) {
var newPassphrase;
event.preventDefault();
newPassphrase = this.refs['new-passphrase'].getDOMNode().value;
newPassphrase = this.refs['new-passphrase'].value;
this.resetForm();
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'changePassphrase', newPassphrase);
@@ -65,19 +67,19 @@ Clipperz.PM.UI.Components.ExtraFeatures.PassphraseClass = React.createClass({
var deferredResult;
deferredResult = new Clipperz.Async.Deferred("Passphrase.handleFormChange", {trace: false});
deferredResult.addCallback(this.props.userInfo['checkPassphraseCallback'], this.refs['old-passphrase'].getDOMNode().value);
deferredResult.addCallback(this.props.userInfo['checkPassphraseCallback'], this.refs['old-passphrase'].value);
deferredResult.addMethod(this, function(passCheck){
var username = this.refs['username'].getDOMNode().value;
var oldPassphrase = this.refs['old-passphrase'].getDOMNode().value;
var newPassphrase = this.refs['new-passphrase'].getDOMNode().value;
var confirmNewPassphrase = this.refs['confirm-new-passphrase'].getDOMNode().value;
var username = this.refs['username'].value;
var oldPassphrase = this.refs['old-passphrase'].value;
var newPassphrase = this.refs['new-passphrase'].value;
var confirmNewPassphrase = this.refs['confirm-new-passphrase'].value;
this.setState({
'username': (username != '') ? [(username == this.props.userInfo['username']) ? 'valid' : 'invalid'] : 'empty',
'old-passphrase': (oldPassphrase != '') ? [(passCheck) ? 'valid' : 'invalid'] : 'empty',
'new-passphrase': (newPassphrase != '') ? 'valid' : 'empty',
'confirm-new-passphrase': (confirmNewPassphrase != '') ? [(confirmNewPassphrase == newPassphrase) ? 'valid' : 'invalid'] : 'empty',
'confirm': this.refs['confirm'].getDOMNode().checked,
'confirm': this.refs['confirm'].checked,
});
});

View File

@@ -0,0 +1,118 @@
/*
Copyright 2008-2015 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.UI.Components.ExtraFeatures');
Clipperz.PM.UI.Components.ExtraFeatures.PlanClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.Plan',
propTypes: {
},
formatQuota: function (aValue) {
return filesize(aValue);
},
percentageOfUsedAttachmentQuota: function () {
return this.props['accountInfo']['attachmentQuota']['used'] / this.props['accountInfo']['attachmentQuota']['available'] * 100;
},
//=========================================================================
renderCertificateIcons: function () {
var result;
var totalCertificates;
var publishedCertificates;
var requestedCertificates;
var availableCertificates;
// var i, c;
// result = [];
totalCertificates = this.props['accountInfo']['certificateQuota']['totalNumber'];
publishedCertificates = this.props['accountInfo']['certificateQuota']['used']['published'];
requestedCertificates = this.props['accountInfo']['certificateQuota']['used']['requested'];
availableCertificates = totalCertificates - (publishedCertificates + requestedCertificates);
// c = totalCertificates;
// for (i=0; i<c; i++) {
// var className;
//
// if (i < publishedCertificates) {
// className = 'published';
// } else if (i < (publishedCertificates + requestedCertificates)) {
// className = 'requested';
// } else {
// className = 'available'
// };
// result.push(React.DOM.li({'className':className}, "certificate"));
// }
return React.DOM.ul({}, [
React.DOM.li({'className':'published'}, "published: " + ((publishedCertificates > 0) ? publishedCertificates : '-')),
React.DOM.li({'className':'requested'}, "requested: " + ((requestedCertificates > 0) ? requestedCertificates : '-')),
React.DOM.li({'className':'available'}, "available: " + ((availableCertificates > 0) ? availableCertificates : '-')),
]);
},
render: function () {
return React.DOM.div({className:'extraFeature plan'}, [
React.DOM.div({'className':'header'}, [
React.DOM.h1({}, "Current plan"),
]),
React.DOM.div({'className': 'content'}, [
React.DOM.div({'className': 'attachments'}, [
React.DOM.h2({}, "Attachments quota"),
React.DOM.dl({}, [
React.DOM.dt({}, [
React.DOM.span({'className':'key'}, 'available')
]), React.DOM.dd({}, this.formatQuota(this.props['accountInfo']['attachmentQuota']['available'])),
React.DOM.dt({}, [
React.DOM.span({'className':'key'}, 'used'),
]), React.DOM.dd({}, this.formatQuota(this.props['accountInfo']['attachmentQuota']['used'])),
]),
React.DOM.div({'className': 'progressBar'}, [
React.DOM.span({'className': 'progress', 'style': {'width': this.percentageOfUsedAttachmentQuota() + '%'}})
// MochiKit.Style.setElementDimensions(this.getElement('progress'), {'w': aProgressPercentage}, '%');
])
]),
React.DOM.div({'className': 'certificates'}, [
React.DOM.h2({}, "Certificates"),
this.renderCertificateIcons(),
]),
React.DOM.div({'className': 'upgrade'}, [
React.DOM.button({'className':'button disabled', 'type':'submit'}, "upgrade")
])
])
]);
},
//=========================================================================
});
Clipperz.PM.UI.Components.ExtraFeatures.Plan = React.createFactory(Clipperz.PM.UI.Components.ExtraFeatures.PlanClass);

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.ExtraFeatures');
Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.ExtraFeatures.Preferences',
getInitialState: function() {
return {
'editedPreferences': new Clipperz.KeyValueObjectStore()
@@ -66,7 +68,7 @@ Clipperz.PM.UI.Components.ExtraFeatures.PreferencesClass = React.createClass({
checkboxClick: function (aRef) {
return MochiKit.Base.bind(function (anEvent) {
// console.log("CHECKBOX CLICK", this, this.refs, this.refs[aRef]);
this.refs[aRef].getDOMNode().click();
this.refs[aRef].click();
}, this);
},

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.HelpClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Help',
close: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'closeHelp');
},

View File

@@ -25,6 +25,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.MessageBoxClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.MessageBox',
propTypes: {
'level': React.PropTypes.oneOf(['HIDE', 'INFO', 'WARNING', 'ERROR']).isRequired,
'message': React.PropTypes.string.isRequired

View File

@@ -24,33 +24,13 @@ refer to http://www.clipperz.com.
Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.CardDetailPageClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Pages.CardDetailPage',
propTypes: {
'allTags': React.PropTypes.array,
},
/*
viewComponentProps: function () {
var result;
result = this.props['selectedCard'];
if (result) {
result['style'] = this.props['style'];
}
return result;
},
render: function () {
var result;
if (this.props['mode'] == 'edit') {
result = Clipperz.PM.UI.Components.Cards.Edit(this.viewComponentProps());
} else {
result = Clipperz.PM.UI.Components.Cards.View(this.viewComponentProps());
}
return result;
},
*/
render: function () {
return Clipperz.PM.UI.Components.Cards.Detail(this.props);
}

View File

@@ -25,26 +25,28 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.ErrorPageClass = React.createClass({
getDefaultProps: function () {
return {
// template: Clipperz.PM.UI.Components.PageTemplate
}
},
'propTypes': {
// type: React.PropTypes.oneOf(['PERMANENT', 'TEMPORARY']),
message: React.PropTypes.string.isRequired,
template: React.PropTypes.func
},
displayName: 'Clipperz.PM.UI.Components.Pages.ErrorPage',
render: function () {
return React.DOM.div({className:'error-message'}, this.props.message);
},
// render: function () {
// return new this.props.template({'innerComponent': this._render()});
// }
//console.log("ERROR PAGE", this.props);
return React.DOM.div({}, [
React.DOM.header({}, [
React.DOM.h2({}, 'clipperz')
]),
React.DOM.div({}, [
React.DOM.div({'className':'error-box'}, [
React.DOM.div({}, [
React.DOM.p({}, "Ops!"),
React.DOM.p({}, "Sorry, something went wrong."),
React.DOM.p({}, "Please reload."),
]),
React.DOM.div({'className':'error-message'}, [
React.DOM.p({}, this.props['error'] ? this.props['error']['message'] : '')
])
])
])
])
}
});
Clipperz.PM.UI.Components.Pages.ErrorPage = React.createFactory(Clipperz.PM.UI.Components.Pages.ErrorPageClass);

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Pages.LoginPage',
propTypes: {
mode: React.PropTypes.oneOf(['CREDENTIALS','PIN']).isRequired,
isNewUserRegistrationAvailable: React.PropTypes.bool.isRequired,
@@ -59,7 +61,7 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
handleChange: function (anEvent) {
var refs = this.refs;
var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName] == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
var newState = {};
newState[refName] = anEvent.target.value;
@@ -70,8 +72,8 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
if (this.mode() == 'CREDENTIALS') {
var newState;
var usernameValue = this.refs['username'].getDOMNode().value;
var passphraseValue = this.refs['passphrase'].getDOMNode().value;
var usernameValue = this.refs['username'].value;
var passphraseValue = this.refs['passphrase'].value;
newState = {};
@@ -87,11 +89,11 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
handleCredentialSubmit: function (event) {
event.preventDefault();
this.refs['passphrase'].getDOMNode().blur();
this.refs['passphrase'].blur();
var credentials = {
'username': this.refs['username'].getDOMNode().value,
'passphrase': this.refs['passphrase'].getDOMNode().value
'username': this.refs['username'].value,
'passphrase': this.refs['passphrase'].value
}
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials);
},
@@ -116,7 +118,7 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
},
loginForm: function () {
return React.DOM.form({'key':'form', 'className':'loginForm credentials', 'autoComplete':'off', 'autoCorrect':'off', 'autoCapitalize':'off', 'onChange':this.handleChange, 'onSubmit':this.handleCredentialSubmit}, [
return React.DOM.form({'key':'loginForm', 'className':'loginForm credentials', 'autoComplete':'off', 'autoCorrect':'off', 'autoCapitalize':'off', 'onChange':this.handleChange, 'onSubmit':this.handleCredentialSubmit}, [
React.DOM.div({'key':'fields'},[
React.DOM.label({'key':'username-label', 'htmlFor' :'name'}, "username"),
React.DOM.input({'key':'username', 'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'autoComplete':'off', 'autoCorrect':'off', 'autoCapitalize':'off', 'spellCheck': false}),
@@ -128,10 +130,10 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
},
submitPIN: function (event) {
this.refs['pin'].getDOMNode().blur();
this.refs['pin'].blur();
var credentials = {
pin: this.refs['pin'].getDOMNode().value
pin: this.refs['pin'].value
}
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials);
@@ -153,7 +155,7 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
// handlePinFocus: function(anEvent) {
// // anEvent.preventDefault();
// this.refs['pin'].getDOMNode().focus();
// this.refs['pin'].focus();
// },
// pinFormDigits: function() {
@@ -179,12 +181,13 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
pinForm: function () {
return React.DOM.form({
'key':'pinForm',
'className':'pinForm pin',
'autoComplete':'off',
'onSubmit': function(anEvent) {anEvent.preventDefault();},
}, [
React.DOM.div({'key':'pinFormDiv'},[
React.DOM.label({'htmlFor':'pin'}, "Enter your PIN"),
React.DOM.label({'key':'pinLabel', 'htmlFor':'pin'}, "Enter your PIN"),
React.DOM.input({
'type':'tel',
'name':'pin',
@@ -199,6 +202,7 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
}),
// React.DOM.div({'className': 'pinContainer'}, this.pinFormDigits()),
React.DOM.a({
'key':'pinAnchor',
'className': 'passphraseLogin',
'onClick': this.forcePassphraseLogin,
}, "Login with passphrase")
@@ -212,12 +216,12 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
this.setState({
'pin': ''
})
this.refs['pin'].getDOMNode().focus();
this.refs['pin'].focus();
} else {
if (this.refs['username'].getDOMNode().value == '') {
this.refs['username'].getDOMNode().focus();
if (this.refs['username'].value == '') {
this.refs['username'].focus();
} else{
this.refs['passphrase'].getDOMNode().select();
this.refs['passphrase'].select();
}
}
},
@@ -234,22 +238,22 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
return React.DOM.div({'key':'loginForm', 'className':'loginForm content ' + this.props['style']}, [
Clipperz.PM.UI.Components.AccountStatus(MochiKit.Base.update(this.props['proxyInfo'])),
React.DOM.header({'key':'header'}, [
React.DOM.div({'className':'headerContent'}, [
React.DOM.h3({}, 'clipperz'),
React.DOM.h5({}, 'keep it to yourself'),
React.DOM.div({'key':'div', 'className':'headerContent'}, [
React.DOM.h3({'key':'h3'}, 'clipperz'),
React.DOM.h5({'key':'h5'}, 'keep it to yourself'),
]),
]),
React.DOM.div({'key':'formWrapper', 'className':'form body'}, [
React.DOM.div({'className':'bodyContent'}, [
React.DOM.div({'key':'div', 'className':'bodyContent'}, [
this.mode() == 'PIN' ? this.pinForm() : this.loginForm(),
this.props['isNewUserRegistrationAvailable'] ? registrationLink : null,
]),
]),
React.DOM.div({'key':'afterBody', 'className':'afterBody'}),
React.DOM.div({'className':'other', 'key':'other'}, [
React.DOM.div({'className':'otherContent'}, [
React.DOM.div({'key':'other', 'className':'other'}, [
React.DOM.div({'key':'otherContent', 'className':'otherContent'}, [
React.DOM.div({'key':'links', 'className':'links'}, [
React.DOM.ul({}, [
React.DOM.ul({'key':'ul'}, [
React.DOM.li({'key':'about', 'onClick':this.showUrl('/about/')}, "About"),
React.DOM.li({'key':'terms', 'onClick':this.showUrl('/terms_service/')}, "Terms of service"),
React.DOM.li({'key':'privacy', 'onClick':this.showUrl('/privacy_policy/')}, "Privacy"),
@@ -258,7 +262,7 @@ Clipperz.PM.UI.Components.Pages.LoginPageClass = React.createClass({
])
]),
React.DOM.footer({'key':'footer'}, [
React.DOM.div({'className':'footerContent'}, [
React.DOM.div({'key':'div', 'className':'footerContent'}, [
React.DOM.div({'key':'applicationVersion', 'className':'applicationVersion'}, [
React.DOM.span({'key':'applicationVersionLabel'}, "application version"),
React.DOM.a({'key':'applicationVersionLink', 'href':'https://github.com/clipperz/password-manager/commit/' + Clipperz_version, 'target':'github'}, Clipperz_version)

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.MainPageClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Pages.MainPage',
getDefaultProps: function () {
return {
featureSet: 'FULL',
@@ -69,6 +71,7 @@ Clipperz.PM.UI.Components.Pages.MainPageClass = React.createClass({
result = React.DOM.div({'key':'mainPage', 'className':Clipperz.PM.UI.Components.classNames(classes)}, [
Clipperz.PM.UI.Components.AttachmentQueueBox(this.props),
Clipperz.PM.UI.Components.CertificateQueueBox(this.props),
this.props['style'] != 'extra-wide' ? Clipperz.PM.UI.Components.Panels.SelectionPanel(this.props) : null,
Clipperz.PM.UI.Components.Panels.MainPanel(this.props),
Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel(this.props),

View File

@@ -25,6 +25,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.RegistrationPageClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Pages.RegistrationPage',
getDefaultProps: function () {
return {
steps: [
@@ -126,8 +128,8 @@ Clipperz.PM.UI.Components.Pages.RegistrationPageClass = React.createClass({
toggleCheckbox: function (aCheckboxRef, anEvent) {
var newState = {};
this.refs[aCheckboxRef].getDOMNode().checked = ! this.refs[aCheckboxRef].getDOMNode().checked;
newState[aCheckboxRef] = this.refs[aCheckboxRef].getDOMNode().checked;
this.refs[aCheckboxRef].checked = ! this.refs[aCheckboxRef].checked;
newState[aCheckboxRef] = this.refs[aCheckboxRef].checked;
this.setState(newState);
},
@@ -154,7 +156,7 @@ Clipperz.PM.UI.Components.Pages.RegistrationPageClass = React.createClass({
handleChange: function (anEvent) {
var refs = this.refs;
var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName] == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
var newState = {};
if ((anEvent.target.type == 'checkbox') || (anEvent.target.type == 'radio')) {
@@ -213,12 +215,12 @@ Clipperz.PM.UI.Components.Pages.RegistrationPageClass = React.createClass({
return React.DOM.div({'key':'termsOfService'}, [
React.DOM.div({'key':'termsOfService_choice_1', 'className':'checkboxBlock'}, [
React.DOM.label({'key':'termsOfService_label_1', 'htmlFor':'no_password_recovery'}, "I understand that Clipperz is unable to recover a lost passphrase."),
React.DOM.input({'key':'no_password_recovery', 'type':'checkbox', 'name':'no_password_recovery', 'ref':'no_password_recovery', 'id':'no_password_recovery', 'tabindex':'0'}),
React.DOM.input({'key':'no_password_recovery', 'type':'checkbox', 'name':'no_password_recovery', 'ref':'no_password_recovery', 'id':'no_password_recovery', 'tabIndex':'0'}),
React.DOM.p({'key':'termsOfService_description_1', 'onClick':MochiKit.Base.method(this, 'toggleCheckbox', 'no_password_recovery')}, "I understand that Clipperz is unable to recover a lost passphrase.")
]),
React.DOM.div({'key':'termsOfService_choice_2', 'className':'checkboxBlock'}, [
React.DOM.label({'key':'termsOfService_label_2', 'htmlFor':'agree_terms_of_service'}, "I have read and agreed to the Terms of Service."),
React.DOM.input({'key':'agree_terms_of_service', 'type':'checkbox', 'name':'agree_terms_of_service', 'ref':'agree_terms_of_service', 'id':'agree_terms_of_service', 'tabindex':'1'}),
React.DOM.input({'key':'agree_terms_of_service', 'type':'checkbox', 'name':'agree_terms_of_service', 'ref':'agree_terms_of_service', 'id':'agree_terms_of_service', 'tabIndex':'1'}),
React.DOM.p({'key':'termsOfService_description_2'}, [
React.DOM.span({'key':'termsOfService_description_2_p1', 'onClick':MochiKit.Base.method(this, 'toggleCheckbox', 'agree_terms_of_service')}, "I have read and agreed to the "),
React.DOM.a({'key':'termsOfService_description_2_p2', 'onClick':this.showUrl('/terms_service/')}, "Terms of Service.")
@@ -290,15 +292,15 @@ Clipperz.PM.UI.Components.Pages.RegistrationPageClass = React.createClass({
//=========================================================================
setInitialFocus: function () {
this.refs['username'].getDOMNode().focus();
this.refs['username'].focus();
},
componentDidUpdate: function (prevProps, prevState, rootNode) {
if (prevState['currentStep'] != this.state['currentStep']) {
if (this.state['currentStep'] == 'CREDENTIALS') {
this.refs['passphrase'].getDOMNode().select();
this.refs['passphrase'].select();
} else if (this.state['currentStep'] == 'PASSWORD_VERIFICATION') {
this.refs['verify_passphrase'].getDOMNode().select();
this.refs['verify_passphrase'].select();
}
}
}

View File

@@ -26,10 +26,12 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Pages');
Clipperz.PM.UI.Components.Pages.UnlockPageClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Pages.UnlockPage',
propTypes: {
username: React.PropTypes.string.isRequired,
// username: React.PropTypes.string.isRequired,
mode: React.PropTypes.oneOf(['CREDENTIALS','PIN']).isRequired,
disabled: React.PropTypes.bool.isRequired,
// disabled: React.PropTypes.bool.isRequired,
},
getInitialState: function () {
@@ -70,29 +72,29 @@ Clipperz.PM.UI.Components.Pages.UnlockPageClass = React.createClass({
handlePassphraseSubmit: function (event) {
event.preventDefault();
this.refs['passphrase'].getDOMNode().blur();
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'unlock', this.refs['passphrase'].getDOMNode().value, 'PASSPHRASE');
this.refs['passphrase'].blur();
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'unlock', {'credential':this.refs['passphrase'].value, 'credentialType':'PASSPHRASE'});
// this.resetUnlockForm();
},
submitPIN: function() {
this.refs['pin'].getDOMNode().blur();
this.refs['pin'].blur();
var pin = this.refs['pin'].getDOMNode().value;
var pin = this.refs['pin'].value;
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'unlock', pin, 'PIN');
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'unlock', {'credential':pin, 'credentialType':'PIN'});
// this.resetUnlockForm();
},
resetUnlockForm: function() {
if (this.mode() == 'CREDENTIALS') {
this.refs['passphrase'].getDOMNode().value = '';
this.refs['passphrase'].getDOMNode().blur();
this.refs['passphrase'].value = '';
this.refs['passphrase'].blur();
} else if (this.mode() == 'PIN') {
this.refs['pin'].getDOMNode().value = '';
this.refs['pin'].getDOMNode().blur();
this.refs['pin'].value = '';
this.refs['pin'].blur();
}
},
@@ -104,9 +106,9 @@ Clipperz.PM.UI.Components.Pages.UnlockPageClass = React.createClass({
setInitialFocus: function () {
if (this.mode() == 'PIN') {
this.refs['pin'].getDOMNode().focus();
this.refs['pin'].focus();
} else {
this.refs['passphrase'].getDOMNode().focus();
this.refs['passphrase'].focus();
}
},

View File

@@ -26,12 +26,13 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Panels');
Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanelClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanel',
componentDidMount: function () {
MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, 'closeSettingsPanel', MochiKit.Base.method(this, 'hideExtraFeatureContent'));
},
settingsToggleHandler: function (anEvent) {
//console.log("settingsToggleHandler");
this.hideExtraFeatureContent();
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'toggleSettingsPanel');
},
@@ -120,6 +121,10 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanelClass = React.createClass({
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'updateOTPListAndDetails');
}
if (aComponentName == 'Plan') {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'updateUserAccountInfo');
}
this.setState({
'isFullyOpen':true,
'extraFeatureComponentName': aComponentName,
@@ -147,23 +152,29 @@ Clipperz.PM.UI.Components.Panels.ExtraFeaturesPanelClass = React.createClass({
React.DOM.li({'key':'account', 'className':this.state['index']['account'] ? 'open' : 'closed'}, [
React.DOM.h1({'key':'accountH1', 'onClick':this.toggleIndexState('account')}, "Account"),
React.DOM.ul({'key':'accountUL'}, [
React.DOM.li({'key':'account_preferences', 'onClick':this.toggleExtraFeatureComponent('Preferences'), 'className':(this.state['extraFeatureComponentName'] == 'Preferences') ? 'selected' : ''}, [
React.DOM.h2({'key':'account_preferences_h2'}, "Preferences"),
React.DOM.li({'key':'account_0', 'onClick':this.toggleExtraFeatureComponent('Preferences'), 'className':(this.state['extraFeatureComponentName'] == 'Preferences') ? 'selected' : ''}, [
React.DOM.h2({}, "Preferences"),
]),
React.DOM.li({'key':'account_1', 'onClick':this.toggleExtraFeatureComponent('Passphrase'), 'className':(this.state['extraFeatureComponentName'] == 'Passphrase') ? 'selected' : ''}, [
React.DOM.h2({'key':'account_1_h2'}, "Passphrase"),
React.DOM.h2({}, "Passphrase"),
// React.DOM.div({'key':'account_1_div'}, [
// React.DOM.p({'key':'account_1_p'}, "Change your account passphrase.")
// ])
]),
React.DOM.li({'key':'account_2', 'onClick':this.toggleExtraFeatureComponent('OTP')}, [
React.DOM.li({'key':'account_2', 'onClick':this.toggleExtraFeatureComponent('Plan'), 'className':(this.state['extraFeatureComponentName'] == 'Plan') ? 'selected' : ''}, [
React.DOM.h2({}, "Plan"),
// React.DOM.div({}, [
// React.DOM.p({}, "Current plan.")
// ])
]),
React.DOM.li({'key':'account_3', 'onClick':this.toggleExtraFeatureComponent('OTP')}, [
React.DOM.h2({}, "One-Time Passwords"),
// React.DOM.div({}, [
// React.DOM.p({}, "Manage your OTPs.")
// ])
]),
React.DOM.li({'key':'account_3', 'onClick':this.toggleExtraFeatureComponent('DevicePIN')}, [
React.DOM.li({'key':'account_4', 'onClick':this.toggleExtraFeatureComponent('DevicePIN')}, [
React.DOM.h2({}, "Device PIN"),
// React.DOM.div({}, [
// React.DOM.p({}, "Configure a PIN that will allow to get access to your cards, but only on this device.")

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.Panels.MainPanelClass = React.createClass({
//=========================================================================
displayName: 'Clipperz.PM.UI.Components.Panels.MainPanel',
propTypes: {
'allTags': React.PropTypes.array,
'messageBox': React.PropTypes.object.isRequired,

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components.Panels');
Clipperz.PM.UI.Components.Panels.SelectionPanelClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.Panels.SelectionPanel',
propTypes: {
selectionPanelStatus: React.PropTypes.oneOf(['OPEN', 'CLOSED']).isRequired
},

View File

@@ -26,6 +26,8 @@ Clipperz.Base.module('Clipperz.PM.UI.Components');
Clipperz.PM.UI.Components.RadialProgressIndicatorClass = React.createClass({
displayName: 'Clipperz.PM.UI.Components.RadialProgressIndicator',
propTypes: {
'progress': React.PropTypes.number.isRequired,
},

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.SelectionsClass = React.createClass({
//=========================================================================
displayName: 'Clipperz.PM.UI.Components.Selections',
selectAll: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectAllCards');
},
@@ -36,6 +38,10 @@ Clipperz.PM.UI.Components.SelectionsClass = React.createClass({
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectRecentCards');
},
selectWithCertificate: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCardsWithCertificate');
},
selectWithAttachments: function (anEvent) {
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'selectCardsWithAttachments');
},
@@ -99,6 +105,10 @@ Clipperz.PM.UI.Components.SelectionsClass = React.createClass({
React.DOM.span({'className':'label'}, "Recent"),
React.DOM.span({'className':'count'}, this.props['allCardsCount'] ? '10' : '-')
]),
React.DOM.li({'className':'withCertificateCards', 'onClick': this.selectWithCertificate}, [
React.DOM.span({'className':'label'}, "With certificate"),
React.DOM.span({'className':'count'}, this.props['withCertificateCardsCount'] ? this.props['withCertificateCardsCount'] : '-')
]),
React.DOM.li({'className':'withAttachmentCards', 'onClick': this.selectWithAttachments}, [
React.DOM.span({'className':'label'}, "With attachments"),
React.DOM.span({'className':'count'}, this.props['withAttachmentCardsCount'] ? this.props['withAttachmentCardsCount'] : '-')

View File

@@ -28,6 +28,8 @@ Clipperz.PM.UI.Components.TagIndexItemClass = React.createClass({
//=========================================================================
displayName: 'Clipperz.PM.UI.Components.TagIndexItem',
propTypes: {
'label': React.PropTypes.string.isRequired,
'count': React.PropTypes.number.isRequired,

View File

@@ -33,7 +33,7 @@ Clipperz.PM.UI.ExportController = function(args) {
this._style =
"body {" +
"font-family: 'Dejavu Sans', monospace;" +
"font-family: 'DejaVu Sans Mono', monospace;" +
"margin: 0px;" +
"}" +

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/* Blob.js
* A Blob implementation.
* 2014-07-24

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 2015-03-04

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/**
* filesize
*

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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";
/**

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/*
2015
@version 3.1.3

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/*!
* Modernizr v2.8.2
* www.modernizr.com

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/*!
Papa Parse
v4.1.1

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/*!
Papa Parse
v4.1.1

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
(function () {
global = this

View File

@@ -1,24 +1 @@
/*
Copyright 2008-2015 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/.
*/
!function(){function a(a){global.setImmediate?setImmediate(a):global.importScripts?setTimeout(a):(c++,d[c]=a,global.postMessage(c,"*"))}function b(c){"use strict";function d(a,b,c,d){if("object"!=typeof j&&"function"!=typeof j||"function"!=typeof a)d();else try{var e=0;a.call(j,function(a){e++||(j=a,b())},function(a){e++||(j=a,c())})}catch(f){j=f,c()}}function e(){var a;try{a=j&&j.then}catch(b){return j=b,i=2,e()}d(a,function(){i=1,e()},function(){i=2,e()},function(){try{1==i&&"function"==typeof f?j=f(j):2==i&&"function"==typeof g&&(j=g(j),i=1)}catch(b){return j=b,l()}j==h?(j=TypeError(),l()):d(a,function(){l(3)},l,function(){l(1==i&&3)})})}if("function"!=typeof c&&void 0!=c)throw TypeError();if("object"!=typeof this||this&&this.then)throw TypeError();var f,g,h=this,i=0,j=0,k=[];h.promise=h,h.resolve=function(b){return f=h.fn,g=h.er,i||(j=b,i=1,a(e)),h},h.reject=function(b){return f=h.fn,g=h.er,i||(j=b,i=2,a(e)),h},h._d=1,h.then=function(a,c){if(1!=this._d)throw TypeError();var d=new b;return d.fn=a,d.er=c,3==i?d.resolve(j):4==i?d.reject(j):k.push(d),d},h["catch"]=function(a){return h.then(null,a)};var l=function(a){i=a||4,k.map(function(a){3==i&&a.resolve(j)||a.reject(j)})};try{"function"==typeof c&&c(h.resolve,h.reject)}catch(m){h.reject(m)}return h}global=this;var c=1,d={},e=!1;global.setImmediate||global.addEventListener("message",function(b){if(b.source==global)if(e)a(d[b.data]);else{e=!0;try{d[b.data]()}catch(b){}delete d[b.data],e=!1}}),b.resolve=function(a){if(1!=this._d)throw TypeError();return a instanceof b?a:new b(function(b){b(a)})},b.reject=function(a){if(1!=this._d)throw TypeError();return new b(function(b,c){c(a)})},b.all=function(a){function c(b,e){if(e)return d.resolve(e);if(b)return d.reject(b);var f=a.reduce(function(a,b){return b&&b.then?a+1:a},0);0==f&&d.resolve(a),a.map(function(b,d){b&&b.then&&b.then(function(b){return a[d]=b,c(),b},c)})}if(1!=this._d)throw TypeError();if(!(a instanceof Array))return b.reject(TypeError());var d=new b;return c(),d},b.race=function(a){function c(b,e){if(e)return d.resolve(e);if(b)return d.reject(b);var f=a.reduce(function(a,b){return b&&b.then?a+1:a},0);0==f&&d.resolve(a),a.map(function(a,b){a&&a.then&&a.then(function(a){c(null,a)},c)})}if(1!=this._d)throw TypeError();if(!(a instanceof Array))return b.reject(TypeError());if(0==a.length)return new b;var d=new b;return c(),d},b._d=1,"undefined"!=typeof module?module.exports=b:global.Promise=global.Promise||b}();

View File

@@ -0,0 +1,42 @@
/**
* ReactDOM v0.14.7
*
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
// Based off https://github.com/ForbesLindesay/umd/blob/master/template.js
;(function(f) {
// CommonJS
if (typeof exports === "object" && typeof module !== "undefined") {
module.exports = f(require('react'));
// RequireJS
} else if (typeof define === "function" && define.amd) {
define(['react'], f);
// <script>
} else {
var g;
if (typeof window !== "undefined") {
g = window;
} else if (typeof global !== "undefined") {
g = global;
} else if (typeof self !== "undefined") {
g = self;
} else {
// works providing we're not in "use strict";
// needed for Java 8 Nashorn
// see https://github.com/facebook/react/issues/3037
g = this;
}
g.ReactDOM = f(g.React);
}
})(function(React) {
return React.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
});

View File

@@ -0,0 +1,12 @@
/**
* ReactDOM v0.14.7
*
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e(require("react"));else if("function"==typeof define&&define.amd)define(["react"],e);else{var f;f="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,f.ReactDOM=e(f.React)}}(function(e){return e.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/**
* @file Web Cryptography API shim
* @author Artem S Vybornov <vybornov@gmail.com>

View File

@@ -43,7 +43,7 @@ MochiKit.Logging.logError("## MainController - GENERIC ERROR" + "\n" + "==>> " +
return result;
}
React.initializeTouchEvents(true);
//React.initializeTouchEvents(true);
Clipperz.PM.RunTime = {};

View File

@@ -0,0 +1,9 @@
var npm = {
base58: require('bs58'),
bitcoin: require('bitcoinjs-lib'),
ecurve: require('ecurve'),
BigInteger: require('bigi'),
buffer: require('buffer')
}
module.exports = npm

12609
frontend/delta/js/npm/npm.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,3 @@
/*
Copyright 2008-2015 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/.
*/
/**
* @preserve XDate v@VERSION
* Docs & Licensing: http://arshaw.com/xdate/

View File

@@ -37,6 +37,36 @@
* Copyright (c) 2000-2013, Leemon Baird
* License: Public Domain
# PapaParse (https://github.com/mholt/PapaParse)
* Repository: @papaparse.repository@ (version: @papaparse.version@ - commit: @papaparse.commit@)
* Copyright (c) 2015 Matthew Holt
* License: http://opensource.org/licenses/mit-license.php
# xDate (http://arshaw.com/xdate/)
* Repository: @xDate.repository@ (version: @xDate.version@ - commit: @xDate.commit@)
* Copyright (c) 2011 Adam Shaw, http://arshaw.com/xdate/
* License: http://opensource.org/licenses/mit-license.php
# FileSize (http://filesizejs.com)
* Repository: @filesize.repository@ (version: @filesize.version@ - commit: @filesize.commit@)
* Copyright (c) 2015, Jason Mulligan
* License: https://github.com/avoidwork/filesize.js/blob/master/LICENSE
# Promiz (https://github.com/Zolmeister/promiz.git)
* Repository: @promiz.repository@ (version: @promiz.version@ - commit: @promiz.commit@)
* Copyright (c) 2014 Zolmeister
* License: http://opensource.org/licenses/mit-license.php
# WebCryptoShim (https://vibornoff.github.io/webcrypto-shim/)
* Repository: @webcryptoshim.repository@ (version: @webcryptoshim.version@ - commit: @webcryptoshim.commit@)
* Copyright (c) 2015 Artem S Vybornov
* License: http://opensource.org/licenses/mit-license.php
# BitCoinJS-lib (http://bitcoinjs.org)
* Repository: @bitcoinjs-lib.repository@ (version: @bitcoinjs-lib.version@ - commit: @bitcoinjs-lib.commit@)
* Copyright (c) 2011-2016 bitcoinjs-lib contributors
* License: http://opensource.org/licenses/mit-license.php
===============================================================================
Other code snippets used in the first demo of the program, and still

View File

@@ -42,7 +42,15 @@
"webcryptoshim.repository": "https://github.com/vibornoff/webcrypto-shim.git",
"webcryptoshim.version": "0.1.0",
"webcryptoshim.commit": "fb82fe0936adb4813a9b513991266a2bef51d32b"
"webcryptoshim.commit": "fb82fe0936adb4813a9b513991266a2bef51d32b",
"bitcoinjs-lib.repository": "https://github.com/bitcoinjs/bitcoinjs-lib",
"bitcoinjs-lib.version": "2.1.4",
"bitcoinjs-lib.commit": "e25ce449dcd932d34d15e47a269a54ea155bdb4d",
"buffer.repository": "https://github.com/feross/buffer",
"buffer.version": "4.3.0",
"buffer.commit": "e52c33af77a79060c4d423a5447bd77c331ee9c9"
},
"html.template": "index_template.html",
@@ -63,10 +71,10 @@
"MochiKit/Selector.js",
"-- MochiKit/Visual.js",
"-- React/react-0.13.3.js",
"React/react.min-0.13.3.js",
"-- #React/react-with-addons-0.13.1.js",
"-- #React/react-with-addons.min-0.13.1.js",
"-- React/react-0.14.7.js",
"-- React/react-dom-0.14.7.js",
"React/react.min-0.14.7.js",
"React/react-dom.min-0.14.7.js",
"MouseTrap/mousetrap.js",
"-- MouseTrap/mousetrap-bind-dictionary.js",
@@ -84,7 +92,7 @@
"PapaParse/papaparse.js",
"-- PapaParse/papaparse.min.js",
"xDate/xdate.js",
"xDate/xdate-0.8.js",
"-- Filesize/filesize.js",
"Filesize/filesize.min.js",
@@ -93,6 +101,8 @@
"Promiz/promiz.min.js",
"WebcryptoShim/webcrypto-shim.js",
"npm/npm.js",
"-- IT WOULD BE NICE TO BE ABLE TO GET RID OF THESE IMPORTS",
"Clipperz/YUI/Utils.js",
"Clipperz/YUI/DomHelper.js",
@@ -163,6 +173,7 @@
"Clipperz/PM/DataModel/User.Header.RecordIndex.js",
"Clipperz/PM/DataModel/User.Header.Preferences.js",
"Clipperz/PM/DataModel/User.Header.OneTimePasswords.js",
"Clipperz/PM/DataModel/User.Header.Wallet.js",
"Clipperz/PM/DataModel/User.AccountInfo.js",
"Clipperz/PM/DataModel/Record.js",
"Clipperz/PM/DataModel/Record.Version.js",
@@ -195,6 +206,7 @@
"Clipperz/PM/UI/Components/TagIndexItem.js",
"Clipperz/PM/UI/Components/Help.js",
"Clipperz/PM/UI/Components/AttachmentQueueBox.js",
"Clipperz/PM/UI/Components/CertificateQueueBox.js",
"Clipperz/PM/UI/Components/RadialProgressIndicator.js",
"Clipperz/PM/UI/Components/ExpiredPanel.js",
@@ -213,6 +225,7 @@
"Clipperz/PM/UI/Components/ExtraFeatures/DevicePIN.js",
"Clipperz/PM/UI/Components/ExtraFeatures/Preferences.js",
"Clipperz/PM/UI/Components/ExtraFeatures/Passphrase.js",
"Clipperz/PM/UI/Components/ExtraFeatures/Plan.js",
"Clipperz/PM/UI/Components/ExtraFeatures/OTP.js",
"Clipperz/PM/UI/Components/ExtraFeatures/DeleteAccount.js",
"Clipperz/PM/UI/Components/ExtraFeatures/DataExport.js",
@@ -238,6 +251,8 @@
"Clipperz/PM/UI/Components/Cards/EditToolbar.js",
"Clipperz/PM/UI/Components/Cards/TagEditor.js",
"Clipperz/PM/UI/Components/Cards/PasswordGenerator.js",
"Clipperz/PM/UI/Components/Cards/CertificateRenderer.js",
"Clipperz/PM/UI/Components/AccountStatus.js",
@@ -245,6 +260,7 @@
"-- Clipperz/PM/UI/MainDesktopController.js",
"Clipperz/PM/UI/DirectLoginController.js",
"Clipperz/PM/UI/ExportController.js",
"Clipperz/PM/UI/CertificateDownloadController.js",
"Clipperz/PM/UI/AttachmentController.js",
"Clipperz/PM/UI/ImportContext.js",
"main.js"

View File

@@ -36,6 +36,7 @@ refer to http://www.clipperz.com.
@import "style/loadingPage";
@import "style/loginPage";
@import "style/errorPage";
@import "style/selectionPanel";
@import "style/settingsPanel";
@import "style/accountStatus";

File diff suppressed because one or more lines are too long

View File

@@ -109,6 +109,7 @@ input[type=text] {
&.selection {
@include flex($selectionPanelFlexWidth, 0);
max-width: 20%;
// height: 100%;
// @include overflow-auto;
}
@@ -326,6 +327,8 @@ div.cardToolbar {
@include flex(4);
text-align: center;
font-size: 28pt;
&.tag, &.search {
font-size: 14pt;
}
@@ -374,17 +377,16 @@ div.cardToolbar {
h3 {
font-weight: bold;
font-size: 33pt;
letter-spacing: -10px;
font-size: 32pt;
letter-spacing: -12px;
}
.badge {
position: absolute;
margin-left: -12px;
margin-left: -8px;
width: auto;
height: 16px;
line-height: 17px;
line-height: 16px;
background-color: #1863a1;
color: white;
text-align: center;
@@ -399,10 +401,49 @@ div.cardToolbar {
&.bottom {
top: 25px;
margin-left: -42px;
margin-left: -36px;
}
}
}
.certificateQueueToggleButton {
@include icon-font();
margin-right: 10px;
h3 {
font-weight: bold;
font-size: 22pt;
letter-spacing: -12px;
line-height: 34pt;
}
.badge {
position: absolute;
margin-left: -4px;
width: auto;
height: 16px;
line-height: 16px;
// background-color: #1863a1;
color: white;
text-align: center;
font-size: 8pt;
font-weight: bold;
border-radius: 8px;
padding: 0px 4px;
&.top {
top: 8px;
background-color: #666;
}
&.bottom {
top: 26px;
// margin-left: -42px;
background-color: #EAD143;
}
}
}
}
}
@@ -450,7 +491,7 @@ div.attachmentQueueStatus {
border-width: 0 $arrow-size $arrow-size $arrow-size;
border-color: transparent transparent black transparent;
position: fixed;
right: 70px;
right: 66px;
top: calc(49px - #{$arrow-size});
}
@@ -609,6 +650,208 @@ div.attachmentQueueStatus {
}
}
//=============================================================================
$arrow-size: 8px;
div.certificateQueueStatus {
position: fixed;
top: calc(48px - #{$arrow-size});
right: 0;
padding: 0;
color: white;
z-index: 10;
height: calc(100% - 48px);
pointer-events: none;
.arrow {
width: 0;
height: 0;
border-style: solid;
border-width: 0 $arrow-size $arrow-size $arrow-size;
border-color: transparent transparent black transparent;
position: fixed;
right: 110px;
top: calc(49px - #{$arrow-size});
}
&.closed {
display: none;
visibility: hidden;
}
p {
padding: 1em;
}
ul {
pointer-events: all;
max-height: 100%;
overflow-y: auto;
overflow-x: hidden;
white-space: nowrap;
background: black;
margin-top: $arrow-size;
padding: 0;
box-shadow: 0px 2px 5px #888888;
li {
padding: 0 0 0 1em;;
border-bottom: 1px solid white;
span {
display: inline-block;
vertical-align: middle;
height: 40px;
line-height: 40px;
}
&.requested {
.icon {
color: #999;
}
.close {
// display: none;
visibility: hidden;
}
}
&.published {
.icon {
color: #EAD143;
}
}
.icon {
@include icon-font();
color: white;
width: 30px;
}
.name {
width: 250px;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
.status {
width: 150px;
text-align: right;
.statusString {
font-size: 14px;
}
// .processIcon {
// margin-left: 1em;
// font-size: 15pt;
// }
}
.close {
a {
@include icon-font();
display: inline-block;
width: 50px;
text-align: center;
letter-spacing: 1px;
font-size: 20px;
@include userSelectNone();
}
a:hover {
color: lightgray;
cursor: pointer;
}
}
// .progress {
// width: 50px;
// height: 30px;
// text-align: center;
//
// @include userSelectNone();
// }
&:last-child {
border-bottom: 0;
}
}
}
/*
.radialProgressIndicator {
height: 25px;
.background {
fill: white;
}
.progress {
fill: $clipperz-orange;
}
}
*/
}
.narrow {
.certificateQueueStatus {
width:100%;
ul {
white-space: inherit;
li {
padding-top: .2em;
padding-bottom: .2em;
span {
height: 25px;
line-height: 30px;
}
.name {
width: 225px;
}
.status {
padding-left: 30px;
text-align: left;
width: calc(100% - 100px);
line-height: 20px;
font-size: .8em;
.processIcon {
margin-left: 0.5em;
}
}
.close {
height: inherit;
line-height: inherit;
float: right;
display: block;
margin-top: -5px;
}
// .progress {
// padding-left: 18px;
// margin-top: -24px;
// }
}
}
}
}
//=============================================================================
div.cardContent {
// @include flex(flex-grow); // ???

View File

@@ -116,46 +116,6 @@ div.overlay {
}
}
// http://37signals.com/svn/posts/2577-loading-spinner-animation-using-css-and-webkit
div.spinner {
position: relative;
width: 100px;
height: 100px;
left: 50%;
top: 40%;
margin-left: -50px;
margin-top: -50px;
// display: inline-block;
display: block;
div {
width: 12%;
height: 26%;
background: #ffffff;
position: absolute;
left: 44.5%;
top: 37%;
opacity: 0;
@include animation(overlay-spin, 1s, linear, infinite);
@include border-radius(50px);
@include box-shadow(0, 0, 3px, rgba(0,0,0,0.2));
}
div.bar01 {@include transform( 0deg, 0, -142%); @include animation-delay(-0.00000s);}
div.bar02 {@include transform( 30deg, 0, -142%); @include animation-delay(-0.91670s);}
div.bar03 {@include transform( 60deg, 0, -142%); @include animation-delay(-0.83300s);}
div.bar04 {@include transform( 90deg, 0, -142%); @include animation-delay(-0.75000s);}
div.bar05 {@include transform(120deg, 0, -142%); @include animation-delay(-0.66700s);}
div.bar06 {@include transform(150deg, 0, -142%); @include animation-delay(-0.58330s);}
div.bar07 {@include transform(180deg, 0, -142%); @include animation-delay(-0.50000s);}
div.bar08 {@include transform(210deg, 0, -142%); @include animation-delay(-0.41667s);}
div.bar09 {@include transform(240deg, 0, -142%); @include animation-delay(-0.33300s);}
div.bar10 {@include transform(270deg, 0, -142%); @include animation-delay(-0.25000s);}
div.bar11 {@include transform(300deg, 0, -142%); @include animation-delay(-0.16670s);}
div.bar12 {@include transform(330deg, 0, -142%); @include animation-delay(-0.08330s);}
}
&.spinnerOnly {
.spinner {
@@ -183,6 +143,46 @@ div.overlay {
}
}
// http://37signals.com/svn/posts/2577-loading-spinner-animation-using-css-and-webkit
div.spinner {
position: relative;
width: 100px;
height: 100px;
left: 50%;
top: 40%;
margin-left: -50px;
margin-top: -50px;
display: block;
div {
width: 12%;
height: 26%;
background: #ffffff;
position: absolute;
left: 44.5%;
top: 37%;
opacity: 0;
@include animation(overlay-spin, 1s, linear, infinite);
@include border-radius(50px);
@include box-shadow(0, 0, 3px, rgba(0,0,0,0.2));
}
div.bar01 {@include transform( 0deg, 0, -142%); @include animation-delay(-0.00000s);}
div.bar02 {@include transform( 30deg, 0, -142%); @include animation-delay(-0.91670s);}
div.bar03 {@include transform( 60deg, 0, -142%); @include animation-delay(-0.83300s);}
div.bar04 {@include transform( 90deg, 0, -142%); @include animation-delay(-0.75000s);}
div.bar05 {@include transform(120deg, 0, -142%); @include animation-delay(-0.66700s);}
div.bar06 {@include transform(150deg, 0, -142%); @include animation-delay(-0.58330s);}
div.bar07 {@include transform(180deg, 0, -142%); @include animation-delay(-0.50000s);}
div.bar08 {@include transform(210deg, 0, -142%); @include animation-delay(-0.41667s);}
div.bar09 {@include transform(240deg, 0, -142%); @include animation-delay(-0.33300s);}
div.bar10 {@include transform(270deg, 0, -142%); @include animation-delay(-0.25000s);}
div.bar11 {@include transform(300deg, 0, -142%); @include animation-delay(-0.16670s);}
div.bar12 {@include transform(330deg, 0, -142%); @include animation-delay(-0.08330s);}
}
//========================================================
@include keyframes(overlay-spin) {

File diff suppressed because one or more lines are too long

View File

@@ -21,38 +21,47 @@ refer to http://www.clipperz.com.
*/
div.dialog {
@include box-shadow(0px, 2px, 5px, rgba(50, 50, 50, 0.75));
@include border-radius(8px);
div.dialogBox {
div.dialog {
@include box-shadow(0px, 2px, 5px, rgba(50, 50, 50, 0.75));
@include border-radius(8px);
max-width: 70%;
background-color: white;
padding: 30px;
box-shadow: 4px 4px 6px 5px rgba(0,0,0, 0.3);
max-width: 70%;
background-color: white;
padding: 30px;
box-shadow: 4px 4px 6px 5px rgba(0,0,0, 0.3);
h3.message {
font-size: 18pt;
font-weight: bold;
padding-bottom: 20px;
white-space: pre-wrap;
word-wrap: break-word;
}
h3.message {
font-size: 18pt;
font-weight: bold;
padding-bottom: 20px;
white-space: pre-wrap;
word-wrap: break-word;
}
div.answers {
div.answers {
div.button {
@include border-radius(4);
// border: 1px solid black;
margin-left: 10px;
font-size: 16pt;
padding: 15px 25px;
background-color: #ddd;
div.button {
@include border-radius(4);
// border: 1px solid black;
margin-left: 10px;
font-size: 16pt;
padding: 15px 25px;
background-color: #ddd;
&.isDefault {
font-weight: bold;
color: white;
background-color: #666;
&.isDefault {
font-weight: bold;
color: white;
background-color: #666;
}
&.disabled {
cursor: default;
color: #aaa;
background-color: #ddd;
font-weight: normal;
}
}
}
}
}
}

View File

@@ -0,0 +1,71 @@
/*
Copyright 2008-2015 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/.
*/
#errorPage {
header {
background-color: $clipperz-orange;
color: white;
height: 48px;
text-align: center;
@include icon-font();
h2 {
font-size: 24pt;
padding-top: 8px;
}
}
.error-box {
@include border-radius(16px);
background-color: #000;
color: white;
width: 300px;
height: 300px;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
div {
padding: 20px;
padding-bottom: 0px;
p {
font-size: 20pt;
line-height: 1.5em;
text-align: center;
padding-bottom: 8px;
}
&.error-message {
padding-top: 0px;
p {
font-size: 14pt;
color: #333;
}
}
}
}
}

View File

@@ -57,8 +57,11 @@ refer to http://www.clipperz.com.
content: "recent";
}
&.withCertificateCards:before {
content: "certificate";
}
&.withAttachmentCards:before {
// transform: scaleX(-1);
content: "attachment";
font-weight: bold;
}
@@ -270,6 +273,16 @@ $selectionColor: $clipperz-orange;
}
}
#selections.WITH_CERTIFICATE {
li.withCertificateCards {
color: $selectionColor;
&:before {
color: white;
}
}
}
#selections.WITH_ATTACHMENTS {
li.withAttachmentCards {
color: $selectionColor;

Some files were not shown because too many files have changed in this diff Show More