MsgVisitor.prototype.message = function()

in static/js/zamboni/validator.js [246:346]


  MsgVisitor.prototype.message = function (msg, options) {
    if (!this.filterMessage(msg)) {
      return;
    }

    if (typeof this.msgSet[msg.uid] !== 'undefined') {
      return;
    }
    this.msgSet[msg.uid] = true;

    let tier = this.getTier(msg.tier, options),
      msgDiv = $('<div class="msg"><h5></h5></div>'),
      effectiveType = this.getMsgType(msg),
      prefix = effectiveType == 'error' ? gettext('Error') : gettext('Warning');

    tier.tallyMsgType(effectiveType);
    msgDiv.attr('id', 'v-msg-' + msg.uid);
    msgDiv.addClass('msg-' + effectiveType);

    // The "message" and "description" properties are escaped and linkified
    // before we receive them.
    $('h5', msgDiv).html(msg.message); // Sanitized HTML value.

    // The validator returns the "description" as either string, or
    // arrays of strings. We turn it into arrays when sanitizing.
    $.each(msg.description, function (i, val) {
      let $desc = $('<p>').html(val); // Sanitized HTML value.
      if (i === 0) {
        $desc.prepend(format('<strong>{0}:</strong> ', prefix));
      }
      msgDiv.append($desc);
    });

    if (msg.file) {
      let file = msg.file;
      if (typeof file !== 'string') {
        // For sub-packages, this will be a list of archive paths and
        // a final file path, which we need to turn into a string.
        //   ['foo.xpi', 'chrome/thing.jar', 'content/file.js']
        file = file.join('/');
      }
      let $link;

      if (this.fileURL && this.fileID) {
        let url = this.fileURL + '?path=' + file;
        if (msg.line) {
          url += '#L' + msg.line;
        }
        $link = $('<a>', {
          href: url,
          text: file,
          target: 'file-viewer-' + this.fileID,
        });
      } else {
        // There's no file browse URL for bare file uploads, so
        // just display a path without a link to the sources.
        $link = $('<span>', { text: file });
      }

      let $context = $('<div class="context">').append(
        $('<div class="file">').append($link),
      );

      if (msg.context) {
        let $code = $('<div class="code"></div>');
        let $lines = $('<div class="lines"></div>');
        let $innerCode = $('<div class="inner-code"></div>');

        $code.append($lines, $innerCode);

        // The line number in the message refers to the middle
        // line of the context, so adjust accordingly.
        let offset = Math.floor(msg.context.length / 2);
        msg.context = formatCodeIndentation(msg.context);
        $.each(msg.context, function (idx, code) {
          if (code != null) {
            $lines.append($('<div>', { text: msg.line + idx - offset }));
            $innerCode.append($('<div>', { text: code }));
          }
        });
        $context.append($code);
      } else if (msg.line && typeof msg.column !== 'undefined') {
        // Normally, the line number would be displayed with the
        // context. If we have no context, display it with the
        // filename.
        $link.text(
          format(gettext('{0} line {1} column {2}'), [
            file,
            msg.line,
            msg.column,
          ]),
        );
      } else if (msg.line) {
        $link.text(format(gettext('{0} line {1}'), [file, msg.line]));
      }

      msgDiv.append($context);
    }

    $('.tier-results', tier.$dom).append(msgDiv);
  };