OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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('wallpapers', function() { | 5 cr.define('wallpapers', function() { |
6 /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; | 6 /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; |
7 /** @const */ var Grid = cr.ui.Grid; | 7 /** @const */ var Grid = cr.ui.Grid; |
8 /** @const */ var GridItem = cr.ui.GridItem; | 8 /** @const */ var GridItem = cr.ui.GridItem; |
9 /** @const */ var GridSelectionController = cr.ui.GridSelectionController; | 9 /** @const */ var GridSelectionController = cr.ui.GridSelectionController; |
10 /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; | 10 /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel; |
11 /** @const */ var ThumbnailSuffix = '_thumbnail.png'; | 11 /** @const */ var ThumbnailSuffix = '_thumbnail.png'; |
12 /** @const */ var ShowSpinnerDelayMs = 500; | 12 /** @const */ var ShowSpinnerDelayMs = 500; |
13 | 13 |
14 /** | 14 /** |
15 * Creates a new wallpaper thumbnails grid item. | 15 * Creates a new wallpaper thumbnails grid item. |
16 * @param {{baseURL: string, layout: string, source: string, | 16 * @param {{wallpaperId: number, baseURL: string, layout: string, |
17 * availableOffline: boolean, opt_dynamicURL: string, | 17 * source: string, availableOffline: boolean, |
18 * opt_author: string, opt_authorWebsite: string}} | 18 * opt_dynamicURL: string, opt_author: string, |
| 19 * opt_authorWebsite: string}} |
19 * wallpaperInfo Wallpaper data item in WallpaperThumbnailsGrid's data | 20 * wallpaperInfo Wallpaper data item in WallpaperThumbnailsGrid's data |
20 * model. | 21 * model. |
21 * @param {number} dataModelId A unique ID that this item associated to. | 22 * @param {number} dataModelId A unique ID that this item associated to. |
| 23 * @param {object} thumbnail The thumbnail image Object associated with this |
| 24 * grid item. |
22 * @param {function} callback The callback function when decoration finished. | 25 * @param {function} callback The callback function when decoration finished. |
23 * @constructor | 26 * @constructor |
24 * @extends {cr.ui.GridItem} | 27 * @extends {cr.ui.GridItem} |
25 */ | 28 */ |
26 function WallpaperThumbnailsGridItem(wallpaperInfo, dataModelId, callback) { | 29 function WallpaperThumbnailsGridItem(wallpaperInfo, |
| 30 dataModelId, |
| 31 thumbnail, |
| 32 callback) { |
27 var el = new GridItem(wallpaperInfo); | 33 var el = new GridItem(wallpaperInfo); |
28 el.__proto__ = WallpaperThumbnailsGridItem.prototype; | 34 el.__proto__ = WallpaperThumbnailsGridItem.prototype; |
29 el.dataModelId = dataModelId; | 35 el.dataModelId_ = dataModelId; |
30 el.callback = callback; | 36 el.thumbnail_ = thumbnail; |
| 37 el.callback_ = callback; |
31 return el; | 38 return el; |
32 } | 39 } |
33 | 40 |
34 WallpaperThumbnailsGridItem.prototype = { | 41 WallpaperThumbnailsGridItem.prototype = { |
35 __proto__: GridItem.prototype, | 42 __proto__: GridItem.prototype, |
36 | 43 |
37 /** | 44 /** |
38 * The unique ID this thumbnail grid associated to. | 45 * The unique ID this thumbnail grid associated to. |
39 * @type {number} | 46 * @type {number} |
40 */ | 47 */ |
41 dataModelId: null, | 48 dataModelId_: null, |
| 49 |
| 50 /** |
| 51 * The thumbnail image associated with the current grid item. |
| 52 */ |
| 53 thumbnail_: null, |
42 | 54 |
43 /** | 55 /** |
44 * Called when the WallpaperThumbnailsGridItem is decorated or failed to | 56 * Called when the WallpaperThumbnailsGridItem is decorated or failed to |
45 * decorate. If the decoration contains image, the callback function should | 57 * decorate. If the decoration contains image, the callback function should |
46 * be called after image loaded. | 58 * be called after image loaded. |
47 * @type {function} | 59 * @type {function} |
48 */ | 60 */ |
49 callback: null, | 61 callback_: null, |
50 | 62 |
51 /** @override */ | 63 /** @override */ |
52 decorate: function() { | 64 decorate: function() { |
53 GridItem.prototype.decorate.call(this); | 65 GridItem.prototype.decorate.call(this); |
54 // Removes garbage created by GridItem. | 66 // Removes garbage created by GridItem. |
55 this.innerText = ''; | 67 this.innerText = ''; |
| 68 |
| 69 if (this.thumbnail_) { |
| 70 this.appendChild(this.thumbnail_); |
| 71 this.callback_(this.dataModelId_); |
| 72 return; |
| 73 } |
| 74 |
56 var imageEl = cr.doc.createElement('img'); | 75 var imageEl = cr.doc.createElement('img'); |
57 imageEl.classList.add('thumbnail'); | 76 imageEl.classList.add('thumbnail'); |
58 cr.defineProperty(imageEl, 'offline', cr.PropertyKind.BOOL_ATTR); | 77 cr.defineProperty(imageEl, 'offline', cr.PropertyKind.BOOL_ATTR); |
59 imageEl.offline = this.dataItem.availableOffline; | 78 imageEl.offline = this.dataItem.availableOffline; |
60 this.appendChild(imageEl); | 79 this.appendChild(imageEl); |
61 var self = this; | 80 var self = this; |
62 | 81 |
63 switch (this.dataItem.source) { | 82 switch (this.dataItem.source) { |
64 case Constants.WallpaperSourceEnum.AddNew: | 83 case Constants.WallpaperSourceEnum.AddNew: |
65 this.id = 'add-new'; | 84 this.id = 'add-new'; |
66 this.addEventListener('click', function(e) { | 85 this.addEventListener('click', function(e) { |
67 var checkbox = $('surprise-me').querySelector('#checkbox'); | 86 var checkbox = $('surprise-me').querySelector('#checkbox'); |
68 if (!checkbox.classList.contains('checked')) | 87 if (!checkbox.classList.contains('checked')) |
69 $('wallpaper-selection-container').hidden = false; | 88 $('wallpaper-selection-container').hidden = false; |
70 }); | 89 }); |
71 // Delay dispatching the completion callback until all items have | 90 // Delay dispatching the completion callback until all items have |
72 // begun loading and are tracked. | 91 // begun loading and are tracked. |
73 window.setTimeout(this.callback.bind(this, this.dataModelId), 0); | 92 window.setTimeout(this.callback_.bind(this, this.dataModelId_), 0); |
74 break; | 93 break; |
75 case Constants.WallpaperSourceEnum.Custom: | 94 case Constants.WallpaperSourceEnum.Custom: |
76 var errorHandler = function(e) { | 95 var errorHandler = function(e) { |
77 self.callback(self.dataModelId); | 96 self.callback_(self.dataModelId_); |
78 console.error('Can not access file system.'); | 97 console.error('Can not access file system.'); |
79 }; | 98 }; |
80 var wallpaperDirectories = WallpaperDirectories.getInstance(); | 99 var wallpaperDirectories = WallpaperDirectories.getInstance(); |
81 var getThumbnail = function(fileName) { | 100 var getThumbnail = function(fileName) { |
82 var setURL = function(fileEntry) { | 101 var setURL = function(fileEntry) { |
83 imageEl.src = fileEntry.toURL(); | 102 imageEl.src = fileEntry.toURL(); |
84 self.callback(self.dataModelId); | 103 self.callback_(self.dataModelId_, |
| 104 self.dataItem.wallpaperId, |
| 105 imageEl); |
85 }; | 106 }; |
86 var fallback = function() { | 107 var fallback = function() { |
87 wallpaperDirectories.getDirectory( | 108 wallpaperDirectories.getDirectory( |
88 Constants.WallpaperDirNameEnum.ORIGINAL, function(dirEntry) { | 109 Constants.WallpaperDirNameEnum.ORIGINAL, function(dirEntry) { |
89 dirEntry.getFile(fileName, {create: false}, setURL, | 110 dirEntry.getFile(fileName, {create: false}, setURL, |
90 errorHandler); | 111 errorHandler); |
91 }, errorHandler); | 112 }, errorHandler); |
92 }; | 113 }; |
93 var success = function(dirEntry) { | 114 var success = function(dirEntry) { |
94 dirEntry.getFile(fileName, {create: false}, setURL, fallback); | 115 dirEntry.getFile(fileName, {create: false}, setURL, fallback); |
95 }; | 116 }; |
96 wallpaperDirectories.getDirectory( | 117 wallpaperDirectories.getDirectory( |
97 Constants.WallpaperDirNameEnum.THUMBNAIL, success, errorHandler); | 118 Constants.WallpaperDirNameEnum.THUMBNAIL, success, errorHandler); |
98 }; | 119 }; |
99 getThumbnail(self.dataItem.baseURL); | 120 getThumbnail(self.dataItem.baseURL); |
100 break; | 121 break; |
101 case Constants.WallpaperSourceEnum.OEM: | 122 case Constants.WallpaperSourceEnum.OEM: |
102 case Constants.WallpaperSourceEnum.Online: | 123 case Constants.WallpaperSourceEnum.Online: |
103 chrome.wallpaperPrivate.getThumbnail(this.dataItem.baseURL, | 124 chrome.wallpaperPrivate.getThumbnail(this.dataItem.baseURL, |
104 this.dataItem.source, | 125 this.dataItem.source, |
105 function(data) { | 126 function(data) { |
106 if (data) { | 127 if (data) { |
107 var blob = new Blob([new Int8Array(data)], | 128 var blob = new Blob([new Int8Array(data)], |
108 {'type': 'image\/png'}); | 129 {'type': 'image\/png'}); |
109 imageEl.src = window.URL.createObjectURL(blob); | 130 imageEl.src = window.URL.createObjectURL(blob); |
110 imageEl.addEventListener('load', function(e) { | 131 imageEl.addEventListener('load', function(e) { |
111 self.callback(self.dataModelId); | 132 self.callback_(self.dataModelId_, |
| 133 self.dataItem.wallpaperId, |
| 134 imageEl); |
112 window.URL.revokeObjectURL(this.src); | 135 window.URL.revokeObjectURL(this.src); |
113 }); | 136 }); |
114 } else if (self.dataItem.source == | 137 } else if (self.dataItem.source == |
115 Constants.WallpaperSourceEnum.Online) { | 138 Constants.WallpaperSourceEnum.Online) { |
116 var xhr = new XMLHttpRequest(); | 139 var xhr = new XMLHttpRequest(); |
117 xhr.open('GET', self.dataItem.baseURL + ThumbnailSuffix, true); | 140 xhr.open('GET', self.dataItem.baseURL + ThumbnailSuffix, true); |
118 xhr.responseType = 'arraybuffer'; | 141 xhr.responseType = 'arraybuffer'; |
119 xhr.send(null); | 142 xhr.send(null); |
120 xhr.addEventListener('load', function(e) { | 143 xhr.addEventListener('load', function(e) { |
121 if (xhr.status === 200) { | 144 if (xhr.status === 200) { |
122 chrome.wallpaperPrivate.saveThumbnail(self.dataItem.baseURL, | 145 chrome.wallpaperPrivate.saveThumbnail(self.dataItem.baseURL, |
123 xhr.response); | 146 xhr.response); |
124 var blob = new Blob([new Int8Array(xhr.response)], | 147 var blob = new Blob([new Int8Array(xhr.response)], |
125 {'type' : 'image\/png'}); | 148 {'type' : 'image\/png'}); |
126 imageEl.src = window.URL.createObjectURL(blob); | 149 imageEl.src = window.URL.createObjectURL(blob); |
127 // TODO(bshe): We currently use empty div to reserve space for | 150 // TODO(bshe): We currently use empty div to reserve space for |
128 // thumbnail. Use a placeholder like "loading" image may | 151 // thumbnail. Use a placeholder like "loading" image may |
129 // better. | 152 // better. |
130 imageEl.addEventListener('load', function(e) { | 153 imageEl.addEventListener('load', function(e) { |
131 self.callback(self.dataModelId); | 154 self.callback_(self.dataModelId_, |
| 155 self.dataItem.wallpaperId, |
| 156 this); |
132 window.URL.revokeObjectURL(this.src); | 157 window.URL.revokeObjectURL(this.src); |
133 }); | 158 }); |
134 } else { | 159 } else { |
135 self.callback(self.dataModelId); | 160 self.callback_(self.dataModelId_); |
136 } | 161 } |
137 }); | 162 }); |
138 } | 163 } |
139 }); | 164 }); |
140 break; | 165 break; |
141 default: | 166 default: |
142 console.error('Unsupported image source.'); | 167 console.error('Unsupported image source.'); |
143 // Delay dispatching the completion callback until all items have | 168 // Delay dispatching the completion callback until all items have |
144 // begun loading and are tracked. | 169 // begun loading and are tracked. |
145 window.setTimeout(this.callback.bind(this, this.dataModelId), 0); | 170 window.setTimeout(this.callback_.bind(this, this.dataModelId_), 0); |
146 } | 171 } |
147 }, | 172 }, |
148 }; | 173 }; |
149 | 174 |
150 /** | 175 /** |
151 * Creates a selection controller that wraps selection on grid ends | 176 * Creates a selection controller that wraps selection on grid ends |
152 * and translates Enter presses into 'activate' events. | 177 * and translates Enter presses into 'activate' events. |
153 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to | 178 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to |
154 * interact with. | 179 * interact with. |
155 * @param {cr.ui.Grid} grid The grid to interact with. | 180 * @param {cr.ui.Grid} grid The grid to interact with. |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 * id wont increase if the new dataModel is null or empty. | 257 * id wont increase if the new dataModel is null or empty. |
233 */ | 258 */ |
234 dataModelId_: 0, | 259 dataModelId_: 0, |
235 | 260 |
236 /** | 261 /** |
237 * The number of items that need to be generated after a new dataModel is | 262 * The number of items that need to be generated after a new dataModel is |
238 * set. | 263 * set. |
239 */ | 264 */ |
240 pendingItems_: 0, | 265 pendingItems_: 0, |
241 | 266 |
| 267 /** |
| 268 * Maintains all grid items' thumbnail images for quickly switching between |
| 269 * different categories. |
| 270 */ |
| 271 thumbnailList_: undefined, |
| 272 |
242 /** @override */ | 273 /** @override */ |
243 set dataModel(dataModel) { | 274 set dataModel(dataModel) { |
244 if (this.dataModel_ == dataModel) | 275 if (this.dataModel_ == dataModel) |
245 return; | 276 return; |
246 | 277 |
247 if (dataModel && dataModel.length != 0) { | 278 if (dataModel && dataModel.length != 0) { |
248 this.dataModelId_++; | 279 this.dataModelId_++; |
249 // Clears old pending items. The new pending items will be counted when | 280 // Clears old pending items. The new pending items will be counted when |
250 // item is constructed in function itemConstructor below. | 281 // item is constructed in function itemConstructor below. |
251 this.pendingItems_ = 0; | 282 this.pendingItems_ = 0; |
(...skipping 23 matching lines...) Expand all Loading... |
275 createSelectionController: function(sm) { | 306 createSelectionController: function(sm) { |
276 return new WallpaperThumbnailsGridSelectionController(sm, this); | 307 return new WallpaperThumbnailsGridSelectionController(sm, this); |
277 }, | 308 }, |
278 | 309 |
279 /** | 310 /** |
280 * Check if new thumbnail grid finished loading. This reduces the count of | 311 * Check if new thumbnail grid finished loading. This reduces the count of |
281 * remaining items to be loaded and when 0, shows the thumbnail grid. Note | 312 * remaining items to be loaded and when 0, shows the thumbnail grid. Note |
282 * it does not reduce the count on a previous |dataModelId|. | 313 * it does not reduce the count on a previous |dataModelId|. |
283 * @param {number} dataModelId A unique ID that a thumbnail item is | 314 * @param {number} dataModelId A unique ID that a thumbnail item is |
284 * associated to. | 315 * associated to. |
| 316 * @param {number} opt_wallpaperId The unique wallpaper ID that associated |
| 317 * with this thumbnail gird item. |
| 318 * @param {object} opt_thumbnail The thumbnail image that associated with |
| 319 * the opt_wallpaperId. |
285 */ | 320 */ |
286 pendingItemComplete: function(dataModelId) { | 321 pendingItemComplete: function(dataModelId, |
| 322 opt_wallpaperId, |
| 323 opt_thumbnail) { |
287 if (dataModelId != this.dataModelId_) | 324 if (dataModelId != this.dataModelId_) |
288 return; | 325 return; |
289 this.pendingItems_--; | 326 this.pendingItems_--; |
| 327 if (opt_wallpaperId != null) |
| 328 this.thumbnailList_[opt_wallpaperId] = opt_thumbnail; |
290 if (this.pendingItems_ == 0) { | 329 if (this.pendingItems_ == 0) { |
291 this.style.visibility = 'visible'; | 330 this.style.visibility = 'visible'; |
292 window.clearTimeout(this.spinnerTimeout_); | 331 window.clearTimeout(this.spinnerTimeout_); |
293 this.spinnerTimeout_ = 0; | 332 this.spinnerTimeout_ = 0; |
294 $('spinner-container').hidden = true; | 333 $('spinner-container').hidden = true; |
295 } | 334 } |
296 }, | 335 }, |
297 | 336 |
298 /** @override */ | 337 /** @override */ |
299 decorate: function() { | 338 decorate: function() { |
300 Grid.prototype.decorate.call(this); | 339 Grid.prototype.decorate.call(this); |
301 // checkmark_ needs to be initialized before set data model. Otherwise, we | 340 // checkmark_ needs to be initialized before set data model. Otherwise, we |
302 // may try to access checkmark before initialization in | 341 // may try to access checkmark before initialization in |
303 // updateActiveThumb_(). | 342 // updateActiveThumb_(). |
304 this.checkmark_ = cr.doc.createElement('div'); | 343 this.checkmark_ = cr.doc.createElement('div'); |
305 this.checkmark_.classList.add('check'); | 344 this.checkmark_.classList.add('check'); |
306 this.dataModel = new ArrayDataModel([]); | 345 this.dataModel = new ArrayDataModel([]); |
| 346 this.thumbnailList_ = new ArrayDataModel([]); |
307 var self = this; | 347 var self = this; |
308 this.itemConstructor = function(value) { | 348 this.itemConstructor = function(value) { |
309 var dataModelId = self.dataModelId_; | 349 var dataModelId = self.dataModelId_; |
310 self.pendingItems_++; | 350 self.pendingItems_++; |
311 return WallpaperThumbnailsGridItem(value, dataModelId, | 351 return WallpaperThumbnailsGridItem(value, dataModelId, |
| 352 self.thumbnailList_[value.wallpaperId], |
312 self.pendingItemComplete.bind(self)); | 353 self.pendingItemComplete.bind(self)); |
313 }; | 354 }; |
314 this.selectionModel = new ListSingleSelectionModel(); | 355 this.selectionModel = new ListSingleSelectionModel(); |
315 this.inProgramSelection_ = false; | 356 this.inProgramSelection_ = false; |
316 }, | 357 }, |
317 | 358 |
318 /** | 359 /** |
319 * Should only be queried from the 'change' event listener, true if the | 360 * Should only be queried from the 'change' event listener, true if the |
320 * change event was triggered by a programmatical selection change. | 361 * change event was triggered by a programmatical selection change. |
321 * @type {boolean} | 362 * @type {boolean} |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
390 // The active thumbnail maybe deleted in the above redraw(). Sets it again | 431 // The active thumbnail maybe deleted in the above redraw(). Sets it again |
391 // to make sure checkmark shows correctly. | 432 // to make sure checkmark shows correctly. |
392 this.updateActiveThumb_(); | 433 this.updateActiveThumb_(); |
393 } | 434 } |
394 }; | 435 }; |
395 | 436 |
396 return { | 437 return { |
397 WallpaperThumbnailsGrid: WallpaperThumbnailsGrid | 438 WallpaperThumbnailsGrid: WallpaperThumbnailsGrid |
398 }; | 439 }; |
399 }); | 440 }); |
OLD | NEW |