Add public API generation with cr.makePublic() and handle it in compiler pass
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / login / oobe_screen_user_image.js
blob8c68ab3f4303267ac2364b7df82ae0fe58e35431
1 // Copyright (c) 2012 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 /**
6  * @fileoverview Oobe user image screen implementation.
7  */
9 cr.define('login', function() {
10   var UserImagesGrid = options.UserImagesGrid;
11   var ButtonImages = UserImagesGrid.ButtonImages;
13   /**
14    * Array of button URLs used on this page.
15    * @type {Array.<string>}
16    * @const
17    */
18   var ButtonImageUrls = [
19     ButtonImages.TAKE_PHOTO
20   ];
22   /**
23    * Whether the web camera item should be preselected, if available.
24    * @type {boolean}
25    * @const
26    */
27   var PRESELECT_CAMERA = false;
29   /**
30    * Creates a new OOBE screen div.
31    * @constructor
32    * @extends {HTMLDivElement}
33    */
34   var UserImageScreen = cr.ui.define(login.Screen);
36   /**
37    * Registers with Oobe.
38    * @param {boolean} lazyInit If true, screen is decorated on first show.
39    */
40   UserImageScreen.register = function(lazyInit) {
41     var screen = $('user-image');
42     if (lazyInit) {
43       screen.__proto__ = UserImageScreen.prototype;
44       screen.deferredDecorate = function() {
45         UserImageScreen.decorate(screen);
46       };
47     } else {
48       UserImageScreen.decorate(screen);
49     }
50     Oobe.getInstance().registerScreen(screen);
51   };
53   UserImageScreen.prototype = {
54     __proto__: login.Screen.prototype,
56     /**
57      * Currently selected user image index (take photo button is with zero
58      * index).
59      * @type {number}
60      */
61     selectedUserImage_: -1,
63     /**
64      * URL for profile picture.
65      */
66     profileImageUrl_: null,
68     /** @override */
69     decorate: function(element) {
70       var imageGrid = $('user-image-grid');
71       UserImagesGrid.decorate(imageGrid);
73       // Preview image will track the selected item's URL.
74       var previewElement = $('user-image-preview');
75       previewElement.oncontextmenu = function(e) { e.preventDefault(); };
77       imageGrid.previewElement = previewElement;
78       imageGrid.selectionType = 'default';
79       imageGrid.flipPhotoElement = $('flip-photo');
81       imageGrid.addEventListener('select',
82                                  this.handleSelect_.bind(this));
83       imageGrid.addEventListener('activate',
84                                  this.handleImageActivated_.bind(this));
85       imageGrid.addEventListener('phototaken',
86                                  this.handlePhotoTaken_.bind(this));
87       imageGrid.addEventListener('photoupdated',
88                                  this.handlePhotoUpdated_.bind(this));
90       // Set the title for camera item in the grid.
91       imageGrid.setCameraTitles(
92           loadTimeData.getString('takePhoto'),
93           loadTimeData.getString('photoFromCamera'));
95       this.profileImageLoading = true;
97       // Profile image data (if present).
98       this.profileImage_ = imageGrid.addItem(
99           ButtonImages.PROFILE_PICTURE,           // Image URL.
100           loadTimeData.getString('profilePhoto'), // Title.
101           undefined,                              // Click handler.
102           0,                                      // Position.
103           function(el) {
104             // Custom decorator for Profile image element.
105             var spinner = el.ownerDocument.createElement('div');
106             spinner.className = 'spinner';
107             var spinnerBg = el.ownerDocument.createElement('div');
108             spinnerBg.className = 'spinner-bg';
109             spinnerBg.appendChild(spinner);
110             el.appendChild(spinnerBg);
111             el.id = 'profile-image';
112           });
113       this.profileImage_.type = 'profile';
115       $('take-photo').addEventListener(
116           'click', this.handleTakePhoto_.bind(this));
117       $('discard-photo').addEventListener(
118           'click', this.handleDiscardPhoto_.bind(this));
120       // Toggle 'animation' class for the duration of WebKit transition.
121       $('flip-photo').addEventListener(
122           'click', this.handleFlipPhoto_.bind(this));
123       $('user-image-stream-crop').addEventListener(
124           'webkitTransitionEnd', function(e) {
125             previewElement.classList.remove('animation');
126           });
127       $('user-image-preview-img').addEventListener(
128           'webkitTransitionEnd', function(e) {
129             previewElement.classList.remove('animation');
130           });
132       this.updateLocalizedContent();
134       chrome.send('getImages');
135     },
137     /**
138      * Header text of the screen.
139      * @type {string}
140      */
141     get header() {
142       return loadTimeData.getString('userImageScreenTitle');
143     },
145     /**
146      * Buttons in oobe wizard's button strip.
147      * @type {array} Array of Buttons.
148      */
149     get buttons() {
150       var okButton = this.ownerDocument.createElement('button');
151       okButton.id = 'ok-button';
152       okButton.textContent = loadTimeData.getString('okButtonText');
153       okButton.addEventListener('click', this.acceptImage_.bind(this));
154       return [okButton];
155     },
157     /**
158      * The caption to use for the Profile image preview.
159      * @type {string}
160      */
161     get profileImageCaption() {
162       return this.profileImageCaption_;
163     },
164     set profileImageCaption(value) {
165       this.profileImageCaption_ = value;
166       this.updateCaption_();
167     },
169     /**
170      * True if the Profile image is being loaded.
171      * @type {boolean}
172      */
173     get profileImageLoading() {
174       return this.profileImageLoading_;
175     },
176     set profileImageLoading(value) {
177       this.profileImageLoading_ = value;
178       $('user-image-screen-main').classList.toggle('profile-image-loading',
179                                                    value);
180       if (value)
181         announceAccessibleMessage(loadTimeData.getString('syncingPreferences'));
182       this.updateProfileImageCaption_();
183     },
185     /**
186      * Handles image activation (by pressing Enter).
187      * @private
188      */
189     handleImageActivated_: function() {
190       switch ($('user-image-grid').selectedItemUrl) {
191         case ButtonImages.TAKE_PHOTO:
192           this.handleTakePhoto_();
193           break;
194         default:
195           this.acceptImage_();
196           break;
197       }
198     },
200     /**
201      * Handles selection change.
202      * @param {Event} e Selection change event.
203      * @private
204      */
205     handleSelect_: function(e) {
206       var imageGrid = $('user-image-grid');
207       $('ok-button').disabled = false;
209       // Camera selection
210       if (imageGrid.selectionType == 'camera') {
211         $('flip-photo').tabIndex = 1;
212         // No current image selected.
213         if (imageGrid.cameraLive) {
214           imageGrid.previewElement.classList.remove('phototaken');
215           $('ok-button').disabled = true;
216         } else {
217           imageGrid.previewElement.classList.add('phototaken');
218           this.notifyImageSelected_();
219         }
220       } else {
221         imageGrid.previewElement.classList.remove('phototaken');
222         $('flip-photo').tabIndex = -1;
223         this.notifyImageSelected_();
224       }
225       // Start/stop camera on (de)selection.
226       if (!imageGrid.inProgramSelection &&
227           imageGrid.selectionType != e.oldSelectionType) {
228         if (imageGrid.selectionType == 'camera') {
229           // Programmatic selection of camera item is done in
230           // startCamera callback where streaming is started by itself.
231           imageGrid.startCamera(
232               function() {
233                 // Start capture if camera is still the selected item.
234                 $('user-image-preview-img').classList.toggle(
235                     'animated-transform', true);
236                 return imageGrid.selectedItem == imageGrid.cameraImage;
237               });
238         } else {
239           $('user-image-preview-img').classList.toggle('animated-transform',
240                                                        false);
241           imageGrid.stopCamera();
242         }
243       }
244       this.updateCaption_();
245       // Update image attribution text.
246       var image = imageGrid.selectedItem;
247       $('user-image-author-name').textContent = image.author;
248       $('user-image-author-website').textContent = image.website;
249       $('user-image-author-website').href = image.website;
250       $('user-image-attribution').style.visibility =
251           (image.author || image.website) ? 'visible' : 'hidden';
252     },
254     /**
255      * Handle camera-photo flip.
256      */
257     handleFlipPhoto_: function() {
258       var imageGrid = $('user-image-grid');
259       imageGrid.previewElement.classList.add('animation');
260       imageGrid.flipPhoto = !imageGrid.flipPhoto;
261       var flipMessageId = imageGrid.flipPhoto ?
262          'photoFlippedAccessibleText' : 'photoFlippedBackAccessibleText';
263       announceAccessibleMessage(loadTimeData.getString(flipMessageId));
264     },
266     /**
267      * Handle photo capture from the live camera stream.
268      */
269     handleTakePhoto_: function(e) {
270       $('user-image-grid').takePhoto();
271       chrome.send('takePhoto');
272     },
274     /**
275      * Handle photo captured event.
276      * @param {Event} e Event with 'dataURL' property containing a data URL.
277      */
278     handlePhotoTaken_: function(e) {
279       chrome.send('photoTaken', [e.dataURL]);
280       announceAccessibleMessage(
281           loadTimeData.getString('photoCaptureAccessibleText'));
282     },
284     /**
285      * Handle photo updated event.
286      * @param {Event} e Event with 'dataURL' property containing a data URL.
287      */
288     handlePhotoUpdated_: function(e) {
289       chrome.send('photoTaken', [e.dataURL]);
290     },
292     /**
293      * Handle discarding the captured photo.
294      */
295     handleDiscardPhoto_: function(e) {
296       var imageGrid = $('user-image-grid');
297       imageGrid.discardPhoto();
298       chrome.send('discardPhoto');
299       announceAccessibleMessage(
300           loadTimeData.getString('photoDiscardAccessibleText'));
301     },
303     /**
304      * Event handler that is invoked just before the screen is shown.
305      * @param {object} data Screen init payload.
306      */
307     onBeforeShow: function(data) {
308       Oobe.getInstance().headerHidden = true;
309       var imageGrid = $('user-image-grid');
310       imageGrid.updateAndFocus();
311       chrome.send('onUserImageScreenShown');
312     },
314     /**
315      * Event handler that is invoked just before the screen is hidden.
316      */
317     onBeforeHide: function() {
318       $('user-image-grid').stopCamera();
319     },
321     /**
322      * Accepts currently selected image, if possible.
323      * @private
324      */
325     acceptImage_: function() {
326       var okButton = $('ok-button');
327       if (!okButton.disabled) {
328         // This ensures that #ok-button won't be re-enabled again.
329         $('user-image-grid').disabled = true;
330         okButton.disabled = true;
331         chrome.send('onUserImageAccepted');
332       }
333     },
335     /**
336      * Updates user profile image.
337      * @param {?string} imageUrl Image encoded as data URL. If null, user has
338      *     the default profile image, which we don't want to show.
339      * @private
340      */
341     setProfileImage_: function(imageUrl) {
342       this.profileImageLoading = false;
343       this.profileImageUrl_ = imageUrl;
344       if (imageUrl !== null) {
345         this.profileImage_ =
346             $('user-image-grid').updateItem(this.profileImage_, imageUrl);
347       }
348     },
350     /**
351      * @param {boolean} present Whether camera is detected.
352      */
353     setCameraPresent_: function(present) {
354       $('user-image-grid').cameraPresent = present;
355     },
357     /**
358      * Controls the profile image as one of image options.
359      * @param {enabled} Whether profile image option should be displayed.
360      * @private
361      */
362     setProfilePictureEnabled_: function(enabled) {
363       var imageGrid = $('user-image-grid');
364       if (enabled) {
365       } else {
366         imageGrid.removeItem(this.profileImage_);
367       }
368     },
370     /**
371      * Appends default images to the image grid. Should only be called once.
372      * @param {Array.<{url: string, author: string, website: string}>} images
373      *   An array of default images data, including URL, author and website.
374      * @private
375      */
376     setDefaultImages_: function(imagesData) {
377       var imageGrid = $('user-image-grid');
378       for (var i = 0, data; data = imagesData[i]; i++) {
379         var item = imageGrid.addItem(data.url, data.title);
380         item.type = 'default';
381         item.author = data.author || '';
382         item.website = data.website || '';
383       }
384       chrome.send('screenReady');
385     },
387     /**
388      * Selects user image with the given URL.
389      * @param {string} url URL of the image to select.
390      * @private
391      */
392     setSelectedImage_: function(url) {
393       var imageGrid = $('user-image-grid');
394       imageGrid.selectedItemUrl = url;
395       imageGrid.focus();
396     },
398     /**
399      * Hides curtain with spinner.
400      * @private
401      */
402     hideCurtain_: function() {
403       this.classList.remove('loading');
404       Oobe.getInstance().updateScreenSize(this);
405     },
407     /**
408      * Updates the image preview caption.
409      * @private
410      */
411     updateCaption_: function() {
412       $('user-image-preview-caption').textContent =
413           $('user-image-grid').selectionType == 'profile' ?
414           this.profileImageCaption : '';
415     },
417     /**
418      * Updates localized content of the screen that is not updated via template.
419      */
420     updateLocalizedContent: function() {
421       this.updateProfileImageCaption_();
422     },
424     /**
425      * Updates profile image caption.
426      * @private
427      */
428     updateProfileImageCaption_: function() {
429       this.profileImageCaption = loadTimeData.getString(
430         this.profileImageLoading_ ? 'profilePhotoLoading' : 'profilePhoto');
431     },
433     /**
434      * Notifies chrome about image selection.
435      * @private
436      */
437     notifyImageSelected_: function() {
438       var imageGrid = $('user-image-grid');
439       chrome.send('selectImage',
440                   [imageGrid.selectedItemUrl,
441                    imageGrid.selectionType,
442                    !imageGrid.inProgramSelection]);
443     },
444   };
446   // Forward public APIs to private implementations.
447   cr.makePublic(UserImageScreen, [
448     'setDefaultImages',
449     'setCameraPresent',
450     'setProfilePictureEnabled',
451     'setProfileImage',
452     'setSelectedImage',
453     'hideCurtain'
454   ], $('user-image'));
456   return {
457     UserImageScreen: UserImageScreen
458   };