OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Oobe user image screen implementation. | 6 * @fileoverview Oobe user image screen implementation. |
7 */ | 7 */ |
8 | 8 |
9 cr.define('oobe', function() { | 9 cr.define('oobe', function() { |
10 | |
11 var UserImagesGrid = options.UserImagesGrid; | 10 var UserImagesGrid = options.UserImagesGrid; |
12 var ButtonImages = UserImagesGrid.ButtonImages; | 11 var ButtonImages = UserImagesGrid.ButtonImages; |
13 | 12 |
14 /** | 13 /** |
15 * Array of button URLs used on this page. | 14 * Array of button URLs used on this page. |
16 * @type {Array.<string>} | 15 * @type {Array.<string>} |
| 16 * @const |
17 */ | 17 */ |
18 const ButtonImageUrls = [ | 18 var ButtonImageUrls = [ |
19 ButtonImages.TAKE_PHOTO | 19 ButtonImages.TAKE_PHOTO |
20 ]; | 20 ]; |
21 | 21 |
22 /** | 22 /** |
23 * Creates a new oobe screen div. | 23 * Creates a new OOBE screen div. |
24 * @constructor | 24 * @constructor |
25 * @extends {HTMLDivElement} | 25 * @extends {HTMLDivElement} |
26 */ | 26 */ |
27 var UserImageScreen = cr.ui.define('div'); | 27 var UserImageScreen = cr.ui.define('div'); |
28 | 28 |
29 /** | 29 /** |
| 30 * Dimensions for camera capture. |
| 31 * @const |
| 32 */ |
| 33 var CAPTURE_SIZE = { |
| 34 height: 480, |
| 35 width: 480 |
| 36 }; |
| 37 |
| 38 /** |
| 39 * Interval between consecutive camera presence checks in msec while camera is |
| 40 * not present. |
| 41 * @const |
| 42 */ |
| 43 var CAMERA_CHECK_INTERVAL_MS = 3000; |
| 44 |
| 45 /** |
| 46 * Interval between consecutive camera liveness checks in msec. |
| 47 * @const |
| 48 */ |
| 49 var CAMERA_LIVENESS_CHECK_MS = 1000; |
| 50 |
| 51 /** |
30 * Registers with Oobe. | 52 * Registers with Oobe. |
31 */ | 53 */ |
32 UserImageScreen.register = function() { | 54 UserImageScreen.register = function() { |
33 var screen = $('user-image'); | 55 var screen = $('user-image'); |
| 56 var isWebRTC = document.documentElement.getAttribute('camera') == 'webrtc'; |
| 57 UserImageScreen.prototype = isWebRTC ? UserImageScreenWebRTCProto : |
| 58 UserImageScreenOldProto; |
34 UserImageScreen.decorate(screen); | 59 UserImageScreen.decorate(screen); |
35 Oobe.getInstance().registerScreen(screen); | 60 Oobe.getInstance().registerScreen(screen); |
36 }; | 61 }; |
37 | 62 |
38 UserImageScreen.prototype = { | 63 var UserImageScreenOldProto = { |
39 __proto__: HTMLDivElement.prototype, | 64 __proto__: HTMLDivElement.prototype, |
40 | 65 |
41 /** | 66 /** |
42 * Currently selected user image index (take photo button is with zero | 67 * Currently selected user image index (take photo button is with zero |
43 * index). | 68 * index). |
44 * @type {number} | 69 * @type {number} |
45 */ | 70 */ |
46 selectedUserImage_: -1, | 71 selectedUserImage_: -1, |
47 | 72 |
48 /** @inheritDoc */ | 73 /** @inheritDoc */ |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 | 120 |
96 /** | 121 /** |
97 * Buttons in oobe wizard's button strip. | 122 * Buttons in oobe wizard's button strip. |
98 * @type {array} Array of Buttons. | 123 * @type {array} Array of Buttons. |
99 */ | 124 */ |
100 get buttons() { | 125 get buttons() { |
101 var okButton = this.ownerDocument.createElement('button'); | 126 var okButton = this.ownerDocument.createElement('button'); |
102 okButton.id = 'ok-button'; | 127 okButton.id = 'ok-button'; |
103 okButton.textContent = localStrings.getString('okButtonText'); | 128 okButton.textContent = localStrings.getString('okButtonText'); |
104 okButton.addEventListener('click', this.acceptImage_.bind(this)); | 129 okButton.addEventListener('click', this.acceptImage_.bind(this)); |
105 return [ okButton ]; | 130 return [okButton]; |
106 }, | 131 }, |
107 | 132 |
108 /** | 133 /** |
109 * The caption to use for the Profile image preview. | 134 * The caption to use for the Profile image preview. |
110 * @type {string} | 135 * @type {string} |
111 */ | 136 */ |
112 get profileImageCaption() { | 137 get profileImageCaption() { |
113 return this.profileImageCaption_; | 138 return this.profileImageCaption_; |
114 }, | 139 }, |
115 set profileImageCaption(value) { | 140 set profileImageCaption(value) { |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 * Updates the image preview caption. | 340 * Updates the image preview caption. |
316 * @private | 341 * @private |
317 */ | 342 */ |
318 updateCaption_: function() { | 343 updateCaption_: function() { |
319 $('user-image-preview-caption').textContent = | 344 $('user-image-preview-caption').textContent = |
320 this.profileImageSelected ? this.profileImageCaption : ''; | 345 this.profileImageSelected ? this.profileImageCaption : ''; |
321 }, | 346 }, |
322 | 347 |
323 /** | 348 /** |
324 * Updates localized content of the screen that is not updated via template. | 349 * Updates localized content of the screen that is not updated via template. |
325 * @public | |
326 */ | 350 */ |
327 updateLocalizedContent: function() { | 351 updateLocalizedContent: function() { |
328 this.updateProfileImageCaption_(); | 352 this.updateProfileImageCaption_(); |
| 353 }, |
| 354 |
| 355 /** |
| 356 * Updates profile image caption. |
| 357 * @private |
| 358 */ |
| 359 updateProfileImageCaption_: function() { |
| 360 this.profileImageCaption = localStrings.getString( |
| 361 this.profileImageLoading_ ? 'profilePhotoLoading' : 'profilePhoto'); |
| 362 } |
| 363 }; |
| 364 |
| 365 var UserImageScreenWebRTCProto = { |
| 366 __proto__: HTMLDivElement.prototype, |
| 367 |
| 368 /** |
| 369 * Currently selected user image index (take photo button is with zero |
| 370 * index). |
| 371 * @type {number} |
| 372 */ |
| 373 selectedUserImage_: -1, |
| 374 |
| 375 /** @inheritDoc */ |
| 376 decorate: function(element) { |
| 377 var imageGrid = $('user-image-grid'); |
| 378 UserImagesGrid.decorate(imageGrid); |
| 379 |
| 380 imageGrid.addEventListener('change', |
| 381 this.handleSelection_.bind(this)); |
| 382 imageGrid.addEventListener('activate', |
| 383 this.handleImageActivated_.bind(this)); |
| 384 imageGrid.addEventListener('dblclick', |
| 385 this.handleImageDblClick_.bind(this)); |
| 386 |
| 387 // Profile image data (if present). |
| 388 this.profileImage_ = imageGrid.addItem( |
| 389 ButtonImages.PROFILE_PICTURE, |
| 390 undefined, undefined, undefined, |
| 391 function(el) { // Custom decorator for Profile image element. |
| 392 var spinner = el.ownerDocument.createElement('div'); |
| 393 spinner.className = 'spinner'; |
| 394 var spinnerBg = el.ownerDocument.createElement('div'); |
| 395 spinnerBg.className = 'spinner-bg'; |
| 396 spinnerBg.appendChild(spinner); |
| 397 el.appendChild(spinnerBg); |
| 398 el.id = 'profile-image'; |
| 399 }); |
| 400 this.profileImage_.type = 'profile'; |
| 401 this.selectionType = 'default'; |
| 402 |
| 403 var video = $('user-image-stream'); |
| 404 video.addEventListener('canplay', this.handleVideoStarted_.bind(this)); |
| 405 video.addEventListener('timeupdate', this.handleVideoUpdate_.bind(this)); |
| 406 $('take-photo').addEventListener('click', |
| 407 this.handleTakePhoto_.bind(this)); |
| 408 $('discard-photo').addEventListener('click', |
| 409 this.handleDiscardPhoto_.bind(this)); |
| 410 this.cameraImage = null; |
| 411 // Perform an early check if camera is present, without starting capture. |
| 412 this.checkCameraPresence_(false, false); |
| 413 }, |
| 414 |
| 415 /** |
| 416 * Header text of the screen. |
| 417 * @type {string} |
| 418 */ |
| 419 get header() { |
| 420 return localStrings.getString('userImageScreenTitle'); |
| 421 }, |
| 422 |
| 423 /** |
| 424 * Buttons in oobe wizard's button strip. |
| 425 * @type {array} Array of Buttons. |
| 426 */ |
| 427 get buttons() { |
| 428 var okButton = this.ownerDocument.createElement('button'); |
| 429 okButton.id = 'ok-button'; |
| 430 okButton.textContent = localStrings.getString('okButtonText'); |
| 431 okButton.addEventListener('click', this.acceptImage_.bind(this)); |
| 432 return [okButton]; |
| 433 }, |
| 434 |
| 435 /** |
| 436 * The caption to use for the Profile image preview. |
| 437 * @type {string} |
| 438 */ |
| 439 get profileImageCaption() { |
| 440 return this.profileImageCaption_; |
| 441 }, |
| 442 set profileImageCaption(value) { |
| 443 this.profileImageCaption_ = value; |
| 444 this.updateCaption_(); |
| 445 }, |
| 446 |
| 447 /** |
| 448 * True if the Profile image is being loaded. |
| 449 * @type {boolean} |
| 450 */ |
| 451 get profileImageLoading() { |
| 452 return this.profileImageLoading_; |
| 453 }, |
| 454 set profileImageLoading(value) { |
| 455 this.profileImageLoading_ = value; |
| 456 $('user-image-screen-main').classList[ |
| 457 value ? 'add' : 'remove']('profile-image-loading'); |
| 458 this.updateProfileImageCaption_(); |
| 459 }, |
| 460 |
| 461 /** |
| 462 * True when camera is in live mode (i.e. no still photo selected). |
| 463 * @type {boolean} |
| 464 */ |
| 465 get cameraLive() { |
| 466 return this.cameraLive_; |
| 467 }, |
| 468 set cameraLive(value) { |
| 469 this.cameraLive_ = value; |
| 470 $('user-image-preview').classList[value ? 'add' : 'remove']('live'); |
| 471 }, |
| 472 |
| 473 /** |
| 474 * Type of the selected image (one of 'default', 'profile', 'camera'). |
| 475 * @type {string} |
| 476 */ |
| 477 get selectionType() { |
| 478 return this.selectionType_; |
| 479 }, |
| 480 set selectionType(value) { |
| 481 this.selectionType_ = value; |
| 482 var previewClassList = $('user-image-preview').classList; |
| 483 previewClassList[value == 'default' ? 'add' : 'remove']('default-image'); |
| 484 previewClassList[value == 'profile' ? 'add' : 'remove']('profile-image'); |
| 485 previewClassList[value == 'camera' ? 'add' : 'remove']('camera'); |
| 486 this.updateCaption_(); |
| 487 }, |
| 488 |
| 489 /** |
| 490 * Handles image activation (by pressing Enter). |
| 491 * @private |
| 492 */ |
| 493 handleImageActivated_: function() { |
| 494 switch ($('user-image-grid').selectedItemUrl) { |
| 495 case ButtonImages.TAKE_PHOTO: |
| 496 this.handleTakePhoto_(); |
| 497 break; |
| 498 default: |
| 499 this.acceptImage_(); |
| 500 break; |
| 501 } |
| 502 }, |
| 503 |
| 504 /** |
| 505 * Handles photo capture from the live camera stream. |
| 506 * @private |
| 507 */ |
| 508 handleTakePhoto_: function() { |
| 509 var self = this; |
| 510 var photoURL = this.captureFrame_($('user-image-stream'), CAPTURE_SIZE); |
| 511 chrome.send('photoTaken', [photoURL]); |
| 512 // Wait until image is loaded before displaying it. |
| 513 var previewImg = new Image(); |
| 514 previewImg.addEventListener('load', function(e) { |
| 515 self.cameraImage = this.src; |
| 516 }); |
| 517 previewImg.src = photoURL; |
| 518 }, |
| 519 |
| 520 /** |
| 521 * Discard current photo and return to the live camera stream. |
| 522 * @private |
| 523 */ |
| 524 handleDiscardPhoto_: function() { |
| 525 this.cameraImage = null; |
| 526 }, |
| 527 |
| 528 /** |
| 529 * Capture a single still frame from a <video> element. |
| 530 * @param {HTMLVideoElement} video Video element to capture from. |
| 531 * @param {{width: number, height: number}} destSize Capture size. |
| 532 * @return {string} Captured frame as a data URL. |
| 533 * @private |
| 534 */ |
| 535 captureFrame_: function(video, destSize) { |
| 536 var canvas = document.createElement('canvas'); |
| 537 canvas.width = destSize.width; |
| 538 canvas.height = destSize.height; |
| 539 var ctx = canvas.getContext('2d'); |
| 540 var width = video.videoWidth; |
| 541 var height = video.videoHeight; |
| 542 if (width < destSize.width || height < destSize.height) { |
| 543 console.error('Video capture size too small: ' + |
| 544 width + 'x' + height + '!'); |
| 545 } |
| 546 var src = {}; |
| 547 if (width / destSize.width > height / destSize.height) { |
| 548 // Full height, crop left/right. |
| 549 src.height = height; |
| 550 src.width = height * destSize.width / destSize.height; |
| 551 } else { |
| 552 // Full width, crop top/bottom. |
| 553 src.width = width; |
| 554 src.height = width * destSize.height / destSize.width; |
| 555 } |
| 556 src.x = (width - src.width) / 2; |
| 557 src.y = (height - src.height) / 2; |
| 558 ctx.drawImage(video, src.x, src.y, src.width, src.height, |
| 559 0, 0, destSize.width, destSize.height); |
| 560 return canvas.toDataURL('image/png'); |
| 561 }, |
| 562 |
| 563 /** |
| 564 * Handles selection change. |
| 565 * @private |
| 566 */ |
| 567 handleSelection_: function() { |
| 568 var selectedItem = $('user-image-grid').selectedItem; |
| 569 if (selectedItem === null) |
| 570 return; |
| 571 |
| 572 // Update preview image URL. |
| 573 var url = selectedItem.url; |
| 574 $('user-image-preview-img').src = url; |
| 575 |
| 576 // Update current selection type. |
| 577 this.selectionType = selectedItem.type; |
| 578 |
| 579 // Show grey silhouette with the same border as stock images. |
| 580 if (/^chrome:\/\/theme\//.test(url)) |
| 581 $('user-image-preview').classList.add('default-image'); |
| 582 |
| 583 if (ButtonImageUrls.indexOf(url) == -1) { |
| 584 // Non-button image is selected. |
| 585 $('ok-button').disabled = false; |
| 586 chrome.send('selectImage', [url]); |
| 587 } else { |
| 588 $('ok-button').disabled = true; |
| 589 } |
| 590 }, |
| 591 |
| 592 /** |
| 593 * Handles double click on the image grid. |
| 594 * @param {Event} e Double click Event. |
| 595 */ |
| 596 handleImageDblClick_: function(e) { |
| 597 // If an image is double-clicked and not the grid itself, handle this |
| 598 // as 'OK' button button press. |
| 599 if (e.target.id != 'user-image-grid') |
| 600 this.acceptImage_(); |
| 601 }, |
| 602 |
| 603 /** |
| 604 * Event handler that is invoked just before the screen is shown. |
| 605 * @param {object} data Screen init payload. |
| 606 */ |
| 607 onBeforeShow: function(data) { |
| 608 Oobe.getInstance().headerHidden = true; |
| 609 $('user-image-grid').updateAndFocus(); |
| 610 chrome.send('onUserImageScreenShown'); |
| 611 // Now check again for camera presence and start capture. |
| 612 this.checkCameraPresence_(true, true); |
| 613 }, |
| 614 |
| 615 /** |
| 616 * Event handler that is invoked just before the screen is hidden. |
| 617 */ |
| 618 onBeforeHide: function() { |
| 619 $('user-image-stream').src = ''; |
| 620 }, |
| 621 |
| 622 /** |
| 623 * Accepts currently selected image, if possible. |
| 624 * @private |
| 625 */ |
| 626 acceptImage_: function() { |
| 627 var okButton = $('ok-button'); |
| 628 if (!okButton.disabled) { |
| 629 // This ensures that #ok-button won't be re-enabled again. |
| 630 $('user-image-grid').disabled = true; |
| 631 okButton.disabled = true; |
| 632 chrome.send('onUserImageAccepted'); |
| 633 } |
| 634 }, |
| 635 |
| 636 /** |
| 637 * @param {boolean} present Whether a camera is present or not. |
| 638 */ |
| 639 get cameraPresent() { |
| 640 return this.cameraPresent_; |
| 641 }, |
| 642 set cameraPresent(value) { |
| 643 this.cameraPresent_ = value; |
| 644 if (this.cameraLive) |
| 645 this.cameraImage = null; |
| 646 }, |
| 647 |
| 648 /** |
| 649 * Start camera presence check. |
| 650 * @param {boolean} autoplay Whether to start capture immediately. |
| 651 * @param {boolean} preselect Whether to select camera automatically. |
| 652 * @private |
| 653 */ |
| 654 checkCameraPresence_: function(autoplay, preselect) { |
| 655 $('user-image-preview').classList.remove('online'); |
| 656 navigator.webkitGetUserMedia( |
| 657 {video: true}, |
| 658 this.handleCameraAvailable_.bind(this, autoplay, preselect), |
| 659 // When ready to capture camera, poll regularly for camera presence. |
| 660 this.handleCameraAbsent_.bind(this, /* recheck= */ autoplay)); |
| 661 }, |
| 662 |
| 663 /** |
| 664 * Handles successful camera check. |
| 665 * @param {boolean} autoplay Whether to start capture immediately. |
| 666 * @param {boolean} preselect Whether to select camera automatically. |
| 667 * @param {MediaStream} stream Stream object as returned by getUserMedia. |
| 668 * @private |
| 669 */ |
| 670 handleCameraAvailable_: function(autoplay, preselect, stream) { |
| 671 if (autoplay) |
| 672 $('user-image-stream').src = window.webkitURL.createObjectURL(stream); |
| 673 this.cameraPresent = true; |
| 674 if (preselect) |
| 675 $('user-image-grid').selectedItem = this.cameraImage; |
| 676 }, |
| 677 |
| 678 /** |
| 679 * Handles camera check failure. |
| 680 * @param {boolean} recheck Whether to check for camera again. |
| 681 * @param {NavigatorUserMediaError=} err Error object. |
| 682 * @private |
| 683 */ |
| 684 handleCameraAbsent_: function(recheck, err) { |
| 685 this.cameraPresent = false; |
| 686 $('user-image-preview').classList.remove('online'); |
| 687 // |preselect| is |false| in this case to not override user's selection. |
| 688 if (recheck) { |
| 689 setTimeout(this.checkCameraPresence_.bind(this, true, false), |
| 690 CAMERA_CHECK_INTERVAL_MS); |
| 691 } |
| 692 if (this.cameraLiveCheckTimer_) { |
| 693 clearInterval(this.cameraLiveCheckTimer_); |
| 694 this.cameraLiveCheckTimer_ = null; |
| 695 } |
| 696 }, |
| 697 |
| 698 /** |
| 699 * Handles successful camera capture start. |
| 700 * @private |
| 701 */ |
| 702 handleVideoStarted_: function() { |
| 703 $('user-image-preview').classList.add('online'); |
| 704 this.cameraLiveCheckTimer_ = setInterval(this.checkCameraLive_.bind(this), |
| 705 CAMERA_LIVENESS_CHECK_MS); |
| 706 }, |
| 707 |
| 708 /** |
| 709 * Handles camera stream update. Called regularly (at rate no greater then |
| 710 * 4/sec) while camera stream is live. |
| 711 * @private |
| 712 */ |
| 713 handleVideoUpdate_: function() { |
| 714 this.lastFrameTime_ = new Date().getTime(); |
| 715 }, |
| 716 |
| 717 /** |
| 718 * Checks if camera is still live by comparing the timestamp of the last |
| 719 * 'timeupdate' event with the current time. |
| 720 * @private |
| 721 */ |
| 722 checkCameraLive_: function() { |
| 723 if (new Date().getTime() - this.lastFrameTime_ > CAMERA_LIVENESS_CHECK_MS) |
| 724 this.handleCameraAbsent_(true, null); |
| 725 }, |
| 726 |
| 727 /** |
| 728 * Current image captured from camera as data URL. Setting to null will |
| 729 * return to the live camera stream. |
| 730 * @type {string=} |
| 731 */ |
| 732 get cameraImage() { |
| 733 return this.cameraImage_; |
| 734 }, |
| 735 set cameraImage(imageUrl) { |
| 736 this.cameraLive = !imageUrl; |
| 737 var imageGrid = $('user-image-grid'); |
| 738 if (this.cameraPresent && !imageUrl) { |
| 739 imageUrl = ButtonImages.TAKE_PHOTO; |
| 740 } |
| 741 if (imageUrl) { |
| 742 this.cameraImage_ = this.cameraImage_ ? |
| 743 imageGrid.updateItem(this.cameraImage_, imageUrl) : |
| 744 imageGrid.addItem(imageUrl, undefined, undefined, 0); |
| 745 this.cameraImage_.type = 'camera'; |
| 746 } else { |
| 747 imageGrid.removeItem(this.cameraImage_); |
| 748 this.cameraImage_ = null; |
| 749 } |
| 750 imageGrid.focus(); |
| 751 }, |
| 752 |
| 753 /** |
| 754 * Updates user profile image. |
| 755 * @param {?string} imageUrl Image encoded as data URL. If null, user has |
| 756 * the default profile image, which we don't want to show. |
| 757 * @private |
| 758 */ |
| 759 setProfileImage_: function(imageUrl) { |
| 760 this.profileImageLoading = false; |
| 761 if (imageUrl !== null) { |
| 762 this.profileImage_ = |
| 763 $('user-image-grid').updateItem(this.profileImage_, imageUrl); |
| 764 } |
| 765 }, |
| 766 |
| 767 /** |
| 768 * Appends received images to the list. |
| 769 * @param {Array.<string>} images An array of URLs to user images. |
| 770 * @private |
| 771 */ |
| 772 setUserImages_: function(images) { |
| 773 var imageGrid = $('user-image-grid'); |
| 774 for (var i = 0, url; url = images[i]; i++) |
| 775 imageGrid.addItem(url).type = 'default'; |
| 776 }, |
| 777 |
| 778 /** |
| 779 * Selects user image with the given URL. |
| 780 * @param {string} url URL of the image to select. |
| 781 * @private |
| 782 */ |
| 783 setSelectedImage_: function(url) { |
| 784 var imageGrid = $('user-image-grid'); |
| 785 imageGrid.selectedItemUrl = url; |
| 786 imageGrid.focus(); |
| 787 }, |
| 788 |
| 789 /** |
| 790 * Updates the image preview caption. |
| 791 * @private |
| 792 */ |
| 793 updateCaption_: function() { |
| 794 $('user-image-preview-caption').textContent = |
| 795 (this.selectionType == 'profile') ? this.profileImageCaption : ''; |
| 796 }, |
| 797 |
| 798 /** |
| 799 * Updates localized content of the screen that is not updated via template. |
| 800 */ |
| 801 updateLocalizedContent: function() { |
| 802 this.updateProfileImageCaption_(); |
329 }, | 803 }, |
330 | 804 |
331 /** | 805 /** |
332 * Updates profile image caption. | 806 * Updates profile image caption. |
333 * @private | 807 * @private |
334 */ | 808 */ |
335 updateProfileImageCaption_: function() { | 809 updateProfileImageCaption_: function() { |
336 this.profileImageCaption = localStrings.getString( | 810 this.profileImageCaption = localStrings.getString( |
337 this.profileImageLoading_ ? 'profilePhotoLoading' : 'profilePhoto'); | 811 this.profileImageLoading_ ? 'profilePhotoLoading' : 'profilePhoto'); |
338 } | 812 } |
339 }; | 813 }; |
340 | 814 |
341 // Forward public APIs to private implementations. | 815 // Forward public APIs to private implementations. |
342 [ | 816 [ |
343 'setCameraPresent', | 817 'setCameraPresent', |
344 'setProfileImage', | 818 'setProfileImage', |
345 'setSelectedImage', | 819 'setSelectedImage', |
346 'setUserImages', | 820 'setUserImages', |
347 'setUserPhoto', | 821 'setUserPhoto', |
348 ].forEach(function(name) { | 822 ].forEach(function(name) { |
349 UserImageScreen[name] = function(value) { | 823 UserImageScreen[name] = function(value) { |
350 $('user-image')[name + '_'](value); | 824 $('user-image')[name + '_'](value); |
351 }; | 825 }; |
352 }); | 826 }); |
353 | 827 |
354 return { | 828 return { |
355 UserImageScreen: UserImageScreen | 829 UserImageScreen: UserImageScreen |
356 }; | 830 }; |
357 }); | 831 }); |
OLD | NEW |