summaryrefslogtreecommitdiff
path: root/frontend/delta/js/Clipperz/PM
Unidiff
Diffstat (limited to 'frontend/delta/js/Clipperz/PM') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/delta/js/Clipperz/PM/DataModel/DevicePreferences.js90
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy.js1
-rwxr-xr-xfrontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js4
-rw-r--r--frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js4
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js85
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/CardList.js13
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/Checkbox.js44
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js6
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js7
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/PreferencePage.js88
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/MainController.js130
11 files changed, 425 insertions, 47 deletions
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DevicePreferences.js b/frontend/delta/js/Clipperz/PM/DataModel/DevicePreferences.js
new file mode 100644
index 0000000..ff3b33f
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/DataModel/DevicePreferences.js
@@ -0,0 +1,90 @@
1/*
2
3Copyright 2008-2013 Clipperz Srl
4
5This file is part of Clipperz, the online password manager.
6For further information about its features and functionalities please
7refer to http://www.clipperz.com.
8
9* Clipperz is free software: you can redistribute it and/or modify it
10 under the terms of the GNU Affero General Public License as published
11 by the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14* Clipperz is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU Affero General Public License for more details.
18
19* You should have received a copy of the GNU Affero General Public
20 License along with Clipperz. If not, see http://www.gnu.org/licenses/.
21
22*/
23
24Clipperz.PM.DataModel.DevicePreferences = function (args) {
25 args = args || {};
26
27 this._data = null;
28
29 Clipperz.PM.DataModel.DevicePreferences.superclass.constructor.apply(this, arguments);
30
31 return this;
32}
33
34Clipperz.Base.extend(Clipperz.PM.DataModel.DevicePreferences, Object, {
35
36 toString: function () {
37 return "Clipperz.PM.DataModel.DevicePreferences";
38 },
39
40 //-------------------------------------------------------------------------
41
42 shouldStoreDataLocally: function () {
43 return (localStorage.getItem('shouldStoreDataLocally') === 'true');
44 },
45
46 setShouldStoreDataLocally: function (aValue) {
47 localStorage.setItem('shouldStoreDataLocally', aValue);
48 },
49
50 //-------------------------------------------------------------------------
51
52 setAccountDataWityResponse: function (aResponse) {
53 localStorage.setItem('clipperz_dump_data', aResponse['data']);
54 localStorage.setItem('clipperz_dump_version',aResponse['version']);
55 localStorage.setItem('clipperz_dump_date', new Date());
56
57 this._data = null;
58 },
59
60 accountData: function () {
61 if (this._data == null) {
62 vardata;
63
64 data = localStorage.getItem('clipperz_dump_data');
65 if (data != null) {
66 this._data = JSON.parse(data);
67 }
68 }
69
70 return this._data;
71 },
72
73 latestDownload: function () {
74 varresult;
75 vardate;
76
77 date = localStorage.getItem('clipperz_dump_date');
78 if (date != null) {
79 result = new Date(date);
80 } else {
81 result = null;
82 }
83
84 return result;
85 },
86
87 //=========================================================================
88 __syntaxFix__: "syntax fix"
89});
90
diff --git a/frontend/delta/js/Clipperz/PM/Proxy.js b/frontend/delta/js/Clipperz/PM/Proxy.js
index 2ac684a..71c784f 100644
--- a/frontend/delta/js/Clipperz/PM/Proxy.js
+++ b/frontend/delta/js/Clipperz/PM/Proxy.js
@@ -152,6 +152,7 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, {
152 'sendMessage': function (aFunctionName, someParameters) { 152 'sendMessage': function (aFunctionName, someParameters) {
153 var deferredResult; 153 var deferredResult;
154 154
155console.log("PROXY.sendMessage", aFunctionName, someParameters);
155 //TODO: read actual application version for a property set at build time 156 //TODO: read actual application version for a property set at build time
156 deferredResult = new Clipperz.Async.Deferred("Proxy.sendMessage", {trace:false}); 157 deferredResult = new Clipperz.Async.Deferred("Proxy.sendMessage", {trace:false});
157 deferredResult.addMethod(this, '_sendMessage', aFunctionName, 'fake-app-version'); 158 deferredResult.addMethod(this, '_sendMessage', aFunctionName, 'fake-app-version');
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js
index 1638d99..6deee3d 100755
--- a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js
@@ -57,8 +57,8 @@ Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, {
57 version: aVersion, 57 version: aVersion,
58 parameters: Clipperz.Base.serializeJSON(someParameters) 58 parameters: Clipperz.Base.serializeJSON(someParameters)
59 }; 59 };
60 60console.log("PROXY.JSON._sendMessage", parameters);
61 deferredResult = new Clipperz.Async.Deferred("Proxy.JSON.sendMessage", {trace:false}); 61 deferredResult = new Clipperz.Async.Deferred("Proxy.JSON._sendMessage", {trace:false});
62 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestSent'); 62 deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestSent');
63 deferredResult.addCallback(MochiKit.Async.doXHR, this.url(), { 63 deferredResult.addCallback(MochiKit.Async.doXHR, this.url(), {
64 method:'POST', 64 method:'POST',
diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js
index a3c238c..3f16f70 100644
--- a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js
+++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js
@@ -30,7 +30,9 @@ try { if (typeof(Clipperz.PM.Proxy.Offline.DataStore) == 'undefined') { throw ""
30Clipperz.PM.Proxy.Offline.LocalStorageDataStore = function(args) { 30Clipperz.PM.Proxy.Offline.LocalStorageDataStore = function(args) {
31 args = args || {}; 31 args = args || {};
32 32
33 this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null); 33 //this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null);
34 this._data = JSON.parse(localStorage.getItem('clipperz_dump_data'));
35
34 this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly); 36 this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly);
35 this._shouldPayTolls = args.shouldPayTolls || false; 37 this._shouldPayTolls = args.shouldPayTolls || false;
36 38
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js
index df514a2..12ddce3 100644
--- a/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js
@@ -37,6 +37,7 @@ Clipperz.PM.UI.Components.CardDetail = React.createClass({
37 return { 37 return {
38 // showSearch: false, 38 // showSearch: false,
39 // searchTimer: null, 39 // searchTimer: null,
40 unmaskedFields: new Clipperz.Set(),
40 starred: false 41 starred: false
41 }; 42 };
42 }, 43 },
@@ -45,6 +46,32 @@ Clipperz.PM.UI.Components.CardDetail = React.createClass({
45 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'runDirectLogin', {record:this.props.card['reference'], directLogin:aDirectLoginReference}); 46 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'runDirectLogin', {record:this.props.card['reference'], directLogin:aDirectLoginReference});
46 }, 47 },
47 48
49 toggleFieldVisibility: function (aField, anEvent) {
50 var unmaskedFields;
51 var fieldReference;
52
53 unmaskedFields = this.state['unmaskedFields'];
54 fieldReference = aField['reference']
55 if (unmaskedFields.contains(fieldReference)) {
56 unmaskedFields.remove(fieldReference)
57 } else {
58 unmaskedFields.add(fieldReference)
59 }
60
61 this.setState({'unmaskedFields': unmaskedFields});
62 },
63
64 handleGoAction: function (aField, anEvent) {
65 var newWindow;
66
67 newWindow = MochiKit.DOM.currentWindow().open(aField['value'], '_blank');
68 newWindow.focus();
69 },
70
71 handleEmailAction: function (aField, anEvent) {
72 MochiKit.DOM.currentWindow().location = 'mailto:' + aField['value'];
73 },
74
48 //========================================================================= 75 //=========================================================================
49 76
50 normalizeFieldValue: function (aValue) { 77 normalizeFieldValue: function (aValue) {
@@ -61,30 +88,56 @@ Clipperz.PM.UI.Components.CardDetail = React.createClass({
61 return result; 88 return result;
62 }, 89 },
63 90
64 renderField: function (aField) { 91 renderFieldActionButton: function (aField) {
65//console.log("FIELD", aField); 92 // varactionLabel;
66 varactionLabel; 93 var result;
67 94
68 if (aField['actionType'] == 'URL') { 95 if (aField['actionType'] == 'URL') {
69 actionLabel = "go"; 96 result = React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'handleGoAction', aField)}, [
97 React.DOM.a({className:aField['actionType']}, "go")
98 ]);
70 } else if (aField['actionType'] == 'PASSWORD') { 99 } else if (aField['actionType'] == 'PASSWORD') {
71 actionLabel = "locked"; 100 var icon;
101
102 if (this.state['unmaskedFields'].contains(aField['reference'])) {
103 icon = "unlocked";
104 } else {
105 icon = "locked";
106 }
107 result =React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'toggleFieldVisibility', aField)}, [
108 React.DOM.a({className:aField['actionType']}, icon)
109 ]);
72 } else if (aField['actionType'] == 'EMAIL') { 110 } else if (aField['actionType'] == 'EMAIL') {
73 actionLabel = "email"; 111 result =React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'handleEmailAction', aField)}, [
112 React.DOM.a({className:aField['actionType']}, "email")
113 ]);
74 } else { 114 } else {
75 actionLabel = ""; 115 result = null;
116 }
117
118 return result;
119 },
120
121 renderField: function (aField) {
122//console.log("FIELD", aField);
123 var fieldExtraClass;
124
125 fieldExtraClass = aField['actionType'];
126 if (this.state['unmaskedFields'].contains(aField['reference'])) {
127 fieldExtraClass = fieldExtraClass + ' unlocked';
76 } 128 }
77 129
78 returnReact.DOM.div({className:'listItem ' + aField['actionType']}, [ 130 returnReact.DOM.div({className:'listItem ' + fieldExtraClass, key:aField['reference']}, [
79 React.DOM.div({className:'fieldWrapper'}, [ 131 React.DOM.div({className:'fieldWrapper'}, [
80 React.DOM.div({className:'fieldInnerWrapper'}, [ 132 React.DOM.div({className:'fieldInnerWrapper'}, [
81 React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aField['label'])), 133 React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aField['label'])),
82 React.DOM.div({className:'valueWrapper'}, React.DOM.span({className:'value ' + aField['actionType']}, this.normalizeFieldValue(aField['value']))) 134 React.DOM.div({className:'valueWrapper'}, React.DOM.span({className:'value ' + fieldExtraClass}, this.normalizeFieldValue(aField['value'])))
83 ]) 135 ])
84 ]), 136 ]),
85 React.DOM.div({className:'actionWrapper'}, [ 137 this.renderFieldActionButton(aField)
86 React.DOM.div({className:aField['actionType']}, actionLabel) 138 // React.DOM.div({className:'actionWrapper'}, [
87 ]) 139 // React.DOM.div({className:aField['actionType']}, actionLabel)
140 // ])
88 ]); 141 ]);
89 }, 142 },
90 143
@@ -98,7 +151,8 @@ Clipperz.PM.UI.Components.CardDetail = React.createClass({
98 }, 151 },
99 152
100 handleBackClick: function (anEvent) { 153 handleBackClick: function (anEvent) {
101 window.history.back(); 154 // window.history.back();
155 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack');
102 }, 156 },
103 157
104 handleStarClick: function (anEvent) { 158 handleStarClick: function (anEvent) {
@@ -109,7 +163,7 @@ Clipperz.PM.UI.Components.CardDetail = React.createClass({
109 163
110 render: function () { 164 render: function () {
111 var card = this.props.card; 165 var card = this.props.card;
112 var starredStatus = (this.state['starred'] ? "starred" : "unstarred"); 166 // var starredStatus = (this.state['starred'] ? "starred" : "unstarred");
113 167
114 if ((typeof(card['fields']) != 'undefined') && (card['notes'] != '')) { 168 if ((typeof(card['fields']) != 'undefined') && (card['notes'] != '')) {
115 card['fields'].push({ 'actionType': 'NOTES', 'isHidden': false, 'label': "notes", 'reference': "notes", 'value': card['notes'] }) 169 card['fields'].push({ 'actionType': 'NOTES', 'isHidden': false, 'label': "notes", 'reference': "notes", 'value': card['notes'] })
@@ -118,9 +172,8 @@ Clipperz.PM.UI.Components.CardDetail = React.createClass({
118 returnReact.DOM.div({className:'cardDetail'}, [ 172 returnReact.DOM.div({className:'cardDetail'}, [
119 React.DOM.div({className:'header'}, [ 173 React.DOM.div({className:'header'}, [
120 React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title)), 174 React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title)),
121 // React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title + ' ' + card.title + ' ' + card.title + ' ' + card.title)),
122 React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")), 175 React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")),
123 React.DOM.div({className:'starWrapper'}, React.DOM.a({className:'star', onClick:this.handleStarClick}, starredStatus)) 176 // React.DOM.div({className:'starWrapper'}, React.DOM.a({className:'star', onClick:this.handleStarClick}, starredStatus))
124 ]), 177 ]),
125 React.DOM.div({className:'content'}, [ 178 React.DOM.div({className:'content'}, [
126 card.fields ? React.DOM.div({className:'fields'}, MochiKit.Base.map(this.renderField, card.fields)) : null, 179 card.fields ? React.DOM.div({className:'fields'}, MochiKit.Base.map(this.renderField, card.fields)) : null,
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js
index 66d20f1..5a44a4a 100644
--- a/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js
@@ -97,11 +97,18 @@ console.log("focusOnSearchField", this.refs['searchField']);
97 97
98 //========================================================================= 98 //=========================================================================
99 99
100 showPreferences: function (anEvent) {
101 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showPreferences', anEvent);
102 },
103
104 //=========================================================================
105
100 cardItem: function (aRecordReference) { 106 cardItem: function (aRecordReference) {
101 varreference = aRecordReference['_reference']; 107 varreference = aRecordReference['_reference'];
102 varselectedCard = (reference == this.props.selectedCard); 108 varselectedCard = (reference == this.props.selectedCard);
103 109
104 returnReact.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleClickOnCardDetail', reference)}, [ 110 //TODO: verify if it is possible to put the onClick handler on the container 'div', instead of adding it to each 'div' item.
111 returnReact.DOM.div({className:'listItem', key:reference, onClick:MochiKit.Base.method(this, 'handleClickOnCardDetail', reference)}, [
105 React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label)), 112 React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label)),
106 // React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label)), 113 // React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label)),
107 React.DOM.div({className:'faviconWrapper'}, aRecordReference.favicon ? React.DOM.img({className:'favicon', src:aRecordReference.favicon}) : React.DOM.div({className:'favicon'}, '\u00A0')), 114 React.DOM.div({className:'faviconWrapper'}, aRecordReference.favicon ? React.DOM.img({className:'favicon', src:aRecordReference.favicon}) : React.DOM.div({className:'favicon'}, '\u00A0')),
@@ -146,9 +153,9 @@ console.log("focusOnSearchField", this.refs['searchField']);
146 React.DOM.div({className:'header'}, [ 153 React.DOM.div({className:'header'}, [
147 React.DOM.a({className:'account'}, 'clipperz'), 154 React.DOM.a({className:'account'}, 'clipperz'),
148 React.DOM.div({className:'features'}, [ 155 React.DOM.div({className:'features'}, [
149 React.DOM.a({className:'addCard'}, 'add'), 156 // React.DOM.a({className:'addCard'}, 'add'),
150 React.DOM.a({className:'search ' + (this.state.showSearch ? 'selected' : ''), onClick:this.toggleSearch}, 'search'), 157 React.DOM.a({className:'search ' + (this.state.showSearch ? 'selected' : ''), onClick:this.toggleSearch}, 'search'),
151 React.DOM.a({className:'settings'}, 'settings') 158 React.DOM.a({className:'settings', onClick:this.showPreferences}, 'settings')
152 ]), 159 ]),
153 // this.searchBox() 160 // this.searchBox()
154 ]), 161 ]),
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Checkbox.js b/frontend/delta/js/Clipperz/PM/UI/Components/Checkbox.js
new file mode 100644
index 0000000..9538063
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/Checkbox.js
@@ -0,0 +1,44 @@
1/*
2
3Copyright 2008-2013 Clipperz Srl
4
5This file is part of Clipperz, the online password manager.
6For further information about its features and functionalities please
7refer to http://www.clipperz.com.
8
9* Clipperz is free software: you can redistribute it and/or modify it
10 under the terms of the GNU Affero General Public License as published
11 by the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14* Clipperz is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU Affero General Public License for more details.
18
19* You should have received a copy of the GNU Affero General Public
20 License along with Clipperz. If not, see http://www.gnu.org/licenses/.
21
22*/
23
24Clipperz.PM.UI.Components.Checkbox = React.createClass({
25 //http://development.tobypitman.com/iphoneCheckboxes/iphoneCheckboxes2.html
26
27 propTypes: {
28 'checked': React.PropTypes.bool.isRequired,
29 'id': React.PropTypes.string.isRequired,
30 'eventHandler':React.PropTypes.func.isRequired
31 },
32
33 //=========================================================================
34
35 render: function () {
36 returnReact.DOM.div({className:'checkbox', onClick:this.props['eventHandler']}, [
37 React.DOM.input({name:this.props['id'], id:this.props['id'], value:this.props['id'], type:'checkbox', checked:this.props['checked']}),
38 React.DOM.label({className:'check', 'for':this.props['id']}),
39 React.DOM.label({className:'info', 'for':this.props['id']}, "enable local storage")
40 ]);
41 }
42
43 //=========================================================================
44});
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js
index 2b5b4a4..801549f 100644
--- a/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js
@@ -92,14 +92,14 @@ Clipperz.PM.UI.Components.LoginForm = React.createClass({
92 92
93 loginForm: function () { 93 loginForm: function () {
94 registrationLink =React.DOM.div({'className':'registrationLink'}, [ 94 registrationLink =React.DOM.div({'className':'registrationLink'}, [
95 React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Need an account") 95 React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Sign up")
96 ]); 96 ]);
97 returnReact.DOM.div({'className':'loginForm credentials'},[ 97 returnReact.DOM.div({'className':'loginForm credentials'},[
98 React.DOM.form({onChange: this.handleChange, onSubmit:this.handleCredentialSubmit}, [ 98 React.DOM.form({onChange: this.handleChange, onSubmit:this.handleCredentialSubmit}, [
99 React.DOM.div(null,[ 99 React.DOM.div(null,[
100 React.DOM.label({'for':'name'}, "username"), 100 React.DOM.label({'for' :'name'}, "username"),
101 React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'}), 101 React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'}),
102 React.DOM.label({'for':'passphrase'}, "passphrase"), 102 React.DOM.label({'for' :'passphrase'}, "passphrase"),
103 React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'}) 103 React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'})
104 ]), 104 ]),
105 React.DOM.button({'type':'submit', 'disabled':!this.shouldEnableLoginButton(), 'className':'button'}, "login") 105 React.DOM.button({'type':'submit', 'disabled':!this.shouldEnableLoginButton(), 'className':'button'}, "login")
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js b/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js
index cc4a06c..cb5f81a 100644
--- a/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js
@@ -94,9 +94,10 @@ Clipperz.Base.extend(Clipperz.PM.UI.Components.Overlay, Object, {
94 }, 94 },
95 95
96 'hide': function () { 96 'hide': function () {
97 MochiKit.DOM.removeElementClass(this.element(), 'ios-overlay-show'); 97 var element = this.element();
98 MochiKit.DOM.addElementClass(this.element(), 'ios-overlay-hide'); 98 MochiKit.DOM.removeElementClass(element, 'ios-overlay-show');
99 MochiKit.Async.callLater(1, MochiKit.Style.hideElement, this.element()); 99 MochiKit.DOM.addElementClass(element, 'ios-overlay-hide');
100 MochiKit.Async.callLater(1, MochiKit.Style.hideElement, element);
100 }, 101 },
101 102
102 'hideSpinner': function () { 103 'hideSpinner': function () {
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/PreferencePage.js b/frontend/delta/js/Clipperz/PM/UI/Components/PreferencePage.js
new file mode 100644
index 0000000..822acc2
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/Components/PreferencePage.js
@@ -0,0 +1,88 @@
1/*
2
3Copyright 2008-2013 Clipperz Srl
4
5This file is part of Clipperz, the online password manager.
6For further information about its features and functionalities please
7refer to http://www.clipperz.com.
8
9* Clipperz is free software: you can redistribute it and/or modify it
10 under the terms of the GNU Affero General Public License as published
11 by the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14* Clipperz is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU Affero General Public License for more details.
18
19* You should have received a copy of the GNU Affero General Public
20 License along with Clipperz. If not, see http://www.gnu.org/licenses/.
21
22*/
23
24Clipperz.PM.UI.Components.PreferencePage = React.createClass({
25
26 getDefaultProps: function () {
27 return {
28 }
29 },
30
31 propTypes: {
32 // card: React.PropTypes.object.isRequired
33 // checked: React.PropTypes.boolean.isRequired
34 },
35
36 getInitialState: function () {
37 // return {
38 // shouldStoreDataLocally: false
39 // };
40 },
41
42 handleBackClick: function (anEvent) {
43 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack');
44 },
45
46 toggleShouldStoreDataLocally: function (anEvent) {
47 // this.setState({shouldStoreDataLocally: !this.state['shouldStoreDataLocally']});
48 Clipperz.PM.DataModel.devicePreferences.setShouldStoreDataLocally(!Clipperz.PM.DataModel.devicePreferences.shouldStoreDataLocally());
49 this.setState({});
50 },
51
52 shouldStoreDataLocally: function () {
53 return Clipperz.PM.DataModel.devicePreferences.shouldStoreDataLocally();
54 },
55
56 syncNow: function (anEvent) {
57 MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'synchronizeLocalData');
58 },
59
60 //=========================================================================
61
62 render: function () {
63 returnReact.DOM.div({className:'preferences'}, [
64 React.DOM.div({className:'header'}, [
65 React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, "Preferences")),
66 React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")),
67 ]),
68 React.DOM.div({className:'content'}, [
69 React.DOM.form(null, [
70 React.DOM.div({className:'section'}, [
71 React.DOM.h4(null, "Local storage"),
72 React.DOM.p(null, "Store you account data locally for offline viewing"),
73 new Clipperz.PM.UI.Components.Checkbox({'id':'shouldStoreLocally_checkbox', 'checked':this.shouldStoreDataLocally(), 'eventHandler':this.toggleShouldStoreDataLocally}),
74 this.shouldStoreDataLocally() ? React.DOM.div({className:'syncInfo'}, [
75 // React.DOM.h5(null, "data were never synchronized before"),
76 React.DOM.a({className:'button', onClick:this.syncNow}, "Sync now")
77 ]) : null
78 ])
79 ])
80 ]),
81 React.DOM.div({className:'footer'}, [
82
83 ])
84 ]);
85 }
86
87 //=========================================================================
88});
diff --git a/frontend/delta/js/Clipperz/PM/UI/MainController.js b/frontend/delta/js/Clipperz/PM/UI/MainController.js
index da7540e..20ff041 100644
--- a/frontend/delta/js/Clipperz/PM/UI/MainController.js
+++ b/frontend/delta/js/Clipperz/PM/UI/MainController.js
@@ -26,7 +26,7 @@ Clipperz.Base.module('Clipperz.PM.UI');
26Clipperz.PM.UI.MainController = function() { 26Clipperz.PM.UI.MainController = function() {
27 var pages; 27 var pages;
28 28
29 this._proxy = null; 29 // this._proxy = null;
30 this._user = null; 30 this._user = null;
31 this._filter= ''; 31 this._filter= '';
32 32
@@ -39,12 +39,14 @@ Clipperz.PM.UI.MainController = function() {
39 'registrationPage':new Clipperz.PM.UI.Components.RegistrationWizard(), 39 'registrationPage':new Clipperz.PM.UI.Components.RegistrationWizard(),
40 'cardListPage': new Clipperz.PM.UI.Components.CardList(), 40 'cardListPage': new Clipperz.PM.UI.Components.CardList(),
41 'cardDetailPage':new Clipperz.PM.UI.Components.CardDetail({card: {}}), 41 'cardDetailPage':new Clipperz.PM.UI.Components.CardDetail({card: {}}),
42 'preferencePage':new Clipperz.PM.UI.Components.PreferencePage(),
42 'errorPage': new Clipperz.PM.UI.Components.ErrorPage({message:''}) 43 'errorPage': new Clipperz.PM.UI.Components.ErrorPage({message:''})
43 }; 44 };
44 45
45 MochiKit.Base.map(function (anId) {React.renderComponent(pages[anId], MochiKit.DOM.getElement(anId))}, MochiKit.Base.keys(pages)); 46 MochiKit.Base.map(function (anId) {React.renderComponent(pages[anId], MochiKit.DOM.getElement(anId))}, MochiKit.Base.keys(pages));
46 this._pages = pages; 47 this._pages = pages;
47 this.registerForNotificationCenterEvents(); 48 this.registerForNotificationCenterEvents();
49 MochiKit.Signal.connect(MochiKit.DOM.currentDocument(), 'onselectionchange', this, 'selectionChangeHandler');
48 50
49 return this; 51 return this;
50} 52}
@@ -73,10 +75,12 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
73 75
74 isOnline: function() { 76 isOnline: function() {
75 return navigator.onLine; 77 return navigator.onLine;
78 // return false;
76 }, 79 },
77 80
78 hasLocalData: function() { 81 hasLocalData: function() {
79 return false; 82 // return false;
83 return (Clipperz.PM.DataModel.devicePreferences.accountData() != null);
80 }, 84 },
81 85
82 loginMode: function () { 86 loginMode: function () {
@@ -98,26 +102,41 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
98 102
99 //========================================================================= 103 //=========================================================================
100 104
105 showOfflineError: function () {
106console.log("THE BROWSER IS OFFLINE");
107 },
108
101 selectInitialProxy: function () { 109 selectInitialProxy: function () {
102 if (this.isOnline()) { 110 if (this.isOnline()) {
103 this._proxy = Clipperz.PM.Proxy.defaultProxy; 111 // this._proxy = Clipperz.PM.Proxy.defaultProxy;
104 } else { 112 } else {
105 if (this.hasLocalData()) { 113 if (this.hasLocalData()) {
106 this._proxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false}); 114 // this._proxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false});
115 Clipperz.PM.Proxy.defaultProxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false});
107 } else { 116 } else {
108 this.showOfflineError(); 117 this.showOfflineError();
109 } 118 }
110 } 119 }
111 }, 120 },
112 121
113 proxy: function () { 122 //proxy: function () {
114 return this._proxy; 123 // return this._proxy;
115 }, 124 //},
116 125
117 //========================================================================= 126 //=========================================================================
118 127
119 registerForNotificationCenterEvents: function () { 128 registerForNotificationCenterEvents: function () {
120 var events= ['doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack', 'showRecord', 'searchCards', 'runDirectLogin']; 129 var events= [
130 'doLogin',
131 'registerNewUser',
132 'showRegistrationForm',
133 'goBack',
134 'showRecord',
135 'searchCards',
136 'showPreferences',
137 'runDirectLogin',
138 'synchronizeLocalData'
139 ];
121 var self= this; 140 var self= this;
122 141
123 MochiKit.Base.map(function (anEvent) { 142 MochiKit.Base.map(function (anEvent) {
@@ -130,12 +149,53 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
130 149
131 //------------------------------------------------------------------------- 150 //-------------------------------------------------------------------------
132 151
152 selectionChangeHandler: function (anEvent) {
153 varselection;
154 varselectionRange;
155 varselectionNode;
156 varvalueElement;
157 //other hints: http://www.bearpanther.com/2013/05/27/easy-text-selection-in-mobile-safari/
158 //SELECTION: https://developer.mozilla.org/en-US/docs/Web/API/Selection
159 //RANGE: https://developer.mozilla.org/en-US/docs/Web/API/Range
160 //NODE TYPES: https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeType
161
162 selection = MochiKit.DOM.currentWindow().getSelection();
163//console.log("-- selection", selection);
164 selectionRange = selection.getRangeAt(0);
165 selectionNode = selectionRange.startContainer.childNodes[selectionRange.startOffset];
166//console.log("-- selectionNode", selectionNode);
167
168 if (selectionNode != undefined) {
169 valueElement = MochiKit.DOM.getFirstElementByTagAndClassName('*', 'value', selectionNode);
170//console.log("-- valueElement", valueElement);
171 }
172
173 if ((valueElement != null) && (valueElement != selectionNode)) {
174 var range;
175 range = MochiKit.DOM.currentDocument().createRange();
176 range.selectNodeContents(valueElement);
177 selection.removeAllRanges();
178 selection.addRange(range);
179
180 anEvent.preventDefault();
181 anEvent.stopPropagation();
182
183//console.log("updated selection", MochiKit.DOM.currentWindow().getSelection());
184 }
185//console.log("-----------");
186 },
187
188 //-------------------------------------------------------------------------
189
133 run: function (parameters) { 190 run: function (parameters) {
134 var shouldShowRegistrationForm; 191 var shouldShowRegistrationForm;
192 varcanRegisterNewUsers;
193
194 canRegisterNewUsers = Clipperz.PM.Proxy.defaultProxy.canRegisterNewUsers();
135 195
136 this.selectInitialProxy(); 196 this.selectInitialProxy();
137 shouldShowRegistrationForm = parameters['shouldShowRegistrationForm'] && this.proxy().canRegisterNewUsers(); 197 shouldShowRegistrationForm = parameters['shouldShowRegistrationForm'] && canRegisterNewUsers;
138 this.pages()['loginPage'].setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()}); 198 this.pages()['loginPage'].setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable':canRegisterNewUsers});
139 199
140 if (shouldShowRegistrationForm) { 200 if (shouldShowRegistrationForm) {
141 this.showRegistrationForm(); 201 this.showRegistrationForm();
@@ -151,7 +211,7 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
151 varloginFormPage; 211 varloginFormPage;
152 212
153 loginFormPage = this.pages()['loginPage']; 213 loginFormPage = this.pages()['loginPage'];
154 loginFormPage.setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()}); 214 loginFormPage.setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable':Clipperz.PM.Proxy.defaultProxy.canRegisterNewUsers()});
155 this.moveInPage(this.currentPage(), 'loginPage'); 215 this.moveInPage(this.currentPage(), 'loginPage');
156 MochiKit.Async.callLater(0.5, MochiKit.Base.method(loginFormPage, 'setInitialFocus')); 216 MochiKit.Async.callLater(0.5, MochiKit.Base.method(loginFormPage, 'setInitialFocus'));
157 }, 217 },
@@ -202,9 +262,9 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
202 deferredResult.addErrback(MochiKit.Base.bind(function (anEvent, anError) { 262 deferredResult.addErrback(MochiKit.Base.bind(function (anEvent, anError) {
203 if (anError['isPermanent'] != true) { 263 if (anError['isPermanent'] != true) {
204 this.pages()['loginPage'].setProps({disabled:false, 'mode':this.loginMode()}); 264 this.pages()['loginPage'].setProps({disabled:false, 'mode':this.loginMode()});
205 this.pages()['loginPage'].setInitialFocus(); 265 this.pages()['loginPage'].setInitialFocus();
206 } 266 }
207 return anError; 267 return anError;
208 }, this, event)) 268 }, this, event))
209 deferredResult.callback(); 269 deferredResult.callback();
210 270
@@ -323,8 +383,11 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
323 383
324 runApplication: function () { 384 runApplication: function () {
325 MochiKit.Signal.connect(window, 'onpopstate',MochiKit.Base.method(this, 'historyGoBack')); 385 MochiKit.Signal.connect(window, 'onpopstate',MochiKit.Base.method(this, 'historyGoBack'));
386 /// TODO: remove this TEST HACK
326 this.moveInPage(this.currentPage(), 'cardListPage'); 387 this.moveInPage(this.currentPage(), 'cardListPage');
327 return this.showRecordList(); 388 return this.showRecordList();
389
390 // this.moveInPage(this.currentPage(), 'preferencePage');
328 }, 391 },
329 392
330 showRecord: function (aRecordReference) { 393 showRecord: function (aRecordReference) {
@@ -333,7 +396,6 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
333 396
334 this.pages()['cardListPage'].setProps({selectedCard:aRecordReference}); 397 this.pages()['cardListPage'].setProps({selectedCard:aRecordReference});
335 deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:false}); 398 deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:false});
336 // deferredResult.addMethod(this.user(), 'getRecord', aRecordReference['_reference']);
337 deferredResult.addMethod(this.user(), 'getRecord', aRecordReference); 399 deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
338 deferredResult.addMethodcaller('content'); 400 deferredResult.addMethodcaller('content');
339 deferredResult.addCallback(MochiKit.Base.bind(function (aCard) { 401 deferredResult.addCallback(MochiKit.Base.bind(function (aCard) {
@@ -348,12 +410,10 @@ MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
348 }, 410 },
349 411
350 runDirectLogin: function (someParameters) { 412 runDirectLogin: function (someParameters) {
351console.log("RUN DIRECT LOGIN", someParameters); 413//console.log("RUN DIRECT LOGIN", someParameters);
352 vardeferredResult; 414 vardeferredResult;
353 415
354 // this.pages()['cardListPage'].setProps({selectedCard:aRecordReference});
355 deferredResult = new Clipperz.Async.Deferred('MainController.runDirectLogin', {trace:false}); 416 deferredResult = new Clipperz.Async.Deferred('MainController.runDirectLogin', {trace:false});
356 // deferredResult.addMethod(this.user(), 'getRecord', aRecordReference['_reference']);
357 deferredResult.addMethod(this.user(), 'getRecord', someParameters['record']); 417 deferredResult.addMethod(this.user(), 'getRecord', someParameters['record']);
358 deferredResult.addMethodcaller('directLoginWithReference', someParameters['directLogin']); 418 deferredResult.addMethodcaller('directLoginWithReference', someParameters['directLogin']);
359 deferredResult.addCallback(Clipperz.PM.UI.DirectLoginRunner.openDirectLogin); 419 deferredResult.addCallback(Clipperz.PM.UI.DirectLoginRunner.openDirectLogin);
@@ -363,13 +423,26 @@ console.log("RUN DIRECT LOGIN", someParameters);
363 }, 423 },
364 424
365 shouldExitApp: function (anEvent) { 425 shouldExitApp: function (anEvent) {
366console.log("SHOULD EXIT APP"); 426//console.log("SHOULD EXIT APP");
367 anEvent.preventDefault(); 427 anEvent.preventDefault();
368 anEvent.stopPropagation(); 428 anEvent.stopPropagation();
369 }, 429 },
370 430
371 //========================================================================= 431 //=========================================================================
372 432
433 showPreferences: function (anEvent) {
434 vardeferredResult;
435
436 this.pages()['preferencePage'].setProps({});
437 deferredResult = new Clipperz.Async.Deferred('MainController.showPreferences', {trace:false});
438 deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'preferencePage', true);
439 deferredResult.callback();
440
441 return deferredResult;
442 },
443
444 //=========================================================================
445
373 genericErrorHandler: function (anEvent, anError) { 446 genericErrorHandler: function (anEvent, anError) {
374 var errorMessage; 447 var errorMessage;
375 varresult; 448 varresult;
@@ -480,6 +553,25 @@ console.log("SHOULD EXIT APP");
480 }, 553 },
481 554
482 //========================================================================= 555 //=========================================================================
556
557 synchronizeLocalData: function (anEvent) {
558 vardeferredResult;
559
560 deferredResult = new Clipperz.Async.Deferred('MainController.synchronizeLocalData', {trace:true});
561 // deferredResult.addMethod(this.proxy(), 'message', 'downloadAccountData', {});
562 deferredResult.addMethod(this.user().connection(), 'message', 'downloadAccountData', {});
563 deferredResult.addCallback(function (aResult) {
564 Clipperz.PM.DataModel.devicePreferences.setAccountDataWityResponse(aResult);
565 // localStorage.setItem('clipperz_dump_data', aResult['data']);
566 // localStorage.setItem('clipperz_dump_version', aResult['version']);
567 // localStorage.setItem('clipperz_dump_date', new Date());
568 })
569 deferredResult.callback();
570
571 return deferredResult;
572 },
573
574 //=========================================================================
483/* 575/*
484 wrongAppVersion: function (anError) { 576 wrongAppVersion: function (anError) {
485 // this.pages()['errorPage'].setProps({message:anError.message}); 577 // this.pages()['errorPage'].setProps({message:anError.message});