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.
6 * WallpaperManager constructor.
8 * WallpaperManager objects encapsulate the functionality of the wallpaper
12 * @param {HTMLElement} dialogDom The DOM node containing the prototypical
16 function WallpaperManager(dialogDom
) {
17 this.dialogDom_
= dialogDom
;
18 this.document_
= dialogDom
.ownerDocument
;
19 this.enableOnlineWallpaper_
= loadTimeData
.valueExists('manifestBaseURL');
20 this.selectedItem_
= null;
21 this.progressManager_
= new ProgressManager();
22 this.customWallpaperData_
= null;
23 this.currentWallpaper_
= null;
24 this.wallpaperRequest_
= null;
25 this.wallpaperDirs_
= WallpaperDirectories
.getInstance();
26 this.preManifestDomInit_();
27 this.fetchManifest_();
30 // Anonymous 'namespace'.
31 // TODO(bshe): Get rid of anonymous namespace.
35 * URL of the learn more page for wallpaper picker.
37 /** @const */ var LearnMoreURL
=
38 'https://support.google.com/chromebook/?p=wallpaper_fileerror&hl=' +
42 * Index of the All category. It is the first category in wallpaper picker.
44 /** @const */ var AllCategoryIndex
= 0;
47 * Index offset of categories parsed from manifest. The All category is added
48 * before them. So the offset is 1.
50 /** @const */ var OnlineCategoriesOffset
= 1;
53 * Returns a translated string.
55 * Wrapper function to make dealing with translated strings more concise.
57 * @param {string} id The id of the string to return.
58 * @return {string} The translated string.
61 return loadTimeData
.getString(id
);
65 * Returns the base name for |file_path|.
66 * @param {string} file_path The path of the file.
67 * @return {string} The base name of the file.
69 function getBaseName(file_path
) {
70 return file_path
.substring(file_path
.lastIndexOf('/') + 1);
74 * Retruns the current selected layout.
75 * @return {string} The selected layout.
77 function getSelectedLayout() {
78 var setWallpaperLayout
= $('set-wallpaper-layout');
79 return setWallpaperLayout
.options
[setWallpaperLayout
.selectedIndex
].value
;
83 * Loads translated strings.
85 WallpaperManager
.initStrings = function(callback
) {
86 chrome
.wallpaperPrivate
.getStrings(function(strings
) {
87 loadTimeData
.data
= strings
;
94 * Requests wallpaper manifest file from server.
96 WallpaperManager
.prototype.fetchManifest_ = function() {
97 var locale
= navigator
.language
;
98 if (!this.enableOnlineWallpaper_
) {
99 this.postManifestDomInit_();
104 str('manifestBaseURL') + locale
+ '.json',
105 // Fallback url. Use 'en' locale by default.
106 str('manifestBaseURL') + 'en.json'];
108 var asyncFetchManifestFromUrls = function(urls
, func
, successCallback
,
113 if (index
< urls
.length
) {
114 func(loop
, urls
[index
]);
121 success: function(response
) {
122 successCallback(response
);
125 failure: function() {
132 var fetchManifestAsync = function(loop
, url
) {
133 var xhr
= new XMLHttpRequest();
135 xhr
.addEventListener('loadend', function(e
) {
136 if (this.status
== 200 && this.responseText
!= null) {
138 var manifest
= JSON
.parse(this.responseText
);
139 loop
.success(manifest
);
147 xhr
.open('GET', url
, true);
154 if (navigator
.onLine
) {
155 asyncFetchManifestFromUrls(urls
, fetchManifestAsync
,
156 this.onLoadManifestSuccess_
.bind(this),
157 this.onLoadManifestFailed_
.bind(this));
159 // If device is offline, fetches manifest from local storage.
160 // TODO(bshe): Always loading the offline manifest first and replacing
161 // with the online one when available.
162 this.onLoadManifestFailed_();
167 * Shows error message in a centered dialog.
169 * @param {string} errroMessage The string to show in the error dialog.
171 WallpaperManager
.prototype.showError_ = function(errorMessage
) {
172 document
.querySelector('.error-message').textContent
= errorMessage
;
173 $('error-container').hidden
= false;
177 * Sets manifest loaded from server. Called after manifest is successfully
179 * @param {object} manifest The parsed manifest file.
181 WallpaperManager
.prototype.onLoadManifestSuccess_ = function(manifest
) {
182 this.manifest_
= manifest
;
183 WallpaperUtil
.saveToLocalStorage(Constants
.AccessLocalManifestKey
,
185 this.postManifestDomInit_();
188 // Sets manifest to previously saved object if any and shows connection error.
189 // Called after manifest failed to load.
190 WallpaperManager
.prototype.onLoadManifestFailed_ = function() {
191 var accessManifestKey
= Constants
.AccessLocalManifestKey
;
193 Constants
.WallpaperLocalStorage
.get(accessManifestKey
, function(items
) {
194 self
.manifest_
= items
[accessManifestKey
] ?
195 items
[accessManifestKey
] : null;
196 self
.showError_(str('connectionFailed'));
197 self
.postManifestDomInit_();
198 $('wallpaper-grid').classList
.add('image-picker-offline');
203 * Toggle surprise me feature of wallpaper picker. It fires an storage
204 * onChanged event. Event handler for that event is in event_page.js.
207 WallpaperManager
.prototype.toggleSurpriseMe_ = function() {
209 var checkbox
= $('surprise-me').querySelector('#checkbox');
210 var shouldEnable
= !checkbox
.classList
.contains('checked');
211 var onSuccess = function() {
212 if (chrome
.runtime
.lastError
== null) {
214 self
.document_
.body
.removeAttribute('surprise-me-disabled');
215 checkbox
.classList
.add('checked');
216 // Hides the wallpaper set by message if there is any.
217 $('wallpaper-set-by-message').textContent
= '';
219 // Unchecking the "Surprise me" checkbox falls back to the previous
220 // wallpaper before "Surprise me" was turned on.
221 if (self
.wallpaperGrid_
.activeItem
) {
222 self
.setSelectedWallpaper_(self
.wallpaperGrid_
.activeItem
);
223 self
.onWallpaperChanged_(self
.wallpaperGrid_
.activeItem
,
224 self
.currentWallpaper_
);
226 checkbox
.classList
.remove('checked');
227 self
.document_
.body
.setAttribute('surprise-me-disabled', '');
229 $('categories-list').disabled
= shouldEnable
;
230 $('wallpaper-grid').disabled
= shouldEnable
;
232 // TODO(bshe): show error message to user.
233 console
.error('Failed to save surprise me option to chrome storage.');
237 // To prevent the onChanged event being fired twice, we only save the value
238 // to sync storage if the sync theme is enabled, otherwise save it to local
240 WallpaperUtil
.enabledSyncThemesCallback(function(syncEnabled
) {
242 WallpaperUtil
.saveToSyncStorage(
243 Constants
.AccessSyncSurpriseMeEnabledKey
, shouldEnable
, onSuccess
);
245 WallpaperUtil
.saveToLocalStorage(
246 Constants
.AccessLocalSurpriseMeEnabledKey
, shouldEnable
, onSuccess
);
251 * One-time initialization of various DOM nodes. Fetching manifest may take a
252 * long time due to slow connection. Dom nodes that do not depend on manifest
253 * should be initialized here to unblock from manifest fetching.
255 WallpaperManager
.prototype.preManifestDomInit_ = function() {
256 $('window-close-button').addEventListener('click', function() {
259 this.document_
.defaultView
.addEventListener(
260 'resize', this.onResize_
.bind(this));
261 this.document_
.defaultView
.addEventListener(
262 'keydown', this.onKeyDown_
.bind(this));
263 $('learn-more').href
= LearnMoreURL
;
264 $('close-error').addEventListener('click', function() {
265 $('error-container').hidden
= true;
267 $('close-wallpaper-selection').addEventListener('click', function() {
268 $('wallpaper-selection-container').hidden
= true;
269 $('set-wallpaper-layout').disabled
= true;
274 * One-time initialization of various DOM nodes. Dom nodes that do depend on
275 * manifest should be initialized here.
277 WallpaperManager
.prototype.postManifestDomInit_ = function() {
278 i18nTemplate
.process(this.document_
, loadTimeData
);
279 this.initCategoriesList_();
280 this.initThumbnailsGrid_();
281 this.presetCategory_();
283 $('file-selector').addEventListener(
284 'change', this.onFileSelectorChanged_
.bind(this));
285 $('set-wallpaper-layout').addEventListener(
286 'change', this.onWallpaperLayoutChanged_
.bind(this));
288 if (loadTimeData
.valueExists('wallpaperAppName')) {
289 $('wallpaper-set-by-message').textContent
= loadTimeData
.getStringF(
290 'currentWallpaperSetByMessage', str('wallpaperAppName'));
293 if (this.enableOnlineWallpaper_
) {
295 self
.document_
.body
.setAttribute('surprise-me-disabled', '');
296 $('surprise-me').hidden
= false;
297 $('surprise-me').addEventListener('click',
298 this.toggleSurpriseMe_
.bind(this));
299 var onSurpriseMeEnabled = function() {
300 $('surprise-me').querySelector('#checkbox').classList
.add('checked');
301 $('categories-list').disabled
= true;
302 $('wallpaper-grid').disabled
= true;
303 self
.document_
.body
.removeAttribute('surprise-me-disabled');
306 WallpaperUtil
.enabledSyncThemesCallback(function(syncEnabled
) {
307 // Surprise me has been moved from local to sync storage, prefer
308 // values from sync, but if unset check local and update synced pref
311 Constants
.WallpaperSyncStorage
.get(
312 Constants
.AccessSyncSurpriseMeEnabledKey
, function(items
) {
313 if (items
.hasOwnProperty(
314 Constants
.AccessSyncSurpriseMeEnabledKey
)) {
315 if (items
[Constants
.AccessSyncSurpriseMeEnabledKey
]) {
316 onSurpriseMeEnabled();
319 Constants
.WallpaperLocalStorage
.get(
320 Constants
.AccessLocalSurpriseMeEnabledKey
, function(items
) {
321 if (items
.hasOwnProperty(
322 Constants
.AccessLocalSurpriseMeEnabledKey
)) {
323 WallpaperUtil
.saveToSyncStorage(
324 Constants
.AccessSyncSurpriseMeEnabledKey
,
325 items
[Constants
.AccessLocalSurpriseMeEnabledKey
]);
326 if (items
[Constants
.AccessLocalSurpriseMeEnabledKey
]) {
327 onSurpriseMeEnabled();
334 Constants
.WallpaperLocalStorage
.get(
335 Constants
.AccessLocalSurpriseMeEnabledKey
, function(items
) {
336 if (items
.hasOwnProperty(
337 Constants
.AccessLocalSurpriseMeEnabledKey
)) {
338 if (items
[Constants
.AccessLocalSurpriseMeEnabledKey
]) {
339 onSurpriseMeEnabled();
346 window
.addEventListener('offline', function() {
347 chrome
.wallpaperPrivate
.getOfflineWallpaperList(function(lists
) {
348 if (!self
.downloadedListMap_
)
349 self
.downloadedListMap_
= {};
350 for (var i
= 0; i
< lists
.length
; i
++) {
351 self
.downloadedListMap_
[lists
[i
]] = true;
353 var thumbnails
= self
.document_
.querySelectorAll('.thumbnail');
354 for (var i
= 0; i
< thumbnails
.length
; i
++) {
355 var thumbnail
= thumbnails
[i
];
356 var url
= self
.wallpaperGrid_
.dataModel
.item(i
).baseURL
;
357 var fileName
= getBaseName(url
) + Constants
.HighResolutionSuffix
;
358 if (self
.downloadedListMap_
&&
359 self
.downloadedListMap_
.hasOwnProperty(encodeURI(fileName
))) {
360 thumbnail
.offline
= true;
364 $('wallpaper-grid').classList
.add('image-picker-offline');
366 window
.addEventListener('online', function() {
367 self
.downloadedListMap_
= null;
368 $('wallpaper-grid').classList
.remove('image-picker-offline');
373 this.initContextMenuAndCommand_();
374 WallpaperUtil
.testSendMessage('launched');
378 * One-time initialization of context menu and command.
380 WallpaperManager
.prototype.initContextMenuAndCommand_ = function() {
381 this.wallpaperContextMenu_
= $('wallpaper-context-menu');
382 cr
.ui
.Menu
.decorate(this.wallpaperContextMenu_
);
383 cr
.ui
.contextMenuHandler
.setContextMenu(this.wallpaperGrid_
,
384 this.wallpaperContextMenu_
);
385 var commands
= this.dialogDom_
.querySelectorAll('command');
386 for (var i
= 0; i
< commands
.length
; i
++)
387 cr
.ui
.Command
.decorate(commands
[i
]);
389 var doc
= this.document_
;
390 doc
.addEventListener('command', this.onCommand_
.bind(this));
391 doc
.addEventListener('canExecute', this.onCommandCanExecute_
.bind(this));
395 * Handles a command being executed.
396 * @param {Event} event A command event.
398 WallpaperManager
.prototype.onCommand_ = function(event
) {
399 if (event
.command
.id
== 'delete') {
400 var wallpaperGrid
= this.wallpaperGrid_
;
401 var selectedIndex
= wallpaperGrid
.selectionModel
.selectedIndex
;
402 var item
= wallpaperGrid
.dataModel
.item(selectedIndex
);
403 if (!item
|| item
.source
!= Constants
.WallpaperSourceEnum
.Custom
)
405 this.removeCustomWallpaper(item
.baseURL
);
406 wallpaperGrid
.dataModel
.splice(selectedIndex
, 1);
407 // Calculate the number of remaining custom wallpapers. The add new button
408 // in data model needs to be excluded.
409 var customWallpaperCount
= wallpaperGrid
.dataModel
.length
- 1;
410 if (customWallpaperCount
== 0) {
411 // Active custom wallpaper is also copied in chronos data dir. It needs
413 chrome
.wallpaperPrivate
.resetWallpaper();
414 this.onWallpaperChanged_(null, null);
415 WallpaperUtil
.saveWallpaperInfo('', '',
416 Constants
.WallpaperSourceEnum
.Default
);
418 selectedIndex
= Math
.min(selectedIndex
, customWallpaperCount
- 1);
419 wallpaperGrid
.selectionModel
.selectedIndex
= selectedIndex
;
421 event
.cancelBubble
= true;
426 * Decides if a command can be executed on current target.
427 * @param {Event} event A command event.
429 WallpaperManager
.prototype.onCommandCanExecute_ = function(event
) {
430 switch (event
.command
.id
) {
432 var wallpaperGrid
= this.wallpaperGrid_
;
433 var selectedIndex
= wallpaperGrid
.selectionModel
.selectedIndex
;
434 var item
= wallpaperGrid
.dataModel
.item(selectedIndex
);
435 if (selectedIndex
!= this.wallpaperGrid_
.dataModel
.length
- 1 &&
436 item
&& item
.source
== Constants
.WallpaperSourceEnum
.Custom
) {
437 event
.canExecute
= true;
441 event
.canExecute
= false;
446 * Preset to the category which contains current wallpaper.
448 WallpaperManager
.prototype.presetCategory_ = function() {
449 this.currentWallpaper_
= str('currentWallpaper');
450 // The currentWallpaper_ is either a url contains HightResolutionSuffix or a
451 // custom wallpaper file name converted from an integer value represent
452 // time (e.g., 13006377367586070).
453 if (!this.enableOnlineWallpaper_
|| (this.currentWallpaper_
&&
454 this.currentWallpaper_
.indexOf(Constants
.HighResolutionSuffix
) == -1)) {
455 // Custom is the last one in the categories list.
456 this.categoriesList_
.selectionModel
.selectedIndex
=
457 this.categoriesList_
.dataModel
.length
- 1;
461 var presetCategoryInner_ = function() {
462 // Selects the first category in the categories list of current
463 // wallpaper as the default selected category when showing wallpaper
465 var presetCategory
= AllCategoryIndex
;
466 if (self
.currentWallpaper_
) {
467 for (var key
in self
.manifest_
.wallpaper_list
) {
468 var url
= self
.manifest_
.wallpaper_list
[key
].base_url
+
469 Constants
.HighResolutionSuffix
;
470 if (url
.indexOf(self
.currentWallpaper_
) != -1 &&
471 self
.manifest_
.wallpaper_list
[key
].categories
.length
> 0) {
472 presetCategory
= self
.manifest_
.wallpaper_list
[key
].categories
[0] +
473 OnlineCategoriesOffset
;
478 self
.categoriesList_
.selectionModel
.selectedIndex
= presetCategory
;
480 if (navigator
.onLine
) {
481 presetCategoryInner_();
483 // If device is offline, gets the available offline wallpaper list first.
484 // Wallpapers which are not in the list will display a grayscaled
486 chrome
.wallpaperPrivate
.getOfflineWallpaperList(function(lists
) {
487 if (!self
.downloadedListMap_
)
488 self
.downloadedListMap_
= {};
489 for (var i
= 0; i
< lists
.length
; i
++)
490 self
.downloadedListMap_
[lists
[i
]] = true;
491 presetCategoryInner_();
497 * Constructs the thumbnails grid.
499 WallpaperManager
.prototype.initThumbnailsGrid_ = function() {
500 this.wallpaperGrid_
= $('wallpaper-grid');
501 wallpapers
.WallpaperThumbnailsGrid
.decorate(this.wallpaperGrid_
);
503 this.wallpaperGrid_
.addEventListener('change', this.onChange_
.bind(this));
504 this.wallpaperGrid_
.addEventListener('dblclick', this.onClose_
.bind(this));
508 * Handles change event dispatched by wallpaper grid.
510 WallpaperManager
.prototype.onChange_ = function() {
511 // splice may dispatch a change event because the position of selected
512 // element changing. But the actual selected element may not change after
513 // splice. Check if the new selected element equals to the previous selected
514 // element before continuing. Otherwise, wallpaper may reset to previous one
515 // as described in http://crbug.com/229036.
516 if (this.selectedItem_
== this.wallpaperGrid_
.selectedItem
)
518 this.selectedItem_
= this.wallpaperGrid_
.selectedItem
;
519 this.onSelectedItemChanged_();
523 * Closes window if no pending wallpaper request.
525 WallpaperManager
.prototype.onClose_ = function() {
526 if (this.wallpaperRequest_
) {
527 this.wallpaperRequest_
.addEventListener('loadend', function() {
528 // Close window on wallpaper loading finished.
537 * Moves the check mark to |activeItem| and hides the wallpaper set by third
538 * party message if any. Called when wallpaper changed successfully.
539 * @param {?Object} activeItem The active item in WallpaperThumbnailsGrid's
541 * @param {?string} currentWallpaperURL The URL or filename of current
544 WallpaperManager
.prototype.onWallpaperChanged_ = function(
545 activeItem
, currentWallpaperURL
) {
546 this.wallpaperGrid_
.activeItem
= activeItem
;
547 this.currentWallpaper_
= currentWallpaperURL
;
548 // Hides the wallpaper set by message.
549 $('wallpaper-set-by-message').textContent
= '';
553 * Sets wallpaper to the corresponding wallpaper of selected thumbnail.
554 * @param {{baseURL: string, layout: string, source: string,
555 * availableOffline: boolean, opt_dynamicURL: string,
556 * opt_author: string, opt_authorWebsite: string}}
557 * selectedItem the selected item in WallpaperThumbnailsGrid's data
560 WallpaperManager
.prototype.setSelectedWallpaper_ = function(selectedItem
) {
562 switch (selectedItem
.source
) {
563 case Constants
.WallpaperSourceEnum
.Custom
:
564 var errorHandler
= this.onFileSystemError_
.bind(this);
565 var success = function(dirEntry
) {
566 dirEntry
.getFile(selectedItem
.baseURL
, {create
: false},
567 function(fileEntry
) {
568 fileEntry
.file(function(file
) {
569 var reader
= new FileReader();
570 reader
.readAsArrayBuffer(file
);
571 reader
.addEventListener('error', errorHandler
);
572 reader
.addEventListener('load', function(e
) {
573 self
.setCustomWallpaper(e
.target
.result
, selectedItem
.layout
,
574 false, selectedItem
.baseURL
,
575 function(thumbnailData
) {
576 self
.onWallpaperChanged_(selectedItem
,
577 selectedItem
.baseURL
, thumbnailData
);
578 WallpaperUtil
.storeWallpaperToSyncFS(
579 selectedItem
.baseURL
, e
.target
.result
);
580 WallpaperUtil
.storeWallpaperToSyncFS(
581 selectedItem
.baseURL
+
582 Constants
.CustomWallpaperThumbnailSuffix
,
590 this.wallpaperDirs_
.getDirectory(
591 Constants
.WallpaperDirNameEnum
.ORIGINAL
, success
, errorHandler
);
593 case Constants
.WallpaperSourceEnum
.OEM
:
594 // Resets back to default wallpaper.
595 chrome
.wallpaperPrivate
.resetWallpaper();
596 this.onWallpaperChanged_(selectedItem
, selectedItem
.baseURL
);
597 WallpaperUtil
.saveWallpaperInfo(wallpaperURL
, selectedItem
.layout
,
598 selectedItem
.source
);
600 case Constants
.WallpaperSourceEnum
.Online
:
601 var wallpaperURL
= selectedItem
.baseURL
+
602 Constants
.HighResolutionSuffix
;
603 var selectedGridItem
= this.wallpaperGrid_
.getListItem(selectedItem
);
605 chrome
.wallpaperPrivate
.setWallpaperIfExists(wallpaperURL
,
609 self
.onWallpaperChanged_(selectedItem
, wallpaperURL
);
610 WallpaperUtil
.saveWallpaperInfo(wallpaperURL
, selectedItem
.layout
,
611 selectedItem
.source
);
615 // Falls back to request wallpaper from server.
616 if (self
.wallpaperRequest_
)
617 self
.wallpaperRequest_
.abort();
619 self
.wallpaperRequest_
= new XMLHttpRequest();
620 self
.progressManager_
.reset(self
.wallpaperRequest_
, selectedGridItem
);
622 var onSuccess = function(xhr
) {
623 var image
= xhr
.response
;
624 chrome
.wallpaperPrivate
.setWallpaper(image
, selectedItem
.layout
,
627 self
.progressManager_
.hideProgressBar(selectedGridItem
);
629 if (chrome
.runtime
.lastError
!= undefined &&
630 chrome
.runtime
.lastError
.message
!=
631 str('canceledWallpaper')) {
632 self
.showError_(chrome
.runtime
.lastError
.message
);
634 self
.onWallpaperChanged_(selectedItem
, wallpaperURL
);
637 WallpaperUtil
.saveWallpaperInfo(wallpaperURL
, selectedItem
.layout
,
638 selectedItem
.source
);
639 self
.wallpaperRequest_
= null;
641 var onFailure = function(status
) {
642 self
.progressManager_
.hideProgressBar(selectedGridItem
);
643 self
.showError_(str('downloadFailed'));
644 self
.wallpaperRequest_
= null;
646 WallpaperUtil
.fetchURL(wallpaperURL
, 'arraybuffer', onSuccess
,
647 onFailure
, self
.wallpaperRequest_
);
651 console
.error('Unsupported wallpaper source.');
656 * Removes the oldest custom wallpaper. If the oldest one is set as current
657 * wallpaper, removes the second oldest one to free some space. This should
658 * only be called when exceeding wallpaper quota.
660 WallpaperManager
.prototype.removeOldestWallpaper_ = function() {
661 // Custom wallpapers should already sorted when put to the data model. The
662 // last element is the add new button, need to exclude it as well.
663 var oldestIndex
= this.wallpaperGrid_
.dataModel
.length
- 2;
664 var item
= this.wallpaperGrid_
.dataModel
.item(oldestIndex
);
665 if (!item
|| item
.source
!= Constants
.WallpaperSourceEnum
.Custom
)
667 if (item
.baseURL
== this.currentWallpaper_
)
668 item
= this.wallpaperGrid_
.dataModel
.item(--oldestIndex
);
670 this.removeCustomWallpaper(item
.baseURL
);
671 this.wallpaperGrid_
.dataModel
.splice(oldestIndex
, 1);
676 * Shows an error message to user and log the failed reason in console.
678 WallpaperManager
.prototype.onFileSystemError_ = function(e
) {
681 case FileError
.QUOTA_EXCEEDED_ERR
:
682 msg
= 'QUOTA_EXCEEDED_ERR';
683 // Instead of simply remove oldest wallpaper, we should consider a
684 // better way to handle this situation. See crbug.com/180890.
685 this.removeOldestWallpaper_();
687 case FileError
.NOT_FOUND_ERR
:
688 msg
= 'NOT_FOUND_ERR';
690 case FileError
.SECURITY_ERR
:
691 msg
= 'SECURITY_ERR';
693 case FileError
.INVALID_MODIFICATION_ERR
:
694 msg
= 'INVALID_MODIFICATION_ERR';
696 case FileError
.INVALID_STATE_ERR
:
697 msg
= 'INVALID_STATE_ERR';
700 msg
= 'Unknown Error';
703 console
.error('Error: ' + msg
);
704 this.showError_(str('accessFileFailure'));
708 * Handles changing of selectedItem in wallpaper manager.
710 WallpaperManager
.prototype.onSelectedItemChanged_ = function() {
711 this.setWallpaperAttribution_(this.selectedItem_
);
713 if (!this.selectedItem_
|| this.selectedItem_
.source
== 'ADDNEW')
716 if (this.selectedItem_
.baseURL
&& !this.wallpaperGrid_
.inProgramSelection
) {
717 if (this.selectedItem_
.source
== Constants
.WallpaperSourceEnum
.Custom
) {
719 var key
= this.selectedItem_
.baseURL
;
721 Constants
.WallpaperLocalStorage
.get(key
, function(items
) {
722 self
.selectedItem_
.layout
=
723 items
[key
] ? items
[key
] : 'CENTER_CROPPED';
724 self
.setSelectedWallpaper_(self
.selectedItem_
);
727 this.setSelectedWallpaper_(this.selectedItem_
);
733 * Set attributions of wallpaper with given URL. If URL is not valid, clear
735 * @param {{baseURL: string, dynamicURL: string, layout: string,
736 * author: string, authorWebsite: string, availableOffline: boolean}}
737 * selectedItem selected wallpaper item in grid.
740 WallpaperManager
.prototype.setWallpaperAttribution_ = function(selectedItem
) {
741 // Only online wallpapers have author and website attributes. All other type
742 // of wallpapers should not show attributions.
744 selectedItem
.source
== Constants
.WallpaperSourceEnum
.Online
) {
745 $('author-name').textContent
= selectedItem
.author
;
746 $('author-website').textContent
= $('author-website').href
=
747 selectedItem
.authorWebsite
;
748 chrome
.wallpaperPrivate
.getThumbnail(selectedItem
.baseURL
,
751 var img
= $('attribute-image');
753 var blob
= new Blob([new Int8Array(data
)], {'type' : 'image\/png'});
754 img
.src
= window
.URL
.createObjectURL(blob
);
755 img
.addEventListener('load', function(e
) {
756 window
.URL
.revokeObjectURL(this.src
);
762 $('wallpaper-attribute').hidden
= false;
763 $('attribute-image').hidden
= false;
766 $('wallpaper-attribute').hidden
= true;
767 $('attribute-image').hidden
= true;
768 $('author-name').textContent
= '';
769 $('author-website').textContent
= $('author-website').href
= '';
770 $('attribute-image').src
= '';
774 * Resize thumbnails grid and categories list to fit the new window size.
776 WallpaperManager
.prototype.onResize_ = function() {
777 this.wallpaperGrid_
.redraw();
778 this.categoriesList_
.redraw();
782 * Close the last opened overlay or app window on pressing the Escape key.
783 * @param {Event} event A keydown event.
785 WallpaperManager
.prototype.onKeyDown_ = function(event
) {
786 if (event
.keyCode
== 27) {
787 // The last opened overlay coincides with the first match of querySelector
788 // because the Error Container is declared in the DOM before the Wallpaper
789 // Selection Container.
790 // TODO(bshe): Make the overlay selection not dependent on the DOM.
791 var closeButtonSelector
= '.overlay-container:not([hidden]) .close';
792 var closeButton
= this.document_
.querySelector(closeButtonSelector
);
795 event
.preventDefault();
803 * Constructs the categories list.
805 WallpaperManager
.prototype.initCategoriesList_ = function() {
806 this.categoriesList_
= $('categories-list');
807 wallpapers
.WallpaperCategoriesList
.decorate(this.categoriesList_
);
809 this.categoriesList_
.selectionModel
.addEventListener(
810 'change', this.onCategoriesChange_
.bind(this));
812 if (this.enableOnlineWallpaper_
&& this.manifest_
) {
813 // Adds all category as first category.
814 this.categoriesList_
.dataModel
.push(str('allCategoryLabel'));
815 for (var key
in this.manifest_
.categories
) {
816 this.categoriesList_
.dataModel
.push(this.manifest_
.categories
[key
]);
819 // Adds custom category as last category.
820 this.categoriesList_
.dataModel
.push(str('customCategoryLabel'));
824 * Handles the custom wallpaper which user selected from file manager. Called
825 * when users select a file.
827 WallpaperManager
.prototype.onFileSelectorChanged_ = function() {
828 var files
= $('file-selector').files
;
829 if (files
.length
!= 1)
830 console
.error('More than one files are selected or no file selected');
831 if (!files
[0].type
.match('image/jpeg') &&
832 !files
[0].type
.match('image/png')) {
833 this.showError_(str('invalidWallpaper'));
836 var layout
= getSelectedLayout();
838 var errorHandler
= this.onFileSystemError_
.bind(this);
839 var setSelectedFile = function(file
, layout
, fileName
) {
840 var saveThumbnail = function(thumbnail
) {
841 var success = function(dirEntry
) {
842 dirEntry
.getFile(fileName
, {create
: true}, function(fileEntry
) {
843 fileEntry
.createWriter(function(fileWriter
) {
844 fileWriter
.onwriteend = function(e
) {
845 $('set-wallpaper-layout').disabled
= false;
846 var wallpaperInfo
= {
849 source
: Constants
.WallpaperSourceEnum
.Custom
,
850 availableOffline
: true
852 self
.wallpaperGrid_
.dataModel
.splice(0, 0, wallpaperInfo
);
853 self
.wallpaperGrid_
.selectedItem
= wallpaperInfo
;
854 self
.onWallpaperChanged_(wallpaperInfo
, fileName
);
855 WallpaperUtil
.saveToLocalStorage(self
.currentWallpaper_
,
859 fileWriter
.onerror
= errorHandler
;
861 var blob
= new Blob([new Int8Array(thumbnail
)],
862 {'type' : 'image\/jpeg'});
863 fileWriter
.write(blob
);
867 self
.wallpaperDirs_
.getDirectory(
868 Constants
.WallpaperDirNameEnum
.THUMBNAIL
, success
, errorHandler
);
870 var onCustomWallpaperSuccess = function(thumbnailData
, wallpaperData
) {
871 WallpaperUtil
.storeWallpaperToSyncFS(fileName
, wallpaperData
);
872 WallpaperUtil
.storeWallpaperToSyncFS(
873 fileName
+ Constants
.CustomWallpaperThumbnailSuffix
,
875 saveThumbnail(thumbnailData
);
877 var success = function(dirEntry
) {
878 dirEntry
.getFile(fileName
, {create
: true}, function(fileEntry
) {
879 fileEntry
.createWriter(function(fileWriter
) {
880 fileWriter
.addEventListener('writeend', function(e
) {
881 var reader
= new FileReader();
882 reader
.readAsArrayBuffer(file
);
883 reader
.addEventListener('error', errorHandler
);
884 reader
.addEventListener('load', function(e
) {
885 self
.setCustomWallpaper(e
.target
.result
, layout
, true, fileName
,
886 function(thumbnail
) {
887 onCustomWallpaperSuccess(thumbnail
, e
.target
.result
);
890 self
.removeCustomWallpaper(fileName
);
895 fileWriter
.addEventListener('error', errorHandler
);
896 fileWriter
.write(file
);
900 self
.wallpaperDirs_
.getDirectory(Constants
.WallpaperDirNameEnum
.ORIGINAL
,
904 setSelectedFile(files
[0], layout
, new Date().getTime().toString());
908 * Removes wallpaper and thumbnail with fileName from FileSystem.
909 * @param {string} fileName The file name of wallpaper and thumbnail to be
912 WallpaperManager
.prototype.removeCustomWallpaper = function(fileName
) {
913 var errorHandler
= this.onFileSystemError_
.bind(this);
915 var removeFile = function(fileName
) {
916 var success = function(dirEntry
) {
917 dirEntry
.getFile(fileName
, {create
: false}, function(fileEntry
) {
918 fileEntry
.remove(function() {
919 WallpaperUtil
.deleteWallpaperFromSyncFS(fileName
);
924 // Removes copy of original.
925 self
.wallpaperDirs_
.getDirectory(Constants
.WallpaperDirNameEnum
.ORIGINAL
,
929 // Removes generated thumbnail.
930 self
.wallpaperDirs_
.getDirectory(Constants
.WallpaperDirNameEnum
.THUMBNAIL
,
934 removeFile(fileName
);
938 * Sets current wallpaper and generate thumbnail if generateThumbnail is true.
939 * @param {ArrayBuffer} wallpaper The binary representation of wallpaper.
940 * @param {string} layout The user selected wallpaper layout.
941 * @param {boolean} generateThumbnail True if need to generate thumbnail.
942 * @param {string} fileName The unique file name of wallpaper.
943 * @param {function(thumbnail):void} success Success callback. If
944 * generateThumbnail is true, the callback parameter should have the
945 * generated thumbnail.
946 * @param {function(e):void} failure Failure callback. Called when there is an
947 * error from FileSystem.
949 WallpaperManager
.prototype.setCustomWallpaper = function(wallpaper
,
956 var onFinished = function(opt_thumbnail
) {
957 if (chrome
.runtime
.lastError
!= undefined &&
958 chrome
.runtime
.lastError
.message
!= str('canceledWallpaper')) {
959 self
.showError_(chrome
.runtime
.lastError
.message
);
960 $('set-wallpaper-layout').disabled
= true;
963 success(opt_thumbnail
);
964 // Custom wallpapers are not synced yet. If login on a different
965 // computer after set a custom wallpaper, wallpaper wont change by sync.
966 WallpaperUtil
.saveWallpaperInfo(fileName
, layout
,
967 Constants
.WallpaperSourceEnum
.Custom
);
971 chrome
.wallpaperPrivate
.setCustomWallpaper(wallpaper
, layout
,
973 fileName
, onFinished
);
977 * Handles the layout setting change of custom wallpaper.
979 WallpaperManager
.prototype.onWallpaperLayoutChanged_ = function() {
980 var layout
= getSelectedLayout();
982 chrome
.wallpaperPrivate
.setCustomWallpaperLayout(layout
, function() {
983 if (chrome
.runtime
.lastError
!= undefined &&
984 chrome
.runtime
.lastError
.message
!= str('canceledWallpaper')) {
985 self
.showError_(chrome
.runtime
.lastError
.message
);
986 self
.removeCustomWallpaper(fileName
);
987 $('set-wallpaper-layout').disabled
= true;
989 WallpaperUtil
.saveToLocalStorage(self
.currentWallpaper_
, layout
);
990 self
.onWallpaperChanged_(self
.wallpaperGrid_
.activeItem
,
991 self
.currentWallpaper_
);
997 * Handles user clicking on a different category.
999 WallpaperManager
.prototype.onCategoriesChange_ = function() {
1000 var categoriesList
= this.categoriesList_
;
1001 var selectedIndex
= categoriesList
.selectionModel
.selectedIndex
;
1002 if (selectedIndex
== -1)
1004 var selectedListItem
= categoriesList
.getListItemByIndex(selectedIndex
);
1006 bar
.style
.left
= selectedListItem
.offsetLeft
+ 'px';
1007 bar
.style
.width
= selectedListItem
.offsetWidth
+ 'px';
1009 var wallpapersDataModel
= new cr
.ui
.ArrayDataModel([]);
1010 var selectedItem
= null;
1011 if (selectedListItem
.custom
) {
1012 this.document_
.body
.setAttribute('custom', '');
1013 var errorHandler
= this.onFileSystemError_
.bind(this);
1014 var toArray = function(list
) {
1015 return Array
.prototype.slice
.call(list
|| [], 0);
1019 var processResults = function(entries
) {
1020 for (var i
= 0; i
< entries
.length
; i
++) {
1021 var entry
= entries
[i
];
1022 var wallpaperInfo
= {
1023 // Set wallpaperId to null to avoid duplicate thumbnail images,
1024 // see crbug.com/506135 for details.
1026 baseURL
: entry
.name
,
1027 // The layout will be replaced by the actual value saved in
1028 // local storage when requested later. Layout is not important
1029 // for constructing thumbnails grid, we use CENTER_CROPPED here
1030 // to speed up the process of constructing. So we do not need to
1031 // wait for fetching correct layout.
1032 layout
: 'CENTER_CROPPED',
1033 source
: Constants
.WallpaperSourceEnum
.Custom
,
1034 availableOffline
: true
1036 wallpapersDataModel
.push(wallpaperInfo
);
1038 if (loadTimeData
.getBoolean('isOEMDefaultWallpaper')) {
1039 var oemDefaultWallpaperElement
= {
1041 baseURL
: 'OemDefaultWallpaper',
1042 layout
: 'CENTER_CROPPED',
1043 source
: Constants
.WallpaperSourceEnum
.OEM
,
1044 availableOffline
: true
1046 wallpapersDataModel
.push(oemDefaultWallpaperElement
);
1048 for (var i
= 0; i
< wallpapersDataModel
.length
; i
++) {
1049 // For custom wallpapers, the file name of |currentWallpaper_|
1050 // includes the first directory level (corresponding to user id hash).
1051 if (getBaseName(self
.currentWallpaper_
) ==
1052 wallpapersDataModel
.item(i
).baseURL
) {
1053 selectedItem
= wallpapersDataModel
.item(i
);
1059 source
: Constants
.WallpaperSourceEnum
.AddNew
,
1060 availableOffline
: true
1062 wallpapersDataModel
.push(lastElement
);
1063 self
.wallpaperGrid_
.dataModel
= wallpapersDataModel
;
1065 self
.wallpaperGrid_
.selectedItem
= selectedItem
;
1066 self
.wallpaperGrid_
.activeItem
= selectedItem
;
1070 var success = function(dirEntry
) {
1071 var dirReader
= dirEntry
.createReader();
1073 // All of a directory's entries are not guaranteed to return in a single
1075 var readEntries = function() {
1076 dirReader
.readEntries(function(results
) {
1077 if (!results
.length
) {
1078 processResults(entries
.sort());
1080 entries
= entries
.concat(toArray(results
));
1085 readEntries(); // Start reading dirs.
1087 this.wallpaperDirs_
.getDirectory(Constants
.WallpaperDirNameEnum
.ORIGINAL
,
1088 success
, errorHandler
);
1090 this.document_
.body
.removeAttribute('custom');
1091 // Need this check for test purpose.
1092 var numOnlineWallpaper
= (this.enableOnlineWallpaper_
&& this.manifest_
) ?
1093 this.manifest_
.wallpaper_list
.length
: 0;
1094 for (var i
= 0; i
< numOnlineWallpaper
; i
++) {
1095 if (selectedIndex
== AllCategoryIndex
||
1096 this.manifest_
.wallpaper_list
[i
].categories
.indexOf(
1097 selectedIndex
- OnlineCategoriesOffset
) != -1) {
1098 var wallpaperInfo
= {
1100 baseURL
: this.manifest_
.wallpaper_list
[i
].base_url
,
1101 layout
: this.manifest_
.wallpaper_list
[i
].default_layout
,
1102 source
: Constants
.WallpaperSourceEnum
.Online
,
1103 availableOffline
: false,
1104 author
: this.manifest_
.wallpaper_list
[i
].author
,
1105 authorWebsite
: this.manifest_
.wallpaper_list
[i
].author_website
,
1106 dynamicURL
: this.manifest_
.wallpaper_list
[i
].dynamic_url
1108 var fileName
= getBaseName(wallpaperInfo
.baseURL
) +
1109 Constants
.HighResolutionSuffix
;
1110 if (this.downloadedListMap_
&&
1111 this.downloadedListMap_
.hasOwnProperty(encodeURI(fileName
))) {
1112 wallpaperInfo
.availableOffline
= true;
1114 wallpapersDataModel
.push(wallpaperInfo
);
1115 var url
= this.manifest_
.wallpaper_list
[i
].base_url
+
1116 Constants
.HighResolutionSuffix
;
1117 if (url
== this.currentWallpaper_
) {
1118 selectedItem
= wallpaperInfo
;
1122 this.wallpaperGrid_
.dataModel
= wallpapersDataModel
;
1124 this.wallpaperGrid_
.selectedItem
= selectedItem
;
1125 this.wallpaperGrid_
.activeItem
= selectedItem
;