Home | History | Annotate | Download | only in app_launcher
      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 
      5 function $(id) {
      6   return document.getElementById(id);
      7 }
      8 
      9 // Returns the largest size icon, or a default icon, for the given |app|.
     10 function 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 
     24 function 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|.
     30 function 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.
     51 var completeList = [];
     52 
     53 // A filtered list of apps we actually want to show.
     54 var appList = [];
     55 
     56 // The index of an app in |appList| that should be highlighted.
     57 var selectedIndex = 0;
     58 
     59 function 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.
     72 function 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.
     90 var didSetExplicitWidth = false;
     91 
     92 function 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.
    104 function onSearchInput() {
    105   var filter = $('search').value;
    106   adjustWidthIfNeeded(filter);
    107   rebuildAppList(filter);
    108   reloadAppDisplay();
    109 }
    110 
    111 function compare(a, b) {
    112   return (a > b) ? 1 : (a == b ? 0 : -1);
    113 }
    114 
    115 function compareByName(app1, app2) {
    116   return compare(app1.name.toLowerCase(), app2.name.toLowerCase());
    117 }
    118 
    119 // Changes the selected app in the list.
    120 function 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 
    139 var 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.
    153 window.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.
    184 document.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