• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4
5/**
6 * @fileoverview Oobe user image screen implementation.
7 */
8
9cr.define('login', function() {
10  var UserImagesGrid = options.UserImagesGrid;
11  var ButtonImages = UserImagesGrid.ButtonImages;
12
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  ];
21
22  /**
23   * Whether the web camera item should be preselected, if available.
24   * @type {boolean}
25   * @const
26   */
27  var PRESELECT_CAMERA = false;
28
29  /**
30   * Creates a new OOBE screen div.
31   * @constructor
32   * @extends {HTMLDivElement}
33   */
34  var UserImageScreen = cr.ui.define(login.Screen);
35
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  };
52
53  UserImageScreen.prototype = {
54    __proto__: login.Screen.prototype,
55
56    /**
57     * Currently selected user image index (take photo button is with zero
58     * index).
59     * @type {number}
60     */
61    selectedUserImage_: -1,
62
63    /**
64     * URL for profile picture.
65     */
66    profileImageUrl_: null,
67
68    /** @override */
69    decorate: function(element) {
70      var imageGrid = $('user-image-grid');
71      UserImagesGrid.decorate(imageGrid);
72
73      // Preview image will track the selected item's URL.
74      var previewElement = $('user-image-preview');
75      previewElement.oncontextmenu = function(e) { e.preventDefault(); };
76
77      imageGrid.previewElement = previewElement;
78      imageGrid.selectionType = 'default';
79      imageGrid.flipPhotoElement = $('flip-photo');
80
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));
89
90      // Set the title for camera item in the grid.
91      imageGrid.setCameraTitles(
92          loadTimeData.getString('takePhoto'),
93          loadTimeData.getString('photoFromCamera'));
94
95      this.profileImageLoading = true;
96
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';
114
115      $('take-photo').addEventListener(
116          'click', this.handleTakePhoto_.bind(this));
117      $('discard-photo').addEventListener(
118          'click', this.handleDiscardPhoto_.bind(this));
119
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          });
131
132      this.updateLocalizedContent();
133
134      chrome.send('getImages');
135    },
136
137    /**
138     * Header text of the screen.
139     * @type {string}
140     */
141    get header() {
142      return loadTimeData.getString('userImageScreenTitle');
143    },
144
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    },
156
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    },
168
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    },
184
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    },
199
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;
208
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    },
253
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    },
265
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    },
273
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    },
283
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    },
291
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    },
302
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    },
313
314    /**
315     * Event handler that is invoked just before the screen is hidden.
316     */
317    onBeforeHide: function() {
318      $('user-image-grid').stopCamera();
319    },
320
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    },
334
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    },
349
350    /**
351     * @param {boolean} present Whether camera is detected.
352     */
353    setCameraPresent_: function(present) {
354      $('user-image-grid').cameraPresent = present;
355    },
356
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    },
369
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    },
386
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    },
397
398    /**
399     * Hides curtain with spinner.
400     * @private
401     */
402    hideCurtain_: function() {
403      this.classList.remove('loading');
404      Oobe.getInstance().updateScreenSize(this);
405    },
406
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    },
416
417    /**
418     * Updates localized content of the screen that is not updated via template.
419     */
420    updateLocalizedContent: function() {
421      this.updateProfileImageCaption_();
422    },
423
424    /**
425     * Updates profile image caption.
426     * @private
427     */
428    updateProfileImageCaption_: function() {
429      this.profileImageCaption = loadTimeData.getString(
430        this.profileImageLoading_ ? 'profilePhotoLoading' : 'profilePhoto');
431    },
432
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  };
445
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');
455
456  return {
457    UserImageScreen: UserImageScreen
458  };
459});
460
461