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 5cr.define('options', function() { 6 var Preferences = options.Preferences; 7 8 /** 9 * A controlled setting indicator that can be placed on a setting as an 10 * indicator that the value is controlled by some external entity such as 11 * policy or an extension. 12 * @constructor 13 * @extends {HTMLSpanElement} 14 */ 15 var ControlledSettingIndicator = cr.ui.define('span'); 16 17 ControlledSettingIndicator.prototype = { 18 __proto__: cr.ui.BubbleButton.prototype, 19 20 /** 21 * Decorates the base element to show the proper icon. 22 */ 23 decorate: function() { 24 cr.ui.BubbleButton.prototype.decorate.call(this); 25 this.classList.add('controlled-setting-indicator'); 26 27 // If there is a pref, track its controlledBy and recommendedValue 28 // properties in order to be able to bring up the correct bubble. 29 if (this.pref) { 30 Preferences.getInstance().addEventListener( 31 this.pref, this.handlePrefChange.bind(this)); 32 this.resetHandler = this.clearAssociatedPref_; 33 } 34 }, 35 36 /** 37 * The given handler will be called when the user clicks on the 'reset to 38 * recommended value' link shown in the indicator bubble. The |this| object 39 * will be the indicator itself. 40 * @param {function()} handler The handler to be called. 41 */ 42 set resetHandler(handler) { 43 this.resetHandler_ = handler; 44 }, 45 46 /** 47 * Clears the preference associated with this indicator. 48 * @private 49 */ 50 clearAssociatedPref_: function() { 51 Preferences.clearPref(this.pref, !this.dialogPref); 52 }, 53 54 /* Handle changes to the associated pref by hiding any currently visible 55 * bubble and updating the controlledBy property. 56 * @param {Event} event Pref change event. 57 */ 58 handlePrefChange: function(event) { 59 OptionsPage.hideBubble(); 60 if (event.value.controlledBy) { 61 if (!this.value || String(event.value.value) == this.value) { 62 this.controlledBy = event.value.controlledBy; 63 if (event.value.extension) { 64 if (this.pref == 'session.restore_on_startup' || 65 this.pref == 'homepage_is_newtabpage') { 66 // Special case for the restore on startup, which is implied 67 // by the startup pages settings being controlled by an 68 // extension, and same for the home page as NTP, so we don't want 69 // to show two buttons in these cases. 70 // TODO(mad): Find a better way to handle this. 71 this.controlledBy = null; 72 } else { 73 this.extensionId = event.value.extension.id; 74 this.extensionIcon = event.value.extension.icon; 75 this.extensionName = event.value.extension.name; 76 } 77 } 78 } else { 79 this.controlledBy = null; 80 } 81 } else if (event.value.recommendedValue != undefined) { 82 this.controlledBy = 83 !this.value || String(event.value.recommendedValue) == this.value ? 84 'hasRecommendation' : null; 85 } else { 86 this.controlledBy = null; 87 } 88 }, 89 90 /** 91 * Open or close a bubble with further information about the pref. 92 * @private 93 */ 94 toggleBubble_: function() { 95 if (this.showingBubble) { 96 OptionsPage.hideBubble(); 97 } else { 98 var self = this; 99 100 // Construct the bubble text. 101 if (this.hasAttribute('plural')) { 102 var defaultStrings = { 103 'policy': loadTimeData.getString('controlledSettingsPolicy'), 104 'extension': loadTimeData.getString('controlledSettingsExtension'), 105 'extensionWithName': loadTimeData.getString( 106 'controlledSettingsExtensionWithName'), 107 }; 108 } else { 109 var defaultStrings = { 110 'policy': loadTimeData.getString('controlledSettingPolicy'), 111 'extension': loadTimeData.getString('controlledSettingExtension'), 112 'extensionWithName': loadTimeData.getString( 113 'controlledSettingExtensionWithName'), 114 'recommended': 115 loadTimeData.getString('controlledSettingRecommended'), 116 'hasRecommendation': 117 loadTimeData.getString('controlledSettingHasRecommendation'), 118 }; 119 if (cr.isChromeOS) { 120 defaultStrings.owner = 121 loadTimeData.getString('controlledSettingOwner'); 122 } 123 } 124 125 // No controller, no bubble. 126 if (!this.controlledBy || !(this.controlledBy in defaultStrings)) 127 return; 128 129 var text = defaultStrings[this.controlledBy]; 130 if (this.controlledBy == 'extension' && this.extensionName) 131 text = defaultStrings.extensionWithName; 132 133 // Apply text overrides. 134 if (this.hasAttribute('text' + this.controlledBy)) 135 text = this.getAttribute('text' + this.controlledBy); 136 137 // Create the DOM tree. 138 var content = document.createElement('div'); 139 content.className = 'controlled-setting-bubble-content'; 140 content.setAttribute('controlled-by', this.controlledBy); 141 content.textContent = text; 142 143 if (this.controlledBy == 'hasRecommendation' && this.resetHandler_ && 144 !this.readOnly) { 145 var container = document.createElement('div'); 146 var action = document.createElement('button'); 147 action.classList.add('link-button'); 148 action.classList.add('controlled-setting-bubble-action'); 149 action.textContent = 150 loadTimeData.getString('controlledSettingFollowRecommendation'); 151 action.addEventListener('click', function(event) { 152 self.resetHandler_(); 153 }); 154 container.appendChild(action); 155 content.appendChild(container); 156 } else if (this.controlledBy == 'extension' && this.extensionName) { 157 var extensionContainer = 158 $('extension-controlled-settings-bubble-template'). 159 cloneNode(true); 160 // No need for an id anymore, and thus remove to avoid id collision. 161 extensionContainer.removeAttribute('id'); 162 extensionContainer.hidden = false; 163 164 var extensionName = extensionContainer.querySelector( 165 '.controlled-setting-bubble-extension-name'); 166 extensionName.textContent = this.extensionName; 167 extensionName.style.backgroundImage = 168 'url("' + this.extensionIcon + '")'; 169 170 var manageLink = extensionContainer.querySelector( 171 '.controlled-setting-bubble-extension-manage-link'); 172 manageLink.onclick = function() { 173 uber.invokeMethodOnWindow( 174 window.top, 'showPage', {pageId: 'extensions'}); 175 }; 176 177 var disableButton = extensionContainer.querySelector('button'); 178 var extensionId = this.extensionId; 179 disableButton.onclick = function() { 180 chrome.send('disableExtension', [extensionId]); 181 }; 182 content.appendChild(extensionContainer); 183 } 184 185 OptionsPage.showBubble(content, this.image, this, this.location); 186 } 187 }, 188 }; 189 190 /** 191 * The name of the associated preference. 192 * @type {string} 193 */ 194 cr.defineProperty(ControlledSettingIndicator, 'pref', cr.PropertyKind.ATTR); 195 196 /** 197 * Whether this indicator is part of a dialog. If so, changes made to the 198 * associated preference take effect in the settings UI immediately but are 199 * only actually committed when the user confirms the dialog. If the user 200 * cancels the dialog instead, the changes are rolled back in the settings UI 201 * and never committed. 202 * @type {boolean} 203 */ 204 cr.defineProperty(ControlledSettingIndicator, 'dialogPref', 205 cr.PropertyKind.BOOL_ATTR); 206 207 /** 208 * The value of the associated preference that the indicator represents. If 209 * this is not set, the indicator will be visible whenever any value is 210 * enforced or recommended. If it is set, the indicator will be visible only 211 * when the enforced or recommended value matches the value it represents. 212 * This allows multiple indicators to be created for a set of radio buttons, 213 * ensuring that only one of them is visible at a time. 214 */ 215 cr.defineProperty(ControlledSettingIndicator, 'value', 216 cr.PropertyKind.ATTR); 217 218 /** 219 * The status of the associated preference: 220 * - 'policy': A specific value is enfoced by policy. 221 * - 'extension': A specific value is enforced by an extension. 222 * - 'recommended': A value is recommended by policy. The user could 223 * override this recommendation but has not done so. 224 * - 'hasRecommendation': A value is recommended by policy. The user has 225 * overridden this recommendation. 226 * - unset: The value is controlled by the user alone. 227 * @type {string} 228 */ 229 cr.defineProperty(ControlledSettingIndicator, 'controlledBy', 230 cr.PropertyKind.ATTR); 231 232 // Export. 233 return { 234 ControlledSettingIndicator: ControlledSettingIndicator 235 }; 236}); 237