summaryrefslogtreecommitdiff
path: root/frontend/delta/js/Clipperz/PM/UI/Components
Side-by-side diff
Diffstat (limited to 'frontend/delta/js/Clipperz/PM/UI/Components') (more/less context) (ignore whitespace changes)
-rw-r--r--frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.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
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 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.CardDetail = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+// searchDelay: 0.3
+ }
+ },
+
+ propTypes: {
+ card: React.PropTypes.object.isRequired
+ },
+
+ getInitialState: function () {
+ return {
+// showSearch: false,
+// searchTimer: null,
+ starred: false
+ };
+ },
+
+ handleDirectLoginClick: function (aDirectLoginReference, anEvent) {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'runDirectLogin', {record:this.props.card['reference'], directLogin:aDirectLoginReference});
+ },
+
+ //=========================================================================
+
+ normalizeFieldValue: function (aValue) {
+ var result = [];
+ var rows = aValue.split('\n');
+
+ for (var i = 0; i < rows.length; i++) {
+ if (i > 0) {
+ result.push(React.DOM.br());
+ }
+ result.push(rows[i].replace(/[\s]/g, '\u00A0'));
+ }
+
+ return result;
+ },
+
+ renderField: function (aField) {
+//console.log("FIELD", aField);
+ var actionLabel;
+
+ if (aField['actionType'] == 'URL') {
+ actionLabel = "go";
+ } else if (aField['actionType'] == 'PASSWORD') {
+ actionLabel = "locked";
+ } else if (aField['actionType'] == 'EMAIL') {
+ actionLabel = "email";
+ } else {
+ actionLabel = "";
+ }
+
+ return React.DOM.div({className:'listItem ' + aField['actionType']}, [
+ React.DOM.div({className:'fieldWrapper'}, [
+ React.DOM.div({className:'fieldInnerWrapper'}, [
+ React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aField['label'])),
+ React.DOM.div({className:'valueWrapper'}, React.DOM.span({className:'value ' + aField['actionType']}, this.normalizeFieldValue(aField['value'])))
+ ])
+ ]),
+ React.DOM.div({className:'actionWrapper'}, [
+ React.DOM.div({className:aField['actionType']}, actionLabel)
+ ])
+ ]);
+ },
+
+ renderDirectLogin: function (aDirectLogin) {
+//console.log("DIRECT LOGIN", aDirectLogin);
+ return React.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleDirectLoginClick', aDirectLogin['reference'])}, [
+ React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aDirectLogin['label'])),
+ React.DOM.div({className:'faviconWrapper'}, React.DOM.img({className:'favicon', src:aDirectLogin['favicon']})),
+ React.DOM.div({className:'directLoginLinkWrapper'}, React.DOM.span({className:'directLoginLink'}, "go"))
+ ]);
+ },
+
+ handleBackClick: function (anEvent) {
+ window.history.back();
+ },
+
+ handleStarClick: function (anEvent) {
+ this.setState({starred: !this.state['starred']});
+ },
+
+ //=========================================================================
+
+ render: function () {
+ var card = this.props.card;
+ var starredStatus = (this.state['starred'] ? "starred" : "unstarred");
+
+ if ((typeof(card['fields']) != 'undefined') && (card['notes'] != '')) {
+ card['fields'].push({ 'actionType': 'NOTES', 'isHidden': false, 'label': "notes", 'reference': "notes", 'value': card['notes'] })
+ }
+
+ return React.DOM.div({className:'cardDetail'}, [
+ React.DOM.div({className:'header'}, [
+ React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title)),
+// React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, card.title + ' ' + card.title + ' ' + card.title + ' ' + card.title)),
+ React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")),
+ React.DOM.div({className:'starWrapper'}, React.DOM.a({className:'star', onClick:this.handleStarClick}, starredStatus))
+ ]),
+ React.DOM.div({className:'content'}, [
+ card.fields ? React.DOM.div({className:'fields'}, MochiKit.Base.map(this.renderField, card.fields)) : null,
+ card.directLogins ? React.DOM.div({className:'directLogins'}, MochiKit.Base.map(this.renderDirectLogin, card.directLogins)): null
+ ]),
+ React.DOM.div({className:'footer'}, [
+/*
+// React.DOM.a({className:'cancel'}, "cancel"),
+// React.DOM.a({className:'save'}, "save")
+
+ React.DOM.a({className:'cancel button'}, "failed"),
+ React.DOM.a({className:'save button'}, "done")
+*/
+ ])
+ ]);
+ }
+
+ //=========================================================================
+});
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 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.CardList = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ selectedCard: null,
+ searchDelay: 0.3
+ }
+ },
+
+ propTypes: {
+ searchDelay: React.PropTypes.number
+ },
+
+ getInitialState: function () {
+ return {
+ showSearch: false,
+ searchTimer: null,
+ searchText: '',
+// passphrase: '',
+// pin: ''
+ };
+ },
+
+ //=========================================================================
+
+ toggleSearch: function (anEvent) {
+ var showSearchBox;
+
+ showSearchBox = !this.state.showSearch;
+
+ this.setState({showSearch: showSearchBox});
+
+ if (showSearchBox) {
+ MochiKit.Async.callLater(0.1, MochiKit.Base.method(this, 'focusOnSearchField'));
+ }
+ },
+
+ updateSearchText: function (anEvent) {
+ var searchText;
+
+ searchText = anEvent.target.value;
+//console.log(">>> updateSearchText", searchText);
+
+ if ((this.state['searchTimer'] != null) && (searchText != this.state['searchText'])) {
+ this.state['searchTimer'].cancel();
+ }
+
+ if (searchText != this.state['searchText']) {
+ this.state['searchText'] = searchText;
+ this.state['searchTimer'] = MochiKit.Async.callLater(this.props['searchDelay'], MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'searchCards', searchText);
+ }
+ },
+
+ focusOnSearchField: function () {
+console.log("focusOnSearchField", this.refs['searchField']);
+ this.refs['searchField'].getDOMNode.focus();
+ },
+
+ searchBox: function () {
+ var result;
+
+ if (this.state.showSearch) {
+ result = React.DOM.div({className:'searchBox'}, [
+ React.DOM.div(null, [
+ React.DOM.input({type:'search', placeholder:"search", ref:'searchField', onChange:this.updateSearchText})
+ ])
+ ]);
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+
+ cardItem: function (aRecordReference) {
+ var reference = aRecordReference['_reference'];
+ var selectedCard = (reference == this.props.selectedCard);
+
+ return React.DOM.div({className:'listItem', onClick:MochiKit.Base.method(this, 'handleClickOnCardDetail', reference)}, [
+ React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label)),
+// React.DOM.div({className:'labelWrapper'}, React.DOM.span({className:'label'}, aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label + ' ' + aRecordReference.label)),
+ React.DOM.div({className:'faviconWrapper'}, aRecordReference.favicon ? React.DOM.img({className:'favicon', src:aRecordReference.favicon}) : React.DOM.div({className:'favicon'}, '\u00A0')),
+ React.DOM.div({className:'detailLinkWrapper'}, React.DOM.span({className:'detailLink ' + (selectedCard ? 'icon-spin' : '')}, (selectedCard ? "loading" : "detail")))
+ ]);
+ },
+
+ handleClickOnCardDetail: function (aRecordReference, anEvent) {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showRecord', aRecordReference);
+ },
+
+ cardListItems: function () {
+ var list;
+ var result;
+
+ list = this.props['cardList'];
+
+ if (typeof(list) != 'undefined') {
+ result = MochiKit.Base.map(MochiKit.Base.method(this, 'cardItem'), list);
+ } else {
+ result = null;
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+
+ handleChange: function (anEvent) {
+// var refs = this.refs;
+// var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
+// var newState = {};
+//
+// newState[refName] = event.target.value;
+// this.setState(newState);
+ },
+
+ //=========================================================================
+
+ render: function() {
+ return React.DOM.div(null, [
+ React.DOM.div({className:'header'}, [
+ React.DOM.a({className:'account'}, 'clipperz'),
+ React.DOM.div({className:'features'}, [
+ React.DOM.a({className:'addCard'}, 'add'),
+ React.DOM.a({className:'search ' + (this.state.showSearch ? 'selected' : ''), onClick:this.toggleSearch}, 'search'),
+ React.DOM.a({className:'settings'}, 'settings')
+ ]),
+// this.searchBox()
+ ]),
+ this.searchBox(),
+ React.DOM.div({className:'content cardList'}, this.cardListItems()),
+ ]);
+ }
+
+ //=========================================================================
+});
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 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.ErrorPage = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ template: Clipperz.PM.UI.Components.PageTemplate
+ }
+ },
+
+ 'propTypes': {
+// type: React.PropTypes.oneOf(['PERMANENT', 'TEMPORARY']),
+ message: React.PropTypes.string.isRequired,
+ template: React.PropTypes.func
+ },
+
+
+ _render: function () {
+ return React.DOM.div({className:'error-message'}, this.props.message);
+ },
+
+ render: function () {
+ return new this.props.template({'innerComponent': this._render()});
+ }
+});
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 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.LoginForm = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ mode: 'CREDENTIALS',
+ isNewUserRegistrationAvailable: false,
+ disabled: false,
+ template: Clipperz.PM.UI.Components.PageTemplate
+ }
+ },
+
+ propTypes: {
+ mode: React.PropTypes.oneOf(['CREDENTIALS','PIN']),
+ isNewUserRegistrationAvailable: React.PropTypes.bool,
+ disabled: React.PropTypes.bool,
+ template: React.PropTypes.func
+ },
+
+ getInitialState: function () {
+ return {
+ username: '',
+ passphrase: '',
+ pin: ''
+ };
+ },
+
+ //=========================================================================
+
+ handleChange: function (anEvent) {
+ var refs = this.refs;
+ var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
+ var newState = {};
+
+ newState[refName] = event.target.value;
+ this.setState(newState);
+ },
+
+ //=========================================================================
+
+ handleCredentialSubmit: function (event) {
+ event.preventDefault();
+
+ this.refs['passphrase'].getDOMNode().blur();
+
+ var credentials = {
+ 'username': this.refs['username'].getDOMNode().value,
+ 'passphrase': this.refs['passphrase'].getDOMNode().value
+ }
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials);
+ },
+
+ handleRegistrationLinkClick: function (event) {
+ event.preventDefault();
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showRegistrationForm');
+ },
+
+ //-------------------------------------------------------------------------
+
+ shouldEnableLoginButton: function () {
+ var result;
+
+ return (
+ ((this.state['username'] != '') && (this.state['passphrase'] != ''))
+ ||
+ (this.state['pin'] != '')
+ ) && !this.props['disabled'];
+ },
+
+
+ loginForm: function () {
+ registrationLink = React.DOM.div({'className':'registrationLink'}, [
+ React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Need an account")
+ ]);
+ return React.DOM.div({'className':'loginForm credentials'},[
+ React.DOM.form({onChange: this.handleChange, onSubmit:this.handleCredentialSubmit}, [
+ React.DOM.div(null,[
+ React.DOM.label({'for':'name'}, "username"),
+ React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'}),
+ React.DOM.label({'for':'passphrase'}, "passphrase"),
+ React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'})
+ ]),
+ React.DOM.button({'type':'submit', 'disabled':!this.shouldEnableLoginButton(), 'className':'button'}, "login")
+ ]),
+ this.props.isNewUserRegistrationAvailable ? registrationLink : null
+ ]);
+ },
+
+ handlePINSubmit: function (event) {
+ event.preventDefault();
+
+ this.refs['pin'].getDOMNode().blur();
+
+ var credentials = {
+ pin: this.refs['pin'].getDOMNode().value
+ }
+
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'doLogin', credentials);
+ },
+
+ pinForm: function () {
+ return React.DOM.div({'className':'loginForm pin'},[
+ React.DOM.form({onChange: this.handleChange, onSubmit:this.handlePINSubmit}, [
+ React.DOM.div(null,[
+ React.DOM.label({'for':'pin'}, "pin"),
+ React.DOM.input({'type':'text', 'name':'pin', 'ref':'pin', placeholder:"PIN", 'key':'pin', 'autocapitalize':'none'})
+ ]),
+ React.DOM.button({'type':'submit', 'disabled':this.props.disabled, 'className':'button'}, "login")
+ ])
+ ]);
+ },
+
+ setInitialFocus: function () {
+ if (this.props.mode == 'PIN') {
+ this.refs['pin'].getDOMNode().select();
+ } else {
+ if (this.refs['username'].getDOMNode().value == '') {
+ this.refs['username'].getDOMNode().focus();
+ } else{
+ this.refs['passphrase'].getDOMNode().select();
+ }
+ }
+ },
+
+ render: function() {
+ return new this.props.template({'innerComponent': this.props.mode == 'PIN' ? this.pinForm() : this.loginForm()});
+ }
+});
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 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.Base.module('Clipperz.PM.UI.Components');
+
+Clipperz.PM.UI.Components.Overlay = function(args) {
+ args = args || {};
+
+ this._defaultDelay = 2;
+ this._element = MochiKit.DOM.getElement('overlay');
+
+ return this;
+}
+
+//=============================================================================
+
+Clipperz.Base.extend(Clipperz.PM.UI.Components.Overlay, Object, {
+
+ //-------------------------------------------------------------------------
+
+ 'toString': function () {
+ return "Clipperz.PM.UI.Components.Overlay component";
+ },
+
+ 'element': function () {
+// return MochiKit.DOM.getElement('overlay');
+ return this._element;
+ },
+
+ 'getElement': function (aClass) {
+ return MochiKit.Selector.findChildElements(this.element(), ['.'+aClass])[0];
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'show': function (aMessage) {
+ this.resetStatus();
+ this.setMessage(aMessage);
+ MochiKit.DOM.removeElementClass(this.element(), 'ios-overlay-hide');
+ MochiKit.DOM.addElementClass(this.element(), 'ios-overlay-show');
+ },
+
+ 'done': function (aMessage, aDelayBeforeHiding) {
+ this.completed(this.showDoneIcon, aMessage, aDelayBeforeHiding);
+ },
+
+ 'failed': function (aMessage, aDelayBeforeHiding) {
+ this.completed(this.showFailIcon, aMessage, aDelayBeforeHiding);
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'resetStatus': function () {
+ MochiKit.Style.showElement(this.element());
+ MochiKit.Style.showElement(this.getElement('spinner'));
+ MochiKit.Style.hideElement(this.getElement('done'));
+ MochiKit.Style.hideElement(this.getElement('failed'));
+ },
+
+ 'setMessage': function (aMessage) {
+ if (typeof(aMessage) != 'undefined') {
+ this.getElement('title').innerHTML = aMessage;
+ }
+ },
+
+ 'completed': function (aFunctionToShowResult, aMessage, aDelayBeforeHiding) {
+ var delay = aDelayBeforeHiding || this.defaultDelay();
+
+ this.hideSpinner();
+ MochiKit.Base.bind(aFunctionToShowResult, this)();
+ this.setMessage(aMessage);
+
+ MochiKit.Async.callLater(delay, MochiKit.Base.bind(this.hide, this))
+ },
+
+ 'hide': function () {
+ MochiKit.DOM.removeElementClass(this.element(), 'ios-overlay-show');
+ MochiKit.DOM.addElementClass(this.element(), 'ios-overlay-hide');
+ MochiKit.Async.callLater(1, MochiKit.Style.hideElement, this.element());
+ },
+
+ 'hideSpinner': function () {
+ MochiKit.Style.hideElement(this.getElement('spinner'));
+ },
+
+ 'showDoneIcon': function () {
+ MochiKit.Style.showElement(this.getElement('done'));
+ },
+
+ 'showFailIcon': function () {
+ MochiKit.Style.showElement(this.getElement('failed'));
+ },
+
+ //-------------------------------------------------------------------------
+
+ 'defaultDelay': function () {
+ return this._defaultDelay;
+ },
+
+ //-------------------------------------------------------------------------
+ __syntaxFix__: "syntax fix"
+});
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 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.PageTemplate = React.createClass({
+ render: function() {
+ return React.DOM.div(null, [
+ React.DOM.div({'className': 'header'}, [
+ React.DOM.h1(null, "clipperz")
+ ]),
+ React.DOM.div({'className': 'content'}, this.props.innerComponent)
+ ])
+ }
+});
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 @@
+/*
+
+Copyright 2008-2013 Clipperz Srl
+
+This file is part of Clipperz, the online password manager.
+For further information about its features and functionalities please
+refer to http://www.clipperz.com.
+
+* Clipperz is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+* Clipperz is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU Affero General Public License for more details.
+
+* You should have received a copy of the GNU Affero General Public
+ License along with Clipperz. If not, see http://www.gnu.org/licenses/.
+
+*/
+
+Clipperz.PM.UI.Components.RegistrationWizard = React.createClass({
+
+ getDefaultProps: function () {
+ return {
+ steps: [
+ {name:'CREDENTIALS', label:'registration', _label:'credentials', description:"Choose your credentails"},
+ {name:'PASSWORD_VERIFICATION', label:'registration', _label:'verify', description:"Verify your passphrase"},
+ {name:'TERMS_OF_SERVICE', label:'registration', _label:'terms', description:"Check our terms of service"}
+ ],
+ disabled: false,
+ template: Clipperz.PM.UI.Components.PageTemplate
+ }
+ },
+
+ getInitialState: function () {
+ return {
+ currentStep: this.props['steps'][0]['name'],
+ username: '',
+ passphrase: '',
+ verify_passphrase: '',
+ no_password_recovery: false,
+ agree_terms_of_service: false
+ };
+ },
+
+ 'propTypes': {
+// steps: React.PropTypes.array,
+ disabled: React.PropTypes.bool,
+ template: React.PropTypes.func
+ },
+
+ //=========================================================================
+
+ currentStepIndex: function () {
+ return this.indexOfStepNamed(this.state['currentStep']);
+ },
+
+ indexOfStepNamed: function (aStepName) {
+ var stepConfiguration;
+ var result;
+
+ stepConfiguration = this.props['steps'].filter(function (aConfig) { return aConfig['name'] == aStepName})[0];
+ result = this.props['steps'].indexOf(stepConfiguration);
+ return result;
+ },
+
+ //=========================================================================
+
+ statusClassForStep: function (aStep) {
+ var currentStepIndex = this.currentStepIndex();
+ var stepIndex = this.indexOfStepNamed(aStep['name']);
+ var result;
+
+ if (stepIndex < currentStepIndex) {
+ result = 'left';
+ } else if (stepIndex == currentStepIndex) {
+ result = 'center';
+ } else {
+ result = 'right';
+ }
+
+ return result;
+ },
+
+ //=========================================================================
+
+ handleBackClick: function (anEvent) {
+ var nextStep;
+ anEvent.preventDefault();
+
+ if (this.currentStepIndex() > 0) {
+ nextStep = this.props['steps'][this.currentStepIndex() - 1];
+ this.setState({currentStep: nextStep['name']});
+ } else {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack');
+ }
+ },
+
+ handleForwardClick: function (anEvent) {
+ var nextStep;
+ anEvent.preventDefault();
+
+ if (this.canMoveForward()) {
+
+ if (this.currentStepIndex() < this.props['steps'].length - 1) {
+ nextStep = this.props['steps'][this.currentStepIndex() + 1];
+ this.setState({currentStep: nextStep['name']});
+ } else {
+ MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'registerNewUser', {
+ username: this.state['username'],
+ passphrase: this.state['passphrase']
+ })
+ }
+ }
+ },
+
+ //-------------------------------------------------------------------------
+
+ canMoveForward: function () {
+ var result;
+ var currentStep;
+
+ result = false;
+ currentStep = this.state['currentStep'];
+ if (currentStep == 'CREDENTIALS') {
+ result = ((this.state['username'] != '') && (this.state['passphrase'] != ''));
+ } else if (currentStep == 'PASSWORD_VERIFICATION') {
+ result = (this.state['passphrase'] == this.state['verify_passphrase']);
+ } else if (currentStep == 'TERMS_OF_SERVICE') {
+ result = (this.state['no_password_recovery'] && this.state['agree_terms_of_service']);
+ }
+
+ return result && !this.props['disabled'];
+ },
+
+ //=========================================================================
+
+ handleChange: function (anEvent) {
+ var refs = this.refs;
+ var refName = MochiKit.Base.filter(function (aRefName) { return refs[aRefName].getDOMNode() == anEvent.target}, MochiKit.Base.keys(this.refs))[0];
+ var newState = {};
+
+ if ((event.target.type == 'checkbox') || (event.target.type == 'radio')) {
+ newState[refName] = event.target.checked;
+ } else {
+ newState[refName] = event.target.value;
+ }
+ this.setState(newState);
+ },
+
+ //=========================================================================
+
+ renderIndexStep: function (aStep) {
+ return React.DOM.div({'className':'stepIndexItem ' + this.statusClassForStep(aStep)}, '.');
+ },
+
+ renderButtons: function () {
+ return [
+ React.DOM.a({className:'back button step_' + (this.currentStepIndex() - 1), onClick:this.handleBackClick}, '<<'),
+ React.DOM.a({className:'forward button step_' + (this.currentStepIndex() + 1) + ' ' + (this.canMoveForward() ? 'enabled' : 'disabled'), onClick:this.handleForwardClick}, '>>')
+ ];
+ },
+
+ render_CREDENTIALS: function () {
+ return React.DOM.div(null,[
+ React.DOM.label({'for':'name'}, "username"),
+ React.DOM.input({'type':'text', 'name':'name', 'ref':'username', 'placeholder':"username", 'key':'username', 'autoCapitalize':'none'/*, value:this.state.username*/}),
+ React.DOM.label({'for':'passphrase'}, "passphrase"),
+ React.DOM.input({'type':'password', 'name':'passphrase', 'ref':'passphrase', 'placeholder':"passphrase", 'key':'passphrase'/*, value:this.state.passphrase*/})
+ ]);
+ },
+
+ render_PASSWORD_VERIFICATION: function () {
+ return React.DOM.div(null,[
+ React.DOM.label({'for':'verify_passphrase'}, "passphrase"),
+ React.DOM.input({'type':'password', 'name':'verify_passphrase', 'ref':'verify_passphrase', 'placeholder':"verify passphrase", 'key':'verify_passphrase'})
+ ]);
+ },
+
+ render_TERMS_OF_SERVICE: function () {
+ return React.DOM.div(null, [
+ React.DOM.div({className:'checkboxBlock'}, [
+ React.DOM.label({'for':'no_password_recovery'}, "I understand that Clipperz will not be able to recover a lost passphrase."),
+ React.DOM.input({'type':'checkbox', 'name':'no_password_recovery', 'ref':'no_password_recovery', 'key':'no_password_recovery'}),
+ React.DOM.p(null, "I understand that Clipperz will not be able to recover a lost passphrase.")
+ ]),
+ React.DOM.div({className:'checkboxBlock'}, [
+ React.DOM.label({'for':'agree_terms_of_service'}, "I have read and agreed to the Terms of Service."),
+ React.DOM.input({'type':'checkbox', 'name':'agree_terms_of_service', 'ref':'agree_terms_of_service', 'key':'agree_terms_of_service'}),
+ React.DOM.p(null, [
+ "I have read and agreed to the ",
+ React.DOM.a({href:'https://clipperz.com/terms_service/', target:'_blank'}, "Terms of Service.")
+ ])
+ ])
+ ]);
+ },
+
+ renderStep: function (aStep) {
+ return React.DOM.div({'className':'step' + ' ' + aStep['name'] + ' ' + this.statusClassForStep(aStep) + ' step_' + this.currentStepIndex()}, [
+ React.DOM.h1(null, aStep['label']),
+ React.DOM.p(null, aStep['description']),
+ this['render_' + aStep['name']].apply(),
+ React.DOM.div({'className':'stepIndex'}, MochiKit.Base.map(this.renderIndexStep, this.props['steps'])),
+ React.DOM.div({'className':'buttons'}, this.renderButtons())
+ ]);
+ },
+
+ _render: function () {
+ return React.DOM.div({'className':'registrationForm'},[
+ React.DOM.form({onChange: this.handleChange}, [
+ React.DOM.div({'className':'steps'}, MochiKit.Base.map(this.renderStep, this.props['steps']))
+ ])
+ ]);
+ },
+
+ render: function () {
+ return new this.props.template({'innerComponent': this._render()});
+ },
+
+ //=========================================================================
+
+ setInitialFocus: function () {
+ this.refs['username'].getDOMNode().focus();
+ },
+
+ componentDidUpdate: function (prevProps, prevState, rootNode) {
+ if (prevState['currentStep'] != this.state['currentStep']) {
+ if (this.state['currentStep'] == 'CREDENTIALS') {
+ this.refs['passphrase'].getDOMNode().select();
+ } else if (this.state['currentStep'] == 'PASSWORD_VERIFICATION') {
+ this.refs['verify_passphrase'].getDOMNode().select();
+ }
+ }
+ }
+
+ //=========================================================================
+});