1// Copyright (c) 2011 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 5function $(id) { 6 return document.getElementById(id); 7} 8 9// Returns the largest size icon, or a default icon, for the given |app|. 10function getIconURL(app) { 11 if (!app.icons || app.icons.length == 0) { 12 return chrome.extension.getURL('icon.png'); 13 } 14 var largest = {size:0}; 15 for (var i = 0; i < app.icons.length; i++) { 16 var icon = app.icons[i]; 17 if (icon.size > largest.size) { 18 largest = icon; 19 } 20 } 21 return largest.url; 22} 23 24function launchApp(id) { 25 chrome.management.launchApp(id); 26 window.close(); // Only needed on OSX because of crbug.com/63594 27} 28 29// Adds DOM nodes for |app| into |appsDiv|. 30function addApp(appsDiv, app, selected) { 31 var div = document.createElement('div'); 32 div.className = 'app' + (selected ? ' app_selected' : ''); 33 34 div.onclick = function() { 35 launchApp(app.id); 36 }; 37 38 var img = document.createElement('img'); 39 img.src = getIconURL(app); 40 div.appendChild(img); 41 42 var title = document.createElement('span'); 43 title.className = 'app_title'; 44 title.innerText = app.name; 45 div.appendChild(title); 46 47 appsDiv.appendChild(div); 48} 49 50// The list of all apps & extensions. 51var completeList = []; 52 53// A filtered list of apps we actually want to show. 54var appList = []; 55 56// The index of an app in |appList| that should be highlighted. 57var selectedIndex = 0; 58 59function reloadAppDisplay() { 60 var appsDiv = $('apps'); 61 62 // Empty the current content. 63 appsDiv.innerHTML = ''; 64 65 for (var i = 0; i < appList.length; i++) { 66 var item = appList[i]; 67 addApp(appsDiv, item, i == selectedIndex); 68 } 69} 70 71// Puts only enabled apps from completeList into appList. 72function rebuildAppList(filter) { 73 selectedIndex = 0; 74 appList = []; 75 for (var i = 0; i < completeList.length; i++){ 76 var item = completeList[i]; 77 // Skip extensions and disabled apps. 78 if (!item.isApp || !item.enabled) { 79 continue; 80 } 81 if (filter && item.name.toLowerCase().search(filter) < 0) { 82 continue; 83 } 84 appList.push(item); 85 } 86} 87 88// In order to keep the popup bubble from shrinking as your search narrows the 89// list of apps shown, we set an explicit width on the outermost div. 90var didSetExplicitWidth = false; 91 92function adjustWidthIfNeeded(filter) { 93 if (filter.length > 0 && !didSetExplicitWidth) { 94 // Set an explicit width, correcting for any scroll bar present. 95 var outer = $('outer'); 96 var correction = window.innerWidth - document.documentElement.clientWidth; 97 var width = outer.offsetWidth; 98 $('spacer_dummy').style.width = width + correction + 'px'; 99 didSetExplicitWidth = true; 100 } 101} 102 103// Shows the list of apps based on the search box contents. 104function onSearchInput() { 105 var filter = $('search').value; 106 adjustWidthIfNeeded(filter); 107 rebuildAppList(filter); 108 reloadAppDisplay(); 109} 110 111function compare(a, b) { 112 return (a > b) ? 1 : (a == b ? 0 : -1); 113} 114 115function compareByName(app1, app2) { 116 return compare(app1.name.toLowerCase(), app2.name.toLowerCase()); 117} 118 119// Changes the selected app in the list. 120function changeSelection(newIndex) { 121 if (newIndex >= 0 && newIndex <= appList.length - 1) { 122 selectedIndex = newIndex; 123 reloadAppDisplay(); 124 125 var selected = document.getElementsByClassName('app_selected')[0]; 126 var rect = selected.getBoundingClientRect(); 127 if (newIndex == 0) { 128 window.scrollTo(0, 0); 129 } else if (newIndex == appList.length - 1) { 130 window.scrollTo(0, document.height); 131 } else if (rect.top < 0) { 132 window.scrollBy(0, rect.top); 133 } else if (rect.bottom > innerHeight) { 134 window.scrollBy(0, rect.bottom - innerHeight); 135 } 136 } 137} 138 139var keys = { 140 ENTER : 13, 141 ESCAPE : 27, 142 END : 35, 143 HOME : 36, 144 LEFT : 37, 145 UP : 38, 146 RIGHT : 39, 147 DOWN : 40 148}; 149 150// Set up a key event handler that handles moving the selected app up/down, 151// hitting enter to launch the selected app, as well as auto-focusing the 152// search box as soon as you start typing. 153window.onkeydown = function(event) { 154 switch (event.keyCode) { 155 case keys.DOWN: 156 changeSelection(selectedIndex + 1); 157 break; 158 case keys.UP: 159 changeSelection(selectedIndex - 1); 160 break; 161 case keys.HOME: 162 changeSelection(0); 163 break; 164 case keys.END: 165 changeSelection(appList.length - 1); 166 break; 167 case keys.ENTER: 168 var app = appList[selectedIndex]; 169 if (app) { 170 launchApp(app.id); 171 } 172 break; 173 default: 174 // Focus the search box and return true so you can just start typing even 175 // when the search box isn't focused. 176 $('search').focus(); 177 return true; 178 } 179 return false; 180}; 181 182 183// Initalize the popup window. 184document.addEventListener('DOMContentLoaded', function () { 185 chrome.management.getAll(function(info) { 186 var appCount = 0; 187 for (var i = 0; i < info.length; i++) { 188 if (info[i].isApp) { 189 appCount++; 190 } 191 } 192 if (appCount == 0) { 193 $('search').style.display = 'none'; 194 $('appstore_link').style.display = ''; 195 return; 196 } 197 completeList = info.sort(compareByName); 198 onSearchInput(); 199 }); 200 201 $('search').addEventListener('input', onSearchInput); 202 203 // Opens the webstore in a new tab. 204 document.querySelector('#appstore_link button').addEventListener('click', 205 function () { 206 chrome.tabs.create({ 207 'url':'https://chrome.google.com/webstore', 208 'selected':true 209 }); 210 window.close(); 211 }); 212}); 213 214