|  | // Copyright 2015 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. | 
|  |  | 
|  | /** | 
|  | * Dimmable UI Controller. | 
|  | * @param {!HTMLElement} container | 
|  | * @constructor | 
|  | * @struct | 
|  | */ | 
|  | function DimmableUIController(container) { | 
|  | /** | 
|  | * @private {!HTMLElement} | 
|  | * @const | 
|  | */ | 
|  | this.container_ = container; | 
|  |  | 
|  | /** | 
|  | * @private {NodeList} | 
|  | */ | 
|  | this.tools_ = null; | 
|  |  | 
|  | /** | 
|  | * @private {number} | 
|  | */ | 
|  | this.timeoutId_ = 0; | 
|  |  | 
|  | /** | 
|  | * @private {boolean} | 
|  | */ | 
|  | this.isCursorInTools_ = false; | 
|  |  | 
|  | /** | 
|  | * @private {Gallery.Mode|undefined} | 
|  | */ | 
|  | this.mode_ = undefined; | 
|  |  | 
|  | /** | 
|  | * @private {Gallery.SubMode|undefined} | 
|  | */ | 
|  | this.subMode_ = undefined; | 
|  |  | 
|  | /** | 
|  | * @private {boolean} | 
|  | */ | 
|  | this.spokenFeedbackEnabled_ = false; | 
|  |  | 
|  | /** | 
|  | * @private {boolean} | 
|  | */ | 
|  | this.loading_ = false; | 
|  |  | 
|  | /** | 
|  | * @private {boolean} | 
|  | */ | 
|  | this.renaming_ = false; | 
|  |  | 
|  | /** | 
|  | * @private {boolean} | 
|  | */ | 
|  | this.disabled_ = false; | 
|  |  | 
|  | /** | 
|  | * @private {number} | 
|  | */ | 
|  | this.madeVisibleAt_ = 0; | 
|  |  | 
|  | this.container_.addEventListener('click', this.onClick_.bind(this)); | 
|  | this.container_.addEventListener('mousemove', this.onMousemove_.bind(this)); | 
|  | this.container_.addEventListener( | 
|  | 'touchstart', this.onTouchOperation_.bind(this)); | 
|  | this.container_.addEventListener( | 
|  | 'touchmove', this.onTouchOperation_.bind(this)); | 
|  | this.container_.addEventListener( | 
|  | 'touchend', this.onTouchOperation_.bind(this)); | 
|  | this.container_.addEventListener( | 
|  | 'touchcancel', this.onTouchOperation_.bind(this)); | 
|  |  | 
|  | chrome.accessibilityFeatures.spokenFeedback.onChange.addListener( | 
|  | this.onGetOrChangedSpokenFeedbackConfiguration_.bind(this)); | 
|  | chrome.accessibilityFeatures.spokenFeedback.get({}, | 
|  | this.onGetOrChangedSpokenFeedbackConfiguration_.bind(this)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Default timeout. | 
|  | * @const {number} | 
|  | */ | 
|  | DimmableUIController.DEFAULT_TIMEOUT = 3000; // ms | 
|  |  | 
|  | /** | 
|  | * We don't allow user to change visibility of tools shorter than this interval. | 
|  | * This is necessary not to hide tools immediately after they become visible by | 
|  | * touchstart event when user taps UI to make them visible. | 
|  | * @const {number} | 
|  | */ | 
|  | DimmableUIController.MIN_OPERATION_INTERVAL = 500; // ms | 
|  |  | 
|  | /** | 
|  | * Returns true if this controller should be disabled. | 
|  | * @param {Gallery.Mode|undefined} mode | 
|  | * @param {Gallery.SubMode|undefined} subMode | 
|  | * @param {boolean} loading | 
|  | * @param {boolean} spokenFeedbackEnabled | 
|  | * @param {boolean} renaming | 
|  | * @return {boolean} | 
|  | */ | 
|  | DimmableUIController.shouldBeDisabled = function( | 
|  | mode, subMode, loading, spokenFeedbackEnabled, renaming) { | 
|  | return spokenFeedbackEnabled || | 
|  | mode === undefined || | 
|  | subMode === undefined || | 
|  | mode === Gallery.Mode.THUMBNAIL || | 
|  | (mode === Gallery.Mode.SLIDE && subMode === Gallery.SubMode.EDIT) || | 
|  | (mode === Gallery.Mode.SLIDE && subMode === Gallery.SubMode.BROWSE && | 
|  | (loading || renaming)); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Sets current mode of Gallery. | 
|  | * @param {Gallery.Mode} mode | 
|  | * @param {Gallery.SubMode} subMode | 
|  | */ | 
|  | DimmableUIController.prototype.setCurrentMode = function(mode, subMode) { | 
|  | if (this.mode_ === mode && this.subMode_ === subMode) | 
|  | return; | 
|  |  | 
|  | this.mode_ = mode; | 
|  | this.subMode_ = subMode; | 
|  | this.updateAvailability_(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Sets whether user is renaming an image or not. | 
|  | * @param {boolean} renaming | 
|  | */ | 
|  | DimmableUIController.prototype.setRenaming = function(renaming) { | 
|  | if (this.renaming_ === renaming) | 
|  | return; | 
|  |  | 
|  | this.renaming_ = renaming; | 
|  | this.updateAvailability_(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Sets whether gallery is currently loading an image or not. | 
|  | * @param {boolean} loading | 
|  | */ | 
|  | DimmableUIController.prototype.setLoading = function(loading) { | 
|  | if (this.loading_ === loading) | 
|  | return; | 
|  |  | 
|  | this.loading_ = loading; | 
|  | this.updateAvailability_(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Handles click event. | 
|  | * @param {!Event} event An event. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.onClick_ = function(event) { | 
|  | if (this.disabled_ || | 
|  | (event.target && | 
|  | this.isPartOfTools_(/** @type {!HTMLElement} */ (event.target)))) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | this.toggle_(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Handles mousemove event. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.onMousemove_ = function() { | 
|  | if (this.disabled_) | 
|  | return; | 
|  |  | 
|  | this.kick(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Handles touch event. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.onTouchOperation_ = function() { | 
|  | if (this.disabled_) | 
|  | return; | 
|  |  | 
|  | this.kick(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Handles mouseover event. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.onMouseover_ = function() { | 
|  | if (this.disabled_) | 
|  | return; | 
|  |  | 
|  | this.isCursorInTools_ = true; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Handles mouseout event. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.onMouseout_ = function() { | 
|  | if (this.disabled_) | 
|  | return; | 
|  |  | 
|  | this.isCursorInTools_ = false; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Returns true if element is a part of tools. | 
|  | * @param {!HTMLElement} element A html element. | 
|  | * @return {boolean} True if element is a part of tools. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.isPartOfTools_ = function(element) { | 
|  | for (var i = 0; i < this.tools_.length; i++) { | 
|  | if (this.tools_[i].contains(element)) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Toggles visibility of UI. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.toggle_ = function() { | 
|  | if (this.isToolsVisible_()) | 
|  | this.show_(false); | 
|  | else | 
|  | this.kick(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Returns true if UI is visible. | 
|  | * @return {boolean} True if UI is visible. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.isToolsVisible_ = function() { | 
|  | return this.container_.hasAttribute('tools'); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Shows UI. | 
|  | * @param {boolean} show True to show UI. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.show_ = function(show) { | 
|  | if (this.isToolsVisible_() === show) | 
|  | return; | 
|  |  | 
|  | if (show) { | 
|  | this.madeVisibleAt_ = Date.now(); | 
|  | this.container_.setAttribute('tools', true); | 
|  | } else { | 
|  | if (Date.now() - this.madeVisibleAt_ < | 
|  | DimmableUIController.MIN_OPERATION_INTERVAL) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | this.container_.removeAttribute('tools'); | 
|  | this.clearTimeout_(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Clears current timeout. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.clearTimeout_ = function() { | 
|  | if (!this.timeoutId_) | 
|  | return; | 
|  |  | 
|  | clearTimeout(this.timeoutId_); | 
|  | this.timeoutId_ = 0; | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Extends current timeout. | 
|  | * @param {number=} opt_timeout Timeout. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.extendTimeout_ = function(opt_timeout) { | 
|  | this.clearTimeout_(); | 
|  |  | 
|  | var timeout = opt_timeout || DimmableUIController.DEFAULT_TIMEOUT; | 
|  | this.timeoutId_ = setTimeout(this.onTimeout_.bind(this), timeout); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Handles timeout. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.onTimeout_ = function() { | 
|  | // If mouse cursor is on tools, extend timeout. | 
|  | if (this.isCursorInTools_) { | 
|  | this.extendTimeout_(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | this.show_(false /* hide */); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Updates availability of this controller with spoken feedback configuration. | 
|  | * @param {Object} details | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.onGetOrChangedSpokenFeedbackConfiguration_ = | 
|  | function(details) { | 
|  | this.spokenFeedbackEnabled_ = !!details.value; | 
|  | this.updateAvailability_(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Sets tools which are controlled by this controller. | 
|  | * This method must not be called more than once for an instance. | 
|  | * @param {!NodeList} tools Tools. | 
|  | */ | 
|  | DimmableUIController.prototype.setTools = function(tools) { | 
|  | assert(this.tools_ === null); | 
|  |  | 
|  | this.tools_ = tools; | 
|  |  | 
|  | for (var i = 0; i < this.tools_.length; i++) { | 
|  | this.tools_[i].addEventListener('mouseover', this.onMouseover_.bind(this)); | 
|  | this.tools_[i].addEventListener('mouseout', this.onMouseout_.bind(this)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Shows UI and set timeout. | 
|  | * @param {number=} opt_timeout Timeout. | 
|  | */ | 
|  | DimmableUIController.prototype.kick = function(opt_timeout) { | 
|  | if (this.disabled_) | 
|  | return; | 
|  |  | 
|  | this.show_(true); | 
|  | this.extendTimeout_(opt_timeout); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Updates availability. | 
|  | * @private | 
|  | */ | 
|  | DimmableUIController.prototype.updateAvailability_ = function() { | 
|  | var disabled = DimmableUIController.shouldBeDisabled( | 
|  | this.mode_, this.subMode_, this.loading_, this.spokenFeedbackEnabled_, | 
|  | this.renaming_); | 
|  |  | 
|  | if (this.disabled_ === disabled) | 
|  | return; | 
|  |  | 
|  | this.disabled_ = disabled; | 
|  |  | 
|  | if (this.disabled_) { | 
|  | this.isCursorInTools_ = false; | 
|  | this.show_(true); | 
|  | this.clearTimeout_(); | 
|  | } else { | 
|  | this.kick(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * Sets cursor's state as out of tools. Mouseout event is not dispatched for | 
|  | * some cases even when mouse cursor goes out of elements. This method is used | 
|  | * to handle these cases manually. | 
|  | */ | 
|  | DimmableUIController.prototype.setCursorOutOfTools = function() { | 
|  | this.isCursorInTools_ = false; | 
|  | }; |