author | Giulio Cesare Solaroli <giulio.cesare@clipperz.com> | 2013-10-02 07:59:30 (UTC) |
---|---|---|
committer | Giulio Cesare Solaroli <giulio.cesare@clipperz.com> | 2013-10-02 07:59:30 (UTC) |
commit | 1180b7b195157aaeb4f0d5380e0c886bbd06c2e2 (patch) (side-by-side diff) | |
tree | 709e33a09d9325d382aabaf0a0828e20ebdb96db /frontend/delta/js/Clipperz/PM | |
parent | 20bea94ab6b91c85b171dcf86baba0a64169d508 (diff) | |
download | clipperz-1180b7b195157aaeb4f0d5380e0c886bbd06c2e2.zip clipperz-1180b7b195157aaeb4f0d5380e0c886bbd06c2e2.tar.gz clipperz-1180b7b195157aaeb4f0d5380e0c886bbd06c2e2.tar.bz2 |
Updated /delta
Switched from less to scss. Still no build script to update the final CSS, though.
Added preliminary support for storing account data on browser's local storage for offline viewing. No public backend currently support this feature.
Diffstat (limited to 'frontend/delta/js/Clipperz/PM') (more/less context) (ignore whitespace changes)
11 files changed, 425 insertions, 47 deletions
diff --git a/frontend/delta/js/Clipperz/PM/DataModel/DevicePreferences.js b/frontend/delta/js/Clipperz/PM/DataModel/DevicePreferences.js new file mode 100644 index 0000000..ff3b33f --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/DataModel/DevicePreferences.js @@ -0,0 +1,90 @@ +/* + +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.DataModel.DevicePreferences = function (args) { + args = args || {}; + + this._data = null; + + Clipperz.PM.DataModel.DevicePreferences.superclass.constructor.apply(this, arguments); + + return this; +} + +Clipperz.Base.extend(Clipperz.PM.DataModel.DevicePreferences, Object, { + + toString: function () { + return "Clipperz.PM.DataModel.DevicePreferences"; + }, + + //------------------------------------------------------------------------- + + shouldStoreDataLocally: function () { + return (localStorage.getItem('shouldStoreDataLocally') === 'true'); + }, + + setShouldStoreDataLocally: function (aValue) { + localStorage.setItem('shouldStoreDataLocally', aValue); + }, + + //------------------------------------------------------------------------- + + setAccountDataWityResponse: function (aResponse) { + localStorage.setItem('clipperz_dump_data', aResponse['data']); + localStorage.setItem('clipperz_dump_version', aResponse['version']); + localStorage.setItem('clipperz_dump_date', new Date()); + + this._data = null; + }, + + accountData: function () { + if (this._data == null) { + var data; + + data = localStorage.getItem('clipperz_dump_data'); + if (data != null) { + this._data = JSON.parse(data); + } + } + + return this._data; + }, + + latestDownload: function () { + var result; + var date; + + date = localStorage.getItem('clipperz_dump_date'); + if (date != null) { + result = new Date(date); + } else { + result = null; + } + + return result; + }, + + //========================================================================= + __syntaxFix__: "syntax fix" +}); + diff --git a/frontend/delta/js/Clipperz/PM/Proxy.js b/frontend/delta/js/Clipperz/PM/Proxy.js index 2ac684a..71c784f 100644 --- a/frontend/delta/js/Clipperz/PM/Proxy.js +++ b/frontend/delta/js/Clipperz/PM/Proxy.js @@ -91,96 +91,97 @@ Clipperz.PM.Proxy.prototype = MochiKit.Base.update(null, { } else { deferredResult = MochiKit.Async.succeed({parameters:someParameters}); } return deferredResult; }, //------------------------------------------------------------------------- 'addToll': function(aToll) { this.tolls()[aToll.requestType()].push(aToll); }, //========================================================================= 'setTollCallback': function(someParameters) { if (typeof(someParameters['toll']) != 'undefined') { this.addToll(new Clipperz.PM.Toll(someParameters['toll'])); } return someParameters['result']; }, //========================================================================= 'registration': function (someParameters) { return this.processMessage('registration', someParameters, 'REGISTER'); }, 'handshake': function (someParameters) { return this.processMessage('handshake', someParameters, 'CONNECT'); }, 'message': function (someParameters) { return this.processMessage('message', someParameters, 'MESSAGE'); }, 'logout': function (someParameters) { return this.processMessage('logout', someParameters, 'MESSAGE'); }, //========================================================================= 'processMessage': function (aFunctionName, someParameters, aRequestType) { var deferredResult; deferredResult = new Clipperz.Async.Deferred("Proxy.processMessage", {trace:false}); deferredResult.addMethod(this, 'payToll', aRequestType); deferredResult.addMethod(this, 'sendMessage', aFunctionName); deferredResult.addMethod(this, 'setTollCallback'); deferredResult.callback(someParameters); return deferredResult; }, //========================================================================= '_sendMessage': function (aFunctionName, aVersion, someParameters) { throw Clipperz.Base.exception.AbstractMethod; }, 'sendMessage': function (aFunctionName, someParameters) { var deferredResult; +console.log("PROXY.sendMessage", aFunctionName, someParameters); // TODO: read actual application version for a property set at build time deferredResult = new Clipperz.Async.Deferred("Proxy.sendMessage", {trace:false}); deferredResult.addMethod(this, '_sendMessage', aFunctionName, 'fake-app-version'); deferredResult.addErrback(MochiKit.Base.method(this, 'handleError')); deferredResult.callback(someParameters); return deferredResult; }, //------------------------------------------------------------------------- 'handleError': function (anError) { if (anError['message'] == 'Wrong application version') { anError['isPermanent'] = true; } return anError; }, //========================================================================= 'isReadOnly': function () { return false; }, 'canRegisterNewUsers': function () { return true; }, //========================================================================= __syntaxFix__: "syntax fix" }); diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js index 1638d99..6deee3d 100755 --- a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js +++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.JSON.js @@ -1,86 +1,86 @@ /* 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/. */ if (typeof(Clipperz) == 'undefined') { Clipperz = {}; } if (typeof(Clipperz.PM) == 'undefined') { Clipperz.PM = {}; } //============================================================================= Clipperz.PM.Proxy.JSON = function(args) { Clipperz.PM.Proxy.JSON.superclass.constructor.call(this, args); this._url = args.url || Clipperz.Base.exception.raise('MandatoryParameter'); return this; } Clipperz.Base.extend(Clipperz.PM.Proxy.JSON, Clipperz.PM.Proxy, { 'toString': function() { return "Clipperz.PM.Proxy.JSON"; }, //========================================================================= 'url': function () { return this._url; }, //========================================================================= '_sendMessage': function(aFunctionName, aVersion, someParameters) { var deferredResult; var parameters; parameters = { method: aFunctionName, version: aVersion, parameters: Clipperz.Base.serializeJSON(someParameters) }; - - deferredResult = new Clipperz.Async.Deferred("Proxy.JSON.sendMessage", {trace:false}); +console.log("PROXY.JSON._sendMessage", parameters); + deferredResult = new Clipperz.Async.Deferred("Proxy.JSON._sendMessage", {trace:false}); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestSent'); deferredResult.addCallback(MochiKit.Async.doXHR, this.url(), { method:'POST', sendContent:MochiKit.Base.queryString(parameters), headers:{"Content-Type":"application/x-www-form-urlencoded"} }); deferredResult.addCallbackPass(MochiKit.Signal.signal, Clipperz.Signal.NotificationCenter, 'remoteRequestReceived'); deferredResult.addCallback(MochiKit.Base.itemgetter('responseText')); deferredResult.addCallback(Clipperz.Base.evalJSON); deferredResult.addCallback(function (someValues) { if (someValues['result'] == 'EXCEPTION') { throw someValues['message']; } return someValues; }) deferredResult.callback(); return deferredResult; }, //========================================================================= __syntaxFix__: "syntax fix" }); diff --git a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js index a3c238c..3f16f70 100644 --- a/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js +++ b/frontend/delta/js/Clipperz/PM/Proxy/Proxy.Offline.LocalStorageDataStore.js @@ -1,97 +1,99 @@ /* 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/. */ try { if (typeof(Clipperz.PM.Proxy.Offline.DataStore) == 'undefined') { throw ""; }} catch (e) { throw "Clipperz.PM.Proxy.Offline.LocalStorageDataStore depends on Clipperz.PM.Proxy.Offline.DataStore!"; } //============================================================================= Clipperz.PM.Proxy.Offline.LocalStorageDataStore = function(args) { args = args || {}; - this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null); +// this._data = args.data || (typeof(_clipperz_dump_data_) != 'undefined' ? _clipperz_dump_data_ : null); + this._data = JSON.parse(localStorage.getItem('clipperz_dump_data')); + this._isReadOnly = (typeof(args.readOnly) == 'undefined' ? true : args.readOnly); this._shouldPayTolls = args.shouldPayTolls || false; this._tolls = {}; this._currentStaticConnection = null; // Clipperz.PM.Proxy.Offline.LocalStorageDataStore.superclass.constructor.apply(this, arguments); return this; } Clipperz.Base.extend(Clipperz.PM.Proxy.Offline.LocalStorageDataStore, Clipperz.PM.Proxy.Offline.DataStore, { //========================================================================= '_knock': function(aConnection, someParameters) { var result; result = { toll: this.getTollForRequestType(someParameters['requestType']) } return result; }, //------------------------------------------------------------------------- '_registration': function(aConnection, someParameters) { throw Clipperz.PM.Proxy.Offline.DataStore.exception.ReadOnly; }, //------------------------------------------------------------------------- '_handshake': function(aConnection, someParameters) { var result; var nextTollRequestType; result = {}; if (someParameters.message == "connect") { var userData; var randomBytes; var v; userData = this.data()['users'][someParameters.parameters.C]; if ((typeof(userData) != 'undefined') && (userData['version'] == someParameters.version)) { aConnection['userData'] = userData; aConnection['C'] = someParameters.parameters.C; } else { aConnection['userData'] = this.data()['users']['catchAllUser']; } randomBytes = Clipperz.Crypto.Base.generateRandomSeed(); aConnection['b'] = new Clipperz.Crypto.BigInt(randomBytes, 16); v = new Clipperz.Crypto.BigInt(aConnection['userData']['v'], 16); aConnection['B'] = v.add(Clipperz.Crypto.SRP.g().powerModule(aConnection['b'], Clipperz.Crypto.SRP.n())); aConnection['A'] = someParameters.parameters.A; result['s'] = aConnection['userData']['s']; result['B'] = aConnection['B'].asString(16); nextTollRequestType = 'CONNECT'; } else if (someParameters.message == "credentialCheck") { diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js index df514a2..12ddce3 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardDetail.js @@ -1,142 +1,195 @@ /* 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, + unmaskedFields: new Clipperz.Set(), starred: false }; }, handleDirectLoginClick: function (aDirectLoginReference, anEvent) { MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'runDirectLogin', {record:this.props.card['reference'], directLogin:aDirectLoginReference}); }, + toggleFieldVisibility: function (aField, anEvent) { + var unmaskedFields; + var fieldReference; + + unmaskedFields = this.state['unmaskedFields']; + fieldReference = aField['reference'] + if (unmaskedFields.contains(fieldReference)) { + unmaskedFields.remove(fieldReference) + } else { + unmaskedFields.add(fieldReference) + } + + this.setState({'unmaskedFields': unmaskedFields}); + }, + + handleGoAction: function (aField, anEvent) { + var newWindow; + + newWindow = MochiKit.DOM.currentWindow().open(aField['value'], '_blank'); + newWindow.focus(); + }, + + handleEmailAction: function (aField, anEvent) { + MochiKit.DOM.currentWindow().location = 'mailto:' + aField['value']; + }, + //========================================================================= 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; + renderFieldActionButton: function (aField) { +// var actionLabel; + var result; if (aField['actionType'] == 'URL') { - actionLabel = "go"; + result = React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'handleGoAction', aField)}, [ + React.DOM.a({className:aField['actionType']}, "go") + ]); } else if (aField['actionType'] == 'PASSWORD') { - actionLabel = "locked"; + var icon; + + if (this.state['unmaskedFields'].contains(aField['reference'])) { + icon = "unlocked"; + } else { + icon = "locked"; + } + result = React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'toggleFieldVisibility', aField)}, [ + React.DOM.a({className:aField['actionType']}, icon) + ]); } else if (aField['actionType'] == 'EMAIL') { - actionLabel = "email"; + result = React.DOM.div({className:'actionWrapper', onClick:MochiKit.Base.method(this, 'handleEmailAction', aField)}, [ + React.DOM.a({className:aField['actionType']}, "email") + ]); } else { - actionLabel = ""; + result = null; + } + + return result; + }, + + renderField: function (aField) { +//console.log("FIELD", aField); + var fieldExtraClass; + + fieldExtraClass = aField['actionType']; + if (this.state['unmaskedFields'].contains(aField['reference'])) { + fieldExtraClass = fieldExtraClass + ' unlocked'; } - return React.DOM.div({className:'listItem ' + aField['actionType']}, [ + return React.DOM.div({className:'listItem ' + fieldExtraClass, key:aField['reference']}, [ 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:'valueWrapper'}, React.DOM.span({className:'value ' + fieldExtraClass}, this.normalizeFieldValue(aField['value']))) ]) ]), - React.DOM.div({className:'actionWrapper'}, [ - React.DOM.div({className:aField['actionType']}, actionLabel) - ]) + this.renderFieldActionButton(aField) +// 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(); +// window.history.back(); + MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack'); }, handleStarClick: function (anEvent) { this.setState({starred: !this.state['starred']}); }, //========================================================================= render: function () { var card = this.props.card; - var starredStatus = (this.state['starred'] ? "starred" : "unstarred"); +// 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:'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 index 66d20f1..5a44a4a 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/CardList.js @@ -36,126 +36,133 @@ Clipperz.PM.UI.Components.CardList = React.createClass({ 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; }, //========================================================================= + showPreferences: function (anEvent) { + MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'showPreferences', anEvent); + }, + + //========================================================================= + 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)}, [ + // TODO: verify if it is possible to put the onClick handler on the container 'div', instead of adding it to each 'div' item. + return React.DOM.div({className:'listItem', key:reference, 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:'addCard'}, 'add'), React.DOM.a({className:'search ' + (this.state.showSearch ? 'selected' : ''), onClick:this.toggleSearch}, 'search'), - React.DOM.a({className:'settings'}, 'settings') + React.DOM.a({className:'settings', onClick:this.showPreferences}, 'settings') ]), // this.searchBox() ]), this.searchBox(), React.DOM.div({className:'content cardList'}, this.cardListItems()), ]); } //========================================================================= }); diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/Checkbox.js b/frontend/delta/js/Clipperz/PM/UI/Components/Checkbox.js new file mode 100644 index 0000000..9538063 --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Checkbox.js @@ -0,0 +1,44 @@ +/* + +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.Checkbox = React.createClass({ +// http://development.tobypitman.com/iphoneCheckboxes/iphoneCheckboxes2.html + + propTypes: { + 'checked': React.PropTypes.bool.isRequired, + 'id': React.PropTypes.string.isRequired, + 'eventHandler': React.PropTypes.func.isRequired + }, + + //========================================================================= + + render: function () { + return React.DOM.div({className:'checkbox', onClick:this.props['eventHandler']}, [ + React.DOM.input({name:this.props['id'], id:this.props['id'], value:this.props['id'], type:'checkbox', checked:this.props['checked']}), + React.DOM.label({className:'check', 'for':this.props['id']}), + React.DOM.label({className:'info', 'for':this.props['id']}, "enable local storage") + ]); + } + + //========================================================================= +}); diff --git a/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js index 2b5b4a4..801549f 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/LoginForm.js @@ -31,120 +31,120 @@ Clipperz.PM.UI.Components.LoginForm = React.createClass({ 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") + React.DOM.a({'onClick':this.handleRegistrationLinkClick}, "Sign up") ]); 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.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.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 index cc4a06c..cb5f81a 100644 --- a/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js +++ b/frontend/delta/js/Clipperz/PM/UI/Components/Overlay.js @@ -33,90 +33,91 @@ Clipperz.PM.UI.Components.Overlay = function(args) { } //============================================================================= 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()); + var element = this.element(); + MochiKit.DOM.removeElementClass(element, 'ios-overlay-show'); + MochiKit.DOM.addElementClass(element, 'ios-overlay-hide'); + MochiKit.Async.callLater(1, MochiKit.Style.hideElement, 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/PreferencePage.js b/frontend/delta/js/Clipperz/PM/UI/Components/PreferencePage.js new file mode 100644 index 0000000..822acc2 --- a/dev/null +++ b/frontend/delta/js/Clipperz/PM/UI/Components/PreferencePage.js @@ -0,0 +1,88 @@ +/* + +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.PreferencePage = React.createClass({ + + getDefaultProps: function () { + return { + } + }, + + propTypes: { +// card: React.PropTypes.object.isRequired +// checked: React.PropTypes.boolean.isRequired + }, + + getInitialState: function () { +// return { +// shouldStoreDataLocally: false +// }; + }, + + handleBackClick: function (anEvent) { + MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'goBack'); + }, + + toggleShouldStoreDataLocally: function (anEvent) { +// this.setState({shouldStoreDataLocally: !this.state['shouldStoreDataLocally']}); + Clipperz.PM.DataModel.devicePreferences.setShouldStoreDataLocally(!Clipperz.PM.DataModel.devicePreferences.shouldStoreDataLocally()); + this.setState({}); + }, + + shouldStoreDataLocally: function () { + return Clipperz.PM.DataModel.devicePreferences.shouldStoreDataLocally(); + }, + + syncNow: function (anEvent) { + MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'synchronizeLocalData'); + }, + + //========================================================================= + + render: function () { + return React.DOM.div({className:'preferences'}, [ + React.DOM.div({className:'header'}, [ + React.DOM.div({className:'titleWrapper'}, React.DOM.div({className:'title'}, "Preferences")), + React.DOM.div({className:'backWrapper'}, React.DOM.a({className:'button back', onClick:this.handleBackClick}, "back")), + ]), + React.DOM.div({className:'content'}, [ + React.DOM.form(null, [ + React.DOM.div({className:'section'}, [ + React.DOM.h4(null, "Local storage"), + React.DOM.p(null, "Store you account data locally for offline viewing"), + new Clipperz.PM.UI.Components.Checkbox({'id':'shouldStoreLocally_checkbox', 'checked':this.shouldStoreDataLocally(), 'eventHandler':this.toggleShouldStoreDataLocally}), + this.shouldStoreDataLocally() ? React.DOM.div({className:'syncInfo'}, [ +// React.DOM.h5(null, "data were never synchronized before"), + React.DOM.a({className:'button', onClick:this.syncNow}, "Sync now") + ]) : null + ]) + ]) + ]), + React.DOM.div({className:'footer'}, [ + + ]) + ]); + } + + //========================================================================= +}); diff --git a/frontend/delta/js/Clipperz/PM/UI/MainController.js b/frontend/delta/js/Clipperz/PM/UI/MainController.js index da7540e..20ff041 100644 --- a/frontend/delta/js/Clipperz/PM/UI/MainController.js +++ b/frontend/delta/js/Clipperz/PM/UI/MainController.js @@ -1,491 +1,583 @@ /* 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'); Clipperz.PM.UI.MainController = function() { var pages; - this._proxy = null; +// this._proxy = null; this._user = null; this._filter = ''; // this._currentPage = 'loadingPage'; this._pageStack = ['loadingPage']; this._overlay = new Clipperz.PM.UI.Components.Overlay(); pages = { 'loginPage': new Clipperz.PM.UI.Components.LoginForm(), 'registrationPage': new Clipperz.PM.UI.Components.RegistrationWizard(), 'cardListPage': new Clipperz.PM.UI.Components.CardList(), 'cardDetailPage': new Clipperz.PM.UI.Components.CardDetail({card: {}}), + 'preferencePage': new Clipperz.PM.UI.Components.PreferencePage(), 'errorPage': new Clipperz.PM.UI.Components.ErrorPage({message:''}) }; MochiKit.Base.map(function (anId) {React.renderComponent(pages[anId], MochiKit.DOM.getElement(anId))}, MochiKit.Base.keys(pages)); this._pages = pages; this.registerForNotificationCenterEvents(); + MochiKit.Signal.connect(MochiKit.DOM.currentDocument(), 'onselectionchange', this, 'selectionChangeHandler'); return this; } MochiKit.Base.update(Clipperz.PM.UI.MainController.prototype, { toString: function () { return "Clipperz.PM.UI.MainController"; }, //========================================================================= overlay: function () { return this._overlay; }, loginForm: function () { return this._loginForm; }, registrationWizard: function () { return this._registrationWizard; }, //========================================================================= isOnline: function() { return navigator.onLine; +// return false; }, hasLocalData: function() { - return false; +// return false; + return (Clipperz.PM.DataModel.devicePreferences.accountData() != null); }, loginMode: function () { // PIN is set using this command: // Clipperz.PM.PIN.setCredentialsWithPIN('1234', {'username':'joe', 'passphrase':'clipperz'}); return Clipperz.PM.PIN.isSet() ? 'PIN' : 'CREDENTIALS'; }, //========================================================================= pages: function () { return this._pages; }, pageStack: function () { return this._pageStack; }, //========================================================================= + showOfflineError: function () { +console.log("THE BROWSER IS OFFLINE"); + }, + selectInitialProxy: function () { if (this.isOnline()) { - this._proxy = Clipperz.PM.Proxy.defaultProxy; +// this._proxy = Clipperz.PM.Proxy.defaultProxy; } else { if (this.hasLocalData()) { - this._proxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false}); +// this._proxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false}); + Clipperz.PM.Proxy.defaultProxy = new Clipperz.PM.Proxy.Offline({dataStore: new Clipperz.PM.Proxy.Offline.LocalStorageDataStore(), shouldPayTolls:false}); } else { this.showOfflineError(); } } }, - proxy: function () { - return this._proxy; - }, +// proxy: function () { +// return this._proxy; +// }, //========================================================================= registerForNotificationCenterEvents: function () { - var events = ['doLogin', 'registerNewUser', 'showRegistrationForm', 'goBack', 'showRecord', 'searchCards', 'runDirectLogin']; + var events = [ + 'doLogin', + 'registerNewUser', + 'showRegistrationForm', + 'goBack', + 'showRecord', + 'searchCards', + 'showPreferences', + 'runDirectLogin', + 'synchronizeLocalData' + ]; var self = this; MochiKit.Base.map(function (anEvent) { MochiKit.Signal.connect(Clipperz.Signal.NotificationCenter, anEvent, MochiKit.Base.method(self, anEvent)); }, events); // MochiKit.Signal.connect(window, 'onpopstate', MochiKit.Base.method(this, 'historyGoBack')); MochiKit.Signal.connect(window, 'onbeforeunload', MochiKit.Base.method(this, 'shouldExitApp')); }, //------------------------------------------------------------------------- + selectionChangeHandler: function (anEvent) { + var selection; + var selectionRange; + var selectionNode; + var valueElement; +// other hints: http://www.bearpanther.com/2013/05/27/easy-text-selection-in-mobile-safari/ +// SELECTION: https://developer.mozilla.org/en-US/docs/Web/API/Selection +// RANGE: https://developer.mozilla.org/en-US/docs/Web/API/Range +// NODE TYPES: https://developer.mozilla.org/en-US/docs/Web/API/Node.nodeType + + selection = MochiKit.DOM.currentWindow().getSelection(); +//console.log("-- selection", selection); + selectionRange = selection.getRangeAt(0); + selectionNode = selectionRange.startContainer.childNodes[selectionRange.startOffset]; +//console.log("-- selectionNode", selectionNode); + + if (selectionNode != undefined) { + valueElement = MochiKit.DOM.getFirstElementByTagAndClassName('*', 'value', selectionNode); +//console.log("-- valueElement", valueElement); + } + + if ((valueElement != null) && (valueElement != selectionNode)) { + var range; + range = MochiKit.DOM.currentDocument().createRange(); + range.selectNodeContents(valueElement); + selection.removeAllRanges(); + selection.addRange(range); + + anEvent.preventDefault(); + anEvent.stopPropagation(); + +//console.log("updated selection", MochiKit.DOM.currentWindow().getSelection()); + } +//console.log("-----------"); + }, + + //------------------------------------------------------------------------- + run: function (parameters) { var shouldShowRegistrationForm; + var canRegisterNewUsers; + + canRegisterNewUsers = Clipperz.PM.Proxy.defaultProxy.canRegisterNewUsers(); this.selectInitialProxy(); - shouldShowRegistrationForm = parameters['shouldShowRegistrationForm'] && this.proxy().canRegisterNewUsers(); - this.pages()['loginPage'].setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()}); + shouldShowRegistrationForm = parameters['shouldShowRegistrationForm'] && canRegisterNewUsers; + this.pages()['loginPage'].setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable':canRegisterNewUsers}); if (shouldShowRegistrationForm) { this.showRegistrationForm(); } else { this.showLoginForm(); } this.overlay().done("", 0.5); }, //------------------------------------------------------------------------- showLoginForm: function () { var loginFormPage; loginFormPage = this.pages()['loginPage']; - loginFormPage.setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable': this.proxy().canRegisterNewUsers()}); + loginFormPage.setProps({'mode':this.loginMode(), 'isNewUserRegistrationAvailable':Clipperz.PM.Proxy.defaultProxy.canRegisterNewUsers()}); this.moveInPage(this.currentPage(), 'loginPage'); MochiKit.Async.callLater(0.5, MochiKit.Base.method(loginFormPage, 'setInitialFocus')); }, showRegistrationForm: function () { var currentPage; var registrationPage; currentPage = this.currentPage(); registrationPage = this.pages()['registrationPage']; this.setCurrentPage('loginPage'); registrationPage.setProps({}); this.moveInPage(currentPage, 'registrationPage'); MochiKit.Async.callLater(0.5, MochiKit.Base.method(registrationPage, 'setInitialFocus')); }, //========================================================================= doLogin: function (event) { var credentials; var getPassphraseDelegate; var user; user = null; this.overlay().show("logging in"); this.pages()['loginPage'].setProps({disabled:true}); if ('pin' in event) { credentials = Clipperz.PM.PIN.credentialsWithPIN(event['pin']); } else { credentials = event; } getPassphraseDelegate = MochiKit.Base.partial(MochiKit.Async.succeed, credentials.passphrase); user = new Clipperz.PM.DataModel.User({'username':credentials.username, 'getPassphraseFunction':getPassphraseDelegate}); deferredResult = new Clipperz.Async.Deferred('MainController.doLogin', {trace:false}); deferredResult.addCallback(MochiKit.Async.wait, 0.1); deferredResult.addMethod(Clipperz.Crypto.PRNG.defaultRandomGenerator(), 'deferredEntropyCollection'); deferredResult.addMethod(user, 'login'); deferredResult.addMethod(Clipperz.PM.PIN, 'resetFailedAttemptCount'); deferredResult.addMethod(this, 'setUser', user); // deferredResult.addMethod(this, 'setupApplication'); deferredResult.addMethod(this, 'runApplication'); deferredResult.addMethod(this.overlay(), 'done', "", 1); deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event)); deferredResult.addErrback(MochiKit.Base.bind(function (anEvent, anError) { if (anError['isPermanent'] != true) { this.pages()['loginPage'].setProps({disabled:false, 'mode':this.loginMode()}); - this.pages()['loginPage'].setInitialFocus(); - } - return anError; + this.pages()['loginPage'].setInitialFocus(); + } + return anError; }, this, event)) deferredResult.callback(); return deferredResult; }, //------------------------------------------------------------------------- registerNewUser: function (credentials) { var deferredResult; this.overlay().show("creating user"); this.pages()['registrationPage'].setProps({disabled:true}); deferredResult = new Clipperz.Async.Deferred('MainController.registerNewUser', {trace:false}); deferredResult.addCallback(Clipperz.PM.DataModel.User.registerNewAccount, credentials['username'], MochiKit.Base.partial(MochiKit.Async.succeed, credentials['passphrase']) ); deferredResult.addMethod(this, 'doLogin', credentials); deferredResult.addErrback(MochiKit.Base.method(this, 'genericErrorHandler', event)); deferredResult.addErrback(MochiKit.Base.bind(function (anError) { if (anError['isPermanent'] != true) { this.pages()['registrationPage'].setProps({disabled:false}); this.pages()['registrationPage'].setInitialFocus(); } return anError; }, this)); deferredResult.callback(); return deferredResult; }, //------------------------------------------------------------------------- user: function () { return this._user; }, setUser: function (aUser) { this._user = aUser; return this._user; }, //========================================================================= allCardInfo: function () { var deferredResult; var cardInfo; cardInfo = { '_rowObject': MochiKit.Async.succeed, '_reference': MochiKit.Base.methodcaller('reference'), '_searchableContent': MochiKit.Base.methodcaller('searchableContent'), 'label': MochiKit.Base.methodcaller('label'), 'favicon': MochiKit.Base.methodcaller('favicon') }; deferredResult = new Clipperz.Async.Deferred('MainController.allCardInfo', {trace:false}); deferredResult.addMethod(this.user(), 'getRecords'); deferredResult.addCallback(MochiKit.Base.map, Clipperz.Async.collectResults("CardList.value - collectResults", cardInfo, {trace:false})); deferredResult.addCallback(Clipperz.Async.collectAll); deferredResult.callback(); return deferredResult; }, filterCards: function (someCardInfo) { var filter; var filterRegExp; var result; filter = this.filter().replace(/[^A-Za-z0-9]/g, "\\$&"); filterRegExp = new RegExp(filter, "i"); result = MochiKit.Base.filter(function (aCardInfo) { return filterRegExp.test(aCardInfo['_searchableContent'])}, someCardInfo); return result; }, sortCards: function (someCardInfo) { return someCardInfo.sort(Clipperz.Base.caseInsensitiveKeyComparator('label')); }, showRecordList: function () { var deferredResult; deferredResult = new Clipperz.Async.Deferred('MainController.showRecordList', {trace:false}); deferredResult.addMethod(this, 'allCardInfo'); deferredResult.addMethod(this, 'filterCards'); deferredResult.addMethod(this, 'sortCards'); deferredResult.addCallback(MochiKit.Base.bind(function (someRecordInfo) { this.pages()['cardListPage'].setProps({cardList: someRecordInfo}); }, this)); deferredResult.callback(); return deferredResult; }, filter: function () { return this._filter; }, setFilter: function (aValue) { this._filter = aValue; }, searchCards: function (someParameters) { //console.log("SEARCH CARDS", someParameters); this.setFilter(someParameters); this.showRecordList(); }, //========================================================================= runApplication: function () { MochiKit.Signal.connect(window, 'onpopstate', MochiKit.Base.method(this, 'historyGoBack')); +/// TODO: remove this TEST HACK this.moveInPage(this.currentPage(), 'cardListPage'); return this.showRecordList(); + +// this.moveInPage(this.currentPage(), 'preferencePage'); }, showRecord: function (aRecordReference) { //console.log("Show Record", aRecordReference); var deferredResult; this.pages()['cardListPage'].setProps({selectedCard:aRecordReference}); deferredResult = new Clipperz.Async.Deferred('MainController.runApplication', {trace:false}); -// deferredResult.addMethod(this.user(), 'getRecord', aRecordReference['_reference']); deferredResult.addMethod(this.user(), 'getRecord', aRecordReference); deferredResult.addMethodcaller('content'); deferredResult.addCallback(MochiKit.Base.bind(function (aCard) { //console.log("CARD DETAILS", aCard); this.pages()['cardDetailPage'].setProps({card: aCard}); this.pages()['cardListPage'].setProps({selectedCard: null}); }, this)); deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'cardDetailPage', true); deferredResult.callback(); return deferredResult; }, runDirectLogin: function (someParameters) { -console.log("RUN DIRECT LOGIN", someParameters); +//console.log("RUN DIRECT LOGIN", someParameters); var deferredResult; -// this.pages()['cardListPage'].setProps({selectedCard:aRecordReference}); deferredResult = new Clipperz.Async.Deferred('MainController.runDirectLogin', {trace:false}); -// deferredResult.addMethod(this.user(), 'getRecord', aRecordReference['_reference']); deferredResult.addMethod(this.user(), 'getRecord', someParameters['record']); deferredResult.addMethodcaller('directLoginWithReference', someParameters['directLogin']); deferredResult.addCallback(Clipperz.PM.UI.DirectLoginRunner.openDirectLogin); deferredResult.callback(); return deferredResult; }, shouldExitApp: function (anEvent) { -console.log("SHOULD EXIT APP"); +//console.log("SHOULD EXIT APP"); anEvent.preventDefault(); anEvent.stopPropagation(); }, //========================================================================= + showPreferences: function (anEvent) { + var deferredResult; + + this.pages()['preferencePage'].setProps({}); + deferredResult = new Clipperz.Async.Deferred('MainController.showPreferences', {trace:false}); + deferredResult.addMethod(this, 'moveInPage', this.currentPage(), 'preferencePage', true); + deferredResult.callback(); + + return deferredResult; + }, + + //========================================================================= + genericErrorHandler: function (anEvent, anError) { var errorMessage; var result; result = anError; errorMessage = "login failed"; if (anError['isPermanent'] === true) { this.pages()['errorPage'].setProps({message:anError.message}); this.moveInPage(this.currentPage(), 'errorPage'); errorMessage = "failure"; } else { if ('pin' in anEvent) { errorCount = Clipperz.PM.PIN.recordFailedAttempt(); if (errorCount == -1) { errorMessage = "PIN resetted"; } } } this.overlay().failed(errorMessage, 1); return result; }, //========================================================================= slidePage: function (fromPage, toPage, direction) { var fromPosition; var toPosition; if (direction == "LEFT") { fromPosition = 'right'; toPosition = 'left' } else { fromPosition = 'left'; toPosition = 'right' } MochiKit.DOM.addElementClass(fromPage, toPosition + ' transition'); MochiKit.DOM.addElementClass(toPage, fromPosition); MochiKit.DOM.removeElementClass(toPage, toPosition); MochiKit.DOM.addElementClass(toPage, 'transition'); MochiKit.Async.callLater(0.1, function () { MochiKit.DOM.removeElementClass(toPage, fromPosition); }) MochiKit.Async.callLater(0.5, function () { MochiKit.DOM.removeElementClass(fromPage, 'transition'); MochiKit.DOM.removeElementClass(toPage, 'transition'); }) }, rotateInPage: function (fromPage, toPage) { // Broken! :( MochiKit.DOM.addElementClass(MochiKit.DOM.getElement('mainDiv'), 'show-right'); }, //......................................................................... goBack: function () { var fromPage; var toPage; fromPage = this.pageStack().shift(); toPage = this.currentPage(); this.pages()[toPage].setProps({}); this.moveOutPage(fromPage, toPage); }, historyGoBack: function (anEvent) { anEvent.preventDefault(); anEvent.stopPropagation(); this.goBack(); }, currentPage: function () { return this.pageStack()[0]; }, setCurrentPage: function (aPage) { this.pageStack().unshift(aPage); }, moveInPage: function (fromPage, toPage, addToHistory) { var shouldAddItemToHistory; shouldAddItemToHistory = typeof(addToHistory) == 'undefined' ? false : addToHistory; this.slidePage(MochiKit.DOM.getElement(fromPage), MochiKit.DOM.getElement(toPage), 'LEFT'); this.setCurrentPage(toPage); if (shouldAddItemToHistory) { //console.log("ADD ITEM TO HISTORY"); //console.log("ADD ITEM TO HISTORY - window", window); //console.log("ADD ITEM TO HISTORY - window.history", window.history); window.history.pushState({'fromPage': fromPage, 'toPage': toPage}); //# window.history.pushState(); //console.log("ADDED ITEM TO HISTORY"); } else { //console.log("Skip HISTORY"); } }, moveOutPage: function (fromPage, toPage) { this.slidePage(MochiKit.DOM.getElement(fromPage), MochiKit.DOM.getElement(toPage), 'RIGHT'); this.setCurrentPage(toPage); }, //========================================================================= + + synchronizeLocalData: function (anEvent) { + var deferredResult; + + deferredResult = new Clipperz.Async.Deferred('MainController.synchronizeLocalData', {trace:true}); +// deferredResult.addMethod(this.proxy(), 'message', 'downloadAccountData', {}); + deferredResult.addMethod(this.user().connection(), 'message', 'downloadAccountData', {}); + deferredResult.addCallback(function (aResult) { + Clipperz.PM.DataModel.devicePreferences.setAccountDataWityResponse(aResult); +// localStorage.setItem('clipperz_dump_data', aResult['data']); +// localStorage.setItem('clipperz_dump_version', aResult['version']); +// localStorage.setItem('clipperz_dump_date', new Date()); + }) + deferredResult.callback(); + + return deferredResult; + }, + + //========================================================================= /* wrongAppVersion: function (anError) { // this.pages()['errorPage'].setProps({message:anError.message}); // this.moveInPage('errorPage', this.currentPage()); }, */ //========================================================================= __syntaxFix__: "syntax fix" }); |