// Copyright (c) 2011 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; /** * Helper function 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.managed = event.value && event.value['managed'] != undefined ? event.value['managed'] : false; // Managed UI elements can only be disabled as a result of being // managed. They cannot be enabled as a result of a pref being // unmanaged. if (el.managed) el.disabled = true; // Disable UI elements if backend says so. if (!el.disabled && event.value && event.value['disabled']) el.disabled = true; } ///////////////////////////////////////////////////////////////////////////// // 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, /** * 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) { var value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; // Invert pref value if inverted_pref == true. if (self.inverted_pref) self.checked = !Boolean(value); else self.checked = Boolean(value); updateElementState_(self, event); }); // Listen to user events. this.addEventListener( 'change', function(e) { var value = self.inverted_pref ? !self.checked : self.checked; switch(self.valueType) { case 'number': Preferences.setIntegerPref(self.pref, Number(value), self.metric); break; case 'boolean': Preferences.setBooleanPref(self.pref, value, self.metric); break; } }); }, /** * Sets up options in checkbox element. * @param {String} valueType The preference type for this checkbox. */ initializeValueType: function(valueType) { this.valueType = valueType || 'boolean'; }, }; /** * The preference name. * @type {string} */ cr.defineProperty(PrefCheckbox, 'pref', 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, /** * Initialization function for the cr.ui framework. */ decorate: function() { this.type = 'radio'; var self = this; // Listen to pref changes. Preferences.getInstance().addEventListener(this.pref, function(event) { var value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; self.checked = String(value) == self.value; updateElementState_(self, event); }); // Listen to user events. // Use the 'click' event instead of 'change', because of a bug in WebKit // which prevents 'change' from being sent when the user changes selection // using the keyboard. // https://bugs.webkit.org/show_bug.cgi?id=32013 this.addEventListener('click', function(e) { if(self.value == 'true' || self.value == 'false') { Preferences.setBooleanPref(self.pref, self.value == 'true', self.metric); } else { Preferences.setIntegerPref(self.pref, parseInt(self.value, 10), self.metric); } }); }, }; /** * The preference name. * @type {string} */ cr.defineProperty(PrefRadio, 'pref', 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 && event.value['value'] != undefined ? event.value['value'] : event.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); } }); } }; /** * The preference name. * @type {string} */ cr.defineProperty(PrefNumeric, 'pref', 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); } }); } }; ///////////////////////////////////////////////////////////////////////////// // 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 && event.value['value'] != undefined ? event.value['value'] : event.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) { }, }; /** * The preference name. * @type {string} */ cr.defineProperty(PrefRange, 'pref', 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, /** * Initialization function for the cr.ui framework. */ decorate: function() { var self = this; // Listen to pref changes. Preferences.getInstance().addEventListener(this.pref, function(event) { var value = event.value && event.value['value'] != undefined ? event.value['value'] : event.value; // Make sure |value| is a string, because the value is stored as a // string in the HTMLOptionElement. value = value.toString(); updateElementState_(self, event); var found = false; for (var i = 0; i < self.options.length; i++) { if (self.options[i].value == value) { self.selectedIndex = i; found = true; } } // Item not found, select first item. if (!found) self.selectedIndex = 0; if (self.onchange != undefined) self.onchange(event); }); // Listen to user events. this.addEventListener('change', function(e) { if (!self.dataType) { console.error('undefined data type for <select> pref'); return; } switch(self.dataType) { case 'number': Preferences.setIntegerPref(self.pref, self.options[self.selectedIndex].value, self.metric); break; case 'double': Preferences.setDoublePref(self.pref, self.options[self.selectedIndex].value, self.metric); break; case 'boolean': var option = self.options[self.selectedIndex]; var value = (option.value == 'true') ? true : false; Preferences.setBooleanPref(self.pref, value, self.metric); break; case 'string': Preferences.setStringPref(self.pref, self.options[self.selectedIndex].value, self.metric); break; default: console.error('unknown data type for <select> pref: ' + self.dataType); } }); }, }; /** * The preference name. * @type {string} */ cr.defineProperty(PrefSelect, 'pref', cr.PropertyKind.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, /** * 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 && event.value['value'] != undefined ? event.value['value'] : event.value; updateElementState_(self, event); }); // Listen to user events. this.addEventListener('change', function(e) { switch(self.dataType) { case 'number': Preferences.setIntegerPref(self.pref, self.value, self.metric); break; case 'double': Preferences.setDoublePref(self.pref, self.value, self.metric); break; default: Preferences.setStringPref(self.pref, self.value, self.metric); break; } }); window.addEventListener('unload', function() { if (document.activeElement == self) self.blur(); }); } }; /** * The preference name. * @type {string} */ cr.defineProperty(PrefTextField, 'pref', 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); // Export return { PrefCheckbox: PrefCheckbox, PrefNumber: PrefNumber, PrefNumeric: PrefNumeric, PrefRadio: PrefRadio, PrefRange: PrefRange, PrefSelect: PrefSelect, PrefTextField: PrefTextField }; });