Пример #1
0
 isValidAddress: function(address) {
   // An address is valid if model.api.parseMailbox thinks it
   // contains a valid address. (It correctly classifies names that
   // are not valid addresses.)
   var mailbox = model.api.parseMailbox(address);
   return mailbox && mailbox.address;
 },
Пример #2
0
    createdCallback: function() {
      this.bindContainerHandler(this.foldersContainer, 'click',
                                this.onClickFolder.bind(this));

      this.updateAccount = this.updateAccount.bind(this);
      model.latest('account', this.updateAccount);

      this.bindContainerHandler(this.accountListContainer, 'click',
                                this.onClickAccount.bind(this));

      this.addEventListener('transitionend', this.onTransitionEnd.bind(this));

      // If more than one account, need to show the account dropdown
      var accountCount = model.getAccountCount();
      if (accountCount > 1) {
        this.classList.remove('one-account');
        // Set up size needed to handle translation animation for showing
        // accounts later.
        this.currentAccountContainerHeight = this.accountHeader
                                             .getBoundingClientRect().height *
                                             accountCount;
        this.hideAccounts();
      }

      this.acctsSlice = model.api.viewAccounts(false);
      this.acctsSlice.onsplice = this.onAccountsSplice.bind(this);
      this.acctsSlice.onchange = this.onAccountsChange.bind(this);
    },
Пример #3
0
  onAddressInput: function(evt) {
    var node = evt.target;
    var container = evt.target.parentNode;

    if (this.isEmptyAddress()) {
      this.sendButton.setAttribute('aria-disabled', 'true');
      return;
    }
    this.sendButton.setAttribute('aria-disabled', 'false');
    var makeBubble = false;
    // When do we want to tie off this e-mail address, put it into a bubble
    // and clear the input box so the user can type another address?
    switch (node.value.slice(-1)) {
      // If they hit space and we believe they've already typed an email
      // address!  (Space is okay in a display name or to delimit a display
      // name from the e-mail address)
      //
      // We use the presence of an '@' character as indicating that the e-mail
      // address
      case ' ':
        makeBubble = node.value.indexOf('@') !== -1;
        break;
      // We started out supporting comma, but now it's not on our keyboard at
      // all in type=email mode!  We aren't terribly concerned about it not
      // being usable in display names, although we really should check for
      // quoting...
      case ',':
      // Semicolon is on the keyboard, and we also don't care about it not
      // being usable in display names.
      case ';':
        makeBubble = true;
        break;
    }
    if (makeBubble) {
      // TODO: Need to match the email with contact name.
      node.style.width = '0.5rem';
      var mailbox = model.api.parseMailbox(node.value);
      this.insertBubble(node, mailbox.name, mailbox.address);
      node.value = '';
    }
    // XXX: Workaround to get the length of the string. Here we create a dummy
    //      div for computing actual string size for changing input
    //      size dynamically.
    if (!this.stringContainer) {
      this.stringContainer = document.createElement('div');
      this.domNode.appendChild(this.stringContainer);

      var inputStyle = window.getComputedStyle(node);
      this.stringContainer.style.fontSize = inputStyle.fontSize;
    }
    this.stringContainer.style.display = 'inline-block';
    this.stringContainer.textContent = node.value;
    node.style.width =
      (this.stringContainer.clientWidth + 2) + 'px';
  },
Пример #4
0
        model.latestOnce('folder', function(folder) {
          this.composer = model.api.beginMessageComposition(data.message,
                                                            folder,
                                                            data.options,
                                                            function() {
            if (data.onComposer)
              data.onComposer(this.composer);

            this._loadStateFromComposer();
          }.bind(this));
        }.bind(this));
Пример #5
0
function FolderPickerCard(domNode, mode, args) {
  this.domNode = domNode;

  this.foldersContainer =
    domNode.getElementsByClassName('fld-folders-container')[0];
  bindContainerHandler(this.foldersContainer, 'click',
                       this.onClickFolder.bind(this));

  domNode.getElementsByClassName('fld-nav-toolbar')[0]
    .addEventListener('click', this.onShowSettings.bind(this), false);

  domNode.getElementsByClassName('fld-header-back')[0]
    .addEventListener('click', this._closeCard.bind(this), false);

  domNode.getElementsByClassName('fld-shield')[0]
    .addEventListener('click', this._closeCard.bind(this), false);

  this.accountHeader = domNode.getElementsByClassName('fld-acct-header')[0];
  this.accountHeader.addEventListener('click',
                                      this.toggleAccounts.bind(this), false);

  this.updateAccount = this.updateAccount.bind(this);
  model.latest('account', this.updateAccount);

  this.fldAcctScrollInner =
    domNode.getElementsByClassName('fld-acct-scrollinner')[0];

  this.fldAcctContainer =
    domNode.getElementsByClassName('fld-acct-container')[0];

  this.accountListContainer =
    domNode.getElementsByClassName('fld-accountlist-container')[0];
  bindContainerHandler(this.accountListContainer, 'click',
                       this.onClickAccount.bind(this));

  this.domNode.addEventListener('transitionend',
                                this.onTransitionEnd.bind(this));

  // If more than one account, need to show the account dropdown
  var accountCount = model.getAccountCount();
  if (accountCount > 1) {
    removeClass(this.domNode, 'one-account');
    // Set up size needed to handle translation animation for showing
    // accounts later.
    this.currentAccountContainerHeight = this.accountHeader
                                         .getBoundingClientRect().height *
                                         accountCount;
    this.hideAccounts();
  }

  this.acctsSlice = model.api.viewAccounts(false);
  this.acctsSlice.onsplice = this.onAccountsSplice.bind(this);
  this.acctsSlice.onchange = this.onAccountsChange.bind(this);
}
Пример #6
0
 function frobAddressNode(node) {
   var container = node.parentNode;
   var addrList = [];
   var bubbles = container.querySelectorAll('.cmp-peep-bubble');
   for (var i = 0; i < bubbles.length; i++) {
     var dataSet = bubbles[i].dataset;
     addrList.push({ name: dataSet.name, address: dataSet.address });
   }
   if (node.value.trim().length !== 0) {
     var mailbox = model.api.parseMailbox(node.value);
     addrList.push({ name: mailbox.name, address: mailbox.address });
   }
   return addrList;
 }
Пример #7
0
  showFolder: function(folder, forceNewSlice) {
    if (folder === this.curFolder && !forceNewSlice)
      return false;

    if (this.messagesSlice) {
      this.messagesSlice.die();
      this.messagesSlice = null;
      this.messagesContainer.innerHTML = '';
    }
    this.curFolder = folder;
    switch (folder.type) {
      case 'drafts':
      case 'localdrafts':
      case 'sent':
        this.isIncomingFolder = false;
        break;
      default:
        this.isIncomingFolder = true;
        break;
    }

    this.domNode.getElementsByClassName('msg-list-header-folder-label')[0]
      .textContent = folder.name;

    this.hideEmptyLayout();

    // you can't refresh the localdrafts folder or move messages out of it.
    if (folder.type === 'localdrafts') {
      this.toolbar.refreshBtn.classList.add('collapsed');
      this.toolbar.moveBtn.classList.add('collapsed');
    }
    else {
      this.toolbar.refreshBtn.classList.remove('collapsed');
      this.toolbar.moveBtn.classList.remove('collapsed');
    }

    // We are creating a new slice, so any pending snippet requests are moot.
    this._snippetRequestPending = false;
    this.messagesSlice = model.api.viewFolderMessages(folder);
    this.messagesSlice.onsplice = this.onMessagesSplice.bind(this);
    this.messagesSlice.onchange = this.onMessagesChange.bind(this);
    this.messagesSlice.onstatus = this.onStatusChange.bind(this);
    this.messagesSlice.oncomplete = this._boundSliceRequestComplete;
    return true;
  },
Пример #8
0
    model.latestOnce('folder', function(folder) {
      var messagesSlice = model.api.viewFolderMessages(folder);

      function clear() {
        messagesSlice.die();
        messagesSlice = null;
      }

      messagesSlice.onsplice = (function(index, howMany, addedItems,
                                         requested, moreExpected) {

        // Avoid doing work if get called while in the process of
        // shutting down.
        if (!messagesSlice)
          return;

        if (!this.header && addedItems && addedItems.length) {
          addedItems.some(function(item) {
              if (item.id === this.messageSuid) {
                this._setHeader(item);
                clear();
                return true;
              }
          }.bind(this));

          // If at the top, and no message was found, then if the UI
          // wants to go back on missing message, do that now. This
          // card may have been created from obsolete data, like an
          // old notification for a message that no longer exists.
          // This stops atTop since the most likely case for this
          // entry point is either clicking on a message that is
          // at the top of the inbox in the HTML cache, or from a
          // notification for a new message, which would be near
          // the top.
          if (messagesSlice && messagesSlice.atTop &&
              !this.header &&
              args.backOnMissingMessage) {
            clear();
            this.onBack();
          }
        }

      }).bind(this);
    }.bind(this));
Пример #9
0
 var frobAddressNode = (function(node) {
   var bubbles = node.parentNode.querySelectorAll('.cmp-peep-bubble');
   var addrList = [];
   for (var i = 0; i < bubbles.length; i++) {
     var dataSet = bubbles[i].dataset;
     addrList.push({ name: dataSet.name, address: dataSet.address });
   }
   if (node.value.trim().length !== 0) {
     var mailbox = model.api.parseMailbox(node.value);
     addrList.push({ name: mailbox.name, address: mailbox.address });
   }
   addrList.forEach(function(addr) {
     allAddresses.push(addr);
     if (!this.isValidAddress(addr.address)) {
       invalidAddresses.push(addr);
     }
   }.bind(this));
   return addrList;
 }.bind(this));
Пример #10
0
  showSearch: function(folder, phrase, filter) {
    console.log('sf: showSearch. phrase:', phrase, phrase.length);
    var tab = this.domNode.getElementsByClassName('filter')[0];
    var nodes = tab.getElementsByClassName('msg-search-filter');
    if (this.messagesSlice) {
      this.messagesSlice.die();
      this.messagesSlice = null;
      this.messagesContainer.innerHTML = '';
    }
    this.curFolder = folder;
    this.curPhrase = phrase;
    this.curFilter = filter;

    for (var i = 0; i < nodes.length; i++) {
      if (nodes[i].dataset.filter != this.curFilter) {
        nodes[i].setAttribute('aria-selected', 'false');
        continue;
      }
      nodes[i].setAttribute('aria-selected', 'true');
    }
    if (phrase.length < 1)
      return false;

    // We are creating a new slice, so any pending snippet requests are moot.
    this._snippetRequestPending = false;
    this.messagesSlice = model.api.searchFolderMessages(
      folder, phrase,
      {
        author: filter === 'all' || filter === 'author',
        recipients: filter === 'all' || filter === 'recipients',
        subject: filter === 'all' || filter === 'subject',
        body: filter === 'all' || filter === 'body'
      });
    this.messagesSlice.onsplice = this.onMessagesSplice.bind(this);
    this.messagesSlice.onchange = this.updateMatchedMessageDom.bind(this,
                                                                    false);
    this.messagesSlice.onstatus = this.onStatusChange.bind(this);
    this.messagesSlice.oncomplete = this._boundSliceRequestComplete;
    return true;
  },
Пример #11
0
 Cards.folderSelector(function(folder) {
   var op = model.api.moveMessages(this.selectedMessages, folder);
   Toaster.logMutation(op);
   this.setEditMode(false);
 }.bind(this));
Пример #12
0
 handler: function() {
   var op = model.api.deleteMessages(this.selectedMessages);
   Toaster.logMutation(op);
   this.setEditMode(false);
 }.bind(this)
Пример #13
0
 onMarkMessagesRead: function() {
   var op = model.api.markMessagesRead(this.selectedMessages, this.setAsRead);
   this.setEditMode(false);
   Toaster.logMutation(op);
 },
Пример #14
0
 onStarMessages: function() {
   var op = model.api.markMessagesStarred(this.selectedMessages,
                                        this.setAsStarred);
   this.setEditMode(false);
   Toaster.logMutation(op);
 },
Пример #15
0
 freshMessagesSlice: function() {
   this.bindToSlice(model.api.viewFolderMessages(model.folder));
 },
Пример #16
0
 startSearch: function(phrase, whatToSearch) {
   this.bindToSlice(model.api.searchFolderMessages(model.folder,
                                                   phrase,
                                                   whatToSearch));
 },