Diffstat (limited to 'frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js') (more/less context) (ignore whitespace changes)
-rw-r--r-- | frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js | 85 |
1 files changed, 69 insertions, 16 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 | |||
@@ -1,142 +1,195 @@ | |||
1 | /* | 1 | /* |
2 | 2 | ||
3 | Copyright 2008-2013 Clipperz Srl | 3 | Copyright 2008-2013 Clipperz Srl |
4 | 4 | ||
5 | This file is part of Clipperz, the online password manager. | 5 | This file is part of Clipperz, the online password manager. |
6 | For further information about its features and functionalities please | 6 | For further information about its features and functionalities please |
7 | refer to http://www.clipperz.com. | 7 | refer to http://www.clipperz.com. |
8 | 8 | ||
9 | * Clipperz is free software: you can redistribute it and/or modify it | 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 | 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 | 11 | by the Free Software Foundation, either version 3 of the License, or |
12 | (at your option) any later version. | 12 | (at your option) any later version. |
13 | 13 | ||
14 | * Clipperz is distributed in the hope that it will be useful, but | 14 | * Clipperz is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. | 17 | See the GNU Affero General Public License for more details. |
18 | 18 | ||
19 | * You should have received a copy of the GNU Affero General Public | 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/. | 20 | License along with Clipperz. If not, see http://www.gnu.org/licenses/. |
21 | 21 | ||
22 | */ | 22 | */ |
23 | 23 | ||
24 | Clipperz.PM.UI.Components.CardDetail = React.createClass({ | 24 | Clipperz.PM.UI.Components.CardDetail = React.createClass({ |
25 | 25 | ||
26 | getDefaultProps: function () { | 26 | getDefaultProps: function () { |
27 | return { | 27 | return { |
28 | // searchDelay: 0.3 | 28 | // searchDelay: 0.3 |
29 | } | 29 | } |
30 | }, | 30 | }, |
31 | 31 | ||
32 | propTypes: { | 32 | propTypes: { |
33 | card: React.PropTypes.object.isRequired | 33 | card: React.PropTypes.object.isRequired |
34 | }, | 34 | }, |
35 | 35 | ||
36 | getInitialState: function () { | 36 | getInitialState: function () { |
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 | }, |
43 | 44 | ||
44 | handleDirectLoginClick: function (aDirectLoginReference, anEvent) { | 45 | handleDirectLoginClick: function (aDirectLoginReference, anEvent) { |
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) { |
51 | varresult = []; | 78 | varresult = []; |
52 | varrows = aValue.split('\n'); | 79 | varrows = aValue.split('\n'); |
53 | 80 | ||
54 | for (var i = 0; i < rows.length; i++) { | 81 | for (var i = 0; i < rows.length; i++) { |
55 | if (i > 0) { | 82 | if (i > 0) { |
56 | result.push(React.DOM.br()); | 83 | result.push(React.DOM.br()); |
57 | } | 84 | } |
58 | result.push(rows[i].replace(/[\s]/g, '\u00A0')); | 85 | result.push(rows[i].replace(/[\s]/g, '\u00A0')); |
59 | } | 86 | } |
60 | 87 | ||
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 | ||
91 | renderDirectLogin: function (aDirectLogin) { | 144 | renderDirectLogin: function (aDirectLogin) { |
92 | //console.log("DIRECT LOGIN", aDirectLogin); | 145 | //console.log("DIRECT LOGIN", aDirectLogin); |
93 | returnReact.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleDirectLoginClick', aDirectLogin['reference'])}, [ | 146 | returnReact.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleDirectLoginClick', aDirectLogin['reference'])}, [ |
94 | React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aDirectLogin['label'])), | 147 | React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aDirectLogin['label'])), |
95 | React.DOM.div({className:'faviconWrapper'}, React.DOM.img({className:'favicon', src:aDirectLogin['favicon']})), | 148 | React.DOM.div({className:'faviconWrapper'}, React.DOM.img({className:'favicon', src:aDirectLogin['favicon']})), |
96 | React.DOM.div({className:'directLoginLinkWrapper'}, React.DOM.span({className:'directLoginLink'}, "go")) | 149 | React.DOM.div({className:'directLoginLinkWrapper'}, React.DOM.span({className:'directLoginLink'}, "go")) |
97 | ]); | 150 | ]); |
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) { |
105 | this.setState({starred: !this.state['starred']}); | 159 | this.setState({starred: !this.state['starred']}); |
106 | }, | 160 | }, |
107 | 161 | ||
108 | //========================================================================= | 162 | //========================================================================= |
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'] }) |
116 | } | 170 | } |
117 | 171 | ||
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, |
127 | card.directLogins ? React.DOM.div({className:'directLogins'}, MochiKit.Base.map(this.renderDirectLogin,card.directLogins)): null | 180 | card.directLogins ? React.DOM.div({className:'directLogins'}, MochiKit.Base.map(this.renderDirectLogin,card.directLogins)): null |
128 | ]), | 181 | ]), |
129 | React.DOM.div({className:'footer'}, [ | 182 | React.DOM.div({className:'footer'}, [ |
130 | /* | 183 | /* |
131 | // React.DOM.a({className:'cancel'}, "cancel"), | 184 | // React.DOM.a({className:'cancel'}, "cancel"), |
132 | // React.DOM.a({className:'save'}, "save") | 185 | // React.DOM.a({className:'save'}, "save") |
133 | 186 | ||
134 | React.DOM.a({className:'cancel button'}, "failed"), | 187 | React.DOM.a({className:'cancel button'}, "failed"), |
135 | React.DOM.a({className:'save button'}, "done") | 188 | React.DOM.a({className:'save button'}, "done") |
136 | */ | 189 | */ |
137 | ]) | 190 | ]) |
138 | ]); | 191 | ]); |
139 | } | 192 | } |
140 | 193 | ||
141 | //========================================================================= | 194 | //========================================================================= |
142 | }); | 195 | }); |