1
0
mirror of https://github.com/moparisthebest/mail synced 2024-11-26 19:02:20 -05:00

Merge pull request #52 from whiteout-io/dev/css

Dev/css
This commit is contained in:
Tankred Hase 2014-04-24 17:34:37 +02:00
commit 5fef73ab99
50 changed files with 2009 additions and 400 deletions

View File

@ -12,8 +12,15 @@
<script src="require-config.js"></script> <script src="require-config.js"></script>
<script src="js/app.js"></script> <script src="js/app.js"></script>
</head> </head>
<body key-shortcuts> <body key-shortcuts>
<div ng-view class="main-app-view"></div> <div ng-view class="main-app-view"></div>
<!-- error dialog lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
</div><!--/.lightbox-overlay-->
</body> </body>
</html> </html>

View File

@ -20,6 +20,13 @@
</head> </head>
<body> <body>
<div ng-view class="main-app-view ios-spacer"></div> <div ng-view class="main-app-view ios-spacer"></div>
<!-- error dialog lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
</div><!--/.lightbox-overlay-->
</body> </body>
</html> </html>

View File

@ -19,9 +19,11 @@ requirejs([
'js/controller/write', 'js/controller/write',
'js/controller/navigation', 'js/controller/navigation',
'cryptoLib/util', 'cryptoLib/util',
'js/util/error',
'angularSanitize', 'angularSanitize',
'angularRoute', 'angularRoute',
'angularTouch' 'angularTouch',
'angularAnimate'
], function( ], function(
angular, angular,
DialogCtrl, DialogCtrl,
@ -39,7 +41,8 @@ requirejs([
ReadCtrl, ReadCtrl,
WriteCtrl, WriteCtrl,
NavigationCtrl, NavigationCtrl,
util util,
errorUtil
) { ) {
'use strict'; 'use strict';
@ -51,6 +54,7 @@ requirejs([
'ngSanitize', 'ngSanitize',
'ngRoute', 'ngRoute',
'ngTouch', 'ngTouch',
'ngAnimate',
'navigation', 'navigation',
'mail-list', 'mail-list',
'write', 'write',
@ -91,6 +95,13 @@ requirejs([
}); });
}); });
app.run(function($rootScope) {
// global state... inherited to all child scopes
$rootScope.state = {};
// attach global error handler
errorUtil.attachHandler($rootScope);
});
// inject controllers from ng-included view templates // inject controllers from ng-included view templates
app.controller('ReadCtrl', ReadCtrl); app.controller('ReadCtrl', ReadCtrl);
app.controller('WriteCtrl', WriteCtrl); app.controller('WriteCtrl', WriteCtrl);

View File

@ -10,9 +10,8 @@ define(function(require) {
var AboutCtrl = function($scope) { var AboutCtrl = function($scope) {
$scope.state.about = { $scope.state.about = {
open: false,
toggle: function(to) { toggle: function(to) {
this.open = to; $scope.state.lightbox = (to) ? 'about' : undefined;
} }
}; };

View File

@ -15,9 +15,8 @@ define(function(require) {
pgp = appController._crypto; pgp = appController._crypto;
$scope.state.account = { $scope.state.account = {
open: false,
toggle: function(to) { toggle: function(to) {
this.open = to; $scope.state.lightbox = (to) ? 'account' : undefined;
} }
}; };

View File

@ -1,15 +1,9 @@
define(function(require) { define(function(require) {
'use strict'; 'use strict';
var appController = require('js/app-controller'), var appController = require('js/app-controller');
errorUtil = require('js/util/error');
var AddAccountCtrl = function($scope, $location) { var AddAccountCtrl = function($scope, $location) {
// global state... inherited to all child scopes
$scope.$root.state = {};
// attach global error handler
errorUtil.attachHandler($scope);
$scope.connectToGoogle = function() { $scope.connectToGoogle = function() {
appController._auth.getCredentials({}, function(err) { appController._auth.getCredentials({}, function(err) {
if (err) { if (err) {

View File

@ -15,9 +15,9 @@ define(function(require) {
pgp = appController._crypto; pgp = appController._crypto;
$scope.state.contacts = { $scope.state.contacts = {
open: false,
toggle: function(to) { toggle: function(to) {
this.open = to; $scope.state.lightbox = (to) ? 'contacts' : undefined;
$scope.listKeys(); $scope.listKeys();
} }
}; };

View File

@ -1,15 +1,9 @@
define(function(require) { define(function(require) {
'use strict'; 'use strict';
var appController = require('js/app-controller'), var appController = require('js/app-controller');
errorUtil = require('js/util/error');
var LoginExistingCtrl = function($scope, $location) { var LoginExistingCtrl = function($scope, $location) {
// global state... inherited to all child scopes
$scope.$root.state = {};
// attach global error handler
errorUtil.attachHandler($scope);
var emailDao = appController._emailDao; var emailDao = appController._emailDao;
$scope.buttonEnabled = true; $scope.buttonEnabled = true;

View File

@ -1,18 +1,12 @@
define(function(require) { define(function(require) {
'use strict'; 'use strict';
var appController = require('js/app-controller'), var appController = require('js/app-controller');
errorUtil = require('js/util/error');
var LoginInitialCtrl = function($scope, $location) { var LoginInitialCtrl = function($scope, $location) {
var emailDao = appController._emailDao, var emailDao = appController._emailDao,
states, termsMsg = 'You must accept the Terms of Service to continue.'; states, termsMsg = 'You must accept the Terms of Service to continue.';
// global state... inherited to all child scopes
$scope.$root.state = {};
// attach global error handler
errorUtil.attachHandler($scope);
states = { states = {
IDLE: 1, IDLE: 1,
PROCESSING: 2, PROCESSING: 2,

View File

@ -2,15 +2,9 @@ define(function(require) {
'use strict'; 'use strict';
var angular = require('angular'), var angular = require('angular'),
errorUtil = require('js/util/error'),
appController = require('js/app-controller'); appController = require('js/app-controller');
var LoginExistingCtrl = function($scope, $location) { var LoginExistingCtrl = function($scope, $location) {
// global state... inherited to all child scopes
$scope.$root.state = {};
// attach global error handler
errorUtil.attachHandler($scope);
var emailDao = appController._emailDao, var emailDao = appController._emailDao,
pgp = appController._crypto; pgp = appController._crypto;

View File

@ -1,15 +1,9 @@
define(function(require) { define(function(require) {
'use strict'; 'use strict';
var appController = require('js/app-controller'), var appController = require('js/app-controller');
errorUtil = require('js/util/error');
var LoginCtrl = function($scope, $location) { var LoginCtrl = function($scope, $location) {
// global state... inherited to all child scopes
$scope.$root.state = {};
// attach global error handler
errorUtil.attachHandler($scope);
// check for app update // check for app update
appController.checkForUpdate(); appController.checkForUpdate();

View File

@ -44,7 +44,7 @@ define(function(require) {
} }
// display fetched body // display fetched body
$scope.$apply(); $scope.$digest();
// automatically decrypt if it's the selected email // automatically decrypt if it's the selected email
if (email === $scope.state.mailList.selected) { if (email === $scope.state.mailList.selected) {
@ -164,7 +164,7 @@ define(function(require) {
var index = getFolder().messages.indexOf(email); var index = getFolder().messages.indexOf(email);
// show the next mail // show the next mail
if (getFolder().messages.length > 1) { if (getFolder().messages.length > 1) {
// if we're about to delete the last entry of the array, show the previous (i.e. the one below in the list), // if we're about to delete the last entry of the array, show the previous (i.e. the one below in the list),
// otherwise show the next one (i.e. the one above in the list) // otherwise show the next one (i.e. the one above in the list)
$scope.select(_.last(getFolder().messages) === email ? getFolder().messages[index - 1] : getFolder().messages[index + 1]); $scope.select(_.last(getFolder().messages) === email ? getFolder().messages[index - 1] : getFolder().messages[index + 1]);
} else { } else {
@ -209,7 +209,6 @@ define(function(require) {
$timeout(function() { $timeout(function() {
// display and select first // display and select first
selectFirstMessage(); selectFirstMessage();
$scope.$apply();
}); });
$scope.synchronize(); $scope.synchronize();
@ -289,6 +288,8 @@ define(function(require) {
}]; // sender address }]; // sender address
this.to = [{ this.to = [{
address: 'max.musterman@gmail.com' address: 'max.musterman@gmail.com'
}, {
address: 'max.musterman@gmail.com'
}]; // list of receivers }]; // list of receivers
this.cc = [{ this.cc = [{
address: 'john.doe@gmail.com' address: 'john.doe@gmail.com'
@ -399,11 +400,13 @@ define(function(require) {
// //
var ngModule = angular.module('mail-list', []); var ngModule = angular.module('mail-list', []);
ngModule.directive('ngIscroll', function() {
ngModule.directive('ngIscroll', function($timeout) {
return { return {
link: function(scope, elm, attrs) { link: function(scope, elm, attrs) {
var model = attrs.ngIscroll, var model = attrs.ngIscroll,
listEl = elm[0]; listEl = elm[0],
myScroll;
/* /*
* iterates over the mails in the mail list and loads their bodies if they are visible in the viewport * iterates over the mails in the mail list and loads their bodies if they are visible in the viewport
@ -418,7 +421,7 @@ define(function(require) {
isPartiallyVisibleTop, isPartiallyVisibleBottom, isVisible; isPartiallyVisibleTop, isPartiallyVisibleBottom, isVisible;
for (var i = 0, len = listItems.length; i < len; i++) { for (var i = 0, len = listItems.length; i < len; i++) {
// the n-th list item (the dom representation of an email) corresponds to // the n-th list item (the dom representation of an email) corresponds to
// the n-th message model in the filteredMessages array // the n-th message model in the filteredMessages array
listItem = listItems.item(i).getBoundingClientRect(); listItem = listItems.item(i).getBoundingClientRect();
message = scope.filteredMessages[i]; message = scope.filteredMessages[i];
@ -439,18 +442,22 @@ define(function(require) {
} }
}; };
// re-init iScroll when model length changes // activate iscroll
scope.$watch(model, function() { myScroll = new IScroll(listEl, {
var myScroll; mouseWheel: true,
// activate iscroll scrollbars: true,
myScroll = new IScroll(listEl, { fadeScrollbars: true
mouseWheel: true });
}); myScroll.on('scrollEnd', scope.loadVisibleBodies);
// refresh iScroll when model length changes
scope.$watchCollection(model, function() {
$timeout(function() {
myScroll.refresh();
});
// load the visible message bodies, when the list is re-initialized and when scrolling stopped // load the visible message bodies, when the list is re-initialized and when scrolling stopped
scope.loadVisibleBodies(); scope.loadVisibleBodies();
myScroll.on('scrollEnd', scope.loadVisibleBodies); });
}, true);
} }
}; };
}); });

View File

@ -4,7 +4,6 @@ define(function(require) {
var angular = require('angular'), var angular = require('angular'),
str = require('js/app-config').string, str = require('js/app-config').string,
appController = require('js/app-controller'), appController = require('js/app-controller'),
errorUtil = require('js/util/error'),
notification = require('js/util/notification'), notification = require('js/util/notification'),
_ = require('underscore'), _ = require('underscore'),
emailDao, outboxBo; emailDao, outboxBo;
@ -14,11 +13,6 @@ define(function(require) {
// //
var NavigationCtrl = function($scope) { var NavigationCtrl = function($scope) {
// global state... inherited to all child scopes
$scope.$root.state = {};
// attach global error handler
errorUtil.attachHandler($scope);
emailDao = appController._emailDao; emailDao = appController._emailDao;
outboxBo = appController._outboxBo; outboxBo = appController._outboxBo;
@ -149,51 +143,46 @@ define(function(require) {
var modifier = e.ctrlKey || e.metaKey; var modifier = e.ctrlKey || e.metaKey;
if (modifier && e.keyCode === 78 && scope.state.writer && !scope.state.writer.open) { if (modifier && e.keyCode === 78 && scope.state.lightbox !== 'write') {
// n -> new mail // n -> new mail
e.preventDefault(); e.preventDefault();
scope.state.writer.write(); scope.state.writer.write();
scope.$apply();
} else if (modifier && e.keyCode === 70 && !scope.state.writer.open) { } else if (modifier && e.keyCode === 70 && scope.state.lightbox !== 'write') {
// f -> find // f -> find
e.preventDefault(); e.preventDefault();
scope.state.mailList.searching = true; scope.state.mailList.searching = true;
$timeout(function() { $timeout(function() {
scope.state.mailList.searching = false; scope.state.mailList.searching = false;
}, 200); }, 200);
scope.$apply();
} else if (modifier && e.keyCode === 82 && scope.state.writer && !scope.state.writer.open && scope.state.mailList.selected) { } else if (modifier && e.keyCode === 82 && scope.state.lightbox !== 'write' && scope.state.mailList.selected) {
// r -> reply // r -> reply
e.preventDefault(); e.preventDefault();
scope.state.writer.write(scope.state.mailList.selected); scope.state.writer.write(scope.state.mailList.selected);
scope.$apply();
} else if (modifier && e.keyCode === 83 && scope.state.writer && !scope.state.writer.open && scope.state.mailList.synchronize) { } else if (modifier && e.keyCode === 83 && scope.state.lightbox !== 'write' && scope.state.mailList.synchronize) {
// s -> sync folder // s -> sync folder
e.preventDefault(); e.preventDefault();
scope.state.mailList.synchronize(); scope.state.mailList.synchronize();
scope.$apply();
} else if (e.keyCode === 27 && scope.state.writer.open) { } else if (e.keyCode === 27 && scope.state.lightbox !== undefined) {
// escape -> close writer // escape -> close current lightbox
e.preventDefault(); e.preventDefault();
scope.state.writer.close(); scope.state.lightbox = undefined;
scope.$apply();
} else if (e.keyCode === 27 && scope.state.account.open) {
// escape -> close account view
e.preventDefault();
scope.state.account.toggle(false);
} else if (e.keyCode === 27 && scope.state.contacts.open) {
// escape -> close contacts view
e.preventDefault();
scope.state.contacts.toggle(false);
} else if (e.keyCode === 27 && scope.state.nav.open) { } else if (e.keyCode === 27 && scope.state.nav.open) {
// escape -> close nav view // escape -> close nav view
e.preventDefault(); e.preventDefault();
scope.state.nav.toggle(false); scope.state.nav.toggle(false);
scope.$apply();
} }
scope.$apply();
}); });
}; };
}); });

View File

@ -13,9 +13,8 @@ define(function(require) {
pgp = appController._crypto; pgp = appController._crypto;
$scope.state.setPassphrase = { $scope.state.setPassphrase = {
open: false,
toggle: function(to) { toggle: function(to) {
this.open = to; $scope.state.lightbox = (to) ? 'set-passphrase' : undefined;
$scope.newPassphrase = undefined; $scope.newPassphrase = undefined;
$scope.oldPassphrase = undefined; $scope.oldPassphrase = undefined;

View File

@ -25,9 +25,8 @@ define(function(require) {
// //
$scope.state.writer = { $scope.state.writer = {
open: false,
write: function(replyTo, replyAll, forward) { write: function(replyTo, replyAll, forward) {
this.open = true; $scope.state.lightbox = 'write';
$scope.replyTo = replyTo; $scope.replyTo = replyTo;
resetFields(); resetFields();
@ -39,7 +38,7 @@ define(function(require) {
$scope.verify($scope.to[0]); $scope.verify($scope.to[0]);
}, },
close: function() { close: function() {
this.open = false; $scope.state.lightbox = undefined;
} }
}; };
@ -48,9 +47,11 @@ define(function(require) {
$scope.to = [{ $scope.to = [{
address: '' address: ''
}]; }];
$scope.showCC = false;
$scope.cc = [{ $scope.cc = [{
address: '' address: ''
}]; }];
$scope.showBCC = false;
$scope.bcc = [{ $scope.bcc = [{
address: '' address: ''
}]; }];
@ -85,6 +86,7 @@ define(function(require) {
$scope.cc.unshift({ $scope.cc.unshift({
address: recipient.address address: recipient.address
}); });
$scope.showCC = true;
}); });
$scope.cc.forEach($scope.verify); $scope.cc.forEach($scope.verify);
} }
@ -172,7 +174,7 @@ define(function(require) {
} }
$scope.checkSendStatus(); $scope.checkSendStatus();
$scope.$apply(); $scope.$digest();
}); });
}; };
@ -332,10 +334,9 @@ define(function(require) {
link: function(scope, elm, attrs, ctrl) { link: function(scope, elm, attrs, ctrl) {
// view -> model // view -> model
elm.on('keyup keydown', function() { elm.on('keyup keydown', function() {
scope.$apply(function() { // set model
// set model ctrl.$setViewValue(elm[0].innerText);
ctrl.$setViewValue(elm[0].innerText); scope.$digest();
});
}); });
// model -> view // model -> view
@ -404,6 +405,11 @@ define(function(require) {
scope.$apply(); scope.$apply();
} }
function removeInput(field, index, scope) {
field.splice(index, 1);
scope.$apply();
}
function checkForEmptyInput(field) { function checkForEmptyInput(field) {
var emptyFieldExists = false; var emptyFieldExists = false;
field.forEach(function(recipient) { field.forEach(function(recipient) {
@ -415,6 +421,18 @@ define(function(require) {
return emptyFieldExists; return emptyFieldExists;
} }
function cleanupEmptyInputs(field, scope) {
var i;
for (i = field.length - 2; i >= 0; i--) {
if (!field[i].address) {
field.splice(i, 1);
}
}
scope.$apply();
}
ngModule.directive('field', function() { ngModule.directive('field', function() {
return { return {
//scope: true, // optionally create a child scope //scope: true, // optionally create a child scope
@ -455,12 +473,14 @@ define(function(require) {
// create new field input // create new field input
addInput(field, scope); addInput(field, scope);
} }
cleanupEmptyInputs(field, scope);
}); });
element.on('keydown', function(e) { element.on('keydown', function(e) {
var code = e.keyCode; var code = e.keyCode;
scope.$apply(); scope.$digest();
if (code === 32 || code === 188 || code === 186) { if (code === 32 || code === 188 || code === 186) {
// catch space, comma, semicolon // catch space, comma, semicolon
@ -476,8 +496,7 @@ define(function(require) {
// backspace, delete on empty input // backspace, delete on empty input
// remove input // remove input
e.preventDefault(); e.preventDefault();
field.splice(index, 1); removeInput(field, index, scope);
scope.$apply();
// focus on previous id // focus on previous id
var previousId = fieldName + (index - 1); var previousId = fieldName + (index - 1);
document.getElementById(previousId).focus(); document.getElementById(previousId).focus();
@ -503,7 +522,7 @@ define(function(require) {
mimeType: file.type, mimeType: file.type,
content: new Uint8Array(e.target.result) content: new Uint8Array(e.target.result)
}); });
scope.$apply(); scope.$digest();
}; };
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
} }

View File

@ -4,7 +4,7 @@ define(function() {
var er = {}; var er = {};
er.attachHandler = function(scope) { er.attachHandler = function(scope) {
scope.$root.onError = function(options) { scope.onError = function(options) {
if (!options) { if (!options) {
scope.$apply(); scope.$apply();
return; return;

27
src/lib/angular/angular-animate.min.js vendored Executable file
View File

@ -0,0 +1,27 @@
/*
AngularJS v1.2.13
(c) 2010-2014 Google, Inc. http://angularjs.org
License: MIT
*/
(function(z,f,T){'use strict';f.module("ngAnimate",["ng"]).factory("$$animateReflow",["$window","$timeout","$document",function(f,h,d){var n=f.requestAnimationFrame||f.webkitRequestAnimationFrame||function(d){return h(d,10,!1)},w=f.cancelAnimationFrame||f.webkitCancelAnimationFrame||function(d){return h.cancel(d)};return function(d){var f=n(function(){d()});return function(){w(f)}}}]).factory("$$asyncQueueBuffer",["$timeout",function(f){var h,d=[];return function(n){f.cancel(h);d.push(n);h=f(function(){for(var f=
0;f<d.length;f++)d[f]();d=[]},0,!1)}}]).config(["$provide","$animateProvider",function($,h){function d(d){for(var f=0;f<d.length;f++){var l=d[f];if(l.nodeType==da)return l}}function n(l){return f.element(d(l))}var w=f.noop,D=f.forEach,ia=h.$$selectors,da=1,l="$$ngAnimateState",U="ng-animate",s={running:!0};$.decorator("$animate",["$delegate","$injector","$sniffer","$rootElement","$$asyncQueueBuffer","$rootScope","$document",function(x,z,ca,F,J,B,T){function V(a){if(a){var c=[],e={};a=a.substr(1).split(".");
(ca.transitions||ca.animations)&&a.push("");for(var A=0;A<a.length;A++){var d=a[A],f=ia[d];f&&!e[d]&&(c.push(z.get(f)),e[d]=!0)}return c}}function t(a,c,e,d,k,C,s){function t(b){var g=e.data(l);b=b||!g||!g.active[c]||m&&g.active[c].event!=a;K();!0===b?G():(g.active[c].done=G,n(L,"after",G))}function n(b,g,ja){"after"==g?H():x();var fa=g+"End";D(b,function(d,f){var A=function(){a:{var a=g+"Complete",c=b[f];c[a]=!0;(c[fa]||w)();for(c=0;c<b.length;c++)if(!b[c][a])break a;ja()}};"before"!=g||"enter"!=
a&&"move"!=a?d[g]?d[fa]=F?d[g](e,E,z,A):m?d[g](e,c,A):d[g](e,A):A():A()})}function h(b){var g="$animate:"+b;u&&(u[g]&&0<u[g].length)&&J(function(){e.triggerHandler(g,{event:a,className:c})})}function x(){h("before")}function H(){h("after")}function B(){h("close");s&&J(function(){s()})}function K(){K.hasBeenRun||(K.hasBeenRun=!0,C())}function G(){if(!G.hasBeenRun){G.hasBeenRun=!0;var b=e.data(l);b&&(m?M(e,c):(J(function(){var b=e.data(l)||{};Q==b.index&&M(e,c,a)}),e.data(l,b)));B()}}var E,z,F="setClass"==
a;F&&(E=c[0],z=c[1],c=E+" "+z);var v,y=e[0];y&&(v=y.className,v=v+" "+c);if(y&&W(v)){var u=f.element._data(y),u=u&&u.events,y=(" "+v).replace(/\s+/g,".");d||(d=k?k.parent():e.parent());var q=V(y),m="addClass"==a||"removeClass"==a||F,I=e.data(l)||{};k=I.active||{};y=I.totalActive||0;v=I.last;if(R(e,d)||0===q.length)K(),x(),H(),G();else{var L=[];m&&(I.disabled||v&&!v.classBased)||D(q,function(b){if(!b.allowCancel||b.allowCancel(e,a,c)){var g=b[a];"leave"==a?(b=g,g=null):b=b["before"+a.charAt(0).toUpperCase()+
a.substr(1)];L.push({before:b,after:g})}});if(0===L.length)K(),x(),H(),B();else{d=!1;if(0<y){q=[];if(m)"setClass"==v.event?(q.push(v),M(e,c)):k[c]&&(N=k[c],N.event==a?d=!0:(q.push(N),M(e,c)));else if("leave"==a&&k["ng-leave"])d=!0;else{for(var N in k)q.push(k[N]),M(e,N);k={};y=0}0<q.length&&f.forEach(q,function(b){(b.done||w)(!0);X(b.animations)})}!m||(F||d)||(d="addClass"==a==e.hasClass(c));if(d)x(),H(),B();else{e.addClass(U);var Q=S++;v={classBased:m,event:a,animations:L,done:t};y++;k[c]=v;e.data(l,
{last:v,active:k,index:Q,totalActive:y});n(L,"before",t)}}}}else K(),x(),H(),B()}function Y(a){a=d(a);D(a.querySelectorAll("."+U),function(a){a=f.element(a);(a=a.data(l))&&a.active&&f.forEach(a.active,function(a){(a.done||w)(!0);X(a.animations)})})}function X(a){D(a,function(a){a.beforeComplete||(a.beforeEnd||w)(!0);a.afterComplete||(a.afterEnd||w)(!0)})}function M(a,c){if(d(a)==d(F))s.disabled||(s.running=!1,s.structural=!1);else if(c){var e=a.data(l)||{},f=!0===c;!f&&(e.active&&e.active[c])&&(e.totalActive--,
delete e.active[c]);if(f||!e.totalActive)a.removeClass(U),a.removeData(l)}}function R(a,c){if(s.disabled)return!0;if(d(a)==d(F))return s.disabled||s.running;do{if(0===c.length)break;var e=d(c)==d(F),f=e?s:c.data(l),f=f&&(!!f.disabled||f.running||0<f.totalActive);if(e||f)return f;if(e)break}while(c=c.parent());return!0}var S=0;F.data(l,s);B.$$postDigest(function(){B.$$postDigest(function(){s.running=!1})});var Z=h.classNameFilter(),W=Z?function(a){return Z.test(a)}:function(){return!0};return{enter:function(a,
c,e,d){this.enabled(!1,a);x.enter(a,c,e);B.$$postDigest(function(){a=n(a);t("enter","ng-enter",a,c,e,w,d)})},leave:function(a,c){Y(a);this.enabled(!1,a);B.$$postDigest(function(){a=n(a);t("leave","ng-leave",a,null,null,function(){x.leave(a)},c)})},move:function(a,c,e,d){Y(a);this.enabled(!1,a);x.move(a,c,e);B.$$postDigest(function(){a=n(a);t("move","ng-move",a,c,e,w,d)})},addClass:function(a,c,d){a=n(a);t("addClass",c,a,null,null,function(){x.addClass(a,c)},d)},removeClass:function(a,c,d){a=n(a);
t("removeClass",c,a,null,null,function(){x.removeClass(a,c)},d)},setClass:function(a,c,d,f){a=n(a);t("setClass",[c,d],a,null,null,function(){x.setClass(a,c,d)},f)},enabled:function(a,c){switch(arguments.length){case 2:if(a)M(c);else{var d=c.data(l)||{};d.disabled=!0;c.data(l,d)}break;case 1:s.disabled=!a;break;default:a=!s.disabled}return!!a}}}]);h.register("",["$window","$sniffer","$timeout","$$animateReflow",function(l,s,h,n){function J(b,g){I&&I();m.push(g);I=n(function(){D(m,function(b){b()});
m=[];I=null;u={}})}function B(b,g){var a=Date.now()+1E3*g;if(!(a<=N)){h.cancel(L);var c=d(b);b=f.element(c);Q.push(b);N=a;L=h(function(){U(Q);Q=[]},g,!1)}}function U(b){D(b,function(b){(b=b.data(E))&&(b.closeAnimationFn||w)()})}function V(b,g){var a=g?u[g]:null;if(!a){var c=0,d=0,f=0,e=0,k,p,r,h;D(b,function(b){if(b.nodeType==da){b=l.getComputedStyle(b)||{};r=b[O+ea];c=Math.max(t(r),c);h=b[O+H];k=b[O+ha];d=Math.max(t(k),d);p=b[P+ha];e=Math.max(t(p),e);var g=t(b[P+ea]);0<g&&(g*=parseInt(b[P+K],10)||
1);f=Math.max(g,f)}});a={total:0,transitionPropertyStyle:h,transitionDurationStyle:r,transitionDelayStyle:k,transitionDelay:d,transitionDuration:c,animationDelayStyle:p,animationDelay:e,animationDuration:f};g&&(u[g]=a)}return a}function t(b){var g=0;b=f.isString(b)?b.split(/\s*,\s*/):[];D(b,function(b){g=Math.max(parseFloat(b)||0,g)});return g}function Y(b){var g=b.parent(),a=g.data(G);a||(g.data(G,++q),a=q);return a+"-"+d(b).className}function X(b,g,a,c){var e=Y(g),k=e+" "+a,l=u[k]?++u[k].total:
0,h={};if(0<l){var p=a+"-stagger",h=e+" "+p;(e=!u[h])&&g.addClass(p);h=V(g,h);e&&g.removeClass(p)}c=c||function(b){return b()};g.addClass(a);var p=g.data(E)||{},r=c(function(){return V(g,k)});c=r.transitionDuration;e=r.animationDuration;if(0===c&&0===e)return g.removeClass(a),!1;g.data(E,{running:p.running||0,itemIndex:l,stagger:h,timings:r,closeAnimationFn:f.noop});b=0<p.running||"setClass"==b;0<c&&M(g,a,b);0<e&&(d(g).style[P]="none 0s");return!0}function M(b,a,c){"ng-enter"!=a&&("ng-move"!=a&&"ng-leave"!=
a)&&c?b.addClass(ga):d(b).style[O+H]="none"}function R(b,a){var c=O+H,e=d(b);e.style[c]&&0<e.style[c].length&&(e.style[c]="");b.removeClass(ga)}function S(b){var a=P;b=d(b);b.style[a]&&0<b.style[a].length&&(b.style[a]="")}function Z(b,a,c,e){function f(b){a.off(w,k);a.removeClass(l);A(a,c);b=d(a);for(var e in q)b.style.removeProperty(q[e])}function k(b){b.stopPropagation();var a=b.originalEvent||b;b=a.$manualTimeStamp||a.timeStamp||Date.now();a=parseFloat(a.elapsedTime.toFixed($));Math.max(b-x,0)>=
u&&a>=s&&e()}var h=d(a);b=a.data(E);if(-1!=h.className.indexOf(c)&&b){var l="";D(c.split(" "),function(b,a){l+=(0<a?" ":"")+b+"-active"});var p=b.stagger,r=b.timings,n=b.itemIndex,s=Math.max(r.transitionDuration,r.animationDuration),t=Math.max(r.transitionDelay,r.animationDelay),u=t*y,x=Date.now(),w=ba+" "+aa,m="",q=[];if(0<r.transitionDuration){var z=r.transitionPropertyStyle;-1==z.indexOf("all")&&(m+=C+"transition-property: "+z+";",m+=C+"transition-duration: "+r.transitionDurationStyle+";",q.push(C+
"transition-property"),q.push(C+"transition-duration"))}0<n&&(0<p.transitionDelay&&0===p.transitionDuration&&(m+=C+"transition-delay: "+W(r.transitionDelayStyle,p.transitionDelay,n)+"; ",q.push(C+"transition-delay")),0<p.animationDelay&&0===p.animationDuration&&(m+=C+"animation-delay: "+W(r.animationDelayStyle,p.animationDelay,n)+"; ",q.push(C+"animation-delay")));0<q.length&&(r=h.getAttribute("style")||"",h.setAttribute("style",r+" "+m));a.on(w,k);a.addClass(l);b.closeAnimationFn=function(){f();
e()};h=(n*(Math.max(p.animationDelay,p.transitionDelay)||0)+(t+s)*v)*y;b.running++;B(a,h);return f}e()}function W(b,a,c){var d="";D(b.split(","),function(b,e){d+=(0<e?",":"")+(c*a+parseInt(b,10))+"s"});return d}function a(b,a,c,d){if(X(b,a,c,d))return function(b){b&&A(a,c)}}function c(b,a,c,d){if(a.data(E))return Z(b,a,c,d);A(a,c);d()}function e(b,g,d,e){var f=a(b,g,d);if(f){var h=f;J(g,function(){R(g,d);S(g);h=c(b,g,d,e)});return function(b){(h||w)(b)}}e()}function A(b,a){b.removeClass(a);var c=
b.data(E);c&&(c.running&&c.running--,c.running&&0!==c.running||b.removeData(E))}function k(b,a){var c="";b=f.isArray(b)?b:b.split(/\s+/);D(b,function(b,d){b&&0<b.length&&(c+=(0<d?" ":"")+b+a)});return c}var C="",O,aa,P,ba;z.ontransitionend===T&&z.onwebkittransitionend!==T?(C="-webkit-",O="WebkitTransition",aa="webkitTransitionEnd transitionend"):(O="transition",aa="transitionend");z.onanimationend===T&&z.onwebkitanimationend!==T?(C="-webkit-",P="WebkitAnimation",ba="webkitAnimationEnd animationend"):
(P="animation",ba="animationend");var ea="Duration",H="Property",ha="Delay",K="IterationCount",G="$$ngAnimateKey",E="$$ngAnimateCSS3Data",ga="ng-animate-block-transitions",$=3,v=1.5,y=1E3,u={},q=0,m=[],I,L=null,N=0,Q=[];return{enter:function(b,a){return e("enter",b,"ng-enter",a)},leave:function(b,a){return e("leave",b,"ng-leave",a)},move:function(a,c){return e("move",a,"ng-move",c)},beforeSetClass:function(b,c,d,e){var f=k(d,"-remove")+" "+k(c,"-add"),h=a("setClass",b,f,function(a){var e=b.attr("class");
b.removeClass(d);b.addClass(c);a=a();b.attr("class",e);return a});if(h)return J(b,function(){R(b,f);S(b);e()}),h;e()},beforeAddClass:function(b,c,d){var e=a("addClass",b,k(c,"-add"),function(a){b.addClass(c);a=a();b.removeClass(c);return a});if(e)return J(b,function(){R(b,c);S(b);d()}),e;d()},setClass:function(a,d,e,f){e=k(e,"-remove");d=k(d,"-add");return c("setClass",a,e+" "+d,f)},addClass:function(a,d,e){return c("addClass",a,k(d,"-add"),e)},beforeRemoveClass:function(b,c,d){var e=a("removeClass",
b,k(c,"-remove"),function(a){var d=b.attr("class");b.removeClass(c);a=a();b.attr("class",d);return a});if(e)return J(b,function(){R(b,c);S(b);d()}),e;d()},removeClass:function(a,d,e){return c("removeClass",a,k(d,"-remove"),e)}}}])}])})(window,window.angular);
//# sourceMappingURL=angular-animate.min.js.map

View File

@ -16,6 +16,7 @@
angularRoute: 'angular/angular-route.min', angularRoute: 'angular/angular-route.min',
angularTouch: 'angular/angular-touch.min', angularTouch: 'angular/angular-touch.min',
angularSanitize: 'angular/angular-sanitize.min', angularSanitize: 'angular/angular-sanitize.min',
angularAnimate: 'angular/angular-animate.min',
uuid: 'uuid/uuid', uuid: 'uuid/uuid',
forge: 'forge/forge.min', forge: 'forge/forge.min',
punycode: 'punycode.min', punycode: 'punycode.min',
@ -41,6 +42,10 @@
exports: 'angular', exports: 'angular',
deps: ['angular'] deps: ['angular']
}, },
angularAnimate: {
exports: 'angular',
deps: ['angular']
},
iscroll: { iscroll: {
exports: 'IScroll' exports: 'IScroll'
}, },

View File

@ -4,6 +4,9 @@
*:before, *:before,
*:after { *:after {
box-sizing: border-box; box-sizing: border-box;
// remove flickering on item touch selection in ios
-webkit-tap-highlight-color: transparent !important;
} }
// Body reset // Body reset
@ -34,6 +37,24 @@ textarea {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
// Custom scrollbars in webkit
// @see http://css-tricks.com/custom-scrollbars-in-webkit/
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: $color-grey-lighter;
border: 3px solid transparent;
background-clip: content-box;
cursor: pointer !important;
&:hover {
background-color: $color-blue;
}
}
// add space at the top since ios7 apps are now fullscreen // add space at the top since ios7 apps are now fullscreen
.ios-spacer { .ios-spacer {
padding-top: 20px; padding-top: 20px;
@ -45,14 +66,4 @@ textarea {
// allow text selection // allow text selection
user-select: none; user-select: none;
// make scrollbars invisible
::-webkit-scrollbar {
width: 0px;
}
// remove flickering on item touch selection in ios
* {
-webkit-tap-highlight-color: transparent !important;
}
} }

View File

@ -51,7 +51,6 @@ $lightbox-padding: 15px;
$lightbox-max-width: 662px; $lightbox-max-width: 662px;
$lightbox-width: 90%; $lightbox-width: 90%;
$lightbox-min-height: 644px; $lightbox-min-height: 644px;
$lightbox-top: 7.5%;
$btn-back-color: $color-blue; $btn-back-color: $color-blue;
$btn-color: #fff; $btn-color: #fff;

View File

@ -3,6 +3,11 @@
@import "functions"; @import "functions";
@import "grid"; @import "grid";
// Third party libs
@import "lib/angular-csp"; // use angular csp specific classes
@import "lib/scut";
// Bootstrap
@import "normalize"; @import "normalize";
@import "variables"; // Modify this for custom colors, font-sizes, etc @import "variables"; // Modify this for custom colors, font-sizes, etc
@import "fonts"; @import "fonts";
@ -20,6 +25,8 @@
@import "components/layout"; @import "components/layout";
@import "components/popover"; @import "components/popover";
@import "components/input"; @import "components/input";
@import "components/mail-addresses";
@import "components/spinner";
// Views // Views
@import "views/shared"; @import "views/shared";
@ -33,4 +40,4 @@
@import "views/mail-list"; @import "views/mail-list";
@import "views/read"; @import "views/read";
@import "views/write"; @import "views/write";
@import "views/login"; @import "views/login";

View File

@ -46,6 +46,14 @@
} }
} }
.label-blank {
background-color: transparent;
color: $color-black;
text-align: left;
padding-left: 0;
padding-right: 0;
}
.label-light { .label-light {
background-color: $label-light-back-color; background-color: $label-light-back-color;
color: $label-light-color; color: $label-light-color;

View File

@ -5,22 +5,27 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
z-index: 2000; z-index: 2000;
visibility: hidden;
backface-visibility: hidden; backface-visibility: hidden;
@include respond-to(desktop) { @include respond-to(desktop) {
top: $lightbox-top;
margin: 0 auto; margin: 0 auto;
width: $lightbox-width; width: $lightbox-width;
max-width: $lightbox-max-width; max-width: $lightbox-max-width;
max-height: $lightbox-min-height;
height: auto; height: auto;
text-align: left;
} }
.lightbox-body { .lightbox-body {
position: relative;
height: 100%; height: 100%;
padding: $lightbox-padding; padding: $lightbox-padding;
background: #fff; background: #fff;
backface-visibility: hidden;
@include respond-to(desktop) {
max-height: $lightbox-min-height;
}
header { header {
text-align: center; text-align: center;
position: relative; position: relative;
@ -52,36 +57,54 @@
} }
} }
.lightbox-overlay.show .lightbox {
visibility: visible;
}
.lightbox-overlay { .lightbox-overlay {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
visibility: hidden; display: none;
top: 0; top: 0;
left: 0; left: 0;
z-index: 1000; z-index: 1000;
opacity: 0;
background: $color-grey-dark-alpha; background: $color-grey-dark-alpha;
transition: all 0.3s;
}
.lightbox-overlay.show { @include respond-to(desktop) {
opacity: 1; @include scut-vcenter-ib(".lightbox");
visibility: visible; text-align: center;
} }
// effect &.show {
.lightbox-effect .lightbox-body { display: block;
transform: scale(0.7); }
opacity: 0;
transition: all 0.3s;
}
.lightbox-overlay.show .lightbox-effect .lightbox-body { /* ngAnimate */
transform: scale(1); &.show-add {
opacity: 1; display: block;
opacity: 0;
transition: opacity 0.3s;
.lightbox-body {
transform: scale(0.7);
transition: transform 0.3s;
}
}
&.show-add-active {
opacity: 1;
.lightbox-body {
transform: scale(1);
}
}
&.show-remove {
display: block;
opacity: 1;
transition: opacity 0.3s;
.lightbox-body {
transform: scale(1);
transition: transform 0.3s;
}
}
&.show-remove-active {
opacity: 0;
.lightbox-body {
transform: scale(0.7);
}
}
} }

View File

@ -0,0 +1,17 @@
.mail-addresses {
p {
margin: 0.4em 0 0.2em;
cursor: text;
}
.label {
margin-bottom: 0.2em;
margin-right: 0.2em;
}
label {
display: inline-block;
width: 2.75em;
color: $color-grey;
}
}

View File

@ -19,6 +19,10 @@
cursor: pointer; cursor: pointer;
transition: background-color $time-li-fade, color $time-li-fade; transition: background-color $time-li-fade, color $time-li-fade;
&.ng-animate {
transition: none;
}
h3 { h3 {
@include text-overflow; @include text-overflow;

View File

@ -0,0 +1,22 @@
.spinner {
vertical-align: middle;
display: inline-block;
margin-right: 0.2em;
height: 1em;
width: 1em;
animation: spinner-rotation .6s linear infinite;
border-left: 0.17em solid $color-grey-light;
border-right: 0.17em solid $color-grey-light;
border-bottom: 0.17em solid $color-grey-light;
border-top: 0.17em solid $color-grey;
border-radius: 100%;
}
@keyframes spinner-rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}

18
src/sass/lib/_angular-csp.scss Executable file
View File

@ -0,0 +1,18 @@
/* Include this file in your html if you are using the CSP mode. */
@charset "UTF-8";
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak,
.ng-hide {
display: none !important;
}
ng\:form {
display: block;
}
.ng-animate-block-transitions {
transition:0s all!important;
-webkit-transition:0s all!important;
}

1485
src/sass/lib/_scut.scss Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,9 +15,13 @@
} }
.key-list { .key-list {
max-height: 400px; position: relative;
margin: 20px; margin: 20px;
overflow-y: scroll;
.key-list-scroll {
max-height: 400px;
overflow-y: scroll;
}
table { table {
th, td { th, td {
@ -37,6 +41,11 @@
outline: none; outline: none;
} }
} }
// pull popover upwards to keep popup inside lightbox
.popover {
margin-top: -20px;
}
} }
} }

View File

@ -3,7 +3,6 @@
@include respond-to(desktop) { @include respond-to(desktop) {
max-width: 350px; max-width: 350px;
top: 30%;
} }
p { p {

View File

@ -63,32 +63,14 @@
} }
} }
.spinner {
display:none;
}
&.syncing { &.syncing {
.spinner { .spinner {
top: 6.5px; display: inline-block;
left: $padding-horizontal;
height: 13px;
width: 13px;
position: absolute;
animation: rotation .6s linear infinite;
border-left: 2px solid $color-grey-light;
border-right: 2px solid $color-grey-light;
border-bottom: 2px solid $color-grey-light;
border-top: 2px solid $color-grey;
border-radius: 100%;
}
.text {
padding-left: 1.5em;
} }
} }
} }
}
@keyframes rotation {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
} }

View File

@ -1,19 +1,19 @@
.view-read { .view-read {
display: flex;
flex-direction: column;
margin: 0px; margin: 0px;
padding: 10px 15px; padding: 10px 15px;
height: 100%; height: 100%;
width: 100%;
color: $color-grey-dark; color: $color-grey-dark;
.headers { .headers {
flex-shrink: 0;
margin-bottom: 1em; margin-bottom: 1em;
p {
margin: 0px;
padding: 0px;
}
.subject { .subject {
font-size: $font-size-bigger; font-size: $font-size-bigger;
margin: 0;
} }
.date { .date {
@ -21,24 +21,31 @@
font-size: $font-size-small; font-size: $font-size-small;
margin-top: 0.25em; margin-top: 0.25em;
margin-bottom: 1.5em; margin-bottom: 1.5em;
padding: 0px;
} }
.address { .mail-addresses {
color: $color-grey;
padding: 0.2em 0;
.label { .label {
margin-left: 0.3em; cursor: pointer;
}
&:hover { p {
cursor: pointer; margin-top: 0.2em;
} margin-bottom: 0;
}
}
.controls {
float: right;
margin: 0 15px 10px;
button {
margin-left: 7px;
} }
} }
} }
.attachments { .attachments {
flex-shrink: 0;
position: relative; position: relative;
width: inherit; width: inherit;
border: 1px; border: 1px;
@ -72,54 +79,30 @@
} }
.seperator-line { .seperator-line {
flex-shrink: 0;
height: 1px; height: 1px;
color: $color-grey-lighter; color: $color-grey-lighter;
background-color: $color-grey-lighter; background-color: $color-grey-lighter;
} }
.body { .body {
flex-grow: 1;
position: relative;
margin-top: 1.75em; margin-top: 1.75em;
cursor: text; cursor: text;
padding-bottom: 250px;
line-height: 1.5em; line-height: 1.5em;
height: 100%;
overflow-y: scroll; overflow-y: scroll;
user-select: text; user-select: text;
.working { .working {
margin: 0 auto; @include scut-vcenter-tt;
height: 100%; width: 100%;
width: 230px; text-align: center;
display: table; font-size: 2em;
.container { strong {
display: table-cell; color: $color-grey-input;
vertical-align: middle; vertical-align: middle;
.spinner {
position: relative;
div {
position: absolute;
top: 0;
left: 0;
height: 30px;
width: 30px;
animation: rotation .6s linear infinite;
border-left: 5px solid $color-grey-light;
border-right: 5px solid $color-grey-light;
border-bottom: 5px solid $color-grey-light;
border-top: 5px solid $color-grey;
border-radius: 100%;
}
}
h1 {
margin: 0;
padding-left: 40px;
line-height: 30px;
color: $color-grey-input;
}
} }
} }
@ -137,6 +120,7 @@
} }
iframe { iframe {
flex-grow: 1;
width: 100%; width: 100%;
} }
@ -145,43 +129,39 @@
padding: 0; padding: 0;
} }
table { ul {
list-style: none;
margin: 0;
}
li {
border-bottom: 1px solid $color-grey-lighter;
&:last-child {
border-bottom: 0;
}
}
button {
display: block;
background: none;
width: 100%;
border: 0;
outline: 0;
padding: 0.5em 1em 0.5em 0.3em;
color: $color-blue; color: $color-blue;
user-select: none; transition: background-color 0.3s;
text-align: left;
tr { &:before {
&:hover, display: inline-block;
&:focus { width: 2.5em;
background-color: darken($color-white, 2%); text-align: center;
cursor: pointer; vertical-align: middle;
}
&.seperator {
border-bottom: 1px solid $color-grey-lighter;
}
} }
&:hover,
td { &:focus {
padding: 7px 5px; background-color: darken($color-white, 2%);
&.left {
padding-left: 15px;
padding-top: 10px;
}
&.right {
padding-right: 15px;
}
} }
} }
} }
}
.controls {
float: right;
margin: 10px 15px;
button {
margin-left: 7px;
}
} }

View File

@ -1,4 +1,6 @@
.view-write { .view-write {
display: flex;
flex-direction: column;
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
color: $color-grey-dark; color: $color-grey-dark;
@ -6,7 +8,8 @@
height: 100%; height: 100%;
@include respond-to(desktop) { @include respond-to(desktop) {
height: 600px; // this number depends on max-height set on .lightbox
height: 590px; // magic number
} }
input { input {
@ -18,26 +21,35 @@
border: 0!important; border: 0!important;
} }
.headers { .mail-addresses {
flex-shrink: 0;
margin-top: 10px; margin-top: 10px;
}
p { .mail-addresses-more {
margin: 0.2em 0; float: right;
padding: 0.2em 0; margin: 0.4em 0;
cursor: text;
}
span { button {
color: $color-grey; display: inline-block;
} background: none;
padding: 0 0.5em;
margin: 0;
text-decoration: none;
color: $color-black;
transition: color 0.3s;
outline: 0;
input { &:hover,
margin-left: 0.3em; &:focus {
width: 80%; color: $color-blue;
text-decoration: underline;
}
} }
} }
.subject-box { .subject-box {
flex-shrink: 0;
position: relative; position: relative;
margin: 20px 0 7px 0; margin: 20px 0 7px 0;
width: inherit; width: inherit;
@ -47,7 +59,6 @@
height: em(44); height: em(44);
.subject-line { .subject-line {
float: left;
padding: 10px; padding: 10px;
width: 80%; width: 80%;
color: $color-grey; color: $color-grey;
@ -82,6 +93,7 @@
} }
.attachments-box { .attachments-box {
flex-shrink: 0;
position: relative; position: relative;
margin: 0 0 5px 0; margin: 0 0 5px 0;
width: inherit; width: inherit;
@ -126,13 +138,10 @@
} }
.body { .body {
flex-grow: 1;
line-height: 1.5em; line-height: 1.5em;
cursor: text; cursor: text;
overflow-y: scroll;
@include respond-to(desktop) {
height: 445px;
overflow-y: scroll;
}
*[contentEditable] { *[contentEditable] {
outline: 0px; outline: 0px;

View File

@ -25,11 +25,6 @@
</div> </div>
<!-- lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
</div>
<!-- popovers --> <!-- popovers -->
<div id="google-info" class="popover right" ng-controller="PopoverCtrl"> <div id="google-info" class="popover right" ng-controller="PopoverCtrl">
<div class="arrow"></div> <div class="arrow"></div>

View File

@ -16,30 +16,34 @@
</div> </div>
<div class="key-list"> <div class="key-list">
<table> <div class="key-list-scroll">
<tr> <table>
<th>Key ID</th> <tr>
<th>Email</th> <th>Key ID</th>
<th>Created</th> <th>Email</th>
<th>Size</th> <th>Created</th>
</tr> <th>Size</th>
<tr ng-repeat="key in keys | orderBy:'userId' | filter:searchText"> </tr>
<td class="hover" ng-mouseover="getFingerprint(key)" popover="#fingerprint-contact">{{key._id.slice(8)}}</td> <tr ng-repeat="key in keys | orderBy:'userId' | filter:searchText">
<td>{{key.userId}}</td> <td class="hover" ng-mouseover="getFingerprint(key)" popover="#fingerprint-contact">{{key._id.slice(8)}}</td>
<td>{{key.created | date:'mediumDate'}}</td> <td>{{key.userId}}</td>
<td>{{key.bitSize}} bit</td> <td>{{key.created | date:'mediumDate'}}</td>
<td><button class="remove" ng-click="removeKey(key)">&#xe007;</button></td> <td>{{key.bitSize}} bit</td>
</tr> <td><button class="remove" ng-click="removeKey(key)">&#xe007;</button></td>
</table> </tr>
</div> </table>
</div><!--/.key-list-scroll-->
<!-- popovers -->
<div id="fingerprint-contact" class="popover right" ng-controller="PopoverCtrl">
<div class="popover-title"><b>Fingerprint</b></div>
<div class="popover-content">{{fingerprint}}</div>
</div><!--/.popover-->
</div><!--/.key-list-->
</div><!-- /.view-contacts --> </div><!-- /.view-contacts -->
</div><!-- /.content --> </div><!-- /.content -->
<!-- popovers -->
<div id="fingerprint-contact" class="popover right" ng-controller="PopoverCtrl">
<div class="popover-title"><b>Fingerprint</b></div>
<div class="popover-content">{{fingerprint}}</div>
</div><!--/.popover-->
</div><!-- /.lightbox-body --> </div><!-- /.lightbox-body -->

View File

@ -19,21 +19,22 @@
</div><!--/.nav-container--> </div><!--/.nav-container-->
<!-- lightbox --> <!-- lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.writer.open}"> <div class="lightbox-overlay" ng-class="{'show': state.lightbox === 'write'}">
<div class="lightbox lightbox-effect" ng-include="'tpl/write.html'"></div> <div class="lightbox" ng-include="'tpl/write.html'"></div>
</div> </div>
<div class="lightbox-overlay" ng-class="{'show': state.account.open}">
<div class="lightbox lightbox-effect" ng-include="'tpl/account.html'"></div> <div class="lightbox-overlay" ng-class="{'show': state.lightbox === 'account'}">
<div class="lightbox" ng-include="'tpl/account.html'"></div>
</div> </div>
<div class="lightbox-overlay" ng-class="{'show': state.setPassphrase.open}">
<div class="lightbox lightbox-effect" ng-include="'tpl/set-passphrase.html'"></div> <div class="lightbox-overlay" ng-class="{'show': state.lightbox === 'set-passphrase'}">
<div class="lightbox" ng-include="'tpl/set-passphrase.html'"></div>
</div> </div>
<div class="lightbox-overlay" ng-class="{'show': state.contacts.open}">
<div class="lightbox lightbox-effect" ng-include="'tpl/contacts.html'"></div> <div class="lightbox-overlay" ng-class="{'show': state.lightbox === 'contacts'}">
<div class="lightbox" ng-include="'tpl/contacts.html'"></div>
</div> </div>
<div class="lightbox-overlay" ng-class="{'show': state.about.open}">
<div class="lightbox lightbox-effect view-about" ng-include="'tpl/about.html'"></div> <div class="lightbox-overlay" ng-class="{'show': state.lightbox === 'about'}">
</div> <div class="lightbox view-about" ng-include="'tpl/about.html'"></div>
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
</div> </div>

View File

@ -18,11 +18,6 @@
</div><!--/content--> </div><!--/content-->
</div> </div>
<!-- lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
</div>
<!-- popovers --> <!-- popovers -->
<div id="passphrase-info" class="popover right" ng-controller="PopoverCtrl"> <div id="passphrase-info" class="popover right" ng-controller="PopoverCtrl">
<div class="arrow"></div> <div class="arrow"></div>

View File

@ -33,11 +33,6 @@
</div><!--/content--> </div><!--/content-->
</div> </div>
<!-- lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
</div>
<!-- popovers --> <!-- popovers -->
<div id="passphrase-info" class="popover right" ng-controller="PopoverCtrl"> <div id="passphrase-info" class="popover right" ng-controller="PopoverCtrl">
<div class="arrow"></div> <div class="arrow"></div>

View File

@ -5,7 +5,7 @@
<div class="content"> <div class="content">
<p><b>Import keyfile.</b> To access your emails on this device, please import your existing key file.</p> <p><b>Import keyfile.</b> To access your emails on this device, please import your existing key file.</p>
<form> <form>
<div> <div>
<input type="file" accept=".asc" file-reader tabindex="1"> <input type="file" accept=".asc" file-reader tabindex="1">
@ -20,11 +20,6 @@
</div> </div>
</div> </div>
<!-- lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
</div>
<!-- popovers --> <!-- popovers -->
<div id="keyfile-info" class="popover right" ng-controller="PopoverCtrl"> <div id="keyfile-info" class="popover right" ng-controller="PopoverCtrl">
<div class="arrow"></div> <div class="arrow"></div>

View File

@ -8,9 +8,4 @@
<p><b>Login.</b> Authenticating with the mail server...</p> <p><b>Login.</b> Authenticating with the mail server...</p>
</div><!--/content--> </div><!--/content-->
</div>
<!-- lightbox -->
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
</div> </div>

View File

@ -8,7 +8,7 @@
<input class="input-text input-search" type="search" results="5" ng-model="searchText" placeholder=" Filter..." focus-me="state.mailList.searching"> <input class="input-text input-search" type="search" results="5" ng-model="searchText" placeholder=" Filter..." focus-me="state.mailList.searching">
</div> </div>
<div class="list-wrapper" ng-iscroll="filteredMessages.length"> <div class="list-wrapper" ng-iscroll="filteredMessages">
<ul class="mail-list"> <ul class="mail-list">
<li ng-class="{'mail-list-active': email === state.mailList.selected, 'mail-list-attachment': email.attachments !== undefined && email.attachments.length > 0, 'mail-list-unread': email.unread, 'mail-list-replied': !email.unread && email.answered}" ng-click="select(email)" ng-repeat="email in (filteredMessages = (state.nav.currentFolder.messages | filter:searchText | orderBy:'uid':true | limitTo:100))"> <li ng-class="{'mail-list-active': email === state.mailList.selected, 'mail-list-attachment': email.attachments !== undefined && email.attachments.length > 0, 'mail-list-unread': email.unread, 'mail-list-replied': !email.unread && email.answered}" ng-click="select(email)" ng-repeat="email in (filteredMessages = (state.nav.currentFolder.messages | filter:searchText | orderBy:'uid':true | limitTo:100))">
<h3>{{email.from[0].name || email.from[0].address}}</h3> <h3>{{email.from[0].name || email.from[0].address}}</h3>

View File

@ -1,38 +1,43 @@
<div class="controls">
<button ng-click="state.mailList.remove(state.mailList.selected)" class="btn-icon" title="Delete mail">&#xe005;</button>
<button class="btn-icon" title="Reply to" reply-selection>&#xe002;</button>
<button ng-click="state.writer.write()" class="btn-icon" title="New mail">&#xe006;</button>
</div><!--/.controls-->
<div class="view-read" ng-controller="ReadCtrl"> <div class="view-read" ng-controller="ReadCtrl">
<div class="headers"> <div class="headers">
<div class="controls">
<button ng-click="state.mailList.remove(state.mailList.selected)" class="btn-icon" title="Delete mail">&#xe005;</button>
<button class="btn-icon" title="Reply to" reply-selection>&#xe002;</button>
<button ng-click="state.writer.write()" class="btn-icon" title="New mail">&#xe006;</button>
</div><!--/.controls-->
<p class="subject" ng-click="state.read.toggle(false)">{{(state.mailList.selected.subject) ? state.mailList.selected.subject : 'No subject'}}</p> <p class="subject" ng-click="state.read.toggle(false)">{{(state.mailList.selected.subject) ? state.mailList.selected.subject : 'No subject'}}</p>
<p class="date">{{state.mailList.selected.sentDate | date:'EEEE, MMM d, yyyy h:mm a'}}</p> <p class="date">{{state.mailList.selected.sentDate | date:'EEEE, MMM d, yyyy h:mm a'}}</p>
<p class="address"> <div class="mail-addresses">
From: <span ng-repeat="u in state.mailList.selected.from" class="label" ng-class="{'label-primary': u.secure === false, 'label-primary-click': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span> <p>
</p> <label>From:</label>
<p class="address"> <span ng-repeat="u in state.mailList.selected.from">
To: <span ng-repeat="u in state.mailList.selected.to" class="label" ng-class="{'label-primary': u.secure === false, 'label-primary-click': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span> <span class="label" ng-class="{'label-primary': u.secure === false, 'label-primary-click': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
</p> </span>
<div ng-switch="state.mailList.selected.cc && state.mailList.selected.cc.length > 0">
<p class="address" ng-switch-when="true">
Cc: <span ng-repeat="u in state.mailList.selected.cc" class="label" ng-class="{'label-primary': u.secure === false, 'label-primary-click': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
</p> </p>
</div> <p>
<label>To:</label>
<span ng-repeat="u in state.mailList.selected.to">
<span class="label" ng-class="{'label-primary': u.secure === false, 'label-primary-click': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
</span>
</p>
<p ng-show="state.mailList.selected.cc && state.mailList.selected.cc.length > 0">
<label>Cc:</label>
<span ng-repeat="u in state.mailList.selected.cc">
<span class="label" ng-class="{'label-primary': u.secure === false, 'label-primary-click': u.secure === false}" data-icon-append="{{(u.secure === false) ? '&#xe001;' : ''}}" ng-mouseover="getKeyId(u.address)" ng-click="invite(u)" popover="#fingerprint-info">{{u.name || u.address}}</span>
</span>
</p>
</div><!--/.mail-addresses-->
</div><!--/.headers--> </div><!--/.headers-->
<div ng-switch="state.mailList.selected.attachments !== undefined && state.mailList.selected.attachments.length > 0"> <div ng-switch="state.mailList.selected.attachments !== undefined && state.mailList.selected.attachments.length > 0">
<div ng-switch-when="true"> <div ng-switch-when="true" class="attachments">
<div class="attachments"> <span class="attachment" ng-repeat="attachment in state.mailList.selected.attachments" ng-click="download(attachment)">
<span class="attachment" ng-repeat="attachment in state.mailList.selected.attachments" ng-click="download(attachment)"> <span data-icon="&#xe003;"></span>
<span data-icon="&#xe003;"></span> {{attachment.filename}}
{{attachment.filename}} </span><!--/.attachment-->
</span><!--/.attachment--> </div><!--/.attachments-->
</div><!--/.attachments--> <div ng-switch-default class="seperator-line"></div>
</div>
<div ng-switch-default>
<div class="seperator-line"></div>
</div>
</div><!--/.ng-switch--> </div><!--/.ng-switch-->
<div class="body" ng-switch="state.mailList.selected === undefined || (state.mailList.selected.encrypted === false && state.mailList.selected.body !== undefined) || (state.mailList.selected.encrypted === true && state.mailList.selected.decrypted === true)"> <div class="body" ng-switch="state.mailList.selected === undefined || (state.mailList.selected.encrypted === false && state.mailList.selected.body !== undefined) || (state.mailList.selected.encrypted === true && state.mailList.selected.decrypted === true)">
@ -41,17 +46,12 @@
<div class="line" ng-repeat="line in state.mailList.selected.body.split('\n') track by $index" ng-class="{'empty-line': lineEmpty(line)}"> <div class="line" ng-repeat="line in state.mailList.selected.body.split('\n') track by $index" ng-class="{'empty-line': lineEmpty(line)}">
<span ng-bind-html="line | createAnchors"></span> <span ng-bind-html="line | createAnchors"></span>
<br> <br>
</div> </div><!--/.line-->
</div> </div><!--/ng-switch-when-->
<div class="working" ng-switch-default> <div class="working" ng-switch-default>
<div class="container"> <span class="spinner"></span>
<div class="spinner"><div></div></div> <strong ng-bind="(state.mailList.selected.loadingBody === true || state.mailList.selected.body === undefined || state.mailList.selected.body === null) ? 'Loading...' : 'Decrypting...'"></strong>
<span ng-switch="state.mailList.selected.loadingBody === true || state.mailList.selected.body === undefined || state.mailList.selected.body === null"> </div><!--/.working-->
<h1 ng-switch-when="true">Loading...</h1>
<h1 ng-switch-default>Decrypting...</h1>
</span>
</div><!--/.container-->
</div>
</div><!--/.body--> </div><!--/.body-->
<!-- popovers --> <!-- popovers -->
@ -62,21 +62,10 @@
<div class="reply-selection popover bottom"> <div class="reply-selection popover bottom">
<div class="arrow"></div> <div class="arrow"></div>
<div class="popover-content"> <ul class="popover-content">
<table> <li><button data-icon="&#xe014;" ng-click="state.writer.write(state.mailList.selected)">Reply</button></li>
<tr class="seperator" ng-click="state.writer.write(state.mailList.selected)"> <li><button data-icon="&#xe013;" ng-click="state.writer.write(state.mailList.selected, true)">Reply All</button></li>
<td class="left" data-icon="&#xe014;"></td> <li><button data-icon="&#xe015;" ng-click="state.writer.write(state.mailList.selected, null, true)">Forward</button></li>
<td class="right">Reply</td> </ul>
</tr>
<tr class="seperator" ng-click="state.writer.write(state.mailList.selected, true)">
<td class="left" data-icon="&#xe013;"></td>
<td class="right">Reply All</td>
</tr>
<tr ng-click="state.writer.write(state.mailList.selected, null, true)">
<td class="left" data-icon="&#xe015;"></td>
<td class="right">Forward</td>
</tr>
</table>
</div>
</div><!--/.reply-selection--> </div><!--/.reply-selection-->
</div><!--/.view-read--> </div><!--/.view-read-->

View File

@ -6,20 +6,30 @@
<div class="content"> <div class="content">
<div class="view-write"> <div class="view-write">
<div class="headers"> <div class="mail-addresses">
<div class="mail-addresses-more">
<button ng-click="showCC = true;" ng-hide="showCC">Cc</button>
<button ng-click="showBCC = true;" ng-hide="showBCC">Bcc</button>
</div>
<p field="to"> <p field="to">
<span>To:</span> <label>To:</label>
<span ng-repeat="recipient in to track by $index"> <span ng-repeat="recipient in to track by $index">
<input id="to{{$index}}" value="{{recipient.address}}" ng-model="recipient.address" ng-trim="false" ng-class="{'label': recipient.secure === true, 'label label-primary': recipient.secure === false && recipient.valid !== true}" auto-size="recipient.address" spellcheck="false" ng-change="onAddressUpdate(to, $index)" address-input="to" tabindex="1" ng-mouseover="getKeyId(recipient)" focus-me="state.writer.open && writerTitle !== 'Reply'"> <input id="to{{$index}}" value="{{recipient.address}}" ng-model="recipient.address" ng-trim="false" class="label" ng-class="{'label-blank': !recipient.address || recipient.secure === undefined, 'label-primary': recipient.secure === false}" auto-size="recipient.address" spellcheck="false" ng-change="onAddressUpdate(to, $index)" address-input="to" tabindex="1" ng-mouseover="getKeyId(recipient)" focus-me="state.lightbox === 'write' && writerTitle !== 'Reply'">
</span> </span>
</p> </p>
<p field="cc"> <p field="cc" ng-show="showCC === true">
<span>Cc:</span> <label>Cc:</label>
<span ng-repeat="recipient in cc track by $index"> <span ng-repeat="recipient in cc track by $index">
<input id="cc{{$index}}" value="{{recipient.address}}" ng-model="recipient.address" ng-trim="false" ng-class="{'label': recipient.secure === true, 'label label-primary': recipient.secure === false && recipient.valid !== true}" auto-size="recipient.address" spellcheck="false" ng-change="onAddressUpdate(cc, $index)" address-input="cc" tabindex="1" ng-mouseover="getKeyId(recipient)"> <input id="cc{{$index}}" value="{{recipient.address}}" ng-model="recipient.address" ng-trim="false" class="label" ng-class="{'label-blank': !recipient.address || recipient.secure === undefined, 'label-primary': recipient.secure === false}" auto-size="recipient.address" spellcheck="false" ng-change="onAddressUpdate(cc, $index)" address-input="cc" tabindex="1" ng-mouseover="getKeyId(recipient)">
</span> </span>
</p> </p>
</div><!--/.address-headers--> <p field="bcc" ng-show="showBCC === true">
<label>Bcc:</label>
<span ng-repeat="recipient in bcc track by $index">
<input id="bcc{{$index}}" value="{{recipient.address}}" ng-model="recipient.address" ng-trim="false" class="label" ng-class="{'label-blank': !recipient.address || recipient.secure === undefined, 'label-primary': recipient.secure === false}" auto-size="recipient.address" spellcheck="false" ng-change="onAddressUpdate(bcc, $index)" address-input="bcc" tabindex="1" ng-mouseover="getKeyId(recipient)">
</span>
</p>
</div><!--/.mail-addresses-->
<div class="subject-box"> <div class="subject-box">
<div class="subject-line"> <div class="subject-line">
@ -44,9 +54,9 @@
</div><!--/ng-switch--> </div><!--/ng-switch-->
<div class="body" focus-child> <div class="body" focus-child>
<p ng-model="body" contentEditable="true" spellcheck="false" ng-change="updatePreview()" tabindex="3" focus-me="state.writer.open && writerTitle === 'Reply'"></p> <p ng-model="body" contentEditable="true" spellcheck="false" ng-change="updatePreview()" tabindex="3" focus-me="state.lightbox === 'write' && writerTitle === 'Reply'"></p>
<div class="encrypt-preview" ng-class="{'invisible': !ciphertextPreview || !sendBtnSecure}"> <div class="encrypt-preview" ng-show="ciphertextPreview && sendBtnSecure">
<p>-----BEGIN ENCRYPTED PREVIEW-----<br>{{ciphertextPreview}}<br>-----END ENCRYPTED PREVIEW-----</p> <p>-----BEGIN ENCRYPTED PREVIEW-----<br>{{ciphertextPreview}}<br>-----END ENCRYPTED PREVIEW-----</p>
</div><!--/.encrypt-preview--> </div><!--/.encrypt-preview-->
</div><!--/.body--> </div><!--/.body-->

View File

@ -79,7 +79,7 @@ define(function(require) {
})).yields(); })).yields();
scope.onError = function(err) { scope.onError = function(err) {
expect(err.title).to.equal('Success'); expect(err.title).to.equal('Success');
expect(scope.state.account.open).to.be.false; expect(scope.state.lightbox).to.equal(undefined);
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
expect(dl.createDownload.calledOnce).to.be.true; expect(dl.createDownload.calledOnce).to.be.true;
dl.createDownload.restore(); dl.createDownload.restore();

View File

@ -40,7 +40,6 @@ define(function(require) {
describe('scope variables', function() { describe('scope variables', function() {
it('should be set correctly', function() { it('should be set correctly', function() {
expect(scope.fingerprint).to.equal('XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX'); expect(scope.fingerprint).to.equal('XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX');
expect(scope.state.contacts.open).to.be.false;
expect(scope.state.contacts.toggle).to.exist; expect(scope.state.contacts.toggle).to.exist;
}); });
}); });

View File

@ -97,10 +97,13 @@ define(function(require) {
scope.passphrase = passphrase; scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd')); keychainMock.getUserKeyPair.withArgs(emailAddress).yields(new Error('asd'));
scope.confirmPassphrase(); scope.onError = function(err) {
expect(err.message).to.equal('asd');
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
done();
};
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; scope.confirmPassphrase();
done();
}); });
}); });
}); });

View File

@ -176,10 +176,14 @@ define(function(require) {
expect(state).to.equal(1); expect(state).to.equal(1);
expect(emailDaoMock.unlock.calledOnce).to.be.true; expect(emailDaoMock.unlock.calledOnce).to.be.true;
scope.setState.restore(); scope.setState.restore();
done();
} }
}); });
scope.onError = function(err) {
expect(err.message).to.equal('asd');
done();
};
scope.confirmPassphrase(); scope.confirmPassphrase();
}); });
}); });

View File

@ -98,7 +98,7 @@ define(function(require) {
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
}); });
it('should not work when keypair upload fails', function() { it('should not work when keypair upload fails', function(done) {
scope.passphrase = passphrase; scope.passphrase = passphrase;
scope.key = { scope.key = {
privateKeyArmored: 'b' privateKeyArmored: 'b'
@ -113,6 +113,11 @@ define(function(require) {
errMsg: 'yo mamma.' errMsg: 'yo mamma.'
}); });
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase(); scope.confirmPassphrase();
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;
@ -120,7 +125,7 @@ define(function(require) {
expect(keychainMock.putUserKeyPair.calledOnce).to.be.true; expect(keychainMock.putUserKeyPair.calledOnce).to.be.true;
}); });
it('should not work when unlock fails', function() { it('should not work when unlock fails', function(done) {
scope.passphrase = passphrase; scope.passphrase = passphrase;
scope.key = { scope.key = {
privateKeyArmored: 'b' privateKeyArmored: 'b'
@ -134,6 +139,11 @@ define(function(require) {
errMsg: 'yo mamma.' errMsg: 'yo mamma.'
}); });
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase(); scope.confirmPassphrase();
expect(scope.incorrect).to.be.true; expect(scope.incorrect).to.be.true;
@ -141,13 +151,18 @@ define(function(require) {
expect(emailDaoMock.unlock.calledOnce).to.be.true; expect(emailDaoMock.unlock.calledOnce).to.be.true;
}); });
it('should not work when keypair retrieval', function() { it('should not work when keypair retrieval', function(done) {
scope.passphrase = passphrase; scope.passphrase = passphrase;
keychainMock.getUserKeyPair.withArgs(emailAddress).yields({ keychainMock.getUserKeyPair.withArgs(emailAddress).yields({
errMsg: 'yo mamma.' errMsg: 'yo mamma.'
}); });
scope.onError = function(err) {
expect(err.errMsg).to.equal('yo mamma.');
done();
};
scope.confirmPassphrase(); scope.confirmPassphrase();
expect(keychainMock.getUserKeyPair.calledOnce).to.be.true; expect(keychainMock.getUserKeyPair.calledOnce).to.be.true;

View File

@ -63,10 +63,8 @@ define(function(require) {
describe('initial state', function() { describe('initial state', function() {
it('should be well defined', function() { it('should be well defined', function() {
expect(scope.state).to.exist; expect(scope.state).to.exist;
expect(scope.state.nav.open).to.be.false; expect(scope.state.lightbox).to.be.undefined;
expect(scope.account.folders).to.not.be.empty; expect(scope.account.folders).to.not.be.empty;
expect(scope.onError).to.exist;
expect(scope.openFolder).to.exist; expect(scope.openFolder).to.exist;
}); });
}); });

View File

@ -55,7 +55,7 @@ define(function(require) {
describe('scope variables', function() { describe('scope variables', function() {
it('should be set correctly', function() { it('should be set correctly', function() {
expect(scope.state.writer).to.exist; expect(scope.state.writer).to.exist;
expect(scope.state.writer.open).to.be.false; expect(scope.state.lightbox).to.be.undefined;
expect(scope.state.writer.write).to.exist; expect(scope.state.writer.write).to.exist;
expect(scope.state.writer.close).to.exist; expect(scope.state.writer.close).to.exist;
expect(scope.verify).to.exist; expect(scope.verify).to.exist;
@ -68,11 +68,11 @@ define(function(require) {
describe('close', function() { describe('close', function() {
it('should close the writer', function() { it('should close the writer', function() {
scope.state.writer.open = true; scope.state.lightbox = 'write';
scope.state.writer.close(); scope.state.writer.close();
expect(scope.state.writer.open).to.be.false; expect(scope.state.lightbox).to.be.undefined;
}); });
}); });
@ -200,7 +200,7 @@ define(function(require) {
keychainMock.getReceiverPublicKey.yields(null, { keychainMock.getReceiverPublicKey.yields(null, {
userId: 'asdf@example.com' userId: 'asdf@example.com'
}); });
scope.$apply = function() { scope.$digest = function() {
expect(recipient.key).to.deep.equal({ expect(recipient.key).to.deep.equal({
userId: 'asdf@example.com' userId: 'asdf@example.com'
}); });
@ -311,8 +311,7 @@ define(function(require) {
expect(outboxMock.put.calledOnce).to.be.true; expect(outboxMock.put.calledOnce).to.be.true;
expect(emailDaoMock.sync.calledOnce).to.be.true; expect(emailDaoMock.sync.calledOnce).to.be.true;
expect(scope.state.lightbox).to.be.undefined;
expect(scope.state.writer.open).to.be.false;
expect(scope.replyTo.answered).to.be.true; expect(scope.replyTo.answered).to.be.true;
}); });
}); });