| Index: chrome/browser/resources/options2/pref_ui.js
|
| diff --git a/chrome/browser/resources/options2/pref_ui.js b/chrome/browser/resources/options2/pref_ui.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0468f04be853dca84375a035feb5ae17deee2fe3
|
| --- /dev/null
|
| +++ b/chrome/browser/resources/options2/pref_ui.js
|
| @@ -0,0 +1,723 @@
|
| +// 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;
|
| +
|
| + /**
|
| + * 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,
|
| +
|
| + /**
|
| + * 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) {
|
| + if (self.customChangeHandler(e))
|
| + return;
|
| + 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';
|
| + },
|
| +
|
| + /**
|
| + * 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);
|
| +
|
| + /**
|
| + * 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,
|
| +
|
| + /**
|
| + * 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.
|
| + this.addEventListener('change',
|
| + 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);
|
| + }
|
| + });
|
| + },
|
| +
|
| + /**
|
| + * See |updateDisabledState_| above.
|
| + */
|
| + setDisabled: function(reason, disabled) {
|
| + updateDisabledState_(this, reason, disabled);
|
| + },
|
| + };
|
| +
|
| + /**
|
| + * The preference name.
|
| + * @type {string}
|
| + */
|
| + cr.defineProperty(PrefRadio, '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(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 && 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);
|
| + }
|
| + });
|
| + },
|
| +
|
| + /**
|
| + * 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 && 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) {
|
| + },
|
| +
|
| + /**
|
| + * 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,
|
| +
|
| + /**
|
| + * 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);
|
| + }
|
| + });
|
| + },
|
| +
|
| + /**
|
| + * 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);
|
| +
|
| + /**
|
| + * 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;
|
| + case 'url':
|
| + Preferences.setURLPref(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();
|
| + });
|
| + },
|
| +
|
| + /**
|
| + * See |updateDisabledState_| above.
|
| + */
|
| + setDisabled: function(reason, disabled) {
|
| + updateDisabledState_(this, reason, disabled);
|
| + },
|
| + };
|
| +
|
| + /**
|
| + * The preference name.
|
| + * @type {string}
|
| + */
|
| + cr.defineProperty(PrefTextField, '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(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
|
| + };
|
| +
|
| +});
|
|
|