2013-09-13 08:11:47 -04:00
|
|
|
define(function(require) {
|
2013-09-11 16:11:26 -04:00
|
|
|
'use strict';
|
|
|
|
|
2013-09-13 08:11:47 -04:00
|
|
|
var angular = require('angular'),
|
2014-05-07 14:19:20 -04:00
|
|
|
_ = require('underscore'),
|
2013-09-15 11:05:37 -04:00
|
|
|
appController = require('js/app-controller'),
|
2013-09-13 08:11:47 -04:00
|
|
|
aes = require('cryptoLib/aes-cbc'),
|
|
|
|
util = require('cryptoLib/util'),
|
2014-01-15 04:57:28 -05:00
|
|
|
str = require('js/app-config').string,
|
2014-02-24 04:14:07 -05:00
|
|
|
crypto, emailDao, outbox;
|
2013-09-13 08:11:47 -04:00
|
|
|
|
|
|
|
//
|
|
|
|
// Controller
|
|
|
|
//
|
2013-09-12 11:22:17 -04:00
|
|
|
|
2013-10-18 21:32:00 -04:00
|
|
|
var WriteCtrl = function($scope, $filter) {
|
2014-01-19 10:58:51 -05:00
|
|
|
crypto = appController._crypto;
|
2014-02-24 04:14:07 -05:00
|
|
|
emailDao = appController._emailDao,
|
|
|
|
outbox = appController._outboxBo;
|
2013-09-13 08:11:47 -04:00
|
|
|
|
2014-01-19 10:58:51 -05:00
|
|
|
// set default value so that the popover height is correct on init
|
2014-02-20 13:20:24 -05:00
|
|
|
$scope.keyId = 'XXXXXXXX';
|
2014-01-19 10:58:51 -05:00
|
|
|
|
2013-10-12 13:39:09 -04:00
|
|
|
//
|
|
|
|
// Init
|
|
|
|
//
|
|
|
|
|
2013-11-08 15:55:08 -05:00
|
|
|
$scope.state.writer = {
|
2014-04-02 13:47:50 -04:00
|
|
|
write: function(replyTo, replyAll, forward) {
|
2014-04-23 09:24:48 -04:00
|
|
|
$scope.state.lightbox = 'write';
|
2013-11-13 11:05:21 -05:00
|
|
|
$scope.replyTo = replyTo;
|
2013-11-08 15:55:08 -05:00
|
|
|
|
|
|
|
resetFields();
|
2013-11-13 11:05:21 -05:00
|
|
|
|
|
|
|
// fill fields depending on replyTo
|
2014-04-02 13:47:50 -04:00
|
|
|
fillFields(replyTo, replyAll, forward);
|
2013-11-13 11:05:21 -05:00
|
|
|
$scope.updatePreview();
|
|
|
|
|
2014-01-14 10:11:59 -05:00
|
|
|
$scope.verify($scope.to[0]);
|
2013-11-08 15:55:08 -05:00
|
|
|
},
|
|
|
|
close: function() {
|
2014-04-23 09:24:48 -04:00
|
|
|
$scope.state.lightbox = undefined;
|
2013-10-18 21:32:00 -04:00
|
|
|
}
|
2013-11-08 15:55:08 -05:00
|
|
|
};
|
2013-10-18 21:32:00 -04:00
|
|
|
|
|
|
|
function resetFields() {
|
|
|
|
$scope.writerTitle = 'New email';
|
2014-01-10 15:35:34 -05:00
|
|
|
$scope.to = [{
|
|
|
|
address: ''
|
|
|
|
}];
|
2014-04-22 10:41:07 -04:00
|
|
|
$scope.showCC = false;
|
2014-01-13 10:42:10 -05:00
|
|
|
$scope.cc = [{
|
|
|
|
address: ''
|
|
|
|
}];
|
2014-04-22 10:41:07 -04:00
|
|
|
$scope.showBCC = false;
|
2014-01-13 10:42:10 -05:00
|
|
|
$scope.bcc = [{
|
|
|
|
address: ''
|
|
|
|
}];
|
2013-10-21 09:02:54 -04:00
|
|
|
$scope.subject = '';
|
|
|
|
$scope.body = '';
|
|
|
|
$scope.ciphertextPreview = '';
|
2014-02-04 15:04:48 -05:00
|
|
|
$scope.attachments = [];
|
2013-10-18 21:32:00 -04:00
|
|
|
}
|
2013-10-12 13:39:09 -04:00
|
|
|
|
2014-04-02 13:47:50 -04:00
|
|
|
function fillFields(re, replyAll, forward) {
|
|
|
|
var from, sentDate, body;
|
2013-10-13 07:49:37 -04:00
|
|
|
|
2013-10-12 13:39:09 -04:00
|
|
|
if (!re) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-02 13:47:50 -04:00
|
|
|
$scope.writerTitle = (forward) ? 'Forward' : 'Reply';
|
|
|
|
|
2013-10-12 13:39:09 -04:00
|
|
|
// fill recipient field
|
2014-04-02 13:47:50 -04:00
|
|
|
if (!forward) {
|
|
|
|
$scope.to.unshift({
|
|
|
|
address: re.from[0].address
|
|
|
|
});
|
|
|
|
$scope.to.forEach($scope.verify);
|
|
|
|
}
|
|
|
|
if (replyAll) {
|
|
|
|
re.to.concat(re.cc).forEach(function(recipient) {
|
2014-05-07 14:19:20 -04:00
|
|
|
var me = emailDao._account.emailAddress;
|
|
|
|
if (recipient.address === me && re.from[0].address !== me) {
|
2014-04-02 13:47:50 -04:00
|
|
|
// don't reply to yourself
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$scope.cc.unshift({
|
|
|
|
address: recipient.address
|
|
|
|
});
|
|
|
|
});
|
2014-05-07 14:19:20 -04:00
|
|
|
|
|
|
|
// filter duplicates
|
|
|
|
$scope.cc = _.uniq($scope.cc, function(recipient) {
|
|
|
|
return recipient.address;
|
|
|
|
});
|
|
|
|
$scope.showCC = true;
|
2014-04-02 13:47:50 -04:00
|
|
|
$scope.cc.forEach($scope.verify);
|
|
|
|
}
|
|
|
|
|
2014-05-14 11:29:27 -04:00
|
|
|
// fill attachments on forward
|
|
|
|
if (forward) {
|
|
|
|
// create a new array, otherwise removing an attachment will also
|
|
|
|
// remove it from the original in the mail list as a side effect
|
|
|
|
$scope.attachments = [].concat(re.attachments);
|
|
|
|
}
|
|
|
|
|
2013-10-12 13:39:09 -04:00
|
|
|
// fill subject
|
2014-04-02 13:47:50 -04:00
|
|
|
if (forward) {
|
|
|
|
$scope.subject = 'Fwd: ' + re.subject;
|
|
|
|
} else {
|
|
|
|
$scope.subject = 'Re: ' + ((re.subject) ? re.subject.replace('Re: ', '') : '');
|
|
|
|
}
|
2013-10-12 13:39:09 -04:00
|
|
|
|
|
|
|
// fill text body
|
2013-10-13 07:49:37 -04:00
|
|
|
from = re.from[0].name || re.from[0].address;
|
2014-04-02 13:47:50 -04:00
|
|
|
sentDate = $filter('date')(re.sentDate, 'EEEE, MMM d, yyyy h:mm a');
|
|
|
|
|
|
|
|
function createString(array) {
|
|
|
|
var str = '';
|
|
|
|
array.forEach(function(to) {
|
|
|
|
str += (str) ? ', ' : '';
|
|
|
|
str += ((to.name) ? to.name : to.address) + ' <' + to.address + '>';
|
|
|
|
});
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (forward) {
|
|
|
|
body = '\n\n' +
|
|
|
|
'---------- Forwarded message ----------\n' +
|
|
|
|
'From: ' + re.from[0].name + ' <' + re.from[0].address + '>\n' +
|
|
|
|
'Date: ' + sentDate + '\n' +
|
|
|
|
'Subject: ' + re.subject + '\n' +
|
|
|
|
'To: ' + createString(re.to) + '\n' +
|
2014-04-05 06:14:20 -04:00
|
|
|
((re.cc && re.cc.length > 0) ? 'Cc: ' + createString(re.cc) + '\n' : '') +
|
|
|
|
'\n\n';
|
2014-04-02 13:47:50 -04:00
|
|
|
|
|
|
|
} else {
|
|
|
|
body = '\n\n' + sentDate + ' ' + from + ' wrote:\n> ';
|
|
|
|
}
|
2013-11-06 02:36:22 -05:00
|
|
|
|
2013-11-26 13:06:37 -05:00
|
|
|
// only display non html mails in reply part
|
2013-11-06 02:36:22 -05:00
|
|
|
if (!re.html) {
|
2014-04-23 12:23:46 -04:00
|
|
|
body += re.body.trim().split('\n').join('\n> ').replace(/ >/g, '>');
|
2013-11-06 02:36:22 -05:00
|
|
|
$scope.body = body;
|
|
|
|
}
|
2013-10-12 13:39:09 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2013-11-04 15:07:32 -05:00
|
|
|
// Editing headers
|
|
|
|
//
|
|
|
|
|
2014-01-14 10:11:59 -05:00
|
|
|
/**
|
|
|
|
* This event is fired when editing the email address headers. It checks is space is pressed and if so, creates a new address field.
|
|
|
|
*/
|
2014-01-13 10:42:10 -05:00
|
|
|
$scope.onAddressUpdate = function(field, index) {
|
2014-01-19 10:18:32 -05:00
|
|
|
var recipient = field[index];
|
2014-01-14 10:11:59 -05:00
|
|
|
$scope.verify(recipient);
|
2014-01-10 15:35:34 -05:00
|
|
|
};
|
|
|
|
|
2014-01-14 10:11:59 -05:00
|
|
|
/**
|
|
|
|
* Verify and email address and fetch its public key
|
|
|
|
*/
|
|
|
|
$scope.verify = function(recipient) {
|
2013-11-04 15:07:32 -05:00
|
|
|
// set display to insecure while fetching keys
|
2014-01-10 15:35:34 -05:00
|
|
|
recipient.key = undefined;
|
|
|
|
recipient.secure = false;
|
2014-02-28 07:50:00 -05:00
|
|
|
$scope.checkSendStatus();
|
2014-01-10 15:35:34 -05:00
|
|
|
|
|
|
|
// verify email address
|
|
|
|
if (!util.validateEmailAddress(recipient.address)) {
|
|
|
|
recipient.secure = undefined;
|
2014-01-14 10:11:59 -05:00
|
|
|
$scope.checkSendStatus();
|
2014-01-10 15:35:34 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-04 15:07:32 -05:00
|
|
|
// check if to address is contained in known public keys
|
2014-01-10 15:35:34 -05:00
|
|
|
emailDao._keychain.getReceiverPublicKey(recipient.address, function(err, key) {
|
2013-11-04 15:07:32 -05:00
|
|
|
if (err) {
|
2013-11-13 11:05:21 -05:00
|
|
|
$scope.onError(err);
|
2013-11-04 15:07:32 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// compare again since model could have changed during the roundtrip
|
2014-01-10 15:35:34 -05:00
|
|
|
if (key && key.userId === recipient.address) {
|
|
|
|
recipient.key = key;
|
|
|
|
recipient.secure = true;
|
2013-11-04 15:07:32 -05:00
|
|
|
}
|
2014-01-13 17:54:53 -05:00
|
|
|
|
2014-01-14 10:11:59 -05:00
|
|
|
$scope.checkSendStatus();
|
2014-04-24 09:44:54 -04:00
|
|
|
$scope.$digest();
|
2013-11-04 15:07:32 -05:00
|
|
|
});
|
2014-01-14 10:11:59 -05:00
|
|
|
};
|
2013-11-04 15:07:32 -05:00
|
|
|
|
2014-02-20 13:20:24 -05:00
|
|
|
$scope.getKeyId = function(recipient) {
|
|
|
|
$scope.keyId = 'Key not found for that user.';
|
2014-01-19 10:58:51 -05:00
|
|
|
|
|
|
|
if (!recipient.key) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var fpr = crypto.getFingerprint(recipient.key.publicKey);
|
2014-02-20 13:20:24 -05:00
|
|
|
var formatted = fpr.slice(32);
|
2014-01-19 10:58:51 -05:00
|
|
|
|
2014-02-20 13:20:24 -05:00
|
|
|
$scope.keyId = formatted;
|
2014-01-19 10:58:51 -05:00
|
|
|
};
|
|
|
|
|
2014-01-14 10:11:59 -05:00
|
|
|
/**
|
|
|
|
* Check if it is ok to send an email depending on the invitation state of the addresses
|
|
|
|
*/
|
|
|
|
$scope.checkSendStatus = function() {
|
2014-01-13 17:54:53 -05:00
|
|
|
$scope.okToSend = false;
|
2013-11-04 15:07:32 -05:00
|
|
|
$scope.sendBtnText = undefined;
|
2014-01-13 17:54:53 -05:00
|
|
|
$scope.sendBtnSecure = undefined;
|
2013-11-04 15:07:32 -05:00
|
|
|
|
2014-01-13 17:54:53 -05:00
|
|
|
var allSecure = true;
|
|
|
|
var numReceivers = 0;
|
|
|
|
|
|
|
|
// count number of receivers and check security
|
|
|
|
$scope.to.forEach(check);
|
|
|
|
$scope.cc.forEach(check);
|
|
|
|
$scope.bcc.forEach(check);
|
|
|
|
|
|
|
|
function check(recipient) {
|
|
|
|
// validate address
|
|
|
|
if (!util.validateEmailAddress(recipient.address)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
numReceivers++;
|
|
|
|
if (!recipient.secure) {
|
|
|
|
allSecure = false;
|
|
|
|
}
|
|
|
|
}
|
2013-11-04 15:07:32 -05:00
|
|
|
|
2014-02-27 09:23:33 -05:00
|
|
|
// only allow sending if receviers exist
|
|
|
|
if (numReceivers < 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (allSecure) {
|
|
|
|
// send encrypted if all secure
|
2014-01-13 17:54:53 -05:00
|
|
|
$scope.okToSend = true;
|
2014-01-15 04:57:28 -05:00
|
|
|
$scope.sendBtnText = str.sendBtnSecure;
|
2014-01-13 17:54:53 -05:00
|
|
|
$scope.sendBtnSecure = true;
|
2014-02-27 09:23:33 -05:00
|
|
|
} else {
|
|
|
|
// send plaintext
|
|
|
|
$scope.okToSend = true;
|
|
|
|
$scope.sendBtnText = str.sendBtnClear;
|
|
|
|
$scope.sendBtnSecure = false;
|
2014-01-13 17:54:53 -05:00
|
|
|
}
|
2014-01-14 10:11:59 -05:00
|
|
|
};
|
2013-11-04 15:07:32 -05:00
|
|
|
|
2014-02-05 18:41:08 -05:00
|
|
|
//
|
|
|
|
// Editing attachments
|
|
|
|
//
|
|
|
|
|
|
|
|
$scope.remove = function(attachment) {
|
|
|
|
$scope.attachments.splice($scope.attachments.indexOf(attachment), 1);
|
|
|
|
};
|
|
|
|
|
2013-11-04 15:07:32 -05:00
|
|
|
//
|
|
|
|
// Editing email body
|
2013-10-12 13:39:09 -04:00
|
|
|
//
|
|
|
|
|
2013-09-13 08:11:47 -04:00
|
|
|
// generate key,iv for encryption preview
|
|
|
|
var key = util.random(128),
|
|
|
|
iv = util.random(128);
|
|
|
|
|
|
|
|
$scope.updatePreview = function() {
|
2013-11-27 05:57:15 -05:00
|
|
|
var body = $scope.body.trim();
|
2013-09-14 08:23:46 -04:00
|
|
|
|
2013-10-05 08:16:04 -04:00
|
|
|
// Although this does encrypt live using AES, this is just for show. The plaintext is encrypted seperately before sending the email.
|
2013-10-18 20:58:53 -04:00
|
|
|
$scope.ciphertextPreview = (body) ? aes.encrypt(body, key, iv) : '';
|
2013-09-13 08:11:47 -04:00
|
|
|
};
|
2013-09-15 11:05:37 -04:00
|
|
|
|
2013-10-23 14:46:42 -04:00
|
|
|
$scope.sendToOutbox = function() {
|
2014-01-13 12:38:45 -05:00
|
|
|
var email;
|
2013-11-11 10:11:06 -05:00
|
|
|
|
2014-01-13 12:38:45 -05:00
|
|
|
// build email model for smtp-client
|
2013-09-15 11:05:37 -04:00
|
|
|
email = {
|
2014-02-24 04:14:07 -05:00
|
|
|
from: [{
|
|
|
|
address: emailDao._account.emailAddress
|
|
|
|
}],
|
2014-02-25 08:10:55 -05:00
|
|
|
to: $scope.to.filter(filterEmptyAddresses),
|
|
|
|
cc: $scope.cc.filter(filterEmptyAddresses),
|
|
|
|
bcc: $scope.bcc.filter(filterEmptyAddresses),
|
2014-01-16 05:58:39 -05:00
|
|
|
subject: $scope.subject.trim() ? $scope.subject.trim() : str.fallbackSubject, // Subject line, or the fallback subject, if nothing valid was entered
|
2014-03-05 10:00:00 -05:00
|
|
|
body: $scope.body.trim() + (!$scope.sendBtnSecure ? str.signature : ''), // use parsed plaintext body
|
2014-03-06 12:02:05 -05:00
|
|
|
attachments: $scope.attachments,
|
|
|
|
sentDate: new Date()
|
2013-09-15 11:05:37 -04:00
|
|
|
};
|
2014-01-13 12:38:45 -05:00
|
|
|
|
2014-02-25 15:05:59 -05:00
|
|
|
// close the writer
|
|
|
|
$scope.state.writer.close();
|
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// persist the email to disk for later sending
|
|
|
|
outbox.put(email, function(err) {
|
2013-09-15 11:05:37 -04:00
|
|
|
if (err) {
|
2013-11-13 11:05:21 -05:00
|
|
|
$scope.onError(err);
|
2013-09-15 11:05:37 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// helper flag to remember if we need to sync back to imap
|
|
|
|
// in case the replyTo.answered changed
|
|
|
|
var needsSync = false;
|
2013-11-13 11:05:21 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// mark replyTo as answered, if necessary
|
|
|
|
if ($scope.replyTo && !$scope.replyTo.answered) {
|
|
|
|
$scope.replyTo.answered = true;
|
2014-02-25 15:05:59 -05:00
|
|
|
// update the ui
|
|
|
|
$scope.$apply();
|
2014-02-24 04:14:07 -05:00
|
|
|
needsSync = true;
|
|
|
|
}
|
2013-11-13 11:05:21 -05:00
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
// if we need to synchronize replyTo.answered, let's do that.
|
|
|
|
// otherwise, we're done
|
|
|
|
if (!needsSync) {
|
2013-12-09 13:21:52 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-24 04:14:07 -05:00
|
|
|
emailDao.sync({
|
|
|
|
folder: $scope.state.nav.currentFolder.path
|
|
|
|
}, function(err) {
|
|
|
|
if (err && err.code === 42) {
|
|
|
|
// offline
|
|
|
|
$scope.onError();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.onError(err);
|
|
|
|
});
|
2013-12-09 13:21:52 -05:00
|
|
|
});
|
2014-02-25 08:10:55 -05:00
|
|
|
|
|
|
|
function filterEmptyAddresses(addr) {
|
|
|
|
return !!addr.address;
|
|
|
|
}
|
2014-02-24 04:14:07 -05:00
|
|
|
};
|
2013-09-12 11:22:17 -04:00
|
|
|
};
|
2013-09-11 16:11:26 -04:00
|
|
|
|
2013-09-13 08:11:47 -04:00
|
|
|
//
|
|
|
|
// Directives
|
|
|
|
//
|
|
|
|
|
|
|
|
var ngModule = angular.module('write', []);
|
|
|
|
ngModule.directive('contenteditable', function() {
|
|
|
|
return {
|
|
|
|
require: 'ngModel',
|
|
|
|
link: function(scope, elm, attrs, ctrl) {
|
|
|
|
// view -> model
|
2013-10-13 07:49:37 -04:00
|
|
|
elm.on('keyup keydown', function() {
|
2014-04-24 09:44:54 -04:00
|
|
|
// set model
|
|
|
|
ctrl.$setViewValue(elm[0].innerText);
|
|
|
|
scope.$digest();
|
2013-09-13 08:11:47 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
// model -> view
|
2013-10-12 13:39:09 -04:00
|
|
|
ctrl.$render = function() {
|
2013-11-26 13:06:37 -05:00
|
|
|
elm[0].innerText = ctrl.$viewValue;
|
2013-09-13 08:11:47 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
// load init value from DOM
|
2013-11-26 13:06:37 -05:00
|
|
|
ctrl.$setViewValue(elm[0].innerText);
|
2013-09-13 08:11:47 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
2013-10-12 13:39:09 -04:00
|
|
|
|
2013-10-19 09:06:23 -04:00
|
|
|
ngModule.directive('focusMe', function($timeout, $parse) {
|
2013-09-13 08:11:47 -04:00
|
|
|
return {
|
2013-10-19 09:06:23 -04:00
|
|
|
//scope: true, // optionally create a child scope
|
|
|
|
link: function(scope, element, attrs) {
|
|
|
|
var model = $parse(attrs.focusMe);
|
|
|
|
scope.$watch(model, function(value) {
|
|
|
|
if (value === true) {
|
|
|
|
$timeout(function() {
|
|
|
|
element[0].focus();
|
2013-11-07 14:00:36 -05:00
|
|
|
}, 100);
|
2013-10-19 09:06:23 -04:00
|
|
|
}
|
|
|
|
});
|
2013-09-13 08:11:47 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2013-11-27 05:57:15 -05:00
|
|
|
ngModule.directive('focusChild', function() {
|
|
|
|
return {
|
|
|
|
//scope: true, // optionally create a child scope
|
|
|
|
link: function(scope, element) {
|
2014-01-19 10:18:32 -05:00
|
|
|
element.on('click', function() {
|
2013-11-27 05:57:15 -05:00
|
|
|
element[0].children[0].focus();
|
2014-01-19 10:18:32 -05:00
|
|
|
});
|
2013-11-27 05:57:15 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2013-11-04 15:07:32 -05:00
|
|
|
ngModule.directive('autoSize', function($parse) {
|
|
|
|
return {
|
|
|
|
require: 'ngModel',
|
|
|
|
link: function(scope, elm, attrs) {
|
2014-01-19 10:18:32 -05:00
|
|
|
// resize text input depending on value length
|
2013-11-04 15:07:32 -05:00
|
|
|
var model = $parse(attrs.autoSize);
|
|
|
|
scope.$watch(model, function(value) {
|
2014-01-10 15:35:34 -05:00
|
|
|
var width;
|
|
|
|
|
|
|
|
if (value.length < 12) {
|
|
|
|
width = (14 * 8) + 'px';
|
|
|
|
} else {
|
|
|
|
width = ((value.length + 2) * 8) + 'px';
|
2013-11-04 15:07:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
elm.css('width', width);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2014-01-19 10:18:32 -05:00
|
|
|
function addInput(field, scope) {
|
|
|
|
field.push({
|
|
|
|
address: ''
|
|
|
|
});
|
|
|
|
scope.$apply();
|
|
|
|
}
|
|
|
|
|
2014-04-22 10:41:07 -04:00
|
|
|
function removeInput(field, index, scope) {
|
|
|
|
field.splice(index, 1);
|
|
|
|
scope.$apply();
|
|
|
|
}
|
|
|
|
|
2014-01-19 10:18:32 -05:00
|
|
|
function checkForEmptyInput(field) {
|
|
|
|
var emptyFieldExists = false;
|
|
|
|
field.forEach(function(recipient) {
|
|
|
|
if (!recipient.address) {
|
|
|
|
emptyFieldExists = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return emptyFieldExists;
|
|
|
|
}
|
|
|
|
|
2014-04-22 10:41:07 -04:00
|
|
|
function cleanupEmptyInputs(field, scope) {
|
|
|
|
var i;
|
|
|
|
|
|
|
|
for (i = field.length - 2; i >= 0; i--) {
|
|
|
|
if (!field[i].address) {
|
|
|
|
field.splice(i, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
scope.$apply();
|
|
|
|
}
|
|
|
|
|
2014-01-19 10:18:32 -05:00
|
|
|
ngModule.directive('field', function() {
|
|
|
|
return {
|
|
|
|
//scope: true, // optionally create a child scope
|
|
|
|
link: function(scope, element, attrs) {
|
|
|
|
element.on('click', function() {
|
|
|
|
var fieldName = attrs.field;
|
|
|
|
var field = scope[fieldName];
|
|
|
|
|
|
|
|
if (!checkForEmptyInput(field)) {
|
|
|
|
// create new field input if no empy one exists
|
|
|
|
addInput(field, scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
// focus on last input when clicking on field
|
|
|
|
var id = fieldName + (field.length - 1);
|
|
|
|
document.getElementById(id).focus();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
ngModule.directive('addressInput', function() {
|
2014-01-10 15:35:34 -05:00
|
|
|
return {
|
|
|
|
//scope: true, // optionally create a child scope
|
|
|
|
link: function(scope, element, attrs) {
|
2014-01-13 10:42:10 -05:00
|
|
|
// get prefix for id
|
2014-01-19 10:18:32 -05:00
|
|
|
var fieldName = attrs.addressInput;
|
|
|
|
var field = scope[fieldName];
|
|
|
|
var index = parseInt(attrs.id.replace(fieldName, ''), 10);
|
|
|
|
|
|
|
|
element.on('click', function(e) {
|
|
|
|
// focus on this one and dont bubble to field click handler
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
|
|
|
element.on('blur', function() {
|
|
|
|
if (!checkForEmptyInput(field)) {
|
|
|
|
// create new field input
|
|
|
|
addInput(field, scope);
|
|
|
|
}
|
2014-04-22 10:41:07 -04:00
|
|
|
|
|
|
|
cleanupEmptyInputs(field, scope);
|
2014-01-19 10:18:32 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
element.on('keydown', function(e) {
|
|
|
|
var code = e.keyCode;
|
|
|
|
|
2014-04-24 09:44:54 -04:00
|
|
|
scope.$digest();
|
2014-02-25 15:32:41 -05:00
|
|
|
|
2014-01-19 10:18:32 -05:00
|
|
|
if (code === 32 || code === 188 || code === 186) {
|
|
|
|
// catch space, comma, semicolon
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
// create new field input
|
|
|
|
addInput(field, scope);
|
|
|
|
// find next input and focus
|
|
|
|
var nextId = fieldName + (index + 1);
|
|
|
|
document.getElementById(nextId).focus();
|
|
|
|
|
|
|
|
} else if ((code === 8 || code === 46) && !field[index].address && field.length > 1) {
|
|
|
|
// backspace, delete on empty input
|
|
|
|
// remove input
|
|
|
|
e.preventDefault();
|
2014-04-22 10:41:07 -04:00
|
|
|
removeInput(field, index, scope);
|
2014-01-19 10:18:32 -05:00
|
|
|
// focus on previous id
|
|
|
|
var previousId = fieldName + (index - 1);
|
|
|
|
document.getElementById(previousId).focus();
|
2014-01-10 15:35:34 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2014-02-05 18:41:08 -05:00
|
|
|
ngModule.directive('attachmentInput', function() {
|
2014-02-04 15:04:48 -05:00
|
|
|
return function(scope, elm) {
|
2014-02-05 18:41:08 -05:00
|
|
|
elm.on('change', function(e) {
|
2014-02-04 15:04:48 -05:00
|
|
|
for (var i = 0; i < e.target.files.length; i++) {
|
|
|
|
addAttachment(e.target.files.item(i));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
function addAttachment(file) {
|
|
|
|
var reader = new FileReader();
|
|
|
|
reader.onload = function(e) {
|
|
|
|
scope.attachments.push({
|
2014-02-06 13:19:00 -05:00
|
|
|
filename: file.name,
|
|
|
|
mimeType: file.type,
|
|
|
|
content: new Uint8Array(e.target.result)
|
2014-02-04 15:04:48 -05:00
|
|
|
});
|
2014-04-24 09:44:54 -04:00
|
|
|
scope.$digest();
|
2014-02-04 15:04:48 -05:00
|
|
|
};
|
|
|
|
reader.readAsArrayBuffer(file);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2014-02-05 18:41:08 -05:00
|
|
|
ngModule.directive('attachmentBtn', function() {
|
|
|
|
return function(scope, elm) {
|
2014-02-28 07:50:00 -05:00
|
|
|
elm.on('click touchstart', function(e) {
|
|
|
|
e.preventDefault();
|
2014-02-05 18:41:08 -05:00
|
|
|
document.querySelector('#attachment-input').click();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
2013-09-11 16:11:26 -04:00
|
|
|
return WriteCtrl;
|
|
|
|
});
|