| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 cr.define('ntp', function() { | 5 cr.define('ntp', function() { |
| 6 'use strict'; | 6 'use strict'; |
| 7 | 7 |
| 8 var Tile = ntp.Tile; | 8 var Tile = ntp.Tile; |
| 9 var TilePage = ntp.TilePage; | 9 var TilePage = ntp.TilePage; |
| 10 var APP_LAUNCH = ntp.APP_LAUNCH; | 10 var APP_LAUNCH = ntp.APP_LAUNCH; |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 } | 113 } |
| 114 }, | 114 }, |
| 115 | 115 |
| 116 /** | 116 /** |
| 117 * Does all the necessary setup to show the menu for the given app. | 117 * Does all the necessary setup to show the menu for the given app. |
| 118 * @param {App} app The App object that will be showing a context menu. | 118 * @param {App} app The App object that will be showing a context menu. |
| 119 */ | 119 */ |
| 120 setupForApp: function(app) { | 120 setupForApp: function(app) { |
| 121 this.app_ = app; | 121 this.app_ = app; |
| 122 | 122 |
| 123 this.launch_.textContent = app.appData.title; | 123 this.launch_.textContent = app.data.title; |
| 124 | 124 |
| 125 this.forAllLaunchTypes_(function(launchTypeButton, id) { | 125 this.forAllLaunchTypes_(function(launchTypeButton, id) { |
| 126 launchTypeButton.disabled = false; | 126 launchTypeButton.disabled = false; |
| 127 launchTypeButton.checked = app.appData.launch_type == id; | 127 launchTypeButton.checked = app.data.launch_type == id; |
| 128 }); | 128 }); |
| 129 | 129 |
| 130 this.options_.disabled = !app.appData.optionsUrl || !app.appData.enabled; | 130 this.options_.disabled = !app.data.optionsUrl || !app.data.enabled; |
| 131 this.details_.disabled = !app.appData.detailsUrl; | 131 this.details_.disabled = !app.data.detailsUrl; |
| 132 this.uninstall_.disabled = !app.appData.mayDisable; | 132 this.uninstall_.disabled = !app.data.mayDisable; |
| 133 | 133 |
| 134 this.disableNotifications_.hidden = true; | 134 this.disableNotifications_.hidden = true; |
| 135 var notificationsDisabled = app.appData.notifications_disabled; | 135 var notificationsDisabled = app.data.notifications_disabled; |
| 136 if (typeof notificationsDisabled != 'undefined') { | 136 if (typeof notificationsDisabled != 'undefined') { |
| 137 this.disableNotifications_.hidden = false; | 137 this.disableNotifications_.hidden = false; |
| 138 this.disableNotifications_.checked = notificationsDisabled; | 138 this.disableNotifications_.checked = notificationsDisabled; |
| 139 } | 139 } |
| 140 }, | 140 }, |
| 141 | 141 |
| 142 /** | 142 /** |
| 143 * Handlers for menu item activation. | 143 * Handlers for menu item activation. |
| 144 * @param {Event} e The activation event. | 144 * @param {Event} e The activation event. |
| 145 * @private | 145 * @private |
| 146 */ | 146 */ |
| 147 onLaunch_: function(e) { | 147 onLaunch_: function(e) { |
| 148 chrome.send('launchApp', [this.app_.appId, APP_LAUNCH.NTP_APPS_MENU]); | 148 chrome.send('launchApp', [this.app_.appId, APP_LAUNCH.NTP_APPS_MENU]); |
| 149 }, | 149 }, |
| 150 onLaunchTypeChanged_: function(e) { | 150 onLaunchTypeChanged_: function(e) { |
| 151 var pressed = e.currentTarget; | 151 var pressed = e.currentTarget; |
| 152 var app = this.app_; | 152 var app = this.app_; |
| 153 this.forAllLaunchTypes_(function(launchTypeButton, id) { | 153 this.forAllLaunchTypes_(function(launchTypeButton, id) { |
| 154 if (launchTypeButton == pressed) { | 154 if (launchTypeButton == pressed) { |
| 155 chrome.send('setLaunchType', [app.appId, id]); | 155 chrome.send('setLaunchType', [app.appId, id]); |
| 156 // Manually update the launch type. We will only get | 156 // Manually update the launch type. We will only get |
| 157 // appsPrefChangeCallback calls after changes to other NTP instances. | 157 // appsPrefChangeCallback calls after changes to other NTP instances. |
| 158 app.appData.launch_type = id; | 158 app.data.launch_type = id; |
| 159 } | 159 } |
| 160 }); | 160 }); |
| 161 }, | 161 }, |
| 162 onShowOptions_: function(e) { | 162 onShowOptions_: function(e) { |
| 163 window.location = this.app_.appData.optionsUrl; | 163 window.location = this.app_.data.optionsUrl; |
| 164 }, | 164 }, |
| 165 onShowDetails_: function(e) { | 165 onShowDetails_: function(e) { |
| 166 var url = this.app_.appData.detailsUrl; | 166 var url = this.app_.data.detailsUrl; |
| 167 url = appendParam(url, 'utm_source', 'chrome-ntp-launcher'); | 167 url = appendParam(url, 'utm_source', 'chrome-ntp-launcher'); |
| 168 window.location = url; | 168 window.location = url; |
| 169 }, | 169 }, |
| 170 onDisableNotifications_: function(e) { | 170 onDisableNotifications_: function(e) { |
| 171 var app = this.app_; | 171 var app = this.app_; |
| 172 app.removeBubble(); | 172 app.removeBubble(); |
| 173 // Toggle the current disable setting. | 173 // Toggle the current disable setting. |
| 174 var newSetting = !this.disableNotifications_.checked; | 174 var newSetting = !this.disableNotifications_.checked; |
| 175 app.appData.notifications_disabled = newSetting; | 175 app.data.notifications_disabled = newSetting; |
| 176 chrome.send('setNotificationsDisabled', [app.appData.id, newSetting]); | 176 chrome.send('setNotificationsDisabled', [app.data.id, newSetting]); |
| 177 }, | 177 }, |
| 178 onUninstall_: function(e) { | 178 onUninstall_: function(e) { |
| 179 var tileCell = this.app_.tileCell; | 179 chrome.send('uninstallApp', [this.app_.data.id]); |
| 180 tileCell.tilePage.setTileRepositioningState(tileCell.index, true); | |
| 181 chrome.send('uninstallApp', [this.app_.appData.id]); | |
| 182 }, | 180 }, |
| 183 onCreateShortcut_: function(e) { | 181 onCreateShortcut_: function(e) { |
| 184 chrome.send('createAppShortcut', [this.app_.appData.id]); | 182 chrome.send('createAppShortcut', [this.app_.data.id]); |
| 185 }, | 183 }, |
| 186 }; | 184 }; |
| 187 | 185 |
| 188 /** | 186 /** |
| 189 * Creates a new App object. | 187 * Creates a new App object. |
| 190 * @param {Object} appData The data object that describes the app. | |
| 191 * @constructor | 188 * @constructor |
| 192 * @extends {HTMLDivElement} | 189 * @extends {HTMLDivElement} |
| 193 */ | 190 */ |
| 194 function App(appData) { | 191 function App(data) { |
| 195 var el = cr.doc.createElement('div'); | 192 var el = cr.doc.createElement('div'); |
| 196 el.__proto__ = App.prototype; | 193 el.__proto__ = App.prototype; |
| 197 el.initialize(appData); | 194 el.initialize_(); |
| 195 el.data = data; |
| 198 | 196 |
| 199 return el; | 197 return el; |
| 200 } | 198 } |
| 201 | 199 |
| 202 App.prototype = Tile.subclass({ | 200 App.prototype = Tile.subclass({ |
| 203 __proto__: HTMLDivElement.prototype, | 201 __proto__: HTMLDivElement.prototype, |
| 204 | 202 |
| 205 /** | 203 /** |
| 206 * Initialize the app object. | 204 * Initialize the app object. |
| 207 * @param {Object} appData The data object that describes the app. | 205 * @private |
| 208 */ | 206 */ |
| 209 initialize: function(appData) { | 207 initialize_: function() { |
| 210 this.className = 'app focusable'; | |
| 211 | |
| 212 Tile.prototype.initialize.apply(this, arguments); | 208 Tile.prototype.initialize.apply(this, arguments); |
| 213 | 209 |
| 214 this.appData = appData; | 210 this.classList.add('app'); |
| 215 assert(this.appData_.id, 'Got an app without an ID'); | 211 this.classList.add('focusable'); |
| 216 this.id = this.appData_.id; | 212 }, |
| 213 |
| 214 /** |
| 215 * Formats this app according to |data|. |
| 216 * @param {Object} data The data object that describes the app. |
| 217 * @private |
| 218 */ |
| 219 formatApp_: function(data) { |
| 220 assert(this.data_.id, 'Got an app without an ID'); |
| 221 this.id = this.data_.id; |
| 217 this.setAttribute('role', 'menuitem'); | 222 this.setAttribute('role', 'menuitem'); |
| 218 | 223 |
| 219 if (!this.appData_.icon_big_exists && this.appData_.icon_small_exists) | 224 if (!this.data_.icon_big_exists && this.data_.icon_small_exists) |
| 220 this.useSmallIcon_ = true; | 225 this.useSmallIcon_ = true; |
| 221 | 226 |
| 222 this.appContents_ = this.useSmallIcon_ ? | 227 // TODO(pedrosimonetti): Fix crbug.com/165612 |
| 223 $('app-small-icon-template').cloneNode(true) : | 228 if (!this.appContents_) { |
| 224 $('app-large-icon-template').cloneNode(true); | 229 this.appContents_ = this.useSmallIcon_ ? |
| 225 this.appContents_.id = ''; | 230 $('app-small-icon-template').cloneNode(true) : |
| 226 this.appendChild(this.appContents_); | 231 $('app-large-icon-template').cloneNode(true); |
| 232 this.appContents_.id = ''; |
| 233 this.appendChild(this.appContents_); |
| 234 } |
| 227 | 235 |
| 228 this.appImgContainer_ = this.querySelector('.app-img-container'); | 236 this.appImgContainer_ = this.querySelector('.app-img-container'); |
| 229 this.appImg_ = this.appImgContainer_.querySelector('img'); | 237 this.appImg_ = this.appImgContainer_.querySelector('img'); |
| 230 this.setIcon(); | 238 this.setIcon(); |
| 231 | 239 |
| 232 if (this.useSmallIcon_) { | 240 if (this.useSmallIcon_) { |
| 233 this.imgDiv_ = this.querySelector('.app-icon-div'); | 241 this.imgDiv_ = this.querySelector('.app-icon-div'); |
| 234 this.addLaunchClickTarget_(this.imgDiv_); | 242 this.addLaunchClickTarget_(this.imgDiv_); |
| 235 this.imgDiv_.title = this.appData_.title; | 243 this.imgDiv_.title = this.data_.title; |
| 236 chrome.send('getAppIconDominantColor', [this.id]); | 244 chrome.send('getAppIconDominantColor', [this.id]); |
| 237 } else { | 245 } else { |
| 238 this.addLaunchClickTarget_(this.appImgContainer_); | 246 this.addLaunchClickTarget_(this.appImgContainer_); |
| 239 this.appImgContainer_.title = this.appData_.title; | 247 this.appImgContainer_.title = this.data_.title; |
| 240 } | 248 } |
| 241 | 249 |
| 242 var appSpan = this.appContents_.querySelector('.title'); | 250 var appSpan = this.appContents_.querySelector('.title'); |
| 243 appSpan.textContent = appSpan.title = this.appData_.title; | 251 appSpan.textContent = appSpan.title = this.data_.title; |
| 244 this.addLaunchClickTarget_(appSpan); | 252 this.addLaunchClickTarget_(appSpan); |
| 245 | 253 |
| 246 var notification = this.appData_.notification; | 254 var notification = this.data_.notification; |
| 247 var hasNotification = typeof notification != 'undefined' && | 255 var hasNotification = typeof notification != 'undefined' && |
| 248 typeof notification['title'] != 'undefined' && | 256 typeof notification['title'] != 'undefined' && |
| 249 typeof notification['body'] != 'undefined' && | 257 typeof notification['body'] != 'undefined' && |
| 250 !this.appData_.notifications_disabled; | 258 !this.data_.notifications_disabled; |
| 251 if (hasNotification) | 259 if (hasNotification) |
| 252 this.setupNotification_(notification); | 260 this.setupNotification_(notification); |
| 253 | 261 |
| 254 this.addEventListener('keydown', cr.ui.contextMenuHandler); | 262 this.addEventListener('keydown', cr.ui.contextMenuHandler); |
| 255 this.addEventListener('keyup', cr.ui.contextMenuHandler); | 263 this.addEventListener('keyup', cr.ui.contextMenuHandler); |
| 256 | 264 |
| 257 // This hack is here so that appContents.contextMenu will be the same as | 265 // This hack is here so that appContents.contextMenu will be the same as |
| 258 // this.contextMenu. | 266 // this.contextMenu. |
| 259 var self = this; | 267 var self = this; |
| 260 this.appContents_.__defineGetter__('contextMenu', function() { | 268 this.appContents_.__defineGetter__('contextMenu', function() { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 281 * been uninstalled. | 289 * been uninstalled. |
| 282 */ | 290 */ |
| 283 remove: function(opt_animate) { | 291 remove: function(opt_animate) { |
| 284 // Unset the ID immediately, because the app is already gone. But leave | 292 // Unset the ID immediately, because the app is already gone. But leave |
| 285 // the tile on the page as it animates out. | 293 // the tile on the page as it animates out. |
| 286 this.id = ''; | 294 this.id = ''; |
| 287 this.tileCell.doRemove(opt_animate); | 295 this.tileCell.doRemove(opt_animate); |
| 288 }, | 296 }, |
| 289 | 297 |
| 290 /** | 298 /** |
| 291 * Set the URL of the icon from |appData_|. This won't actually show the | 299 * Set the URL of the icon from |this.data_|. This won't actually show the |
| 292 * icon until loadIcon() is called (for performance reasons; we don't want | 300 * icon until loadIcon() is called (for performance reasons; we don't want |
| 293 * to load icons until we have to). | 301 * to load icons until we have to). |
| 294 */ | 302 */ |
| 295 setIcon: function() { | 303 setIcon: function() { |
| 296 var src = this.useSmallIcon_ ? this.appData_.icon_small : | 304 var src = this.useSmallIcon_ ? this.data_.icon_small : |
| 297 this.appData_.icon_big; | 305 this.data_.icon_big; |
| 298 if (!this.appData_.enabled || | 306 if (!this.data_.enabled || |
| 299 (!this.appData_.offlineEnabled && !navigator.onLine)) { | 307 (!this.data_.offlineEnabled && !navigator.onLine)) { |
| 300 src += '?grayscale=true'; | 308 src += '?grayscale=true'; |
| 301 } | 309 } |
| 302 | 310 |
| 303 this.appImgSrc_ = src; | 311 this.appImgSrc_ = src; |
| 304 this.classList.add('icon-loading'); | 312 this.classList.add('icon-loading'); |
| 305 }, | 313 }, |
| 306 | 314 |
| 307 /** | 315 /** |
| 308 * Shows the icon for the app. That is, it causes chrome to load the app | 316 * Shows the icon for the app. That is, it causes chrome to load the app |
| 309 * icon resource. | 317 * icon resource. |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 * @param {Object} notification The notification to show in the bubble. | 360 * @param {Object} notification The notification to show in the bubble. |
| 353 * @private | 361 * @private |
| 354 */ | 362 */ |
| 355 setupNotification_: function(notification) { | 363 setupNotification_: function(notification) { |
| 356 if (notification) { | 364 if (notification) { |
| 357 var infoBubble; | 365 var infoBubble; |
| 358 if (!this.currentBubbleShowing_) { | 366 if (!this.currentBubbleShowing_) { |
| 359 // Create a new bubble. | 367 // Create a new bubble. |
| 360 infoBubble = new cr.ui.ExpandableBubble; | 368 infoBubble = new cr.ui.ExpandableBubble; |
| 361 infoBubble.anchorNode = this; | 369 infoBubble.anchorNode = this; |
| 362 infoBubble.appId = this.appData_.id; | 370 infoBubble.appId = this.data_.id; |
| 363 infoBubble.handleCloseEvent = function() { | 371 infoBubble.handleCloseEvent = function() { |
| 364 chrome.send('closeNotification', [this.appId]); | 372 chrome.send('closeNotification', [this.appId]); |
| 365 infoBubble.hide(); | 373 infoBubble.hide(); |
| 366 }; | 374 }; |
| 367 } else { | 375 } else { |
| 368 // Reuse the old bubble instead of popping up a new bubble over | 376 // Reuse the old bubble instead of popping up a new bubble over |
| 369 // the old one. | 377 // the old one. |
| 370 infoBubble = this.currentBubbleShowing_; | 378 infoBubble = this.currentBubbleShowing_; |
| 371 infoBubble.collapseBubble_(); | 379 infoBubble.collapseBubble_(); |
| 372 } | 380 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 388 this.currentBubbleShowing_ = null; | 396 this.currentBubbleShowing_ = null; |
| 389 } | 397 } |
| 390 }, | 398 }, |
| 391 | 399 |
| 392 /** | 400 /** |
| 393 * Invoked when an app is clicked. | 401 * Invoked when an app is clicked. |
| 394 * @param {Event} e The click event. | 402 * @param {Event} e The click event. |
| 395 * @private | 403 * @private |
| 396 */ | 404 */ |
| 397 onClick_: function(e) { | 405 onClick_: function(e) { |
| 398 var url = !this.appData_.is_webstore ? '' : | 406 var url = !this.data_.is_webstore ? '' : |
| 399 appendParam(this.appData_.url, | 407 appendParam(this.data_.url, |
| 400 'utm_source', | 408 'utm_source', |
| 401 'chrome-ntp-icon'); | 409 'chrome-ntp-icon'); |
| 402 | 410 |
| 403 chrome.send('launchApp', | 411 chrome.send('launchApp', |
| 404 [this.appId, APP_LAUNCH.NTP_APPS_MAXIMIZED, url, | 412 [this.appId, APP_LAUNCH.NTP_APPS_MAXIMIZED, url, |
| 405 e.button, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey]); | 413 e.button, e.altKey, e.ctrlKey, e.metaKey, e.shiftKey]); |
| 406 | 414 |
| 407 // Don't allow the click to trigger a link or anything | 415 // Don't allow the click to trigger a link or anything |
| 408 e.preventDefault(); | 416 e.preventDefault(); |
| 409 }, | 417 }, |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 477 } else { | 485 } else { |
| 478 this.appContents_.classList.remove('suppress-active'); | 486 this.appContents_.classList.remove('suppress-active'); |
| 479 } | 487 } |
| 480 | 488 |
| 481 // This class is here so we don't show the focus state for apps that | 489 // This class is here so we don't show the focus state for apps that |
| 482 // gain keyboard focus via mouse clicking. | 490 // gain keyboard focus via mouse clicking. |
| 483 this.classList.add('click-focus'); | 491 this.classList.add('click-focus'); |
| 484 }, | 492 }, |
| 485 | 493 |
| 486 /** | 494 /** |
| 487 * Change the appData and update the appearance of the app. | 495 * Change the data and update the appearance of the app. |
| 488 * @param {Object} appData The new data object that describes the app. | 496 * @param {Object} data The new data object that describes the app. |
| 489 */ | 497 */ |
| 490 replaceAppData: function(appData) { | 498 replaceAppData: function(data) { |
| 491 this.appData_ = appData; | 499 assert(data); |
| 500 this.data = data; |
| 492 this.setIcon(); | 501 this.setIcon(); |
| 493 this.loadIcon(); | 502 this.loadIcon(); |
| 494 }, | 503 }, |
| 495 | 504 |
| 496 /** | 505 /** |
| 497 * The data and preferences for this app. | 506 * The data and preferences for this app. |
| 498 * @type {Object} | 507 * @type {Object} |
| 499 */ | 508 */ |
| 500 set appData(data) { | 509 set data(data) { |
| 501 this.appData_ = data; | 510 Object.getOwnPropertyDescriptor(Tile.prototype, 'data').set.apply(this, |
| 511 arguments); |
| 512 |
| 513 this.formatApp_(data); |
| 502 }, | 514 }, |
| 503 get appData() { | 515 get data() { |
| 504 return this.appData_; | 516 return this.data_; |
| 505 }, | 517 }, |
| 506 | 518 |
| 507 get appId() { | 519 get appId() { |
| 508 return this.appData_.id; | 520 return this.data_.id; |
| 509 }, | 521 }, |
| 510 | 522 |
| 511 /** | 523 /** |
| 512 * Returns a pointer to the context menu for this app. All apps share the | 524 * Returns a pointer to the context menu for this app. All apps share the |
| 513 * singleton AppContextMenu. This function is called by the | 525 * singleton AppContextMenu. This function is called by the |
| 514 * ContextMenuHandler in response to the 'contextmenu' event. | 526 * ContextMenuHandler in response to the 'contextmenu' event. |
| 515 * @type {cr.ui.Menu} | 527 * @type {cr.ui.Menu} |
| 516 */ | 528 */ |
| 517 get contextMenu() { | 529 get contextMenu() { |
| 518 var menu = AppContextMenu.getInstance(); | 530 var menu = AppContextMenu.getInstance(); |
| 519 menu.setupForApp(this); | 531 menu.setupForApp(this); |
| 520 return menu.menu; | 532 return menu.menu; |
| 521 }, | 533 }, |
| 522 | 534 |
| 523 /** | 535 /** |
| 524 * Returns whether this element can be 'removed' from chrome (i.e. whether | 536 * Returns whether this element can be 'removed' from chrome (i.e. whether |
| 525 * the user can drag it onto the trash and expect something to happen). | 537 * the user can drag it onto the trash and expect something to happen). |
| 526 * @return {boolean} True if the app can be uninstalled. | 538 * @return {boolean} True if the app can be uninstalled. |
| 527 */ | 539 */ |
| 528 canBeRemoved: function() { | 540 canBeRemoved: function() { |
| 529 return this.appData_.mayDisable; | 541 return this.data_.mayDisable; |
| 530 }, | 542 }, |
| 531 | 543 |
| 532 /** | 544 /** |
| 533 * Uninstalls the app after it's been dropped on the trash. | 545 * Uninstalls the app after it's been dropped on the trash. |
| 534 */ | 546 */ |
| 535 removeFromChrome: function() { | 547 removeFromChrome: function() { |
| 536 chrome.send('uninstallApp', [this.appData_.id, true]); | 548 chrome.send('uninstallApp', [this.data_.id, true]); |
| 537 this.tile.tilePage.removeTile(this.tile, true); | 549 this.tile.tilePage.removeTile(this.tile, true); |
| 538 if (this.currentBubbleShowing_) | 550 if (this.currentBubbleShowing_) |
| 539 this.currentBubbleShowing_.hide(); | 551 this.currentBubbleShowing_.hide(); |
| 540 }, | 552 }, |
| 541 | |
| 542 /** | |
| 543 * Called when a drag is starting on the tile. Updates dataTransfer with | |
| 544 * data for this tile. | |
| 545 */ | |
| 546 setDragData: function(dataTransfer) { | |
| 547 dataTransfer.setData('Text', this.appData_.title); | |
| 548 dataTransfer.setData('URL', this.appData_.url); | |
| 549 }, | |
| 550 }); | 553 }); |
| 551 | 554 |
| 552 /** | 555 /** |
| 553 * Creates a new AppsPage object. | 556 * Creates a new AppsPage object. |
| 554 * @constructor | 557 * @constructor |
| 555 * @extends {TilePage} | 558 * @extends {TilePage} |
| 556 */ | 559 */ |
| 557 function AppsPage() { | 560 function AppsPage() { |
| 558 var el = new TilePage(); | 561 var el = new TilePage(); |
| 559 el.__proto__ = AppsPage.prototype; | 562 el.__proto__ = AppsPage.prototype; |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 596 // view. | 599 // view. |
| 597 this.addEventListener('carddeselected', this.onCardDeselected_); | 600 this.addEventListener('carddeselected', this.onCardDeselected_); |
| 598 this.addEventListener('cardSlider:card_change_ended', | 601 this.addEventListener('cardSlider:card_change_ended', |
| 599 this.onCardChangeEnded_); | 602 this.onCardChangeEnded_); |
| 600 | 603 |
| 601 this.addEventListener('tilePage:tile_added', this.onTileAdded_); | 604 this.addEventListener('tilePage:tile_added', this.onTileAdded_); |
| 602 }, | 605 }, |
| 603 | 606 |
| 604 /** | 607 /** |
| 605 * Highlight a newly installed app as it's added to the NTP. | 608 * Highlight a newly installed app as it's added to the NTP. |
| 606 * @param {Object} appData The data object that describes the app. | 609 * @param {Object} data The data object that describes the app. |
| 607 */ | 610 */ |
| 608 insertAndHighlightApp: function(appData) { | 611 insertAndHighlightApp: function(data) { |
| 609 ntp.getCardSlider().selectCardByValue(this); | 612 ntp.getCardSlider().selectCardByValue(this); |
| 610 this.insertApp(appData, true); | 613 this.insertApp(data, true); |
| 611 }, | 614 }, |
| 612 | 615 |
| 613 /** | 616 /** |
| 614 * Inserts an App into the TilePage, preserving the alphabetical order. | 617 * Inserts an App into the TilePage, preserving the alphabetical order. |
| 615 * @param {Object} appData The data that describes the app. | 618 * @param {Object} data The data that describes the app. |
| 616 * @param {boolean} animate Whether to animate the insertion. | 619 * @param {boolean} animate Whether to animate the insertion. |
| 617 */ | 620 */ |
| 618 insertApp: function(appData, animate) { | 621 insertApp: function(data, animate) { |
| 619 var index = this.tiles_.length; | 622 var index = this.tiles_.length; |
| 620 for (var i = 0; i < this.tiles_.length; i++) { | 623 for (var i = 0; i < this.tiles_.length; i++) { |
| 621 if (appData.title.toLocaleLowerCase() < | 624 if (data.title.toLocaleLowerCase() < |
| 622 this.tiles_[i].appData.title.toLocaleLowerCase()) { | 625 this.tiles_[i].data.title.toLocaleLowerCase()) { |
| 623 index = i; | 626 index = i; |
| 624 break; | 627 break; |
| 625 } | 628 } |
| 626 } | 629 } |
| 627 | 630 |
| 628 var app = new App(appData); | 631 var app = new App(data); |
| 629 this.addTileAt(app, index); | 632 this.addTileAt(app, index); |
| 630 this.renderGrid_(); | 633 this.renderGrid_(); |
| 631 }, | 634 }, |
| 632 | 635 |
| 633 /** | 636 /** |
| 634 * Handler for 'cardselected' event, fired when |this| is selected. The | 637 * Handler for 'cardselected' event, fired when |this| is selected. The |
| 635 * first time this is called, we load all the app icons. | 638 * first time this is called, we load all the app icons. |
| 636 * @private | 639 * @private |
| 637 */ | 640 */ |
| 638 onCardSelected_: function(e) { | 641 onCardSelected_: function(e) { |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 690 TilePage.prototype.onScroll.apply(this, arguments); | 693 TilePage.prototype.onScroll.apply(this, arguments); |
| 691 | 694 |
| 692 for (var i = 0; i < this.tiles_.length; i++) { | 695 for (var i = 0; i < this.tiles_.length; i++) { |
| 693 var app = this.tiles_[i]; | 696 var app = this.tiles_[i]; |
| 694 assert(app instanceof App); | 697 assert(app instanceof App); |
| 695 if (app.currentBubbleShowing_) | 698 if (app.currentBubbleShowing_) |
| 696 app.currentBubbleShowing_.resizeAndReposition(); | 699 app.currentBubbleShowing_.resizeAndReposition(); |
| 697 } | 700 } |
| 698 }, | 701 }, |
| 699 | 702 |
| 700 /** @override */ | |
| 701 doDragOver: function(e) { | |
| 702 // Only animatedly re-arrange if the user is currently dragging an app. | |
| 703 var tile = ntp.getCurrentlyDraggingTile(); | |
| 704 if (tile && tile.querySelector('.app')) { | |
| 705 TilePage.prototype.doDragOver.call(this, e); | |
| 706 } else { | |
| 707 e.preventDefault(); | |
| 708 this.setDropEffect(e.dataTransfer); | |
| 709 } | |
| 710 }, | |
| 711 | |
| 712 /** @override */ | |
| 713 shouldAcceptDrag: function(e) { | |
| 714 if (ntp.getCurrentlyDraggingTile()) | |
| 715 return true; | |
| 716 if (!e.dataTransfer || !e.dataTransfer.types) | |
| 717 return false; | |
| 718 return Array.prototype.indexOf.call(e.dataTransfer.types, | |
| 719 'text/uri-list') != -1; | |
| 720 }, | |
| 721 | |
| 722 /** @override */ | |
| 723 addDragData: function(dataTransfer, index) { | |
| 724 var sourceId = -1; | |
| 725 var currentlyDraggingTile = ntp.getCurrentlyDraggingTile(); | |
| 726 if (currentlyDraggingTile) { | |
| 727 var tileContents = currentlyDraggingTile.firstChild; | |
| 728 if (tileContents.classList.contains('app')) { | |
| 729 var originalPage = currentlyDraggingTile.tilePage; | |
| 730 var samePageDrag = originalPage == this; | |
| 731 sourceId = samePageDrag ? DRAG_SOURCE.SAME_APPS_PANE : | |
| 732 DRAG_SOURCE.OTHER_APPS_PANE; | |
| 733 this.tileGrid_.insertBefore(currentlyDraggingTile, | |
| 734 this.tileElements_[index]); | |
| 735 this.tileMoved(currentlyDraggingTile); | |
| 736 if (!samePageDrag) { | |
| 737 originalPage.fireRemovedEvent(currentlyDraggingTile, index, true); | |
| 738 this.fireAddedEvent(currentlyDraggingTile, index, true); | |
| 739 } | |
| 740 } else if (currentlyDraggingTile.querySelector('.most-visited')) { | |
| 741 this.generateAppForLink(tileContents.data); | |
| 742 sourceId = DRAG_SOURCE.MOST_VISITED_PANE; | |
| 743 } | |
| 744 } else { | |
| 745 this.addOutsideData_(dataTransfer); | |
| 746 sourceId = DRAG_SOURCE.OUTSIDE_NTP; | |
| 747 } | |
| 748 | |
| 749 assert(sourceId != -1); | |
| 750 chrome.send('metricsHandler:recordInHistogram', | |
| 751 ['NewTabPage.AppsPageDragSource', sourceId, DRAG_SOURCE_LIMIT]); | |
| 752 }, | |
| 753 | |
| 754 /** | |
| 755 * Adds drag data that has been dropped from a source that is not a tile. | |
| 756 * @param {Object} dataTransfer The data transfer object that holds drop | |
| 757 * data. | |
| 758 * @private | |
| 759 */ | |
| 760 addOutsideData_: function(dataTransfer) { | |
| 761 var url = dataTransfer.getData('url'); | |
| 762 assert(url); | |
| 763 | |
| 764 // If the dataTransfer has html data, use that html's text contents as the | |
| 765 // title of the new link. | |
| 766 var html = dataTransfer.getData('text/html'); | |
| 767 var title; | |
| 768 if (html) { | |
| 769 // It's important that we don't attach this node to the document | |
| 770 // because it might contain scripts. | |
| 771 var node = this.ownerDocument.createElement('div'); | |
| 772 node.innerHTML = html; | |
| 773 title = node.textContent; | |
| 774 } | |
| 775 | |
| 776 // Make sure title is >=1 and <=45 characters for Chrome app limits. | |
| 777 if (!title) | |
| 778 title = url; | |
| 779 if (title.length > 45) | |
| 780 title = title.substring(0, 45); | |
| 781 var data = {url: url, title: title}; | |
| 782 | |
| 783 // Synthesize an app. | |
| 784 this.generateAppForLink(data); | |
| 785 }, | |
| 786 | |
| 787 /** | 703 /** |
| 788 * Creates a new crx-less app manifest and installs it. | 704 * Creates a new crx-less app manifest and installs it. |
| 789 * @param {Object} data The data object describing the link. Must have |url| | 705 * @param {Object} data The data object describing the link. Must have |url| |
| 790 * and |title| members. | 706 * and |title| members. |
| 791 */ | 707 */ |
| 792 generateAppForLink: function(data) { | 708 generateAppForLink: function(data) { |
| 793 assert(data.url != undefined); | 709 assert(data.url != undefined); |
| 794 assert(data.title != undefined); | 710 assert(data.title != undefined); |
| 795 chrome.send('generateAppForLink', [data.url, data.title, 0]); | 711 chrome.send('generateAppForLink', [data.url, data.title, 0]); |
| 796 }, | 712 }, |
| 797 | |
| 798 /** @override */ | |
| 799 tileMoved: function(draggedTile) { | |
| 800 if (!(draggedTile.firstChild instanceof App)) | |
| 801 return; | |
| 802 | |
| 803 chrome.send('setPageIndex', [draggedTile.firstChild.appId, 0]); | |
| 804 | |
| 805 var appIds = []; | |
| 806 for (var i = 0; i < this.tiles_.length; i++) { | |
| 807 var tileContents = this.tiles_[i]; | |
| 808 if (tileContents instanceof App) | |
| 809 appIds.push(tileContents.appId); | |
| 810 } | |
| 811 | |
| 812 chrome.send('reorderApps', [draggedTile.firstChild.appId, appIds]); | |
| 813 }, | |
| 814 | |
| 815 /** @override */ | |
| 816 setDropEffect: function(dataTransfer) { | |
| 817 var tile = ntp.getCurrentlyDraggingTile(); | |
| 818 if (tile && tile.querySelector('.app')) | |
| 819 ntp.setCurrentDropEffect(dataTransfer, 'move'); | |
| 820 else | |
| 821 ntp.setCurrentDropEffect(dataTransfer, 'copy'); | |
| 822 }, | |
| 823 }; | 713 }; |
| 824 | 714 |
| 825 /** | 715 /** |
| 826 * Launches the specified app using the APP_LAUNCH_NTP_APP_RE_ENABLE | 716 * Launches the specified app using the APP_LAUNCH_NTP_APP_RE_ENABLE |
| 827 * histogram. This should only be invoked from the AppLauncherHandler. | 717 * histogram. This should only be invoked from the AppLauncherHandler. |
| 828 * @param {String} appID The ID of the app. | 718 * @param {String} appID The ID of the app. |
| 829 */ | 719 */ |
| 830 function launchAppAfterEnable(appId) { | 720 function launchAppAfterEnable(appId) { |
| 831 chrome.send('launchApp', [appId, APP_LAUNCH.NTP_APP_RE_ENABLE]); | 721 chrome.send('launchApp', [appId, APP_LAUNCH.NTP_APP_RE_ENABLE]); |
| 832 } | 722 } |
| 833 | 723 |
| 834 function appNotificationChanged(id, notification) { | 724 function appNotificationChanged(id, notification) { |
| 835 var app = $(id); | 725 var app = $(id); |
| 836 // The app might have been uninstalled, or notifications might be disabled. | 726 // The app might have been uninstalled, or notifications might be disabled. |
| 837 if (app && !app.appData.notifications_disabled) | 727 if (app && !app.data.notifications_disabled) |
| 838 app.setupNotification_(notification); | 728 app.setupNotification_(notification); |
| 839 } | 729 } |
| 840 | 730 |
| 841 return { | 731 return { |
| 842 appNotificationChanged: appNotificationChanged, | 732 appNotificationChanged: appNotificationChanged, |
| 843 AppsPage: AppsPage, | 733 AppsPage: AppsPage, |
| 844 launchAppAfterEnable: launchAppAfterEnable, | 734 launchAppAfterEnable: launchAppAfterEnable, |
| 845 }; | 735 }; |
| 846 }); | 736 }); |
| OLD | NEW |