// Copyright (c) 2012 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. cr.define('options', function() { var Preferences = options.Preferences; /** * A controlled setting indicator that can be placed on a setting as an * indicator that the value is controlled by some external entity such as * policy or an extension. * @constructor * @extends {HTMLSpanElement} */ var ControlledSettingIndicator = cr.ui.define('span'); ControlledSettingIndicator.prototype = { __proto__: cr.ui.BubbleButton.prototype, /** * Decorates the base element to show the proper icon. */ decorate: function() { cr.ui.BubbleButton.prototype.decorate.call(this); this.classList.add('controlled-setting-indicator'); // If there is a pref, track its controlledBy and recommendedValue // properties in order to be able to bring up the correct bubble. if (this.pref) { Preferences.getInstance().addEventListener( this.pref, this.handlePrefChange.bind(this)); this.resetHandler = this.clearAssociatedPref_; } }, /** * The given handler will be called when the user clicks on the 'reset to * recommended value' link shown in the indicator bubble. The |this| object * will be the indicator itself. * @param {function()} handler The handler to be called. */ set resetHandler(handler) { this.resetHandler_ = handler; }, /** * Clears the preference associated with this indicator. * @private */ clearAssociatedPref_: function() { Preferences.clearPref(this.pref, !this.dialogPref); }, /* Handle changes to the associated pref by hiding any currently visible * bubble and updating the controlledBy property. * @param {Event} event Pref change event. */ handlePrefChange: function(event) { OptionsPage.hideBubble(); if (event.value.controlledBy) { if (!this.value || String(event.value.value) == this.value) { this.controlledBy = event.value.controlledBy; if (event.value.extension) { if (this.pref == 'session.restore_on_startup' || this.pref == 'homepage_is_newtabpage') { // Special case for the restore on startup, which is implied // by the startup pages settings being controlled by an // extension, and same for the home page as NTP, so we don't want // to show two buttons in these cases. // TODO(mad): Find a better way to handle this. this.controlledBy = null; } else { this.extensionId = event.value.extension.id; this.extensionIcon = event.value.extension.icon; this.extensionName = event.value.extension.name; } } } else { this.controlledBy = null; } } else if (event.value.recommendedValue != undefined) { this.controlledBy = !this.value || String(event.value.recommendedValue) == this.value ? 'hasRecommendation' : null; } else { this.controlledBy = null; } }, /** * Open or close a bubble with further information about the pref. * @private */ toggleBubble_: function() { if (this.showingBubble) { OptionsPage.hideBubble(); } else { var self = this; // Construct the bubble text. if (this.hasAttribute('plural')) { var defaultStrings = { 'policy': loadTimeData.getString('controlledSettingsPolicy'), 'extension': loadTimeData.getString('controlledSettingsExtension'), 'extensionWithName': loadTimeData.getString( 'controlledSettingsExtensionWithName'), }; } else { var defaultStrings = { 'policy': loadTimeData.getString('controlledSettingPolicy'), 'extension': loadTimeData.getString('controlledSettingExtension'), 'extensionWithName': loadTimeData.getString( 'controlledSettingExtensionWithName'), 'recommended': loadTimeData.getString('controlledSettingRecommended'), 'hasRecommendation': loadTimeData.getString('controlledSettingHasRecommendation'), }; if (cr.isChromeOS) { defaultStrings.owner = loadTimeData.getString('controlledSettingOwner'); } } // No controller, no bubble. if (!this.controlledBy || !(this.controlledBy in defaultStrings)) return; var text = defaultStrings[this.controlledBy]; if (this.controlledBy == 'extension' && this.extensionName) text = defaultStrings.extensionWithName; // Apply text overrides. if (this.hasAttribute('text' + this.controlledBy)) text = this.getAttribute('text' + this.controlledBy); // Create the DOM tree. var content = document.createElement('div'); content.className = 'controlled-setting-bubble-content'; content.setAttribute('controlled-by', this.controlledBy); content.textContent = text; if (this.controlledBy == 'hasRecommendation' && this.resetHandler_ && !this.readOnly) { var container = document.createElement('div'); var action = document.createElement('button'); action.classList.add('link-button'); action.classList.add('controlled-setting-bubble-action'); action.textContent = loadTimeData.getString('controlledSettingFollowRecommendation'); action.addEventListener('click', function(event) { self.resetHandler_(); }); container.appendChild(action); content.appendChild(container); } else if (this.controlledBy == 'extension' && this.extensionName) { var extensionContainer = $('extension-controlled-settings-bubble-template'). cloneNode(true); // No need for an id anymore, and thus remove to avoid id collision. extensionContainer.removeAttribute('id'); extensionContainer.hidden = false; var extensionName = extensionContainer.querySelector( '.controlled-setting-bubble-extension-name'); extensionName.textContent = this.extensionName; extensionName.style.backgroundImage = 'url("' + this.extensionIcon + '")'; var manageLink = extensionContainer.querySelector( '.controlled-setting-bubble-extension-manage-link'); manageLink.onclick = function() { uber.invokeMethodOnWindow( window.top, 'showPage', {pageId: 'extensions'}); }; var disableButton = extensionContainer.querySelector('button'); var extensionId = this.extensionId; disableButton.onclick = function() { chrome.send('disableExtension', [extensionId]); }; content.appendChild(extensionContainer); } OptionsPage.showBubble(content, this.image, this, this.location); } }, }; /** * The name of the associated preference. * @type {string} */ cr.defineProperty(ControlledSettingIndicator, 'pref', cr.PropertyKind.ATTR); /** * Whether this indicator is part of a dialog. If so, changes made to the * associated preference take effect in the settings UI immediately but are * only actually committed when the user confirms the dialog. If the user * cancels the dialog instead, the changes are rolled back in the settings UI * and never committed. * @type {boolean} */ cr.defineProperty(ControlledSettingIndicator, 'dialogPref', cr.PropertyKind.BOOL_ATTR); /** * The value of the associated preference that the indicator represents. If * this is not set, the indicator will be visible whenever any value is * enforced or recommended. If it is set, the indicator will be visible only * when the enforced or recommended value matches the value it represents. * This allows multiple indicators to be created for a set of radio buttons, * ensuring that only one of them is visible at a time. */ cr.defineProperty(ControlledSettingIndicator, 'value', cr.PropertyKind.ATTR); /** * The status of the associated preference: * - 'policy': A specific value is enfoced by policy. * - 'extension': A specific value is enforced by an extension. * - 'recommended': A value is recommended by policy. The user could * override this recommendation but has not done so. * - 'hasRecommendation': A value is recommended by policy. The user has * overridden this recommendation. * - unset: The value is controlled by the user alone. * @type {string} */ cr.defineProperty(ControlledSettingIndicator, 'controlledBy', cr.PropertyKind.ATTR); // Export. return { ControlledSettingIndicator: ControlledSettingIndicator }; });