summaryrefslogtreecommitdiff
path: root/frontend/delta/js/Clipperz/PM/UI/Components
authorGiulio Cesare Solaroli <giulio.cesare@clipperz.com>2013-10-02 07:59:30 (UTC)
committer Giulio Cesare Solaroli <giulio.cesare@clipperz.com>2013-10-02 07:59:30 (UTC)
commit1180b7b195157aaeb4f0d5380e0c886bbd06c2e2 (patch) (unidiff)
tree709e33a09d9325d382aabaf0a0828e20ebdb96db /frontend/delta/js/Clipperz/PM/UI/Components
parent20bea94ab6b91c85b171dcf86baba0a64169d508 (diff)
downloadclipperz-1180b7b195157aaeb4f0d5380e0c886bbd06c2e2.zip
clipperz-1180b7b195157aaeb4f0d5380e0c886bbd06c2e2.tar.gz
clipperz-1180b7b195157aaeb4f0d5380e0c886bbd06c2e2.tar.bz2
Updated /delta
Switched from less to scss. Still no build script to update the final CSS, though. Added preliminary support for storing account data on browser's local storage for offline viewing. No public backend currently support this feature.
Diffstat (limited to 'frontend/delta/js/Clipperz/PM/UI/Components') (more/less context) (ignore whitespace changes)
-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
6 files changed, 218 insertions, 25 deletions
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});