summaryrefslogtreecommitdiffstats
path: root/chrome/browser/resources/options/pref_ui.js
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/resources/options/pref_ui.js')
-rw-r--r--chrome/browser/resources/options/pref_ui.js846
1 files changed, 846 insertions, 0 deletions
diff --git a/chrome/browser/resources/options/pref_ui.js b/chrome/browser/resources/options/pref_ui.js
new file mode 100644
index 0000000..82bab86
--- /dev/null
+++ b/chrome/browser/resources/options/pref_ui.js
@@ -0,0 +1,846 @@
+// 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.
+
+// TODO(jhawkins): Add dialog-pref support to all preference controls.
+
+cr.define('options', function() {
+
+ var Preferences = options.Preferences;
+
+ /**
+ * Allows an element to be disabled for several reasons.
+ * The element is disabled if at least one reason is true, and the reasons
+ * can be set separately.
+ * @private
+ * @param {!HTMLElement} el The element to update.
+ * @param {string} reason The reason for disabling the element.
+ * @param {boolean} disabled Whether the element should be disabled or enabled
+ * for the given |reason|.
+ */
+ function updateDisabledState_(el, reason, disabled) {
+ if (!el.disabledReasons)
+ el.disabledReasons = {};
+ if (el.disabled && (Object.keys(el.disabledReasons).length == 0)) {
+ // The element has been previously disabled without a reason, so we add
+ // one to keep it disabled.
+ el.disabledReasons['other'] = true;
+ }
+ if (!el.disabled) {
+ // If the element is not disabled, there should be no reason, except for
+ // 'other'.
+ delete el.disabledReasons['other'];
+ if (Object.keys(el.disabledReasons).length > 0)
+ console.error('Element is not disabled but should be');
+ }
+ if (disabled) {
+ el.disabledReasons[reason] = true;
+ } else {
+ delete el.disabledReasons[reason];
+ }
+ el.disabled = Object.keys(el.disabledReasons).length > 0;
+ }
+
+ /**
+ * Helper function to update element's state from pref change event.
+ * @private
+ * @param {!HTMLElement} el The element to update.
+ * @param {!Event} event The pref change event.
+ */
+ function updateElementState_(el, event) {
+ el.controlledBy = null;
+
+ if (!event.value)
+ return;
+
+ updateDisabledState_(el, 'notUserModifiable', event.value.disabled);
+
+ el.controlledBy = event.value.controlledBy;
+
+ OptionsPage.updateManagedBannerVisibility();
+ }
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefCheckbox class:
+ // TODO(jhawkins): Refactor all this copy-pasted code!
+
+ // Define a constructor that uses an input element as its underlying element.
+ var PrefCheckbox = cr.ui.define('input');
+
+ PrefCheckbox.prototype = {
+ // Set up the prototype chain
+ __proto__: HTMLInputElement.prototype,
+
+ /**
+ * The stored value of the preference that this checkbox controls.
+ * @type {boolean}
+ */
+ prefValue_: null,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ this.type = 'checkbox';
+ var self = this;
+
+ self.initializeValueType(self.getAttribute('value-type'));
+
+ // Listen to pref changes.
+ Preferences.getInstance().addEventListener(this.pref, function(event) {
+ self.prefValue_ = Boolean(event.value.value);
+ self.resetPrefState();
+
+ updateElementState_(self, event);
+ });
+
+ // Listen to user events.
+ this.addEventListener('change', function(e) {
+ if (self.customChangeHandler(e))
+ return;
+
+ if (!this.dialogPref)
+ this.updatePreferenceValue_();
+ });
+ },
+
+ /**
+ * Update the preference value based on the checkbox state.
+ * @private
+ */
+ updatePreferenceValue_: function() {
+ var value = this.inverted_pref ? !this.checked : this.checked;
+ switch (this.valueType) {
+ case 'number':
+ Preferences.setIntegerPref(this.pref, Number(value), this.metric);
+ break;
+ case 'boolean':
+ Preferences.setBooleanPref(this.pref, value, this.metric);
+ break;
+ }
+ },
+
+ /**
+ * Called by SettingsDialog to save the preference.
+ */
+ savePrefState: function() {
+ this.updatePreferenceValue_();
+ },
+
+ /**
+ * Called by SettingsDialog to reset the UI to match the current preference
+ * value.
+ */
+ resetPrefState: function() {
+ this.checked = this.inverted_pref ? !this.prefValue_ : this.prefValue_;
+ },
+
+ /**
+ * Sets up options in checkbox element.
+ * @param {String} valueType The preference type for this checkbox.
+ */
+ initializeValueType: function(valueType) {
+ this.valueType = valueType || 'boolean';
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+
+ /**
+ * This method is called first while processing an onchange event. If it
+ * returns false, regular onchange processing continues (setting the
+ * associated pref, etc). If it returns true, the rest of the onchange is
+ * not performed. I.e., this works like stopPropagation or cancelBubble.
+ * @param {Event} event Change event.
+ */
+ customChangeHandler: function(event) {
+ return false;
+ },
+ };
+
+ /**
+ * The preference name.
+ * @type {string}
+ */
+ cr.defineProperty(PrefCheckbox, 'pref', cr.PropertyKind.ATTR);
+
+ /**
+ * A special preference type specific to dialogs. These preferences are reset
+ * when the dialog is shown and are not saved until the user confirms the
+ * dialog.
+ * @type {boolean}
+ */
+ cr.defineProperty(PrefCheckbox, 'dialogPref', cr.PropertyKind.BOOL_ATTR);
+
+ /**
+ * Whether the preference is controlled by something else than the user's
+ * settings (either 'policy' or 'extension').
+ * @type {string}
+ */
+ cr.defineProperty(PrefCheckbox, 'controlledBy', cr.PropertyKind.ATTR);
+
+ /**
+ * The user metric string.
+ * @type {string}
+ */
+ cr.defineProperty(PrefCheckbox, 'metric', cr.PropertyKind.ATTR);
+
+ /**
+ * Whether to use inverted pref value.
+ * @type {boolean}
+ */
+ cr.defineProperty(PrefCheckbox, 'inverted_pref', cr.PropertyKind.BOOL_ATTR);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefRadio class:
+
+ //Define a constructor that uses an input element as its underlying element.
+ var PrefRadio = cr.ui.define('input');
+
+ PrefRadio.prototype = {
+ // Set up the prototype chain
+ __proto__: HTMLInputElement.prototype,
+
+ // Stores the initialized value of the preference used to reset the input
+ // in resetPrefState().
+ storedValue_: null,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ this.type = 'radio';
+ var self = this;
+
+ // Listen to preference changes.
+ Preferences.getInstance().addEventListener(this.pref,
+ function(event) {
+ if (self.customChangeHandler(event))
+ return;
+ self.checked = String(event.value.value) == self.value;
+ self.storedValue_ = self.checked;
+
+ updateElementState_(self, event);
+ });
+
+ // Dialog preferences are not saved until savePrefState() is explicitly
+ // called.
+ if (!this.dialogPref)
+ this.onchange = this.savePrefState.bind(this);
+ },
+
+ /**
+ * Resets the input to the stored value.
+ */
+ resetPrefState: function() {
+ this.checked = this.storedValue_;
+ },
+
+ /**
+ * Saves the value of the input back into the preference. May be called
+ * directly to save dialog preferences.
+ */
+ savePrefState: function() {
+ this.storedValue_ = this.checked;
+ if (this.value == 'true' || this.value == 'false') {
+ var value = String(this.value);
+ Preferences.setBooleanPref(this.pref, value == String(this.checked),
+ this.metric);
+ } else {
+ Preferences.setIntegerPref(this.pref, parseInt(this.value, 10),
+ this.metric);
+ }
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+
+ /**
+ * This method is called first while processing an onchange event. If it
+ * returns false, regular onchange processing continues (setting the
+ * associated pref, etc). If it returns true, the rest of the onchange is
+ * not performed. I.e., this works like stopPropagation or cancelBubble.
+ * @param {Event} event Change event.
+ */
+ customChangeHandler: function(event) {
+ return false;
+ },
+ };
+
+ /**
+ * The preference name.
+ * @type {string}
+ */
+ cr.defineProperty(PrefRadio, 'pref', cr.PropertyKind.ATTR);
+
+ /**
+ * A special preference type specific to dialogs. These preferences are reset
+ * when the dialog is shown and are not saved until the user confirms the
+ * dialog.
+ * @type {boolean}
+ */
+ cr.defineProperty(PrefRadio, 'dialogPref', cr.PropertyKind.BOOL_ATTR);
+
+ /**
+ * Whether the preference is controlled by something else than the user's
+ * settings (either 'policy' or 'extension').
+ * @type {string}
+ */
+ cr.defineProperty(PrefRadio, 'controlledBy', cr.PropertyKind.ATTR);
+
+ /**
+ * The user metric string.
+ * @type {string}
+ */
+ cr.defineProperty(PrefRadio, 'metric', cr.PropertyKind.ATTR);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefNumeric class:
+
+ // Define a constructor that uses an input element as its underlying element.
+ var PrefNumeric = function() {};
+ PrefNumeric.prototype = {
+ // Set up the prototype chain
+ __proto__: HTMLInputElement.prototype,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ var self = this;
+
+ // Listen to pref changes.
+ Preferences.getInstance().addEventListener(this.pref,
+ function(event) {
+ self.value = event.value.value;
+
+ updateElementState_(self, event);
+ });
+
+ // Listen to user events.
+ this.addEventListener('change',
+ function(e) {
+ if (this.validity.valid) {
+ Preferences.setIntegerPref(self.pref, self.value, self.metric);
+ }
+ });
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+ };
+
+ /**
+ * The preference name.
+ * @type {string}
+ */
+ cr.defineProperty(PrefNumeric, 'pref', cr.PropertyKind.ATTR);
+
+ /**
+ * Whether the preference is controlled by something else than the user's
+ * settings (either 'policy' or 'extension').
+ * @type {string}
+ */
+ cr.defineProperty(PrefNumeric, 'controlledBy', cr.PropertyKind.ATTR);
+
+ /**
+ * The user metric string.
+ * @type {string}
+ */
+ cr.defineProperty(PrefNumeric, 'metric', cr.PropertyKind.ATTR);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefNumber class:
+
+ // Define a constructor that uses an input element as its underlying element.
+ var PrefNumber = cr.ui.define('input');
+
+ PrefNumber.prototype = {
+ // Set up the prototype chain
+ __proto__: PrefNumeric.prototype,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ this.type = 'number';
+ PrefNumeric.prototype.decorate.call(this);
+
+ // Listen to user events.
+ this.addEventListener('input',
+ function(e) {
+ if (this.validity.valid) {
+ Preferences.setIntegerPref(self.pref, self.value, self.metric);
+ }
+ });
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+ };
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefRange class:
+
+ // Define a constructor that uses an input element as its underlying element.
+ var PrefRange = cr.ui.define('input');
+
+ PrefRange.prototype = {
+ // Set up the prototype chain
+ __proto__: HTMLInputElement.prototype,
+
+ /**
+ * The map from input range value to the corresponding preference value.
+ */
+ valueMap: undefined,
+
+ /**
+ * If true, the associated pref will be modified on each onchange event;
+ * otherwise, the pref will only be modified on the onmouseup event after
+ * the drag.
+ */
+ continuous: true,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ this.type = 'range';
+
+ // Update the UI when the pref changes.
+ Preferences.getInstance().addEventListener(
+ this.pref, this.onPrefChange_.bind(this));
+
+ // Listen to user events.
+ // TODO(jhawkins): Add onmousewheel handling once the associated WK bug is
+ // fixed.
+ // https://bugs.webkit.org/show_bug.cgi?id=52256
+ this.onchange = this.onChange_.bind(this);
+ this.onkeyup = this.onmouseup = this.onInputUp_.bind(this);
+ },
+
+ /**
+ * Event listener that updates the UI when the underlying pref changes.
+ * @param {Event} event The event that details the pref change.
+ * @private
+ */
+ onPrefChange_: function(event) {
+ var value = event.value.value;
+ if (value != undefined)
+ this.value = this.valueMap ? this.valueMap.indexOf(value) : value;
+ },
+
+ /**
+ * onchange handler that sets the pref when the user changes the value of
+ * the input element.
+ * @private
+ */
+ onChange_: function(event) {
+ if (this.continuous)
+ this.setRangePref_();
+
+ if (this.notifyChange)
+ this.notifyChange(this, this.mapValueToRange_(this.value));
+ },
+
+ /**
+ * Sets the integer value of |pref| to the value of this element.
+ * @private
+ */
+ setRangePref_: function() {
+ Preferences.setIntegerPref(
+ this.pref, this.mapValueToRange_(this.value), this.metric);
+
+ if (this.notifyPrefChange)
+ this.notifyPrefChange(this, this.mapValueToRange_(this.value));
+ },
+
+ /**
+ * onkeyup/onmouseup handler that modifies the pref if |continuous| is
+ * false.
+ * @private
+ */
+ onInputUp_: function(event) {
+ if (!this.continuous)
+ this.setRangePref_();
+ },
+
+ /**
+ * Maps the value of this element into the range provided by the client,
+ * represented by |valueMap|.
+ * @param {number} value The value to map.
+ * @private
+ */
+ mapValueToRange_: function(value) {
+ return this.valueMap ? this.valueMap[value] : value;
+ },
+
+ /**
+ * Called when the client has specified non-continuous mode and the value of
+ * the range control changes.
+ * @param {Element} el This element.
+ * @param {number} value The value of this element.
+ */
+ notifyChange: function(el, value) {
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+ };
+
+ /**
+ * The preference name.
+ * @type {string}
+ */
+ cr.defineProperty(PrefRange, 'pref', cr.PropertyKind.ATTR);
+
+ /**
+ * Whether the preference is controlled by something else than the user's
+ * settings (either 'policy' or 'extension').
+ * @type {string}
+ */
+ cr.defineProperty(PrefRange, 'controlledBy', cr.PropertyKind.ATTR);
+
+ /**
+ * The user metric string.
+ * @type {string}
+ */
+ cr.defineProperty(PrefRange, 'metric', cr.PropertyKind.ATTR);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefSelect class:
+
+ // Define a constructor that uses a select element as its underlying element.
+ var PrefSelect = cr.ui.define('select');
+
+ PrefSelect.prototype = {
+ // Set up the prototype chain
+ __proto__: HTMLSelectElement.prototype,
+
+ /**
+ * @type {string} The stored value of the preference that this select
+ * controls.
+ */
+ prefValue_: null,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ var self = this;
+
+ // Listen to pref changes.
+ Preferences.getInstance().addEventListener(this.pref, function(event) {
+ // Make sure |value| is a string, because the value is stored as a
+ // string in the HTMLOptionElement.
+ value = event.value.value.toString();
+
+ updateElementState_(self, event);
+ self.prefValue_ = value;
+ self.resetPrefState();
+ });
+
+ // Listen to user events.
+ this.addEventListener('change', function(event) {
+ if (!self.dialogPref)
+ self.updatePreference_(self.prefValue_);
+ });
+ },
+
+ /**
+ * Resets the input to the stored value.
+ */
+ resetPrefState: function() {
+ var found = false;
+ for (var i = 0; i < this.options.length; i++) {
+ if (this.options[i].value == this.prefValue_) {
+ this.selectedIndex = i;
+ found = true;
+ }
+ }
+
+ // Item not found, select first item.
+ if (!found)
+ this.selectedIndex = 0;
+
+ if (this.onchange)
+ this.onchange(event);
+ },
+
+ /**
+ * Updates the preference to the currently selected value.
+ */
+ updatePreference_: function() {
+ if (!this.dataType) {
+ console.error('undefined data type for <select> pref');
+ return;
+ }
+
+ var prefValue = this.options[this.selectedIndex].value;
+ switch (this.dataType) {
+ case 'number':
+ Preferences.setIntegerPref(this.pref, prefValue, this.metric);
+ break;
+ case 'double':
+ Preferences.setDoublePref(this.pref, prefValue, this.metric);
+ break;
+ case 'boolean':
+ var value = (prefValue == 'true');
+ Preferences.setBooleanPref(this.pref, value, this.metric);
+ break;
+ case 'string':
+ Preferences.setStringPref(this.pref, prefValue, this.metric);
+ break;
+ default:
+ console.error('unknown data type for <select> pref: ' +
+ this.dataType);
+ }
+ },
+
+ /**
+ * Called by SettingsDialog to save the stored value to preferences.
+ */
+ savePrefState: function() {
+ this.updatePreference_();
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+ };
+
+ /**
+ * The preference name.
+ * @type {string}
+ */
+ cr.defineProperty(PrefSelect, 'pref', cr.PropertyKind.ATTR);
+
+ /**
+ * Whether the preference is controlled by something else than the user's
+ * settings (either 'policy' or 'extension').
+ * @type {string}
+ */
+ cr.defineProperty(PrefSelect, 'controlledBy', cr.PropertyKind.ATTR);
+
+ /**
+ * A special preference type specific to dialogs. These preferences are reset
+ * when the dialog is shown and are not saved until the user confirms the
+ * dialog.
+ * @type {boolean}
+ */
+ cr.defineProperty(PrefSelect, 'dialogPref', cr.PropertyKind.BOOL_ATTR);
+
+ /**
+ * The user metric string.
+ * @type {string}
+ */
+ cr.defineProperty(PrefSelect, 'metric', cr.PropertyKind.ATTR);
+
+ /**
+ * The data type for the preference options.
+ * @type {string}
+ */
+ cr.defineProperty(PrefSelect, 'dataType', cr.PropertyKind.ATTR);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefTextField class:
+
+ // Define a constructor that uses an input element as its underlying element.
+ var PrefTextField = cr.ui.define('input');
+
+ PrefTextField.prototype = {
+ // Set up the prototype chain
+ __proto__: HTMLInputElement.prototype,
+
+ /**
+ * @type {string} The stored value of the preference that this text field
+ * controls.
+ */
+ prefValue_: null,
+
+ /**
+ * Saves the value of the input back into the preference. May be called
+ * directly to save dialog preferences.
+ */
+ savePrefState: function() {
+ switch (this.dataType) {
+ case 'number':
+ Preferences.setIntegerPref(this.pref, this.value, this.metric);
+ break;
+ case 'double':
+ Preferences.setDoublePref(this.pref, this.value, this.metric);
+ break;
+ case 'url':
+ Preferences.setURLPref(this.pref, this.value, this.metric);
+ break;
+ default:
+ Preferences.setStringPref(this.pref, this.value, this.metric);
+ break;
+ }
+ },
+
+ /**
+ * Resets the input to the stored value.
+ */
+ resetPrefState: function() {
+ this.value = this.prefValue_;
+ },
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ var self = this;
+
+ // Listen to pref changes.
+ Preferences.getInstance().addEventListener(this.pref,
+ function(event) {
+ self.value = event.value.value;
+
+ updateElementState_(self, event);
+
+ self.prefValue_ = self.value;
+ });
+
+ // Listen to user events.
+ if (!this.dialogPref)
+ this.addEventListener('change', this.savePrefState.bind(this));
+
+ window.addEventListener('unload',
+ function() {
+ if (document.activeElement == self)
+ self.blur();
+ });
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+ };
+
+ /**
+ * The preference name.
+ * @type {string}
+ */
+ cr.defineProperty(PrefTextField, 'pref', cr.PropertyKind.ATTR);
+
+ /**
+ * A special preference type specific to dialogs. These preferences are reset
+ * when the dialog is shown and are not saved until the user confirms the
+ * dialog.
+ * @type {boolean}
+ */
+ cr.defineProperty(PrefTextField, 'dialogPref', cr.PropertyKind.BOOL_ATTR);
+
+ /**
+ * Whether the preference is controlled by something else than the user's
+ * settings (either 'policy' or 'extension').
+ * @type {string}
+ */
+ cr.defineProperty(PrefTextField, 'controlledBy', cr.PropertyKind.ATTR);
+
+ /**
+ * The user metric string.
+ * @type {string}
+ */
+ cr.defineProperty(PrefTextField, 'metric', cr.PropertyKind.ATTR);
+
+ /**
+ * The data type for the preference options.
+ * @type {string}
+ */
+ cr.defineProperty(PrefTextField, 'dataType', cr.PropertyKind.ATTR);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // PrefButton class:
+
+ // Define a constructor that uses a button element as its underlying element.
+ var PrefButton = cr.ui.define('button');
+
+ PrefButton.prototype = {
+ // Set up the prototype chain
+ __proto__: HTMLButtonElement.prototype,
+
+ /**
+ * Initialization function for the cr.ui framework.
+ */
+ decorate: function() {
+ var self = this;
+
+ // Listen to pref changes. This element behaves like a normal button and
+ // doesn't affect the underlying preference; it just becomes disabled
+ // when the preference is managed, and its value is false.
+ // This is useful for buttons that should be disabled when the underlying
+ // boolean preference is set to false by a policy or extension.
+ Preferences.getInstance().addEventListener(this.pref,
+ function(event) {
+ var e = {
+ value: {
+ 'disabled': event.value.disabled && !event.value.value,
+ 'controlledBy': event.value.controlledBy
+ }
+ };
+ updateElementState_(self, e);
+ });
+ },
+
+ /**
+ * See |updateDisabledState_| above.
+ */
+ setDisabled: function(reason, disabled) {
+ updateDisabledState_(this, reason, disabled);
+ },
+ };
+
+ /**
+ * The preference name.
+ * @type {string}
+ */
+ cr.defineProperty(PrefButton, 'pref', cr.PropertyKind.ATTR);
+
+ /**
+ * Whether the preference is controlled by something else than the user's
+ * settings (either 'policy' or 'extension').
+ * @type {string}
+ */
+ cr.defineProperty(PrefButton, 'controlledBy', cr.PropertyKind.ATTR);
+
+ // Export
+ return {
+ PrefCheckbox: PrefCheckbox,
+ PrefNumber: PrefNumber,
+ PrefNumeric: PrefNumeric,
+ PrefRadio: PrefRadio,
+ PrefRange: PrefRange,
+ PrefSelect: PrefSelect,
+ PrefTextField: PrefTextField,
+ PrefButton: PrefButton
+ };
+
+});