Javascript  |  186行  |  5.38 KB

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var localStrings;

// Contents of lines that act as delimiters for multi-line values.
var DELIM_START = '---------- START ----------';
var DELIM_END = '---------- END ----------';

// Limit file size to 10 MiB to prevent hanging on accidental upload.
var MAX_FILE_SIZE = 10485760;

function getValueDivForButton(button) {
  return $(button.id.substr(0, button.id.length - 4));
}

function getButtonForValueDiv(valueDiv) {
  return $(valueDiv.id + '-btn');
}

function handleDragOver(e) {
  e.dataTransfer.dropEffect = 'copy';
  e.preventDefault();
}

function showError(fileName) {
  $('status').textContent = localStrings.getStringF('parseError', fileName);
}

/**
 * Toggles whether an item is collapsed or expanded.
 */
function changeCollapsedStatus() {
  var valueDiv = getValueDivForButton(this);
  if (valueDiv.parentNode.className == 'number-collapsed') {
    valueDiv.parentNode.className = 'number-expanded';
    this.textContent = localStrings.getString('collapseBtn');
  } else {
    valueDiv.parentNode.className = 'number-collapsed';
    this.textContent = localStrings.getString('expandBtn');
  }
}

/**
 * Collapses all log items.
 */
function collapseAll() {
  var valueDivs = document.getElementsByClassName('stat-value');
  for (var i = 0; i < valueDivs.length; i++) {
    var button = getButtonForValueDiv(valueDivs[i]);
    if (button && button.className != 'button-hidden') {
      button.textContent = localStrings.getString('expandBtn');
      valueDivs[i].parentNode.className = 'number-collapsed';
    }
  }
}

/**
 * Expands all log items.
 */
function expandAll() {
  var valueDivs = document.getElementsByClassName('stat-value');
  for (var i = 0; i < valueDivs.length; i++) {
    var button = getButtonForValueDiv(valueDivs[i]);
    if (button && button.className != 'button-hidden') {
      button.textContent = localStrings.getString('collapseBtn');
      valueDivs[i].parentNode.className = 'number-expanded';
    }
  }
}

/**
 * Collapse only those log items with multi-line values.
 */
function collapseMultiLineStrings() {
  var valueDivs = document.getElementsByClassName('stat-value');
  for (var i = 0; i < valueDivs.length; i++) {
    var button = getButtonForValueDiv(valueDivs[i]);
    button.onclick = changeCollapsedStatus;
    if (valueDivs[i].textContent.split('\n').length > 1) {
      button.className = '';
      button.textContent = localStrings.getString('expandBtn');
      valueDivs[i].parentNode.className = 'number-collapsed';
    } else {
      button.className = 'button-hidden';
      valueDivs[i].parentNode.className = 'number';
    }
  }
}

/**
 * Read in a log asynchronously, calling parseSystemLog if successful.
 * @param {Event} e The drop event from dropping a file dragged onto the page.
 */
function importLog(e) {
  var file = e.dataTransfer.files[0];
  if (file && file.size <= MAX_FILE_SIZE) {
    var reader = new FileReader();
    reader.onload = function() {
      if (parseSystemLog(this.result)) {
        // Reset table title and status
        $('tableTitle').textContent =
              localStrings.getStringF('logFileTableTitle', file.name);
        $('status').textContent = '';
      } else {
        showError(file.name);
      }
    };
    reader.readAsText(file);
  } else if (file) {
    showError(file.name);
  }
  e.preventDefault();
}

/**
 * Convert text-based log into list of name-value pairs.
 * @param {string} text The raw text of a log.
 * @return {boolean} True if the log was parsed successfully.
 */
function parseSystemLog(text) {
  var details = [];
  var lines = text.split('\n');
  for (var i = 0, len = lines.length; i < len; i++) {
    // Skip empty lines.
    if (!lines[i])
      continue;

    var delimiter = lines[i].indexOf('=');
    if (delimiter <= 0) {
      if (i == lines.length - 1)
         break;
      // If '=' is missing here, format is wrong.
      return false;
    }

    var name = lines[i].substring(0, delimiter);
    var value = '';
    // Set value if non-empty
    if (lines[i].length > delimiter + 1)
      value = lines[i].substring(delimiter + 1);

    // Delimiters are based on kMultilineIndicatorString, kMultilineStartString,
    // and kMultilineEndString in chrome/browser/feedback/feedback_data.cc.
    // If these change, we should check for both the old and new versions.
    if (value == '<multiline>') {
      // Skip start delimiter.
      if (i == len - 1 ||
          lines[++i].indexOf(DELIM_START) == -1)
        return false;

      ++i;
      value = '';
      // Append lines between start and end delimiters.
      while (i < len && lines[i] != DELIM_END)
        value += lines[i++] + '\n';

      // Remove trailing newline.
      if (value)
        value = value.substr(0, value.length - 1);
    }
    details.push({'statName': name, 'statValue': value});
  }

  templateData['details'] = details;
  i18nTemplate.process(document, templateData);
  jstProcess(new JsEvalContext(templateData), $('t'));

  collapseMultiLineStrings();
  return true;
}

document.addEventListener('DOMContentLoaded', function() {
  localStrings = new LocalStrings();

  $('collapseAll').onclick = collapseAll;
  $('expandAll').onclick = expandAll;

  var tp = $('t');
  tp.addEventListener('dragover', handleDragOver, false);
  tp.addEventListener('drop', importLog, false);

  collapseMultiLineStrings();
});