1// Copyright (c) 2012 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 5/** 6 * Javascript for suggestions_internals.html, served from 7 * chrome://suggestions-internals/. This is used to debug suggestions ranking. 8 * When loaded, the page will show the current set of suggestions, along with a 9 * large set of information (e.g. all the signals that were taken into 10 * consideration for deciding which pages were selected to be shown to the user) 11 * that will aid in debugging and optimizing the algorithms. 12 */ 13cr.define('suggestionsInternals', function() { 14 'use strict'; 15 16 /** 17 * Register our event handlers. 18 */ 19 function initialize() { 20 $('suggestions-form').addEventListener('submit', onRefreshClicked); 21 $('show-discarded').addEventListener('change', refresh); 22 refresh(); 23 } 24 25 /** 26 * Called when the 'Refresh' button is clicked. Reloads the suggestions data. 27 */ 28 function onRefreshClicked(event) { 29 refresh(); 30 event.preventDefault(); 31 } 32 33 /** 34 * Reloads the suggestions data by sending a 'getSuggestions' message to 35 * Chrome. The C++ code is then expected to call 'setSuggestions' when there 36 * are suggestions ready. 37 */ 38 function refresh() { 39 chrome.send('getSuggestions'); 40 } 41 42 /** 43 * A list of columns that we do not want to display. 44 * @type {Array.<string>} 45 * @const 46 */ 47 var IGNORED_COLUMNS = [ 48 'direction' 49 ]; 50 51 /** 52 * A list specifying the name of the first columns to be displayed. If 53 * present, they will be displayed in this order, followed by the remaining 54 * columns. 55 * @type {Array.<string>} 56 * @const 57 */ 58 var PREFERRED_COLUMN_ORDER = [ 59 'title', 60 'url', 61 'score' 62 ]; 63 64 function setBooleanColumn(column, value) { 65 if (value) { 66 column.innerText = 'Y'; 67 column.classList.add('boolean-property-true'); 68 } else { 69 column.innerText = 'N'; 70 column.classList.add('boolean-property-false'); 71 } 72 } 73 74 /** 75 * Called by Chrome code, with a ranked list of suggestions. The columns 76 * to be displayed are calculated automatically from the properties of the 77 * elements in the list, such that all properties have a column. 78 */ 79 function setSuggestions(list) { 80 // Build a list of all the columns that will be displayed. 81 var columns = []; 82 list.forEach(function(entry) { 83 for (var column in entry) { 84 if (typeof entry[column] == 'object') { 85 // Expand one level deep 86 for (var subColumn in entry[column]) { 87 var path = column + '.' + subColumn; 88 if (columns.indexOf(path) < 0) 89 columns.push(path); 90 } 91 } else if (columns.indexOf(column) < 0) { 92 columns.push(column); 93 } 94 } 95 }); 96 97 // Remove columns that we don't want to display. 98 columns = columns.filter(function(column) { 99 return IGNORED_COLUMNS.indexOf(column) < 0; 100 }); 101 102 // Move the preferred columns to the start of the column list. 103 for (var i = PREFERRED_COLUMN_ORDER.length - 1; i >= 0; i--) { 104 var index = columns.indexOf(PREFERRED_COLUMN_ORDER[i]); 105 if (index >= 0) 106 columns.unshift(columns.splice(index, 1)[0]); 107 } 108 109 // Special columns. 110 columns.unshift('favicon'); 111 columns.unshift('screenshot'); 112 columns.unshift('rank'); 113 114 // Erase whatever is currently being displayed. 115 var output = $('suggestions-debug-text'); 116 output.innerHTML = ''; 117 118 // Create the container table and add the header row. 119 var table = document.createElement('table'); 120 table.className = 'suggestions-debug-table'; 121 var header = document.createElement('tr'); 122 columns.forEach(function(entry) { 123 var column = document.createElement('th'); 124 column.innerText = entry; 125 header.appendChild(column); 126 }); 127 table.appendChild(header); 128 129 // Add all the suggestions to the table. 130 var rank = 1; 131 list.forEach(function(entry) { 132 var row = document.createElement('tr'); 133 columns.forEach(function(columnName) { 134 var column = document.createElement('td'); 135 // Expand the path and find the data if it's there. 136 var path = columnName.split('.'); 137 var data = entry; 138 for (var i = 0; i < path.length; ++i) { 139 if (data && data.hasOwnProperty(path[i])) 140 data = data[path[i]]; 141 else 142 data = undefined; 143 } 144 // Only add the column if the current suggestion has this property 145 // (otherwise, leave the cell empty). 146 if (typeof(data) != 'undefined') { 147 if (typeof(data) == 'boolean') { 148 setBooleanColumn(column, data); 149 } else if (/^https?:\/\/.+$/.test(data)) { 150 // If the text is a URL, make it an anchor element. 151 var anchor = document.createElement('a'); 152 anchor.href = data; 153 anchor.innerText = data; 154 column.appendChild(anchor); 155 } else { 156 column.innerText = data; 157 } 158 } else if (columnName == 'rank') { 159 column.innerText = rank++; 160 } else if (columnName == 'screenshot') { 161 var thumbnailUrl = 'chrome://thumb/' + entry.url; 162 var img = document.createElement('img'); 163 img.onload = function() { setBooleanColumn(column, true); } 164 img.onerror = function() { setBooleanColumn(column, false); } 165 img.src = thumbnailUrl; 166 } else if (columnName == 'favicon') { 167 var faviconUrl = 'chrome://favicon/size/16@1x/' + entry.url; 168 column.style.backgroundImage = url(faviconUrl); 169 column.style.backgroundRepeat = 'no-repeat'; 170 column.style.backgroundPosition = 'center center'; 171 } 172 row.appendChild(column); 173 }); 174 table.appendChild(row); 175 }); 176 177 output.appendChild(table); 178 } 179 180 return { 181 initialize: initialize, 182 setSuggestions: setSuggestions 183 }; 184}); 185 186document.addEventListener('DOMContentLoaded', suggestionsInternals.initialize); 187