summaryrefslogtreecommitdiff
path: root/web-ui/public/js/mail_view/ui/recipients/recipients_input.js
diff options
context:
space:
mode:
Diffstat (limited to 'web-ui/public/js/mail_view/ui/recipients/recipients_input.js')
-rw-r--r--web-ui/public/js/mail_view/ui/recipients/recipients_input.js180
1 files changed, 180 insertions, 0 deletions
diff --git a/web-ui/public/js/mail_view/ui/recipients/recipients_input.js b/web-ui/public/js/mail_view/ui/recipients/recipients_input.js
new file mode 100644
index 00000000..8a9c4eaf
--- /dev/null
+++ b/web-ui/public/js/mail_view/ui/recipients/recipients_input.js
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2014 ThoughtWorks, Inc.
+ *
+ * Pixelated 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.
+ *
+ * Pixelated 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 Pixelated. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define([
+ 'flight/lib/component',
+ 'page/events',
+ 'features'
+ ],
+ function (defineComponent, events, features) {
+ 'use strict';
+
+ function recipientsInput() {
+ var EXIT_KEY_CODE_MAP = {
+ 8: 'backspace',
+ 37: 'left'
+ },
+ ENTER_ADDRESS_KEY_CODE_MAP = {
+ 9: 'tab',
+ 186: 'semicolon',
+ 188: 'comma',
+ 13: 'enter',
+ 27: 'esc'
+ },
+ EVENT_FOR = {
+ 8: events.ui.recipients.deleteLast,
+ 37: events.ui.recipients.selectLast
+ },
+ self;
+
+ var simpleAddressMatch = /[^<\w,;]?([^\s<;,]+@[\w-]+\.[^\s>;,]+)/;
+ var canonicalAddressMatch = /([^,;\s][^,;@]+<[^\s;,]+@[\w-]+\.[^\s;,]+>)/;
+ var emailAddressMatch = new RegExp([simpleAddressMatch.source, '|', canonicalAddressMatch.source].join(''), 'g');
+
+ var extractContactNames = function (response) {
+ return _.map(response, function(a) { return { value: a }; });
+ };
+
+ function createEmailCompleter() {
+ var emailCompleter = new Bloodhound({
+ datumTokenizer: function (d) {
+ return [d.value];
+ },
+ queryTokenizer: function (q) {
+ return [q.trim()];
+ },
+ remote: {
+ url: '/contacts?q=%QUERY',
+ filter: extractContactNames
+ }
+ });
+ emailCompleter.initialize();
+ return emailCompleter;
+ }
+
+ function reset(node) {
+ node.typeahead('val', '');
+ }
+
+ function caretIsInTheBeginningOfInput(input) {
+ return input.selectionStart === 0;
+ }
+
+ function isExitKey(keyPressed) {
+ return EXIT_KEY_CODE_MAP.hasOwnProperty(keyPressed);
+ }
+
+ function isEnterAddressKey(keyPressed) {
+ return ENTER_ADDRESS_KEY_CODE_MAP.hasOwnProperty(keyPressed);
+ }
+
+ this.processSpecialKey = function (event) {
+ var keyPressed = event.which;
+
+ if (isExitKey(keyPressed) && caretIsInTheBeginningOfInput(this.$node[0])) {
+ this.trigger(EVENT_FOR[keyPressed]);
+ return;
+ }
+
+ if (!event.shiftKey && isEnterAddressKey(keyPressed)) {
+ this.tokenizeRecipient(event);
+
+ if ((keyPressed !== 9 /* tab */)) {
+ event.preventDefault();
+ }
+ }
+
+ };
+
+ this.tokenizeRecipient = function (event) {
+ if (_.isEmpty(this.$node.val().trim())) {
+ return;
+ }
+
+ this.recipientSelected(null, {value: this.$node.val() });
+ event.preventDefault();
+ };
+
+ this.recipientSelected = function (event, data) {
+ var value = (data && data.value) || this.$node.val();
+
+ var validAddresses = this.extractValidAddresses(value);
+ var invalidAddresses = this.extractInvalidAddresses(value);
+
+ this.triggerEventForEach(validAddresses, events.ui.recipients.entered);
+ this.triggerEventForEach(invalidAddresses, events.ui.recipients.enteredInvalid);
+
+ reset(this.$node);
+ };
+
+ this.triggerEventForEach = function (addresses, event) {
+ var that = this;
+ _.each(addresses, function(address) {
+ if (!_.isEmpty(address.trim())) {
+ that.trigger(that.$node, event, { name: that.attr.name, address: address.trim() });
+ }
+ });
+ };
+
+ this.extractValidAddresses = function(rawAddresses) {
+ return rawAddresses.match(emailAddressMatch);
+ };
+
+ this.extractInvalidAddresses = function(rawAddresses) {
+ return rawAddresses.replace(emailAddressMatch, '').split(/[,;]/);
+ };
+
+ this.init = function () {
+ this.$node.typeahead({
+ hint: true,
+ highlight: true,
+ minLength: 1
+ }, {
+ source: createEmailCompleter().ttAdapter(),
+ templates: {
+ suggestion: function (o) { return _.escape(o.value); }
+ }
+ });
+ };
+
+ this.attachAndReturn = function (node, name) {
+ var input = new this.constructor();
+ input.initialize(node, { name: name});
+ return input;
+ };
+
+ this.warnSendButtonOfInputState = function () {
+ var toTrigger = _.isEmpty(this.$node.val()) ? events.ui.recipients.inputFieldIsEmpty : events.ui.recipients.inputFieldHasCharacters;
+ this.trigger(document, toTrigger, { name: this.attr.name });
+ };
+
+ this.after('initialize', function () {
+ self = this;
+ this.init();
+ this.on('typeahead:selected typeahead:autocompleted', this.recipientSelected);
+ this.on(this.$node, 'focusout', this.tokenizeRecipient);
+ this.on(this.$node, 'keydown', this.processSpecialKey);
+ this.on(this.$node, 'keyup', this.warnSendButtonOfInputState);
+
+ this.on(document, events.dispatchers.rightPane.clear, this.teardown);
+ });
+ }
+
+ return defineComponent(recipientsInput);
+
+ }
+);