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';
14 * Creates a new wallpaper thumbnails grid item.
15 * @param {{baseURL: string, layout: string, source: string,
16 * availableOffline: boolean, opt_dynamicURL: string,
17 * opt_author: string, opt_authorWebsite: string}}
18 * wallpaperInfo Wallpaper data item in WallpaperThumbnailsGrid's data
21 * @extends {cr.ui.GridItem}
23 function WallpaperThumbnailsGridItem(wallpaperInfo) {
24 var el = new GridItem(wallpaperInfo);
25 el.__proto__ = WallpaperThumbnailsGridItem.prototype;
29 WallpaperThumbnailsGridItem.prototype = {
30 __proto__: GridItem.prototype,
33 decorate: function() {
34 GridItem.prototype.decorate.call(this);
35 // Removes garbage created by GridItem.
37 var imageEl = cr.doc.createElement('img');
38 imageEl.classList.add('thumbnail');
39 cr.defineProperty(imageEl, 'offline', cr.PropertyKind.BOOL_ATTR);
40 imageEl.offline = this.dataItem.availableOffline;
41 this.appendChild(imageEl);
44 switch (this.dataItem.source) {
45 case Constants.WallpaperSourceEnum.AddNew:
47 this.addEventListener('click', function(e) {
48 $('wallpaper-selection-container').hidden = false;
51 case Constants.WallpaperSourceEnum.Custom:
52 var errorHandler = function(e) {
53 console.error('Can not access file system.');
55 var wallpaperDirectories = WallpaperDirectories.getInstance();
56 var getThumbnail = function(fileName) {
57 var setURL = function(fileEntry) {
58 imageEl.src = fileEntry.toURL();
60 var fallback = function() {
61 wallpaperDirectories.getDirectory(WallpaperDirNameEnum.ORIGINAL,
63 dirEntry.getFile(fileName, {create: false}, setURL,
67 var success = function(dirEntry) {
68 dirEntry.getFile(fileName, {create: false}, setURL, fallback);
70 wallpaperDirectories.getDirectory(WallpaperDirNameEnum.THUMBNAIL,
74 getThumbnail(self.dataItem.baseURL);
76 case Constants.WallpaperSourceEnum.OEM:
77 case Constants.WallpaperSourceEnum.Online:
78 chrome.wallpaperPrivate.getThumbnail(this.dataItem.baseURL,
82 var blob = new Blob([new Int8Array(data)],
83 {'type': 'image\/png'});
84 imageEl.src = window.URL.createObjectURL(blob);
85 imageEl.addEventListener('load', function(e) {
86 window.URL.revokeObjectURL(this.src);
88 } else if (self.dataItem.source ==
89 Constants.WallpaperSourceEnum.Online) {
90 var xhr = new XMLHttpRequest();
91 xhr.open('GET', self.dataItem.baseURL + ThumbnailSuffix, true);
92 xhr.responseType = 'arraybuffer';
94 xhr.addEventListener('load', function(e) {
95 if (xhr.status === 200) {
96 chrome.wallpaperPrivate.saveThumbnail(self.dataItem.baseURL,
98 var blob = new Blob([new Int8Array(xhr.response)],
99 {'type' : 'image\/png'});
100 imageEl.src = window.URL.createObjectURL(blob);
101 // TODO(bshe): We currently use empty div to reserve space for
102 // thumbnail. Use a placeholder like "loading" image may
104 imageEl.addEventListener('load', function(e) {
105 window.URL.revokeObjectURL(this.src);
113 console.error('Unsupported image source.');
119 * Creates a selection controller that wraps selection on grid ends
120 * and translates Enter presses into 'activate' events.
121 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to
123 * @param {cr.ui.Grid} grid The grid to interact with.
125 * @extends {cr.ui.GridSelectionController}
127 function WallpaperThumbnailsGridSelectionController(selectionModel, grid) {
128 GridSelectionController.call(this, selectionModel, grid);
131 WallpaperThumbnailsGridSelectionController.prototype = {
132 __proto__: GridSelectionController.prototype,
135 getIndexBefore: function(index) {
137 GridSelectionController.prototype.getIndexBefore.call(this, index);
138 return result == -1 ? this.getLastIndex() : result;
142 getIndexAfter: function(index) {
144 GridSelectionController.prototype.getIndexAfter.call(this, index);
145 return result == -1 ? this.getFirstIndex() : result;
149 handleKeyDown: function(e) {
150 if (e.keyIdentifier == 'Enter')
151 cr.dispatchSimpleEvent(this.grid_, 'activate');
153 GridSelectionController.prototype.handleKeyDown.call(this, e);
158 * Creates a new user images grid element.
159 * @param {Object=} opt_propertyBag Optional properties.
161 * @extends {cr.ui.Grid}
163 var WallpaperThumbnailsGrid = cr.ui.define('grid');
165 WallpaperThumbnailsGrid.prototype = {
166 __proto__: Grid.prototype,
169 * The checkbox element.
171 checkmark_: undefined,
174 * The item in data model which should have a checkmark.
175 * @type {{baseURL: string, dynamicURL: string, layout: string,
176 * author: string, authorWebsite: string,
177 * availableOffline: boolean}}
178 * wallpaperInfo The information of the wallpaper to be set active.
180 activeItem_: undefined,
181 set activeItem(activeItem) {
182 if (this.activeItem_ != activeItem) {
183 this.activeItem_ = activeItem;
184 this.updateActiveThumb_();
189 createSelectionController: function(sm) {
190 return new WallpaperThumbnailsGridSelectionController(sm, this);
194 decorate: function() {
195 Grid.prototype.decorate.call(this);
196 // checkmark_ needs to be initialized before set data model. Otherwise, we
197 // may try to access checkmark before initialization in
198 // updateActiveThumb_().
199 this.checkmark_ = cr.doc.createElement('div');
200 this.checkmark_.classList.add('check');
201 this.dataModel = new ArrayDataModel([]);
202 this.itemConstructor = WallpaperThumbnailsGridItem;
203 this.selectionModel = new ListSingleSelectionModel();
204 this.inProgramSelection_ = false;
208 * Should only be queried from the 'change' event listener, true if the
209 * change event was triggered by a programmatical selection change.
212 get inProgramSelection() {
213 return this.inProgramSelection_;
217 * Set index to the image selected.
218 * @type {number} index The index of selected image.
220 set selectedItemIndex(index) {
221 this.inProgramSelection_ = true;
222 this.selectionModel.selectedIndex = index;
223 this.inProgramSelection_ = false;
228 * @type {!Object} Wallpaper information inserted into the data model.
231 var index = this.selectionModel.selectedIndex;
232 return index != -1 ? this.dataModel.item(index) : null;
234 set selectedItem(selectedItem) {
235 var index = this.dataModel.indexOf(selectedItem);
236 this.inProgramSelection_ = true;
237 this.selectionModel.leadIndex = index;
238 this.selectionModel.selectedIndex = index;
239 this.inProgramSelection_ = false;
243 * Forces re-display, size re-calculation and focuses grid.
245 updateAndFocus: function() {
246 // Recalculate the measured item size.
247 this.measured_ = null;
254 * Shows a checkmark on the active thumbnail and clears previous active one
255 * if any. Note if wallpaper was not set successfully, checkmark should not
256 * show on that thumbnail.
258 updateActiveThumb_: function() {
259 var selectedGridItem = this.getListItem(this.activeItem_);
260 if (this.checkmark_.parentNode &&
261 this.checkmark_.parentNode == selectedGridItem) {
265 // Clears previous checkmark.
266 if (this.checkmark_.parentNode)
267 this.checkmark_.parentNode.removeChild(this.checkmark_);
269 if (!selectedGridItem)
271 selectedGridItem.appendChild(this.checkmark_);
275 * Redraws the viewport.
278 Grid.prototype.redraw.call(this);
279 // The active thumbnail maybe deleted in the above redraw(). Sets it again
280 // to make sure checkmark shows correctly.
281 this.updateActiveThumb_();
286 WallpaperThumbnailsGrid: WallpaperThumbnailsGrid