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
3 // found in the LICENSE file.
5 cr
.define('wallpapers', function() {
6 /** @const */ var ArrayDataModel
= cr
.ui
.ArrayDataModel
;
7 /** @const */ var Grid
= cr
.ui
.Grid
;
8 /** @const */ var GridItem
= cr
.ui
.GridItem
;
9 /** @const */ var GridSelectionController
= cr
.ui
.GridSelectionController
;
10 /** @const */ var ListSingleSelectionModel
= cr
.ui
.ListSingleSelectionModel
;
11 /** @const */ var ThumbnailSuffix
= '_thumbnail.png';
12 /** @const */ var ShowSpinnerDelayMs
= 500;
15 * Creates a new wallpaper thumbnails grid item.
16 * @param {{wallpaperId: number, baseURL: string, layout: string,
17 * source: string, availableOffline: boolean,
18 * opt_dynamicURL: string, opt_author: string,
19 * opt_authorWebsite: string}}
20 * wallpaperInfo Wallpaper data item in WallpaperThumbnailsGrid's data
22 * @param {number} dataModelId A unique ID that this item associated to.
23 * @param {object} thumbnail The thumbnail image Object associated with this
25 * @param {function} callback The callback function when decoration finished.
27 * @extends {cr.ui.GridItem}
29 function WallpaperThumbnailsGridItem(wallpaperInfo
,
33 var el
= new GridItem(wallpaperInfo
);
34 el
.__proto__
= WallpaperThumbnailsGridItem
.prototype;
35 el
.dataModelId_
= dataModelId
;
36 el
.thumbnail_
= thumbnail
;
37 el
.callback_
= callback
;
41 WallpaperThumbnailsGridItem
.prototype = {
42 __proto__
: GridItem
.prototype,
45 * The unique ID this thumbnail grid associated to.
51 * The thumbnail image associated with the current grid item.
56 * Called when the WallpaperThumbnailsGridItem is decorated or failed to
57 * decorate. If the decoration contains image, the callback function should
58 * be called after image loaded.
64 decorate: function() {
65 GridItem
.prototype.decorate
.call(this);
66 // Removes garbage created by GridItem.
69 if (this.thumbnail_
) {
70 this.appendChild(this.thumbnail_
);
71 this.callback_(this.dataModelId_
);
75 var imageEl
= cr
.doc
.createElement('img');
76 imageEl
.classList
.add('thumbnail');
77 cr
.defineProperty(imageEl
, 'offline', cr
.PropertyKind
.BOOL_ATTR
);
78 imageEl
.offline
= this.dataItem
.availableOffline
;
79 this.appendChild(imageEl
);
82 switch (this.dataItem
.source
) {
83 case Constants
.WallpaperSourceEnum
.AddNew
:
85 this.addEventListener('click', function(e
) {
86 var checkbox
= $('surprise-me').querySelector('#checkbox');
87 if (!checkbox
.classList
.contains('checked'))
88 $('wallpaper-selection-container').hidden
= false;
90 // Delay dispatching the completion callback until all items have
91 // begun loading and are tracked.
92 window
.setTimeout(this.callback_
.bind(this, this.dataModelId_
), 0);
94 case Constants
.WallpaperSourceEnum
.Custom
:
95 var errorHandler = function(e
) {
96 self
.callback_(self
.dataModelId_
);
97 console
.error('Can not access file system.');
99 var wallpaperDirectories
= WallpaperDirectories
.getInstance();
100 var getThumbnail = function(fileName
) {
101 var setURL = function(fileEntry
) {
102 imageEl
.src
= fileEntry
.toURL();
103 self
.callback_(self
.dataModelId_
,
104 self
.dataItem
.wallpaperId
,
107 var fallback = function() {
108 wallpaperDirectories
.getDirectory(
109 Constants
.WallpaperDirNameEnum
.ORIGINAL
, function(dirEntry
) {
110 dirEntry
.getFile(fileName
, {create
: false}, setURL
,
114 var success = function(dirEntry
) {
115 dirEntry
.getFile(fileName
, {create
: false}, setURL
, fallback
);
117 wallpaperDirectories
.getDirectory(
118 Constants
.WallpaperDirNameEnum
.THUMBNAIL
, success
, errorHandler
);
120 getThumbnail(self
.dataItem
.baseURL
);
122 case Constants
.WallpaperSourceEnum
.OEM
:
123 case Constants
.WallpaperSourceEnum
.Online
:
124 chrome
.wallpaperPrivate
.getThumbnail(this.dataItem
.baseURL
,
125 this.dataItem
.source
,
128 var blob
= new Blob([new Int8Array(data
)],
129 {'type': 'image\/png'});
130 imageEl
.src
= window
.URL
.createObjectURL(blob
);
131 imageEl
.addEventListener('load', function(e
) {
132 self
.callback_(self
.dataModelId_
,
133 self
.dataItem
.wallpaperId
,
135 window
.URL
.revokeObjectURL(this.src
);
137 } else if (self
.dataItem
.source
==
138 Constants
.WallpaperSourceEnum
.Online
) {
139 var xhr
= new XMLHttpRequest();
140 xhr
.open('GET', self
.dataItem
.baseURL
+ ThumbnailSuffix
, true);
141 xhr
.responseType
= 'arraybuffer';
143 xhr
.addEventListener('load', function(e
) {
144 if (xhr
.status
=== 200) {
145 chrome
.wallpaperPrivate
.saveThumbnail(self
.dataItem
.baseURL
,
147 var blob
= new Blob([new Int8Array(xhr
.response
)],
148 {'type' : 'image\/png'});
149 imageEl
.src
= window
.URL
.createObjectURL(blob
);
150 // TODO(bshe): We currently use empty div to reserve space for
151 // thumbnail. Use a placeholder like "loading" image may
153 imageEl
.addEventListener('load', function(e
) {
154 self
.callback_(self
.dataModelId_
,
155 self
.dataItem
.wallpaperId
,
157 window
.URL
.revokeObjectURL(this.src
);
160 self
.callback_(self
.dataModelId_
);
167 console
.error('Unsupported image source.');
168 // Delay dispatching the completion callback until all items have
169 // begun loading and are tracked.
170 window
.setTimeout(this.callback_
.bind(this, this.dataModelId_
), 0);
176 * Creates a selection controller that wraps selection on grid ends
177 * and translates Enter presses into 'activate' events.
178 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to
180 * @param {cr.ui.Grid} grid The grid to interact with.
182 * @extends {cr.ui.GridSelectionController}
184 function WallpaperThumbnailsGridSelectionController(selectionModel
, grid
) {
185 GridSelectionController
.call(this, selectionModel
, grid
);
188 WallpaperThumbnailsGridSelectionController
.prototype = {
189 __proto__
: GridSelectionController
.prototype,
192 getIndexBefore: function(index
) {
194 GridSelectionController
.prototype.getIndexBefore
.call(this, index
);
195 return result
== -1 ? this.getLastIndex() : result
;
199 getIndexAfter: function(index
) {
201 GridSelectionController
.prototype.getIndexAfter
.call(this, index
);
202 return result
== -1 ? this.getFirstIndex() : result
;
206 handleKeyDown: function(e
) {
207 if (e
.keyIdentifier
== 'Enter')
208 cr
.dispatchSimpleEvent(this.grid_
, 'activate');
210 GridSelectionController
.prototype.handleKeyDown
.call(this, e
);
215 * Creates a new user images grid element.
216 * @param {Object=} opt_propertyBag Optional properties.
218 * @extends {cr.ui.Grid}
220 var WallpaperThumbnailsGrid
= cr
.ui
.define('grid');
222 WallpaperThumbnailsGrid
.prototype = {
223 __proto__
: Grid
.prototype,
226 * The checkbox element.
228 checkmark_
: undefined,
231 * ID of spinner delay timer.
237 * The item in data model which should have a checkmark.
238 * @type {{baseURL: string, dynamicURL: string, layout: string,
239 * author: string, authorWebsite: string,
240 * availableOffline: boolean}}
241 * wallpaperInfo The information of the wallpaper to be set active.
243 activeItem_
: undefined,
244 set activeItem(activeItem
) {
245 if (this.activeItem_
!= activeItem
) {
246 this.activeItem_
= activeItem
;
247 this.updateActiveThumb_();
252 return this.activeItem_
;
256 * A unique ID that assigned to each set dataModel operation. Note that this
257 * id wont increase if the new dataModel is null or empty.
262 * The number of items that need to be generated after a new dataModel is
268 * Maintains all grid items' thumbnail images for quickly switching between
269 * different categories.
271 thumbnailList_
: undefined,
274 set dataModel(dataModel
) {
275 if (this.dataModel_
== dataModel
)
278 if (dataModel
&& dataModel
.length
!= 0) {
280 // Clears old pending items. The new pending items will be counted when
281 // item is constructed in function itemConstructor below.
282 this.pendingItems_
= 0;
284 this.style
.visibility
= 'hidden';
285 // If spinner is hidden, schedule to show the spinner after
286 // ShowSpinnerDelayMs delay. Otherwise, keep it spinning.
287 if ($('spinner-container').hidden
) {
288 this.spinnerTimeout_
= window
.setTimeout(function() {
289 $('spinner-container').hidden
= false;
290 }, ShowSpinnerDelayMs
);
293 // Sets dataModel to null should hide spinner immediately.
294 $('spinner-container').hidden
= true;
297 var parentSetter
= cr
.ui
.Grid
.prototype.__lookupSetter__('dataModel');
298 parentSetter
.call(this, dataModel
);
302 return this.dataModel_
;
306 createSelectionController: function(sm
) {
307 return new WallpaperThumbnailsGridSelectionController(sm
, this);
311 * 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
313 * it does not reduce the count on a previous |dataModelId|.
314 * @param {number} dataModelId A unique ID that a thumbnail item is
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.
321 pendingItemComplete: function(dataModelId
,
324 if (dataModelId
!= this.dataModelId_
)
326 this.pendingItems_
--;
327 if (opt_wallpaperId
!= null)
328 this.thumbnailList_
[opt_wallpaperId
] = opt_thumbnail
;
329 if (this.pendingItems_
== 0) {
330 this.style
.visibility
= 'visible';
331 window
.clearTimeout(this.spinnerTimeout_
);
332 this.spinnerTimeout_
= 0;
333 $('spinner-container').hidden
= true;
338 decorate: function() {
339 Grid
.prototype.decorate
.call(this);
340 // checkmark_ needs to be initialized before set data model. Otherwise, we
341 // may try to access checkmark before initialization in
342 // updateActiveThumb_().
343 this.checkmark_
= cr
.doc
.createElement('div');
344 this.checkmark_
.classList
.add('check');
345 this.dataModel
= new ArrayDataModel([]);
346 this.thumbnailList_
= new ArrayDataModel([]);
348 this.itemConstructor = function(value
) {
349 var dataModelId
= self
.dataModelId_
;
350 self
.pendingItems_
++;
351 return WallpaperThumbnailsGridItem(value
, dataModelId
,
352 (value
.wallpaperId
== null) ?
353 null : self
.thumbnailList_
[value
.wallpaperId
],
354 self
.pendingItemComplete
.bind(self
));
356 this.selectionModel
= new ListSingleSelectionModel();
357 this.inProgramSelection_
= false;
361 * Should only be queried from the 'change' event listener, true if the
362 * change event was triggered by a programmatical selection change.
365 get inProgramSelection() {
366 return this.inProgramSelection_
;
370 * Set index to the image selected.
371 * @type {number} index The index of selected image.
373 set selectedItemIndex(index
) {
374 this.inProgramSelection_
= true;
375 this.selectionModel
.selectedIndex
= index
;
376 this.inProgramSelection_
= false;
381 * @type {!Object} Wallpaper information inserted into the data model.
384 var index
= this.selectionModel
.selectedIndex
;
385 return index
!= -1 ? this.dataModel
.item(index
) : null;
387 set selectedItem(selectedItem
) {
388 var index
= this.dataModel
.indexOf(selectedItem
);
389 this.inProgramSelection_
= true;
390 this.selectionModel
.leadIndex
= index
;
391 this.selectionModel
.selectedIndex
= index
;
392 this.inProgramSelection_
= false;
396 * Forces re-display, size re-calculation and focuses grid.
398 updateAndFocus: function() {
399 // Recalculate the measured item size.
400 this.measured_
= null;
407 * Shows a checkmark on the active thumbnail and clears previous active one
408 * if any. Note if wallpaper was not set successfully, checkmark should not
409 * show on that thumbnail.
411 updateActiveThumb_: function() {
412 var selectedGridItem
= this.getListItem(this.activeItem_
);
413 if (this.checkmark_
.parentNode
&&
414 this.checkmark_
.parentNode
== selectedGridItem
) {
418 // Clears previous checkmark.
419 if (this.checkmark_
.parentNode
)
420 this.checkmark_
.parentNode
.removeChild(this.checkmark_
);
422 if (!selectedGridItem
)
424 selectedGridItem
.appendChild(this.checkmark_
);
428 * Redraws the viewport.
431 Grid
.prototype.redraw
.call(this);
432 // The active thumbnail maybe deleted in the above redraw(). Sets it again
433 // to make sure checkmark shows correctly.
434 this.updateActiveThumb_();
439 WallpaperThumbnailsGrid
: WallpaperThumbnailsGrid