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