Diffstat (limited to 'frontend/delta/js/Clipperz/PM/UI/Components') (more/less context) (show whitespace changes)
7 files changed, 894 insertions, 0 deletions
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js new file mode 100644 index 0000000..df514a2 --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer 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 | |||
24 | Clipperz.PM.UI.Components.CardDetail = React.createClass({ | ||
25 | |||
26 | getDefaultProps: function () { | ||
27 | return { | ||
28 | // searchDelay: 0.3 | ||
29 | } | ||
30 | }, | ||
31 | |||
32 | propTypes: { | ||
33 | card: React.PropTypes.object.isRequired | ||
34 | }, | ||
35 | |||
36 | getInitialState: function () { | ||
37 | return { | ||
38 | // showSearch: false, | ||
39 | // searchTimer: null, | ||
40 | starred: false | ||
41 | }; | ||
42 | }, | ||
43 | |||
44 | handleDirectLoginClick: function (aDirectLoginReference, anEvent) { | ||
45 | MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'runDirectLogin', {record:this.props.card['reference'], directLogin:aDirectLoginReference}); | ||
46 | }, | ||
47 | |||
48 | //========================================================================= | ||
49 | |||
50 | normalizeFieldValue: function (aValue) { | ||
51 | varresult = []; | ||
52 | varrows = aValue.split('\n'); | ||
53 | |||
54 | for (var i = 0; i < rows.length; i++) { | ||
55 | if (i > 0) { | ||
56 | result.push(React.DOM.br()); | ||
57 | } | ||
58 | result.push(rows[i].replace(/[\s]/g, '\u00A0')); | ||
59 | } | ||
60 | |||
61 | return result; | ||
62 | }, | ||
63 | |||
64 | renderField: function (aField) { | ||
65 | //console.log("FIELD", aField); | ||
66 | varactionLabel; | ||
67 | |||
68 | if (aField['actionType'] == 'URL') { | ||
69 | actionLabel = "go"; | ||
70 | } else if (aField['actionType'] == 'PASSWORD') { | ||
71 | actionLabel = "locked"; | ||
72 | } else if (aField['actionType'] == 'EMAIL') { | ||
73 | actionLabel = "email"; | ||
74 | } else { | ||
75 | actionLabel = ""; | ||
76 | } | ||
77 | |||
78 | returnReact.DOM.div({className:'listItem ' + aField['actionType']}, [ | ||
79 | React.DOM.div({className:'fieldWrapper'}, [ | ||
80 | React.DOM.div({className:'fieldInnerWrapper'}, [ | ||
81 | 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']))) | ||
83 | ]) | ||
84 | ]), | ||
85 | React.DOM.div({className:'actionWrapper'}, [ | ||
86 | React.DOM.div({className:aField['actionType']}, actionLabel) | ||
87 | ]) | ||
88 | ]); | ||
89 | }, | ||
90 | |||
91 | renderDirectLogin: function (aDirectLogin) { | ||
92 | //console.log("DIRECT LOGIN", aDirectLogin); | ||
93 | 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'])), | ||
95 | 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")) | ||
97 | ]); | ||
98 | }, | ||
99 | |||
100 | handleBackClick: function (anEvent) { | ||
101 | window.history.back(); | ||
102 | }, | ||
103 | |||
104 | handleStarClick: function (anEvent) { | ||
105 | this.setState({starred: !this.state['starred']}); | ||
106 | }, | ||
107 | |||
108 | //========================================================================= | ||
109 | |||
110 | render: function () { | ||
111 | var card = this.props.card; | ||
112 | var starredStatus = (this.state['starred'] ? "starred" : "unstarred"); | ||
113 | |||
114 | if ((typeof(card['fields']) != 'undefined') && (card['notes'] != '')) { | ||
115 | card['fields'].push({ 'actionType': 'NOTES', 'isHidden': false, 'label': "notes", 'reference': "notes", 'value': card['notes'] }) | ||
116 | } | ||
117 | |||
118 | returnReact.DOM.div({className:'cardDetail'}, [ | ||
119 | React.DOM.div({className:'header'}, [ | ||
120 | 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")), | ||
123 | React.DOM.div({className:'starWrapper'}, React.DOM.a({className:'star', onClick:this.handleStarClick}, starredStatus)) | ||
124 | ]), | ||
125 | React.DOM.div({className:'content'}, [ | ||
126 | 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 | ||
128 | ]), | ||
129 | React.DOM.div({className:'footer'}, [ | ||
130 | /* | ||
131 | // React.DOM.a({className:'cancel'}, "cancel"), | ||
132 | // React.DOM.a({className:'save'}, "save") | ||
133 | |||
134 | React.DOM.a({className:'cancel button'}, "failed"), | ||
135 | React.DOM.a({className:'save button'}, "done") | ||
136 | */ | ||
137 | ]) | ||
138 | ]); | ||
139 | } | ||
140 | |||
141 | //========================================================================= | ||
142 | }); | ||
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js new file mode 100644 index 0000000..66d20f1 --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js | |||
@@ -0,0 +1,161 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer 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 | |||
24 | Clipperz.PM.UI.Components.CardList = React.createClass({ | ||
25 | |||
26 | getDefaultProps: function () { | ||
27 | return { | ||
28 | selectedCard: null, | ||
29 | searchDelay: 0.3 | ||
30 | } | ||
31 | }, | ||
32 | |||
33 | propTypes: { | ||
34 | searchDelay: React.PropTypes.number | ||
35 | }, | ||
36 | |||
37 | getInitialState: function () { | ||
38 | return { | ||
39 | showSearch: false, | ||
40 | searchTimer: null, | ||
41 | searchText: '', | ||
42 | // passphrase: '', | ||
43 | // pin: '' | ||
44 | }; | ||
45 | }, | ||
46 | |||
47 | //========================================================================= | ||
48 | |||
49 | toggleSearch: function (anEvent) { | ||
50 | varshowSearchBox; | ||
51 | |||
52 | showSearchBox = !this.state.showSearch; | ||
53 | |||
54 | this.setState({showSearch: showSearchBox}); | ||
55 | |||
56 | if (showSearchBox) { | ||
57 | MochiKit.Async.callLater(0.1, MochiKit.Base.method(this, 'focusOnSearchField')); | ||
58 | } | ||
59 | }, | ||
60 | |||
61 | updateSearchText: function (anEvent) { | ||
62 | varsearchText; | ||
63 | |||
64 | searchText = anEvent.target.value; | ||
65 | //console.log(">>> updateSearchText", searchText); | ||
66 | |||
67 | if ((this.state['searchTimer'] != null) && (searchText != this.state['searchText'])) { | ||
68 | this.state['searchTimer'].cancel(); | ||
69 | } | ||
70 | |||
71 | if (searchText != this.state['searchText']) { | ||
72 | this.state['searchText'] = searchText; | ||
73 | this.state['searchTimer'] = MochiKit.Async.callLater(this.props['searchDelay'], MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'searchCards', searchText); | ||
74 | } | ||
75 | }, | ||
76 | |||
77 | focusOnSearchField: function () { | ||
78 | console.log("focusOnSearchField", this.refs['searchField']); | ||
79 | this.refs['searchField'].getDOMNode.focus(); | ||
80 | }, | ||
81 | |||
82 | searchBox: function () { | ||
83 | var result; | ||
84 | |||
85 | if (this.state.showSearch) { | ||
86 | result =React.DOM.div({className:'searchBox'}, [ | ||
87 | React.DOM.div(null, [ | ||
88 | React.DOM.input({type:'search', placeholder:"search", ref:'searchField', onChange:this.updateSearchText}) | ||
89 | ]) | ||
90 | ]); | ||
91 | } else { | ||
92 | result = null; | ||
93 | } | ||
94 | |||
95 | return result; | ||
96 | }, | ||
97 | |||
98 | //========================================================================= | ||
99 | |||
100 | cardItem: function (aRecordReference) { | ||
101 | varreference = aRecordReference['_reference']; | ||
102 | varselectedCard = (reference == this.props.selectedCard); | ||
103 | |||
104 | returnReact.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleClickOnCardDetail', reference)}, [ | ||
105 | 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)), | ||
107 | React.DOM.div({className:'faviconWrapper'}, aRecordReference.favicon ? React.DOM.img({className:'favicon', src:aRecordReference.favicon}) : React.DOM.div({className:'favicon'}, '\u00A0')), | ||
108 | React.DOM.div({className:'detailLinkWrapper'}, React.DOM.span({className:'detailLink ' + (selectedCard ? 'icon-spin' : '')}, (selectedCard ? "loading" : "detail"))) | ||
109 | ]); | ||
110 | }, | ||
111 | |||
112 | handleClickOnCardDetail: function (aRecordReference, anEvent) { | ||
113 | MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showRecord', aRecordReference); | ||
114 | }, | ||
115 | |||
116 | cardListItems: function () { | ||
117 | varlist; | ||
118 | varresult; | ||
119 | |||
120 | list = this.props['cardList']; | ||
121 | |||
122 | if (typeof(list) != 'undefined') { | ||
123 | result = MochiKit.Base.map(MochiKit.Base.method(this, 'cardItem'), list); | ||
124 | } else { | ||
125 | result = null; | ||
126 | } | ||
127 | |||
128 | return result; | ||
129 | }, | ||
130 | |||
131 | //========================================================================= | ||
132 | |||
133 | handleChange: function (anEvent) { | ||
134 | // varrefs = this.refs; | ||
135 | // var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0]; | ||
136 | // var newState = {}; | ||
137 | // | ||
138 | // newState[refName] = event.target.value; | ||
139 | // this.setState(newState); | ||
140 | }, | ||
141 | |||
142 | //========================================================================= | ||
143 | |||
144 | render: function() { | ||
145 | returnReact.DOM.div(null, [ | ||
146 | React.DOM.div({className:'header'}, [ | ||
147 | React.DOM.a({className:'account'}, 'clipperz'), | ||
148 | React.DOM.div({className:'features'}, [ | ||
149 | React.DOM.a({className:'addCard'}, 'add'), | ||
150 | React.DOM.a({className:'search ' + (this.state.showSearch ? 'selected' : ''), onClick:this.toggleSearch}, 'search'), | ||
151 | React.DOM.a({className:'settings'}, 'settings') | ||
152 | ]), | ||
153 | // this.searchBox() | ||
154 | ]), | ||
155 | this.searchBox(), | ||
156 | React.DOM.div({className:'content cardList'}, this.cardListItems()), | ||
157 | ]); | ||
158 | } | ||
159 | |||
160 | //========================================================================= | ||
161 | }); | ||
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js b/frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js new file mode 100644 index 0000000..a1979ec --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer 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 | |||
24 | Clipperz.PM.UI.Components.ErrorPage = React.createClass({ | ||
25 | |||
26 | getDefaultProps: function () { | ||
27 | return { | ||
28 | template: Clipperz.PM.UI.Components.PageTemplate | ||
29 | } | ||
30 | }, | ||
31 | |||
32 | 'propTypes': { | ||
33 | // type: React.PropTypes.oneOf(['PERMANENT', 'TEMPORARY']), | ||
34 | message:React.PropTypes.string.isRequired, | ||
35 | template:React.PropTypes.func | ||
36 | }, | ||
37 | |||
38 | |||
39 | _render: function () { | ||
40 | returnReact.DOM.div({className:'error-message'}, this.props.message); | ||
41 | }, | ||
42 | |||
43 | render: function () { | ||
44 | returnnew this.props.template({'innerComponent': this._render()}); | ||
45 | } | ||
46 | }); | ||
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js new file mode 100644 index 0000000..2b5b4a4 --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer 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 | |||
24 | Clipperz.PM.UI.Components.LoginForm = React.createClass({ | ||
25 | |||
26 | getDefaultProps: function () { | ||
27 | return { | ||
28 | mode: 'CREDENTIALS', | ||
29 | isNewUserRegistrationAvailable: false, | ||
30 | disabled: false, | ||
31 | template: Clipperz.PM.UI.Components.PageTemplate | ||
32 | } | ||
33 | }, | ||
34 | |||
35 | propTypes: { | ||
36 | mode: React.PropTypes.oneOf(['CREDENTIALS','PIN']), | ||
37 | isNewUserRegistrationAvailable:React.PropTypes.bool, | ||
38 | disabled: React.PropTypes.bool, | ||
39 | template: React.PropTypes.func | ||
40 | }, | ||
41 | |||
42 | getInitialState: function () { | ||
43 | return { | ||
44 | username: '', | ||
45 | passphrase: '', | ||
46 | pin: '' | ||
47 | }; | ||
48 | }, | ||
49 | |||
50 | //========================================================================= | ||
51 | |||
52 | handleChange: function (anEvent) { | ||
53 | varrefs = this.refs; | ||
54 | var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0]; | ||
55 | var newState = {}; | ||
56 | |||
57 | newState[refName] = event.target.value; | ||
58 | this.setState(newState); | ||
59 | }, | ||
60 | |||
61 | //========================================================================= | ||
62 | |||
63 | handleCredentialSubmit: function (event) { | ||
64 | event.preventDefault(); | ||
65 | |||
66 | this.refs['passphrase'].getDOMNode().blur(); | ||
67 | |||
68 | var credentials = { | ||
69 | 'username': this.refs['username'].getDOMNode().value, | ||
70 | 'passphrase': this.refs['passphrase'].getDOMNode().value | ||
71 | } | ||
72 | MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials); | ||
73 | }, | ||
74 | |||
75 | handleRegistrationLinkClick: function (event) { | ||
76 | event.preventDefault(); | ||
77 | MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showRegistrationForm'); | ||
78 | }, | ||
79 | |||
80 | //------------------------------------------------------------------------- | ||
81 | |||
82 | shouldEnableLoginButton: function () { | ||
83 | var result; | ||
84 | |||
85 | return( | ||
86 | ((this.state['username'] != '') && (this.state['passphrase'] != '')) | ||
87 | || | ||
88 | (this.state['pin'] != '') | ||
89 | ) && !this.props['disabled']; | ||
90 | }, | ||
91 | |||
92 | |||
93 | loginForm: function () { | ||
94 | registrationLink =React.DOM.div({'className':'registrationLink'}, [ | ||
95 | React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Need an account") | ||
96 | ]); | ||
97 | returnReact.DOM.div({'className':'loginForm credentials'},[ | ||
98 | React.DOM.form({onChange: this.handleChange, onSubmit:this.handleCredentialSubmit}, [ | ||
99 | React.DOM.div(null,[ | ||
100 | React.DOM.label({'for':'name'}, "username"), | ||
101 | React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'}), | ||
102 | React.DOM.label({'for':'passphrase'}, "passphrase"), | ||
103 | React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'}) | ||
104 | ]), | ||
105 | React.DOM.button({'type':'submit', 'disabled':!this.shouldEnableLoginButton(), 'className':'button'}, "login") | ||
106 | ]), | ||
107 | this.props.isNewUserRegistrationAvailable ? registrationLink : null | ||
108 | ]); | ||
109 | }, | ||
110 | |||
111 | handlePINSubmit: function (event) { | ||
112 | event.preventDefault(); | ||
113 | |||
114 | this.refs['pin'].getDOMNode().blur(); | ||
115 | |||
116 | var credentials = { | ||
117 | pin: this.refs['pin'].getDOMNode().value | ||
118 | } | ||
119 | |||
120 | MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials); | ||
121 | }, | ||
122 | |||
123 | pinForm: function () { | ||
124 | returnReact.DOM.div({'className':'loginForm pin'},[ | ||
125 | React.DOM.form({onChange: this.handleChange, onSubmit:this.handlePINSubmit}, [ | ||
126 | React.DOM.div(null,[ | ||
127 | React.DOM.label({'for':'pin'}, "pin"), | ||
128 | React.DOM.input({'type':'text', 'name':'pin', 'ref':'pin', placeholder:"PIN", 'key':'pin', 'autocapitalize':'none'}) | ||
129 | ]), | ||
130 | React.DOM.button({'type':'submit', 'disabled':this.props.disabled, 'className':'button'}, "login") | ||
131 | ]) | ||
132 | ]); | ||
133 | }, | ||
134 | |||
135 | setInitialFocus: function () { | ||
136 | if (this.props.mode == 'PIN') { | ||
137 | this.refs['pin'].getDOMNode().select(); | ||
138 | } else { | ||
139 | if (this.refs['username'].getDOMNode().value == '') { | ||
140 | this.refs['username'].getDOMNode().focus(); | ||
141 | } else{ | ||
142 | this.refs['passphrase'].getDOMNode().select(); | ||
143 | } | ||
144 | } | ||
145 | }, | ||
146 | |||
147 | render: function() { | ||
148 | returnnew this.props.template({'innerComponent': this.props.mode == 'PIN' ? this.pinForm() : this.loginForm()}); | ||
149 | } | ||
150 | }); | ||
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js b/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js new file mode 100644 index 0000000..cc4a06c --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer 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 | |||
24 | Clipperz.Base.module('Clipperz.PM.UI.Components'); | ||
25 | |||
26 | Clipperz.PM.UI.Components.Overlay = function(args) { | ||
27 | args = args || {}; | ||
28 | |||
29 | this._defaultDelay = 2; | ||
30 | this._element = MochiKit.DOM.getElement('overlay'); | ||
31 | |||
32 | return this; | ||
33 | } | ||
34 | |||
35 | //============================================================================= | ||
36 | |||
37 | Clipperz.Base.extend(Clipperz.PM.UI.Components.Overlay, Object, { | ||
38 | |||
39 | //------------------------------------------------------------------------- | ||
40 | |||
41 | 'toString': function () { | ||
42 | return "Clipperz.PM.UI.Components.Overlay component"; | ||
43 | }, | ||
44 | |||
45 | 'element': function () { | ||
46 | // return MochiKit.DOM.getElement('overlay'); | ||
47 | return this._element; | ||
48 | }, | ||
49 | |||
50 | 'getElement': function (aClass) { | ||
51 | return MochiKit.Selector.findChildElements(this.element(), ['.'+aClass])[0]; | ||
52 | }, | ||
53 | |||
54 | //------------------------------------------------------------------------- | ||
55 | |||
56 | 'show': function (aMessage) { | ||
57 | this.resetStatus(); | ||
58 | this.setMessage(aMessage); | ||
59 | MochiKit.DOM.removeElementClass(this.element(), 'ios-overlay-hide'); | ||
60 | MochiKit.DOM.addElementClass(this.element(), 'ios-overlay-show'); | ||
61 | }, | ||
62 | |||
63 | 'done': function (aMessage, aDelayBeforeHiding) { | ||
64 | this.completed(this.showDoneIcon, aMessage, aDelayBeforeHiding); | ||
65 | }, | ||
66 | |||
67 | 'failed': function (aMessage, aDelayBeforeHiding) { | ||
68 | this.completed(this.showFailIcon, aMessage, aDelayBeforeHiding); | ||
69 | }, | ||
70 | |||
71 | //------------------------------------------------------------------------- | ||
72 | |||
73 | 'resetStatus': function () { | ||
74 | MochiKit.Style.showElement(this.element()); | ||
75 | MochiKit.Style.showElement(this.getElement('spinner')); | ||
76 | MochiKit.Style.hideElement(this.getElement('done')); | ||
77 | MochiKit.Style.hideElement(this.getElement('failed')); | ||
78 | }, | ||
79 | |||
80 | 'setMessage': function (aMessage) { | ||
81 | if (typeof(aMessage) != 'undefined') { | ||
82 | this.getElement('title').innerHTML = aMessage; | ||
83 | } | ||
84 | }, | ||
85 | |||
86 | 'completed': function (aFunctionToShowResult, aMessage, aDelayBeforeHiding) { | ||
87 | var delay = aDelayBeforeHiding || this.defaultDelay(); | ||
88 | |||
89 | this.hideSpinner(); | ||
90 | MochiKit.Base.bind(aFunctionToShowResult, this)(); | ||
91 | this.setMessage(aMessage); | ||
92 | |||
93 | MochiKit.Async.callLater(delay, MochiKit.Base.bind(this.hide, this)) | ||
94 | }, | ||
95 | |||
96 | 'hide': function () { | ||
97 | MochiKit.DOM.removeElementClass(this.element(), 'ios-overlay-show'); | ||
98 | MochiKit.DOM.addElementClass(this.element(), 'ios-overlay-hide'); | ||
99 | MochiKit.Async.callLater(1, MochiKit.Style.hideElement, this.element()); | ||
100 | }, | ||
101 | |||
102 | 'hideSpinner': function () { | ||
103 | MochiKit.Style.hideElement(this.getElement('spinner')); | ||
104 | }, | ||
105 | |||
106 | 'showDoneIcon': function () { | ||
107 | MochiKit.Style.showElement(this.getElement('done')); | ||
108 | }, | ||
109 | |||
110 | 'showFailIcon': function () { | ||
111 | MochiKit.Style.showElement(this.getElement('failed')); | ||
112 | }, | ||
113 | |||
114 | //------------------------------------------------------------------------- | ||
115 | |||
116 | 'defaultDelay': function () { | ||
117 | return this._defaultDelay; | ||
118 | }, | ||
119 | |||
120 | //------------------------------------------------------------------------- | ||
121 | __syntaxFix__: "syntax fix" | ||
122 | }); | ||
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js b/frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js new file mode 100644 index 0000000..9b7c748 --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer 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 | |||
24 | Clipperz.PM.UI.Components.PageTemplate = React.createClass({ | ||
25 | render: function() { | ||
26 | returnReact.DOM.div(null, [ | ||
27 | React.DOM.div({'className': 'header'}, [ | ||
28 | React.DOM.h1(null, "clipperz") | ||
29 | ]), | ||
30 | React.DOM.div({'className': 'content'}, this.props.innerComponent) | ||
31 | ]) | ||
32 | } | ||
33 | }); | ||
diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js b/frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js new file mode 100644 index 0000000..051dcc5 --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | |||
3 | Copyright 2008-2013 Clipperz Srl | ||
4 | |||
5 | This file is part of Clipperz, the online password manager. | ||
6 | For further information about its features and functionalities please | ||
7 | refer 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 | |||
24 | Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({ | ||
25 | |||
26 | getDefaultProps: function () { | ||
27 | return { | ||
28 | steps: [ | ||
29 | {name:'CREDENTIALS', label:'registration', _label:'credentials',description:"Choose your credentails"}, | ||
30 | {name:'PASSWORD_VERIFICATION', label:'registration', _label:'verify', description:"Verify your passphrase"}, | ||
31 | {name:'TERMS_OF_SERVICE', label:'registration', _label:'terms', description:"Check our terms of service"} | ||
32 | ], | ||
33 | disabled: false, | ||
34 | template: Clipperz.PM.UI.Components.PageTemplate | ||
35 | } | ||
36 | }, | ||
37 | |||
38 | getInitialState: function () { | ||
39 | return { | ||
40 | currentStep: this.props['steps'][0]['name'], | ||
41 | username: '', | ||
42 | passphrase: '', | ||
43 | verify_passphrase: '', | ||
44 | no_password_recovery: false, | ||
45 | agree_terms_of_service: false | ||
46 | }; | ||
47 | }, | ||
48 | |||
49 | 'propTypes': { | ||
50 | // steps: React.PropTypes.array, | ||
51 | disabled:React.PropTypes.bool, | ||
52 | template:React.PropTypes.func | ||
53 | }, | ||
54 | |||
55 | //========================================================================= | ||
56 | |||
57 | currentStepIndex: function () { | ||
58 | return this.indexOfStepNamed(this.state['currentStep']); | ||
59 | }, | ||
60 | |||
61 | indexOfStepNamed: function (aStepName) { | ||
62 | var stepConfiguration; | ||
63 | varresult; | ||
64 | |||
65 | stepConfiguration = this.props['steps'].filter(function (aConfig) { return aConfig['name'] == aStepName})[0]; | ||
66 | result = this.props['steps'].indexOf(stepConfiguration); | ||
67 | return result; | ||
68 | }, | ||
69 | |||
70 | //========================================================================= | ||
71 | |||
72 | statusClassForStep: function (aStep) { | ||
73 | varcurrentStepIndex = this.currentStepIndex(); | ||
74 | var stepIndex = this.indexOfStepNamed(aStep['name']); | ||
75 | varresult; | ||
76 | |||
77 | if (stepIndex < currentStepIndex) { | ||
78 | result = 'left'; | ||
79 | } else if (stepIndex == currentStepIndex) { | ||
80 | result = 'center'; | ||
81 | } else { | ||
82 | result = 'right'; | ||
83 | } | ||
84 | |||
85 | return result; | ||
86 | }, | ||
87 | |||
88 | //========================================================================= | ||
89 | |||
90 | handleBackClick: function (anEvent) { | ||
91 | var nextStep; | ||
92 | anEvent.preventDefault(); | ||
93 | |||
94 | if (this.currentStepIndex() > 0) { | ||
95 | nextStep = this.props['steps'][this.currentStepIndex() - 1]; | ||
96 | this.setState({currentStep: nextStep['name']}); | ||
97 | } else { | ||
98 | MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack'); | ||
99 | } | ||
100 | }, | ||
101 | |||
102 | handleForwardClick: function (anEvent) { | ||
103 | var nextStep; | ||
104 | anEvent.preventDefault(); | ||
105 | |||
106 | if (this.canMoveForward()) { | ||
107 | |||
108 | if (this.currentStepIndex() < this.props['steps'].length - 1) { | ||
109 | nextStep = this.props['steps'][this.currentStepIndex() + 1]; | ||
110 | this.setState({currentStep: nextStep['name']}); | ||
111 | } else { | ||
112 | MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'registerNewUser', { | ||
113 | username: this.state['username'], | ||
114 | passphrase: this.state['passphrase'] | ||
115 | }) | ||
116 | } | ||
117 | } | ||
118 | }, | ||
119 | |||
120 | //------------------------------------------------------------------------- | ||
121 | |||
122 | canMoveForward: function () { | ||
123 | var result; | ||
124 | var currentStep; | ||
125 | |||
126 | result = false; | ||
127 | currentStep = this.state['currentStep']; | ||
128 | if (currentStep == 'CREDENTIALS') { | ||
129 | result = ((this.state['username'] != '') && (this.state['passphrase'] != '')); | ||
130 | } else if (currentStep == 'PASSWORD_VERIFICATION') { | ||
131 | result = (this.state['passphrase'] == this.state['verify_passphrase']); | ||
132 | } else if (currentStep == 'TERMS_OF_SERVICE') { | ||
133 | result = (this.state['no_password_recovery'] && this.state['agree_terms_of_service']); | ||
134 | } | ||
135 | |||
136 | return result && !this.props['disabled']; | ||
137 | }, | ||
138 | |||
139 | //========================================================================= | ||
140 | |||
141 | handleChange: function (anEvent) { | ||
142 | varrefs = this.refs; | ||
143 | var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0]; | ||
144 | var newState = {}; | ||
145 | |||
146 | if ((event.target.type == 'checkbox') || (event.target.type == 'radio')) { | ||
147 | newState[refName] = event.target.checked; | ||
148 | } else { | ||
149 | newState[refName] = event.target.value; | ||
150 | } | ||
151 | this.setState(newState); | ||
152 | }, | ||
153 | |||
154 | //========================================================================= | ||
155 | |||
156 | renderIndexStep: function (aStep) { | ||
157 | returnReact.DOM.div({'className':'stepIndexItem ' + this.statusClassForStep(aStep)}, '.'); | ||
158 | }, | ||
159 | |||
160 | renderButtons: function () { | ||
161 | return [ | ||
162 | React.DOM.a({className:'back button step_' + (this.currentStepIndex() - 1), onClick:this.handleBackClick}, '<<'), | ||
163 | React.DOM.a({className:'forward button step_' + (this.currentStepIndex() + 1) + ' ' + (this.canMoveForward() ? 'enabled' : 'disabled'), onClick:this.handleForwardClick}, '>>') | ||
164 | ]; | ||
165 | }, | ||
166 | |||
167 | render_CREDENTIALS: function () { | ||
168 | returnReact.DOM.div(null,[ | ||
169 | React.DOM.label({'for':'name'}, "username"), | ||
170 | React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'/*, value:this.state.username*/}), | ||
171 | React.DOM.label({'for':'passphrase'}, "passphrase"), | ||
172 | React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'/*, value:this.state.passphrase*/}) | ||
173 | ]); | ||
174 | }, | ||
175 | |||
176 | render_PASSWORD_VERIFICATION: function () { | ||
177 | returnReact.DOM.div(null,[ | ||
178 | React.DOM.label({'for':'verify_passphrase'}, "passphrase"), | ||
179 | React.DOM.input({'type':'password', 'name':'verify_passphrase', 'ref':'verify_passphrase', 'placeholder':"verify passphrase", 'key':'verify_passphrase'}) | ||
180 | ]); | ||
181 | }, | ||
182 | |||
183 | render_TERMS_OF_SERVICE: function () { | ||
184 | returnReact.DOM.div(null, [ | ||
185 | React.DOM.div({className:'checkboxBlock'}, [ | ||
186 | React.DOM.label({'for':'no_password_recovery'}, "I understand that Clipperz will not be able to recover a lost passphrase."), | ||
187 | React.DOM.input({'type':'checkbox', 'name':'no_password_recovery', 'ref':'no_password_recovery', 'key':'no_password_recovery'}), | ||
188 | React.DOM.p(null, "I understand that Clipperz will not be able to recover a lost passphrase.") | ||
189 | ]), | ||
190 | React.DOM.div({className:'checkboxBlock'}, [ | ||
191 | React.DOM.label({'for':'agree_terms_of_service'}, "I have read and agreed to the Terms of Service."), | ||
192 | React.DOM.input({'type':'checkbox', 'name':'agree_terms_of_service', 'ref':'agree_terms_of_service', 'key':'agree_terms_of_service'}), | ||
193 | React.DOM.p(null, [ | ||
194 | "I have read and agreed to the ", | ||
195 | React.DOM.a({href:'https://clipperz.com/terms_service/', target:'_blank'}, "Terms of Service.") | ||
196 | ]) | ||
197 | ]) | ||
198 | ]); | ||
199 | }, | ||
200 | |||
201 | renderStep: function (aStep) { | ||
202 | returnReact.DOM.div({'className':'step' + ' ' + aStep['name'] + ' ' + this.statusClassForStep(aStep) + ' step_' + this.currentStepIndex()}, [ | ||
203 | React.DOM.h1(null, aStep['label']), | ||
204 | React.DOM.p(null, aStep['description']), | ||
205 | this['render_' + aStep['name']].apply(), | ||
206 | React.DOM.div({'className':'stepIndex'}, MochiKit.Base.map(this.renderIndexStep, this.props['steps'])), | ||
207 | React.DOM.div({'className':'buttons'}, this.renderButtons()) | ||
208 | ]); | ||
209 | }, | ||
210 | |||
211 | _render: function () { | ||
212 | returnReact.DOM.div({'className':'registrationForm'},[ | ||
213 | React.DOM.form({onChange: this.handleChange}, [ | ||
214 | React.DOM.div({'className':'steps'}, MochiKit.Base.map(this.renderStep, this.props['steps'])) | ||
215 | ]) | ||
216 | ]); | ||
217 | }, | ||
218 | |||
219 | render: function () { | ||
220 | returnnew this.props.template({'innerComponent': this._render()}); | ||
221 | }, | ||
222 | |||
223 | //========================================================================= | ||
224 | |||
225 | setInitialFocus: function () { | ||
226 | this.refs['username'].getDOMNode().focus(); | ||
227 | }, | ||
228 | |||
229 | componentDidUpdate: function (prevProps, prevState, rootNode) { | ||
230 | if (prevState['currentStep'] != this.state['currentStep']) { | ||
231 | if (this.state['currentStep'] == 'CREDENTIALS') { | ||
232 | this.refs['passphrase'].getDOMNode().select(); | ||
233 | } else if (this.state['currentStep'] == 'PASSWORD_VERIFICATION') { | ||
234 | this.refs['verify_passphrase'].getDOMNode().select(); | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | //========================================================================= | ||
240 | }); | ||