1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5var localStrings; 6 7// Contents of lines that act as delimiters for multi-line values. 8var DELIM_START = '---------- START ----------'; 9var DELIM_END = '---------- END ----------'; 10 11// Limit file size to 10 MiB to prevent hanging on accidental upload. 12var MAX_FILE_SIZE = 10485760; 13 14function getValueDivForButton(button) { 15 return $(button.id.substr(0, button.id.length - 4)); 16} 17 18function getButtonForValueDiv(valueDiv) { 19 return $(valueDiv.id + '-btn'); 20} 21 22function handleDragOver(e) { 23 e.dataTransfer.dropEffect = 'copy'; 24 e.preventDefault(); 25} 26 27function showError(fileName) { 28 $('status').textContent = localStrings.getStringF('parseError', fileName); 29} 30 31/** 32 * Toggles whether an item is collapsed or expanded. 33 */ 34function changeCollapsedStatus() { 35 var valueDiv = getValueDivForButton(this); 36 if (valueDiv.parentNode.className == 'number-collapsed') { 37 valueDiv.parentNode.className = 'number-expanded'; 38 this.textContent = localStrings.getString('collapseBtn'); 39 } else { 40 valueDiv.parentNode.className = 'number-collapsed'; 41 this.textContent = localStrings.getString('expandBtn'); 42 } 43} 44 45/** 46 * Collapses all log items. 47 */ 48function collapseAll() { 49 var valueDivs = document.getElementsByClassName('stat-value'); 50 for (var i = 0; i < valueDivs.length; i++) { 51 var button = getButtonForValueDiv(valueDivs[i]); 52 if (button && button.className != 'button-hidden') { 53 button.textContent = localStrings.getString('expandBtn'); 54 valueDivs[i].parentNode.className = 'number-collapsed'; 55 } 56 } 57} 58 59/** 60 * Expands all log items. 61 */ 62function expandAll() { 63 var valueDivs = document.getElementsByClassName('stat-value'); 64 for (var i = 0; i < valueDivs.length; i++) { 65 var button = getButtonForValueDiv(valueDivs[i]); 66 if (button && button.className != 'button-hidden') { 67 button.textContent = localStrings.getString('collapseBtn'); 68 valueDivs[i].parentNode.className = 'number-expanded'; 69 } 70 } 71} 72 73/** 74 * Collapse only those log items with multi-line values. 75 */ 76function collapseMultiLineStrings() { 77 var valueDivs = document.getElementsByClassName('stat-value'); 78 for (var i = 0; i < valueDivs.length; i++) { 79 var button = getButtonForValueDiv(valueDivs[i]); 80 button.onclick = changeCollapsedStatus; 81 if (valueDivs[i].textContent.split('\n').length > 1) { 82 button.className = ''; 83 button.textContent = localStrings.getString('expandBtn'); 84 valueDivs[i].parentNode.className = 'number-collapsed'; 85 } else { 86 button.className = 'button-hidden'; 87 valueDivs[i].parentNode.className = 'number'; 88 } 89 } 90} 91 92/** 93 * Read in a log asynchronously, calling parseSystemLog if successful. 94 * @param {Event} e The drop event from dropping a file dragged onto the page. 95 */ 96function importLog(e) { 97 var file = e.dataTransfer.files[0]; 98 if (file && file.size <= MAX_FILE_SIZE) { 99 var reader = new FileReader(); 100 reader.onload = function() { 101 if (parseSystemLog(this.result)) { 102 // Reset table title and status 103 $('tableTitle').textContent = 104 localStrings.getStringF('logFileTableTitle', file.name); 105 $('status').textContent = ''; 106 } else { 107 showError(file.name); 108 } 109 }; 110 reader.readAsText(file); 111 } else if (file) { 112 showError(file.name); 113 } 114 e.preventDefault(); 115} 116 117/** 118 * Convert text-based log into list of name-value pairs. 119 * @param {string} text The raw text of a log. 120 * @return {boolean} True if the log was parsed successfully. 121 */ 122function parseSystemLog(text) { 123 var details = []; 124 var lines = text.split('\n'); 125 for (var i = 0, len = lines.length; i < len; i++) { 126 // Skip empty lines. 127 if (!lines[i]) 128 continue; 129 130 var delimiter = lines[i].indexOf('='); 131 if (delimiter <= 0) { 132 if (i == lines.length - 1) 133 break; 134 // If '=' is missing here, format is wrong. 135 return false; 136 } 137 138 var name = lines[i].substring(0, delimiter); 139 var value = ''; 140 // Set value if non-empty 141 if (lines[i].length > delimiter + 1) 142 value = lines[i].substring(delimiter + 1); 143 144 // Delimiters are based on kMultilineIndicatorString, kMultilineStartString, 145 // and kMultilineEndString in chrome/browser/feedback/feedback_data.cc. 146 // If these change, we should check for both the old and new versions. 147 if (value == '<multiline>') { 148 // Skip start delimiter. 149 if (i == len - 1 || 150 lines[++i].indexOf(DELIM_START) == -1) 151 return false; 152 153 ++i; 154 value = ''; 155 // Append lines between start and end delimiters. 156 while (i < len && lines[i] != DELIM_END) 157 value += lines[i++] + '\n'; 158 159 // Remove trailing newline. 160 if (value) 161 value = value.substr(0, value.length - 1); 162 } 163 details.push({'statName': name, 'statValue': value}); 164 } 165 166 templateData['details'] = details; 167 i18nTemplate.process(document, templateData); 168 jstProcess(new JsEvalContext(templateData), $('t')); 169 170 collapseMultiLineStrings(); 171 return true; 172} 173 174document.addEventListener('DOMContentLoaded', function() { 175 localStrings = new LocalStrings(); 176 177 $('collapseAll').onclick = collapseAll; 178 $('expandAll').onclick = expandAll; 179 180 var tp = $('t'); 181 tp.addEventListener('dragover', handleDragOver, false); 182 tp.addEventListener('drop', importLog, false); 183 184 collapseMultiLineStrings(); 185}); 186