apps.js revision 513209b27ff55e2841eac0e4120199c23acce758
1// Copyright (c) 2010 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 MAX_APPS_PER_ROW = []; 6MAX_APPS_PER_ROW[LayoutMode.SMALL] = 4; 7MAX_APPS_PER_ROW[LayoutMode.NORMAL] = 6; 8 9function getAppsCallback(data) { 10 logEvent('received apps'); 11 var appsSection = $('apps'); 12 var appsSectionContent = $('apps-content'); 13 var appsMiniview = appsSection.getElementsByClassName('miniview')[0]; 14 var appsPromo = $('apps-promo'); 15 var webStoreEntry; 16 17 appsMiniview.textContent = ''; 18 appsSectionContent.textContent = ''; 19 20 data.apps.sort(function(a,b) { 21 return a.app_launch_index - b.app_launch_index 22 }); 23 24 clearClosedMenu(apps.menu); 25 if (data.apps.length == 0 && !data.showLauncher) { 26 appsSection.classList.add('disabled'); 27 layoutSections(); 28 } else { 29 data.apps.forEach(function(app) { 30 appsSectionContent.appendChild(apps.createElement(app)); 31 }); 32 33 webStoreEntry = apps.createWebStoreElement(); 34 appsSectionContent.appendChild(webStoreEntry); 35 36 data.apps.slice(0, MAX_MINIVIEW_ITEMS).forEach(function(app) { 37 appsMiniview.appendChild(apps.createMiniviewElement(app)); 38 addClosedMenuEntryWithLink(apps.menu, apps.createClosedMenuElement(app)); 39 }); 40 41 if (!(shownSections & MINIMIZED_APPS)) { 42 appsSection.classList.remove('disabled'); 43 } 44 } 45 addClosedMenuFooter(apps.menu, 'apps', MINIMIZED_APPS, Section.APPS); 46 47 apps.loaded = true; 48 if (data.showPromo) 49 document.documentElement.classList.add('apps-promo-visible'); 50 else 51 document.documentElement.classList.remove('apps-promo-visible'); 52 maybeDoneLoading(); 53 54 if (data.apps.length > 0 && isDoneLoading()) { 55 if (!data.showPromo && data.apps.length >= MAX_APPS_PER_ROW[layoutMode]) 56 webStoreEntry.classList.add('loner'); 57 else 58 webStoreEntry.classList.remove('loner'); 59 60 updateMiniviewClipping(appsMiniview); 61 layoutSections(); 62 } 63} 64 65function appsPrefChangeCallback(data) { 66 // Currently the only pref that is watched is the launch type. 67 data.apps.forEach(function(app) { 68 var appLink = document.querySelector('.app a[app-id=' + app['id'] + ']'); 69 if (appLink) 70 appLink.setAttribute('launch-type', app['launch_type']); 71 }); 72} 73 74var apps = (function() { 75 76 function createElement(app) { 77 var div = document.createElement('div'); 78 div.className = 'app'; 79 80 var a = div.appendChild(document.createElement('a')); 81 a.setAttribute('app-id', app['id']); 82 a.setAttribute('launch-type', app['launch_type']); 83 a.xtitle = a.textContent = app['name']; 84 a.href = app['launch_url']; 85 86 return div; 87 } 88 89 function createContextMenu(app) { 90 var menu = new cr.ui.Menu; 91 var button = document.createElement(button); 92 } 93 94 function launchApp(appId) { 95 var appsSection = $('apps'); 96 var expanded = !appsSection.classList.contains('hidden'); 97 var element = document.querySelector( 98 (expanded ? '.maxiview' : '.miniview') + ' a[app-id=' + appId + ']'); 99 100 // TODO(arv): Handle zoom? 101 var rect = element.getBoundingClientRect(); 102 var cs = getComputedStyle(element); 103 var size = cs.backgroundSize.split(/\s+/); // background-size has the 104 // format '123px 456px'. 105 106 var width = parseInt(size[0], 10); 107 var height = parseInt(size[1], 10); 108 109 var top, left; 110 if (expanded) { 111 // We are using background-position-x 50%. 112 top = rect.top + parseInt(cs.backgroundPositionY, 10); 113 left = rect.left + ((rect.width - width) >> 1); // Integer divide by 2. 114 115 } else { 116 // We are using background-position-y 50%. 117 top = rect.top + ((rect.height - width) >> 1); // Integer divide by 2. 118 if (getComputedStyle(element).direction == 'rtl') 119 left = rect.left + rect.width - width; 120 else 121 left = rect.left; 122 } 123 124 chrome.send('launchApp', [appId, 125 String(left), String(top), 126 String(width), String(height)]); 127 } 128 129 /** 130 * @this {!HTMLAnchorElement} 131 */ 132 function handleClick(e) { 133 var appId = e.currentTarget.getAttribute('app-id'); 134 launchApp(appId); 135 return false; 136 } 137 138 // Keep in sync with LaunchType in extension_prefs.h 139 var LaunchType = { 140 LAUNCH_PINNED: 0, 141 LAUNCH_REGULAR: 1, 142 LAUNCH_FULLSCREEN: 2 143 }; 144 145 // Keep in sync with LaunchContainer in extension.h 146 var LaunchContainer = { 147 LAUNCH_WINDOW: 0, 148 LAUNCH_PANEL: 1, 149 LAUNCH_TAB: 2 150 }; 151 152 var currentApp; 153 154 function addContextMenu(el, app) { 155 el.addEventListener('contextmenu', cr.ui.contextMenuHandler); 156 el.addEventListener('keydown', cr.ui.contextMenuHandler); 157 el.addEventListener('keyup', cr.ui.contextMenuHandler); 158 159 Object.defineProperty(el, 'contextMenu', { 160 get: function() { 161 currentApp = app; 162 163 $('apps-launch-command').label = app['name']; 164 $('apps-options-command').canExecuteChange(); 165 166 var appLinkSel = '.app a[app-id=' + app['id'] + ']'; 167 var launchType = 168 el.querySelector(appLinkSel).getAttribute('launch-type'); 169 170 var launchContainer = app['launch_container']; 171 var isPanel = launchContainer == LaunchContainer.LAUNCH_PANEL; 172 173 // Update the commands related to the launch type. 174 var launchTypeIds = ['apps-launch-type-pinned', 175 'apps-launch-type-regular', 176 'apps-launch-type-fullscreen']; 177 launchTypeIds.forEach(function(id) { 178 var command = $(id); 179 command.disabled = isPanel; 180 command.checked = !isPanel && 181 launchType == command.getAttribute('launch-type'); 182 }); 183 184 return $('app-context-menu'); 185 } 186 }); 187 } 188 189 document.addEventListener('command', function(e) { 190 if (!currentApp) 191 return; 192 193 var commandId = e.command.id; 194 switch (commandId) { 195 case 'apps-options-command': 196 window.location = currentApp['options_url']; 197 break; 198 case 'apps-launch-command': 199 launchApp(currentApp['id']); 200 break; 201 case 'apps-uninstall-command': 202 chrome.send('uninstallApp', [currentApp['id']]); 203 break; 204 case 'apps-launch-type-pinned': 205 case 'apps-launch-type-regular': 206 case 'apps-launch-type-fullscreen': 207 chrome.send('setLaunchType', 208 [currentApp['id'], e.command.getAttribute('launch-type')]); 209 break; 210 } 211 }); 212 213 document.addEventListener('canExecute', function(e) { 214 switch (e.command.id) { 215 case 'apps-options-command': 216 e.canExecute = currentApp && currentApp['options_url']; 217 break; 218 case 'apps-launch-command': 219 case 'apps-uninstall-command': 220 e.canExecute = true; 221 break; 222 } 223 }); 224 225 return { 226 loaded: false, 227 228 menu: $('apps-menu'), 229 230 createElement: function(app) { 231 var div = createElement(app); 232 var a = div.firstChild; 233 234 a.onclick = handleClick; 235 a.style.backgroundImage = url(app['icon_big']); 236 if (hashParams['app-id'] == app['id']) { 237 div.setAttribute('new', 'new'); 238 // Delay changing the attribute a bit to let the page settle down a bit. 239 setTimeout(function() { 240 // Make sure the new icon is scrolled into view. 241 document.body.scrollTop = document.body.scrollHeight; 242 243 // This will trigger the 'bounce' animation defined in apps.css. 244 div.setAttribute('new', 'installed'); 245 }, 500); 246 div.addEventListener('webkitAnimationEnd', function(e) { 247 div.removeAttribute('new'); 248 249 // If we get new data (eg because something installs in another tab, 250 // or because we uninstall something here), don't run the install 251 // animation again. 252 document.documentElement.setAttribute("install-animation-enabled", 253 "false"); 254 }); 255 } 256 257 var settingsButton = div.appendChild(new cr.ui.ContextMenuButton); 258 settingsButton.className = 'app-settings'; 259 settingsButton.title = localStrings.getString('appsettings'); 260 261 addContextMenu(div, app); 262 263 return div; 264 }, 265 266 createMiniviewElement: function(app) { 267 var span = document.createElement('span'); 268 var a = span.appendChild(document.createElement('a')); 269 270 a.setAttribute('app-id', app['id']); 271 a.textContent = app['name']; 272 a.href = app['launch_url']; 273 a.onclick = handleClick; 274 a.style.backgroundImage = url(app['icon_small']); 275 a.className = 'item'; 276 span.appendChild(a); 277 278 addContextMenu(span, app); 279 280 return span; 281 }, 282 283 createClosedMenuElement: function(app) { 284 var a = document.createElement('a'); 285 a.setAttribute('app-id', app['id']); 286 a.textContent = app['name']; 287 a.href = app['launch_url']; 288 a.onclick = handleClick; 289 a.style.backgroundImage = url(app['icon_small']); 290 a.className = 'item'; 291 return a; 292 }, 293 294 createWebStoreElement: function() { 295 var elm = createElement({ 296 'id': 'web-store-entry', 297 'name': localStrings.getString('web_store_title'), 298 'launch_url': localStrings.getString('web_store_url') 299 }); 300 elm.setAttribute('app-id', 'web-store-entry'); 301 return elm; 302 } 303 }; 304})(); 305