Exemplo n.º 1
0
  _loadStateFromComposer: function() {
    var self = this;
    function expandAddresses(node, addresses) {
      if (!addresses)
        return '';
      var container = node.parentNode;
      var normed = addresses.map(function(aval) {
        var name, address;
        if (typeof(aval) === 'string') {
          // TODO: We will apply email address parser for showing bubble
          //       properly. We set both name and address same as aval string
          //       before parser is ready.
          name = address = aval;
        } else {
          name = aval.name;
          address = aval.address;
        }
        self.insertBubble(node, name, address);
      });
    }
    expandAddresses(this.toNode, this.composer.to);
    expandAddresses(this.ccNode, this.composer.cc);
    expandAddresses(this.bccNode, this.composer.bcc);

    this.validateAddresses();

    this.renderSendStatus();

    // Add attachments
    this.renderAttachments();

    this.subjectNode.value = this.composer.subject;
    this.populateEditor(this.composer.body.text);

    if (this.composer.body.html) {
      // Although (still) sanitized, this is still HTML we did not create and so
      // it gets to live in an iframe.  Its read-only and the user needs to be
      // able to see what they are sending, so reusing the viewing functionality
      // is desirable.
      var ishims = iframeShims.createAndInsertIframeForContent(
        this.composer.body.html, this.scrollContainer,
        this.htmlBodyContainer, /* append */ null,
        'noninteractive',
        /* no click handler because no navigation desired */ null);
      this.htmlIframeNode = ishims.iframe;
    }

    // There is a bit more possibility of async work done in the iframeShims
    // internals, but this is close enough and is better than breaking open
    // the internals of the iframeShims to get the final number.
    if (!this._emittedContentEvents) {
      evt.emit('metrics:contentDone');
      this._emittedContentEvents = true;
    }
  },
Exemplo n.º 2
0
  _loadStateFromComposer: function() {
    var self = this;
    function expandAddresses(node, addresses) {
      if (!addresses)
        return '';
      var container = node.parentNode;
      var normed = addresses.map(function(aval) {
        var name, address;
        if (typeof(aval) === 'string') {
          // TODO: We will apply email address parser for showing bubble
          //       properly. We set both name and address same as aval string
          //       before parser is ready.
          name = address = aval;
        } else {
          name = aval.name;
          address = aval.address;
        }
        self.insertBubble(node, name, address);
      });
    }
    expandAddresses(this.toNode, this.composer.to);
    expandAddresses(this.ccNode, this.composer.cc);
    expandAddresses(this.bccNode, this.composer.bcc);

    if (this.isEmptyAddress()) {
      this.sendButton.setAttribute('aria-disabled', 'true');
    }

    // Add attachments
    this.insertAttachments();

    this.subjectNode.value = this.composer.subject;
    this.textBodyNode.value = this.composer.body.text;
    // force the textarea to be sized.
    this.onTextBodyDelta();

    if (this.composer.body.html) {
      // Although (still) sanitized, this is still HTML we did not create and so
      // it gets to live in an iframe.  Its read-only and the user needs to be
      // able to see what they are sending, so reusing the viewing functionality
      // is desirable.
      var ishims = iframeShims.createAndInsertIframeForContent(
        this.composer.body.html, this.scrollContainer,
        this.htmlBodyContainer, /* append */ null,
        'noninteractive',
        /* no click handler because no navigation desired */ null);
      this.htmlIframeNode = ishims.iframe;
    }
  },
Exemplo n.º 3
0
  buildBodyDom: function(/* optional */ changeDetails) {
    var body = this.body,
        domNode = this.domNode,
        rootBodyNode = this.rootBodyNode,
        reps = body.bodyReps,
        hasExternalImages = false,
        showEmbeddedImages = body.embeddedImageCount &&
                             body.embeddedImagesDownloaded;


    // The first time we build the body DOM, do one-time bootstrapping:
    if (!this._builtBodyDom) {
      iframeShims.bindSanitizedClickHandler(rootBodyNode,
                                            this.onHyperlinkClick.bind(this),
                                            rootBodyNode,
                                            null);
      this._builtBodyDom = true;
    }

    // If we have fully downloaded one body part, the user has
    // something to read so get rid of the spinner.
    // XXX: Potentially improve the UI to show if we're still
    // downloading the rest of the body even if we already have some
    // of it.
    if (reps.length && reps[0].isDownloaded) {
      // remove progress bar if we've retrieved the first rep
      var progressNode = rootBodyNode.querySelector('progress');
      if (progressNode) {
        progressNode.parentNode.removeChild(progressNode);
      }
    }

    // The logic below depends on having removed the progress node!

    for (var iRep = 0; iRep < reps.length; iRep++) {
      var rep = reps[iRep];

      // Create an element to hold this body rep. Even if we aren't
      // updating this rep right now, we need to have a placeholder.
      var repNode = rootBodyNode.childNodes[iRep];
      if (!repNode) {
        repNode = rootBodyNode.appendChild(document.createElement('div'));
      }

      // Skip updating this rep if it's not updated.
      if (changeDetails && changeDetails.bodyReps &&
          changeDetails.bodyReps.indexOf(iRep) === -1) {
        continue;
      }

      // Wipe out the existing contents of the rep node so we can
      // replace it. We can just nuke innerHTML since we add click
      // handlers on the rootBodyNode, and for text/html parts the
      // listener is a child of repNode so it will get destroyed too.
      repNode.innerHTML = '';

      if (rep.type === 'plain') {
        this._populatePlaintextBodyNode(repNode, rep.content);
      }
      else if (rep.type === 'html') {
        var iframeShim = iframeShims.createAndInsertIframeForContent(
          rep.content, this.scrollContainer, repNode, null,
          'interactive', this.onHyperlinkClick.bind(this));
        var iframe = iframeShim.iframe;
        var bodyNode = iframe.contentDocument.body;
        this.iframeResizeHandler = iframeShim.resizeHandler;
        model.api.utils.linkifyHTML(iframe.contentDocument);
        this.htmlBodyNodes.push(bodyNode);

        if (body.checkForExternalImages(bodyNode))
          hasExternalImages = true;
        if (showEmbeddedImages)
          body.showEmbeddedImages(bodyNode, this.iframeResizeHandler);
      }
    }

    // The image logic checks embedded image counts, so this should be
    // able to run every time:
    // -- HTML-referenced Images
    var loadBar = this.loadBar;
    if (body.embeddedImageCount && !body.embeddedImagesDownloaded) {
      loadBar.classList.remove('collapsed');
      mozL10n.setAttributes(this.loadBarText, 'message-download-images-tap',
                            { n: body.embeddedImageCount });
    }
    else if (hasExternalImages) {
      loadBar.classList.remove('collapsed');
      mozL10n.setAttributes(this.loadBarText, 'message-show-external-images');
    }
    else {
      loadBar.classList.add('collapsed');
    }

    // -- Attachments (footer)
    // An attachment can be in 1 of 3 possible states for UI purposes:
    // - Not downloadable: We can't download this message because we wouldn't
    //   be able to do anything with it if we downloaded it.  Anything that's
    //   not a supported image type falls in this category.
    // - Downloadable, not downloaded: The user can trigger download of the
    //   attachment to DeviceStorage.
    // - Downloadable, downloaded: The attachment is already fully downloaded
    //   to DeviceStorage and we can trigger its display.
    var attachmentsContainer =
      domNode.getElementsByClassName('msg-attachments-container')[0];
    if (body.attachments && body.attachments.length) {
      // If buildBodyDom is called multiple times, the attachment
      // state might change, so we must ensure the attachment list is
      // not collapsed if we now have attachments.
      attachmentsContainer.classList.remove('collapsed');
      // We need MimeMapper to help us determining the downloadable attachments
      // but it might not be loaded yet, so load before use it
      require(['shared/js/mime_mapper'], function(mapper) {
        if (!MimeMapper)
          MimeMapper = mapper;

        var attTemplate = msgAttachmentItemNode,
            filenameTemplate =
              attTemplate.getElementsByClassName('msg-attachment-filename')[0],
            filesizeTemplate =
              attTemplate.getElementsByClassName('msg-attachment-filesize')[0];
        for (var iAttach = 0; iAttach < body.attachments.length; iAttach++) {

          // Create an element to hold this attachment.
          var attNode = attachmentsContainer.childNodes[iAttach];
          if (!attNode) {
            attNode = attachmentsContainer.appendChild(
              document.createElement('li'));
          }

          // Skip updating this attachment if it's not updated.
          if (changeDetails && changeDetails.attachments &&
              changeDetails.attachments.indexOf(iAttach) === -1) {
            continue;
          }

          var attachment = body.attachments[iAttach], state;
          var extension = attachment.filename.split('.').pop();

          var MAX_ATTACHMENT_SIZE = 20 * 1024 * 1024;
          var attachmentDownloadable = true;
          var mimeClass = mimeToClass(attachment.mimetype ||
                          MimeMapper.guessTypeFromExtension(extension));

          if (attachment.isDownloaded) {
            state = 'downloaded';
          } else if (!attachment.isDownloadable) {
            state = 'nodownload';
            attachmentDownloadable = false;
          } else if (attachment.sizeEstimateInBytes > MAX_ATTACHMENT_SIZE) {
            state = 'toolarge';
            attachmentDownloadable = false;
          } else if (MimeMapper.isSupportedType(attachment.mimetype) ||
                   MimeMapper.isSupportedExtension(extension)) {
            state = 'downloadable';
          } else {
            state = 'nodownload';
          }
          attTemplate.setAttribute('state', state);
          filenameTemplate.textContent = attachment.filename;
          filesizeTemplate.textContent = prettyFileSize(
            attachment.sizeEstimateInBytes);

          var attachmentNode = attTemplate.cloneNode(true);
          attachmentNode.classList.add(mimeClass);
          attachmentsContainer.replaceChild(attachmentNode, attNode);

          var downloadButton = attachmentNode.getElementsByClassName(
            'msg-attachment-download')[0];
          downloadButton.disabled = !attachmentDownloadable;
          if (attachmentDownloadable) {
            downloadButton.addEventListener(
              'click', this.onDownloadAttachmentClick.bind(
                this, attachmentNode, attachment));
          }
          attachmentNode.getElementsByClassName('msg-attachment-view')[0]
            .addEventListener('click',
                              this.onViewAttachmentClick.bind(
                                this, attachmentNode, attachment));
          this.enableReply();
        }
      }.bind(this));
    }
    else {
      attachmentsContainer.classList.add('collapsed');
      this.enableReply();
    }
  },
Exemplo n.º 4
0
  buildBodyDom: function() {
    var body = this.body;
    var domNode = this.domNode;

    var rootBodyNode = domNode.getElementsByClassName('msg-body-container')[0],
        reps = body.bodyReps,
        hasExternalImages = false,
        showEmbeddedImages = body.embeddedImageCount &&
                             body.embeddedImagesDownloaded;

    iframeShims.bindSanitizedClickHandler(rootBodyNode,
                                          this.onHyperlinkClick.bind(this),
                                          rootBodyNode,
                                          null);

    for (var iRep = 0; iRep < reps.length; iRep++) {
      var rep = reps[iRep];

      if (rep.type === 'plain') {
        this._populatePlaintextBodyNode(rootBodyNode, rep.content);
      }
      else if (rep.type === 'html') {
        var iframeShim = iframeShims.createAndInsertIframeForContent(
          rep.content, this.scrollContainer, rootBodyNode, null,
          'interactive', this.onHyperlinkClick.bind(this));
        var iframe = iframeShim.iframe;
        var bodyNode = iframe.contentDocument.body;
        this.iframeResizeHandler = iframeShim.resizeHandler;
        model.api.utils.linkifyHTML(iframe.contentDocument);
        this.htmlBodyNodes.push(bodyNode);

        if (body.checkForExternalImages(bodyNode))
          hasExternalImages = true;
        if (showEmbeddedImages)
          body.showEmbeddedImages(bodyNode, this.iframeResizeHandler);
      }

      if (iRep === 0) {
        // remove progress bar
        var progressNode = rootBodyNode.querySelector('progress');
        if (progressNode) {
          progressNode.parentNode.removeChild(progressNode);
        }
      }
    }

    // -- HTML-referenced Images
    var loadBar = domNode.getElementsByClassName('msg-reader-load-infobar')[0];
    if (body.embeddedImageCount && !body.embeddedImagesDownloaded) {
      loadBar.classList.remove('collapsed');
      loadBar.textContent =
        mozL10n.get('message-download-images',
                    { n: body.embeddedImageCount });
    }
    else if (hasExternalImages) {
      loadBar.classList.remove('collapsed');
      loadBar.textContent =
        mozL10n.get('message-show-external-images');
    }
    else {
      loadBar.classList.add('collapsed');
    }

    // -- Attachments (footer)
    // An attachment can be in 1 of 3 possible states for UI purposes:
    // - Not downloadable: We can't download this message because we wouldn't
    //   be able to do anything with it if we downloaded it.  Anything that's
    //   not a supported image type falls in this category.
    // - Downloadable, not downloaded: The user can trigger download of the
    //   attachment to DeviceStorage.
    // - Downloadable, downloaded: The attachment is already fully downloaded
    //   to DeviceStorage and we can trigger its display.
    var attachmentsContainer =
      domNode.getElementsByClassName('msg-attachments-container')[0];
    if (body.attachments && body.attachments.length) {
      // We need MimeMapper to help us determining the downloadable attachments
      // but it might not be loaded yet, so load before use it
      require(['shared/js/mime_mapper'], function(mapper) {
        if (!MimeMapper)
          MimeMapper = mapper;

        var attTemplate = msgAttachmentItemNode,
            filenameTemplate =
              attTemplate.getElementsByClassName('msg-attachment-filename')[0],
            filesizeTemplate =
              attTemplate.getElementsByClassName('msg-attachment-filesize')[0];
        for (var iAttach = 0; iAttach < body.attachments.length; iAttach++) {
          var attachment = body.attachments[iAttach], state;
          var extension = attachment.filename.split('.').pop();

          if (attachment.isDownloaded)
            state = 'downloaded';
          else if (MimeMapper.isSupportedType(attachment.mimetype) ||
                   MimeMapper.isSupportedExtension(extension))
            state = 'downloadable';
          else
            state = 'nodownload';
          attTemplate.setAttribute('state', state);
          filenameTemplate.textContent = attachment.filename;
          filesizeTemplate.textContent = prettyFileSize(
            attachment.sizeEstimateInBytes);

          var attachmentNode = attTemplate.cloneNode(true);
          attachmentsContainer.appendChild(attachmentNode);
          attachmentNode.getElementsByClassName('msg-attachment-download')[0]
            .addEventListener('click',
                              this.onDownloadAttachmentClick.bind(
                                this, attachmentNode, attachment));
          attachmentNode.getElementsByClassName('msg-attachment-view')[0]
            .addEventListener('click',
                              this.onViewAttachmentClick.bind(
                                this, attachmentNode, attachment));
          this.enableReply();
        }
      }.bind(this));
    }
    else {
      attachmentsContainer.classList.add('collapsed');
      this.enableReply();
    }
  },