summaryrefslogtreecommitdiff
path: root/frontend/delta/js/Clipperz/PM/UI
Unidiff
Diffstat (limited to 'frontend/delta/js/Clipperz/PM/UI') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js142
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/CardList.js161
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/ErrorPage.js46
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js150
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js122
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/PageTemplate.js33
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/RegistrationWizard.js240
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js256
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/MainController.js491
9 files changed, 1641 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
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.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
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.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 () {
78console.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
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.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
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.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
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.Base.module('Clipperz.PM.UI.Components');
25
26Clipperz.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
37Clipperz.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
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.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
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.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});
diff --git a/frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js b/frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js
new file mode 100644
index 0000000..d9dfe6d
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/DirectLoginController.js
@@ -0,0 +1,256 @@
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.Base.module('Clipperz.PM.UI');
25
26Clipperz.PM.UI.DirectLoginRunner = function(args) {
27 this._directLogin = args['directLogin'] || Clipperz.Base.exception.raise('MandatoryParameter');
28 this._target = Clipperz.PM.Crypto.randomKey();
29
30 return this;
31}
32
33MochiKit.Base.update(Clipperz.PM.UI.DirectLoginRunner.prototype, {
34
35 'toString': function() {
36 return "Clipperz.PM.UI.DirectLoginRunner";
37 },
38
39 //-----------------------------------------------------------------------------
40
41 'directLogin': function () {
42 return this._directLogin;
43 },
44
45 //-----------------------------------------------------------------------------
46
47 'target': function () {
48 return this._target;
49 },
50
51 //=============================================================================
52
53 'setWindowTitle': function (aWindow, aTitle) {
54 aWindow.document.title = aTitle;
55 },
56
57 'setWindowBody': function (aWindow, anHTML) {
58 aWindow.document.body.innerHTML = anHTML;
59 },
60
61 //=============================================================================
62
63 'initialWindowSetup': function (aWindow) {
64 this.setWindowTitle(aWindow, "Loading Clipperz Direct Login");
65 this.setWindowBody (aWindow, MochiKit.DOM.toHTML(MochiKit.DOM.H3("Loading Clipperz Direct Login ...")));
66 },
67
68 //-----------------------------------------------------------------------------
69
70 'updateWindowWithDirectLoginLabel': function (aWindow, aLabel) {
71 var titleText;
72 var bodyText;
73
74 titleText = "Loading '__label__' Direct Login".replace(/__label__/, aLabel)
75 bodyText = "Loading '__label__' Direct Login... ".replace(/__label__/, aLabel)
76
77 this.setWindowTitle(aWindow, titleText);
78 this.setWindowBody (aWindow, MochiKit.DOM.toHTML(MochiKit.DOM.H3(bodyText)));
79 },
80
81 //-----------------------------------------------------------------------------
82
83 'updateWindowWithHTMLContent': function (aWindow, anHtml) {
84 this.setWindowBody(aWindow, anHtml);
85 },
86
87 //=============================================================================
88
89 'submitLoginForm': function(aWindow, aSubmitFunction) {
90 MochiKit.DOM.withWindow(aWindow, MochiKit.Base.bind(function () {
91 var formElement;
92 var submitButtons;
93
94 formElement = MochiKit.DOM.getElement('directLoginForm');
95
96 submitButtons = MochiKit.Base.filter(function(anInputElement) {
97 return ((anInputElement.tagName.toLowerCase() == 'input') && (anInputElement.getAttribute('type').toLowerCase() == 'submit'));
98 }, formElement.elements);
99
100 if (submitButtons.length == 0) {
101 if (typeof(formElement.submit) == 'function') {
102 formElement.submit();
103 } else {
104 aSubmitFunction.apply(formElement);
105 }
106/*
107 varformSubmitFunction;
108
109 formSubmitFunction = MochiKit.Base.method(formElement, 'submit');
110 if (Clipperz_IEisBroken == true) {
111 formElement.submit();
112 } else {
113 formSubmitFunction();
114 }
115*/
116 } else {
117 submitButtons[0].click();
118 }
119 }, this));
120 },
121
122 //-------------------------------------------------------------------------
123
124 'runSubmitFormDirectLogin': function (aWindow, someAttributes) {
125 var html;
126 var formElement;
127 var submitFunction;
128
129 formElement = MochiKit.DOM.FORM({
130 'id':'directLoginForm',
131 'method':someAttributes['formAttributes']['method'],
132 'action':someAttributes['formAttributes']['action']
133 });
134
135 submitFunction = formElement.submit;
136
137 MochiKit.DOM.appendChildNodes(formElement, MochiKit.Base.map(function (anInputAttributes) {
138 return MochiKit.DOM.INPUT({'type':'hidden', 'name':anInputAttributes[0], 'value':anInputAttributes[1]});
139 }, MochiKit.Base.items(someAttributes['inputValues'])));
140
141 html ='';
142 html += '<h3>Loading ' + someAttributes['label'] + ' ...</h3>';
143 html +=MochiKit.DOM.appendChildNodes(MochiKit.DOM.DIV(), MochiKit.DOM.appendChildNodes(MochiKit.DOM.DIV({style:'display:none; visibility:hidden;'}), formElement)).innerHTML;
144
145 this.updateWindowWithHTMLContent(aWindow, html);
146 this.submitLoginForm(aWindow, submitFunction);
147 },
148
149 //-------------------------------------------------------------------------
150
151 'runHttpAuthDirectLogin': function(aWindow, someAttributes) {
152 var completeUrl;
153 var url;
154
155 url = someAttributes['inputValues']['url'];
156
157 if (/^https?\:\/\//.test(url) == false) {
158 url = 'http://' + url;
159 }
160
161 if (Clipperz_IEisBroken === true) {
162 completeUrl = url;
163 } else {
164 var username;
165 var password;
166
167 username = someAttributes['inputValues']['username'];
168 password = someAttributes['inputValues']['password'];
169 /(^https?\:\/\/)?(.*)/.test(url);
170
171 completeUrl = RegExp.$1 + username + ':' + password + '@' + RegExp.$2;
172 }
173
174 window.open(completeUrl, this.target());
175 },
176
177 //=============================================================================
178
179 'runDirectLogin': function (aWindow) {
180 var deferredResult;
181
182 deferredResult = new Clipperz.Async.Deferred("DirectLoginRunner.openDirectLogin", {trace:false});
183 deferredResult.addMethod(this, 'initialWindowSetup', aWindow);
184 deferredResult.addMethod(this.directLogin(), 'label');
185 deferredResult.addMethod(this, 'updateWindowWithDirectLoginLabel', aWindow);
186 deferredResult.collectResults({
187 'type': MochiKit.Base.method(this.directLogin(), 'type'),
188 'label': MochiKit.Base.method(this.directLogin(), 'label'),
189 'formAttributes':MochiKit.Base.method(this.directLogin(), 'formAttributes'),
190 'inputValues': MochiKit.Base.method(this.directLogin(), 'inputValues')
191 });
192 deferredResult.addCallback(MochiKit.Base.bind(function (someAttributes) {
193 switch (someAttributes['type']) {
194 case 'http_auth':
195 this.runHttpAuthDirectLogin(aWindow, someAttributes);
196 break;
197 case 'simple_url':
198 this.runSimpleUrlDirectLogin(aWindow, someAttributes);
199 break;
200 default:
201 this.runSubmitFormDirectLogin(aWindow, someAttributes);
202 break;
203 }
204 }, this));
205 deferredResult.callback();
206
207 return deferredResult;
208 },
209
210 //=============================================================================
211
212 'run': function () {
213 var newWindow;
214
215 newWindow = window.open(Clipperz.PM.Strings.getValue('directLoginJumpPageUrl'), this.target());
216
217 return this.runDirectLogin(newWindow);
218 },
219
220 //=============================================================================
221
222 'test': function () {
223 var iFrame;
224 var newWindow;
225
226 iFrame = MochiKit.DOM.createDOM('iframe');
227 MochiKit.DOM.appendChildNodes(MochiKit.DOM.currentDocument().body, iFrame);
228
229 newWindow = iFrame.contentWindow;
230
231 return this.runDirectLogin(newWindow);
232 },
233
234 //=============================================================================
235 __syntaxFix__: "syntax fix"
236});
237
238//-----------------------------------------------------------------------------
239
240Clipperz.PM.UI.DirectLoginRunner.openDirectLogin = function (aDirectLogin) {
241 varrunner;
242
243 runner = new Clipperz.PM.UI.DirectLoginRunner({directLogin:aDirectLogin});
244 return runner.run();
245};
246
247//-----------------------------------------------------------------------------
248
249Clipperz.PM.UI.DirectLoginRunner.testDirectLogin = function (aDirectLogin) {
250 varrunner;
251
252 runner = new Clipperz.PM.UI.DirectLoginRunner({directLogin:aDirectLogin});
253 return runner.test();
254};
255
256//-----------------------------------------------------------------------------
diff --git a/frontend/delta/js/Clipperz/PM/UI/MainController.js b/frontend/delta/js/Clipperz/PM/UI/MainController.js
new file mode 100644
index 0000000..da7540e
--- a/dev/null
+++ b/frontend/delta/js/Clipperz/PM/UI/MainController.js
@@ -0,0 +1,491 @@
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.Base.module('Clipperz.PM.UI');
25
26Clipperz.PM.UI.MainController = function() {
27 var pages;
28
29 this._proxy = null;
30 this._user = null;
31 this._filter= '';
32
33 //this._currentPage = 'loadingPage';
34
35 this._pageStack = ['loadingPage'];
36 this._overlay = new Clipperz.PM.UI.Components.Overlay();
37 pages = {
38 'loginPage': new Clipperz.PM.UI.Components.LoginForm(),
39 'registrationPage':new Clipperz.PM.UI.Components.RegistrationWizard(),
40 'cardListPage': new Clipperz.PM.UI.Components.CardList(),
41 'cardDetailPage':new Clipperz.PM.UI.Components.CardDetail({card: {}}),
42 'errorPage': new Clipperz.PM.UI.Components.ErrorPage({message:''})
43 };
44
45 MochiKit.Base.map(function (anId) {React.renderComponent(pages[anId], MochiKit.DOM.getElement(anId))}, MochiKit.Base.keys(pages));
46 this._pages = pages;
47 this.registerForNotificationCenterEvents();
48
49 return this;
50}
51
52MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, {
53
54 toString: function () {
55 return "Clipperz.PM.UI.MainController";
56 },
57
58 //=========================================================================
59
60 overlay: function () {
61 return this._overlay;
62 },
63
64 loginForm: function () {
65 return this._loginForm;
66 },
67
68 registrationWizard: function () {
69 return this._registrationWizard;
70 },
71
72 //=========================================================================
73
74 isOnline: function() {
75 return navigator.onLine;
76 },
77
78 hasLocalData: function() {
79 return false;
80 },
81
82 loginMode: function () {
83 //PIN is set using this command:
84 //Clipperz.PM.PIN.setCredentialsWithPIN('1234', {'username':'joe', 'passphrase':'clipperz'});
85
86 return Clipperz.PM.PIN.isSet() ? 'PIN' : 'CREDENTIALS';
87 },
88
89 //=========================================================================
90
91 pages: function () {
92 return this._pages;
93 },
94
95 pageStack: function () {
96 return this._pageStack;
97 },
98
99 //=========================================================================
100
101 selectInitialProxy: function () {
102 if (this.isOnline()) {
103 this._proxy = Clipperz.PM.Proxy.defaultProxy;
104 } else {
105 if (this.hasLocalData()) {
106 this._proxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false});
107 } else {
108 this.showOfflineError();
109 }
110 }
111 },
112
113 proxy: function () {
114 return this._proxy;
115 },
116
117 //=========================================================================
118
119 registerForNotificationCenterEvents: function () {
120 var events= ['doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack', 'showRecord', 'searchCards', 'runDirectLogin'];
121 var self= this;
122
123 MochiKit.Base.map(function (anEvent) {
124 MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, anEvent, MochiKit.Base.method(self, anEvent));
125 }, events);
126
127 // MochiKit.Signal.connect(window, 'onpopstate', MochiKit.Base.method(this, 'historyGoBack'));
128 MochiKit.Signal.connect(window, 'onbeforeunload',MochiKit.Base.method(this, 'shouldExitApp'));
129 },
130
131 //-------------------------------------------------------------------------
132
133 run: function (parameters) {
134 var shouldShowRegistrationForm;
135
136 this.selectInitialProxy();
137 shouldShowRegistrationForm = parameters['shouldShowRegistrationForm'] && this.proxy().canRegisterNewUsers();
138 this.pages()['loginPage'].setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()});
139
140 if (shouldShowRegistrationForm) {
141 this.showRegistrationForm();
142 } else {
143 this.showLoginForm();
144 }
145 this.overlay().done("", 0.5);
146 },
147
148 //-------------------------------------------------------------------------
149
150 showLoginForm: function () {
151 varloginFormPage;
152
153 loginFormPage = this.pages()['loginPage'];
154 loginFormPage.setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()});
155 this.moveInPage(this.currentPage(), 'loginPage');
156 MochiKit.Async.callLater(0.5, MochiKit.Base.method(loginFormPage, 'setInitialFocus'));
157 },
158
159 showRegistrationForm: function () {
160 var currentPage;
161 varregistrationPage;
162
163 currentPage = this.currentPage();
164 registrationPage = this.pages()['registrationPage'];
165 this.setCurrentPage('loginPage');
166 registrationPage.setProps({});
167 this.moveInPage(currentPage, 'registrationPage');
168 MochiKit.Async.callLater(0.5, MochiKit.Base.method(registrationPage, 'setInitialFocus'));
169 },
170
171 //=========================================================================
172
173 doLogin: function (event) {
174 varcredentials;
175 var getPassphraseDelegate;
176 varuser;
177
178 user = null;
179
180 this.overlay().show("logging in");
181 this.pages()['loginPage'].setProps({disabled:true});
182
183 if ('pin' in event) {
184 credentials = Clipperz.PM.PIN.credentialsWithPIN(event['pin']);
185 } else {
186 credentials = event;
187 }
188 getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, credentials.passphrase);
189 user = new Clipperz.PM.DataModel.User({'username':credentials.username, 'getPassphraseFunction':getPassphraseDelegate});
190
191 deferredResult = new Clipperz.Async.Deferred('MainController.doLogin', {trace:false});
192 deferredResult.addCallback(MochiKit.Async.wait, 0.1);
193 deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection');
194 deferredResult.addMethod(user, 'login');
195 deferredResult.addMethod(Clipperz.PM.PIN, 'resetFailedAttemptCount');
196 deferredResult.addMethod(this, 'setUser', user);
197
198 // deferredResult.addMethod(this, 'setupApplication');
199 deferredResult.addMethod(this, 'runApplication');
200 deferredResult.addMethod(this.overlay(), 'done', "", 1);
201 deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event));
202 deferredResult.addErrback(MochiKit.Base.bind(function (anEvent, anError) {
203 if (anError['isPermanent'] != true) {
204 this.pages()['loginPage'].setProps({disabled:false, 'mode':this.loginMode()});
205 this.pages()['loginPage'].setInitialFocus();
206 }
207 return anError;
208 }, this, event))
209 deferredResult.callback();
210
211 return deferredResult;
212 },
213
214 //-------------------------------------------------------------------------
215
216 registerNewUser: function (credentials) {
217 vardeferredResult;
218
219 this.overlay().show("creating user");
220
221 this.pages()['registrationPage'].setProps({disabled:true});
222 deferredResult = new Clipperz.Async.Deferred('MainController.registerNewUser', {trace:false});
223 deferredResult.addCallback(Clipperz.PM.DataModel.User.registerNewAccount,
224 credentials['username'],
225 MochiKit.Base.partial(MochiKit.Async.succeed, credentials['passphrase'])
226 );
227 deferredResult.addMethod(this, 'doLogin', credentials);
228 deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event));
229 deferredResult.addErrback(MochiKit.Base.bind(function (anError) {
230 if (anError['isPermanent'] != true) {
231 this.pages()['registrationPage'].setProps({disabled:false});
232 this.pages()['registrationPage'].setInitialFocus();
233 }
234 return anError;
235 }, this));
236
237 deferredResult.callback();
238
239 return deferredResult;
240
241 },
242
243 //-------------------------------------------------------------------------
244
245 user: function () {
246 return this._user;
247 },
248
249 setUser: function (aUser) {
250 this._user = aUser;
251 return this._user;
252 },
253
254 //=========================================================================
255
256 allCardInfo: function () {
257 var deferredResult;
258 varcardInfo;
259
260 cardInfo = {
261 '_rowObject': MochiKit.Async.succeed,
262 '_reference': MochiKit.Base.methodcaller('reference'),
263 '_searchableContent':MochiKit.Base.methodcaller('searchableContent'),
264 'label': MochiKit.Base.methodcaller('label'),
265 'favicon': MochiKit.Base.methodcaller('favicon')
266 };
267
268 deferredResult = new Clipperz.Async.Deferred('MainController.allCardInfo', {trace:false});
269 deferredResult.addMethod(this.user(), 'getRecords');
270 deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("CardList.value - collectResults", cardInfo, {trace:false}));
271 deferredResult.addCallback(Clipperz.Async.collectAll);
272 deferredResult.callback();
273
274 return deferredResult;
275 },
276
277 filterCards: function (someCardInfo) {
278 var filter;
279 varfilterRegExp;
280 varresult;
281
282 filter = this.filter().replace(/[^A-Za-z0-9]/g, "\\$&");
283 filterRegExp = new RegExp(filter, "i");
284 result = MochiKit.Base.filter(function (aCardInfo) { return filterRegExp.test(aCardInfo['_searchableContent'])}, someCardInfo);
285
286 return result;
287 },
288
289 sortCards: function (someCardInfo) {
290 return someCardInfo.sort(Clipperz.Base.caseInsensitiveKeyComparator('label'));
291 },
292
293 showRecordList: function () {
294 var deferredResult;
295
296 deferredResult = new Clipperz.Async.Deferred('MainController.showRecordList', {trace:false});
297 deferredResult.addMethod(this, 'allCardInfo');
298 deferredResult.addMethod(this, 'filterCards');
299 deferredResult.addMethod(this, 'sortCards');
300 deferredResult.addCallback(MochiKit.Base.bind(function (someRecordInfo) {
301 this.pages()['cardListPage'].setProps({cardList: someRecordInfo});
302 }, this));
303 deferredResult.callback();
304
305 return deferredResult;
306 },
307
308 filter: function (){
309 return this._filter;
310 },
311
312 setFilter: function (aValue) {
313 this._filter = aValue;
314 },
315
316 searchCards: function (someParameters) {
317//console.log("SEARCH CARDS", someParameters);
318 this.setFilter(someParameters);
319 this.showRecordList();
320 },
321
322 //=========================================================================
323
324 runApplication: function () {
325 MochiKit.Signal.connect(window, 'onpopstate',MochiKit.Base.method(this, 'historyGoBack'));
326 this.moveInPage(this.currentPage(), 'cardListPage');
327 return this.showRecordList();
328 },
329
330 showRecord: function (aRecordReference) {
331//console.log("Show Record", aRecordReference);
332 vardeferredResult;
333
334 this.pages()['cardListPage'].setProps({selectedCard:aRecordReference});
335 deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:false});
336 // deferredResult.addMethod(this.user(), 'getRecord', aRecordReference['_reference']);
337 deferredResult.addMethod(this.user(), 'getRecord', aRecordReference);
338 deferredResult.addMethodcaller('content');
339 deferredResult.addCallback(MochiKit.Base.bind(function (aCard) {
340//console.log("CARD DETAILS", aCard);
341 this.pages()['cardDetailPage'].setProps({card: aCard});
342 this.pages()['cardListPage'].setProps({selectedCard: null});
343 }, this));
344 deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'cardDetailPage', true);
345 deferredResult.callback();
346
347 return deferredResult;
348 },
349
350 runDirectLogin: function (someParameters) {
351console.log("RUN DIRECT LOGIN", someParameters);
352 vardeferredResult;
353
354 // this.pages()['cardListPage'].setProps({selectedCard:aRecordReference});
355 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']);
358 deferredResult.addMethodcaller('directLoginWithReference', someParameters['directLogin']);
359 deferredResult.addCallback(Clipperz.PM.UI.DirectLoginRunner.openDirectLogin);
360 deferredResult.callback();
361
362 return deferredResult;
363 },
364
365 shouldExitApp: function (anEvent) {
366console.log("SHOULD EXIT APP");
367 anEvent.preventDefault();
368 anEvent.stopPropagation();
369 },
370
371 //=========================================================================
372
373 genericErrorHandler: function (anEvent, anError) {
374 var errorMessage;
375 varresult;
376
377 result = anError;
378 errorMessage = "login failed";
379
380 if (anError['isPermanent'] === true) {
381 this.pages()['errorPage'].setProps({message:anError.message});
382 this.moveInPage(this.currentPage(), 'errorPage');
383 errorMessage = "failure";
384 } else {
385 if ('pin' in anEvent) {
386 errorCount = Clipperz.PM.PIN.recordFailedAttempt();
387 if (errorCount == -1) {
388 errorMessage = "PIN resetted";
389 }
390 }
391 }
392 this.overlay().failed(errorMessage, 1);
393
394 return result;
395 },
396
397 //=========================================================================
398
399 slidePage: function (fromPage, toPage, direction) {
400 varfromPosition;
401 var toPosition;
402
403 if (direction == "LEFT") {
404 fromPosition = 'right';
405 toPosition = 'left'
406 } else {
407 fromPosition = 'left';
408 toPosition = 'right'
409 }
410
411 MochiKit.DOM.addElementClass(fromPage, toPosition + ' transition');
412
413 MochiKit.DOM.addElementClass(toPage, fromPosition);
414 MochiKit.DOM.removeElementClass(toPage, toPosition);
415 MochiKit.DOM.addElementClass(toPage, 'transition');
416 MochiKit.Async.callLater(0.1, function () {
417 MochiKit.DOM.removeElementClass(toPage, fromPosition);
418 })
419
420 MochiKit.Async.callLater(0.5, function () {
421 MochiKit.DOM.removeElementClass(fromPage, 'transition');
422 MochiKit.DOM.removeElementClass(toPage, 'transition');
423 })
424 },
425
426 rotateInPage: function (fromPage, toPage) {
427 //Broken! :(
428 MochiKit.DOM.addElementClass(MochiKit.DOM.getElement('mainDiv'), 'show-right');
429 },
430
431 //.........................................................................
432
433 goBack: function () {
434 varfromPage;
435 var toPage;
436
437 fromPage = this.pageStack().shift();
438 toPage = this.currentPage();
439 this.pages()[toPage].setProps({});
440 this.moveOutPage(fromPage, toPage);
441 },
442
443 historyGoBack: function (anEvent) {
444 anEvent.preventDefault();
445 anEvent.stopPropagation();
446 this.goBack();
447 },
448
449 currentPage: function () {
450 return this.pageStack()[0];
451 },
452
453 setCurrentPage: function (aPage) {
454 this.pageStack().unshift(aPage);
455 },
456
457 moveInPage: function (fromPage, toPage, addToHistory) {
458 varshouldAddItemToHistory;
459
460 shouldAddItemToHistory = typeof(addToHistory) == 'undefined' ? false : addToHistory;
461
462 this.slidePage(MochiKit.DOM.getElement(fromPage), MochiKit.DOM.getElement(toPage), 'LEFT');
463 this.setCurrentPage(toPage);
464
465 if (shouldAddItemToHistory) {
466//console.log("ADD ITEM TO HISTORY");
467//console.log("ADD ITEM TO HISTORY - window", window);
468//console.log("ADD ITEM TO HISTORY - window.history", window.history);
469 window.history.pushState({'fromPage': fromPage, 'toPage': toPage});
470 //# window.history.pushState();
471//console.log("ADDED ITEM TO HISTORY");
472 } else {
473//console.log("Skip HISTORY");
474 }
475 },
476
477 moveOutPage: function (fromPage, toPage) {
478 this.slidePage(MochiKit.DOM.getElement(fromPage), MochiKit.DOM.getElement(toPage), 'RIGHT');
479 this.setCurrentPage(toPage);
480 },
481
482 //=========================================================================
483/*
484 wrongAppVersion: function (anError) {
485 // this.pages()['errorPage'].setProps({message:anError.message});
486 // this.moveInPage('errorPage', this.currentPage());
487 },
488*/
489 //=========================================================================
490 __syntaxFix__: "syntax fix"
491});