blob: 14100d6a24cfe390802280221e75a85713a0690d [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* WallpaperManager constructor.
*
* WallpaperManager objects encapsulate the functionality of the wallpaper
* manager extension.
*
* @constructor
* @param {HTMLElement} dialogDom The DOM node containing the prototypical
* extension UI.
*/
function WallpaperManager(dialogDom) {
this.dialogDom_ = dialogDom;
this.document_ = dialogDom.ownerDocument;
this.enableOnlineWallpaper_ = loadTimeData.valueExists('manifestBaseURL');
this.selectedCategory = null;
this.selectedItem_ = null;
this.progressManager_ = new ProgressManager();
this.customWallpaperData_ = null;
this.currentWallpaper_ = null;
this.wallpaperRequest_ = null;
this.wallpaperDirs_ = WallpaperDirectories.getInstance();
this.fetchManifest_();
}
// Anonymous 'namespace'.
// TODO(bshe): Get rid of anonymous namespace.
(function() {
/**
* URL of the learn more page for wallpaper picker.
*/
/** @const */ var LearnMoreURL =
'https://support.google.com/chromeos/?p=wallpaper_fileerror&hl=' +
navigator.language;
/**
* Index of the All category. It is the first category in wallpaper picker.
*/
/** @const */ var AllCategoryIndex = 0;
/**
* Index offset of categories parsed from manifest. The All category is added
* before them. So the offset is 1.
*/
/** @const */ var OnlineCategoriesOffset = 1;
/**
* Returns a translated string.
*
* Wrapper function to make dealing with translated strings more concise.
* Equivilant to localStrings.getString(id).
*
* @param {string} id The id of the string to return.
* @return {string} The translated string.
*/
function str(id) {
return loadTimeData.getString(id);
}
/**
* Retruns the current selected layout.
* @return {string} The selected layout.
*/
function getSelectedLayout() {
var setWallpaperLayout = $('set-wallpaper-layout');
return setWallpaperLayout.options[setWallpaperLayout.selectedIndex].value;
}
/**
* Loads translated strings.
*/
WallpaperManager.initStrings = function(callback) {
chrome.wallpaperPrivate.getStrings(function(strings) {
loadTimeData.data = strings;
if (callback)
callback();
});
};
/**
* Requests wallpaper manifest file from server.
*/
WallpaperManager.prototype.fetchManifest_ = function() {
var locale = navigator.language;
if (!this.enableOnlineWallpaper_) {
this.initDom_();
return;
}
var urls = [
str('manifestBaseURL') + locale + '.json',
// Fallback url. Use 'en' locale by default.
str('manifestBaseURL') + 'en.json'];
var asyncFetchManifestFromUrls = function(urls, func, successCallback,
failureCallback) {
var index = 0;
var loop = {
next: function() {
if (index < urls.length) {
func(loop, urls[index]);
index++;
} else {
failureCallback();
}
},
success: function(response) {
successCallback(response);
},
failure: function() {
failureCallback();
}
};
loop.next();
};
var fetchManifestAsync = function(loop, url) {
var xhr = new XMLHttpRequest();
try {
xhr.addEventListener('loadend', function(e) {
if (this.status == 200 && this.responseText != null) {
try {
var manifest = JSON.parse(this.responseText);
loop.success(manifest);
} catch (e) {
loop.failure();
}
} else {
loop.next();
}
});
xhr.open('GET', url, true);
xhr.send(null);
} catch (e) {
loop.failure();
}
};
if (navigator.onLine) {
asyncFetchManifestFromUrls(urls, fetchManifestAsync,
this.onLoadManifestSuccess_.bind(this),
this.onLoadManifestFailed_.bind(this));
} else {
// If device is offline, fetches manifest from local storage.
// TODO(bshe): Always loading the offline manifest first and replacing
// with the online one when available.
this.onLoadManifestFailed_();
}
};
/**
* Shows error message in a centered dialog.
* @private
* @param {string} errroMessage The string to show in the error dialog.
*/
WallpaperManager.prototype.showError_ = function(errorMessage) {
document.querySelector('.error-message').textContent = errorMessage;
$('error-container').hidden = false;
};
/**
* Sets manifest loaded from server. Called after manifest is successfully
* loaded.
* @param {object} manifest The parsed manifest file.
*/
WallpaperManager.prototype.onLoadManifestSuccess_ = function(manifest) {
this.manifest_ = manifest;
WallpaperUtil.saveToStorage(Constants.AccessManifestKey, manifest, false);
this.initDom_();
};
// Sets manifest to previously saved object if any and shows connection error.
// Called after manifest failed to load.
WallpaperManager.prototype.onLoadManifestFailed_ = function() {
var accessManifestKey = Constants.AccessManifestKey;
var self = this;
Constants.WallpaperLocalStorage.get(accessManifestKey, function(items) {
self.manifest_ = items[accessManifestKey] ? items[accessManifestKey] : {};
self.showError_(str('connectionFailed'));
self.initDom_();
$('wallpaper-grid').classList.add('image-picker-offline');
});
};
/**
* Toggle surprise me feature of wallpaper picker. It fires an storage
* onChanged event. Event handler for that event is in event_page.js.
* @private
*/
WallpaperManager.prototype.toggleSurpriseMe_ = function() {
var checkbox = $('surprise-me').querySelector('#checkbox');
var shouldEnable = !checkbox.classList.contains('checked');
WallpaperUtil.saveToStorage(Constants.AccessSurpriseMeEnabledKey,
shouldEnable, false, function() {
if (chrome.runtime.lastError == null) {
if (shouldEnable) {
checkbox.classList.add('checked');
} else {
checkbox.classList.remove('checked');
}
$('categories-list').disabled = shouldEnable;
$('wallpaper-grid').disabled = shouldEnable;
} else {
// TODO(bshe): show error message to user.
console.error('Failed to save surprise me option to chrome storage.');
}
});
};
/**
* One-time initialization of various DOM nodes.
*/
WallpaperManager.prototype.initDom_ = function() {
i18nTemplate.process(this.document_, loadTimeData);
this.initCategoriesList_();
this.initThumbnailsGrid_();
this.presetCategory_();
$('file-selector').addEventListener(
'change', this.onFileSelectorChanged_.bind(this));
$('set-wallpaper-layout').addEventListener(
'change', this.onWallpaperLayoutChanged_.bind(this));
if (this.enableOnlineWallpaper_) {
var self = this;
$('surprise-me').hidden = false;
$('surprise-me').addEventListener('click',
this.toggleSurpriseMe_.bind(this));
Constants.WallpaperLocalStorage.get(Constants.AccessSurpriseMeEnabledKey,
function(items) {
if (items[Constants.AccessSurpriseMeEnabledKey]) {
$('surprise-me').querySelector('#checkbox').classList.add('checked');
$('categories-list').disabled = true;
$('wallpaper-grid').disabled = true;
}
});
window.addEventListener('offline', function() {
chrome.wallpaperPrivate.getOfflineWallpaperList(function(lists) {
if (!self.downloadedListMap_)
self.downloadedListMap_ = {};
for (var i = 0; i < lists.length; i++) {
self.downloadedListMap_[lists[i]] = true;
}
var thumbnails = self.document_.querySelectorAll('.thumbnail');
for (var i = 0; i < thumbnails.length; i++) {
var thumbnail = thumbnails[i];
var url = self.wallpaperGrid_.dataModel.item(i).baseURL;
var fileName = url.substring(url.lastIndexOf('/') + 1) +
Constants.HighResolutionSuffix;
if (self.downloadedListMap_ &&
self.downloadedListMap_.hasOwnProperty(encodeURI(fileName))) {
thumbnail.offline = true;
}
}
});
$('wallpaper-grid').classList.add('image-picker-offline');
});
window.addEventListener('online', function() {
self.downloadedListMap_ = null;
$('wallpaper-grid').classList.remove('image-picker-offline');
});
}
$('window-close-button').addEventListener('click', function() {
window.close();
});
this.document_.defaultView.addEventListener(
'resize', this.onResize_.bind(this));
this.document_.defaultView.addEventListener(
'keydown', this.onKeyDown_.bind(this));
$('learn-more').href = LearnMoreURL;
$('close-error').addEventListener('click', function() {
$('error-container').hidden = true;
});
$('close-wallpaper-selection').addEventListener('click', function() {
$('wallpaper-selection-container').hidden = true;
$('set-wallpaper-layout').disabled = true;
});
this.onResize_();
this.initContextMenuAndCommand_();
};
/**
* One-time initialization of context menu and command.
*/
WallpaperManager.prototype.initContextMenuAndCommand_ = function() {
this.wallpaperContextMenu_ = $('wallpaper-context-menu');
cr.ui.Menu.decorate(this.wallpaperContextMenu_);
cr.ui.contextMenuHandler.setContextMenu(this.wallpaperGrid_,
this.wallpaperContextMenu_);
var commands = this.dialogDom_.querySelectorAll('command');
for (var i = 0; i < commands.length; i++)
cr.ui.Command.decorate(commands[i]);
var doc = this.document_;
doc.addEventListener('command', this.onCommand_.bind(this));
doc.addEventListener('canExecute', this.onCommandCanExecute_.bind(this));
};
/**
* Handles a command being executed.
* @param {Event} event A command event.
*/
WallpaperManager.prototype.onCommand_ = function(event) {
if (event.command.id == 'delete') {
var wallpaperGrid = this.wallpaperGrid_;
var selectedIndex = wallpaperGrid.selectionModel.selectedIndex;
var item = wallpaperGrid.dataModel.item(selectedIndex);
if (!item || item.source != Constants.WallpaperSourceEnum.Custom)
return;
this.removeCustomWallpaper(item.baseURL);
wallpaperGrid.dataModel.splice(selectedIndex, 1);
// Calculate the number of remaining custom wallpapers. The add new button
// in data model needs to be excluded.
var customWallpaperCount = wallpaperGrid.dataModel.length - 1;
if (customWallpaperCount == 0) {
// Active custom wallpaper is also copied in chronos data dir. It needs
// to be deleted.
chrome.wallpaperPrivate.resetWallpaper();
} else {
selectedIndex = Math.min(selectedIndex, customWallpaperCount - 1);
wallpaperGrid.selectionModel.selectedIndex = selectedIndex;
}
event.cancelBubble = true;
}
};
/**
* Decides if a command can be executed on current target.
* @param {Event} event A command event.
*/
WallpaperManager.prototype.onCommandCanExecute_ = function(event) {
switch (event.command.id) {
case 'delete':
var wallpaperGrid = this.wallpaperGrid_;
var selectedIndex = wallpaperGrid.selectionModel.selectedIndex;
var item = wallpaperGrid.dataModel.item(selectedIndex);
if (selectedIndex != this.wallpaperGrid_.dataModel.length - 1 &&
item && item.source == Constants.WallpaperSourceEnum.Custom) {
event.canExecute = true;
break;
}
default:
event.canExecute = false;
}
};
/**
* Preset to the category which contains current wallpaper.
*/
WallpaperManager.prototype.presetCategory_ = function() {
this.currentWallpaper_ = str('currentWallpaper');
// The currentWallpaper_ is either a url contains HightResolutionSuffix or a
// custom wallpaper file name converted from an integer value represent
// time (e.g., 13006377367586070).
if (!this.enableOnlineWallpaper_ || (this.currentWallpaper_ &&
this.currentWallpaper_.indexOf(Constants.HighResolutionSuffix) == -1)) {
// Custom is the last one in the categories list.
this.categoriesList_.selectionModel.selectedIndex =
this.categoriesList_.dataModel.length - 1;
return;
}
var self = this;
var presetCategoryInner_ = function() {
// Selects the first category in the categories list of current
// wallpaper as the default selected category when showing wallpaper
// picker UI.
var presetCategory = AllCategoryIndex;
if (self.currentWallpaper_) {
for (var key in self.manifest_.wallpaper_list) {
var url = self.manifest_.wallpaper_list[key].base_url +
Constants.HighResolutionSuffix;
if (url.indexOf(self.currentWallpaper_) != -1 &&
self.manifest_.wallpaper_list[key].categories.length > 0) {
presetCategory = self.manifest_.wallpaper_list[key].categories[0] +
OnlineCategoriesOffset;
break;
}
}
}
self.categoriesList_.selectionModel.selectedIndex = presetCategory;
};
if (navigator.onLine) {
presetCategoryInner_();
} else {
// If device is offline, gets the available offline wallpaper list first.
// Wallpapers which are not in the list will display a grayscaled
// thumbnail.
chrome.wallpaperPrivate.getOfflineWallpaperList(function(lists) {
if (!self.downloadedListMap_)
self.downloadedListMap_ = {};
for (var i = 0; i < lists.length; i++)
self.downloadedListMap_[lists[i]] = true;
presetCategoryInner_();
});
}
};
/**
* Constructs the thumbnails grid.
*/
WallpaperManager.prototype.initThumbnailsGrid_ = function() {
this.wallpaperGrid_ = $('wallpaper-grid');
wallpapers.WallpaperThumbnailsGrid.decorate(this.wallpaperGrid_);
this.wallpaperGrid_.autoExpands = true;
this.wallpaperGrid_.addEventListener('change', this.onChange_.bind(this));
this.wallpaperGrid_.addEventListener('dblclick', this.onClose_.bind(this));
};
/**
* Handles change event dispatched by wallpaper grid.
*/
WallpaperManager.prototype.onChange_ = function() {
// splice may dispatch a change event because the position of selected
// element changing. But the actual selected element may not change after
// splice. Check if the new selected element equals to the previous selected
// element before continuing. Otherwise, wallpaper may reset to previous one
// as described in http://crbug.com/229036.
if (this.selectedItem_ == this.wallpaperGrid_.selectedItem)
return;
this.selectedItem_ = this.wallpaperGrid_.selectedItem;
this.onSelectedItemChanged_();
};
/**
* Closes window if no pending wallpaper request.
*/
WallpaperManager.prototype.onClose_ = function() {
if (this.wallpaperRequest_) {
this.wallpaperRequest_.addEventListener('loadend', function() {
// Close window on wallpaper loading finished.
window.close();
});
} else {
window.close();
}
};
/**
* Sets wallpaper to the corresponding wallpaper of selected thumbnail.
* @param {{baseURL: string, layout: string, source: string,
* availableOffline: boolean, opt_dynamicURL: string,
* opt_author: string, opt_authorWebsite: string}}
* selectedItem the selected item in WallpaperThumbnailsGrid's data
* model.
*/
WallpaperManager.prototype.setSelectedWallpaper_ = function(selectedItem) {
var self = this;
switch (selectedItem.source) {
case Constants.WallpaperSourceEnum.Custom:
var errorHandler = this.onFileSystemError_.bind(this);
var setActive = function() {
self.wallpaperGrid_.activeItem = selectedItem;
self.currentWallpaper_ = selectedItem.baseURL;
};
var success = function(dirEntry) {
dirEntry.getFile(selectedItem.baseURL, {create: false},
function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.addEventListener('error', errorHandler);
reader.addEventListener('load', function(e) {
self.setCustomWallpaper(e.target.result,
selectedItem.layout,
false, selectedItem.baseURL,
setActive, errorHandler);
});
}, errorHandler);
}, errorHandler);
}
this.wallpaperDirs_.getDirectory(WallpaperDirNameEnum.ORIGINAL,
success, errorHandler);
break;
case Constants.WallpaperSourceEnum.OEM:
// Resets back to default wallpaper.
chrome.wallpaperPrivate.resetWallpaper();
this.currentWallpaper_ = selectedItem.baseURL;
this.wallpaperGrid_.activeItem = selectedItem;
WallpaperUtil.saveWallpaperInfo(wallpaperURL, selectedItem.layout,
selectedItem.source);
break;
case Constants.WallpaperSourceEnum.Online:
var wallpaperURL = selectedItem.baseURL +
Constants.HighResolutionSuffix;
var selectedGridItem = this.wallpaperGrid_.getListItem(selectedItem);
chrome.wallpaperPrivate.setWallpaperIfExists(wallpaperURL,
selectedItem.layout,
function(exists) {
if (exists) {
self.currentWallpaper_ = wallpaperURL;
self.wallpaperGrid_.activeItem = selectedItem;
WallpaperUtil.saveWallpaperInfo(wallpaperURL, selectedItem.layout,
selectedItem.source);
return;
}
// Falls back to request wallpaper from server.
if (self.wallpaperRequest_)
self.wallpaperRequest_.abort();
self.wallpaperRequest_ = new XMLHttpRequest();
self.progressManager_.reset(self.wallpaperRequest_, selectedGridItem);
var onSuccess = function(xhr) {
var image = xhr.response;
chrome.wallpaperPrivate.setWallpaper(image, selectedItem.layout,
wallpaperURL,
self.onFinished_.bind(self, selectedGridItem, selectedItem));
self.currentWallpaper_ = wallpaperURL;
WallpaperUtil.saveWallpaperInfo(wallpaperURL, selectedItem.layout,
selectedItem.source);
self.wallpaperRequest_ = null;
};
var onFailure = function() {
self.progressManager_.hideProgressBar(selectedGridItem);
self.showError_(str('downloadFailed'));
self.wallpaperRequest_ = null;
};
WallpaperUtil.fetchURL(wallpaperURL, 'arraybuffer', onSuccess,
onFailure, self.wallpaperRequest_);
});
break;
default:
console.error('Unsupported wallpaper source.');
}
};
/*
* Removes the oldest custom wallpaper. If the oldest one is set as current
* wallpaper, removes the second oldest one to free some space. This should
* only be called when exceeding wallpaper quota.
*/
WallpaperManager.prototype.removeOldestWallpaper_ = function() {
// Custom wallpapers should already sorted when put to the data model. The
// last element is the add new button, need to exclude it as well.
var oldestIndex = this.wallpaperGrid_.dataModel.length - 2;
var item = this.wallpaperGrid_.dataModel.item(oldestIndex);
if (!item || item.source != Constants.WallpaperSourceEnum.Custom)
return;
if (item.baseURL == this.currentWallpaper_)
item = this.wallpaperGrid_.dataModel.item(--oldestIndex);
if (item) {
this.removeCustomWallpaper(item.baseURL);
this.wallpaperGrid_.dataModel.splice(oldestIndex, 1);
}
};
/*
* Shows an error message to user and log the failed reason in console.
*/
WallpaperManager.prototype.onFileSystemError_ = function(e) {
var msg = '';
switch (e.code) {
case FileError.QUOTA_EXCEEDED_ERR:
msg = 'QUOTA_EXCEEDED_ERR';
// Instead of simply remove oldest wallpaper, we should consider a
// better way to handle this situation. See crbug.com/180890.
this.removeOldestWallpaper_();
break;
case FileError.NOT_FOUND_ERR:
msg = 'NOT_FOUND_ERR';
break;
case FileError.SECURITY_ERR:
msg = 'SECURITY_ERR';
break;
case FileError.INVALID_MODIFICATION_ERR:
msg = 'INVALID_MODIFICATION_ERR';
break;
case FileError.INVALID_STATE_ERR:
msg = 'INVALID_STATE_ERR';
break;
default:
msg = 'Unknown Error';
break;
}
console.error('Error: ' + msg);
this.showError_(str('accessFileFailure'));
};
/**
* Handles changing of selectedItem in wallpaper manager.
*/
WallpaperManager.prototype.onSelectedItemChanged_ = function() {
this.setWallpaperAttribution_(this.selectedItem_);
if (!this.selectedItem_ || this.selectedItem_.source == 'ADDNEW')
return;
if (this.selectedItem_.baseURL && !this.wallpaperGrid_.inProgramSelection) {
if (this.selectedItem_.source == Constants.WallpaperSourceEnum.Custom) {
var items = {};
var key = this.selectedItem_.baseURL;
var self = this;
Constants.WallpaperLocalStorage.get(key, function(items) {
self.selectedItem_.layout =
items[key] ? items[key] : 'CENTER_CROPPED';
self.setSelectedWallpaper_(self.selectedItem_);
});
} else {
this.setSelectedWallpaper_(this.selectedItem_);
}
}
};
/**
* Set attributions of wallpaper with given URL. If URL is not valid, clear
* the attributions.
* @param {{baseURL: string, dynamicURL: string, layout: string,
* author: string, authorWebsite: string, availableOffline: boolean}}
* selectedItem selected wallpaper item in grid.
* @private
*/
WallpaperManager.prototype.setWallpaperAttribution_ = function(selectedItem) {
// Only online wallpapers have author and website attributes. All other type
// of wallpapers should not show attributions.
if (selectedItem &&
selectedItem.source == Constants.WallpaperSourceEnum.Online) {
$('author-name').textContent = selectedItem.author;
$('author-website').textContent = $('author-website').href =
selectedItem.authorWebsite;
chrome.wallpaperPrivate.getThumbnail(selectedItem.baseURL,
selectedItem.source,
function(data) {
var img = $('attribute-image');
if (data) {
var blob = new Blob([new Int8Array(data)], {'type' : 'image\/png'});
img.src = window.URL.createObjectURL(blob);
img.addEventListener('load', function(e) {
window.URL.revokeObjectURL(this.src);
});
} else {
img.src = '';
}
});
$('wallpaper-attribute').hidden = false;
$('attribute-image').hidden = false;
return;
}
$('wallpaper-attribute').hidden = true;
$('attribute-image').hidden = true;
$('author-name').textContent = '';
$('author-website').textContent = $('author-website').href = '';
$('attribute-image').src = '';
};
/**
* Resize thumbnails grid and categories list to fit the new window size.
*/
WallpaperManager.prototype.onResize_ = function() {
this.wallpaperGrid_.redraw();
this.categoriesList_.redraw();
};
/**
* Close the last opened overlay on pressing the Escape key.
* @param {Event} event A keydown event.
*/
WallpaperManager.prototype.onKeyDown_ = function(event) {
if (event.keyCode == 27) {
// The last opened overlay coincides with the first match of querySelector
// because the Error Container is declared in the DOM before the Wallpaper
// Selection Container.
// TODO(bshe): Make the overlay selection not dependent on the DOM.
var closeButtonSelector = '.overlay-container:not([hidden]) .close';
var closeButton = this.document_.querySelector(closeButtonSelector);
if (closeButton) {
closeButton.click();
event.preventDefault();
}
}
};
/**
* Constructs the categories list.
*/
WallpaperManager.prototype.initCategoriesList_ = function() {
this.categoriesList_ = $('categories-list');
cr.ui.List.decorate(this.categoriesList_);
// cr.ui.list calculates items in view port based on client height and item
// height. However, categories list is displayed horizontally. So we should
// not calculate visible items here. Sets autoExpands to true to show every
// item in the list.
// TODO(bshe): Use ul to replace cr.ui.list for category list.
this.categoriesList_.autoExpands = true;
var self = this;
this.categoriesList_.itemConstructor = function(entry) {
return self.renderCategory_(entry);
};
this.categoriesList_.selectionModel = new cr.ui.ListSingleSelectionModel();
this.categoriesList_.selectionModel.addEventListener(
'change', this.onCategoriesChange_.bind(this));
var categoriesDataModel = new cr.ui.ArrayDataModel([]);
if (this.enableOnlineWallpaper_) {
// Adds all category as first category.
categoriesDataModel.push(str('allCategoryLabel'));
for (var key in this.manifest_.categories) {
categoriesDataModel.push(this.manifest_.categories[key]);
}
}
// Adds custom category as last category.
categoriesDataModel.push(str('customCategoryLabel'));
this.categoriesList_.dataModel = categoriesDataModel;
};
/**
* Constructs the element in categories list.
* @param {string} entry Text content of a category.
*/
WallpaperManager.prototype.renderCategory_ = function(entry) {
var li = this.document_.createElement('li');
cr.defineProperty(li, 'custom', cr.PropertyKind.BOOL_ATTR);
li.custom = (entry == str('customCategoryLabel'));
cr.defineProperty(li, 'lead', cr.PropertyKind.BOOL_ATTR);
cr.defineProperty(li, 'selected', cr.PropertyKind.BOOL_ATTR);
var div = this.document_.createElement('div');
div.textContent = entry;
li.appendChild(div);
return li;
};
/**
* Handles the custom wallpaper which user selected from file manager. Called
* when users select a file.
*/
WallpaperManager.prototype.onFileSelectorChanged_ = function() {
var files = $('file-selector').files;
if (files.length != 1)
console.error('More than one files are selected or no file selected');
if (!files[0].type.match('image/jpeg') &&
!files[0].type.match('image/png')) {
this.showError_(str('invalidWallpaper'));
return;
}
var layout = getSelectedLayout();
var self = this;
var errorHandler = this.onFileSystemError_.bind(this);
var setSelectedFile = function(file, layout, fileName) {
var saveThumbnail = function(thumbnail) {
var success = function(dirEntry) {
dirEntry.getFile(fileName, {create: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.onwriteend = function(e) {
$('set-wallpaper-layout').disabled = false;
var wallpaperInfo = {
baseURL: fileName,
layout: layout,
source: Constants.WallpaperSourceEnum.Custom,
availableOffline: true
};
self.wallpaperGrid_.dataModel.splice(0, 0, wallpaperInfo);
self.wallpaperGrid_.selectedItem = wallpaperInfo;
self.wallpaperGrid_.activeItem = wallpaperInfo;
self.currentWallpaper_ = fileName;
WallpaperUtil.saveToStorage(self.currentWallpaper_, layout,
false);
};
fileWriter.onerror = errorHandler;
var blob = new Blob([new Int8Array(thumbnail)],
{'type' : 'image\/jpeg'});
fileWriter.write(blob);
}, errorHandler);
}, errorHandler);
};
self.wallpaperDirs_.getDirectory(WallpaperDirNameEnum.THUMBNAIL,
success, errorHandler);
};
var success = function(dirEntry) {
dirEntry.getFile(fileName, {create: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
fileWriter.addEventListener('writeend', function(e) {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.addEventListener('error', errorHandler);
reader.addEventListener('load', function(e) {
self.setCustomWallpaper(e.target.result, layout, true, fileName,
saveThumbnail, function() {
self.removeCustomWallpaper(fileName);
errorHandler();
});
});
});
fileWriter.addEventListener('error', errorHandler);
fileWriter.write(file);
}, errorHandler);
}, errorHandler);
};
self.wallpaperDirs_.getDirectory(WallpaperDirNameEnum.ORIGINAL, success,
errorHandler);
};
setSelectedFile(files[0], layout, new Date().getTime().toString());
};
/**
* Removes wallpaper and thumbnail with fileName from FileSystem.
* @param {string} fileName The file name of wallpaper and thumbnail to be
* removed.
*/
WallpaperManager.prototype.removeCustomWallpaper = function(fileName) {
var errorHandler = this.onFileSystemError_.bind(this);
var self = this;
var removeFile = function(fileName) {
var success = function(dirEntry) {
dirEntry.getFile(fileName, {create: false}, function(fileEntry) {
fileEntry.remove(function() {
}, errorHandler);
}, errorHandler);
}
// Removes copy of original.
self.wallpaperDirs_.getDirectory(WallpaperDirNameEnum.ORIGINAL, success,
errorHandler);
// Removes generated thumbnail.
self.wallpaperDirs_.getDirectory(WallpaperDirNameEnum.THUMBNAIL, success,
errorHandler);
};
removeFile(fileName);
};
/**
* Sets current wallpaper and generate thumbnail if generateThumbnail is true.
* @param {ArrayBuffer} wallpaper The binary representation of wallpaper.
* @param {string} layout The user selected wallpaper layout.
* @param {boolean} generateThumbnail True if need to generate thumbnail.
* @param {string} fileName The unique file name of wallpaper.
* @param {function(thumbnail):void} success Success callback. If
* generateThumbnail is true, the callback parameter should have the
* generated thumbnail.
* @param {function(e):void} failure Failure callback. Called when there is an
* error from FileSystem.
*/
WallpaperManager.prototype.setCustomWallpaper = function(wallpaper,
layout,
generateThumbnail,
fileName,
success,
failure) {
var self = this;
var onFinished = function(opt_thumbnail) {
if (chrome.runtime.lastError != undefined) {
self.showError_(chrome.runtime.lastError.message);
$('set-wallpaper-layout').disabled = true;
failure();
} else {
success(opt_thumbnail);
// Custom wallpapers are not synced yet. If login on a different
// computer after set a custom wallpaper, wallpaper wont change by sync.
WallpaperUtil.saveWallpaperInfo(fileName, layout,
Constants.WallpaperSourceEnum.Custom);
}
};
chrome.wallpaperPrivate.setCustomWallpaper(wallpaper, layout,
generateThumbnail,
fileName, onFinished);
};
/**
* Sets wallpaper finished. Displays error message if any.
* @param {WallpaperThumbnailsGridItem=} opt_selectedGridItem The wallpaper
* thumbnail grid item. It extends from cr.ui.ListItem.
* @param {{baseURL: string, layout: string, source: string,
* availableOffline: boolean, opt_dynamicURL: string,
* opt_author: string, opt_authorWebsite: string}=}
* opt_selectedItem the selected item in WallpaperThumbnailsGrid's data
* model.
*/
WallpaperManager.prototype.onFinished_ = function(opt_selectedGridItem,
opt_selectedItem) {
if (opt_selectedGridItem)
this.progressManager_.hideProgressBar(opt_selectedGridItem);
if (chrome.runtime.lastError != undefined) {
this.showError_(chrome.runtime.lastError.message);
} else if (opt_selectedItem) {
this.wallpaperGrid_.activeItem = opt_selectedItem;
}
};
/**
* Handles the layout setting change of custom wallpaper.
*/
WallpaperManager.prototype.onWallpaperLayoutChanged_ = function() {
var layout = getSelectedLayout();
var self = this;
chrome.wallpaperPrivate.setCustomWallpaperLayout(layout, function() {
if (chrome.runtime.lastError != undefined) {
self.showError_(chrome.runtime.lastError.message);
self.removeCustomWallpaper(fileName);
$('set-wallpaper-layout').disabled = true;
} else {
WallpaperUtil.saveToStorage(self.currentWallpaper_, layout, false);
}
});
};
/**
* Handles user clicking on a different category.
*/
WallpaperManager.prototype.onCategoriesChange_ = function() {
var categoriesList = this.categoriesList_;
var selectedIndex = categoriesList.selectionModel.selectedIndex;
if (selectedIndex == -1)
return;
var selectedListItem = categoriesList.getListItemByIndex(selectedIndex);
var bar = $('bar');
bar.style.left = selectedListItem.offsetLeft + 'px';
bar.style.width = selectedListItem.offsetWidth + 'px';
var wallpapersDataModel = new cr.ui.ArrayDataModel([]);
var selectedItem;
if (selectedListItem.custom) {
this.document_.body.setAttribute('custom', '');
var errorHandler = this.onFileSystemError_.bind(this);
var toArray = function(list) {
return Array.prototype.slice.call(list || [], 0);
}
var self = this;
var processResults = function(entries) {
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
var wallpaperInfo = {
baseURL: entry.name,
// The layout will be replaced by the actual value saved in
// local storage when requested later. Layout is not important
// for constructing thumbnails grid, we use CENTER_CROPPED here
// to speed up the process of constructing. So we do not need to
// wait for fetching correct layout.
layout: 'CENTER_CROPPED',
source: Constants.WallpaperSourceEnum.Custom,
availableOffline: true
};
wallpapersDataModel.push(wallpaperInfo);
}
if (loadTimeData.getBoolean('isOEMDefaultWallpaper')) {
var oemDefaultWallpaperElement = {
baseURL: 'OemDefaultWallpaper',
layout: 'CENTER_CROPPED',
source: Constants.WallpaperSourceEnum.OEM,
availableOffline: true
};
wallpapersDataModel.push(oemDefaultWallpaperElement);
}
for (var i = 0; i < wallpapersDataModel.length; i++) {
if (self.currentWallpaper_ == wallpapersDataModel.item(i).baseURL)
selectedItem = wallpapersDataModel.item(i);
}
var lastElement = {
baseURL: '',
layout: '',
source: Constants.WallpaperSourceEnum.AddNew,
availableOffline: true
};
wallpapersDataModel.push(lastElement);
self.wallpaperGrid_.dataModel = wallpapersDataModel;
self.wallpaperGrid_.selectedItem = selectedItem;
self.wallpaperGrid_.activeItem = selectedItem;
}
var success = function(dirEntry) {
var dirReader = dirEntry.createReader();
var entries = [];
// All of a directory's entries are not guaranteed to return in a single
// call.
var readEntries = function() {
dirReader.readEntries(function(results) {
if (!results.length) {
processResults(entries.sort());
} else {
entries = entries.concat(toArray(results));
readEntries();
}
}, errorHandler);
};
readEntries(); // Start reading dirs.
}
this.wallpaperDirs_.getDirectory(WallpaperDirNameEnum.ORIGINAL,
success, errorHandler);
} else {
this.document_.body.removeAttribute('custom');
for (var key in this.manifest_.wallpaper_list) {
if (selectedIndex == AllCategoryIndex ||
this.manifest_.wallpaper_list[key].categories.indexOf(
selectedIndex - OnlineCategoriesOffset) != -1) {
var wallpaperInfo = {
baseURL: this.manifest_.wallpaper_list[key].base_url,
layout: this.manifest_.wallpaper_list[key].default_layout,
source: Constants.WallpaperSourceEnum.Online,
availableOffline: false,
author: this.manifest_.wallpaper_list[key].author,
authorWebsite: this.manifest_.wallpaper_list[key].author_website,
dynamicURL: this.manifest_.wallpaper_list[key].dynamic_url
};
var startIndex = wallpaperInfo.baseURL.lastIndexOf('/') + 1;
var fileName = wallpaperInfo.baseURL.substring(startIndex) +
Constants.HighResolutionSuffix;
if (this.downloadedListMap_ &&
this.downloadedListMap_.hasOwnProperty(encodeURI(fileName))) {
wallpaperInfo.availableOffline = true;
}
wallpapersDataModel.push(wallpaperInfo);
var url = this.manifest_.wallpaper_list[key].base_url +
Constants.HighResolutionSuffix;
if (url == this.currentWallpaper_) {
selectedItem = wallpaperInfo;
}
}
}
this.wallpaperGrid_.dataModel = wallpapersDataModel;
this.wallpaperGrid_.selectedItem = selectedItem;
this.wallpaperGrid_.activeItem = selectedItem;
}
};
})();